如何修复C ++ Connect4 checkGameOver函数

时间:2018-11-26 23:48:07

标签: c++ g++

我们必须使用C ++编写Connect4,这是我上一堂课的最后一个作业。截至目前,我有一个功能正常的版本。对于检查游戏是否结束的递归解决方案,我有一个非常有前途的想法,但问题是它仅从开始时的令牌开始进行检查。例如,如果最近放置的令牌在任何有效方向上具有3个相同种类的令牌,则它起作用,但在一个方向上具有2个,而在相反方向上具有1个令牌,则在相反方向上不起作用。是否有人对我如何编辑函数以合并第二种情况有任何想法,或者我应该只是将其废弃并以不同的方式重写。这是我的代码。

//TODO implement checking spots other than start

#include <iostream>
#include <string>
#include <ctime>
#include <cstdlib>

int runGame(int, int, int);
void promptUser(int**, int[], int, int, int);
void genRanMove(int**, int[], int, int);
bool checkGameOver(int**, int, int, int, int, int, int, int, int);
void showBoard(int**, int, int);
void playAgain();

using namespace std;

int main(int argc, char *argv[]) {

    if (argc < 4) {
        cerr << "Not enough input, syntax is 1/2 players, # columns, and # of rows" << endl;
        exit(0);
    }

    if (argc == 4 && (argv[1][0] == '1' || argv[1][0] == '2') && (argv[2][0] >= '1' && argv[2][0] <= '9') && (argv[3][0] >= '1' && argv[3][0] <= '9')) {
        cout << "\n\nYou have chosen to play Connect 4 in " << argv[1][0] << " player mode and with " << argv[2][0] << " columns and with " << argv[3][0] << " rows.\n" << endl;
    }
    else {
        cerr << "Improper syntax, syntax is 1/2 players, # columns, and # of rows" << endl;
        exit(0);
    }

    cout << "Welcome to Connect 4, each player will take a turn choosing a column to select, a metaphorical coin will fall down to the highest availible row.\nThe game ends when a player has gotten 4 of their coins in a row horizontally, vertically, or diagonally.\nPlayer 1 will always use 1's and Player 2 will always be 2's.\nThe AI will always be 3's.\nEmpty slots will be shown as 0's.\n\n" << endl;

    srand(time(NULL));
    int winner = runGame(argv[1][0] - '0', argv[2][0] - '0', argv[3][0] - '0');

    if (winner == 1 || winner == 2) {
        cout << "Winner was player # " << winner << endl;
    }
    else if (winner == 3)
        cout << "You let the AI win :(" << endl;
    else if (winner == -1)
        cout << "The game ended in a tie" << endl;

    playAgain();

}

