【C语言小游戏】三子棋(井字棋)

hello,csdn的伙伴们,大家好,我们已经学习到了分支与循环,函数,数组这三大块知识,那么我们现在就可以尝试综合运用前面所学的知识,来完成一个简单的小游戏-----三子棋(井字棋)


目录

 一、采用多文件,分模块来实现

二、建立游戏基本框架

1.为什么是使用do while语句?

2.打印出菜单

 3.实现通过选择菜单,跳转至相应界面

4.测试前面的逻辑

 三、具体游戏的逻辑实现

1.在我们的case 1下加上我们的游戏函数且定义和声明好我们的函数

 2.思考游戏的棋盘是什么样子的,该如何绘制?

 3.数据如何存储,游戏该如何去玩?

4.设计部分游戏功能函数

 5.初始化棋盘模块的实现(init_board)

 6.打印棋盘模块的实现(print_board)

(1)一些需要注意的思考

(2)尝试实现功能

(3)更进一步优化

7.截止至目前已经实现功能,所写的代码展示

8. 玩家下棋(player_move)

(1)玩家下棋函数的定义和声明

 (2)实现玩家下棋

(3)测试玩家下棋模块

(4)经典的错误标准的零分

(5)玩家下棋的代码

9.电脑下棋(computer_move)

(1)电脑下棋函数的声明和定义

(2)电脑下棋的具体实现

(3)电脑下棋的代码测试

(4)电脑下棋的代码展示

10.判断输赢

(1)判断输赢的声明和定义以及判断输赢的基本逻辑

 (2)具体函数的实现

  (3)代码测试

(4)本模块代码

四、最终代码展示(分文件)

1.test.c文件

2.game.h文件

3.game.c文件

五、代码展示(三个文件合成为一个文件)

总结


 一、采用多文件,分模块来实现

我们在之前讲到过,在我们以后完成一个项目时候,我们使用的是多文件,分模块的进行完成。那么今天我们也采用这个方法

我们创立三个文件:

test.c   //测试的逻辑

game.h

game.c  //游戏的实现

二、建立游戏基本框架

1.为什么是使用do while语句?

我们想要完成一个三子棋游戏,那么我们使用一个简单的do while 逻辑

 在这段代码中,我们为了使我们的代码逻辑清晰,我们直接使用一个test函数,test里面的函数执行我们游戏的逻辑,而在test函数里面,我们注意到,我们使用了一个do while 循环,这是考虑到,我们完成一个游戏肯定不可能只玩了一把就自动退出去了,我们希望玩完一把后可以由我们自己来选择是否进行下一把或者结束游戏。所以我们必须使用一个循环,但是我

们又必需得先玩一把在考虑是否玩下去,所以这就是使用do while语句的原因了。

2.打印出菜单

是一个游戏肯定要有他的界面,所以我们do while进入以后直接开始打印菜单。不妨我们就将菜单设为meau函数吧,如下图所示,这块不难理解

 3.实现通过选择菜单,跳转至相应界面

 如上图所示,我们通过一个input,来记录我们输入的值,并通过switch语句来跳转至相应的功能。需要解释的就是while里面为什么是input就可以了?其实这句话是一个简写,我们的全写应该是input!=0,当他不等于0的时候,我们可以继续循环,如果等于0,循环结束。而这个的真值表恰好与input本身的真值表相同,因为只有0为假,非零都为真。所以这里使用input就是很合理的。

4.测试前面的逻辑

我们写代码一定要写一点测一点。而不是一股脑写到最后,代码几百个bug无从下手。

下面是截止至目前我们目前所写的代码,这些代码全部是属于test.c这个文件里面的。

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
void meau()
{
	printf("********************************\n");
	printf("********     1.play      *******\n");
	printf("********     0.exit      *******\n");
	printf("********************************\n");
}
void test()
{
	int input = 0;
	do
	{
		meau();
		printf("请输入>:\n");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("进入三子棋游戏\n");
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("选择错误,请重新选择\n");
			break;
		}
	} while (input);
}
int main()
{
	test();
	return 0;
}

