简单的三子棋

经过这几天的学习,有所感悟,简单的写一个小游戏来检验成果

        在写三子棋这个简单的小游戏之前首先还得提一嘴数组,这也是即函数之后所学,数组这部分比较简单,但却很实用。

        首先简单的介绍一下数组,数组跟高中所学的函数集合比较类似,他也是代表着相同元素的集合,但就是不同的元素表达的方式可能不太一样,大体的定义形式为:数组元素类型+数组命[元素个数]={元素内容}或者“字符串”例如定义整型数组:int arr[5]={1,2,3,4,5};或者:char arr[5]=“abcde”;元素个数可以为空,如果为空就相当于元素内容为多少,元素个数就是多少。当然这块也有一个隐藏的事情就是数组长度,并不是元素个数是多少,数组长度就是多少。因为每个数组的末尾都会隐藏一个“\0”,这个字符是一直都有的,意义在于停止,就好比你输入一组数组,计算机怎么能知道你是输入完了还是没输入完呢?前面定义的元素个数并不能说明,因为你完全可以少些,多些,但是你定义了元素个数就相当于跟计算机说,我在我定义的元素个数的位置加了“\0”,所以计算机只有读到”\0”时候就知道,“啊,到位置了,可以停了” 这样数组才算打印出你想要的个数:例如int arr[5]={1,2,3,4,5},计算机就会读到“1,2,3,4,5”,如果int arr[5]={1,2,3,4,5,6};计算机也会读到“1,2,3,4,5”,如果int arr[5]={1,2,3};那么计算机就会自动在空的位置填0,变成“1,2,3,0,0”;再说一个重中之重那就是,数组名既可以表示数组的地址,也可以表示数组首元素的地址,两个在数值上是一样的,但是含义不一样。说完这部分,大体提一下数组的分类,其实可以说是一维数组或者多维数组,顾名思义,定义一个[]的就是一维数组,定义两个[]的就是二维数组,依此类推,简单记一下小笔记二维数组部分例如int arr[行][列]={};行代表这有几行,列代表着每行有几个元素,这里行可以省略但是列不能。列要省略了就不能确定一行有多少个元素,也不能确定数组有多少行。

        数组还有一点就是,他起始的位置的数组下标为0,从0开始,为什么从0开始,有兴趣者可以去搜一下,我在这里就不详细介绍了。

        OK,接下来开始本次的重点了,三子棋的实现。

根据以往学到的内容,三子棋的实现我也分为三个部分,主函数,函数定义,函数实现。接下来请君看详解代码:

开始简单写一个游戏,说起游戏最开始能引入眼帘的就是菜单,这里将引用一个名为菜单的库函数menu很好使:

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

做到最基本的选择功能,这样到时候一接收就好了,当然都这种选择了,肯定会用到switch语句了,这里很基本我也就不一一细说了之间看代码:

int main()
{	
	int a = 0;
		menu();
		printf("请选择: \n");
		scanf_s("%d" ,& a);
		switch (a)
		{
			case 1:
				game();
				break;
			case 0:
				printf("游戏结束");
				break;
			default:
				printf("输入有误,请重新输入");
				break;
        }
	return 0;
}

那么既然是游戏,我们就不能让他就玩一次,这样也不过隐,总不能想玩第二次的时候就得重新运行,很麻烦所以我们需要在外面套上一个循环,那么循环条件是什么呢?总不能我们人为规定你就能玩三次,玩四次这样,所以for循环肯定是不行了,那么就是while,do while循环了,根据所写的条件,我们这里选择了do while循环(如果有不懂,请看前面循环章节),那么完整的主函数代码就基本上出来了:

int main()
{	
	int a = 0;
	do 
	{
		menu();
		printf("请选择: \n");
		scanf_s("%d" ,& a);
		switch (a)
		{
			case 1:
				game();
				break;
			case 0:
				printf("游戏结束");
				break;
			default:
				printf("输入有误,请重新输入");
				break;
		}
	} while (a);
	return 0;
}

菜单环节完事,那么就开始我们今天的重头戏,游戏函数的编写,三子棋顾名思义就是一种下棋的游戏,下棋游戏的基本条件就是咱们得有一个棋盘,所以下面来写一写棋盘的代码:首先还是在主函数页面里写一下游戏的大框:

void game()
{	
	char ret = 0;
	char board[Hang][Lie] = {0};
	InitBoard(board, Hang, Lie);//初始化棋盘
	DisplayBoard(board, Hang, Lie);//打印棋盘

}

这里我就为了让小白能读懂,我之间有拼音代替了(当然也是为了让自己一下就能知道是啥,毕竟英语一般),ok现在就开始写关于棋盘的函数了,当然嗷,写这些函数之前一定要在头文件声明一下例如这样,往后的头文件声明我就不依次展示了,最后在总结,当然,我在头文件里先定义了行和列的全局变量,因为毕竟是棋盘吗,也为了以后方便好改,所以没有定义具体数字,好了看代码:

#define Hang 3
#define Lie  3
#include<stdio.h>

void InitBoard(char board[Hang][Lie], int hang, int lie);
void DisplayBoard(char board[Hang][Lie], int hang, int lie);

接下来开始咱们的核心代码,三子棋棋盘的代码书写:定义棋盘和打印棋盘,其实这两部分的核心就是嵌套循环,嵌套for循环,跟之前写的乘法口诀表一样,三子棋嘛,肯定是3*3的方块,根据上述的二维数组,我们也轻松的就能知道那个是行,那个是列,一循环就出来了

void InitBoard(char board[Hang][Lie], int hang, int lie)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < hang; i++)
	{
		for (j = 0; j < lie; j++)
		{
			board[i][j] = ' ';
		}
	}
}

当然我肯定不能直接这样说,根据代码理解一下,我就不画图了,for循环是一层一层进行的,外面的第一层for循环会令i为0,然后带入第二层循环,这是i的值就固定住了,就开始j的不断变化,就会形成board[i][],也是在第i行j列中的列不断变化,直到把i行填满,依次类推,我们简单的棋盘就建好了。

那接下来就开始打印棋盘了,打印棋盘的原理跟这个相同也是两层循环如下:

void DisplayBoard(char board[Hang][Lie], int hang, int lie)
{
	int i = 0;
	for (i = 0; i < hang; i++)
	{
		int j = 0;
		for (j = 0; j < lie; j++) //打印一行数据
		{
			printf(" %c ", board[i][j]);   
			
		}
		
			printf("\n");
	}

}

但是当你这样打出来你就会发现,一片空白,毕竟我们建的就是一片空白的初始数据,所以我们在打印的时候就要稍加润色一下,让他有分割线的出现,如图

 根据图片我们就不难分析出来“|”这个标识肯定是从列循环中出来的,先就看一行,如果是打印出来那么这个顺序就是空白,竖线,空白,竖线,空白,空白就是我们的初始化数据,循环了三次,竖线在空白中间,可以理解为循环两次,但是在运行的时候,我们不用去在嵌套一个循环,可以把其看成一个整体,只不过在循环最后一次的时候,就不执行了就可以,所以根据前后顺序我们就能写出如下代码:

for (j = 0; j < lie; j++) //打印一行数据
		{
			printf(" %c ", board[i][j]);   
			if (j < lie - 1)               
				printf("|");
		}
	

打完竖线就开始打印横线了,为了让其不是一条直线有距离美,我们也需要用到列,让他变成一个独立小空间,代码如下:

        if (i < hang - 1)  //打印分割行
		{
			for (j = 0; j < lie; j++)
			{
				printf("---");             
				
			}
			printf("\n");
		}

所以这个整体代码如下:

void DisplayBoard(char board[Hang][Lie], int hang, int lie)
{
	int i = 0;
	for (i = 0; i < hang; i++)
	{
		int j = 0;
		for (j = 0; j < lie; j++) 
		{
			printf(" %c ", board[i][j]);   
			if (j < lie - 1)               
				printf("|");
		}
		printf("\n");
		if (i < hang - 1)  //打印分割行
		{
			for (j = 0; j < lie; j++)
			{
				printf("---");             
		
			}
			printf("\n");
		}
	}

}

这样,我们的棋盘初始化和打印棋盘就完事了,接下来就是行走规则了,我们这个肯定不能用鼠标点击控制走路,所以我们就接近键盘,用数组控制走路,毕竟我们定义的就是数组:当然我们也需要给玩家走的坐标和电脑走的坐标区分出来,我们暂定玩家走的标记为“*”,电脑走的为“#”,当然为了更加大众化,我们需要稍微手动改一下数组的下标,因为并不是所有人都知道数组是从0开始的,所以接下来就很简单,当然,为了能一直走下去,外面得套一个循环,代码如下:

void PlayerMove(char board[Hang][Lie], int hang, int lie)
{
	int x = 0;
	int y = 0;
	printf("玩家走: \n");
	while (1)
	{
		printf("请输入坐标:");
		scanf_s("%d%d", &x, &y);
		if (x >= 1 && x <= hang && y >= 1 && y <= lie)
		{
			if (board[x - 1][y - 1] = ' ')
			{
				board[x - 1][y - 1] = '*';
				break;
			}
			else
			{
				printf("该坐标被占用");
			}
		}
		else
		{
			printf("该坐标非法,请重新输入\n");
		}
	}
}

