三字棋游戏(C语言详细解释)
hello,小伙伴们大家好,算是失踪人口回归了哈,主要原因是期末考试完学校组织实训,做了俄罗斯方块,后续也会更新,不过今天先从简单的三字棋说起
话不多说,开始今天的内容
一、大体思路
我们都知道,要做一个游戏,游戏菜单是必不可少的,所以首先我们要做一个开始的菜单
其次,我们知道三字棋,你菜单完了开始界面,棋盘是必不可少的,因此我们可以做一个棋盘
再次,有了棋盘就是玩家下棋和电脑下棋了,这里应该包含下棋位置的约束
之后还要有个功能判断输赢,之后就游戏结束
总体而言,包含如下流程:
游戏菜单:开始 / 离开
开始棋盘:初始化棋盘
玩家下棋:这里包括下棋位置是否合理
判断输赢:玩家赢 / 电脑赢 / 平局
电脑下棋:随机数的生成(如果让电脑玩家厉害一点,后续有时间我会补充)
判断输赢:玩家赢 / 电脑赢 / 平局
......
重复以上步骤,直到游戏结束
二、头文件与原文件的分类
因为我用的是Visual Stdio 2022写的三字棋游戏,我是这么分配的
首先有个头文件 game.h 我用来放头文件和一些注释,函数声明,全局变量,这样的话,其它原文件就不用写头文件,只要包含一句 #include "game.h"就可以把头文件里面的内容包含进去
而针对于main函数,是写流程的,也能一下看清楚咱们的思路
其它函数功能我放在了另一个game.c的原文件里面,game.c原文件是专门写函数功能的
分配好这些,我们就可以详细的写代码了
三、各个流程详细解释+代码
1.菜单(进入游戏+退出游戏)
首先游戏菜单我们要让游戏实现,开始或者离开,假如我们设定玩家输入 1 为开始游戏,进入游戏界面,输入 0 为离开游戏,游戏结束,那么我们只需要放到一个 while 循环里面就可以实现
而此时为了main函数简洁明了,我们选择用函数单另写一个菜单 menu();
//这里是game.c的原文件
#include "game.h" //这句话只出现一次,后续再往进写东西我就不加这句话了
void menu()
{printf("————————————\n");printf("————1.enter ————\n");printf("————0.exit ————\n");printf("————————————\n");printf("请输入你的选择:>");
}
此时参考代码如下:
//这里是main.c的原文件
#include "game.h" //这句话只出现一次,后续再往进写东西我就不加这句话了
int main()
{while (1) {int c = 0;//游戏菜单menu();scanf("%d", &c);if (c == 0)//玩家输入0退出游戏break;else if (c == 1){game();//这里是三字棋游戏函数,之后要细写//玩家输入1进入游戏}}return 0;
}
别忘了头文件game.h的函数声明和头文件:
//这里是game.h头文件
#include <stdio.h>//游戏菜单
void menu();
2. 初始化棋盘
我们都知道,三字棋长这样,如下图:
首先棋盘是三行三列,因为我们就想到了二维数组
那么在没有 * 或者 # 的时候其它应该是空格,这就是我们初始化棋盘的原因
初始化我们需要将棋盘打印出来,因此还要写打印棋盘的函数
参考代码如下:
//这里是game.h头文件
#include <stdio.h>#define ROW 3 //棋盘的行数
#define COL 3 //棋盘的列数//游戏菜单
void menu();//初始化棋盘
void initboard(char board[ROW][COL], int row, int col);//打印棋盘
void displayboard(char board[ROW][COL], int row, int col);
//这里是main.c的原文件void game()
{char board[ROW][COL] = { 0 };initboard(board, ROW, COL);//初始化棋盘displayboard(board, ROW, COL);//打印棋盘
}
//这里是game.c的原文件
void initboard(char board[ROW][COL], int row, int col)
{for (int i = 0; i <= ROW - 1; i++){for (int j = 0; j <= ROW - 1; j++){board[i][j] = ' ';}}
}void displayboard(char board[ROW][COL], int row, int col)
{for (int i = 0; i <= ROW - 1; i++){for (int j = 0; j <= COL - 1; j++){printf(" %c ", board[i][j]);if (j <= COL - 2)printf("|");}printf("\n");if (i <= ROW - 2)printf("------------\n");}
}
给大家看一下我的棋盘效果,可以做个参考:
3.玩家下棋和电脑下棋
首先先理清我们的思路,写进main.c的原文件里
//这里是main.c的原文件
void game()
{char board[ROW][COL] = { 0 };initboard(board, ROW, COL);//初始化棋盘displayboard(board, ROW, COL);//打印棋盘while (1) {playerboard(board, ROW, COL);//玩家下棋的函数displayboard(board, ROW, COL);//打印棋盘cpboard(board, ROW, COL);//电脑下棋的函数displayboard(board, ROW, COL);//打印棋盘}
}
之后再在头文件进行声明:
PS:电脑下棋,因为用到随机数rand();函数,这里要想时时刻刻都发生变化,那么就需要用到time函数,而这两个函数需要用到另外两个头文件 stdlib.h 和 time.h,我们一起加到game.h的头文件里,此时完整的game.h头文件如下
#include <stdio.h>
#include <stdlib.h>
#include <time.h>#define ROW 3
#define COL 3//游戏菜单
void menu();//初始化棋盘
void initboard(char board[ROW][COL], int row, int col);//打印棋盘
void displayboard(char board[ROW][COL], int row, int col);//玩家下棋
void playerboard(char board[ROW][COL],int row,int col);//电脑下棋
void cpboard(char board[ROW][COL], int row, int col);
玩家下棋,只要玩家输入横纵坐标在1~3即可,但是数组的三行是0~2,所以我们要注意数组的坐标要减一
还要注意,玩家只能下在空格地方,也不能重复的下棋
电脑下棋,用随机数取余3,这样的话范围就在0~2,不会数组越界,因此这两个函数代码如下:
//这里是game.c的原文件
void playerboard(char board[ROW][COL],int row,int col)
{printf("玩家请下棋:>\n");int x = 0, y = 0;scanf("%d %d", &x, &y);while (1){if (x <= ROW && y <= COL && x >= 1 && y >= 1 && board[x - 1][y - 1] == ' '){board[x - 1][y - 1] = '*';break;}else if (board[x - 1][y - 1] != ' ' || x>ROW || x<0 || y>COL || y<0 ){printf("坐标不合理,重新输入\n");scanf("%d %d", &x, &y);}}
}void cpboard(char board[ROW][COL], int row, int col)
{printf("电脑下棋:>\n");srand((unsigned int) time(NULL));int Row = 0, Col = 0;while (1) {Row = rand() % ROW;Col = rand() % COL;if (board[Row][Col] == ' '){board[Row][Col] = '#';break;}}
}
给大家展示一下效果,如下图:
4.判断输赢
这个时候有三种情况,那么不管谁每走一步,就要判断输赢,写个函数返回 * 就是玩家赢,返回 # 就是电脑赢,否则就是平局返回 Q ,没有分出胜负就返回 c 让游戏继续进行
大体思路代码如下:
//这里是main.c的原文件
void game()
{char board[ROW][COL] = { 0 };char ret = 0;initboard(board, ROW, COL);displayboard(board, ROW, COL);while (1) {playerboard(board, ROW, COL);displayboard(board, ROW, COL);ret = win(board, ROW, COL);if (ret != 'c')break;cpboard(board, ROW, COL);displayboard(board, ROW, COL);ret = win(board, ROW, COL);if (ret != 'c')break;}if (ret == '*')printf("玩家赢\n");else if (ret == '#')printf("电脑赢\n");elseprintf("平局\n");displayboard(board, ROW, COL);
}
在头文件进行声明:
//这里是game.h文件
#include <stdio.h>
#include <stdlib.h>
#include <time.h>#define ROW 3
#define COL 3//游戏菜单
void menu();//初始化棋盘
void initboard(char board[ROW][COL], int row, int col);//打印棋盘
void displayboard(char board[ROW][COL], int row, int col);//玩家下棋
void playerboard(char board[ROW][COL],int row,int col);//电脑下棋
void cpboard(char board[ROW][COL], int row, int col);//判断玩家赢
char win(char board[ROW][COL], int row, int col);
写出具体函数:
char win(char board[ROW][COL], int row, int col)
{int count = 0;for (int i = 0; i <= 2; i++){//行if (board[i][0] == board[i][1] && board[i][2] == board[i][1] && board[i][1] != ' ')return board[i][1];}//列for (int j = 0; j <= 2; j++){if (board[0][j] == board[1][j] && board[1][j] == board[2][j] && board[1][j] != ' ')return board[1][j];}//对角if (board[0][0] == board[1][1] && board[2][2] == board[1][1] && board[1][1] != ' ')return board[1][1];if (board[0][2] == board[1][1] && board[2][0] == board[1][1] && board[1][1] != ' ')return board[1][1];for (int i1 = 0; i1 <= 2; i1++){for (int j1 = 0; j1 <= 2; j1++){if (board[i1][j1] == ' ')count++;}}if (count == 0)return 'Q';return 'c';
}
给大家展示我的效果:
这样的话一个三字棋就完成啦~
四、所有代码整理
顺序为game.h文件 -> main.c文件 -> game.c 文件
#include <stdio.h>
#include <stdlib.h>
#include <time.h>#define ROW 3
#define COL 3//游戏菜单
void menu();//初始化棋盘
void initboard(char board[ROW][COL], int row, int col);//打印棋盘
void displayboard(char board[ROW][COL], int row, int col);//玩家下棋
void playerboard(char board[ROW][COL],int row,int col);//电脑下棋
void cpboard(char board[ROW][COL], int row, int col);//判断玩家赢
char win(char board[ROW][COL], int row, int col);
#include "game.h"void game()
{char board[ROW][COL] = { 0 };char ret = 0;initboard(board, ROW, COL);displayboard(board, ROW, COL);while (1) {playerboard(board, ROW, COL);displayboard(board, ROW, COL);ret = win(board, ROW, COL);if (ret != 'c')break;cpboard(board, ROW, COL);displayboard(board, ROW, COL);ret = win(board, ROW, COL);if (ret != 'c')break;}if (ret == '*')printf("玩家赢\n");else if (ret == '#')printf("电脑赢\n");elseprintf("平局\n");displayboard(board, ROW, COL);
}int main()
{//游戏菜单while (1) {int c = 0;menu();scanf("%d", &c);if (c == 0)break;else if (c == 1){game();}}return 0;
}
#include "game.h"void menu()
{printf("————————————\n");printf("————1.enter ————\n");printf("————0.exit ————\n");printf("————————————\n");printf("请输入你的选择:>");
}void initboard(char board[ROW][COL], int row, int col)
{for (int i = 0; i <= ROW - 1; i++){for (int j = 0; j <= ROW - 1; j++){board[i][j] = ' ';}}
}void displayboard(char board[ROW][COL], int row, int col)
{for (int i = 0; i <= ROW - 1; i++){for (int j = 0; j <= COL - 1; j++){printf(" %c ", board[i][j]);if (j <= COL - 2)printf("|");}printf("\n");if (i <= ROW - 2)printf("------------\n");}
}void playerboard(char board[ROW][COL],int row,int col)
{printf("玩家请下棋:>\n");int x = 0, y = 0;scanf("%d %d", &x, &y);while (1){if (x <= ROW && y <= COL && x >= 1 && y >= 1 && board[x - 1][y - 1] == ' '){board[x - 1][y - 1] = '*';break;}else if (board[x - 1][y - 1] != ' ' || x>ROW || x<0 || y>COL || y<0 ){printf("坐标不合理,重新输入\n");scanf("%d %d", &x, &y);}}
}void cpboard(char board[ROW][COL], int row, int col)
{printf("电脑下棋:>\n");srand((unsigned int) time(NULL));int Row = 0, Col = 0;while (1) {Row = rand() % ROW;Col = rand() % COL;if (board[Row][Col] == ' '){board[Row][Col] = '#';break;}}
}char win(char board[ROW][COL], int row, int col)
{int count = 0;for (int i = 0; i <= 2; i++){//行if (board[i][0] == board[i][1] && board[i][2] == board[i][1] && board[i][1] != ' ')return board[i][1];}//列for (int j = 0; j <= 2; j++){if (board[0][j] == board[1][j] && board[1][j] == board[2][j] && board[1][j] != ' ')return board[1][j];}//对角if (board[0][0] == board[1][1] && board[2][2] == board[1][1] && board[1][1] != ' ')return board[1][1];if (board[0][2] == board[1][1] && board[2][0] == board[1][1] && board[1][1] != ' ')return board[1][1];for (int i1 = 0; i1 <= 2; i1++){for (int j1 = 0; j1 <= 2; j1++){if (board[i1][j1] == ' ')count++;}}if (count == 0)return 'Q';return 'c';
}
今天内容就到这里啦~希望大家有所收获
最后还是想和大家共勉,聊句心里话
因为我不是很聪明,所以才要比别人更努力!加油!
相关文章:

三字棋游戏(C语言详细解释)
hello,小伙伴们大家好,算是失踪人口回归了哈,主要原因是期末考试完学校组织实训,做了俄罗斯方块,后续也会更新,不过今天先从简单的三字棋说起 话不多说,开始今天的内容 一、大体思路 我们都知…...

H3CNE(计算机网络的概述)
1. 计算机网络的概述 1.1 计算机网络的三大基本功能 1. 资源共享 2. 分布式处理与负载均衡 3. 综合信息服务 1.2 计算机网络的三大基本类型 1.3 网络拓扑 定义: 网络设备连接排列的方式 网络拓扑的类型: 总线型拓扑: 所有的设备共享一…...
【极客日常】Golang一个的slice数据替换的bug排查
上周某天下班前,接到同事转来一个bug要排查,症状是代码重构之后某些业务效果不符合预期,由于代码重构人是笔者,于是blame到笔者这边。经过10min左右的排查和尝试后,解决了这个问题:既往逻辑没有改动&#x…...

HarmonyOS应用开发者高级认证,Next版本发布后最新题库 - 单选题序号3
基础认证题库请移步:HarmonyOS应用开发者基础认证题库 注:有读者反馈,题库的代码块比较多,打开文章时会卡死。所以笔者将题库拆分,单选题20个为一组,多选题10个为一组,题库目录如下,…...

UE4-光照重建
当我们拉入新的光源和模型到我们的场景中后,会产生这样的情况: Preview:预览 表示此时由于光照物体所产生的阴影都是预览级别的并不是真正的效果。 方法一: 或者也可以在世界大纲中选中我们的光源,然后将我们的光源改变为可以…...
【2024德国签证】留学面签问题汇总
在去交材料的时候,可能会被随机安排面试。这些面试问题一般都很简单,主要是测试你的基本英文交流能力。无需担心,签证官不会问太专业的问题,因为他们也不懂专业内容。到目前为止,没有一个博士生因为这个面试被拒签。毕…...

知识点大纲
学习方法 学习、整理笔记过程中,顺便整理出一个以问题为模版的大纲,到时候对着问题,就像是在和面试官讲解那样,相当于升级版的费曼学习法 除了看博客,问gpt外,亲自实验也是获取知识及加深印象的关键点 很…...

MySQL:库表操作
MySQL:库表操作 库操作查看创建字符编码集 删除修改备份 表操作创建查看删除修改 库操作 查看 查看存在哪些数据库: show databases;示例: 查看自己当前处于哪一个数据库: select database();示例: 此处由于我不处于任…...
8.3 End-to-end Data Protection (Optional)
8.3 End-to-end Data Protection (Optional) 为了提供从应用程序到NVM介质并返回到应用程序本身的稳健数据保护,可以使用端到端数据保护。如果启用了此可选机制,则将额外的保护信息(例如CRC)添加到逻辑块中,控制器和/或主机软件可以对其进行评估,以确定逻辑块的完整性。…...

python实现图像对比度增强算法
python实现直方图均衡化、自适应直方图均衡化、连接组件标记算法 1.直方图均衡化算法详解算法步骤公式Python 实现详细解释优缺点 2.自适应直方图均衡化算法详解算法步骤公式Python 实现详细解释优缺点 3.连接组件标记算法详解算法步骤8连通与4连通公式Python 实现详细解释优缺…...

【D3.js in Action 3 精译_020】2.6 用 D3 设置与修改元素样式 + 名人专访(Nadieh Bremer)+ 2.7 本章小结
当前内容所在位置 第一部分 D3.js 基础知识 第一章 D3.js 简介(已完结) 1.1 何为 D3.js?1.2 D3 生态系统——入门须知1.3 数据可视化最佳实践(上)1.3 数据可视化最佳实践(下)1.4 本章小结 第二章…...

GIT命令学习 二
📑打牌 : da pai ge的个人主页 🌤️个人专栏 : da pai ge的博客专栏 ☁️宝剑锋从磨砺出,梅花香自苦寒来 ☁️运维工程师的职责:监…...
LeetCode 150, 112, 130
文章目录 150. 逆波兰表达式求值题目链接标签思路代码 112. 路径总和题目链接标签思路代码 130. 被围绕的区域题目链接标签思路代码 150. 逆波兰表达式求值 题目链接 150. 逆波兰表达式求值 标签 栈 数组 数学 思路 本题很像 JVM 中的 操作数栈,当写出以下三行…...
c++应用网络编程之五Windows常用的网络IO模型
一、Windows的网络编程 其实对开发者而言,只有Windows和其它平台。做为一种普遍流行的图形OS,其一定会与类Linux的编程有着明显的区别,这点当然也会体现在网络编程上。Windows有着自己一套相对独立的上层Socket编程模型或者说框架࿰…...

PostgreSQL 中如何解决因大量并发删除和插入操作导致的索引抖动?
🍅关注博主🎗️ 带你畅游技术世界,不错过每一次成长机会!📚领书:PostgreSQL 入门到精通.pdf 文章目录 PostgreSQL 中如何解决因大量并发删除和插入操作导致的索引抖动一、理解索引抖动二、索引抖动的影响三…...

鑫创SSS1700USB音频桥芯片USB转IIS芯片
鑫创SSS1700支持IIC初始外部编(EEPROM选项),两线串行总线(I2C总线)用于外部MCU控制整个EEPROM空间可以通过MCU访问用于主机控制同步的USB HID外部串行EEPROM(24C02~24C16)接口,用于客户特定的USB视频、PID、…...
计算机视觉发展历程
文章目录 前言一、发展历程1)、萌芽期(1960s-1970s)2)、基础发展期(1980s)3)、系统开发期(1990s-2000s)4)、深度学习兴起期(2010s)5&a…...

