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

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

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

A算法详解(go实现)

A*算法详解&#xff08;go实现&#xff09; 推荐一位大佬的文章&#xff0c;建议可以去看看https://hogwartsrico.github.io/2016/03/11/AStarPathFinding/index.html 下面贴出来文章中用于举例的网站&#xff1a; https://anvaka.github.io/ngraph.path.demo/#?graphamste…

丹摩征文活动 | 0基础带你上手经典目标检测模型 Faster-Rcnn

文章目录 &#x1f34b;1 引言&#x1f34b;2 平台优势&#x1f34b;3 丹摩平台服务器配置教程&#x1f34b;4 实操案例&#xff08; Faster-rcnn 项目&#xff09;&#x1f34b;4.1 文件处理&#x1f34b;4.2 环境配置&#x1f34b;4.3 训练模型&#x1f34b;4.4 数据保存并导…

Java poi 模板导出Word 带图片

Java poi 模板导出Word 带图片 重点&#xff01;&#xff01;&#xff01; 官方文档&#xff1a;https://deepoove.com/poi-tl/#_maven 最终效果 模板 其实内容都在官方文档里写的非常明白了 我这里只是抛砖引玉。 Maven依赖 <poi.version>4.1.2</poi.version>…

LabVIEW车辆侧翻预警系统

在工业和实验室环境中&#xff0c;搬运车辆、叉车和特种作业车辆经常在负载和高速转弯过程中发生侧翻事故&#xff0c;导致设备损坏和人员伤害。为提高工作环境的安全性&#xff0c;开发了一种基于LabVIEW的工业车辆侧翻预警系统&#xff0c;能够实时监测车辆状态并发出预警&am…

Unity3D UI 双击和长按

Unity3D 实现 UI 元素双击和长按功能。 UI 双击和长按 上一篇文章实现了拖拽接口&#xff0c;这篇文章来实现 UI 的双击和长按。 双击 创建脚本 UIDoubleClick.cs&#xff0c;创建一个 Image&#xff0c;并把脚本挂载到它身上。 在脚本中&#xff0c;继承 IPointerClickHa…

计算机网络——SDN

分布式控制路由 集中式控制路由

深入浅出rust内存对齐

在 Rust 中&#xff0c;内存对齐是一个重要的概念&#xff0c;它涉及到数据在内存中的存储方式&#xff0c;以及如何优化内存访问的效率。往往一门语言的内存布局以及对齐方式决定了一门语言的性能&#xff0c;因此学会并深入理解rust中内存布局会让我们写出高性能的rust代码&a…

ARM64环境使用docker-compose进行ElasticSearch8集群部署

环境规划 主机IP系统ES版本CPU架构用户名密码192.168.174.18Ubuntu 22.04.4 LTSelasticsearch:8.10.4ARM64elasticllodyi4TMmZD192.168.174.218Ubuntu 22.04.4 LTSelasticsearch:8.10.4ARM64192.168.174.112Ubuntu 22.04.4 LTSelasticsearch:8.10.4ARM64 概念&#xff1a; no…

28.医院管理系统(基于springboot和vue)

目录 1.系统的受众说明 2. 相关技术和开发环境 2.1 相关技术 2.1.1 Java语言 2.1.2 HTML、CSS、JavaScript 2.1.3 Redis 2.1.4 MySQL 2.1.5 SSM框架 2.1.6 Vue.js 2.1.7 SpringBoot 2.2 开发环境 3. 系统分析 3.1 可行性分析 3.1.1 经济可行性 3.1.2 技术…

高性能分布式缓存Redis-高可用部署

一、主从架构搭建 为什么要进行主从架构搭建&#xff0c;一台redis不行吗&#xff1f; ①、持久化后的数据只在一台机器上&#xff0c;因此当硬件发生故障时&#xff0c;比如主板或CPU坏了&#xff0c;这时候无法重启服务器&#xff0c;有什么办法可以保证服务器发生故障时数…

