【入门级小游戏】C语言数组函数:解析三(N)子棋

在这里插入图片描述

C语言也学习一段时间了,为了巩固我们学习的知识,今天我们写一个三子棋的小游戏。这对初学者是个大工程,跟着我一起开始吧。

一.逻辑思维梳理

我们今天写的是三子棋小游戏,说到游戏肯定就有很多模块组成,所以为了提升游戏的可维护性和移植性
,我们应该采用模块化编写程序,将不同的版块分装在不同文件下。
在这里插入图片描述
上图为不同文件的任务,接下来我们就开始逐步讲解我们三字棋的实现。

二.代码板块的实现

1.游戏主逻辑实现

在c语言程序中 最重要的就是main函数,所以我们现在main函数中引用test函数,实现代码分装 使得我们的编写更有层次。
接下来我们开始实现test函数。 在游戏的开始界面我们应该需要一个入口 可以选择玩游戏 或者退出游戏。do while循环可以很好的实现我们的需求。

void test()
{
	int i = 0;
	do
	{
		menu();
		printf("请选择>");
		scanf("%d", &i);
		switch (i)
		{
		case 1:
			game();
			break;
		case 0:
			printf("退出游戏n");
			break;
		default:
			printf("选择错误,请重新选择:>n");
			break;
		}
	} while (i);
}

switch在游戏界面选择中非常实用,在上图的代码逻辑实现中我们又引用了菜单meua函数和游戏主体函数game。
meua菜单需要实现一个界面 提供玩游戏和 退出游戏的人两个选择 代码和效果图如下

void menu()
{
	printf("*****************************n");
	printf("********  1.play    *********n");
	printf("********  0.exit    *********n");
	printf("*****************************n");
}

在这里插入图片描述
接下来我们着重讲解game函数。

2.棋盘的打印的数组的创建

在菜单选择开始游戏后我们进入到游戏后,我们应该可以看到三子棋的棋盘。在打印棋盘前,我们应该创建一个二维数组对应三子棋盘上的9个位置 接受棋子。

#define ROW 3
#define COL 3

首先我们.h文件中定义好数组的行和列,而不是直接对数组规定行和列,这样有利于后续我们对三子棋升级优化

	char board[ROW][COL];//创建3*3数组存放字符

接着我们创建一个二维数组,用来接受存放旗子。
在这里插入图片描述

这是我们想要实现的棋盘效果,但是我们并没有对数组board初始化,所以数组中默认储存的是0,这就达不到上图的效果。
所以在打印棋盘之前,我们应当先对棋盘初始化,将数组的元素都定义为空格。
我们在test中引用函数 Int_board(board, ROW, COL);
在game.h声明函数后 在game.c文件中编写这个函数。

void Int_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] = ' ';
		}
	}
}

这里只需要用一个简单遍历 就可以实现。
接下来我们就要打印棋盘。棋盘的每两行可以看成一个部分,一共有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");
		}
	}
}

上面我们吧棋盘分为了三个部分,所以在整个打印代码段 都是被一个for套嵌的。之后只要编写一部分的打印就可以了。

2.玩家下棋的实现

在编写玩家下棋的代码部分时 我们需要注意一下几点:
1.数组接受玩家下的棋子
2.判断玩家坐标输入是否合法
3.玩家输入坐标后 将棋盘打印出来

void player_move(char board[ROW][COL], int row, int  col)
{
	printf("玩家下棋n");
	while (1)
	{
		printf("玩家输入坐标:>");
		int a, b;
		scanf("%d %d", &a, &b);
		if (a >= 1 && a <= row && b >= 1 && b <= col)
		{
			if (board[a - 1][b - 1] == ' ')
			{
				board[a - 1][b - 1] = '*';
				break;
			}
			else
			{
				printf("该坐标已被占用n");
			}
		}
		else
		{
			printf("坐标非法n");
		}
	}
}

当玩家scanf输入坐标后,应该要判断坐标输入是否合法,如果玩家输入的坐标已经被占用或者超出了二维数组的范围,就应该循环再次进入玩家输入坐标的地方,知道坐标输入正确break跳出循环。
当玩家正确输入坐标后,我们要讲新的棋盘打印出来。所以我们需要再次在test.c文件中引用 Print_board(board, ROW, COL); 函数。