运行结果为

 可见我们所预想的逻辑是全部满足了。这样我们才可以放心的往下继续写下去。

 三、具体游戏的逻辑实现

1.在我们的case 1下加上我们的游戏函数且定义和声明好我们的函数

我们想要实现的是按一个1,就能开始玩游戏,而不是就单单弹出一个进入三子棋游戏然后就没有下文了。我们在这块封装一共函数game,这个函数的返回类型是void,不需要传参,如下所示

 2.思考游戏的棋盘是什么样子的,该如何绘制?

相信大家小时候玩过三子棋或者说井字棋小游戏。这个游戏的棋盘是一个#字,如下图所示

 当然这是我们平时所画的棋盘。那么在我们c语言中我们如何制作一共形状和功能都差不多的棋盘呢。我们是这样思考的,如下图所示,三个-和一些|刚好可以组成一些正方形的格子。

 3.数据如何存储,游戏该如何去玩?

根据上面的棋盘,我们其实不难得出,这个数据需要一个3*3的数组来存储。这些数据的类型我们可以使用一个char来实现,玩家打印*字符,电脑打印#字符,而我们最开始其实那些似乎没有棋子的地方,我们可以看成一个空格字符。所以最开始我们的数组都是空格字符

在玩游戏的时候,我们可以选择输入一个坐标,这是玩家下棋,然后电脑下棋。这样循环下去。直到有人赢了或者平局。

4.设计部分游戏功能函数

根据上面的分析可以得到,我们手下需要定义一个char类型的数组,这个数组的类型是3*3,并且我们需要对其初始化,这个初始化的我们可以封装成一个函数,init_board,这个函数不难想象要三个参数,分别是数组名,行和列。然后初始化完后我们可以写一个打印棋盘的函数。如下图所示

 不过值得注意的是,又很多人在传这个数组的时候,传了一个board[3][3]过去,注意这是一个经典的错误标准的零分,这个传进去的是第三行第三列的元素,而且这个元素甚至还越界了。这就错上加错了。我们应该传一个数组名过去,数组名也就是首元素的地址。如下图所示就是错误的

 当然我们这样设计这些函数,我们其实就把代码写死了。 未来我们想要打印一个5行5列的数组就修改比较麻烦。所以我们可以这样做,使用一个define,如下图所示,我们在game.h中将ROW,和COL都设置为3。然后我们在test.c中就可以引入game.h这个头文件。将game这个函数里面的3都可以改为行和列了。这样未来我们在想进行一些修改的时候就很简单了。

当然上图中有一处我们刚刚提到过的经典的错误标准的零分,不知道你有没有发现呢?答案就是我们此处数组传参仍然是错的,我们只需要传数组名,这下相信大家都记住了吧。

 5.初始化棋盘模块的实现(init_board)

 我们在game.h中声明函数,在game.c中实现

game.h如下所示

代码为

void init_board(char board[3][3], int row, int col);

 game.c如下所示,注意不要忘记game.h这个头文件,因为我们声明是放在了game.h中

 代码实现为

void init_board(char board[3][3], 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] = ' ';
		}
	}
}

 6.打印棋盘模块的实现(print_board)

(1)一些需要注意的思考

我们初始化完后,我们就想要打印出来检验一下我们初始化后的结果,所以我们现在来实现打印棋盘

game.h如下所示

 代码为

void print_board(char board[3][3], int row, int col);

game.c如下所示(注意,在此处,可能有小伙伴们发现头文件里面多了个stdio.h的头文件,这样的话,game.c中虽然使用了printf但是就不需要重复添加这个头文件了,因为我们直接引用了game.h这个头文件。而且我们还可以将test.c这个头文件给删除掉了)

和上文所说的一样,写一点测一点。我们运行测试一下,我们发现,没有棋盘。所以说,我们这个打印棋盘的代码是不完整的,我们需要完善一下。

 我们是这样思考这个打印的函数的。我们将下图看成三组逻辑,打印一行空格行和一个分割行是一组逻辑,最后一行也是一个分割行,只不过这个不用打印。

