Sign in to follow this  

Connect Four Help

This topic is 3887 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hey All I'm making a connect four game. What I need help is to drop an x or o into the grid and get it to update the grid. - i cant think how to program it. I've used an array and struct to create the grid. The grid is 7 by 7. Thank for any help. The code i have is the follow :-
#include <iostream>
using namespace std;

struct player 
{
	char counter; // 'O' or 'X'
	char name[20]; // player 1 and player 2 name
	int  won; // counts how many time player 1 or 2 has won 
	int lost; // counts how many times player 1 and 2 has lost
	int draw; //counts how many time player 1 or 2 has draw 
};

struct column 
{
	char rows[7]; // arrays 7 row on the grid
	int count; // 
};

column frame[7]; // used for the frame

void players_details()
{
  system("cls");
  
  player player1; 
  player player2; 

  player1.counter = 'X';
  player2.counter = 'O';

  cout<<"Player 1 Please Enter Your First Name\n";
  cout<<endl;
  cin >> player1.name;
  cout<<endl;
  
  cout<<"Player 2 Please Enter Your First Name\n";
  cout<<endl;
  cin>>player2.name;
  cout<<endl;
  
  cout<<player1.name <<" Counter Is "<<player1.counter<<endl;
  cout<<endl;
  cout<<player2.name <<" Counter Is "<<player2.counter<<endl;
  cout<<endl;
  cout<<player1.name <<" Vs "<<player2.name<<endl;
  cout<<endl;
}

void clearGrid()
{
	for (int i=0;i<7;i++) {
		for (int j=0; j<7; j++)
		{         
			frame[i].rows[j] = ' ';
		}
		frame[i].count = 0;
	}
}

void drawGrid()
{
  system("cls");
  cout<<endl;
	for (int i=6; i>= 0; i--) { 
		for (int j=0; j<7; j++)
		{         
			cout << "|" << frame[i].rows[j]; 
		}
		cout << "|";
		cout << endl;
	}
	cout<<endl;
}

void drawCounterGrid() -The is the function to draw an x or o which i need help
{
  
}

int main()
{
 system("cls");
 int choice;

 cout<<"Connect Four Version 1.0\n";
 cout<<endl;
 
 cout<<"Menu\n";
 cout<<endl;
 cout<<"1) Start A Game\n";
 cout<<"2) Manual How To Play The Game\n"; // TODO
 cout<<"3) Top 20 High Players\n"; // TODO
 cout<<"4) Quit Game\n"<<endl;
 cout<<"Please Select 1 To 4:\n";
 cout<<endl;
 cin>>choice;
 cout<<endl; 
 switch(choice)
 {
	 case 1:
        players_details();
        drawGrid();
        clearGrid();
        drawCounterGrid();
 }
 return choice;
}

[Edited by - Cool_Program_Guy on March 9, 2007 4:47:39 PM]

Share this post


Link to post
Share on other sites
I might have misunderstood what you're asking for here but this is my shot anyway:

First off: you're really missing a game loop [which I'd say is the basic component of any game]. You *could* carry on doing it as you have it set up by adding most of your game logic to the drawCounterGrid() function but there really is a better way of organizing this. What I would do is go to your initial menu [in your main() function] and have case 1: call a new function to init your game info [this gets the players details and clears/draws the grid] then you jump into your main game loop:

Within the game loop what happens each time would be something along the lines of:

1] Ask player 1 where he wants to place his counter [simple cin cout stuff all he needs to say is the column he wants to place the counter in]
2] Check if there is space in that column to place a counter
3] If the column is full: ask for input again [1]
4] If the column is not full: place the counter in the appropriate row in that column [loop through the column until you either hit the bottom or find a non-empty slot, then place your counter in the slot above it]
5] Redraw the grid with the new counter in place.
6] Check for a win condition [Have some algorithm that checks if there are 3 connected counters - I would probably just check the positions adjacent to the newly placed counter instead of looping through the whole grid]
7] If the player won : end the game [quit the game loop , increment the player's win stat : display any end messages/menu etc]
8] If the player did not win - are there any other endgame scenarios? [check for them and end appropriately]
9] Switch players and start at [1] for the next player

hope this helps.

Share this post


Link to post
Share on other sites
Xqwzts seemed to put it pretty good I thought. It isn't as simple as just calling a function, though that doesn't mean it's difficult.

