人生第一款人机对战小程序——三子棋(五千字无敌详解还有图)

2023-10-30 09:59

本文主要是介绍人生第一款人机对战小程序——三子棋(五千字无敌详解还有图),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

🍉说在前面

🍉程序整体思路

🍉定义一个头文件

🍉来一个漂亮的界面

🍉main函数的大结构

🍉Game()函数的大结构

🍇给二维数组初始化

🍇打印一个棋盘

🍇玩家下棋部分

🍇判断输赢的函数

🍇《关于我将这些函数整合在一起这些事》

🍉写在最后


🍉说在前面

五一小长假之后还是要继续努力了呢,来个鸡汤🍜

🍉程序整体思路

我们要写的是一个三子棋小游戏,也就是我们常说的井字棋。接下来盘一盘,我们需要那些部分来写成这个小程序。

  1. 下棋需要棋盘——打印一个棋盘
  2. 下棋需要两个人——分别实现玩家和电脑的下棋操作
  3. 需要记录我们下的棋子——利用一个二维字符数组记录
  4. 需要有人判断谁赢了——写一个函数判断赢还是输还是平局
  5. 写的程序需要好看——编写一个菜单界面

这就是我们在写这个小游戏前想到需要写的东西,分开成每个部分其实都不难(循环、二位数组、判断语句)就是小小的组合了一下。

这里我们采用的是分头文件、源文件的写法,顺便附上教程链接:程序的分文件写法及注意事项

 在这里我构建了三个文件,game.h文件存放函数的声明、game.c函数存放函数的定义、三子棋.c文件是程序的实现必不可少的部分。(这里不懂也不影响往下面看的)

🍉定义一个头文件

#include<stdio.h>
#include<time.h>//下文解释,实现人机将会用到
#include<stdlib.h>//实现人机部分用到
#define ROW 3//定义棋盘的行
#define COL 3//定义棋盘的列
void menu();//给菜单的定义
void Game();//给游戏函数的定义
void InitBoard(char Broad[ROW][COL], int row, int col);//定义初始化储存二维数组的函数
void BoardDis(char broad[ROW][COL],int row,int col);//定义打印棋盘
void Player(char broad[ROW][COL], int row, int col);//定义玩家下棋的函数
void Com(char broad[ROW][COL],int row, int col);//电脑下棋的函数
char Is_win(char broad[ROW][COL], int row, int col);//判断那方获胜的函数

 在这个文件里我们定义了我们写这个程序将会用到的定义,这样集中起来的一个好处是,这样程序的各个函数将会更直观的呈现出来。这里面的函数被定义出来后,将会在Game.c中声明

这个定义不是我一口气就想出来的,是一边写一边补充的。

 

🍉来一个漂亮的界面

void menu()//游戏菜单
{printf("*******************\n");printf("***** 1. play *****\n");printf("***** 0. exit *****\n");printf("*******************\n");
}

这一部分的作用是打印出一个游戏菜单。 这个函数写在game.c文件中。

 

🍉main函数的大结构

int main()
{srand((usigned int)time(NULL));//这个在电脑下棋那一段会用到,这里设置了一个随机数的种子int op = 0;do{menu();//菜单函数printf("请选择\n");scanf("%d", &op);switch (op){case 1:Game()//这个函数来将游戏实现break;case 0:printf("正在退出游戏\n");break;default:printf("输入错误,请重新输入\n");break;}} while (op);//当你选择了0,循环自动结束return 0;
}

我们的main函数值负责是否进行游戏的功能。

这里用到了switch选择结构来实现menu()函数的选择,并加了一个循环来实现重复游戏的效果。玩家在进行了一局游戏之后可以继续选择是否继续玩这个游戏。

🍉Game()函数的大结构

void Game()
{char Board[ROW][COL]={0};//储存旗子的二维数组//给二维数组初始化的函数//打印棋盘while(1){//玩家下棋的函数//判断输赢的函数//打印棋盘的函数//电脑下棋的函数//判断输赢的函数//打印棋盘的函数}
}

在这个函数中,我们会实现三子棋游戏的游戏部分。玩家下棋、电脑下棋、棋盘打印、判断输赢都会整合在这里。

🍇给二维数组初始化

void InitBoard(char board[ROW][COL], int row, int col)
{int i = 0; int j = 0;for (i; i < row; i++){for (j=0; j < col; j++){board[i][j] = ' ';}}return;}

在开始下棋前,我们将储存棋子的二维数组初始化为‘ ’(空格),这样就可以实现下面这样的效果。

 

 每次打印出来的在我们的棋盘上其实打印的是一个空格。

🍇打印一个棋盘

我们来观察这个棋盘,实质上它是两个空格夹着我们的棋子的如下图的位置

 

void BroadDis(char board[ROW][COL], int row, int col)//我们需要知道要摆放什么棋子,以及棋盘的大小
{int i = 0, j = 0;for (i; 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)for (j=0;j<col;j++){printf("---");if (j < col - 1)printf("|");}printf("\n");}
}

 

if (j < COL-1)printf("|");