(2)尝试实现功能

那么代码实现如下所示

void print_board(char board[ROW][COL], int row, int col)
{
	int i = 0;
	for (i = 0; i < row; i++)
	{
		printf(" %c | %c | %c \n", board[i][0], board[i][1], board[i][2]);
		if (i < row - 1)
		{
			printf("---|---|---\n");
		}
	}
}

运行结果如下所示

可是这样的话,我们会发现,这样其实就被写死了,以后想要修改就很难修改了。比如我们将行和列都改为十的话。就会发现和我们所设想的棋盘大大不符合

(3)更进一步优化

 所以我们要改。要优化代码,那么怎么优化呢?其实我们可以在我们上面的代码上进行修改,我们上面的代码是由两次打印,我们将两次打印都进行展开。具体到每一列上操作。第一次打印就把一个数据和一个|字符当成一组逻辑,第二次打印就把---|当成一组逻辑。当然每一组逻辑最后一次是不需要打印|的。所以根据这些分析,我们已经可以推导出代码了

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

 运行结果为

可见符合了我们的预期。

7.截止至目前已经实现功能,所写的代码展示

作为一个新手,刚开始写这种小游戏的时候,常常读着读着就已经晕了。这是因为新手暂时还没有一种全局的目光。所以为了方便读者理解,我们将截止至目前已经实现的代码在此处进行一下展示方便读者更好的阅读下去。

test.c模块

#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"

void meau()
{
	printf("********************************\n");
	printf("********     1.play      *******\n");
	printf("********     0.exit      *******\n");
	printf("********************************\n");
}
void game()
{
	char board[ROW][COL];
	init_board(board, ROW, COL);
	print_board(board, ROW, COL);
}
void test()
{
	int input = 0;
	do
	{
		meau();
		printf("请输入>:\n");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("进入三子棋游戏\n");
			game();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("选择错误,请重新选择\n");
			break;
		}
	} while (input);
}
int main()
{
	test();
	return 0;
}

game.c模块

#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"

void init_board(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 print_board(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");
		}
	}
}

game.h模块

#pragma once
#include<stdio.h>

#define ROW 3
#define COL 3

//头文件声明函数

void init_board(char board[ROW][COL], int row, int col);

//打印棋盘
void print_board(char board[ROW][COL], int row, int col);

8. 玩家下棋(player_move)

有了棋盘,自然就需要下棋了。

(1)玩家下棋函数的定义和声明

void player_move(char board[ROW][COL], int row, int col);

 

 (2)实现玩家下棋

我们要让玩家下棋,那么我们不妨就让玩家通过输入坐标来进行下棋,如下图所示,这样的话,就可以使玩家输入一个坐标。

有了坐标,我们首先要通过这个坐标来判断是否在我们棋盘的范围内,也就是是否合法,而且要注意的是,玩家可不是程序员,他们不知道这个数组的下标是从0开始的,所以我们要在玩家输入的坐标上-1,而且我们这样应该是一个循环,坐标非法时候需要重新输入坐标,当我成功下棋以后,我们可以在里面放入一个break语句来打破循环。

坐标合法,但是如果这个位置之前有人下过棋了呢,这种情况也是不允许的,所以我们要在坐标合法里面加入一条判断,这个位置是一个空格字符,不能有棋子。

 (提示:此处存在一处错误,这是我们之前所讲过的经典的错误标准的零分,细心的你发现了吗,没有发现的话,那就接着往下看吧!后文会讲解的)

(3)测试玩家下棋模块

我们试着测试一下,在test.c种加入打印棋盘,方便我们查看

运行结果为

输入成功案例

坐标非法案例

此处位置已经被下了棋案例

这部分我们可以使用两次玩家下棋来完成我们先将test.c在进行一次玩家输入和打印,这是因为我们目前这是第一次下棋,只有在第二次下棋才会出现这种情况

 我们试着下棋

我们发现居然不符合我们的预期了!!!,这是怎么回事呢,其实这个错误细心的同学肯定在前文中已经发现了。

(4)经典的错误标准的零分