based on what you have so far, you would need to loop through the elements of the appropriate column to find the location of the lower-most square.


//frame = array of columns
//choice = chosen column to place marker in
//current_marker = current player's marker. (X or O)

if(frame[choice].rows[6] != ' '){
//THIS ROW IS FULL, ASK AGAIN
}else{
for(int i = 5; i >= -1; i--){ //start at the top, and work down.
if(frame[choice].rows[i] != ' ' || i == -1){
//FOUND FIRST EMPTY SPOT
frame[choice].rows[(i+1)] = current_marker;
break;
}
}
}





I'm sure it could be done much better, including removing all the magic numbers, not to mention the loop is kindof ugly. But the basic idea is there.

Share this post


Link to post
Share on other sites
I would suggest using a pointer to the current player for getting input. This way you only have one loop with one section of code to get input and check if there is a game over situation. As for knowing when to stop, just check if the board is full, and check to see if the last move made any win situations (by checking in lines going away from the last move).

Share this post


Link to post
Share on other sites
First of all, I wanted to clarify that you are making a console project (which is why it's black and white). Console projects don't use graphics, only text. If you stick with programming you will learn that you can also make Win32 applications, but those require you to use something like OpenGL or Direct3D for graphics, and a bunch of windows API code. In other words, that is a HUGE HUGE step, but you are on the right track by learning the basics with a console/text game. I just want you to realize that you won't be making Halo 2 next week, this stuff takes A LOT of practice, patience, and persistence. :)

You should try to get in the mindset of writing functions to accomplish things. So, when you ask the question "how am I going to check for a win?" You should start thinking how you could make a function that knows nothing about your project, other than how to check if there was a win last turn, and simply return the results. In order to do this, what would your function need access to? Well, it needs the grid, correct? Since you are using it as a global variable you don't need to pass it in to the function so you're good. The other thing I would suggest is giving access to is the last square on the grid somebody moved to, so I will make that as two parameters for the row and column of that given square.

Here is the general idea you should shoot for. This code isn't tested and it most definitely isn't optimized. If I were writing it for that I woulda probably made the algorithm in one for-loop, but this was just a quick mock up to give you an idea of what you should be trying to do.



bool CheckWin(const int last_move_row, const int last_move_col)
{
const char check_for = frame[last_move_col].rows[last_move_row];
int in_a_row = 0;

/* since your grid is only 7x7 you might as well check the entire
row and the entire column the last move lied on, because if the
player went right in the middle it could be a win on either side */


/* check the row */
for(int col = 0; col < 7; ++col)
{
if(frame[col].rows[last_move_row] == check_for)
++in_a_row;
else
in_a_row = 0;

if(in_a_row == 4)
return true;
}

in_a_row = 0;

/* check the column */
for(int row = 0; row < 7; ++row)
{
if(frame[last_move_col].rows[row] == check_for)
++in_a_row;
else
in_a_row = 0;

if(in_a_row == 4)
return true;
}

in_a_row = 0;

/* this would normally be - 3, but I didn't want to check
in the for loop if I did a continue... */

int current_col = last_move_col - 4;

/* check one of the diagnols */
for(int row = last_move_row - 3; row < 7; ++row)
{
++current_col;

/* make sure to only check on the grid... */
if(row < 0 || current_col < 0 || current_col >= 7)
continue;

if(frame[current_col].rows[row] == check_for)
++in_a_row;
else
in_a_row = 0;

if(in_a_row == 4)
return true;
}

in_a_row = 0;
current_col = last_move_col + 4; /* normally 3, same as above */

/* check the other diagnol */
for(int row = last_move_row - 3; row < 7; ++row)
{
--current_col;

/* make sure to only check on the grid... */
if(row < 0 || current_col < 0 || current_col >= 7)
continue;

if(frame[current_col].rows[row] == check_for)
++in_a_row;
else
in_a_row = 0;

if(in_a_row == 4)
return true;
}

return false;
}




I hope this helps! Good luck!

[Edited by - Nvii on April 7, 2007 2:01:30 PM]

Share this post


Link to post
Share on other sites
Hey, I just testing bit that code you used and it giving an error saying:-

Error 1 error C2660: 'CheckWin' : function does not take 0 arguments


if (move = 1) {
do {
do {
cout<<player1.name<<" Please Enter A Column 1 To 7\n";
cout<<endl;
cin>>choice;
choice--;
} while ((choice <0) ||(choice >7));
} while (frame[choice].count ==7);
}
{ drawGrid();
CheckWin();
addCounter(player1.counter,choice);
move++;
}



I assume you put the CheckWin() before you allow the user to put a peice into the grid??

The only thing I changed with the code you gave was an
int num_in_a_row; 

as that was declared anywhere.

Would I be right thinking its somthing to with the bool in it??

Share this post


Link to post
Share on other sites
The "CheckWin" function he posted takes 2 integers, the row of the last move and the column of the last move. When you call it in your code, you just have "CheckWin()". You need to send it those two numbers for it to compile correctly.

CheckWin(rownum, columnnum);

Share this post


Link to post
Share on other sites
Quote:
Original post by Cool_Program_Guy
if (move = 1) {...}

"Put the value 1 into variable move." The result of this assignment is 1, which is interpreted as true. Are you sure this is what you want?

To compare two integer expressions, you must use the "==" operator.

Share this post


Link to post
Share on other sites
I kind of assumed you were really new. You are already making huge steps without really knowing what is going on, haha. But, that's okay, we've all been there!

Functions can take parameters. Parameters are values, or known data, that you have already computed and do not want to calculate again. So in this case, after you find out what the last move was, you then need to send these to the function so it can work with them. The errors you were getting were telling you that you needed to pass CheckWin two integers for it to work.

"Do-while" statements, in general, are a bad thing. You can almost always do the same things with just a "while" statement, and it is usually easier to follow that way.

Now this isn't everything, but this is the general idea you should be doing for a game loop. I hope ya can see how it is nice to seperate things into functions, and have a better idea on how they are supposed to work together. You will still need a lot of your other code to get this to work, including the CheckWin function I wrote (sorry, that I messed up that in_a_row variable, wasn't using a compiler, it's fixed now).



bool game_loop = true; // global


// The & in a parameter means you are passing by reference, which means you have direct access
// to the variable that was passed in, that is why when you change it here it is affected in
// the PlayerTurn function
bool GetValidInput(int& row, int& col)
{
cout << "use your name stuff here" <<" Please Enter A Row 1 To 7" << endl << endl;
cin >> row;
cout << "Please Enter A Column 1 to 7" << endl << endl;
cin >> col;

// let user put in a -1 in order to quit
if( (row >= -1 && row < 7) &&
(col >= -1 && col < 7) )
{
return true;
}

return false;
}


void PlayerTurn(const int player_num)
{
int row, col;

while(1) // loop until I say break
{
// NOTE: this is the same as
// if(GetValidInput(row, col) == true)
// the difference being a style issue...

if(GetValidInput(row, col) )
break;
}

// this allows for the user to quit by putting in -1
if(row == -1 || col == -1)
{
game_loop = false;
return;
}


// I would think your drawGrid() would need row and col? so that
// it can put an X or an O in the correct spot...
// drawGrid(row, col);

drawGrid();

if(CheckWin(row, col) ) // if there was a win, end game
{
game_loop = false;
cout << player_num << " wins" << endl;
}
}


int main()
{
int players_turn = 1;

while(game_loop) // keep looping until game is over
{
PlayerTurn(players_turn);

// switch turns
if(players_turn == 1)
players_turn = 2;
else
players_turn = 1;
}

return 0;
}











[Edited by - Nvii on April 7, 2007 3:57:37 PM]

Share this post


Link to post
Share on other sites
Hey yeh i am new to C++ allthough, I can program in Visual Basic :p.

Yeh, that looks a lot better then what I had allthough, I'll have to make a function to show my menu. And I'll need to code the program so it drops a peice into the grid (i assume that goes into the if state in the int main due to it swap p1 and p2) cos atm it just saying someone won due to the fact the function of putting a counter into the grid aint in.

Would it be best to declar p1 and p2 as a global rather then just one function?

Thanks 4 the help (I am reading lots of books n looking at inet examples to get better with c++!)

Cheers for all the help

[Edited by - Cool_Program_Guy on April 7, 2007 5:35:26 PM]

Share this post


Link to post
Share on other sites
Quote:
And I'll need to code the program so it drops a peice into the grid


Yes, you still need to do this. I was originally thinking you'd call drawGrid(row, col); which would update your "frame" and draw the new grid. But, you can put that in PlayerTurn if you want, just before drawGrid(); Also, you probably want to use your player struct so I updated the whole thing to work with your stuff. Its working great for me, I hope ya have the time to look over what I did, to see if it makes sense. And, if you have any more questions or clarifications, I'm willing to help! ;)

