【C语言】小游戏-三字棋

news2024/11/27 7:27:16

大家好,我是深鱼~


目录

一、游戏介绍

 二、文件分装

 三、代码实现步骤

1.制作简易游戏菜单

 2.初始化棋盘

3.打印棋盘

4.玩家下棋

5.电脑随机下棋

6.判断输赢

7.判断棋盘是否满了

 四、完整代码

game.h(相关函数的声明,整个代码要引用的头文件以及宏定义)

game.c(实现游戏的相关函数)

test.c(:整个游戏相关的测试)


一、游戏介绍

《三子棋》是一款古老的民间传统游戏,又被称为黑白棋、圈圈叉叉棋、井字棋、一条龙、九宫棋等。游戏分为双方对战,双方依次在9宫格棋盘上摆放棋子,率先将自己的三个棋子连成一条线的一方则视为胜利者


 二、文件分装

源文件:内含函数实现,变量定义等内容

头文件:内含函数声明、宏定义、结构体定义等内容 

实现这个三子棋,我创建了三个文件

源文件:

test.c:整个游戏相关的测试

game.c:实现游戏的相关函数

头文件:

game.h: 相关函数的声明,整个代码要引用的头文件以及宏定义


 三、代码实现步骤

1.制作简易游戏菜单

(1)定义一个menu函数来打印游戏菜单,菜单中需要有游戏菜单选项:1.开始游戏  0.退出游戏,当我们输入1就进入游戏,0就退出游戏

(2)考虑到玩家可能玩一局以后还想继续玩,就用do while循环打印菜单,while(input),当输入的值为0,就跳出循环(退出游戏),如果输入非0值就再次进入循环(再次选择)

(3)玩家选择了进入游戏还是退出游戏需要判断,就用switch case语句来进行判断

test.c

#include"game.h"
void menu()
{
	printf("*****************************************\n");
	printf("**********      1.开始游戏      *********\n");
	printf("**********      0.退出游戏      *********\n");
	printf("*****************************************\n");

}

int main()
{
	int input = 0;
	srand((unsigned int)time(NULL));
	do//实现玩多盘游戏
	{ 
		menu();//打印菜单
		printf("请选择1/0:>\n");
		scanf("%d", &input);//选择进入游戏/退出游戏
		switch (input)//判断输入input的值决定是进入游戏还是退出游戏
		{
		case 1:
			game();//三子棋游戏的实现
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("选择错误,请重新选择\n");
			break;
		}
	} while (input);//当input为0,跳出循环;为1再玩一盘游戏(重新进入循环);为其他值进入循环重新选择
	return 0;
}

考虑到game.c和test.c可能都要用printf函数的头文件#include<stdio.h>,我们直接把这个头文件写进game.h,那么test.c这个文件只需引用#inlclude"game.h"即可

系统头文件一般用<>

用户自己定义的则可以使用" ",加快搜索速度

效果展示:

选择1,进入game()

选择0,退出游戏:

选择非1非0(选择错误),重新选择 

 


 2.初始化棋盘

(1)首先我们需要定义一个二维数组来存放数据,可以采用宏定义

宏定义:方便程序的修改,如果我们要改变棋盘的大小,直接修改宏定义的数字即可

(2)初始化棋盘我们定义一个InitBoard(board)函数,将棋盘全部初始化为' '

game.h

//宏定义:
//为了方便直接改变棋盘的大小,在头文件里面定义(ROW和COL就是常量)
#define ROW 3 //没有逗号     //行
#define COL 3                //列

//初始化数组为空格
void InitBoard(char board[ROW][COL]);

game.c

//初始化数组为空格
void InitBoard(char board[ROW][COL])
{
	for (int i = 0; i <ROW; i++)
	{
		for (int j = 0; j <COL; j++)
		{
			board[i][j] = ' ';//全部初始化为空格
		}
	}
}

3.打印棋盘

我们想打印这样打一个棋盘:用分隔线分开方便观察

(1)先打印数据(最后一个不用打印‘|’ )

