Hello, OnlineGDB Q&A section lets you put your programming query to fellow community users. Asking a solution for whole assignment is strictly not allowed. You may ask for help where you are stuck. Try to add as much information as possible so that fellow users can know about your problem statement easily.

Why will my connect 4 game board not update?

0 votes
asked Mar 14, 2021 by Michael Fabian (190 points)
I have provided my code below. I used comments to try to help make sense of what is going on. Basically, in my function check_row, it doesn't seem like it is checking for whether or not there is a game piece (X for player one and O for player two) in the space it is supposed to look at. The function check_row should check if the column that the user inputed in the function get_int has any free row spaces for the game piece to go. Please let me know if you either can alter my current solution to make it work correctly or provide a new solution to that problem. Thank you for your help!

#include <iostream>
#include <cstdlib>
#include <cstring>
#include <string>

void get_int(int&, int&, int&);
void check_row(char**, int&, int&, int&, int&, int&);
void player_swap(int&);
void print_board(char **, int, int);
void initialize_board(char **, int, int);
void update_board(char**, int, int, int&, int&, int&);
void check_win();
void delete_board(char**, int);
void check_args(int, char**, int&, int&, int&);
void reprompt_args(int&, int&, int&);
void error();

using namespace std;

//I have been using "a.out 2 7 9" for the sake of simplicity when compiling
int main(int argc, char** argv) {
    int cols, rows, players, player = 1, col, row, play_again = 1;
    
    //checks the arguments for the game
    check_args(argc, argv, cols, rows, players);  

    //initializes connect 4 board
    char** board = new char*[rows];
    for (int i = 0; i < rows; i++)
        board[i] = new char[cols];
        
    //prints out initial board
        initialize_board(board, cols, rows);
    do {
        get_int(col, cols, player); //gets the column from the user
        check_row(board, col, row, cols, rows, player); //checks if row is occupied
        update_board(board, cols, rows, col, row, player); //updates game board and reprints it to user
        player_swap(player); //changes turns for the player
        
        //This is just a temporary way to loop the game
        cout << "Again? (1 - Yes) (0 - No)" << endl;
        cin >> play_again;
    }
    while(play_again == 1);
    delete_board(board, rows); //clears heap

    return 0;
}

void get_int(int& col, int& cols, int& player) {
    cout << "Player " << player << ", Enter a column: ";
    while((!(cin >> col)) || (col > cols)) {
        error();
    }
}

void check_row(char** board, int& col, int& row, int& cols, int& rows, int& player) {
    // this sets the rows of the board equal to the row being used in the for loop
    row = rows;
    
    //I was using these cout statements to check if variable "row" was being updated or not when applicable
    cout << "instance of row 1 = " << row << endl;
   
   
    for(int i = 0; i < rows; i++) {
        //The if and else if shoudl check if the given element in the array have a game piece
        if ((board[col-1][row-1] == 'X') && (row > 0)) {
            row--;
            cout << "instance of row 2 = " << row << endl;
        }
        else if ((board[col-1][row-1] == 'O') && (row > 0)) {
            row--;
            cout << "instance of row 3 = " << row << endl;
        }
        //The else statement is a reprompt if the column inputed by the user is full
        else {
            if (row == 0) {
                cout << "Invalid Row Try Again!" << endl;
                get_int(col, cols, player);
                row = rows;
            }
        }
    }
    cout << "instance of row 4 = " << row << endl;
}
    

//Swaps player after turn is over
void player_swap(int& player) {
    if (player == 1)
        player++;
    else
        player--;
}

void print_board(char ** board, int cols, int rows) {
    //prints out checkered board with contents of array board
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            if (i % 2 == 0 && j % 2 == 0)
                cout << "|\033[30;47m " << board[i][j] << " ";
            else if (i % 2 == 1 && j % 2 == 1)
                cout << "|\033[30;47m " << board[i][j] << " ";
            else
               cout << "|\033[0m " << board[i][j] << " ";
            cout << "\033[0m";
        }   
        cout << endl;
    }
}

void initialize_board(char ** board, int cols, int rows) {
    // provides column numbers for board
    for (int i = 0; i < cols; i++)
        cout << " " << (i + 1) << " ";
    cout << endl;

    //same code fragment for printing out the array onto a checkered board
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            if (i % 2 == 0 && j % 2 == 0)
                cout << "|\033[30;47m " << board[i][j] << " ";
            else if (i % 2 == 1 && j % 2 == 1)
                cout << "|\033[30;47m " << board[i][j] << " ";
            else
                cout << "|\033[0m " << board[i][j] << " ";
            cout << "\033[0m";
        }   
        cout << endl;
    }
    cout << endl;
}

void update_board(char** board, int cols, int rows, int& col, int& row, int& player) {
    // assigns the gamepiece to the appopriate row and col
    if (player == 1) {
        board[row-1][col-1] = 'X';
    }
    if (player == 2) {
        board[row-1][col-1] = 'O';
    }
    print_board(board, cols, rows);
}

//Deletes Board
void delete_board(char** board, int rows) {
    for(int i = 0; i < rows; i++)
        delete[] board[i];
    delete[] board;  
}