Happy coding!



#include <iostream>
#include <string> // strings are much nicer than char arrays...

using namespace std;


bool game_loop = true;


struct Player
{
// this is a constructor, you'll learn about them when you learn
// about classes
Player(const string& player_name, const char type)
{
name = player_name;
counter = type;
won = 0;
lost = 0;
draw = 0;
}

char counter; // 'O' or 'X'
string name; // player 1 and player 2 name
int won; // counts how many time player 1 or 2 has won
int lost; // counts how many times player 1 and 2 has lost
int draw; //counts how many time player 1 or 2 has draw
};

struct column
{
char rows[7]; // arrays 7 row on the grid
int count; //
};

column frame[7]; // used for the frame

void drawGrid()
{
system("cls");
cout << endl;

for(int col = 0; col < 7; ++col)
{
for(int row = 0; row < 7; ++row)
{
cout << "|" << frame[col].rows[row] << "|";
}

cout << endl;
}

cout << endl;
}


bool CheckWin(const int last_move_row, const int last_move_col)
{
const char check_for = frame[last_move_col].rows[last_move_row];
int in_a_row = 0;

/* since your grid is only 7x7 you might as well check the entire
row and the entire column the last move lied on, because if the
player went right in the middle it could be a win on either side */


/* check the row */
for(int col = 0; col < 7; ++col)
{
if(frame[col].rows[last_move_row] == check_for)
++in_a_row;
else
in_a_row = 0;

if(in_a_row == 4)
return true;
}

in_a_row = 0;

/* check the column */
for(int row = 0; row < 7; ++row)
{
if(frame[last_move_col].rows[row] == check_for)
++in_a_row;
else
in_a_row = 0;

if(in_a_row == 4)
return true;
}

in_a_row = 0;

/* this would normally be - 3, but I didn't want to check
in the for loop if I did a continue... */

int current_col = last_move_col - 4;

/* check one of the diagnols */
for(int row = last_move_row - 3; row < 7; ++row)
{
++current_col;

/* make sure to only check on the grid... */
if(row < 0 || current_col < 0 || current_col >= 7)
continue;

if(frame[current_col].rows[row] == check_for)
++in_a_row;
else
in_a_row = 0;

if(in_a_row == 4)
return true;
}

in_a_row = 0;
current_col = last_move_col + 4; /* normally 3, same as above */

/* check the other diagnol */
for(int row = last_move_row - 3; row < 7; ++row)
{
--current_col;

/* make sure to only check on the grid... */
if(row < 0 || current_col < 0 || current_col >= 7)
continue;

if(frame[current_col].rows[row] == check_for)
++in_a_row;
else
in_a_row = 0;

if(in_a_row == 4)
return true;
}

return false;
}