(2)再打印分割行(最后一行不用打印)

game.h

//打印棋盘
void DisplayBoard(char board[ROW][COL]);

 game.c

//打印棋盘
void DisplayBoard(char board[ROW][COL])
{
	for (int i = 0; i < ROW; i++)
	{
			//先打印数据
		for (int j = 0; j < COL; j++)
		{
			printf(" %c ", board[i][j]);
			if(j<COL-1)//一行的最后一个不打印
			printf("|");
		 }
		printf("\n");

			//再打印分割行(最后一行不打印)
		if (i < ROW - 1)//最后一行不打印
		{
			for (int j = 0; j < COL; j++)
			{
				printf("---");
				if (j < COL - 1)//也是一行的最后一个不打印
					printf("|");
			}
			printf("\n");
		}
		
	}
}

4.玩家下棋

注意:

(1)玩家下棋输入坐标要在1-3(如果不在这个范围要重新输入),而不是数组下标的范围0-2,所以放入坐标是输入坐标分别-1得到的结果放入

(2)玩家下棋我们需要考虑坐标是否已经被占用

(3)字符的比较可以用==,既可用于常量也可用于变量比较

       字符串的比较要用strcmp函数,并且只能用于变量比较

game.h

//打印棋盘
void DisplayBoard(char board[ROW][COL]);

 game.c

//玩家下棋
void PlayerMove(char board[ROW][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)//下棋的坐标必须在二维数组内部
		{
			if (board[x - 1][y - 1] == ' ')//1.输入的横纵坐标-1才是数组的下标  2.字符串的比较不能用==,但是这只是一个字符空格而已
			{
				board[x - 1][y - 1] = '*';
				break;
			}
			else
			{
				printf("该坐标已被占用,请输入其他坐标\n");
			}
		}
		else
		{
			printf("坐标非法,请重新输入\n");
		}
	}
}

5.电脑随机下棋

电脑随机下棋就需要随机生成两个随机数,但是这两个数要在棋盘范围内,那就对生成的随机数进行取模x = rand() % ROW;产生的数为0-(ROW-1),这个时候就不用-1了,因为电脑下棋不用我们自己输入,它就以数组的形式下棋就行

这里介绍三个函数

rand():

头文件:#include<stdlib.h>

这个函数返回的是0-rand max之间的值(0-32767)

在使用rand函数之前,我们要求调用srand函数,这样才能实现rand的功能

srand():

头文件:#include<stdlib.h>

要想生成一个随机数,还得给srand一个随机数(要想rand函数产生的随机数不同,就需要给srand函数输入一个不断改变的值(如果只给一个常数,那么rand给出的随机数就是同一个值,不会改变))

srand的参数需要是unsigned int(无符号整形)eg:srand((unsigned int)time(NULL));

time():

头文件:#include<time.h>

原理:时间戳:把不停在改变的时间转化为数字返回,从而给srand一个随机值

game.h

//电脑下棋
void ComputerMove(char board[ROW][COL]);

 game.c

//电脑随机下棋
void ComputerMove(char board[ROW][COL]) 
{
	int x = 0;
	int y = 0;
	printf("电脑下棋\n");

	while (1)
	{
		x = rand() % ROW;//产生的数为0-(ROW-1)
		y = rand() % COL;

		if (board[x][y] == ' ')//如果这个位置已经有棋子了,再进入循环,重新生成x和y直到下了一个棋子为止
		{
			board[x][y] = '#';
			break;
		}
	}
}

6.判断输赢

判断输赢:设置四种结果:
玩家赢-返回*
电脑赢-#
平局-Q
游戏继续—C

game.h

//判断输赢
char IsWin(char board[ROW][COL]);

 game.c