这个错误有太多人犯了,所以在此处故意设下了一个坑,当然也是提醒大家,细心的重要性。当然这个错误看似简单,但是在这个一个很简单的项目里可不简单,即便我在前文中提醒过此处有一个经典错误标准零分,但还是有人找不到,那如果将整个游戏写完之后在想找到这个错误是很困难的。这么一个小错误对于这个游戏而言是很致命的。而这个错误很难发现,所以这就再次提醒大家,一定要细心,细心。写一部分测一部分。否则未来在企业中,那种超大规模的代码想要调试出来是极其困难的!!!

修改后再次测试

这下就是符合我们的预期了

(5)玩家下棋的代码

void player_move(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)
		{
			if (board[x - 1][y - 1] == ' ')
			{
				board[x - 1][y - 1] = '*';
				break;
			}
			else
			{
				printf("该位置已经有棋子了,请重新下棋");
			}
		}
		else
		{
			printf("坐标非法,请重新输入\n");
		}
	}

}

9.电脑下棋(computer_move)

(1)电脑下棋函数的声明和定义

我们发现我们使用了一个while循环,这样是为了实现电脑和人交互下棋。

void computer_move(char board[ROW][COL], int row, int col);

(2)电脑下棋的具体实现

电脑下棋是如何实现的呢,我们可以制作一个简单版本的电脑下棋。也就是让电脑随机下。当然想让电脑智能下棋的话对于目前的阶段还是比较困难的。我们目前只完成电脑随机下。想要让电脑随机下你,那么就必须得要让电脑产生一个随机的坐标,这个坐标不能非法,也必须在一个没有下过棋子的位置才可以,这样一思考,其实就简单了。这可比我们的人下棋要简单一点。唯一一点需要注意的就是如何生成随机坐标。其实随机坐标的生成也不难理解,我们之前在讲解猜数字小游戏的时候已经讲解过了。这里给出链接:猜数字小游戏详解,有了随机坐标。那么电脑下棋的步骤也就很简单了。

具体实现如下

 当然有些人觉得这个电脑下棋的代码有一些没有必要的东西,比如x和y不需要+1,如果这样做,还能少了一层判断。因为他是电脑内部操作的。不需要玩家来认识,这样做当然是可以的,但是我们这样写形式上与玩家下棋一致。更容易理解。

(3)电脑下棋的代码测试

还是和之前一样,写一点测一点,满足我们的预期。

(4)电脑下棋的代码展示

void computer_move(char board[ROW][COL], int row, int col)
{
	int x = 0;
	int y = 0;
	printf("电脑下棋\n");
	while (1)
	{
		x = rand() % row + 1;
		y = rand() % col + 1;
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (board[x - 1][y - 1] == ' ')
			{
				board[x - 1][y - 1] = '#';
				break;
			}
		}
	}
}

10.判断输赢

我们已经实现了玩家和电脑的下棋,那么游戏肯定是有输赢的,我们接下来来实现游戏的输赢

(1)判断输赢的声明和定义以及判断输赢的基本逻辑

我们思考一下游戏一共有多少种状态呢?

答案是四种,分别为,电脑赢,玩家赢,平局,游戏继续

我们将电脑赢记作#

玩家赢记作*

平局记作 Q

游戏继续记作C

这样一来的话,我们就可以构建出我们的函数了,吧目前的数组和行列传给他,他生成一个返回值。然后根据这个返回值就能判断出游戏的状态,从而得出结论了

函数的定义和声明如下

char is_win(char board[ROW][COL], int row, int col);

 输赢的基本逻辑实现如下

 我们直接给出目前test.c文件中的所有代码

#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"