到这我们就完成了人操作的步骤,接下来就开始进行电脑的操作步骤了,解决电脑的操作步骤的最大问题就是,该如何让电脑自己行走,也就是相当于如何让电脑产生相应的随机数,这里我们就不得不介绍两个库函数了,rand和srand,首先我们来介绍一下rand,rand是C语言提供的能生成随机函数的一个函数,但是这个函数有一个弊端,那就是起点一样生成的随机函数也是一样的,就好比你第一次运行他随机出个3,第二次他还是会随机生成3,所以我们就需要改变他的起点,这样就出现了我们的srand函数,srand原型:void srand(unsigned int seed),其中srand设置产生一系列伪随机数发生器的起始点,要想把发生器重新初始化,可用1作seed值。任何其它的值都把发生器匿成一个随机的起始点。通俗来说他就是rand函数的随机起点的一个框架,seed就是那个随意改动的随机值,改动seed值,我们的随机函数rand就会产生一个新的随机函数,但是毕竟是电脑,我们不可能让他运行一次我们就给他输入一次,这样也不自动化了,那什么是不断在变化的呢?答案就是时间,时间是一直不断的流失,我们不可能暂停时间,所以这里我们也就引用了time函数做完seed的值,time函数会不断获取电脑的提供的时间,达到无时无刻都在变化,从而引起rand的随机起点再不断变化,从而引起rand 的不断变化所以接下来代码如下:

void ComputerMove(char board[Hang][Lie], int hang, int lie)
{
	int x = 0;
	int y = 0;
	printf("电脑走:\n");
	while (1)
	{
		x = rand() % hang;
		y = rand() % lie;
		if (board[x][y] == ' ')
		{
			board[x][y] = '#';
			break;
		}
	}

}

在主函数里面插入:

srand((unsigned int)time(NULL));

当然运用这些函数的时候一定要引对应的头文件,rand和srand的头文件为#include<stdlib.h>,time的头文件为#include<time.h>,这些函数的运用和头文件的查找请详细看我第一章推荐的网站,都有结果能搜查到。

解决完棋手的操作,那么我们就得知道规则是啥了,得知道我们该如何赢,所以接下来就是书写这些规则,横三,竖三,斜三算赢,棋盘满了为平局:

int IsFull(char board[Hang][Lie], int hang, int lie)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < hang; i++)
	{
		for (j = 0; j < lie; j++)
		{
			if (board[i][j] == ' ')
				return 0;
		}
	}
	return 1;
}
char IsWin(char board[Hang][Lie], int hang, int lie) //行三列
{
	int i = 0;
	for (i = 0; i < hang; i++)
	{
		if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][1] != ' ')
		{
			return board[i][1];
		}
	}
	for (i = 0; i < lie; i++) // 竖三列
	{
		if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[1][i] != ' ')
		{
			return board[1][i];
		}
		// 对角线
		if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] != ' ')
		{
			return board[1][1];
		}
		if (board[2][0] == board[1][1] && board[1][1] == board[0][2] && board[1][1] != ' ')
		{
			return board[1][1];
		}
		//判断是否平局
		if (1 == IsFull(board, hang, lie))
		{
			return 'Q'; //平局
		}
		return 'C'; // 继续
	}
}

在这里,横三,竖三,斜三满足条件的,我们传回他们其中任意一个地方的符号,为什么这样呢,因为我们现在都是再写函数的功能实现,最后的决定权还都是在主函数里面,我们剩下的就需要在主函数里面相应的润色一下,就能完成最终代码:

void game()
{	
	char ret = 0;
	char board[Hang][Lie] = {0};
	InitBoard(board, Hang, Lie);//初始化棋盘
	DisplayBoard(board, Hang, Lie);//打印棋盘
	while (1)
	{
		PlayerMove(board, Hang, Lie); //玩家
		DisplayBoard(board, Hang, Lie);
		ret = IsWin(board, Hang, Lie);                   //判断输赢
		if (ret != 'C')
		{
			break;
		}
		ComputerMove(board, Hang, Lie);//电脑
		DisplayBoard(board, Hang, Lie);
		ret = IsWin(board, Hang, Lie);                       //判断输赢
		if (ret != 'C')
		{
				break;
		}
	}
	if (ret == '*')
	{
		printf("玩家赢\n");
	}
	else if (ret == '#')
	{
		printf("电脑赢\n");
	}
	else
		printf("平局\n");
}

这些就是我们所写的全部代码了,当然做的很简单,电脑走的也十分的水,但最起码可以自娱自乐一下,这些都是前几章收获所得,也是所学的结果的一种体现,写了这篇文章也算是勉励自己吧,各位大佬有能优化的可以尽情评论,感谢你的阅读,也感谢自己写的这些。

附带函数声明头文件:

#define Hang 3
#define Lie  3
#include<stdio.h>
#include<stdlib.h>
#include<time.h>

//声明
void InitBoard(char board[Hang][Lie], int hang, int lie);
void DisplayBoard(char board[Hang][Lie], int hang, int lie);
void PlayerMove(char board[Hang][Lie], int hang, int lie);
void ComputerMove(char board[Hang][Lie], int hang, int lie);
char IsWin(char board[Hang][Lie], int hang, int lie);
int IsFull(char board[Hang][Lie], int hang, int lie);

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值