//判断输赢:四种结果:
//玩家赢-返回*
//电脑赢-#
//平局-Q
//游戏继续—C
char IsWin(char board[ROW][COL])
{
	for (int i = 0; i < ROW; i++)
	{
		if (board[i][0] == board[i][1] && board[i][0] == board[i][2] && board[i][0] != ' ')//注意要排除三个空格的情况
			return board[i][0];//谁的棋子连着三个就返回谁的棋子
	}
	for (int i = 0; i < COL; i++)
	{
		if (board[0][i] == board[1][i] && board[0][i] == board[2][i] && board[0][i] != ' ')//注意要排除三个空格的情况
			return board[0][i];//谁的棋子连着三个就返回谁的棋子
	}

	if (board[0][0] == board[1][1] && board[0][0] == board[2][2] && board[0][0] != ' ')
		return board[0][0];
	if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[1][1] != ' ')
		return board[1][1];

	if (IsFull(board))
		return 'Q';//如果棋盘满了,就表示平局,返回Q
	return 'C';//否则就继续下棋

}

7.判断棋盘是否满了

遍历二维数组,只要有一个空格就继续下棋(没满),如果都不是空格,那就是表示满了

game.h

//判断棋盘是否满了
int IsFull(char board[ROW][COL]);

 game.c

//判断棋盘是否是满的
static int IsFull(char board[ROW][COL])//staic表示只在这个game.c的文件中使用这个函数
{
	for (int i = 0; i < ROW; i++)
	{
		for (int j = 0; j < COL; j++)
		{
			if (board[i][j] == ' ')//只要有一个空格就继续进行
				return 0;
		}
	}
	return 1;//如果都不是空格,那就是表示全满
}

 四、完整代码

game.h(相关函数的声明,整个代码要引用的头文件以及宏定义)

//头文件:内含函数声明、宏定义、结构体定义等内容

#include<stdio.h>
#include<time.h>
#include<stdlib.h>

//宏定义:
//为了方便直接改变棋盘的大小,在头文件里面定义(ROW和COL就是常量)
#define ROW 3 //没有逗号     //行
#define COL 3                //列

//函数声明:
//初始化数组为空格
void InitBoard(char board[ROW][COL]);//row和col为真实的行和列
//打印棋盘
void DisplayBoard(char board[ROW][COL]);
//玩家下棋
void PlayerMove(char board[ROW][COL]);
//电脑下棋
void ComputerMove(char board[ROW][COL]);
//判断输赢
char IsWin(char board[ROW][COL]);
//判断棋盘是否满了
int IsFull(char board[ROW][COL]);

game.c(实现游戏的相关函数)

//实现游戏相关的代码
#include"game.h"//这里也要包含自己定义的头文件,不然ROW和COL宏定义就不起作用了

//初始化数组为空格
void InitBoard(char board[ROW][COL])
{
	for (int i = 0; i <ROW; i++)
	{
		for (int j = 0; j <COL; j++)
		{
			board[i][j] = ' ';//全部初始化为空格
		}
	}
}

//打印棋盘
void DisplayBoard(char board[ROW][COL])
{
	for (int i = 0; i < ROW; i++)
	{
			//先打印数据
		for (int j = 0; j < COL; j++)
		{
			printf(" %c ", board[i][j]);
			if(j<COL-1)//一行的最后一个不打印
			printf("|");
		 }
		printf("\n");

			//再打印分割行(最后一行不打印)
		if (i < ROW - 1)//最后一行不打印
		{
			for (int j = 0; j < COL; j++)
			{
				printf("---");
				if (j < COL - 1)//也是一行的最后一个不打印
					printf("|");
			}
			printf("\n");
		}
		
	}
}

//玩家下棋
void PlayerMove(char board[ROW][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)//下棋的坐标必须在二维数组内部
		{
			if (board[x - 1][y - 1] == ' ')//1.输入的横纵坐标-1才是数组的下标  2.字符串的比较不能用==,但是这只是一个字符空格而已
			{
				board[x - 1][y - 1] = '*';
				break;
			}
			else
			{
				printf("该坐标已被占用,请输入其他坐标\n");
			}
		}
		else
		{
			printf("坐标非法,请重新输入\n");
		}
	}
}