bool GetValidInput(const string& name, int& row, int& col)
{
cout << name <<" Please Enter A Column 0 To 6" << endl << endl;
cin >> row;
cout << endl << name <<"Please Enter A Row 0 To 6" << endl << endl;
cin >> col;
cout << endl;

if( (row >= -1 && row < 7) &&
(col >= -1 && col < 7) )
{
return true;
}

return false;
}


void PlayerTurn(Player& current_player)
{
int row, col;

while(1) // loop until I say break
{
if(GetValidInput(current_player.name, row, col) )
break;
}

if(row == -1 || col == -1)
{
game_loop = false;
return;
}

frame[col].rows[row] = current_player.counter;
drawGrid();

if(CheckWin(row, col) )
{
game_loop = false;
cout << current_player.name << " wins" << endl;
}
}

void GetNames(string& name1, string& name2)
{
cout << "Player 1 Please Enter Your First Name" << endl << endl;
cin >> name1;
cout << endl;

cout << "Player 2 Please Enter Your First Name" << endl << endl;
cin >> name2;
cout << endl;
}


void InitializeGrid()
{
for(int col = 0; col < 7; ++col)
{
for(int row = 0; row < 7; ++row)
{
frame[col].rows[row] = ' ';
}
}
}


int main()
{

string name1, name2;
GetNames(name1, name2);

Player player_one(name1, 'X');
Player player_two(name2, 'O');

InitializeGrid();

int players_turn = 1;

while(game_loop) // keep looping until game is over
{
if(players_turn == 1)
{
PlayerTurn(player_one);
players_turn = 2; // for next time around
}
else
{
PlayerTurn(player_two);
players_turn = 1; // for next time around
}
}

return 0;
}