从安装Node到TypeScript到VsCode的配置教程
从安装Node到TypeScript到VsCode的配置教程 1.下载Node安装包, 链接 2.双击安装包,选择安装路径,如下: 3.一直点击下一步,直至安装结束即可: 这个时候,node会默认配置好环境变量,并且…...

Jackson详解
文章目录 一、Jackson介绍二、基础序列化和反序列化1、快速入门2、序列化API3、反序列化API4、常用配置 三、常用注解1、JsonProperty2、JsonAlias3、JsonIgnore4、JsonIgnoreProperties5、JsonFormat6、JsonPropertyOrder 四、高级特性1、处理泛型1.1、反序列化List泛型1.2、反…...

【算法】字符串
快乐的流畅:个人主页 个人专栏:《算法神殿》《数据结构世界》《进击的C》 远方有一堆篝火,在为久候之人燃烧! 文章目录 引言一、最长公共前缀二、最长回文子串三、二进制求和四、字符串相乘 引言 字符串题,大多数是模…...
在软件开发中正确使用MySQL日期时间类型的深度解析
在日常软件开发场景中,时间信息的存储是底层且核心的需求。从金融交易的精确记账时间、用户操作的行为日志,到供应链系统的物流节点时间戳,时间数据的准确性直接决定业务逻辑的可靠性。MySQL作为主流关系型数据库,其日期时间类型的…...