//电脑随机下棋
void ComputerMove(char board[ROW][COL]) 
{
	int x = 0;
	int y = 0;
	printf("电脑下棋\n");

	while (1)
	{
		x = rand() % ROW;//产生的数为0-(ROW-1)
		y = rand() % COL;

		if (board[x][y] == ' ')//如果这个位置已经有棋子了,再进入循环,重新生成x和y直到下了一个棋子为止
		{
			board[x][y] = '#';
			break;
		}
	}
}

//判断棋盘是否是满的
static int IsFull(char board[ROW][COL])//staic表示只在这个game.c的文件中使用这个函数
{
	for (int i = 0; i < ROW; i++)
	{
		for (int j = 0; j < COL; j++)
		{
			if (board[i][j] == ' ')//只要有一个空格就继续进行
				return 0;
		}
	}
	return 1;//如果都不是空格,那就是表示全满
}

//判断输赢:四种结果:
//玩家赢-返回*
//电脑赢-#
//平局-Q
//游戏继续—C
char IsWin(char board[ROW][COL])
{
	for (int i = 0; i < ROW; i++)
	{
		if (board[i][0] == board[i][1] && board[i][0] == board[i][2] && board[i][0] != ' ')//注意要排除三个空格的情况
			return board[i][0];//谁的棋子连着三个就返回谁的棋子
	}
	for (int i = 0; i < COL; i++)
	{
		if (board[0][i] == board[1][i] && board[0][i] == board[2][i] && board[0][i] != ' ')//注意要排除三个空格的情况
			return board[0][i];//谁的棋子连着三个就返回谁的棋子
	}

	if (board[0][0] == board[1][1] && board[0][0] == board[2][2] && board[0][0] != ' ')
		return board[0][0];
	if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[1][1] != ' ')
		return board[1][1];

	if (IsFull(board))
		return 'Q';//如果棋盘满了,就表示平局,返回Q
	return 'C';//否则就继续下棋

}

test.c(:整个游戏相关的测试)

game函数的逻辑:

(1)初始化数组,并打印出来看看什么样子

(2)开始下棋,玩家先下,下完进行打印,打印完进行判断输赢,如果不是游戏继续,就直接跳出循环,输出游戏结果(玩家赢/电脑赢/平局)

(3)电脑下棋下棋的逻辑一样 

//源文件,内含函数实现,变量定义等内容

#include"game.h"//系统头文件一般用<>;用户自己定义的则可以使用""
//整个游戏相关的测试
void menu()
{
	printf("*****************************************\n");
	printf("**********      1.开始游戏      *********\n");
	printf("**********      0.退出游戏      *********\n");
	printf("*****************************************\n");

}

void game()
{
	char ret = 0;
	char board[ROW][COL];
	//开始的时候,数组的内容应该全是空格
	InitBoard(board);//用于初始化数组为空格
	DisplayBoard(board);//用于打印棋盘
	//下棋
	while (1)
	{
		PlayerMove(board);//玩家下棋放*
		DisplayBoard(board);
		//判断输赢
		ret=IsWin(board);
		if (ret != 'C')
		{
			break;//不是游戏继续就跳出循环,输出结果
		}

		ComputerMove(board);//电脑随机下棋放#
		DisplayBoard(board);
		//判断输赢
		ret = IsWin(board);
		if (ret != 'C')
		{
			break;
		}
	}

	if (ret == '*')
		printf("玩家赢\n");
	else if (ret =='#')
		printf("电脑赢\n");
	else
		printf("平局\n");
}
int main()
{
	int input = 0;
	srand((unsigned int)time(NULL));
	do//实现玩多盘游戏
	{ 
		menu();//打印菜单
		printf("请选择1/0:>\n");
		scanf("%d", &input);//选择进入游戏/退出游戏
		switch (input)//判断输入input的值决定是进入游戏还是退出游戏
		{
		case 1:
			game();//三子棋游戏的实现
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("选择错误,请重新选择\n");
			break;
		}
	} while (input);//当input为0,跳出循环;为1再玩一盘游戏(重新进入循环);为其他值进入循环重新选择
	return 0;
}

 本次C语言小游戏三子棋的内容就到此啦,有什么问题欢迎评论区或者私信交流,觉得笔者写的还可以,或者自己有些许收获的,麻烦铁汁们动动小手,给俺来个一键三连,万分感谢 ! 

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

