金鞍玉勒寻芳客,未信我庐别有春。这篇文章主要讲述C语言三子棋小游戏开发详细解析相关的知识,希望能为你提供帮助。
三子棋游戏
游戏介绍:
游戏规则:
文章图片
项目要求:
创建一个显目,在显目中添加三个源代码,一个用来执行主函数、一个用来定义函数和常量、一个用来完成函数功能
文章图片
文章图片
执行主函数:Chess.c
用来定义函数和常量:sanziqi.h(就类似库函数那样,到时候我们需要引用它,所以后缀是 .h )
用来完成函数功能:hanshu.c
打印菜单函数:
这一块没什么可以说的,主要就是输出函数的使用,比较简单
void menu()
{
printf("*****************************\\n");
printf("*****1.play0.exit*****\\n");
printf("*****************************\\n");
}
菜单写出来了,是不是还需要用户来选择,既然用户有选择,那就需要东西来接收用户输入的数据,并进行判断,这里我们就可以使用 switch 分支语句来接收并执行用户的要求
void choic()//游戏入口
{
int input = 0;
srand((unsigned int)time(NULL));
//生成随机数种子
do
{
printf("请选择:>
");
scanf("%d",&
input);
switch(input)
{
case 1:
printf("游戏开始\\n");
game();
break;
case 0:
printf("游戏结束\\n");
break;
default:
printf("输入错误\\n");
break;
}
}while(input);
}
分析:那个 srand((unsigned int)time(NULL)); 现在先不用去理他,看其他部分代码,那一句下面会分析他的作用;switch 里面的 game()是游戏执行的主体函数,后面也会讲到;有人可能不理解为什么要用循环,循环的条件为什么用 input ,很简单,我们平时玩游戏的时候,是不是每完玩一局,都会出现再来一把或者退出这样的界面让你选择,这就是用了循环,如果没有这个循环,你玩完一把之后程序就自动结束了,那又要重开;至于 input ,while( ) 的条件为真(非零数)则执行,为假(0)则不执行,input 又是接收用户数据的变量,而在菜单中退出我们给的对应值是 0,开始游戏给的对应值是非零值 1 只要用户不退出那么 while 的条件就为真,循环执行;退出的话,那 input = 0,条件为假,循环不执行,程序结束。
前面的代码写完后,基本的界面要求就完成了,接下来就要写游戏的主体部分了
执行游戏主体部分的函数:game( )
- 绘制棋盘,三子棋一共有九个格子,横三行,竖三列,所以这里要用到数组来保存棋盘的数据
创建棋盘,让计算机知道有这么块位置
char board[ROW][COL] = {0};
ROW:表示行
COL :表示列
这两个定义成了常量
#define ROW 3//棋盘行数
#define COL 3//棋盘列数
第二步:
初始化棋盘
clean(board,ROW,COL);
定义函数:
void clean(char board[ROW][COL],int row,int col);
row、col 同样也是代表行和列
函数功能实现:
void clean(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] = \' \';
}
}
}
前面我们定义数组的时候给的是 0 ,但是真正要初始化的时候我们要使每个格子里面放的是空格
第三步:
绘制出棋盘的边格
draw(board,ROW,COL);
函数定义:
void draw(char board[ROW][COL],int row,int col);
函数功能实现:
void draw(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 ++)
{
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");
}
}
}
效果图:
文章图片
注意:我们的格子顶上下面左边和右边都是没有格子的,所以在写代码时要注意
棋盘都画完了,那接下来就要开始下棋了,这里为了方便,我们就让玩家先,其实如果要公平的话也很简单,我们可以写一个猜拳的函数,来判断一下,但这里图个简单就不写了,之后的有写小游戏会涉及到,到那时候在讲
第四步:
玩家落子
函数定义:
void playermove(char board[ROW][COL],int row,int col);
函数功能实现:
void playermove(char board[ROW][COL],int row,int col)//下棋部分
{
int x = 0;
int y = 0;
printf("玩家走:>
\\n");
while(1)
{
printf("请输入落子位置:>
\\n");
scanf("%d%d",&
x,&
y);
if(x >
= 1 &
&
x <
= row &
&
y >
= 1 &
&
y <
= col)// x y 可以取 1 是从用户的角度来看,1 实际是 0
{
if(board[x-1][y-1] == \' \')
{
board[x-1][y-1] = \'*\';
break;
//落子完成后跳出循环打印 *
}
else
{
printf("该坐标已被占用\\n");
}
}
else
{
printf("不在棋盘内请重新输入\\n");
}
}
}
分析:board[x-1][y-1] 减一这里有人会不明白,在编程语言中数组下标是从零开始的,那么正常的每学过编程的人他不知道,他们就认为是从一开始,那我们就要满足用户的需求了,在它输入了落子的坐标后,程序自动帮他们减一找到对应的坐标;
* 号代表用户的落子标志
第五步:
电脑落子
【C语言三子棋小游戏开发详细解析】函数定义:
void computermove(char board[ROW][COL],int row,int col);
函数功能实现:
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;
}
}
}
分析:我们完成的是简单的三子棋游戏,不是向那些比较牛的棋类游戏还有棋谱,电脑知道如何判断你的位置做出相应对策的那种,所以我们只需要电脑随机落子就行,当然要满足落子的位置是空位置,随机落子,当然需要产生随机数,还要随机种子,前面写菜单那里就有涉及到,这个位置来详细讲解:srand((unsigned int)time(NULL));
我们在使用rand()的时候生成一个随机数,但是在用rand的时候本身又需要一个随机数(有的套娃的感觉),那怎么办?
文章图片
我们知道,电脑的时间是不是在每分每秒都在变动,这里我们就提到一个 “时间戳” 的概念,时间戳:当前计算机的时间减去计算机的起始时间(1970.1.1.0:0:0)
文章图片
这样我们就可以用时间戳来设置随机数的生成起点,C语言又是如何获取时间戳的
文章图片
time函数:
文章图片
- time的参数是个指针,我们不需要所以把参数写成(NULL)空指针(头文件:time.h)
- 将得到的时间戳强制转换为整型(unsigned int)(其实 time 本身是个长整型,是对其他函数进行的重命名,这里不多讲,了解一下)
完整的下棋代码(写在game()函数里)
while(1)
{
playermove(board,ROW,COL);
draw(board,ROW,COL);
//打印棋盘显示 * ret = win(board,ROW,COL);
//判断玩家是否赢
if(ret != \'C\')
{
break;
} computermove(board,ROW,COL);
draw(board,ROW,COL);
ret = win(board,ROW,COL);
//判断电脑是否赢
if(ret != \'C\')
{
break;
}
}
//win 函数会返回四种状态
//1.玩家赢‘* ’
//2.电脑赢‘# ’
//3.平局‘Q ’
//4.继续‘C ’
下面会讲到 win 函数,他的作用是判断输赢
最后一步:
1.检查棋盘是否满了
int pingju(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;
//满了
}
分析:这里草率了一点,函数名就叫做 “平局” 因为棋盘要是满了还没分出胜负那就是平局了,没毛病哈哈,用户和电脑每下一步都要执行一次这个函数判断棋盘是否满了,还要判断一下是否有哪一方获胜。
文章图片
2.判断输赢
一共有八中情况可能赢
文章图片
文章图片
文章图片
char win(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][0] != \' \')
{
return board[i][0];
}
}
for(i = 0;
i <
row;
i ++)//竖三列
{
if(board[0][i] == board[1][i] &
&
board[1][i] == board[2][i] &
&
board[0][i] != \' \')
{
return board[0][i];
}
}
//对角线
if(board[0][0] == board[1][1] &
&
board[1][1] == board[2][2] &
&
board[0][0] != \' \')
{
return board[0][0];
}
if(board[0][2] == board[1][1] &
&
board[1][1] == board[2][0] &
&
board[0][2] != \' \')
{
return board[0][2];
}
//判断平局
if(1 == pingju(board,ROW,COL))
{
return \'Q\';
}
return \'C\';
//这里 else 可写可不写
}
分析:return board[i][0] ; return board[0][i] ; return board[0][0] ; return board[0][2] ,因为如果条件成立就代表赢了,且字符都一样,我就取了第一个判断的字符作为返回值
接收 win 的返回值并判断获胜方
if(ret == \'*\')
{
printf("玩家获胜\\n");
}
else if(ret == \'#\')
{
printf("电脑获胜\\n");
}
else
{
printf("平局\\n");
}
程序的完整代码
Chess.c
#include <
stdlib.h>
#include "sanziqi.h"void menu()//菜单
{
printf("*****************************\\n");
printf("*****1.play0.exit*****\\n");
printf("*****************************\\n");
}void game()//游戏主体
{
int ret = 0;
//接收 win 的返回值
char board[ROW][COL] = {0};
//保存棋盘上的数据
clean(board,ROW,COL);
//初始化棋盘数据
draw(board,ROW,COL);
//绘制棋盘
//玩家落子
while(1)
{
playermove(board,ROW,COL);
draw(board,ROW,COL);
//打印棋盘显示 * ret = win(board,ROW,COL);
//判断玩家是否赢
if(ret != \'C\')
{
break;
}computermove(board,ROW,COL);
draw(board,ROW,COL);
ret = win(board,ROW,COL);
//判断电脑是否赢
if(ret != \'C\')
{
break;
}
}
if(ret == \'*\')
{
printf("玩家获胜\\n");
}
else if(ret == \'#\')
{
printf("电脑获胜\\n");
}
else
{
printf("平局\\n");
}
}void choic()//游戏入口
{
int input = 0;
srand((unsigned int)time(NULL));
//生成随机数
do
{
printf("请选择:>
");
scanf("%d",&
input);
switch(input)
{
case 1:
printf("游戏开始\\n");
game();
break;
case 0:
printf("游戏结束\\n");
break;
default:
printf("输入错误\\n");
break;
}
}while(input);
}int main(int argc, char** argv)
{
choic();
return 0;
}
sanziqi.h
#define ROW 3//棋盘行数
#define COL 3//棋盘列数 #include <
stdio.h>
#include <
stdlib.h>
#include <
time.h>
void clean(char board[ROW][COL],int row,int col);
void draw(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 win(char board[ROW][COL],int row,int col);
//这个函数会返回四种状态
//1.玩家赢‘* ’
//2.电脑赢‘# ’
//3.平局‘Q ’
//4.继续‘C ’
hanshu.c
#include "sanziqi.h"void clean(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] = \' \';
}
}
}void draw(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 ++)
{
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");
}
}
}void playermove(char board[ROW][COL],int row,int col)//下棋部分
{
int x = 0;
int y = 0;
printf("玩家走:>
\\n");
while(1)
{
printf("请输入落子位置:>
\\n");
scanf("%d%d",&
x,&
y);
if(x >
= 1 &
&
x <
= row &
&
y >
= 1 &
&
y <
= col) // x y 可以取 1 是从用户的角度来看,1 实际是 0
{
if(board[x-1][y-1] == \' \')
{
board[x-1][y-1] = \'*\';
break;
//落子完成后跳出循环打印 *
}
else
{
printf("该坐标已被占用\\n");
}
}
else
{
printf("不在棋盘内请重新输入\\n");
}
}
}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;
}
}
}//返回 1 表示棋盘满了,返回 0 表示棋盘未满
int pingju(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 win(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][0] != \' \')
{
return board[i][0];
}
}
for(i = 0;
i <
row;
i ++)//竖三列
{
if(board[0][i] == board[1][i] &
&
board[1][i] == board[2][i] &
&
board[0][i] != \' \')
{
return board[0][i];
}
}
//对角线
if(board[0][0] == board[1][1] &
&
board[1][1] == board[2][2] &
&
board[0][0] != \' \')
{
return board[0][0];
}
if(board[0][2] == board[1][1] &
&
board[1][1] == board[2][0] &
&
board[0][2] != \' \')
{
return board[0][2];
}
//判断平局
if(1 == pingju(board,ROW,COL))
{
return \'Q\';
}
return \'C\';
//这里 else 可写可不写
}
文章图片
文章图片
文章图片
文章图片
还有不明白的地方评论区留言
推荐阅读
- 鸿蒙分布式小车游戏Demo
- 鸿蒙轻内核 M 核源码分析(数据结构之任务排序链表)
- C语言进阶—— 字符操作函数+内存操作函数详解(吐血爆肝 !!!)
- 能否与安卓iOS抗衡(鸿蒙OS+华为P40第一时间上手体验)
- 中断 Hwi(提高鸿蒙轻内核系统实时性及执行效率的秘密武器)
- 基于鸿蒙OS的按键驱动
- 鸿蒙开源第三方组件——自定义图片缩放组件PinchImageView-ohos
- 鸿蒙第三方组件——SwipeCaptcha滑动拼图验证组件
- 鸿蒙编译构建丨hb工具分析