Share this post


Link to post
Share on other sites
Haha, yeah I kinda forgot you only pick a column in connect 4 :/

But, also there's more you can still put into this project, and you would learn a lot doing it. Here's some things you could do:

1) Make the input idiot-proof. So, if they type in a letter instead of an integer the program doesn't break. You need to google "iostream" and learn about how cin can fail, and how to fix it. (googling stuff is usually the best way to learn these kind of things, if you get stuck for a long period then you should ask on forums, since you should then be really familiar with the problem)

2) You mentioned how you wanted to read/write profiles (like their wins and losses) to and from file. Google "fstream"

3) Make a computer opponent. This one would probably take the most time, depending on how "good" you wanted to make the AI. Also, depending on if you wanted to teach the AI as you played, thats a whole other ocean to bathe in...so yeah, probably stay away from this one for now, until ya get better at programming :) Although, a random moving AI is rather easy.

Oh, and the only thing I could come up with for you question was something like this; sadly, ASCII characters are a bit limited :( But, maybe I'm not thinking of one that could make it look better.



void drawGrid()
{
system("cls");
cout << endl;

// 21 is 3 * 7, when you print out
// the grid there is 3 characters for
// each column
for(int col = 0; col < 21; ++col)
cout << "_";

cout << endl;

for(int col = 0; col < 7; ++col)
{
for(int row = 0; row < 7; ++row)
{
cout << "|" << frame[col].rows[row] << "|";
}

cout << endl;
}

// same as above
for(int col = 0; col < 21; ++col)
cout << "-";

cout << endl;
}




Share this post


Link to post
Share on other sites
hmm, if i try to take the row away from the code all hell breaks lose lol :P, Hmm the
 void addCounter(char counter, int col)
{
// adds the count +1 everytime someone puts a counter into the grid
frame[col].rows[frame[col].count++] = counter;
}
function could that solve putting the col into the right grid and stop the grid for overloading with to many counters??. Atm when i take the row out i dont get an error it just crashes

Share this post


Link to post
Share on other sites
Well, there's a few things you have to change in order to get it to work. First of all you still need to get what row you are placing it on, so that CheckWin knows what to do. Other than that, you were on the right track. But, you want to either initialize frame[all columns].count to 6 or in addCounter set 6 - frame[col].count, otherwise the X's and O's will be put at the top and go down, as opposed to put at the bottom going up.

I made the changes for ya; I also had it backwards when I drew the grid, so I flipped that as well. Here's all the functions I updated. There would still be a problem if somebody over-filled a column...I'll let you deal with that. :D



// this now returns an int, which is actually the row you placed it on
int addCounter(char counter, int col)
{
// adds the count -1 everytime someone puts a counter into the grid
frame[col].rows[frame[col].count] = counter;

return frame[col].count--;
}

void drawGrid()
{
system("cls");
cout << endl;

for(int col = 0; col < 21; ++col)
cout << "_";

cout << endl;

// here is where I had rows and cols in the wrong order...
// but it's fixed now
for(int row = 0; row < 7; ++row)
{
for(int col = 0; col < 7; ++col)
{
cout << "|" << frame[col].rows[row] << "|";
}

cout << endl;
}

for(int col = 0; col < 21; ++col)
cout << "-";

cout << endl;
}

bool GetValidInput(const string& name, int& col)
{
cout << name <<" Please Enter A Column 0 To 6" << endl << endl;
cin >> col;
cout << endl;

if(col >= -1 && col < 7)
return true;

return false;
}

void PlayerTurn(Player& current_player)
{
int col;

while(1) // loop until I say break
{
if(GetValidInput(current_player.name, col) )
break;
}

if(col == -1)
{
game_loop = false;
return;
}

// still need to know the row to CheckWin,
// so return it after setting it
int row = addCounter(current_player.counter, col);


drawGrid();

if(CheckWin(row, col) )
{
game_loop = false;
cout << current_player.name << " wins" << endl;
}
}

void InitializeGrid()
{
for(int col = 0; col < 7; ++col)
{
for(int row = 0; row < 7; ++row)
{
frame[col].rows[row] = ' ';
frame[col].count = 6; // this is where the init changes
}
}
}




Share this post


Link to post
Share on other sites

This topic is 3887 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this