# 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.

## 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<<endl;
cin >> player1.name;
cout<<endl;

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<<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<<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 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 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 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 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 on other sites
Hey thanks, yeh I done bit of background reading on c++ I do have a book on c++ but its not the best (might buy a new 1)

Thanks for the code :) What I'll do is use your code and then do as you suggested.

Anyway thanks again mate :)

##### 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 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 on other sites
Quote:
 Original post by Cool_Program_Guyif (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 on other sites
Hmm,still doest wrk, does it need a pointer?

Error 1 error C2065: 'last_move_row' : undeclared identifier
Error 2 error C2065: 'last_move_col' : undeclared identifier

  const int last_move_row, const int last_move_col) addCounter(player2.counter,choice);

##### 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 functionbool 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 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 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 framevoid 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 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 on other sites
Hey, thanks yeh i reading that up now :) I'm going put a bubble sort to so that the to player ranks out to :D So far the game works fab n thanks again for all the help :)I'll try making an AI I think i know how i would do it :)

##### 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 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 onint 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 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.

## Create an account

Register a new account

• ### Forum Statistics

• Total Topics
628647
• Total Posts
2984035

• 10
• 9
• 9
• 10
• 21