Jump to content
  • Advertisement
Sign in to follow this  
eektor

Connect 4 - please critique

This topic is 4810 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

Here's the source for my connect 4, but first I have a question What other game projects could I work on to practice my C++? (I'm still waiting for my Accelerated C++ book but I think after I finish with that book, I'll probably get into a graphic API) I know the source is long but I figure its still short enough to not download it. Well please critique away.
// Connect 4
// The basic game where you try to get 4 pieces in a row

#include <iostream>
#include <string>
#include <sstream>

using namespace std;

// global constants
const char X = 'X';
const char O = 'O';
const char EMPTY = ' ';
const char NO_ONE = 'N';
const char TIE = 'T';
const int NUM_ROWS = 6;
const int NUM_COLUMNS = 7;

// Board class
class Board
{
private:
        char storage[NUM_ROWS][NUM_COLUMNS];
public:
       Board();
       char show(int row, int column){return storage[row][column];};
       void place(int column, char what);
       void place(int row, int column, char what){storage[row][column] = what;};
       bool isLegal(int column) const;
       void display()const;
};

Board::Board()
{
       for(int i = 0; i < NUM_ROWS; i++)
               for(int j = 0; j < NUM_COLUMNS; j++)
                       storage[j] = EMPTY;
}

void Board::place(int column, char what)
{
     for (int row=0; row < NUM_ROWS; row++)
     {
         if (storage[row][column] == EMPTY)
         {
            storage[row][column] = what;
            break;
         }
     }
}

bool Board::isLegal(int column) const
{
     for (int row = 0; row < NUM_ROWS; row++)
     {
         if (storage[row][column] == EMPTY)
            return true;
     }
     return false;
}

void Board::display() const
{
     for (int i = NUM_ROWS; i > 0; i--)
     {
         cout << "\n\t|";
         for (int j = 0; j < NUM_COLUMNS; j++)
             cout << storage[i-1][j] << "|";
     }
     cout << "\n\t---------------\n\n";
}

// function prototypes
void Instructions();
char AskYesNo(string question);
int AskNumber(string question, int high, int low = 1);
char HumanPiece();
char SwitchPiece(char piece);
char Winner(Board& board, int move, char current_player);
int HumanMove(Board& board, char player);
int ComputerMove(Board board, char computer);
void AnnounceWinner(char winner, char player, char computer);
void PlayGame();

// main function
int main()
{
    // main game loop
    int choice;
    do
    {
        system("cls");
        cout << "\n\n\tWelcome to Connect 4!\n\n";
        cout << "1 - Instructions\n";
        cout << "2 - New Game\n";
        cout << "3 - Quit\n";
        cin >> choice;
        
        switch(choice)
        {
        case 1:  Instructions();
                 break;
        case 2:  PlayGame();
                 break;
        case 3:  cout << "Thanks for playing.\n";
                 break;
        default:  cout << "That is not a correct option.\n";
        }
        
    }while(choice != 3);
    
    system("PAUSE");
    
    return 0;
}
      
// function definitions
void Instructions()
{
     system("cls");
     cout << "This is a computer game of the classic game Connect 4.\n\n";
     cout << "In the game you need to get 4 pieces in a row,\n";
     cout << "either diagonally, horizontally, or vertically.\n"; 
     cout << "You can only pick what column to put your piece in.\n\n";
     cout << "After you pick the column, your piece will fall\n";
     cout << "all the way to the bottom of the column or\n";
     cout << "will fall on top of a piece in the column.\n\n";
     cout << "If nobody wins when the board is filled the game ends in a tie.\n\n";
     cout << "The columns are numbered like this:\n\n";
     cout << "\t| | | | | | | |\n";
     cout << "\t| | | | | | | |\n";
     cout << "\t| | | | | | | |\n";
     cout << "\t| | | | | | | |\n";
     cout << "\t| | | | | | | |\n";
     cout << "\t| | | | | | | |\n";
     cout << "\t---------------\n";
     cout << "\t 1 2 3 4 5 6 7\n\n";
     system("PAUSE");
}  

char AskYesNo(string question)
{
     char response;
     do
     {
          cout << question << " (y/n): ";
          cin >> response;
          cin.ignore();
     }while (response != 'y' && response != 'n');
     
     return response;
}

int AskNumber(string question, int high, int low)
{
    int number;
    do
    {
        cout << question << " (" << low << " - " << high << "): ";
        stringstream ss;
        string input;
        getline(cin,input);
        if (!(stringstream(input) >> number))
           continue;
    }while (number > high || number < low);
    
    return number;
}

char HumanPiece()
{
     char go_first = AskYesNo("Do you want to move first?");
     if (go_first == 'y')
     {
        cout << "\nThen take the first move.\n";
        return X;
     }
     else
     {
         cout << "\nAlright, I will go first.\n";
         return O;
     }
}

