[C语言] N子棋小游戏的实现

今天做一个N子棋的小游戏,就是3x3,4x4直到NxN都可以实现的一个小游戏

要求:能够按照输入坐标的方式下棋且能够判断输赢

先写出主函数的大体框架:

int main()
{
	int input = 0;
	srand((unsigned)time(NULL));
	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
		{
			game();
			break;
		}
		case 0:
		{
			printf("退出游戏\n");
			break;
		}
		default:
		{
			printf("选择错误,请重行选择\n");
			break;
		}
		}
	} while (input);
	return 0;
}

srand的设置是为了之后电脑能够按照随机数在棋盘上随机下棋


1.菜单的创建

void menu()
{
	printf("**********************************\n");
	printf("*********** 1.游戏开始 ***********\n");
	printf("*********** 0.游戏结束 ***********\n");
	printf("**********************************\n");
}

2.game函数的编写

void game()
{
	char out;
	//存储游戏内部数据
	char board[ROW][COL];
	//初始化棋盘(空格)
	InBoard(board, ROW, COL);
	//打印棋盘
	PrintBoard(board, ROW, COL);
	while (1)
	{
		//玩家行动
		Player(board, ROW, COL);
		PrintBoard(board, ROW, COL);
		//判断游戏是否结束
		out = Win(board, ROW, COL);
		if (out != 'G')
		{
			break;
		}
		//电脑行动
		PC(board, ROW, COL);
		PrintBoard(board, ROW, COL);
		//判断游戏是否结束
		out = Win(board, ROW, COL);
		if (out != 'G')
		{
			break;
		}
	}
	//游戏结束判断胜负
	if (out == '*')
	{
		printf("玩家胜利\n");
	}
	else if (out == '#')
	{
		printf("电脑胜利\n");
	}
	else
	{
		printf("平局\n");
	}
}

       这里我创建的存储游戏内部数据的二位数组的数据用的是两个常量,我定义了两个全局常量,为了方便随时改动想玩的N子棋的大小

       我这里想的是玩家下的棋为“ * ”,电脑下的棋为“ # ”

       这里所有的函数先提前想好,然后一步一步的写实现的函数内容

       游戏的总流程:

1.先打印一个空棋盘给玩家看

2.玩家就可以通过输入坐标的方式在空棋盘上下棋,然后判断玩家下了这一步棋之后,游戏有没有胜负或者平局的结果,游戏还要不要继续,然后把下了这个子之后的棋盘再次打印出来

3.让电脑在空棋盘上随机下棋(还不知道怎么写比较有智慧的电脑),同时也要判断电脑下完这步棋之后的游戏结果,需不需要继续游戏,然后把下了这个子之后的棋盘再次打印出来

4.最后,如果游戏终止了,判断是玩家胜利还是电脑胜利还是平局的结果


1.首先需要打印一个空棋盘给玩家看到:

void InBoard(char board[ROW][COL], int row, int col)
{
	int a = 0;
	int b = 0;
	for (a = 0; a < row; a++)
	{
		for (b = 0; b < col; b++)
		{
			board[a][b] = ' ';
		}
	}
}

这个函数是用来先把棋盘初始化,把游戏存储的数据全部变成空格给玩家看,不初始化的话,棋盘打印出来会出现乱码的情况

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

这里打印棋盘是每一次改变二维数组中的数字都可以打印出来改变以后的棋盘的

这里是根据需要的棋盘大小打印出棋盘格式,想要3x3的棋盘就打印一个井字格的棋盘,想要4x4的也可以打印更大的,只需要在全局常量那里更改常量的值就可以了(这个函数之后会经常用到)


2.然后玩家就可以开始输入坐标来在棋盘上下棋

void Player(char board[ROW][COL], int row, int col)
{
	int a = 0;
	int b = 0;
	printf("玩家行动\n");
	while (1)
	{
		printf("请输入坐标:>");
		scanf("%d %d", &a, &b);
		//判断输入的坐标是否合法
		if (a >= 1 && a <= col && b >= 1 && b <= row)
		{
			if (board[a - 1][b - 1] == ' ')
			{
				board[a - 1][b - 1] = '*';
				break;
			}
			else
			{
				printf("输入的坐标内已有棋子,请重新输入\n");
			}
		}
		else
		{
			printf("坐标输入错误,请重新输入\n");
		}

	}
}

让玩家输入的时候就需要判断玩家输入的坐标的合法性:

1.有没有超出棋盘的大小

2.玩家想下棋的位置是不是空的位置可以下棋

       因为玩家下棋的第一个格子的坐标是“1 1”,但是在二维数组中第一个格子的坐标为“0 0”,所以存储的格子的坐标应该是玩家输入的每个数字-1,才能保证输入的正确性

       如果玩家输入的坐标合法,就在棋盘相应位置的二维数组中存储“ * ”

(判断游戏进程放到电脑下棋之后写)


3.电脑下棋

void PC(char board[ROW][COL], int row, int col)
{
	printf("电脑行动\n");
	while (1)
	{
		int a = rand() % row;
		int b = rand() % col;
		if (board[a][b] == ' ')
		{
			board[a][b] = '#';
			break;
		}
	}
}

        我采用的是给电脑两个随机数字的坐标,随机数采用的时间戳的随机方式,因为电脑输入的坐标是我们可控的,所以不需要判断电脑输入的坐标的合法性,只需要判断电脑输入的坐标内部有没有元素。而且不需要提示电脑,只需要一直循环,找到那个空的坐标把“ # ”填入二维数组就可以了


