1. 游戏介绍
一个简单的 C++ 五子棋小游戏
1.1 游戏规则:
- 双人轮流输入下入点坐标
- 横竖撇捺先成五子连线者胜
- 同一坐标点不允许重复输入
1.2 初始化与游戏界面
初始化界面
X 输入坐标后
O 输入坐标后
X 先达到胜出条件
2. 源代码
cpp
#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 优化了判胜代码
cpp
// 检查给定玩家是否在(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
块捕获异常,仅供参考
cpp
#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
异常,并打印出异常信息。根据你的游戏规则,你可以选择让当前玩家重新输入,或者跳过当前玩家的回合,或者采取其他适当的错误处理措施。
在这个例子中,我们只是打印了错误信息,然后循环会继续,提示玩家重新输入。