char SwitchPiece(char piece)
{
     if (piece == X)
        return O;
     else
         return X;
}

char Winner(Board& board, int column, char current_player)
{
     if (column == 8)
        return NO_ONE;
        
     current_player = SwitchPiece(current_player);
        
     // determine which row the last move was in
     int row = 5;
     while (board.show(row, column) == EMPTY)
           row--;
     
     // check for a win horizontally
     int piecesInRow = 1;
     if ((board.show(row, column-1) == current_player) && ((column-1)>= 0))
     {
           piecesInRow++;
           if ((board.show(row, column-2) == current_player) && ((column-2)>= 0))
           {
               piecesInRow++;
               if ((board.show(row, column-3) == current_player) && ((column-3)>= 0))
                  piecesInRow++;
           }
     }
     if ((board.show(row, column+1) == current_player) && ((column+1)< NUM_COLUMNS))
     {
           piecesInRow++;
           if ((board.show(row, column+2) == current_player) && ((column+2)< NUM_COLUMNS))
           {
               piecesInRow++;
               if ((board.show(row, column+3) == current_player) && ((column+3)< NUM_COLUMNS))
                  piecesInRow++;
           }
     }
     if (piecesInRow > 3)
        return current_player;
        
     // check for a win vertically
     piecesInRow = 1;
     if ((board.show(row-1, column) == current_player) && ((row-1)>= 0))
     {
           piecesInRow++;
           if ((board.show(row-2, column) == current_player) && ((row-2) >= 0))
           {
               piecesInRow++;
               if ((board.show(row-3, column) == current_player) && ((row-3) >= 0))
                  piecesInRow++;
           }
     }
     if ((board.show(row+1, column) == current_player) && ((row+1)< NUM_ROWS))
     {
           piecesInRow++;
           if ((board.show(row+2, column) == current_player) && ((row+2) < NUM_ROWS))
           {
               piecesInRow++;
               if ((board.show(row+3, column) == current_player) && ((row+3) < NUM_ROWS))
                  piecesInRow++;
           }
     }
     if (piecesInRow > 3)
        return current_player;
        
     // check for a win diagonally - slope positive
     piecesInRow = 1;
     if ((board.show(row-1, column-1) == current_player)&&((row-1) >= 0)
        && ((column-1) >= 0))
     {
           piecesInRow++;
           if ((board.show(row-2, column-2) == current_player)&&((row-2) >= 0)
              && ((column-2) >= 0))
           {
               piecesInRow++;
               if ((board.show(row-3, column-3) == current_player)
                  &&((row-3) >= 0) && ((column-3) >= 0))
                  piecesInRow++;
           }
     }
     if ((board.show(row+1, column+1) == current_player)&&((row+1) < NUM_ROWS)
        && ((column+1) < NUM_COLUMNS))
     {
           piecesInRow++;
           if ((board.show(row+2, column+2) == current_player)&&((row+2) < NUM_ROWS)
              && ((column+2) < NUM_COLUMNS))
           {
               piecesInRow++;
               if ((board.show(row+3, column+3) == current_player)
                  &&((row+3) < NUM_ROWS) && ((column+3) < NUM_COLUMNS))
                  piecesInRow++;
           }
     }
     if (piecesInRow > 3)
        return current_player;
        
     // check for a win diagonally - slope negative
     piecesInRow = 1;
     if ((board.show(row-1, column+1) == current_player)&&((row-1) >= 0)
        && ((column+1) < NUM_COLUMNS))
     {
           piecesInRow++;
           if ((board.show(row-2, column+2) == current_player)&&((row-2) >= 0)
              && ((column+2) < NUM_COLUMNS))
           {
               piecesInRow++;
               if ((board.show(row-3, column+3) == current_player)
                  &&((row-3) >= 0) && ((column+3) < NUM_COLUMNS))
                  piecesInRow++;
           }
     }
     if ((board.show(row+1, column-1) == current_player)&&((row+1) < NUM_ROWS)
        && ((column-1) >= 0))
     {
           piecesInRow++;
           if ((board.show(row+2, column-2) == current_player)&&((row+2) < NUM_ROWS)
              && ((column-2) >= 0))
           {
               piecesInRow++;
               if ((board.show(row+3, column-3) == current_player)
                  &&((row+3) < NUM_ROWS) && ((column-3) >= 0))
                  piecesInRow++;
           }
     }
     if (piecesInRow > 3)
        return current_player;
        
     // check for tie
     row = 0;
     column = 0;
     for (row = 0; row < NUM_ROWS; row++)
         for (column = 0; column < NUM_COLUMNS; column++)
         {
             if (board.show(row, column) == EMPTY)
                return NO_ONE;
         }
     
     return TIE;
}

