二维数组应用:井字棋

一  井字棋游戏整体逻辑

       1.首先打印一个 3 * 3 的空白棋盘,然后根据输入在棋盘上打印棋子,最后根据井字棋规则输出赢家。

        那么怎么才能打印空白棋盘呢,看到3 * 3的棋盘,我首先想到了棋盘的样子和二维数组在我们想象中的样子一样(实际上二维数组在内存中是连续的空间,并非一排一排的),所以我们可以用二维数组代替棋盘中的格子,落子后在对应位置存储一个“棋子”,然后根据井字棋规则判断数组内对应的位置是否为同一棋子即可。

void PrintBoard(char arr[ROW][COL], int row, int col)//打印棋盘
{
	for (int i = 0; i < row ; i++)
	{
		for (int j = 0; j < col; j++)
		{
			printf(" %c ", arr[i][j]);
			if (j < col - 1)
			{
				printf("|");
			}
		}
		printf("\n");
		for (int j = 0; j < col ; j++)
		{
			if (i < row - 1)
			{
				printf("---");
				if (j < col - 1)
				{
					printf("|");
				}
			}		
		}
		if(i < row - 1)
			printf("\n");
	}
}

        代码如上,首先将数组与行数列数传入函数,采用两个for循环嵌套打印数组内容并同时打印棋盘分隔线,效果如下:

         你可能发现,你打印的并非如上图所示,而是每格中都是  ?,因为你缺少了重要的一步:数组初始化。

        2.数组初始化,顾名思义,游戏刚开始时,你必须将数组内的所有内容重置为空格,这样才能打印出上述效果,很简单,与打印相同,采用两个for循环嵌套,即可完成,如下:

void InitBoard(char arr[ROW][COL], int row, int col)
{
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			arr[i][j] = ' ';
		}
	}
}

        3.棋盘打印好了自然就该下棋了,下棋的逻辑很简单,首先提示玩家输入下棋的坐标,由于绝大多数人会根据格子数输入,但数组的下标是0开头,因此只需要将玩家输入的player_row与player_col都减一,就能得到玩家想要落子的数组下标。得到输入的下一步必定是判断,我们需要判断的点有两个,第一个,判断这两个值是否都在棋盘内,若不在,则需要玩家重新输入,当输入合法时,就该判断第二个,判断落子点是否已经有棋子,如果有,仍需要玩家重新输入并提示玩家坐标已有棋子,大体逻辑如上,代码如下:

void PlayerMove(char arr[ROW][COL], int row, int col)
{
	int player_row = -1;
	int player_col = -1;
	
	while (1)
	{
		printf("请输入要下的坐标:");
		if (scanf("%d%d", &player_row, &player_col) == 2)
		{
			if (player_row - 1 >= 0 && player_row - 1 < row && player_col - 1 >= 0 && player_col - 1 < col)
			{
				if (arr[player_row - 1][player_col - 1] == ' ')
				{
					arr[player_row - 1][player_col - 1] = '*';
					break;
				}
				else
				{
					printf("位置已被占用,请重新输入:\n");					
				}
			}
			else
			{
				printf("输入不合法,请重新输入:\n");				
			}
		}
		else
		{
			printf("输入不合法,请重新输入:\n");	
		}
	}
}

        玩家下棋的逻辑已经实现了,现在轮到电脑了,让还是菜鸟的我写一个AI陪我下井字棋还是太难为我了,我只能通过让电脑生成随机数的方法来完成电脑的落子,那么随机数怎么生成呢?

        这里我们用rand()这个函数来生成随机数,rand可以生成0~32767(0x7fff)的随机数, 但是rand()使用起来会发现,他一旦生成随机值,在此次运行当中,就不会再发生改变了。为什么呢,因为:

rand函数调用
rand()函数每次调用前都会查询是否调用过srand(seed),是否给seed设定了一个值,如果有那么它会自动调用srand(seed)一次来初始化它的起始值
若之前没有调用srand(seed),那么系统会自动给seed赋初始值,即srand(1)自动调用它一次
————————————————
版权声明:本文为CSDN博主「TLpigff」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/lvyibin890/article/details/80141412

        说明用rand()函数生成随机数需要调用srand()初始化起始值,而srand()必须给一个会时刻发生改变的参数才能让rand()实现我们的需求,而时刻改变的参数,我目前能想到的只有一个:时间

        电脑如何拿到时间的数据呢?time()函数,time_t time(time_t * timer),给time函数传入一个NULL空指针,再把time作为参数给srand就可以实现每次都有不同的起始值,代码如下:

srand((unsigned int)time(NULL));
//重置时间戳起点,为后续随机值生成用,只需调用一次,不用多次调用