这一部分是一个巧妙的设计,为了防止多打一个‘|’,我们在最后一次循环阻止了打印‘|’。下面这一部分也是同理:

if(i<ROW-1)for (j=0;j<col;j++){printf("---");if (j < col - 1)printf("|");}

有些同学很好奇为什么我们要在头文件宏定义一个ROW、COL,这样我们想改变棋盘大小的时候只需要改变头文件里面的数据就行了,比如:我把ROW、COL改成了10,棋盘就会变成下面这样:

这么大写个五子棋都不过分吧😁。

🍇玩家下棋部分

开始下棋咯!

void Player(char broad[ROW][COL], int row, int col)//我们要用到二维数组来储存棋子
{int x = 0, y = 0;printf("玩家下棋\n");while (1)//在玩家棋子下位置前循环不会结束{scanf("%d %d", &x, &y);if (x <= row && y <= col && x > 0 && y > 0)//我们的棋子不能越界{if (broad[x - 1][y - 1] != ' ')//我们的棋子不能占用已经棋子的位置printf("该位置被占用\n");else{broad[x-1][y-1] = '#';break;//玩家位置下对了,训话结束}}elseprintf("输入的位置不合法\n");}
}

几乎每一行都进行了标注,贼良心🎉。

🍇电脑下棋了

电脑下棋,我们这里采用随机数的方式来模拟电脑下棋。随机数的操作方法可以看看这篇文章的前小半部分(极简随机数)。

