跳转到内容

C-EASY-4:小应用之五子棋游戏

计算机系统Banner

前言:在前面几个题中你已经入门了c语言,语言是用来使用的,只有去编写一些东西,你才会有学习的动力。因此在这道题中我们要从头开始,编写一个本地的五子棋小游戏。同时,在上面的题目中,我曾提到,在实际项目中都会使用多文件来构成一个项目,在这道题中也要使用多文件操作。你可以借助ai,但是具体的度你自己考量。

功能要求:

  1. 在终端中显示 9×9 ASCII 方格棋盘。
  2. 玩家 X 先手,O 后手。
  3. 使用鼠标左键点击棋盘格落子。
  4. 已经有棋子的格子不能重复落子。
  5. 横向、纵向或斜向连续五子即获胜。
  6. 81 个格子填满且无人获胜则为平局。

在这一题中,我要求你完成上述项目,并上传你的项目文件夹和一个记录你思考过程的md文件。

相信你看完要求后一定会有自己的思考,接下来我会给出我的一套思路,你可以按照我的思路和提示完成这个项目。

建议将项目拆分为以下文件:

  • game.h
    • 定义棋盘大小等常量
    • 定义游戏状态枚举
    • 定义落子结果枚举
    • 定义 Game 结构体
    • 声明游戏逻辑函数
  • game.c
    • 判断胜负和平局
  • console.h
    • 定义控制台相关结构体
    • 声明函数
  • console.c
    • 读取鼠标事件
    • 将鼠标坐标转换成棋盘坐标
    • 初始化棋盘
    • 执行落子
  • main.c
    • 初始化游戏
    • 执行主游戏循环
    • 协调 gameconsole 两个模块

定义一些基础数据结构辅助:

#define BOARD_SIZE 9
#define WIN_LENGTH 5
#define EMPTY_CELL '.'
typedef enum {
GAME_RUNNING, // 游戏进行中
GAME_X_WINS, // X胜出
GAME_O_WINS, // O胜出
GAME_DRAW //平局
} GameStatus; // 枚举游戏状态
typedef enum {
MOVE_ACCEPTED, // 可以下棋
MOVE_OUT_OF_BOUNDS, // 当前位置在棋盘外
MOVE_OCCUPIED // 当前位置已占用
} MoveResult; // 下棋结果
typedef struct {
char cells[BOARD_SIZE][BOARD_SIZE]; // 棋盘信息
char current_player;
int move_count; // 步数统计
GameStatus status;
} Game;
// 你可以不完全按照我的结构体定义,你可以自行更改变量名,或者你有别的定义方法也可以

接下来让我为你简单阐述以下面对这个项目要求的一个思路:可以把问题分为两个板块,第一个是处理“五子连珠”的游戏结束判定,第二个是要解决绘制棋盘和如何落子的问题。显然,第二部分是更可视化的,是第一部分的基础,也就是我们先要解决的。

因此我们先要解决如何在终端中绘制一个棋盘的问题,接下来要解决如何下棋的问题,最后确定怎样结束游戏。

我们可以先设想主函数中的循环样式,

int main(void)
{
初始化 Game;
初始化 GameConsole;
while (游戏状态为 GAME_RUNNING) {
绘制棋盘和底部状态;
等待有效的鼠标格子点击;
调用 game_place;
}
绘制最终棋盘和底部胜负结果;
等待用户按键;
恢复控制台状态;
return 0;
}

“使用鼠标左键点击棋盘格落子”有这么一个要求,相信初学c语言的你可能会产生困惑,应该怎样才能实现这样的一个操作?这需要使用到c语言开发中很常用“Windows Console API ”。

这个接口用于控制终端窗口,包括读取键盘和鼠标、移动光标、修改颜色以及操作屏幕缓冲区。

现在请你自行学习这个api,在这一题中,你只需要会读取鼠标左键时光标的位置即可,不用学的很深入。

接下来我们要绘制棋盘。下面是我给出的一种棋盘的样式,每一个棋格长4宽2(字符坐标),而棋子坐标可由棋盘坐标算出。从而实现鼠标光标落点到对应棋子的转化。

+---+---+---+---+---+---+---+---+---+
| . | . | . | . | . | . | . | . | . |
+---+---+---+---+---+---+---+---+---+
| . | . | . | . | . | . | . | . | . |
+---+---+---+---+---+---+---+---+---+

请你推算出字符坐标到棋盘坐标的计算公式。

为了实现下棋这一个功能,我给出我的实现思路:

  1. 初始化棋盘,将game初始化,cells全部赋值为“.”,currentplayer为”X”,move_count为0,status为GAME_RUNNING
  2. 绘制棋盘
  3. 读取鼠标左键位置,将光标坐标转化为棋盘坐标,将棋盘坐标对应的cells数组位置赋值为currentplayer,currentplayer变为相反元素
  4. 重新绘制棋盘
  5. 在3,4间循环直到达成游戏结束的条件

具体代码请你自己实现,循环功能可以放在主函数中,完成绘制棋盘,初始化棋盘,和改变game这几个功能函数即可。

提示:在这里,你可以先在主函数中调用这些函数,看能否打印出棋盘,能否刷新棋盘,进行简单的测试。

要进行游戏是否结束的判定,你需要在上述的哪一步对cells数组进行检测?

你要做怎样的检测?

通过对棋盘坐标的计算可以简单的完成游戏结束的条件判定。请你说说为什么要设计棋盘坐标和字符坐标两种坐标?

相信你在思考完上述问题后,已经知道该如何补全这部分代码了。请你将这部分代码写在game.c当中。

完成功能函数后,请你根据我上面给出的提示完成主函数中的循环。

完成主循环后你就可以开始测试工作了,在这里我提几个你可能会犯的小错误:

  1. 当光标落在棋盘外时是否有判错?有效步数是否增加?光标落在棋格边界上呢?
  2. 光标到棋盘坐标的计算是否正确?是否会出现偏移情况?
  3. 斜向的棋子判定是否正常?当五子连珠后游戏能否正常结束?是否有考虑平局的情况?

在完成上面的工作后,你会发现现在这个项目很单调,只有一个棋盘,没有别的内容。因此你可以在刷新棋盘时添加提示语句,在棋盘下面打印current_player和game_status等信息。为什么是在棋盘下面?

在游戏结束后,你可以打印获胜一方(current_player)和有效步数等信息,丰富游戏内容。

最后在游戏结束时,别忘了加入初始化控制台,完成内存释放,规范细节。

五子棋游戏是一个很经典的c语言项目,可以帮助你提升代码熟练度,更好的面对接下来的招新题目。如果你还想写点什么,你可以研究研究贪吃蛇这个同样经典且简单的项目,想想应该如何实现。你也可以看看网上一些项目介绍视频,看看其中是怎样切入一个项目并完成项目结构的设计。

提交点这里

出题人:出题人头像  taaaakoooo

QQ:1748798371

邮箱:1748798371@qq.com