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

news2024/11/14 5:55:46

1. 游戏介绍

一个简单的 C++ 五子棋小游戏

1.1 游戏规则:
  • 双人轮流输入下入点坐标
  • 横竖撇捺先成五子连线者胜
  • 同一坐标点不允许重复输入
1.2 初始化与游戏界面

在这里插入图片描述

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

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

  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 优化了判胜代码
// 检查给定玩家是否在(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块被用来捕获两种异常情况:

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2238711.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

计算机新手练级攻略——如何搜索问题

目录 计算机学生新手练级攻略——如何搜索问题1.明确搜索意图2.使用精确关键词3.使用专业引擎搜索4.利用好技术社区1. Stack Overflow2. GitHub3. IEEE Xplore4. DBLP 5.使用代码搜索工具1. GitHub 代码搜索2. Stack Overflow 代码搜索3. Papers with Code4. IEEE Xplore 6.查阅…

【MySQL】MySQL基础知识复习(下)

前言 上一篇博客介绍了MySQL的库操作&#xff0c;表操作以及CRUD。 【MySQL】MySQL基础知识复习&#xff08;上&#xff09;-CSDN博客 本篇将进一步介绍CRUD操作&#xff0c;尤其是查找操作 目录 一.数据库约束 1.约束类型 1.1NULL约束 1.2UNIQUE&#xff1a;唯一约束 …

从0开始学docker (每日更新 24-11-6)

限制容器运行的资源 限制容器的内存使用 容器的内存包括两部分&#xff1a;物理内存和交换空间&#xff08;swap&#xff09; 用户内存限制 -m,--memory&#xff1a;容器可用的最大内存&#xff0c;该值最低为4MB --memory-swap&#xff1a;允许容器置入磁盘交换空间的内存…

C++初阶——vector

一、什么是vector vector是表示可变大小的数组的序列容器&#xff0c;就像数组一样&#xff0c;vector也采用连续空间来存储元素。也就是说它的访问和数组一样高效&#xff0c;但是它的大小是动态可变的&#xff0c;并且它的大小会被容器自动处理。 二、vector的构造 常用的构…

GIT的基本使用与进阶

GIT的简单入门 一.什么是git&#xff1f; Git 是一个开源的分布式版本控制系统&#xff0c;用于跟踪文件更改、管理代码版本以及协作开发。它主要由 Linus Torvalds 于 2005 年创建&#xff0c;最初是为 Linux 内核开发而设计的。如今&#xff0c;Git 已经成为现代软件开发中…

CulturalBench :一个旨在评估大型语言模型在全球不同文化背景下知识掌握情况的基准测试数据集

2024-10-04&#xff0c;为了提升大型语言模型在不同文化背景下的实用性&#xff0c;华盛顿大学、艾伦人工智能研究所等机构联合创建了CulturalBench。这个数据集包含1,227个由人类编写和验证的问题&#xff0c;覆盖了包括被边缘化地区在内的45个全球区域。CulturalBench的推出&…

C语言中,“extern”关键字的含义与用法

在C语言中&#xff0c;extern 关键字用于声明一个已经在其他地方定义的变量或函数。它的主要作用是告诉编译器&#xff0c;某个变量或函数是在当前文件之外定义的&#xff0c;编译器应该在链接阶段找到这个变量或函数的实际定义。以下是 extern 的一些常见用途和用法&#xff1…

「Mac玩转仓颉内测版1」入门篇1 - Cangjie环境的搭建

本篇详细介绍在Mac系统上快速搭建Cangjie开发环境的步骤&#xff0c;涵盖VSCode的下载与安装、Cangjie插件的离线安装、工具链的配置及验证。通过这些步骤&#xff0c;确保开发环境配置完成&#xff0c;为Cangjie项目开发提供稳定的基础支持。 关键词 Cangjie开发环境搭建VSC…

Kubernetes网络揭秘:从DNS到核心概念,一站式综述

文章目录 一.overlay vs underlayL2 underlayL3 underlay 二、calico vs flannel2.1 calico架构2.2 flannel架构 三、iptables四、Vxlan五、kubernetes网络架构综述六、DNS七、Kubernetes域名解析策略 一.overlay vs underlay overlay网络是在传统网络上虚拟出一个虚拟网络&am…

飞凌嵌入式FET527N-C核心板现已适配Android 13