Spark 之 入门讲解详细版(1)
1、简介 1.1 Spark简介 Spark是加州大学伯克利分校AMP实验室(Algorithms, Machines, and People Lab)开发通用内存并行计算框架。Spark在2013年6月进入Apache成为孵化项目,8个月后成为Apache顶级项目,速度之快足见过人之处&…...
React Native 开发环境搭建(全平台详解)
React Native 开发环境搭建(全平台详解) 在开始使用 React Native 开发移动应用之前,正确设置开发环境是至关重要的一步。本文将为你提供一份全面的指南,涵盖 macOS 和 Windows 平台的配置步骤,如何在 Android 和 iOS…...

遍历 Map 类型集合的方法汇总
1 方法一 先用方法 keySet() 获取集合中的所有键。再通过 gey(key) 方法用对应键获取值 import java.util.HashMap; import java.util.Set;public class Test {public static void main(String[] args) {HashMap hashMap new HashMap();hashMap.put("语文",99);has…...
QMC5883L的驱动
简介 本篇文章的代码已经上传到了github上面,开源代码 作为一个电子罗盘模块,我们可以通过I2C从中获取偏航角yaw,相对于六轴陀螺仪的yaw,qmc5883l几乎不会零飘并且成本较低。 参考资料 QMC5883L磁场传感器驱动 QMC5883L磁力计…...
Frozen-Flask :将 Flask 应用“冻结”为静态文件
Frozen-Flask 是一个用于将 Flask 应用“冻结”为静态文件的 Python 扩展。它的核心用途是:将一个 Flask Web 应用生成成纯静态 HTML 文件,从而可以部署到静态网站托管服务上,如 GitHub Pages、Netlify 或任何支持静态文件的网站服务器。 &am…...
vue3 定时器-定义全局方法 vue+ts
1.创建ts文件 路径:src/utils/timer.ts 完整代码: import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...
【HarmonyOS 5 开发速记】如何获取用户信息(头像/昵称/手机号)
1.获取 authorizationCode: 2.利用 authorizationCode 获取 accessToken:文档中心 3.获取手机:文档中心 4.获取昵称头像:文档中心 首先创建 request 若要获取手机号,scope必填 phone,permissions 必填 …...

项目部署到Linux上时遇到的错误(Redis,MySQL,无法正确连接,地址占用问题)
Redis无法正确连接 在运行jar包时出现了这样的错误 查询得知问题核心在于Redis连接失败,具体原因是客户端发送了密码认证请求,但Redis服务器未设置密码 1.为Redis设置密码(匹配客户端配置) 步骤: 1).修…...

【C++特殊工具与技术】优化内存分配(一):C++中的内存分配
目录 一、C 内存的基本概念 1.1 内存的物理与逻辑结构 1.2 C 程序的内存区域划分 二、栈内存分配 2.1 栈内存的特点 2.2 栈内存分配示例 三、堆内存分配 3.1 new和delete操作符 4.2 内存泄漏与悬空指针问题 4.3 new和delete的重载 四、智能指针…...