1. 游戏介绍
一个简单的 C++ 五子棋小游戏
1.1 游戏规则:
双人轮流输入下入点坐标横竖撇捺先成五子连线者胜同一坐标点不允许重复输入1.2 初始化与游戏界面
初始化界面
X 输入坐标后
O 输入坐标后
X 先达到胜出条件
2. 源代码
#include <iostream>#include <vector>#include <algorithm>#include <limits>using namespace std;const int BOARD_SIZE = 15;char board[BOARD_SIZE][BOARD_SIZE];void initBoard() { for (int i = 0; i < BOARD_SIZE; ++i) { for (int j = 0; j < BOARD_SIZE; ++j) { board[i][j] = '.'; } }}void printBoard() { for (int i = 0; i < BOARD_SIZE; ++i) { for (int j = 0; j < BOARD_SIZE; ++j) { cout << board[i][j] << " "; } cout << endl; }}bool isBoardFull() { for (int i = 0; i < BOARD_SIZE; ++i) { for (int j = 0; j < BOARD_SIZE; ++j) { if (board[i][j] == '.') { return false; } } } return true;}bool checkWin(int x, int y, char player) { int count; // 横向检查 count = 0; for (int i = max(0, x - 4); i <= x; ++i) { if (board[y][i] == player) { count++; } else { count = 0; } } for (int i = x + 1; i < min(BOARD_SIZE, x + 5); ++i) { if (board[y][i] == player) { count++; } else { break; } } if (count >= 5) return true; // 纵向检查 count = 0; for (int i = max(0, y - 4); i <= y; ++i) { if (board[i][x] == player) { count++; } else { count = 0; } } for (int i = y + 1; i < min(BOARD_SIZE, y + 5); ++i) { if (board[i][x] == player) { count++; } else { break; } } if (count >= 5) return true; // 斜向(从左上到右下)检查 count = 0; for (int i = max(-4, -x); i <= 0; ++i) { if (x + i >= 0 && x + i < BOARD_SIZE && y + i >= 0 && y + i < BOARD_SIZE && board[y + i][x + i] == player) { count++; } else { count = 0; } } for (int i = 1; i <= min(4, BOARD_SIZE - 1 - x); ++i) { if (x + i >= 0 && x + i < BOARD_SIZE && y + i >= 0 && y + i < BOARD_SIZE && board[y + i][x + i] == player) { count++; } else { break; } } if (count >= 5) return true; // 斜向(从右上到左下)检查 count = 0; for (int i = max(-4, -x); i <= 0; ++i) { if (x + i >= 0 && x + i < BOARD_SIZE && y - i >= 0 && y - i < BOARD_SIZE && board[y - i][x + i] == player) { count++; } else { count = 0; } } for (int i = 1; i <= min(4, BOARD_SIZE - 1 - x); ++i) { if (x + i >= 0 && x + i < BOARD_SIZE && y - i >= 0 && y - i < BOARD_SIZE && board[y - i][x + i] == player) { count++; } else { break; } } if (count >= 5) return true; return false;}int main() { initBoard(); bool isPlayerX = true; bool gameOver = false; while (!gameOver) { printBoard(); int x, y; cout << (isPlayerX ? "Player X" : "Player O") << ", enter your move (row column): "; cin >> y >> x; if (x < 0 || x >= BOARD_SIZE || y < 0 || y >= BOARD_SIZE || board[y][x] != '.') { cout << "Invalid move. Try again." << endl; continue; } board[y][x] = isPlayerX ? 'X' : 'O'; if (checkWin(x, y, board[y][x])) { printBoard(); cout << (isPlayerX ? "Player X wins!" : "Player O wins!") << endl; gameOver = true; } else if (isBoardFull()) { printBoard(); cout << "It's a draw!" << endl; gameOver = true; } else { isPlayerX = !isPlayerX; } } return 0;}
3. 主要代码释解
这段代码是一个简单的五子棋游戏的实现,下面是对主要函数和异常错误处理机制的详解:
initBoard()
功能:初始化棋盘,将所有位置设置为’.',表示空白。实现:通过双重循环遍历二维数组board
,并将每个元素设置为’.'。 printBoard()
功能:打印当前棋盘的状态。实现:通过双重循环遍历二维数组board
,并打印每个元素。 isBoardFull()
功能:检查棋盘是否已满。实现:通过双重循环遍历二维数组board
,如果所有位置都不是’.',则返回true
,表示棋盘已满。 checkWin(int x, int y, char player)
功能:检查给定玩家是否在(x, y)位置获胜。实现:检查横向、纵向、两个对角线方向是否有连续的 5 个相同的棋子。如果找到,则返回true
,表示该玩家获胜。 main()
功能:游戏的主循环,处理玩家的输入,更新棋盘状态,并判断游戏是否结束。实现: 初始化棋盘。在一个循环中交替让两个玩家输入他们的移动。检查移动是否有效(即在棋盘范围内且位置为空)。更新棋盘,并检查是否有玩家获胜或棋盘已满。 4. 异常和错误处理机制
输入有效性检查 在main()
函数中,玩家输入移动后,代码检查移动是否在棋盘范围内,以及对应位置是否为空。如果移动无效(即x
或y
超出范围,或者对应位置不是’.'),则打印错误消息,并通过continue
跳过当前循环的剩余部分,提示玩家重新输入。 棋盘满时结束游戏 在玩家每次移动后,调用isBoardFull()
检查棋盘是否已满。如果棋盘已满,则打印平局消息,并通过设置gameOver
为true
结束游戏。 检查获胜条件 在玩家每次移动后,调用checkWin()
检查该玩家是否获胜。如果玩家获胜,则打印获胜消息,并通过设置gameOver
为true
结束游戏。 5. 可改进点
异常处理:代码中没有使用 C++ 的异常处理机制,例如try-catch
块。在某些情况下,如果输入不是整数,cin
会进入错误状态,这可能导致无限循环。可以通过检查cin
的状态并清除错误标志来处理这种情况。边界条件检查:在checkWin()
函数中,对斜向检查的边界条件处理可以进一步优化,以避免不必要的条件判断。代码重用:checkWin()
函数中的横向、纵向和斜向检查有大量重复代码,可以通过提取重复代码到单独的函数中来简化。 这个游戏实现简单,但包含了基本的游戏逻辑和错误处理机制,适合作为学习C++
和游戏编程的入门项目。
6. 追更
6.1 优化了判胜代码
// 检查给定玩家是否在(x, y)位置沿一个方向获胜bool checkDirection(int x, int y, int dx, int dy, char player) { int count = 0; for (int i = 0; i < 5; ++i) { int checkX = x + i * dx; int checkY = y + i * dy; if (checkX >= 0 && checkX < BOARD_SIZE && checkY >= 0 && checkY < BOARD_SIZE && board[checkY][checkX] == player) { count++; } else { break; } } return count == 5;}// 检查给定玩家是否在(x, y)位置获胜bool checkWin(int x, int y, char player) { // 检查水平方向 if (checkDirection(x, y, 1, 0, player) || checkDirection(x, y, -1, 0, player)) { return true; } // 检查垂直方向 if (checkDirection(x, y, 0, 1, player) || checkDirection(x, y, 0, -1, player)) { return true; } // 检查两个斜线方向 if (checkDirection(x, y, 1, 1, player) || checkDirection(x, y, -1, -1, player)) { return true; } if (checkDirection(x, y, 1, -1, player) || checkDirection(x, y, -1, 1, player)) { return true; } return false;}
6.2 完整代码
优化了判胜代码并增加了try-catch
块捕获异常,仅供参考
#include <iostream>#include <vector>#include <limits>using namespace std;const int BOARD_SIZE = 15;char board[BOARD_SIZE][BOARD_SIZE];// 初始化棋盘void initBoard() { for (int i = 0; i < BOARD_SIZE; ++i) { for (int j = 0; j < BOARD_SIZE; ++j) { board[i][j] = '.'; } }}// 打印棋盘void printBoard() { for (int i = 0; i < BOARD_SIZE; ++i) { for (int j = 0; j < BOARD_SIZE; ++j) { cout << board[i][j] << " "; } cout << endl; }}// 检查棋盘是否已满bool isBoardFull() { for (int i = 0; i < BOARD_SIZE; ++i) { for (int j = 0; j < BOARD_SIZE; ++j) { if (board[i][j] == '.') { return false; } } } return true;}// 检查给定玩家是否在(x, y)位置沿一个方向获胜bool checkDirection(int x, int y, int dx, int dy, char player) { int count = 0; for (int i = 0; i < 5; ++i) { int checkX = x + i * dx; int checkY = y + i * dy; if (checkX >= 0 && checkX < BOARD_SIZE && checkY >= 0 && checkY < BOARD_SIZE && board[checkY][checkX] == player) { count++; } else { break; } } return count == 5;}// 检查给定玩家是否在(x, y)位置获胜bool checkWin(int x, int y, char player) { // 检查水平方向 if (checkDirection(x, y, 1, 0, player) || checkDirection(x, y, -1, 0, player)) { return true; } // 检查垂直方向 if (checkDirection(x, y, 0, 1, player) || checkDirection(x, y, 0, -1, player)) { return true; } // 检查两个斜线方向 if (checkDirection(x, y, 1, 1, player) || checkDirection(x, y, -1, -1, player)) { return true; } if (checkDirection(x, y, 1, -1, player) || checkDirection(x, y, -1, 1, player)) { return true; } return false;}int main() { initBoard(); bool isPlayerX = true; bool gameOver = false; while (!gameOver) { printBoard(); int x, y; cout << (isPlayerX ? "Player X" : "Player O") << ", enter your move (row column): "; try { cin >> y >> x; if (cin.fail()) { cin.clear(); // 清除错误标志 cin.ignore(numeric_limits<streamsize>::max(), '\n'); // 忽略错误输入 throw runtime_error("Invalid input. Please enter numbers."); } if (x < 0 || x >= BOARD_SIZE || y < 0 || y >= BOARD_SIZE || board[y][x] != '.') { throw runtime_error("Invalid move. Try again."); } board[y][x] = isPlayerX ? 'X' : 'O'; if (checkWin(x, y, board[y][x])) { printBoard(); cout << (isPlayerX ? "Player X wins!" : "Player O wins!") << endl; gameOver = true; } else if (isBoardFull()) { printBoard(); cout << "It's a draw!" << endl; gameOver = true; } else { isPlayerX = !isPlayerX; } } catch (const runtime_error& e) { cout << e.what() << endl; // 可以选择在这里处理错误,例如跳过当前玩家的回合 // 或者让玩家重新输入,取决于你的游戏规则 } } return 0;}
在这个修改后的代码中,try-catch
块被用来捕获两种异常情况:
cin
接收到非数字输入时,会进入错误状态。cin.fail()
检查输入流是否失败,如果是,则清除错误标志,并忽略错误输入直到下一个换行符。然后抛出一个runtime_error
异常。当用户输入的坐标无效时(即不在棋盘范围内或该位置已被占用),也会抛出一个runtime_error
异常。在
catch
块中,我们捕获了runtime_error
异常,并打印出异常信息。根据你的游戏规则,你可以选择让当前玩家重新输入,或者跳过当前玩家的回合,或者采取其他适当的错误处理措施。 在这个例子中,我们只是打印了错误信息,然后循环会继续,提示玩家重新输入。