int HumanMove(Board& board, char player)
{
    int move = AskNumber("Where will you move?", (NUM_COLUMNS));
    while(!(board.isLegal(move-1)))
    {
         cout << "\nSorry, you cannot move there.\n";
         move = AskNumber("Where will you move?", (NUM_COLUMNS));
    }
    cout << "Ok.\n";
    return move-1;
}

int ComputerMove(Board board, char computer)
{
    cout << "I will place my piece in column ";
    
    // if coumputer can win on next move, make that move
    for(int column = 0; column < NUM_COLUMNS; column++)
    {
            if (board.isLegal(column))
            {
                  board.place(column, computer);
                  if (Winner(board,column, SwitchPiece(computer)) == computer)
                  {
                        cout << column + 1 << endl;
                        return column;
                  }
                  // done checking this move, undo it
                  int row = NUM_ROWS-1;
                  while (board.show(row,column) == EMPTY)
                        row--;
                  board.place(row,column,EMPTY);                        
            }
    }
    
    // if human can win on next move, block that move
    char human = SwitchPiece(computer);
    for(int column = 0; column < NUM_COLUMNS; column++)
    {
            if(board.isLegal(column))
            {
                  board.place(column, human);
                  if (Winner(board,column, SwitchPiece(human)) == human)
                  {
                       cout << column + 1 << endl;
                       return column;
                  }
                  // done checking this move, undo it
                  int row = NUM_ROWS-1;
                  while (board.show(row,column) == EMPTY)
                        row--;
                  board.place(row,column,EMPTY);
            }
    }
    // just for testing purposes i'm going to make the column fixed
    // i'll make the column pick random - the AI needs some work here
    if (board.isLegal(3)){  cout << 4 << endl; return 3;}
    if (board.isLegal(2)){  cout << 3 << endl; return 2;}
    if (board.isLegal(4)){  cout << 5 << endl; return 4;}
    if (board.isLegal(1)){  cout << 2 << endl; return 1;}
    if (board.isLegal(5)){  cout << 6 << endl; return 5;}
    if (board.isLegal(0)){  cout << 1 << endl; return 0;}
    if (board.isLegal(6)){  cout << 7 << endl; return 6;}
}

void AnnounceWinner(char winner, char computer, char human)
{
     if (winner == computer)
     {
                cout << winner << "'s won!\n";
                cout << "Good game.  Practice some more and then play me again\n";
     }
     
     else if (winner == human)
     {
          cout << winner << "'s won!\n";
          cout << "Wow!  You got lucky.  Let's have a rematch soon.\n";
     }
     
     else
     {
         cout << "It's a tie.  Let's play again!\n";
     }
}

void PlayGame()
{
     Board board;
     int move = 8;
     char human = HumanPiece();
     char computer = SwitchPiece(human);
     char current_player = X;
     board.display();
    
     while (Winner(board, move, current_player) == NO_ONE)
     {
          if (current_player == human)
          {
             move = HumanMove(board, human);
             board.place(move, human);
          }
          else
          {
              move = ComputerMove(board, computer);
              board.place(move, computer);
          }
          board.display();
          current_player = SwitchPiece(current_player);
     }
    
     AnnounceWinner(Winner(board, move, current_player), computer, human);
    
     system("PAUSE");
}
So if you noticed my big problem with the game is with the AI. I was thinking of making the computer pick randomly but I don't think that's such a good idea. Any ideas on how to make it better? Some suggestions would be great. Also if there is a way to make the code smaller or more readable please tell me.

Share this post


Link to post
Share on other sites
Advertisement
I must say you have a really nice coding style! Well done! [grin]
To make your program better, try to simplify the Winner routine.

For the next project, what about a text adventure or a Snake game?

Share this post


Link to post
Share on other sites
Thanks Konfusius. I really don't know how to simplify the Winner function. I could probably split it up into different functions but not sure how I can simplify the process.

I was thinking of doing a snake clone when I start getting into the graphic API's. The text adventure might be the way to go. Thanks for the suggestions.

Share this post


Link to post
Share on other sites
your AI seems like its about as good as it needs to be. you have it do a block check and a win check.. which is all you can really do unless you want to go to advacned stuff like minmax trees... I mean it should try to win or try to block and thats all really...

And on the AI picking randomly.. it would be a good idea for its first move... then after its first move try and move in any direction it can and if block in all of those directions move on to a new random starting spot.. or even have it check to see if any of the current pieces have four open then it moves there... but that might be a lil much... but defitnaly make it do random on first move or on MoveIsBlocked type of thing... if you want to talk about it a lil more you can send me a PM... thanks for your time.

and it looks good btw..

Share this post


Link to post
Share on other sites
Thanks RanBlade. I'll work on implementing the random move at the beginning. Just wondering is it hard to have the computer think about two moves in advance?
Maybe I should buy a beginners AI book.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!