void meau()
{
	printf("********************************\n");
	printf("********     1.play      *******\n");
	printf("********     0.exit      *******\n");
	printf("********************************\n");
}
void game()
{
	char board[ROW][COL];
	//初始化棋盘
	init_board(board, ROW, COL);
	//打印棋盘
	print_board(board, ROW, COL);
	//玩家下棋
	char ret = ' ';
	//ret用来接受一个返回值,用来判断输赢
	while (1)
	{
		player_move(board, ROW, COL);
		print_board(board, ROW, COL);
		ret = is_win(board, ROW, COL);
		//判断游戏是否继续
		if (ret != "C")
		{
			break;//如果不继续,打破循环
		}
		computer_move(board, ROW, COL);
		print_board(board, ROW, COL);
		//判断游戏是否继续
		ret = is_win(board, ROW, COL);
		if (ret != "C")
		{
			break;//如果不继续,打破循环
		}
	}
	if (ret == '*')
	{
		printf("玩家赢\n");
	}
	else if (ret == '#')
	{
		printf("电脑赢\n");
	}
	else if (ret == 'Q')
	{
		printf("平局\n");
	}
}
void test()
{
	srand((unsigned int)time(NULL));
	int input = 0;
	do
	{
		meau();
		printf("请输入>:\n");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("进入三子棋游戏\n");
			game();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("选择错误,请重新选择\n");
			break;
		}
	} while (input);
}
int main()
{
	test();
	return 0;
}

 (2)具体函数的实现

我们先来实现一下谁赢了的场景,如下图所示,一共有四种判断方式,这种方式其实是比较写死了。不适合进行推广到N子棋。这个后续是可以进行优化的。但是我们今天先不做优化,我们今天的目标就只是三子棋。不过我们后续会专门出一篇文章,来将三子棋的各大功能进行全方位的升级。今天我们只讨论基础版本的三子棋。

 判断玩输赢,那么还需要平局和游戏继续两种状态,平局其实不难理解,就是整个棋盘都满了,还没有决出胜负。所以我们是需要将判断平局放在决出胜负之后的。判断棋盘是否满,我们可以封装一共函数。is_full,这个函数判断是否满。如果满返回1,否则返回0。

具体代码实现如下,这个代码没必要写在头文件声明,因为这个代码其实只需要被判断输赢函数使用即可

 然后is_win剩余的逻辑为

  (3)代码测试

玩家赢

 电脑赢

和棋

这块当时好不容易下成了和棋,结果网卡了没传上........不过好歹还有没上传成功时候的正在处理的截图,还是可以使用的。

(4)本模块代码

//判断棋盘是否满了
int is_full(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 is_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 < col; 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 (is_full(board,row,col) == 1)
	{
		return 'Q';
	}
	return 'C';
}

四、最终代码展示(分文件)

我们代码是三个文件的

1.test.c文件

#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"

void meau()
{
	printf("********************************\n");
	printf("********     1.play      *******\n");
	printf("********     0.exit      *******\n");
	printf("********************************\n");
}
void game()
{
	char board[ROW][COL];
	//初始化棋盘
	init_board(board, ROW, COL);
	//打印棋盘
	print_board(board, ROW, COL);
	//玩家下棋
	char ret = ' ';
	//ret用来接受一个返回值,用来判断输赢
	while (1)
	{
		player_move(board, ROW, COL);
		print_board(board, ROW, COL);
		ret = is_win(board, ROW, COL);
		//判断游戏是否继续
		if (ret != 'C')
		{
			break;//如果不继续,打破循环
		}
		computer_move(board, ROW, COL);
		print_board(board, ROW, COL);
		//判断游戏是否继续
		ret = is_win(board, ROW, COL);
		if (ret != 'C')
		{
			break;//如果不继续,打破循环
		}
	}
	if (ret == '*')
	{
		printf("玩家赢\n");
	}
	else if (ret == '#')
	{
		printf("电脑赢\n");
	}
	else if (ret == 'Q')
	{
		printf("平局\n");
	}
}
void test()
{
	srand((unsigned int)time(NULL));
	int input = 0;
	do
	{
		meau();
		printf("请输入>:\n");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("进入三子棋游戏\n");
			game();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("选择错误,请重新选择\n");
			break;
		}
	} while (input);
}
int main()
{
	test();
	return 0;
}

2.game.h文件

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define ROW 3
#define COL 3

//头文件声明函数

void init_board(char board[ROW][COL], int row, int col);