Profinet转CanOpen网关连接与CanOpen协议磁轨道实现高效连接

该项目旨在展示如何通过开疆智能Profinet转Canopen网关实现西门子1200PLC与磁轨道之间的连接。以下是项目实施的步骤概要&#xff1a;安装必要的GSD文件到西门子组态软件中&#xff0c;确保系统能够识别并使用Profinet转Canopen网关设备。 进行设备配置&#xff0c;包括将PLC和…

openai Realtime API (实时语音)

https://openai.com/index/introducing-the-realtime-api/ 官方demo https://github.com/openai/openai-realtime-console 官方demo使用到的插件 https://github.com/openai/openai-realtime-api-beta?tabreadme-ov-file 装包配置 修改yarn.lock 这个包是从github下载的 &q…

Docker--Docker是什么和对Docker的了解

Docker 的本质 Docker的本质是LXC&#xff08;Linux容器&#xff09;之类的增强版&#xff0c;它本身不是容器&#xff0c;而是容器的易用工具。 Docker通过虚拟化技术&#xff0c;将代码、依赖项和运行环境打包成一个容器&#xff0c;并利用隔离机制来使得容器之间互相独立、…

【报错记录】Steam迁移(移动)游戏报:移动以下应用的内容失败:XXX: 磁盘写入错误

前言 由于黑神话悟空&#xff0c;导致我的2TB的SSD系统盘快满了&#xff0c;我又买了一块4TB的SSD用来存放游戏&#xff0c;我就打算把之前C盘里的游戏移动到D盘&#xff0c;结果Steam移动游戏居然报错了&#xff0c;报的还是“磁盘写入错误”&#xff0c;如下图所示&#xff…

攻防世界37-unseping-CTFWeb

攻防世界37-unseping-CTFWeb <?php highlight_file(__FILE__);class ease{private $method;private $args;function __construct($method, $args) {$this->method $method;$this->args $args;}function __destruct(){if (in_array($this->method, array("…

LabVIEW 版本控制

在软件开发中&#xff0c;版本控制系统&#xff08;VCS&#xff09; 是管理代码版本变化的核心工具。对于 LabVIEW 用户&#xff0c;虽然图形化编程带来高效开发体验&#xff0c;但由于其特有的二进制 VI 文件格式&#xff0c;传统文本比较工具无法直接用于 LabVIEW 项目。这时…

Cesium着色器的创意和方法(五——Polyline)

不接受反驳&#xff0c;线段在三维里面的渲染比多边形的渲染更复杂。因为线是没有宽度的&#xff0c;并且还需要时时刻刻向着你。 首先看下Cesium的线段的一些效果。这条线段非常宽&#xff08;20个像素&#xff09;&#xff0c;有两个需要留意观察的。一是线段并非实时面对我…

独立开发者赚钱心法

一、独立开发者的身份转变 开发者到多重角色&#xff1a;独立开发者不仅是程序员&#xff0c;还是产品经理、设计师、文案、销售、运营、客服&#xff0c;最重要的是成为“老板”。思维转变&#xff1a;将开发程序视为一门生意&#xff0c;而非单纯的技术实现。 二、赚钱的核…

Hook小程序

下载&#xff1a; https://github.com/JaveleyQAQ/WeChatOpenDevTools-Python 配置&#xff1a; pip install -r requirements 实现&#xff1a; 开启小程序开发者模式&#xff0c;类似浏览器F12 效果&#xff1a; 使用&#xff1a; 退出微信&#xff0c;进入安装的目录…

【Spring Security系列】10分钟实现 SpringSecurity + CAS 完美单点登录方案

作者&#xff1a;后端小肥肠 &#x1f347; 我写过的文章中的相关代码放到了gitee&#xff0c;地址&#xff1a;xfc-fdw-cloud: 公共解决方案 &#x1f34a; 有疑问可私信或评论区联系我。 &#x1f951; 创作不易未经允许严禁转载。 姊妹篇&#xff1a; 【Spring Security系列…