基于C语言 --- 自己写一个三子棋小游戏

news/2024/3/29 19:19:01/文章来源:https://blog.csdn.net/m0_69455439/article/details/132003476

C语言程序设计笔记---019

  • 初阶三子棋小游戏(开源)
    • 1、arr_main.c程序大纲
    • 2、arr_game1.h
    • 3、arr_game1.c
      • 3.1、 自定义初识化函数 InitBoard( ) 和 自定义显示函数 DisPlayBoard( )
      • 3.2、 自定义玩家下棋函数 PlayerMove( )
      • 3.4、 自定义电脑下棋函数 ComputerMove( )
      • 3.5、 输赢判断函数 IsWin( )
    • 4、结束语

初阶三子棋小游戏(开源)

前言:
游戏规则:
(1)、在打印的数组棋盘里,输入坐标,下棋。
(2)、坐标格式:x(空格) y
(3)、x横坐标,y竖坐标
(4)、连成三个棋子为一线则判定胜利
如图所示
在这里插入图片描述

采用模块化编写
arr_game1.c执行主要逻辑程序
arr_game1.h存放头文件或函数声明等程序
arr_main.c放主函数逻辑程序

1、arr_main.c程序大纲

首先,从以往玩游戏的经验来谈,我们需要为游戏写一个游戏开始菜单,由玩家选择是否开始游戏。
这里可以借助所学的menu( )自定义函数,设计一个简易的菜单

void menu()
{printf("****************************************\n");printf("************** 1.play game *************\n");printf("************** 0.game over *************\n");printf("****************************************\n");
}

当我们选择1,则开始游戏;当选择0,则退出游戏。
那么就得思考,如何对玩家得选择进行判定
1.利用 scanf( ) 函数获取输入值,将获取的值,借用 do while 循环语句和switch( )选择语句,进行下一步。
2.当选择1,开始游戏则执行game()自定义,游戏主逻辑执行程序
3.当选择0,switch中 进入case 0 : 的入口,执行退出游戏,并且 do while( ),判定为0,则退出程序
4.当玩家误选择非法数值,则default : 提示玩家输入错误