//打印棋盘
void print_board(char board[ROW][COL], int row, int col);

//玩家下棋
void player_move(char board[ROW][COL], int row, int col);

//电脑下棋
void computer_move(char board[ROW][COL], int row, int col);

//判断输赢
char is_win(char board[ROW][COL], int row, int col);

3.game.c文件

#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"

void meau()
{
	printf("********************************\n");
	printf("********     1.play      *******\n");
	printf("********     0.exit      *******\n");
	printf("********************************\n");
}
void game()
{
	char board[ROW][COL];
	//初始化棋盘
	init_board(board, ROW, COL);
	//打印棋盘
	print_board(board, ROW, COL);
	//玩家下棋
	char ret = ' ';
	//ret用来接受一个返回值,用来判断输赢
	while (1)
	{
		player_move(board, ROW, COL);
		print_board(board, ROW, COL);
		ret = is_win(board, ROW, COL);
		//判断游戏是否继续
		if (ret != 'C')
		{
			break;//如果不继续,打破循环
		}
		computer_move(board, ROW, COL);
		print_board(board, ROW, COL);
		//判断游戏是否继续
		ret = is_win(board, ROW, COL);
		if (ret != 'C')
		{
			break;//如果不继续,打破循环
		}
	}
	if (ret == '*')
	{
		printf("玩家赢\n");
	}
	else if (ret == '#')
	{
		printf("电脑赢\n");
	}
	else if (ret == 'Q')
	{
		printf("平局\n");
	}
}
void test()
{
	srand((unsigned int)time(NULL));
	int input = 0;
	do
	{
		meau();
		printf("请输入>:\n");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("进入三子棋游戏\n");
			game();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("选择错误,请重新选择\n");
			break;
		}
	} while (input);
}
int main()
{
	test();
	return 0;
}

五、代码展示(三个文件合成为一个文件)

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define ROW 3
#define COL 3

//头文件声明函数

void init_board(char board[ROW][COL], int row, int col);

//打印棋盘
void print_board(char board[ROW][COL], int row, int col);

//玩家下棋
void player_move(char board[ROW][COL], int row, int col);

//电脑下棋
void computer_move(char board[ROW][COL], int row, int col);

//判断输赢
char is_win(char board[ROW][COL], int row, int col);
void meau()
{
	printf("********************************\n");
	printf("********     1.play      *******\n");
	printf("********     0.exit      *******\n");
	printf("********************************\n");
}
void game()
{
	char board[ROW][COL];
	//初始化棋盘
	init_board(board, ROW, COL);
	//打印棋盘
	print_board(board, ROW, COL);
	//玩家下棋
	char ret = ' ';
	//ret用来接受一个返回值,用来判断输赢
	while (1)
	{
		player_move(board, ROW, COL);
		print_board(board, ROW, COL);
		ret = is_win(board, ROW, COL);
		//判断游戏是否继续
		if (ret != 'C')
		{
			break;//如果不继续,打破循环
		}
		computer_move(board, ROW, COL);
		print_board(board, ROW, COL);
		//判断游戏是否继续
		ret = is_win(board, ROW, COL);
		if (ret != 'C')
		{
			break;//如果不继续,打破循环
		}
	}
	if (ret == '*')
	{
		printf("玩家赢\n");
	}
	else if (ret == '#')
	{
		printf("电脑赢\n");
	}
	else if (ret == 'Q')
	{
		printf("平局\n");
	}
}
void test()
{
	srand((unsigned int)time(NULL));
	int input = 0;
	do
	{
		meau();
		printf("请输入>:\n");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("进入三子棋游戏\n");
			game();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("选择错误,请重新选择\n");
			break;
		}
	} while (input);
}
int main()
{
	test();
	return 0;
}
//初始化棋盘
void init_board(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 print_board(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 player_move(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)
		{
			if (board[x - 1][y - 1] == ' ')
			{
				board[x - 1][y - 1] = '*';
				break;
			}
			else
			{
				printf("该位置已经有棋子了,请重新下棋");
			}
		}
		else
		{
			printf("坐标非法,请重新输入\n");
		}
	}

}

