C:从三子棋到n子棋

一个半小时写完三子棋,又花了半个小时才完成从三到‘n’,但感觉写篇博客可能得搞到天黑 ,我什么时候才能记住markdown这麽多的快捷键啊 。。。

思路:

先用三子棋实现为例,三子棋是一种只要行,列,斜中任意情况三个相同棋子相连便视为获胜的游戏;在未出现上述情况,且棋盘已无空闲的情况下便视为平局。首先,在本程序中,要求玩家与电脑PK,玩家通过键入棋盘上的坐标来下棋,而电脑则通过srand()与sand函数来产生随机的坐标;其次,我们将每一次电脑和玩家所下的棋子的坐标用一个二维数组保存起来,最后,在每次玩家或电脑下完棋后,我们都应判断一下是否有一方赢了,如果赢了,退出游戏,如果在棋盘满了还没有人赢,则平局。同理,n子棋在此基础上可以通过灵活地改变获胜条件与棋盘大小。

编程模块:

1.给用户一个选择菜单
2.创建并初始化棋盘
3.可以打印棋盘的实时状态,便于我们调控
4.电脑落子的算法(总感觉随机大法不能叫算法吧)
5.将用户输入的坐标‘放’在棋盘上
6.判断每次电脑或用户下完是否有人赢了或者平局。

代码实现:

选择菜单用printf就能搞定,在这我就不往上贴了

初始化数组

因为一开始的棋盘上啥都没有,所以我这个储存棋子的数组全都初始化成空格 这里给出了两种实现方法,一种是遍历赋值,另一种是使用memset函数

void InitBoard(char arr[ROWS][COLS],int rows,int cols)
{
	/*int i = 0;
	int j = 0;
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col; j++)
		{
			arr[i][j] = ' ';
		}
	}*/
	memset(arr, ' ', rows*cols);   //void *memset(void *s, int ch, size_t n);函数解释:将s中当前位置后面的n个字节,用 ch 替换并返回 s ;作用是在一段内存块中填充某个给定的值;
}

为了后面进化三子棋到n子棋方便修改棋盘大小和获胜条件,我在这里采用了宏定义。

#include
#include
#include
#include
#include

#define ROW 5
#define COL  5
#define LONG_ture   5
#define ROWS ROW+2
#define COLS  COL+2

#define LONG  LONG_ture-1

显示棋盘

打印棋子,为了美化同时打印了边界

void DisplayBoard(char arr[ROWS][COLS], int row, int col)
{
	int i = 0;
	int j = 0;
	for (i = 1; i <=row; i++)
	{
		for (j = 1; j <= col; j++)
		{
			printf(" %c ", arr[i][j]);
			if (j <= col - 1)
			{
				printf("|");
			}
			else
			{
				printf("\n");
			}
		}
		for (j = 1; j <=col; j++)
		{
			printf("---");
			if (j <=col- 1)
			{
				printf("|");
			}
			else
			{
				printf("\n");
			}
		}
	}
}

这是显示效果:
C:从三子棋到n子棋_第1张图片

玩家落子

注意这里还要考虑非法输入和输入的坐标已经有棋子的情况

void PlayerMov(char arr[ROWS][COLS], int row, int col)
{
	int a = 0;
	int b = 0;
	while (1)
	{
		printf("\n玩家走:\n请输入坐标>");
		scanf("%d%d", &a, &b);
		if (a > 0 && a <= row && b > 0 && b <= col)
		{
			if (arr[a ][b] == ' ')
			{
				arr[a][b] = 'Y';
				break;
			}
			else
			{
				printf("\n该位置已有棋子,请重新输入!\n");
			}
		}
		else
		{
			printf("\n输入非法坐标!请重新输入.\n");
		}
	}
}

电脑落子

采用的是随机数算法,这样出来的电脑走棋会比较ZZ,所以让它应比自己赢还要困难…

void ComputerMov(char arr[ROWS][COLS], int row, int col)
{
	int x = (rand() % row) + 1;
	int y = (rand() % col) + 1;
	while (arr[x][y] != ' ')
	{
		x =(rand() % row)+1;
		y = (rand() % col)+1;
	}
	printf("\n电脑走:\n");
	arr[x][y] = 'C';
}

判断

1.Full函数是为了判断和棋(棋盘满了)而单独存在的只服务于JudgeWin函数,为了保护它不受其他干扰,我用static来使它在其他文件里被隐藏起来。
2.这里查找是否有相连棋子时我在判断这个位置有棋子后向这个点周围的八个方向同时查找,每个方向查找的个数取决于宏定义里的Long(也就是几个棋子能获胜),一旦查找到与中心棋子不同立刻停止计数,有任意一个方向相同棋子数量足够便返回赢家
在这里,考虑到在角落开始检测时往往会导致越界,我特地多创建了一圈空间(也就是说55的棋盘实际创建的是77的空间)但并不打印,也不会有棋子,这里储存的永远都是空格,一旦检测到空格便会立刻停止。

