【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异常,并打印出异常信息。根据你的游戏规则,你可以选择让当前玩家重新输入,或者跳过当前玩家的回合,或者采取其他适当的错误处理措施。

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

相关推荐
理论最高的吻8 小时前
98. 验证二叉搜索树【 力扣(LeetCode) 】
数据结构·c++·算法·leetcode·职场和发展·二叉树·c
时光の尘11 小时前
C语言菜鸟入门·关键字·void的用法
c语言·开发语言·c++·算法·c#·c·关键字
神一样的老师1 天前
C/C++ 优化,strlen 示例
c
万物复苏1014 天前
【汇编】c++游戏开发
开发语言·笔记·游戏·c
石牌桥网管5 天前
用正则表达式检查是IP否为内网地址
java·c++·golang·正则表达式·php·c
Koishi_TvT6 天前
蓝桥杯c++算法学习【3】之思维与贪心(重复字符串、翻硬币、乘积最大、皮亚诺曲线距离【难】:::非常典型的必刷例题!!!)
c++·学习·算法·游戏·贪心算法·蓝桥杯·c
一叶飘零_sweeeet11 天前
Spring Boot 的核心注解
java·spring boot·c
m1chiru13 天前
C++ 游戏开发:打造高效、性能优越的游戏世界
c
charlie11451419115 天前
从0开始的STM32之旅8 串口通信(II)
stm32·单片机·嵌入式硬件·c·串口通信