/******************************************************
** Function: runGame
** Description: runsGame
** Parameters: gameMode columns rows
** Pre-Conditions:
** Post-Conditions: returns winning player, 1 or 2 for player # and 3 for ai
******************************************************/
int runGame(int gameMode, int columns, int rows) { //returns which player won
    int holdPoints[2] = { 0, 0 };
    switch (gameMode) {
    case 1:
        cout << "You have chosen to play against an AI" << endl;
        break;
    case 2:
        cout << "You have chosen to play with another human player" << endl;
        break;
    }
    int** board = new int*[columns];
    for (int i = 0; i< columns; i++) {
        board[i] = new int[rows];
    }

    for (int i = 0; i< columns; i++) {
        for (int j = 0; j< rows; j++) {
            board[i][j] = 0;
        }
    }

    for (int i = 0; i< rows*columns/2; i++) { //check for number of possible moves in order to have a tie
        showBoard(board, rows, columns);
        promptUser(board, holdPoints, rows, columns, 1);
        if (checkGameOver(board, holdPoints[0], holdPoints[1], 0, 1, 0, 1, rows, columns) || checkGameOver(board, holdPoints[0], holdPoints[1], 1, 1, 0, 1, rows, columns) || checkGameOver(board, holdPoints[0], holdPoints[1], 1, 0, 0, 1, rows, columns) || checkGameOver(board, holdPoints[0], holdPoints[1], 0, -1, 0, 1, rows, columns) || checkGameOver(board, holdPoints[0], holdPoints[1], -1, -1, 0, 1, rows, columns) || checkGameOver(board, holdPoints[0], holdPoints[1], -1, 0, 0, 1, rows, columns) || checkGameOver(board, holdPoints[0], holdPoints[1], 1, -1, 0, 1, rows, columns) || checkGameOver(board, holdPoints[0], holdPoints[1], -1, 1, 0, 1, rows, columns)) {
            return 1;
        }
        cout << "\n\n";
        showBoard(board, rows, columns);
        if (gameMode == 2) {
            promptUser(board, holdPoints, rows, columns, 2);
            if (checkGameOver(board, holdPoints[0], holdPoints[1], 0, 1, 0, 2, rows, columns) || checkGameOver(board, holdPoints[0], holdPoints[1], 1, 1, 0, 2, rows, columns) || checkGameOver(board, holdPoints[0], holdPoints[1], 1, 0, 0, 2, rows, columns) || checkGameOver(board, holdPoints[0], holdPoints[1], 0, -1, 0, 2, rows, columns) || checkGameOver(board, holdPoints[0], holdPoints[1], -1, -1, 0, 2, rows, columns) || checkGameOver(board, holdPoints[0], holdPoints[1], -1, 0, 0, 2, rows, columns) || checkGameOver(board, holdPoints[0], holdPoints[1], 1, -1, 0, 2, rows, columns) || checkGameOver(board, holdPoints[0], holdPoints[1], -1, 1, 0, 2, rows, columns)) {
                return 2;
            }
        }
        else if (gameMode == 1) {
            genRanMove(board, holdPoints, rows, columns);
            if (checkGameOver(board, holdPoints[0], holdPoints[1], 0, 1, 0, 3, rows, columns) || checkGameOver(board, holdPoints[0], holdPoints[1], 1, 1, 0, 3, rows, columns) || checkGameOver(board, holdPoints[0], holdPoints[1], 1, 0, 0, 3, rows, columns) || checkGameOver(board, holdPoints[0], holdPoints[1], 0, -1, 0, 3, rows, columns) || checkGameOver(board, holdPoints[0], holdPoints[1], -1, -1, 0, 3, rows, columns) || checkGameOver(board, holdPoints[0], holdPoints[1], -1, 0, 0, 3, rows, columns) || checkGameOver(board, holdPoints[0], holdPoints[1], 1, -1, 0, 3, rows, columns) || checkGameOver(board, holdPoints[0], holdPoints[1], -1, 1, 0, 3, rows, columns)) {
                return 3;
            }
        }

        //showBoard(board, rows, columns);

        cout << "\n\n";
    }

    for (int i = 0; i < columns; i++) {
        delete[] board[i];
    }
    delete[] board;

    return -1; //signifies a tie
}


/******************************************************
** Function: promptUser
** Description: gets input from user for their turn, stores the resting place of their token in points
** Parameters: board and array to hold point,num columns
** Pre-Conditions:
** Post-Conditions:
******************************************************/
void promptUser(int** board, int point[], int numRows, int numColumns, int playerNum) {
    int col, lowZero;
    lowZero = -1;
    cout << "What column would you like to choose" << endl;
    cin >> col;
    if (col<0 || col > numColumns -1) {
        cout << "The # you chose must be >=0 and <= " << numColumns-1 << endl;
        promptUser(board, point, numRows, numColumns, playerNum);
    }
    //now need to find the lowest possible row in the given column that has a 0
    for (int i = 0; i < numRows; i++) {
        if (board[col][i] == 0)
            lowZero = i;
    }

    if (lowZero != -1) {
        board[col][lowZero] = playerNum;
        cout << "Player " << playerNum << " dropped a token in column " << col << " that rested at row " << lowZero << endl;
        point[0] = col;
        point[1] = lowZero;
        return;
    }

    else {
        cout << "Column " << col << " was full, please pick a new one" << endl;
        promptUser(board, point, numRows, numColumns, playerNum);
    }

}

/******************************************************
** Function: genRanMove
** Description: gets random column from "ai", stores the resting place of their token in points
** Parameters: board and array to hold point
** Pre-Conditions:
** Post-Conditions:
******************************************************/
void genRanMove(int** board, int point[], int numRows, int numColumns) {
    cout << "AI is finding a column" << endl;
    int col = rand() % numColumns;
    int lowZero = -1;

    for (int i = 0; i < numRows; i++) {
        if (board[col][i] == 0)
            lowZero = i;
    }

    if (lowZero != -1) {
        board[col][lowZero] = 3;
        cout << "AI dropped a token in column " << col << " that rested at row " << lowZero << endl;
        point[0] = col;
        point[1] = lowZero;
        return;
    }

    //didnt find a row that works
    genRanMove(board, point, numRows, numColumns);

}