static int Full(char arr[ROWS][COLS], int row, int col)
{
	int i = 0;
	int j = 0;
	int count = row*col;
	for (i = 1; i <= row; i++)
	{
		for (j = 1; j <= col; j++)
		{
			if (arr[i][j] != ' ')
			{
				count--;
			}
		}
	}
	if (count == 0)
	{
		return 1;
	}
	else
	{
		return 0;
	}
}

char JudgeWin(char arr[ROWS][COLS], int row, int col)
{
	int count1= 0;
	int count2= 0;
	int count3= 0;
	int count4= 0;
	int count5 = 0;
	int count6= 0;
	int count7= 0;
	int count8= 0;
	int i = 0;
	int j = 0;
	int n = 0;
	int falg = 0;
	falg = Full(arr,row,col);
	if (falg == 1)
	{
		return 'P';
	}
	for (i = 1; i <= row; i++)
		{
			for (j = 1; j <= col; j++)
			{
				if (arr[i][j] != ' ')
				{
					for (n = 1; n <= LONG; n++)//方向下
					{
						if (arr[i + n][j] == arr[i][j])
						{
							count1++;
						}
						else
						{
							count1 = 0;
							break;
						}
					}
					for (n = 1; n <= LONG; n++)//方向上
					{
						if (arr[i - n][j] == arr[i][j])
						{
							count2++;
						}
						else
						{
							count2 = 0;
							break;
						}
					}
					for (n = 1; n <= LONG; n++)//方向左
					{
						if (arr[i][j - n] == arr[i][j])
						{
							count3++;
						}
						else
						{
							count3 = 0;
							break;
						}
					}
					for (n = 1; n <= LONG; n++)//方向右
					{
						if (arr[i][j + n] == arr[i][j])
						{
							count4++;
						}
						else
						{
							count4 = 0;
							break;
						}
					}
					for (n = 1; n <= LONG; n++)//方向左上
					{
						if (arr[i - n][j - n] == arr[i][j])
						{
							count5++;
						}
						else
						{
							count5 = 0;
							break;
						}
					}
					for (n = 1; n <= LONG; n++)//方向右上
					{
						if (arr[i - n][j + n] == arr[i][j])
						{
							count6++;
						}
						else
						{
							count6 = 0;
							break;
						}
					}
					for (n = 1; n <= LONG; n++)//方向左下
					{
						if (arr[i + n][j - n] == arr[i][j])
						{
							count7++;
						}
						else
						{
							count7 = 0;
							break;
						}
					}
					for (n = 1; n <= LONG; n++)//方向右下
					{
						if (arr[i + n][j + n] == arr[i][j])
						{
							count8++;
						}
						else
						{
							count8 = 0;
							break;
						}
					}
					//从八个方向开始抓取元素若相同且不为空格则计数器加一;
					if (count1 == LONG || count2 == LONG || count3 == LONG || count4 == LONG || count5 == LONG || count6 == LONG || count7 == LONG || count8 == LONG)
					{
						return arr[i][j];
					}
				}
			}
		}

}//八方向识别实现灵活控制获胜所需相连棋子的数量;

这里还有一点没来得及实现的想法,希望能给你带来一些灵感

void ComputerMov_smart()
{
	//假设玩家白子电脑黑子
	//围堵:采取对玩家落子点进行八方向识别,根据不同方向采集到的相连白子加以不同权重得出威胁最高的那条线路,在此线路上的白子两端取周围八方有黑子的位置有限落子;若无则随机.
	//难度调整:调整上文白子数量不同的权值差,当权值相同时,电脑随机落子

}

测试模块的代码

void game()
{
	int Break = 0;
	char flag;
	char arr[ROWS][COLS];
	InitBoard(arr, ROWS, COLS);
	DisplayBoard(arr, ROW, COL);
	do
	{
		PlayerMov(arr, ROW, COL);
		DisplayBoard(arr, ROW, COL);
		flag = JudgeWin(arr, ROW, COL);
		Break = FinalJudge(flag);
		ComputerMov(arr, ROW, COL);
		DisplayBoard(arr, ROW, COL);
		flag = JudgeWin(arr, ROW, COL);
		Break = FinalJudge(flag);
	} while (Break);
}

int main()
{
	assert(LONG_ture <= ROW);//断言:long_ture不可大于ROW与COL;
	assert(LONG_ture <= COL);
	int input = 0;
	srand((unsigned int)time(NULL));
	do
	{
		menu();
		printf("请选择:>\n");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("正在进入游戏...\n");
			game();
			break;
		case 0:
			printf("退出游戏中...\n");
			break;
		default:
			printf("输入错误,请重新输入!\n");
		}
	} while (input);
    system("pause");
	return 0;
}

程序跑起来,我们就可以通过修改在宏定义里long_turn来决定要玩几子棋,修改ROW和COL来改变棋盘大小.

注:因为long_turn不可以大于ROW和COL(你总不能在一个3*3的格子里玩四子棋吧…)所以我在main()里的一开始使用了assert函数,当上述条件不成立时,程序便会报错并返回我设置好的提示信息,这样就万无一失啦!

希望我的分享可以帮助到你!一起加油!

你可能感兴趣的:(C/C++)