4.判断游戏是否继续,不继续了的胜负和平局

这里我判断游戏结果的方式为:
返回*为玩家胜利
返回#为电脑胜利
返回N为平局
返回G为游戏继续

首先需要判断一行的相同

char Win(char board[ROW][COL], int row, int col)
{
	int a = 0;
	int b = 0;
	char c;
	//判断一行
	for (a = 0; a < row; a++)
	{
		for (b = 0; b < col - 1; b++)
		{
			if ((board[a][b] == board[a][b + 1] && board[a][b + 1] == ' ') || board[a][b] != board[a][b + 1])
			{
				break;
			}
			if (b == row - 2)
			{
				return board[a][b];
			}
		}
	}

       前面创建的字符类型c可以先不管,后面判断的时候要用

       这里我就是一行一行的判断的,如果一行的元素全部一样的话,嵌套的for循环里面的b就会跑到最大值,就把那一行最后一个格子里面放的元素返回去(这样的话不管是玩家还是电脑都可以使用这个判断函数)

再判断一行的相同

//判断一列
	for (b = 0; b < col; b++)
	{
		//if (board[0][a] == board[1][a] && board[1][a] == board[2][a] && board[1][a] != ' ')
		//{
		//	return board[1][a];
		//}
		for (a = 0; a < row - 1; a++)
		{
			if (board[a][b] == board[a + 1][b] && board[a + 1][b] == ' ' || board[a][b] != board[a + 1][b])
			{
				break;
			}
			if (a == row - 2)
			{
				return board[a][b];
			}
		}
	}

跟上面判断一行的函数逻辑基本相同,知识改变了a和b的变化而已

再判断斜角相同

//判断左上到右下对角线
	while (1)
	{
		for (a = 0; a < row-1; a++)
		{
			for (b = 0; b < col-1; b++)
			{
				if (a == b && board[a][b] != ' ')
				{
					c = board[a][b];
					if (c != board[a + 1][b + 1])
					{
						goto right;
					}
				}
				if (a == b && board[a][b] == ' ')
				{
					goto right;
				}
			}
		}
		return board[a][b];
	}
	right:

我用的判断方式是判断左上角到右下角的元素是否不同,先把第一个元素存储在‘c’中,然后和下一个袁术进行比较,如果不同或者其中还有空格就跳出这个判断直接到下一个判断,下面的右上角到左下角同理

//判断右上到左下对角线
	while (1)
	{
		for (a = 0; a < row-1; a++)
		{
			for (b = col-1; b > 0; b--)
			{
				if ((a + b) == (row - 1) && board[a][b] != ' ')
				{
					c = board[a][b];
					if (c != board[a + 1][b - 1])
					{
						goto third;
					}
				}
				if ((a + b) == (row - 1) && board[a][b] == ' ')
				{
					goto third;
				}
			}
		}
		return board[a][b];
	}
	third:

逻辑都是一样的,只是改变检索的方向而已

这些所有的判断正确之后都是返回的检索的最后一个格子里的元素

最后判断平局

	int tmp = FULL(board, row, col);
	if (tmp == 1)
	{
		return 'N';
	}
	return 'G';
}

判断平局这里我又写了个函数,如果函数的返回值为1,就是平局结果,如果返回别的游戏就要继续

int FULL(char board[ROW][COL], int row, int col)
{
	int a = 0;
	int b = 0;
	for (a = 0; a < row; a++)
	{
		for (b = 0; b < col; b++)
		{
			if (board[a][b] == ' ')
			{
				return 0;//棋盘没满
			}
		}
	}
	return 1;
}

因为上面已经把所有的可以胜利的方式都判断过了

所以这里判断平局只需要判断棋盘是不是被填满了,并且没有任何一方胜利

如果检索完所有的格子都不是空格的话,就返回1,只要还有一个空格就返回0


最后这个判断函数放在玩家行动和电脑行动的后面

while (1)
	{

		//玩家行动
		Player(board, ROW, COL);
		PrintBoard(board, ROW, COL);
		//判断游戏是否结束
		out = Win(board, ROW, COL);
		if (out != 'G')
		{
			break;
		}
		//电脑行动
		PC(board, ROW, COL);
		PrintBoard(board, ROW, COL);
		//判断游戏是否结束
		out = Win(board, ROW, COL);
		if (out != 'G')
		{
			break;
		}
	}

如果没有人胜利并且棋盘还没下满的时候函数返回的应该是‘G’,这样游戏就继续循环

如果有人胜利了,或者平局了,函数返回的有可能是‘ * ’ , ‘ # ’, ‘G’这其中的任何一个,就会跳出这个while循环来到下面判断游戏的结果


5.最终的结果判断

//游戏结束判断胜负
	if (out == '*')
	{
		printf("玩家胜利\n");
	}
	else if (out == '#')
	{
		printf("电脑胜利\n");
	}
	else
	{
		printf("平局\n");
	}

这个时候分辨前面判断返回的out里面存储的是什么元素

然后给最终的结果反馈

然后整个程序就跳回最开始的主函数里面,再次判断是否想再玩一次这个游戏


我觉得这个游戏我在判断输赢部分写的还是复杂了,写出来的电脑也不够智能,我也不知道怎么优化判断输赢的部分了,有大佬能帮我优化一下吗(虚心接受指导)

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值