int input = 0;do{menu();printf("请选择:>");scanf("%d", &input);switch (input){case 1:printf("\n*********** 三子棋游戏开始 *************\n\n");game();//游戏执行逻辑函数break;case 0:printf("\n退出游戏\n");break;default:printf("\n选择错误请重新选择\n\n");break;}} while (input);

接下来,主要阐述game( )函数内容,游戏的执行逻辑
1.当玩家选择1,开始游戏后,会显示整个空棋盘
如何实现空棋盘的显示呢?
(1)、利用所学的数组知识可以联想到,棋盘不过是由一个个字符拼接而成,在需要下棋的地方,放置空格字符进行占位,达到肉眼所见的空棋盘。
(2)、又因为棋盘是一个平面,平面由一条条线组成,线又由一个个点组成,首先得定义和初识化一个二维数组 board[ROW][COL] ,由几行几列的方式实现一个面的显示/打印
(3)、自定义初识化函数 InitBoard( ) ,自定义显示棋盘函数 DisPlayBoard( ) ;
(4)、函数的参数,可想而知,需要数组名(board) — 首元素的地址指定需操作的数组,需要行(ROW)和列(COL)指定操作的元素或坐标或地址;

void game()
{//定义棋盘大小char board[ROW][COL] = { 0 };//初识化棋盘InitBoard(board,ROW,COL);//显示棋盘DisPlayBoard(board, ROW, COL);}

2.当我们完成棋盘的初识化和显示后,那么默认玩家先下棋,电脑再下棋;
所以就得写一个玩家下棋的自定义逻辑函数和电脑下棋的自定义函数,交替下棋直到游戏判定输赢或平局
(1)、玩家下棋自定义函数 PlayerMove( ) ;
(2)、电脑下棋自定义函数 ComputerMove( ) ;
(3)、判断输赢函数 IsWin( );
那么很容易思考到,交替进行下棋,就是反复的调用玩家下棋函数和电脑下棋函数,同时不停的判断每一步棋是否达到输赢的结果。所以需要一个循环来实现,这里就用while循环,实现不停的下棋。
但是,不停的下棋,始终在死循环,那么就思考利用,break跳出循环,所以 IsWin( )函数是具备返回值的,可以由IsWin( )函数的返回值进行判定跳出。 且由于棋局均有字符进行的操作,所以确定了 IsWin( )数据类型也为 char 类型。
(4)、IsWin( )跳出循环的返回值判定
a、玩家获胜 - - - - ‘ * ’
a、电脑获胜 - - - - ‘ # ’
a、平局 - - - - ‘ Q ’
a、游戏继续 - - - - ‘ C ’

//游戏执行逻辑函数
void game()
{//定义棋盘大小char board[ROW][COL] = { 0 };//初识化棋盘InitBoard(board,ROW,COL);//显示棋盘DisPlayBoard(board, ROW, COL);//根据IsWin()返回值,判断输赢char ret = 0;//下棋while (1){//玩家下棋PlayerMove(board, ROW, COL);//判断输赢ret = IsWin(board, ROW, COL);if (ret != 'C'){break;}//显示棋局DisPlayBoard(board, ROW, COL);//电脑下棋ComputerMove(board, ROW, COL);//判断输赢ret = IsWin(board, ROW, COL);if (ret != 'C'){break;}//显示棋局DisPlayBoard(board, ROW, COL);}if (ret == '*')printf("\n玩家赢\n\n");else if (ret == '#')printf("\n电脑赢\n\n");elseprintf("\n平局\n\n");//显示棋局DisPlayBoard(board, ROW, COL);
}

arr_main.c程序大纲展示

#include "arr_game1.h"//游戏菜单
void menu()
{printf("****************************************\n");printf("************** 1.play game *************\n");printf("************** 0.game over *************\n");printf("****************************************\n");
}
//游戏执行逻辑函数
void game()
{//定义棋盘大小char board[ROW][COL] = { 0 };//初识化棋盘InitBoard(board,ROW,COL);//显示棋盘DisPlayBoard(board, ROW, COL);//根据IsWin()返回值,判断输赢char ret = 0;//下棋while (1){//玩家下棋PlayerMove(board, ROW, COL);//判断输赢ret = IsWin(board, ROW, COL);if (ret != 'C'){break;}//显示棋局DisPlayBoard(board, ROW, COL);//电脑下棋ComputerMove(board, ROW, COL);//判断输赢ret = IsWin(board, ROW, COL);if (ret != 'C'){break;}//显示棋局DisPlayBoard(board, ROW, COL);}if (ret == '*')printf("\n玩家赢\n\n");else if (ret == '#')printf("\n电脑赢\n\n");elseprintf("\n平局\n\n");//显示棋局DisPlayBoard(board, ROW, COL);
}int main()
{srand((unsigned int)time(NULL));//初识化随机值生成器int input = 0;do{menu();printf("请选择:>");scanf("%d", &input);switch (input){case 1:printf("\n*********** 三子棋游戏开始 *************\n\n");game();//游戏执行逻辑函数break;case 0:printf("\n退出游戏\n");break;default:printf("\n选择错误请重新选择\n\n");break;}} while (input);return 0;
}

2、arr_game1.h

用于存放所自定义的函数和头文件等声明的程序
通俗易懂,就不多赘述,详见代码注释的说明。

#include <stdio.h>
#include <time.h>//调用time函数,所需声明的头文件
#include <stdlib.h>//调用srand和rand函数,所需声明的头文件//宏定义棋盘大小
#define ROW 3
#define COL 3//初识化棋盘
void InitBoard(char board[ROW][COL],int row,int col);//打印棋盘
void DisPlayBoard(char board[ROW][COL], int row, int col);//玩家下棋
void PlayerMove(char board[ROW][COL], int row, int col);//电脑下棋
void ComputerMove(char board[ROW][COL], int row, int col);//判断输赢、平局
char IsWin(char board[ROW][COL], int row, int col);

3、arr_game1.c

用于存放对 arr_main.c 程序大纲做提到的函数进行封装,实现具体的功能的程序
说明:基于arr_main.c 程序大纲逻辑对代码进行讲解
注意:这里均以3*3的棋盘为例哦

3.1、 自定义初识化函数 InitBoard( ) 和 自定义显示函数 DisPlayBoard( )

首先,根据需求我们需要一个棋盘才可以正常的下棋.
如何让棋盘初识化呢?
1.根据所学的二维数组知识,便可以知道,当我们遍历二维数组的每一个元素,使得填充为空格字符,便可以由InitBoard( ) 达到效果。

//初识化棋盘
void InitBoard(char board[ROW][COL], int row, int col)
{int i = 0;for (i = 0; i < row; i++){int j = 0;for (j = 0; j < col; j++){board[i][j] = ' ';}}
}

如图所示
在这里插入图片描述
如图所示
在这里插入图片描述

2.但是,这样的棋盘全是空格字符,只有程序员可见字符空格,玩家并不知道,所以将对棋盘进行修饰美化,使得大众化。所以借助 DisPlayBoard( ) 达到修饰棋盘的效果。
这里使用一些分隔符,将棋盘划分出,三组,并且数组元素加分隔符为一组。

//打印棋盘
void DisPlayBoard(char board[ROW][COL], int row, int col)
{int i = 0;int j = 0;for (i = 0; i < row; i++){for (j = 0; j < col; j++){printf(" %c ",board[i][j]);printf("|");}printf("\n");//每行打印完换行}printf("\n");
}

如图所示:
在这里插入图片描述

但是目前的效果达不到理想的棋盘效果,导致最后一组的分隔符,也被打印了出来。
所以思考可知:由 if (j < col - 1) 进行判断让最后一行的分割符被约束不打印出来。
然后打印完一行,换行一次
同理,每一列打印完,使用 if (i < row - 1) 使得最后一列的分隔符不被打印出来
所以优化后得到以下代码:

//打印棋盘
void DisPlayBoard(char board[ROW][COL], int row, int col)
{int i = 0;int j = 0;for (i = 0; i < row; i++){for (j = 0; j < col; j++){printf(" %c ",board[i][j]);if(j < col-1)printf("|");}printf("\n");//每行打印完换行if (i < row - 1){int j = 0;for (j = 0; j < col; j++){printf("---");if (j < col - 1)printf("|");}printf("\n");//每行打印完换行}}printf("\n");
}

如图所示
在这里插入图片描述
棋盘显示最终效果如图所示
在这里插入图片描述

3.2、 自定义玩家下棋函数 PlayerMove( )

1.首先,我们下棋,需要给棋子一个坐标,并且需要以作为玩家的角度,思考坐标的取值范围。因为玩家不一定知道数组的下标从0开始,所以我们定义 x,y 坐标变量,使得 if (x >= 1 && x <= row && y >= 1 && y <= col )//符合玩家下棋坐标的逻辑
2.当我们坐标符合了范围,还得判定玩家输入错误坐标超出范围时,给予提示信息,超出范围,重新输入。
3.当玩家输入坐标范围正确,但是此时的坐标处已经被上一步棋子占用时,也得提示重新输入。

//玩家下棋
void PlayerMove(char board[ROW][COL], int row, int col)
{//定义下棋的坐标int x = 0;int y = 0;while (1){printf("玩家下,请输入棋子的坐标:>");scanf("%d %d", &x, &y);if (x >= 1 && x <= row && y >= 1 && y <= col )//符合玩家下棋坐标逻辑{//判断下棋的坐标是否被占用if (board[x - 1][y - 1] == ' '){board[x - 1][y - 1] = '*';break;}else{printf("\n坐标被占用,请重新输入坐标\n");}}else {printf("\n坐标超出棋盘范围,请重新输入棋子,坐标\n");}}
}

如图所示
在这里插入图片描述

3.4、 自定义电脑下棋函数 ComputerMove( )

1.下棋的逻辑与玩家如出一辙,不多赘述。
2.这里强调一下,电脑如何实现产生随机的坐标下棋?
首先,在之前的篇章链接: link 里提到的srand函数与rand函数的应用原理相同。通过初识化srand随机值生成器函数,提供变化的随机值范围,给rand随机值函数,将rand()%row 和 rand()%col,就可以得到符合范围的数组元素坐标。
然后根据srand的参数要求,放一个时刻变化的数据,这里就用到了time时间转换函数,相互作用即可。

查阅资料文档的具体参数:
void srand( unsigned int seed ); 头文件 <stdlib.h>
int rand(void ); 头文件 <stdlib.h>
time_t time( time_t *timer ); 头文件 <time.h>

所以得到:

srand((unsigned int)time(NULL));
x = rand() % row;
y = rand() % col;

注意:srand只需要整个工程初识化一次即可

//电脑下棋
void ComputerMove(char board[ROW][COL], int row, int col)
{//定义棋子坐标int x = 0;int y = 0;printf("电脑下棋:>\n");while (1){x = rand() % row;y = rand() % col;if (board[x][y] == ' '){board[x][y] = '#';break;}}
}

如图所示
在这里插入图片描述

3.5、 输赢判断函数 IsWin( )

由arr_main.c程序大纲提到的思路可知:
如何判定输赢:
玩家赢 — ‘*’
电脑赢 — ‘#’
平局 ---- ‘Q’
继续游戏 — ‘C’
所以可以明确 IsWin( ) 的返回值类型为char字符型

根据三子棋的游戏规则,三个棋子连城一线即可判定获胜,所以有:
(1)、一行三个棋子
(2)、一列三个棋子
(3)、对角线三个棋子
那么如何判定平局呢?
顾名思义,当我们的棋盘被占用完,还是没有连成一线,则判定为平局。
这里利用 int IsFull( )函数,遍历判断数组每个元素是否已用完即可,当返回0,则仍然有空位,继续游戏;当返回1,则所有元素均被占用且未连成一线,判定为平局。


//判断输赢
//玩家赢 --- '*'
//电脑赢 --- '#'
//平局 ---- 'Q'
//继续游戏 --- 'C'
int Isfull(char board[ROW][COL], int row, int col)
{int i = 0;int j = 0;for (i = 0; i < row; i++){for (j = 0; j < col; j++){if (board[i][j] == ' '){return 0;//棋盘没满}}}return 1;//满了
}char IsWin(char board[ROW][COL], int row, int col)
{int i = 0;//判断三行,均相同for (i = 0; i < row; i++){if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][1] != ' '){return board[i][1];//*/#}}//判断三列,均相同for (i = 0; i < col; i++){if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[1][i] != ' '){return board[1][i];//#/*}}//判断对角线,均相同(三子棋)//主对角if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] != ' '){return board[1][1];//*/#}//副对角if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[1][1] != ' '){return board[1][1];//*/#}//判断平局int ret = Isfull(board, ROW, COL);if (ret == 1)//棋盘无空位,平局{return 'Q';}//以上所有情况不满足时,继续游戏return 'C';
}

输赢结果如图所示
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4、结束语

相信通过这样一个三子棋的小游戏,更具掌握了对数组的操作以及对自定义函数的深刻认识;
如果觉着文章对您有所帮助,请不要吝啬的一赞三连哦,谢谢阅读,不足之处还请多多指教。

三子棋源码获取链接: 三子棋

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

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

相关文章

飞致云开源社区月度动态报告(2023年7月)

自2023年6月起&#xff0c;中国领先的开源软件公司FIT2CLOUD飞致云将以月度为单位发布《飞致云开源社区月度动态报告》&#xff0c;旨在向广大社区用户同步飞致云旗下系列开源软件的发展情况&#xff0c;以及当月主要的产品新版本发布、社区运营成果等相关信息。 飞致云开源大…

c++ | 动态链接库 | 小结

//环境 linux c //生成动态链接库 //然后调用动态链接库中的函数//出现的问题以及解决//注意在win和在linux中调用动态链接库的函数是不一样的//在要生成链接库的cpp文件中比如以后要调用本文件中的某个函数&#xff0c;需要extern "c" 把你定的函数“再封装”避免重…

Postgresql源码(109)并行框架实例与分析

1 PostgreSQL并行参数 系统参数 系统总worker限制&#xff1a;max_worker_processes 默认8 系统总并发限制&#xff1a;max_parallel_workers 默认8 单Query限制&#xff1a;max_parallel_workers_per_gather 默认2 表参数限制&#xff1a;parallel_workers alter table tbl …

4090Ti被取消,NVIDIA还要推出新“甜品卡“

不知不觉距离 NVIDIA RTX 40 系显卡发布已快一年&#xff0c;4090 到 4060 从旗舰到甜品也都差不多了。 不过每个男孩子都想要的礼物 - RTX 4090 Ti &#xff0c;至今仅在春晚发布。 从核心架构上来看&#xff0c;RTX 4090 上的 AD 102-300 也确实不是完全体。 仅拥有144组 S…

适配器模式与装饰器模式对比分析:优雅解决软件设计中的复杂性

适配器模式与装饰器模式对比分析&#xff1a;优雅解决软件设计中的复杂性 在软件设计中&#xff0c;我们常常面临着需要将不同接口或类协调工作的情况&#xff0c;同时还要满足灵活性和可扩展性的需求。为了应对这些挑战&#xff0c;适配器模式和装饰器模式应运而生&#xff0c…

【计算机视觉】BLIP:源代码示例demo(含源代码)

文章目录 一、Image Captioning二、VQA三、Feature Extraction四、Image-Text Matching 一、Image Captioning 首先配置代码&#xff1a; import sys if google.colab in sys.modules:print(Running in Colab.)!pip3 install transformers4.15.0 timm0.4.12 fairscale0.4.4!g…

linux备份与还原系统(类似window上ghost备份还原)

一、摘要 在linux上进行了几年的开发工作 &#xff08;qt ros&#xff09; 突然发现&#xff0c;现在有公司硬件、笔记本台式机一台占一个系统&#xff0c;导致硬件太浪费&#xff0c;又不能用虚拟机&#xff08;有时候要链接硬件必须物理机&#xff09;怎么办&#xff1f; 二…

Spring框架中的Bean的各种加载方式

大家好&#xff0c;这里向大家主要介绍Spring框架以及SpringBoot框架中的Bean的各种加载方式&#xff0c;有时候我们的学习&#xff0c;就是单纯为了工作效率而作为工具使用&#xff0c;于是乎&#xff0c;往往忽略了其最重要的一点&#xff0c;那就是底层原理&#xff01;所以…

什么是MES,什么是WMS,MES与WMS有什么区别?

什么是MES&#xff1f;什么是WMS&#xff1f;以及MES&#xff08;制造执行系统&#xff09;与WMS&#xff08;仓库管理系统&#xff09;的区别&#xff0c;下面分为三块跟大家详细讲解。 一、什么是MES&#xff1f; 1、概念&#xff1a; MES&#xff08;英文全称&#xff1a…

蓝桥杯2018省赛全球变暖dfs

全球变暖 问题描述格式输入格式输出样例输入样例输出评测用例规模与约定解析参考程序 问题描述 格式输入 格式输出 输出一个整数 样例输入 样例输出 1 评测用例规模与约定 最大运行时间&#xff1a;1s最大运行内存: 256M 解析 采用dfs的方式进行搜索&#xff0c;首先输入地…

有点慌,新公司项目构建用的Gradle

入职新公司&#xff0c;构建项目的工具用的gradle&#xff0c;以前没用过&#xff0c;看到一个build.gradle&#xff0c;点进去&#xff0c;心里一句我曹&#xff0c;这写的都是些什么玩意&#xff0c;方得一批&#xff0c;赶紧去补了下课。 好吧&#xff0c;先学点语法&#…

HTML+CSS前端 动态响应用户登录界面

day2 知道了动态响应设计的概念&#xff0c;在原先登录界面的基础上进行升级 动态响应 由于前端页面需要在不同大小和分辨率的屏幕上显示&#xff0c;所以需要它具有动态适应的特性。 常用的方式是在 css 文件中用 media 动态查询&#xff0c;同时使用 flex 弹性布局。 例如&a…

Java集合篇

前言&#xff1a;笔者参考了JavaGuide、三分恶等博主的八股文&#xff0c;结合Chat老师和自己的理解&#xff0c;整理了一篇关于Java集合的八股文。希望对各位读者有所帮助~~ 引言 常见集合有哪些&#xff1f; Java集合相关类和接口都在java.util包中&#xff0c;按照其存储…

国内外遥感数据处理软件对比

1.国内遥感数据处理软件概况 1.1北京航天宏图信息技术股份有限公司 1.1.1公司简介 航天宏图信息技术股份有限公司成立于2008年,是国内遥感和北斗导航卫星应用服务商,致力于卫星应用软件国产化、行业应用产业化、应用服务商业化,研发并掌握了具有完全自主知识产权的PIE(Pix…

Python源码:Tkinter组件布局管理的3种方式

Tkinter组件布局管理可以使用pack()方法、grid()方法和place()方法。pack()方法将组件放置在窗口中&#xff0c;grid()方法将组件放置在网格布局中&#xff0c;place()方法将组件放置在指定位置。 01使用pack()方法布局&#xff1a; 在Tkinter中&#xff0c;pack方法用于将控…

【Git系列】Git到远程仓库

&#x1f433;Git到远程仓库 &#x1f9ca;1. github账号注册&#x1f9ca;2. 初始化本地仓库&#x1f9ca;3. 创建GitHub远程仓库&#x1f9ca;4. 给本地仓库起别名&#x1fa9f;4.1 查看远程库的连接地址&#x1fa9f;4.2 起别名 &#x1f9ca;5. git推送操作&#x1f9ca;6.…

WAF绕过-信息收集篇

WAF绕过主要集中在信息收集&#xff0c;漏洞发现&#xff0c;漏洞利用&#xff0c;权限控制四个阶段。 1、什么是WAF&#xff1f; Web Application Firewall&#xff08;web应用防火墙&#xff09;&#xff0c;一种公认的说法是“web应用防火墙通过执行一系列针对HTTP/HTTPS的安…

【模仿学习】:离线和在线模仿

一、说明 模仿学习&#xff08;Imitation Learning &#xff09;是机器学习的一种&#xff0c;代理通过观察和模仿专家的行为来学习。在这种方法中&#xff0c;为代理提供了一组所需行为的演示或示例&#xff0c;并通过尝试复制专家的行为来学习输入观察和输出操作之间的映射。…

安装win版本的neo4j(2023最新版本)

安装win版本的neo4j 写在最前面安装 win版本的neo4j1. 安装JDK2.下载配置环境变量&#xff08;也可选择直接点击快捷方式&#xff0c;就可以不用配环境了&#xff09;3. 启动neo4j 测试代码遇到的问题及解决&#xff08;每次环境都太离谱了&#xff0c;各种问题&#xff09;连接…

【linux 结束pts/1踢人踢除另一个终端】

centos7上误执行了个命令&#xff0c;导致一直刷屏&#xff0c;强制CTRLC无法正常退出&#xff0c;一直出现如下&#xff1a; 网上搜索通过ctrlD&#xff0c;q均无法正常退出&#xff0c; 不想强行关掉&#xff0c;通过&#xff1a;who命令查看均用户&#xff1a; who mshns…