五金仓库的管理数字化

随着信息技术的快速发展&#xff0c;数字化管理在各行各业中迅速普及。数字化管理可以让企业轻松高效地收集、存储和共享数据&#xff0c;并利用大数据分析和人工智能等工具进行精确分析和预测&#xff0c;从而更好地理解业务运作情况并做出相应调整&#xff0c;以提高企业效率…

已有公司将ChatGPT集成到客服中心以增强用户体验

Ozonetel正在利用ChatGPT来改善客户体验。该公司表示&#xff0c;他们通过使用ChatGPT收集与客户互动过程收集的“语料”能够更有针对性地提高服务效率&#xff0c;提供个性化的用户体验&#xff0c;并实现更高的客户满意度。[1] 通过这套解决方案&#xff0c;客服中心将拥有一…

IDEA之Debug调试

资料来源于韩老师视频 &#xff08;一&#xff09;初探debug 1、打断点的话&#xff1a;直接在该行前面单击左键&#xff0c;出现小红点就是断点了。 想要取消断点的话&#xff0c;再单击小红点即可。 运行debug时&#xff0c;右键选择"Debug…"而不是选“Run…”…

sip语音对讲终端怎么样?

sip语音对讲终端怎么样&#xff1f; IP语音对讲终端是一种通过网络进行语音通信的设备&#xff0c;具有以下特点&#xff1a; 1. 便捷性&#xff1a;IP语音对讲终端可以通过互联网实现远程通信&#xff0c;用户可在任何地点与他人进行语音交流&#xff0c;无需受到距离的限制…

MySql011——检索数据:过滤数据(使用正则表达式)

前提&#xff1a;使用《MySql006——检索数据&#xff1a;基础select语句》中创建的products表 一、正则表达式介绍 关于正则表达式的介绍大家可以看我的这一篇博客《Java038——正则表达式》&#xff0c;这里就不再累赘。 二、使用MySQL正则表达式 2.1、基本字符匹配 检索…

18-有假币

题目 居然有假币&#xff01; 现在猪肉涨了&#xff0c;但是农民的工资却不见涨啊&#xff0c;没钱怎么买猪肉啊。nowcoder这就去买猪肉&#xff0c;结果找来的零钱中有假币&#xff01;&#xff01;&#xff01;可惜nowcoder 一不小心把它混进了一堆真币里面去了。只知道假币…

Java项目-苍穹外卖-Day03