飞凌嵌入式FET527N-C核心板现已成功适配Android13&#xff0c;新系统的支持能够为用户提供更优质的使用体验。那么&#xff0c;运行Android13系统的FET527N-C核心板具有哪些突出的优势呢&#xff1f; 1、性能与兼容性提升 飞凌嵌入式FET527N-C核心板搭载了全志T527系列高性能处…

破解仓库管理难题!库存管理流程详解

随着市场竞争加剧和客户需求不断提升&#xff0c;企业的仓库管理在日益复杂的供应链环境中面临诸多挑战。许多企业在仓库管理上仍停留在传统手工操作或基础的数字化管理阶段&#xff0c;这种方式容易导致库存信息不准确、管理效率低下、数据滞后等问题。 企业现状 当前&#…

电缆管道高效智能化管理:智能电缆管道可视化机器人

电缆管道高效智能化管理&#xff1a;智能电缆管道可视化机器人 电缆管道作为城市“生命线”的重要组成部分&#xff0c;其安全性和可靠性日益受到重视。然而&#xff0c;传统的人工巡检方式存在诸多不足&#xff0c;如巡检效率低下、安全隐患大、难以发现隐蔽故障等。 很多电力…

tomcat启动失败和缓存清理办法

tomcat只在学校接触过并且是在window xp和win7的电脑上配置过&#xff08;中途升级过电脑系统&#xff09;&#xff0c;只记得在windows系统上可以将其设置成服务管理。但我已毕业10多年了&#xff0c;学的知识早就不知道丢哪里了。这次为了修改一个07&#xff0c;08年的项目&a…

RDD 算子全面解析:从基础到进阶与面试要点

Spark 的介绍与搭建&#xff1a;从理论到实践_spark环境搭建-CSDN博客 Spark 的Standalone集群环境安装与测试-CSDN博客 PySpark 本地开发环境搭建与实践-CSDN博客 Spark 程序开发与提交&#xff1a;本地与集群模式全解析-CSDN博客 Spark on YARN&#xff1a;Spark集群模式…

如何在CentOS 7上搭建SMB服务

如何在CentOS 7上搭建SMB服务 因项目测试需求&#xff0c;需要自行搭建SMB服务&#xff0c;**SMB&#xff08;Server Message Block&#xff09;**协议是一种常用的文件共享方式&#xff0c;它可以让不同操作系统之间共享文件、打印机等资源。本文将带你一步步搭建一个简单的S…

ubuntu18.04 配置安卓编译环境

目前有个项目&#xff0c;验收时有个要求是在linux中进行编译打包生成apk文件。我平时都是在windows环境android studio中进行打包的&#xff0c;花了半天时间研究了一下&#xff0c;记录如下&#xff1a; 安装安卓sdk cd /opt wget https://dl.google.com/android/reposito…

英伟达HOVER——用于人形机器人的多功能全身控制器:整合不同的控制模式且实现彼此之间的无缝切换

前言 前几天&#xff0c;一在长沙的朋友李总发我一个英伟达HOVER的视频(自从我今年年初以来持续不断的解读各大顶级实验室的最前沿paper、以及分享我司七月在具身领域的探索与落地后&#xff0c;影响力便越来越大了&#xff0c;不断加油 )&#xff0c;该视频说的有点玄乎&…

C语言中“type”的含义

在C语言中&#xff0c;“type”是指数据类型&#xff0c;它定义了变量可以存储的数据种类以及可以对这些数据执行的操作。C语言提供了一系列基本的数据类型&#xff0c;它们包括但不限于&#xff1a; 整型&#xff08;Integer Types&#xff09;&#xff1a; int&#xff1a;用…

ffmpeg 视频滤镜:屏蔽边框杂色- fillborders

滤镜描述 fillborders 官网链接 > FFmpeg Filters Documentation fillborders滤镜有几种方式帮你屏蔽边框的杂色、不好的图案。 滤镜使用 参数 left <int> ..FV.....T. set the left fill border (from 0 to INT_MAX) (default 0)right …

云计算答案

情境一习题练习 一、选择题 1、在虚拟机VMware软件中实现联网过程&#xff0c;图中箭头所指的网络连接方式与下列哪个相关&#xff08; C &#xff09;。 A.仅主机模式 B.桥接 C.NAT D.嫁接 2、请问下图这个虚拟化架构属于什么类型&#xff08; A …