C语言三子棋小游戏开发详细解析

金鞍玉勒寻芳客,未信我庐别有春。这篇文章主要讲述C语言三子棋小游戏开发详细解析相关的知识,希望能为你提供帮助。
三子棋游戏
游戏介绍:

游戏规则:

C语言三子棋小游戏开发详细解析

文章图片


项目要求:
创建一个显目,在显目中添加三个源代码,一个用来执行主函数、一个用来定义函数和常量、一个用来完成函数功能
C语言三子棋小游戏开发详细解析

文章图片

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"); } } }

效果图:
C语言三子棋小游戏开发详细解析

文章图片

注意:我们的格子顶上下面左边和右边都是没有格子的,所以在写代码时要注意

棋盘都画完了,那接下来就要开始下棋了,这里为了方便,我们就让玩家先,其实如果要公平的话也很简单,我们可以写一个猜拳的函数,来判断一下,但这里图个简单就不写了,之后的有写小游戏会涉及到,到那时候在讲

第四步:
玩家落子
函数定义:
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的时候本身又需要一个随机数(有的套娃的感觉),那怎么办?
C语言三子棋小游戏开发详细解析

文章图片

我们知道,电脑的时间是不是在每分每秒都在变动,这里我们就提到一个 “时间戳” 的概念,时间戳:当前计算机的时间减去计算机的起始时间(1970.1.1.0:0:0)
C语言三子棋小游戏开发详细解析

文章图片

这样我们就可以用时间戳来设置随机数的生成起点,C语言又是如何获取时间戳的
C语言三子棋小游戏开发详细解析

文章图片

time函数:
C语言三子棋小游戏开发详细解析

文章图片

  • 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; //满了 }

分析:这里草率了一点,函数名就叫做 “平局” 因为棋盘要是满了还没分出胜负那就是平局了,没毛病哈哈,用户和电脑每下一步都要执行一次这个函数判断棋盘是否满了,还要判断一下是否有哪一方获胜。
C语言三子棋小游戏开发详细解析

文章图片


2.判断输赢
一共有八中情况可能赢
C语言三子棋小游戏开发详细解析

文章图片
C语言三子棋小游戏开发详细解析

文章图片
C语言三子棋小游戏开发详细解析

文章图片


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 可写可不写 }


C语言三子棋小游戏开发详细解析

文章图片
C语言三子棋小游戏开发详细解析

文章图片
C语言三子棋小游戏开发详细解析

文章图片

C语言三子棋小游戏开发详细解析

文章图片

还有不明白的地方评论区留言

    推荐阅读