【C++】 C++游戏设计---五子棋小游戏

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. 主要代码释解

这段代码是一个简单的五子棋游戏的实现,下面是对主要函数和异常错误处理机制的详解:

  1. initBoard()
    • 功能:初始化棋盘,将所有位置设置为'.',表示空白。
    • 实现 :通过双重循环遍历二维数组board,并将每个元素设置为'.'。
  2. printBoard()
    • 功能:打印当前棋盘的状态。
    • 实现 :通过双重循环遍历二维数组board,并打印每个元素。
  3. isBoardFull()
    • 功能:检查棋盘是否已满。
    • 实现 :通过双重循环遍历二维数组board,如果所有位置都不是'.',则返回true,表示棋盘已满。
  4. checkWin(int x, int y, char player)
    • 功能:检查给定玩家是否在(x, y)位置获胜。
    • 实现 :检查横向、纵向、两个对角线方向是否有连续的 5 个相同的棋子。如果找到,则返回true,表示该玩家获胜。
  5. main()
    • 功能:游戏的主循环,处理玩家的输入,更新棋盘状态,并判断游戏是否结束。
    • 实现
      • 初始化棋盘。
      • 在一个循环中交替让两个玩家输入他们的移动。
      • 检查移动是否有效(即在棋盘范围内且位置为空)。
      • 更新棋盘,并检查是否有玩家获胜或棋盘已满。

4. 异常和错误处理机制

  1. 输入有效性检查
    • main()函数中,玩家输入移动后,代码检查移动是否在棋盘范围内,以及对应位置是否为空。
    • 如果移动无效(即xy超出范围,或者对应位置不是'.'),则打印错误消息,并通过continue跳过当前循环的剩余部分,提示玩家重新输入。
  2. 棋盘满时结束游戏
    • 在玩家每次移动后,调用isBoardFull()检查棋盘是否已满。
    • 如果棋盘已满,则打印平局消息,并通过设置gameOvertrue结束游戏。
  3. 检查获胜条件
    • 在玩家每次移动后,调用checkWin()检查该玩家是否获胜。
    • 如果玩家获胜,则打印获胜消息,并通过设置gameOvertrue结束游戏。

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块被用来捕获两种异常情况:

  1. cin接收到非数字输入时,会进入错误状态。cin.fail()检查输入流是否失败,如果是,则清除错误标志,并忽略错误输入直到下一个换行符。然后抛出一个runtime_error异常。
  2. 当用户输入的坐标无效时(即不在棋盘范围内或该位置已被占用),也会抛出一个runtime_error异常。
    catch块中,我们捕获了runtime_error异常,并打印出异常信息。根据你的游戏规则,你可以选择让当前玩家重新输入,或者跳过当前玩家的回合,或者采取其他适当的错误处理措施。

在这个例子中,我们只是打印了错误信息,然后循环会继续,提示玩家重新输入。

相关推荐
一叶飘零_sweeeet3 天前
Spring Boot 的核心注解
java·spring boot·c
m1chiru5 天前
C++ 游戏开发:打造高效、性能优越的游戏世界
c
charlie1145141917 天前
从0开始的STM32之旅8 串口通信(II)
stm32·单片机·嵌入式硬件·c·串口通信
孤寂大仙v7 天前
【C++】AVL树
开发语言·数据结构·c++·c
charlie1145141919 天前
嵌入式Linux入门具备:C语言基础与基本驱动学习(2):Linux GIibc IO基础
c语言·学习·c·io·嵌入式软件
charlie11451419111 天前
从0开始的STM32之旅 7 串口通信(I)
stm32·单片机·嵌入式硬件·c
Java Fans12 天前
C++ 实现俄罗斯方块游戏
c
我要学脑机12 天前
电路知识的回顾
c·电路
Ljw...12 天前
C++游戏开发
c++·c·游戏开发