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