void check_args(int argc, char** argv, int& cols, int& rows, int& players) {
    int arg;
    
    //checks for commands that are not of integer type
    for(int i = 1; i < argc; i++) {
        if (atoi(argv[i]) == 0) {
            arg = 0;
        }       
    }
    //Checks argument paramets: argv[1] must be 2 or 1, argv[2]&argv[3] must be less that 20 and at least 2, and there must be 4 total arguments
    if ((atoi(argv[1]) > 2) || (atoi(argv[2]) > 20) || (atoi(argv[3]) > 20) || (atoi(argv[2]) < 2) || (atoi(argv[3]) < 2)|| (argc != 4))
        arg = 0;
    else
        arg = 1;
    
    if (arg == 1) {
        players = atoi(argv[1]);
        rows = atoi(argv[2]);
        cols = atoi(argv[3]);
    }
    else {
        reprompt_args(players, rows, cols);
    }
}
//Takes input for the command line arguments if they were invalid
void reprompt_args(int& players, int& rows, int& cols) {
    cout << "Invalid Command Line Arguments!" << endl << endl;
    cout << "Enter Players: ";
    while((!(cin >> players)) || (players > 2) || (players < 1)) {
        error();
    }
    cout << "Enter rows: ";
    while((!(cin >> rows)) || (rows > 20) || (rows < 2)) {
        error();
    }
    cout << "Enter cols: ";
    while((!(cin >> cols)) || (cols > 20) || (cols < 2)) {
        error();
    }
}

//General Invalid Input function
void error() {
    cout << "Error! Enter a valid input: " << endl;
    cin.clear();
    cin.ignore(1024, '\n');
}

2 Answers

0 votes
answered Mar 15, 2021 by Peter Minarik (84,720 points)
edited Mar 17, 2021 by Peter Minarik

If you add the following extra logging into the for loop in your check_row() method, you'll see what the code actually does:

printf("board[%d][%d]: [%c]\n", col - 1, row - 1, board[col - 1][row - 1]);

The output is:

board[0][6]: []
board[0][6]: []
board[0][6]: []
board[0][6]: []
board[0][6]: []
board[0][6]: []
board[0][6]: []

This is not what you want, right? You're checking the same column and row every time in the loop.

Please, review your logic in check_row().

Also, when no arguments are provided check_args() can cause segmentation fault due to trying to access memory that does not belong to you. The argv is of size 1, but you try to access the 2nd, 3rd and 4th elements, which are outside of the actual boundary of the array. You should first check argc and call your prompt_args() if it's not 4.

0 votes
answered Mar 17, 2021 by xDELLx (10,500 points)
edited Mar 17, 2021 by xDELLx

my ver of checkrow func .. please clean up unwanted code

void check_row(char** board, int& col, int& row, int& cols, int& rows, int& player) {
    // this sets the rows of the board equal to the row being used in the for loop
    row = -1;
    
    //I was using these cout statements to check if variable "row" was being updated or not when applicable
    cout << "instance of row 1 = " << row << endl;
   
   
    for(int i = 0; i < rows; i++) {
        //The if and else if shoudl check if the given element in the array have a game piece
        //if ((board[row-1][col-1] == 'X') && (row > 0)) {
        if (board[i][col-1] != 'X' && board[i][col-1]!='O' /*this i,j cell is empty, return it*/) {
            //row--;
            row=i;
            cout << "instance of row 2 = " << row << endl;
            break;
        }
#if 0 //just a fancy block comment to prevent compiler flagging nested comments as error
        else if ((board[i][col-1] == 'O') && (row > 0)) {
            //row--;
            row=i;
            cout << "instance of row 3 = " << row << endl;
            break;
        }
        //The else statement is a reprompt if the column inputed by the user is full
        else {
            if (i == rows -1 && row==0 /*row == 0*/ ) {
                cout << "Invalid Row Try Again!" << endl;
                get_int(col, cols, player);
                row = rows;
            }
        }
#endif
    }
    if (row == -1 ){
        //we tried all rows for empty cell ,but couldnt find
        //ask for new col from user ??
        cout << "No cell empty in col "<< col <<" Invalid Row Try Again!" << endl;
        //get_int(col, cols, player);
        // i clld have called chk row recursively from here ... but will let calling func handle faILURE ..
        //row = rows;
        
    }
    cout << "instance of row 4 = " << row << endl;
}
   

================================

and added minor changes to main function, code below:

    //prints out initial board
        initialize_board(board, cols, rows);
    do {
        get_int(col, cols, player); //gets the column from the user
        check_row(board, col, row, cols, rows, player); //checks if row is occupied
        if (row != -1 ) {
            update_board(board, cols, rows, col, row, player); //updates game board and reprints it to user
            player_swap(player); //changes turns for the player
        }    
        //This is just a temporary way to loop the game
        cout << "Again? (1 - Yes) (0 - No)" << endl;
        cin >> play_again;
    }
    while(play_again == 1);



============================================

Please patch these changes.. hopefully they ll work !!

My output below

 ./a.out 2 3 2
 1  2 
|  |  
|  |  
|  |  

Player 1, Enter a column: 1
| X |  
|  |  
|  |  
Again? (1 - Yes) (0 - No)
1
Player 2, Enter a column: 1
| X |  
| O |  
|  |  
Again? (1 - Yes) (0 - No)
1
Player 1, Enter a column: 1
| X |  
| O |  
| X |  
Again? (1 - Yes) (0 - No)
1
Player 2, Enter a column: 1
No cell empty in col 1 Invalid Row Try Again!
Again? (1 - Yes) (0 - No)
1
Player 2, Enter a column: 2
| X | O 
| O |  
| X |  
Again? (1 - Yes) (0 - No)
1
Player 1, Enter a column: 2
| X | O 
| O | X 
| X |  
Again? (1 - Yes) (0 - No)
1
Player 2, Enter a column: 2
| X | O 
| O | X 
| X | O 
Again? (1 - Yes) (0 - No)
1
Player 1, Enter a column: 2
instance of row 1 = -1
No cell empty in col 2 Invalid Row Try Again!
instance of row 4 = -1
Again? (1 - Yes) (0 - No)
0
shri@pop-os:/media/shri/g/shri/progs$ 

Welcome to OnlineGDB Q&A, where you can ask questions related to programming and OnlineGDB IDE and and receive answers from other members of the community.
...