3.电脑随机下棋的实现
void comper_move(char board[ROW][COL], int row, int  col)
{
	srand((unsigned int)time(NULL));
	while (1)
	{
		printf("电脑下棋n");
		int x = rand() % row;
		int y = rand() % col;
		if (board[x][y] == ' ')
		{
			board[x][y] = '#';
			break;
		}
	}
}

这里需要注意的是电脑随机坐标应该如何生成,还是运用我们熟知的rand函数,srand函数他们需要引用头文件**#include <stdio.h>**,rand函数借助srand种子由time强制转化为unsigned int类型时间戳生成的随机数应该不能超过二维数组行列的范围,所以我们写了

		int x = rand() % row;
		int y = rand() % col;

这里我们有个结论可以 理解记忆一下
在这里插入图片描述
接着我们再将电脑随机下的棋 再打印出来。

4.判断输赢

游戏的结果判断 我们现在test.c文件中编写出逻辑 再去编写具体函数
在这里我们规定 输赢平局的返回值
在这里插入图片描述

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

用ret接受 is_win函数的返回值
接下来我们主要编写is_win函数 以及对他的优化升级
在这里插入图片描述
在三子棋中有这几种赢法

static 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[1][1] != ' ')
	{
		return board[1][1];
	}
	if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[1][1] != ' ')
	{
		return board[1][1];
	}

	//平局?
	if (is_full(board, row, col) == 1)
	{
		return 'Q';
	}

	//继续
	//没有玩家或者电脑赢,也没有平局,游戏继续
	return 'C';
}

我们将所有情况列举出来 再写出平局的情况将 字符返回到ret
判断游戏是否需要继续。

三.分析优化和升级

在上面判断三子棋是否输赢时,我们是将所有的情况都一一列举出来。这样就限制的游戏的升级 所以我们能不能优化is_win函数呢?

char is_win(char board[ROW][COL], int row, int col)
{
	int flag1 = 0;///玩家棋数
	int flag2 = 0;//电脑棋数
	int i, j;
	for (i = 0; i < row; i++)///行  计算
	{
		flag1 = flag2 = 0;
		for (j = 0; j < col; j++)
		{
			if (board[i][j] == '*')
			{
				flag1++;
			}
			if (board[i][j] == '#')
			{
				flag2++;
			}
		}
		if (flag1 == col)///修改怎么应该实现N子棋
		{
			return '*';
		}
		if (flag2 == col)///修改怎么应该实现N子棋
		{
			return '#';
		}
	} 
	for (j = 0; j < col; j++)///列  计算
	{
		flag1 = flag2 = 0;
		for (i = 0; i < row; i++)
		{
			if (board[i][j] == '*')
			{
				flag1++;
			}
			if (board[i][j] == '#')
			{
				flag2++;
			}
		}
		if (flag1 == col)///修改怎么应该实现N子棋
		{
			return '*';
		}
		if (flag2 == col)///修改怎么应该实现N子棋
		{
			return '#';
		}
	}
	flag1 = flag2 = 0;

	for (i = 0, j = 0; i < row, j < col; i++, j++)//正对角线
	{
		if (board[i][j] == '*')
		{
			flag1++;
		}
		if (board[i][j] == '#')
		{
			flag2++;
		}
	}
	if (flag1 == col)///修改怎么应该实现N子棋
	{
		return '*';
	}
	if (flag2 == col)///修改怎么应该实现N子棋
	{
		return '#';
	}	
	flag1 = flag2 = 0;
	for (i = 0, j = col-1; i<row, j >=0; i++, j--)//逆对角线
	{
		if (board[i][j] == '*')
		{
			flag1++;
		}
		if (board[i][j] == '#')
		{
			flag2++;
		}
	}
	if (flag1 == col)///修改怎么应该实现N子棋
	{
		return '*';
	}
	if (flag2 == col)///修改怎么应该实现N子棋
	{
		return '#';
	}
	if (is_full(board, row, col) == 1)
	{
		return 'Q';
	}

	//继续
	//没有玩家或者电脑赢,也没有平局,游戏继续
	return 'C';
}

上图我用计数器写了 判断棋盘输赢的代码,虽看起来没有原来简单,但是如果升级到N子棋时,这种写法就比较方便
在这里插入图片描述
在我玩了七七九十九局后 感觉这个电脑实在太蠢了,所以我们还有什么方法能将电脑下棋更智能些呢? 我们在下篇讲解。

关于三子棋游戏的分享就到这里。
感谢相遇,共同进步。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>