/******************************************************
** Function: showBoard
** Description: prints he current gameboard
** Parameters: the board, numrows and numcolumns
** Pre-Conditions:
** Post-Conditions:
******************************************************/
void showBoard(int** board, int numRows, int numColumns) {
    for (int i = 0; i < numColumns; i++) {
        for (int j = 0; j < numRows; j++) {
            cout << board[j][i] << " ";
        }
        cout << endl;
    }
}

/******************************************************
** Function: playAgain
** Description: gets info to play again
** Parameters: n/a
** Pre-Conditions:
** Post-Conditions:
******************************************************/
void playAgain() {
    int again;
    int mode, rows, columns;
    cout << "Would you like to play again? Type 1 if so." << endl;
    cin >> again;
    if (again) {
        cout << "What mode? 1 for 1 player and 2 for 2 player" << endl;
        cin >> mode;
        cout << "How many rows?" << endl;
        cin >> rows;
        cout << "How many columns?" << endl;
        cin >> columns;

        if ((mode<=0 || mode >2) || rows<= 0 || columns<= 0) {
            cout << "Improper inputs. Mode must be 1/2 and rows and columns must both be >0" << endl;
            playAgain();
        }
        else {
            runGame(mode, columns, rows);
        }

    }
}

/******************************************************
** Function: checkGameOver
** Description: checks if game is over by recursively checking spots around original
** Parameters: board, curr col, curr row, change col, change row, total found, and what number it is looking for in the board
** Pre-Conditions:
** Post-Conditions:
******************************************************/
bool checkGameOver(int** board, int currCol, int currRow, int changeCol, int changeRow, int totalFound, int numSearch, int numRows, int numColumns) {
    if (totalFound == 4) //base case
        return true;
    if (currRow <0 || currRow >numRows - 1 || currCol<0 || currCol> numColumns - 1)
        return false;

    if (board[currCol][currRow] == numSearch)
        totalFound++;


    else
        return false; // the checked index didnt contain the same # we wanted

    return checkGameOver(board, currCol + changeCol, currRow + changeRow, changeCol, changeRow, totalFound, numSearch, numRows, numColumns);
}

1 个答案:

答案 0 :(得分:0)

  

...还是我应该将其报废并以不同的方式重写?

那是我要使用的选项:-)

尽管递归解决方案可能很聪明,但对于这种规模的游戏空间,您可能会更好,让代码保持简单,只检查每种可能性,而不是尝试从新填充的单元中递归< sup>(a)。

您可以按“专家”组块进行操作,因此您不必担心在任何时候都超出板的边缘。为此,您需要限制每种类型的起始单元格,以使其在板的边缘单元格上结束,例如(伪代码):

# Check four cells given start and direction.

def didWin(row, col, rdelta, cdelta):
  # Check if start cell not populated.

  if cell[row][col] == empty:
    return false

  # Check if any cell in sequence doesn't match.

  for i = 1 to 3 inclusive:
    if cell[row][col] != cell[row+rdelta*i][col+cdelta*i]:
      return false

  # Otherwise, it's a win

  return true

def getWinner():
  # Check all possible horizontals.

  for row = 0 to 5 inclusive:
    for col = 0 to 3 inclusive:
      if didWin(row, col, 0, 1): # cdelta=1, right.
        return cell[row][col]

  # Check all possible verticals.

  for col = 0 to 6 inclusive:
    for row = 0 to 2 inclusive:
      if didWin(row, col, 1, 0): # rdelta=1, down.
        return cell[row][col]

  # Check all right-downs.

  for row = 0 to 2 inclusive:
    for col = 0 to 3 inclusive:
      if didWin(row, col, 1, 1): # r/cdelta=1, right-down.
        return cell[row][col]

  # Check all right-ups.

  for row = 3 to 5 inclusive:
    for col = 0 to 3 inclusive:
      if didWin(row, col, 1, -1): # rdelta=1, cdelta=-1, right-up.
        return cell[row][col]

  # No winner.

  return empty

(a) 另一个这样做的一个很好的理由是,在一个教育机构中,他们几乎总是重视思想的清晰性而不是聪明。这种清晰的想法将在您的职业生涯开始,甚至在您进入职业生涯四十年的时候(例如我所在的位置:-)