void ComputerMove(char arr[ROW][COL], int row, int col)
{
	
	printf("电脑的回合\n");

	while (1)
	{
		int computer_row = rand() % row;
		int computer_col = rand() % col;
		if (arr[computer_row][computer_col] == ' ')
		{
			arr[computer_row][computer_col] = '#';
			break;
		}
	}
}

        这里用computer_row和computer_col接收随机值,因为我们只需要小于棋盘大小的值,因此用rand % 行或列,就能得到所需的合法数值,再判断目标位置是否有棋子,有棋子则重新生成数据再比较,没有棋子则落子。

        4.现在就剩下最后一步了,下棋了就改判断输赢了,井字棋是只要有三个相连即可获胜,而3 * 3棋盘中只有每列  每行  每个对角线相连,这三种情况才能获胜,因此采用面向结果编程,可以得到如下代码:

char WhichWin(char arr[ROW][COL], int row, int col)
{
	for (int i = 0; i < row; i++)
	{
		if (arr[i][0] == arr[i][1] && arr[i][1] == arr[i][2] && arr[i][0] != ' ')//判断一行是否都是'*'或'#'
			return arr[i][0]; 
	}
	for (int i = 0; i < col; i++)
	{
		if (arr[0][i] == arr[1][i] && arr[1][i] == arr[2][i] && arr[0][i] != ' ')//判断一列是否都是'*'或'#'
			return arr[0][i];
	}
	//判断对角线
	if (arr[0][0] == arr[1][1] && arr[1][1] == arr[2][2] && arr[1][1]!= ' ')
	{
		return arr[0][0];
	}
	if (arr[0][2] == arr[1][1] && arr[1][1] == arr[2][0] && arr[1][1] != ' ')
	{
		return arr[0][2];
	}
	if (BoardFull(arr,row,col) == 1)//判断棋盘满了吗,走到这里说明还没有赢家,如果棋盘满了还没有赢家,说明平局,返回Q
		return 'Q';
	return 'C';//都不满足就继续

}

        这里只要判断满足胜利条件,即每行、每列或者两条对角线的三个数组元素相等,就出现了胜者,那么,用函数返回这三个数组中的其中一个,就是返回了胜者的棋子,就能根据谁的棋子return了,谁就获胜了。同时,井字棋也会出现平局的情况,怎么平局呢?棋盘满了,但是没人赢,那我们只需再写一个函数用于判断棋盘是否全为空格即可,代码如下:

int BoardFull(char arr[ROW][COL],int row,int col)//判断棋盘是否满了,满了返回1,不满返回0
{
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			if (arr[i][j] == ' ')//有空格则说明棋盘还有位置,不满,返回0
				return 0;
		}
	}
	return 1;//走到这里说明:遍历一遍出了循环,说明一个空格都没有,棋盘已满,返回1
}

        和初始化以及打印棋盘一样,面对二维数组,两个for循环嵌套,判断数组元素是否为空格,一旦发现空格,说明棋盘非空,立刻返回零,要是遍历完了数组,都找不到空格,说明是期盼满了,返回1,告诉判断输赢的函数棋盘已经满了。

        至此,实现游戏所必需的接口函数基本已经完成,让我们写一个game函数测试一下吧

void PrintWinner(char WhichWin)// *--玩家赢   #--电脑赢   Q--平局   C--继续游戏
{
	if (WhichWin == '*')
	{
		printf("玩家赢\n");
		//return 1;
	}
	else if (WhichWin == '#')
	{
		printf("电脑赢\n");
		//return 2;
	}
	else if (WhichWin == 'Q')
	{
		printf("平局\n");
		//return 3;
	}
}

void menu()
{
	printf("----------------------------------\n");
	printf("--------------1.game--------------\n");
	printf("--------------0.exit--------------\n");
	printf("----------------------------------\n");
	printf("\n");
	printf("请选择(0/1):");
}

void game()
{
	srand((unsigned int)time(NULL));//重置时间戳起点,为后续随机值生成用,只需调用一次,不用多次调用
	char arr[ROW][COL];
	InitBoard(arr, ROW, COL);//初始化数组,初始为空格
	char flag = 0;
	PrintBoard(arr, ROW, COL);//打印棋盘

	while (1)
	{
		PlayerMove(arr, ROW, COL);//玩家输入坐标下棋
		PrintBoard(arr, ROW, COL);
		flag = WhichWin(arr, ROW, COL);
		if (flag != 'C')
		{
			break;
		}
		ComputerMove(arr, ROW, COL);
		PrintBoard(arr, ROW, COL);
		flag = WhichWin(arr, ROW, COL);
		if (flag != 'C')
		{
			break;
		}
	}
	PrintWinner(flag);
}

int main()
{
	int input = 1;
	do
	{
		menu();
		if (scanf("%d", &input) == 1)
		{
			switch (input)
			{
			case 1:
				game();
				break;
			case 0:

				break;
			default:
				printf("输入不合法,请重新输入\n");
				break;
			}
		}
	} while (input);
	return 0;
}

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值