员工分页查询功能实现 需求分析和设计 代码开发 先设计类 将对应分页查询的传参类以及结果类进行封装 对应真正返回的为Result<PageResult>Controller /**** param employeePageQueryDTO* return*/GetMapping("/page")ApiOperation("员工分页查询&qu…

【C++】开源:glog日志库配置使用

&#x1f60f;★,:.☆(&#xffe3;▽&#xffe3;)/$:.★ &#x1f60f; 这篇文章主要介绍glog日志库配置使用。 无专精则不能成&#xff0c;无涉猎则不能通。——梁启超 欢迎来到我的博客&#xff0c;一起学习&#xff0c;共同进步。 喜欢的朋友可以关注一下&#xff0c;下次…

Mac M1 安装Oracle Java 与 IEDA

文章目录 1 官网下载2 安装IDEA参考 1 官网下载 https://www.oracle.com/ 使用finder中的拖拽进行安装即可 2 安装IDEA https://www.jetbrains.com/zh-cn/idea/download/?sectionmac 同样的&#xff0c;下载完后拖拽安装即可 参考 Mac M1 安装Java 开发环境 https://blog.…

Cmder:从此告别记事本记命令的日子

前言 平时开发中遇到这样那样的命令需要记下来&#xff0c;一般做法是这样。 新建记事本将需要记下的关键命令保存。每次需要使用时&#xff0c;粘贴复制即可。 好像没什么毛病&#xff01;直到遇到了 Cmder。。。 当看到同事分析问题时在 Cmder 里命令快捷键刷刷一顿操作&…

体渲染原理及WebGL实现【Volume Rendering】

体渲染&#xff08;Volume Rendering&#xff09;是NeRF神经场辐射AI模型的基础&#xff0c;与传统渲染使用三角形来显示 3D 图形不同&#xff0c;体渲染使用其他方法&#xff0c;例如体积光线投射 (Volume Ray Casting)。本文介绍体渲染的原理并提供Three.js实现代码&#xff…

【AI底层逻辑】——篇章7(上):海量运算背后的算力支持

目录 引入 一、计算机芯片 1、芯片的制造 2、复杂指令集&精简指令集 3、并行计算的GPU 二、协作计算 1、分布式技术“三论文” 2、不可兼得的CAP定理 3、故障类型 续下篇... 往期精彩&#xff1a; 引入 早在2016年DeepMind就公布了AlphaGo的算法细节&#xff0…

易服客工作室:如何在WordPress网站中举办虚拟活动

您是否正在寻找举办和管理虚拟活动的最佳方式&#xff1f; 也许您想在线举行下一次会议或举办有关您的产品和服务的网络研讨会。您可能担心它太贵&#xff0c;或者您没有技术知识来实现​​它。 在本文中&#xff0c;我们将列出您所需的在线服务的所有设备&#xff0c;并教您…

ubuntu python 查看系统是否可用 cuda

ubuntu python 查看系统是否可用 cuda 初环境与设备查看环境演示效果 本篇文章将介绍ubuntu python 查看系统是否可用 cuda 初 希望能写一些简单的教程和案例分享给需要的人 环境与设备 系统&#xff1a;ubuntu 22.04 工具&#xff1a;python 3.10.6 设备&#xff1a;Nvidi…

JavaScript 运行机制详解:再谈Event Loop

一、为什么JavaScript是单线程&#xff1f; JavaScript语言的一大特点就是单线程&#xff0c;也就是说&#xff0c;同一个时间只能做一件事。那么&#xff0c;为什么JavaScript不能有多个线程呢&#xff1f;这样能提高效率啊。 JavaScript的单线程&#xff0c;与它的用途有关。…

【二叉树】1-5,理论基础、前中后序遍历的递归法和迭代法、层序遍历

理论基础、前中后序遍历的递归法和迭代法、层序遍历 1&#xff0c;二叉树的种类满二叉树完全二叉树二叉搜索树平衡二叉搜索树 2&#xff0c;存储方式链式存储线式存储 3&#xff0c;二叉树的遍历深度优先搜索前序遍历&#xff08;递归法、迭代法&#xff09;中序遍历&#xff0…

无涯教程-Perl - msgrcv函数

描述 此函数从队列ID接收消息,并将消息放入变量VAR中,最大大小为SIZE。 语法 以下是此函数的简单语法- msgrcv ID, VAR, SIZE, TYPE, FLAGS返回值 该函数在错误时返回0,在成功时返回1。 Perl 中的 msgrcv函数 - 无涯教程网无涯教程网提供描述此函数从队列ID接收消息,并将消…

山东布谷科技直播软件开发WebRTC技术:建立实时通信优质平台

在数字化的时代&#xff0c;实时通信成为了人们远程交流的主要方式&#xff0c;目前市场上也出现了很多带有实时通信交流的软件&#xff0c;实时通信符合人们现在的需求&#xff0c;所以在直播软件开发过程中&#xff0c;开发者也运用了实时通信技术为直播软件加入了实时通信的…

什么是CSS Grid布局?什么是Flexbox布局?它们两者有什么不同?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ CSS Grid布局⭐ Flexbox布局⭐ 不同之处⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端入门之旅&#xff01;这个专栏是为那些对Web…