void Com(char broad[ROW][COL], int row, int col)
{printf("电脑下棋\n");int x = 0, y = 0;while (1){x = rand() % ROW;//给下的行数赋一个值(取余是为了控制x的大小在0~2,下同)y = rand() % COL;//给下的列数赋一个值if (broad[x][y] == ' ' ){broad[x][y] = '*';//电脑下的棋子是'*'break;//电脑下对了才退出循环}}return;
}

 每下一步,我们就打印一次棋盘,现在可以来看看效果了:

 

🍇判断输赢的函数

我这里的思路是写一个函数判断输赢。

  • 玩家获胜——函数返回’#‘
  • 电脑获胜——函数返回’*‘
  • 平局——函数返回’P‘
  • 棋盘还没下满——函数返回’N‘

那我们这次定义的函数就需要是char类型的了。

char Is_win(char broad[ROW][COL], int row, int col)//把我们的棋盘导进去判断输赢
{//在行上判断有没有人赢了int i = 0, j = 0;for (i = 0; i < ROW; i++){//只要第一个等于第二个、第二个等于第三个就可以判定为赢,且这三个不能是空格,下同if (broad[i][0] == broad[i][1] && broad[i][1] == broad[i][2] && broad[i][0] != ' ')return broad[i][0];//谁赢了就返回谁的棋子}//在列上判断输赢for (j = 0; j < COL; j++){if (broad[0][j] == broad[1][j] && broad[1][j] == broad[2][j] && broad[0][j] != ' '){return broad[0][j];}}//对角线情况比较少,我就直接列出来了if (broad[0][0] == broad[1][1] && broad[1][1] == broad[2][2]&&broad[1][1]!=' ' || broad[0][2] == broad[1][1] && broad[1][1] == broad[2][0] && broad[1][1] != ' '){return broad[1][1];}int leap = 1;//假设棋盘满了//是否满了,如果我们的棋盘没满就还需要继续比赛for (i = 0; i < row; i++){for (j = 0; j < col; j++){if (broad[i][j] == ' '){leap = 0;break;}}}if (leap == 1)//棋盘满了就返回Preturn 'P';return 'N';
}

 这里也有注释,应该能看懂的,要是还没看懂的话,随时私聊我🌹。

🍇《关于我将这些函数整合在一起这些事》

void Game()
{char key = {0};//存放函数返回值的变量char Broad[ROW][COL] = { 0 };//定义一个存放棋子的二维数组InitBroad(Broad, ROW, COL);//数组初始化BroadDis(Broad,ROW,COL);//打印棋盘while (1)//一直下棋,知道分出胜负或平局{Player(Broad, ROW, COL);//玩家下棋BroadDis(Broad, ROW, COL);//打印棋盘key=Is_win(Broad, ROW, COL);//判断下棋结果if (key != 'N')//棋盘没满的话我们继续下棋break;//函数要是返回了N以外的字符我们停止循环Com(Broad,ROW,COL);BroadDis(Broad, ROW, COL);key=Is_win(Broad, ROW, COL);if (key != 'N')break;}//判断一下谁赢了if (key == '#')printf("玩家获胜\n");else if (key == '*')printf("电脑获胜\n");elseprintf("平局\n");return;
}

再次良心的注释了一切。

🍉写在最后

写博客写道一半,突发奇想,拉着室友去外面骑小电驴逛街去了,溜达完回来写完博客已经凌晨一点了哈哈😂,家人们晚安。

尼尔叔叔:你自己试试做吧。

 

 

加油!!评论评论鼓励鼓励博主吧 

 

(小福利) 

这篇关于人生第一款人机对战小程序——三子棋(五千字无敌详解还有图)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/307140

相关文章

uniapp接入微信小程序原生代码配置方案(优化版)

uniapp项目需要把微信小程序原生语法的功能代码嵌套过来,无需把原生代码转换为uniapp,可以配置拷贝的方式集成过来 1、拷贝代码包到src目录 2、vue.config.js中配置原生代码包直接拷贝到编译目录中 3、pages.json中配置分包目录,原生入口组件的路径 4、manifest.json中配置分包,使用原生组件 5、需要把原生代码包里的页面修改成组件的方

Java面试八股之怎么通过Java程序判断JVM是32位还是64位

怎么通过Java程序判断JVM是32位还是64位 可以通过Java程序内部检查系统属性来判断当前运行的JVM是32位还是64位。以下是一个简单的方法: public class JvmBitCheck {public static void main(String[] args) {String arch = System.getProperty("os.arch");String dataM

十四、观察者模式与访问者模式详解

21.观察者模式 21.1.课程目标 1、 掌握观察者模式和访问者模式的应用场景。 2、 掌握观察者模式在具体业务场景中的应用。 3、 了解访问者模式的双分派。 4、 观察者模式和访问者模式的优、缺点。 21.2.内容定位 1、 有 Swing开发经验的人群更容易理解观察者模式。 2、 访问者模式被称为最复杂的设计模式。 21.3.观察者模式 观 察 者 模 式 ( Obser

【操作系统】信号Signal超详解|捕捉函数

🔥博客主页: 我要成为C++领域大神🎥系列专栏:【C++核心编程】 【计算机网络】 【Linux编程】 【操作系统】 ❤️感谢大家点赞👍收藏⭐评论✍️ 本博客致力于知识分享,与更多的人进行学习交流 ​ 如何触发信号 信号是Linux下的经典技术,一般操作系统利用信号杀死违规进程,典型进程干预手段,信号除了杀死进程外也可以挂起进程 kill -l 查看系统支持的信号

Jitter Injection详解

一、定义与作用 Jitter Injection,即抖动注入,是一种在通信系统中人为地添加抖动的技术。该技术通过在发送端对数据包进行延迟和抖动调整,以实现对整个通信系统的时延和抖动的控制。其主要作用包括: 改善传输质量:通过调整数据包的时延和抖动,可以有效地降低误码率,提高数据传输的可靠性。均衡网络负载:通过对不同的数据流进行不同程度的抖动注入,可以实现网络资源的合理分配,提高整体传输效率。增

一道经典Python程序样例带你飞速掌握Python的字典和列表

Python中的列表(list)和字典(dict)是两种常用的数据结构,它们在数据组织和存储方面有很大的不同。 列表(List) 列表是Python中的一种有序集合,可以随时添加和删除其中的元素。列表中的元素可以是任何数据类型,包括数字、字符串、其他列表等。列表使用方括号[]表示,元素之间用逗号,分隔。 定义和使用 # 定义一个列表 fruits = ['apple', 'banana

Steam邮件推送内容有哪些?配置教程详解!

Steam邮件推送功能是否安全?如何个性化邮件推送内容? Steam作为全球最大的数字游戏分发平台之一,不仅提供了海量的游戏资源,还通过邮件推送为用户提供最新的游戏信息、促销活动和个性化推荐。AokSend将详细介绍Steam邮件推送的主要内容。 Steam邮件推送:促销优惠 每当平台举办大型促销活动,如夏季促销、冬季促销、黑色星期五等,用户都会收到邮件通知。这些邮件详细列出了打折游戏、

探索Elastic Search:强大的开源搜索引擎,详解及使用

🎬 鸽芷咕:个人主页  🔥 个人专栏: 《C++干货基地》《粉丝福利》 ⛺️生活的理想,就是为了理想的生活! 引入 全文搜索属于最常见的需求,开源的 Elasticsearch (以下简称 Elastic)是目前全文搜索引擎的首选,相信大家多多少少的都听说过它。它可以快速地储存、搜索和分析海量数据。就连维基百科、Stack Overflow、

常用MQ消息中间件Kafka、ZeroMQ和RabbitMQ对比及RabbitMQ详解

1、概述   在现代的分布式系统和实时数据处理领域,消息中间件扮演着关键的角色,用于解决应用程序之间的通信和数据传递的挑战。在众多的消息中间件解决方案中,Kafka、ZeroMQ和RabbitMQ 是备受关注和广泛应用的代表性系统。它们各自具有独特的特点和优势,适用于不同的应用场景和需求。   Kafka 是一个高性能、可扩展的分布式消息队列系统,被设计用于处理大规模的数据流和实时数据传输。它

Linux中拷贝 cp命令中拷贝所有的写法详解

This text from: http://www.jb51.net/article/101641.htm 一、预备  cp就是拷贝,最简单的使用方式就是: cp oldfile newfile 但这样只能拷贝文件,不能拷贝目录,所以通常用: cp -r old/ new/ 那就会把old目录整个拷贝到new目录下。注意,不是把old目录里面的文件拷贝到new目录,