//电脑下棋
void computer_move(char board[ROW][COL], int row, int col)
{
	int x = 0;
	int y = 0;
	printf("电脑下棋\n");
	while (1)
	{
		x = rand() % row + 1;
		y = rand() % col + 1;
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (board[x - 1][y - 1] == ' ')
			{
				board[x - 1][y - 1] = '#';
				break;
			}
		}
	}
}
//判断棋盘是否满了
int is_full(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 is_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 < col; 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 (is_full(board, row, col) == 1)
	{
		return 'Q';
	}
	return 'C';
}


总结

本节主要讲解了三子棋的详细实现,为大家熟练的打通了各种思考逻辑,为什么是这样子,常见的错误。同时最终也给出了三子棋的最终代码,分文件和合并为一个文件都给出了具体的代码。如果对你有帮助,不要忘记点赞加收藏哦!!!

  • 40
    点赞
  • 68
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 29
    评论
以下是 C 语言井字棋代码加注释: ``` #include <stdio.h> #define BOARD_SIZE 3 // 定义棋盘大小为3 int board[BOARD_SIZE][BOARD_SIZE]; // 存储棋盘的二维数组 void init_board() { // 初始化棋盘,将所有位置都设置为0 int i, j; for (i = 0; i < BOARD_SIZE; i++) { for (j = 0; j < BOARD_SIZE; j++) { board[i][j] = 0; } } } void print_board() { // 打印棋盘 int i, j; for (i = 0; i < BOARD_SIZE; i++) { for (j = 0; j < BOARD_SIZE; j++) { if (board[i][j] == 0) { printf("_ "); // 未下棋的位置用"_"表示 } else if (board[i][j] == 1) { printf("X "); // 玩家1下的棋用"X"表示 } else if (board[i][j] == 2) { printf("O "); // 玩家2下的棋用"O"表示 } } printf("\n"); } } int check_win(int player) { // 检查指定玩家是否胜利,player为1表示玩家1,player为2表示玩家2 int i, j, win; // 检查每一行是否连成一线 for (i = 0; i < BOARD_SIZE; i++) { win = 1; for (j = 0; j < BOARD_SIZE; j++) { if (board[i][j] != player) { win = 0; break; } } if (win) { return 1; } } // 检查每一列是否连成一线 for (i = 0; i < BOARD_SIZE; i++) { win = 1; for (j = 0; j < BOARD_SIZE; j++) { if (board[j][i] != player) { win = 0; break; } } if (win) { return 1; } } // 检查对角线是否连成一线 win = 1; for (i = 0; i < BOARD_SIZE; i++) { if (board[i][i] != player) { win = 0; break; } } if (win) { return 1; } // 检查反对角线是否连成一线 win = 1; for (i = 0; i < BOARD_SIZE; i++) { if (board[i][BOARD_SIZE - 1 - i] != player) { win = 0; break; } } if (win) { return 1; } return 0; // 没有胜利返回0 } int main() { int i, j, row, col, player = 1, num_moves = 0; init_board(); // 初始化棋盘 while (1) { printf("Player %d's turn:\n", player); print_board(); // 打印棋盘 printf("Enter row and column to place %c: ", player == 1 ? 'X' : 'O'); scanf("%d %d", &row, &col); if (board[row][col] != 0) { // 如果该位置已有棋子,重新输入 printf("Invalid move, try again.\n"); continue; } board[row][col] = player; // 将棋子放入棋盘 if (check_win(player)) { // 检查是否胜利 printf("Player %d wins!\n", player); print_board(); break; } num_moves++; // 记录下棋步数 if (num_moves == BOARD_SIZE * BOARD_SIZE) { // 如果下满了棋盘,宣布平局 printf("Draw!\n"); print_board(); break; } player = player == 1 ? 2 : 1; // 切换玩家 } return 0; } ``` 注释中详细解释了每个函数和变量的作用,以及程序的流程。
评论 29
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

青色_忘川

你的鼓励是我创作的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值