数组的应用实例1:三子棋

Posted 孙一鸣

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数组的应用实例1:三子棋相关的知识,希望对你有一定的参考价值。

之前说过:在实际写代码的时候,通常把函数的声明放在头文件(.h),把函数的定义放在.c文件而且调用的时候要引入自己的头文件 用双引号括起来。
因为如果大家都在test.c里面写程序,那么就乱套了。如果公司写代码把所有的程序写在test.c文件,那么只能甲写十分钟,乙写十分钟,开发效率很低!
如果分模块去写:甲写加法模块,只用搞定add.c和add.h文件;乙写减法模块……最后再整合起来,这样就大大提高了效率。

接下来我想一步一步地来缕清独立写完代码的思路,最后将代码上传到百度云,有需要的朋友可以自行获取。

链接:https://pan.baidu.com/s/1bjN8OogL90LUYEUqB-f7-Q
提取码:su37

第一步:写出游戏开始的逻辑

test.c作为游戏开发的逻辑
game.c 作为游戏实现的逻辑
game.h 游戏实现函数的声明

在test.c文件里面,主函数里面运行一个test函数,test函数主要根据输入的值,选择是玩游戏还是退出游戏。

#include<stdio.h>
void menue()

	printf("*******************\\n");
	printf("******1 play  *****\\n");
	printf("******0 exit  *****\\n");
	printf("*******************\\n");

void test()

	int input = 0;
	do
	
		menue();
		printf("请选择:\\n");
		scanf("%d", &input);
		switch (input)
		
		case 1:
			printf("游戏开始\\n");//game();
			break;
		case 0:
			printf("退出\\n");
			break;
		default:
			printf("选择错误\\n");
			break;
		
	 while (input);


int main()

	test();
	return 0;

第二步:初始化并打印棋盘

需要写出一个game函数,将数据存储在一个字符的二维数组中,玩家下棋是“*”,电脑下棋是“#”。

首先要定义一个数组3*3的,然后要初始化棋盘,使每一个数组都打印出空格。这个时候考虑到以后不管是初始化、打印棋盘都会出现大量的3,例如InitBoard(board,3,3);,
所以最好使用宏定义#define ROW 3

test.c里

void game()

	char board[ROW][COL] =  0 ;//数组的最开始的内容应该是全部空格
	InitBoard(board,ROW,COL);//初始化棋盘
	//打印棋盘
	DisplayBoard(board,ROW,COL);

对应的game.c文件里。初始化棋盘,以及打印棋盘。

void InitBoard(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++)
		
			board[i][j] = ' ';//初始化为空格
		
	


void DisplayBoard(char board[ROW][COL], int row, int col)

	for (int i = 0; i < row; i++)
	
		//数据
		//分割行
		for (int j = 0; j < col; j++)
		
			printf(" %c ", board[i][j]);
			if(j<col-1)
				printf("|");//每打印一个数据,就打印一个分割线,最后一个数据不打
		
		printf("\\n");
		if (i < row - 1)
		
			for (int j = 0; j < col; j++)
			
				printf("---");//打印行分割
				if (j < col - 1)
					printf("|");
			
		
		printf("\\n");
	

game.h里定义,以及函数的声明!
注意这里我的形参变量使用的小写,实参变量使用的大写。

#define ROW 3
#define COL 3
void InitBoard(char board[ROW][COL], int row, int col);

void DisplayBoard(char board[ROW][COL], int row, int col);

这样子的设计的优点在于,如果我要10*10的棋盘也能轻松打印,具有可拓展性!

第三步:玩家下棋,电脑下棋

不论是电脑下棋还是玩家下棋,其实都是对这个二维数组进行操作,所以对于玩家下棋的函数,输入的变量应该有这个数组、行、列。

在game.c文件里面,写出玩家下棋的函数:
注意对于一个没有学过数组的萌新来说第一行第一列的那个元素(对应board[0][0])的坐标是(1,1),所以需要board[x - 1] [y - 1] = ‘*’

void Player_move(char board[ROW][COL], int row, int col)

	printf("玩家下棋:");
	int x = 0;
	int y = 0;
	while (1)
	
		scanf("%d %d", &x, &y);
		//先判断这个位置是否越界
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		
			//没有越界,看看是否被占用。
			if (board[x - 1][y - 1] == ' ')
			
				board[x - 1][y - 1] = '*';
				break;
			
			else
			
				printf("坐标被占用,请重新输入!\\n");
			
		
		else
			printf("输入非法!\\n");
	
	

在game.c文件里面写上电脑下棋。这里采用电脑按时间戳随机下棋的方法。

使用rand函数产生的值对行取模,得到的数就刚好可以落在棋盘上的区域。
而要使用rand函数,还要使用 srand((unsigned int)time(NULL));
time函数返回时间戳,转换成无符号整型。需要调用的头文件<time.h><stdlib.h>

void Computer_Move(char board[ROW][COL], int row, int col)

	int x = 0;
	int y = 0;
	printf("电脑下棋:\\n");
	while (1)
	
		x = rand() % ROW;//0~2
		y = rand() % COL;//0~2
		if (board[x][y] == ' ')
		
			board[x][y] = '#';
			break;
		
	

写完了这些之后还要game.h文件声明!

void Player_move(char board[ROW][COL], int row, int col);
void Computer_Move(char board[ROW][COL], int row, int col);

当然还要在test.c文件的game()里面调用

void game()

	char board[ROW][COL] =  0 ;//数组的最开始的内容应该是全部空格
	InitBoard(board,ROW,COL);//初始化棋盘
	DisplayBoard(board,ROW,COL);
	char ret = '0';
	while (1)
	
		Player_move(board,ROW,COL);
		DisplayBoard(board, ROW, COL);
	

但是运行之后发现没有办法判断输赢,棋盘被占满了以后电脑进入死循环。这不是我们想要的结果。

第四步:判断输赢/平局

书写一个函数is_win。我要检测这个数组的行、列、斜边是否有相同的字符,那么我一定要把这个board数组的首元素地址传入。
返回 * 说明玩家赢
返回 # 说明电脑赢
返回 Q 说明平局
返回 C 说明继续

第一个元素等于第二个元素,第二个元素等于第三个元素,这样三个元素就相等了,而且这三个元素是不能为空格的!

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][1];//#*
		
	
	//判断三列
	for (i = 0; i < col; i++)
	
		if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[2][i] != ' ')
		
			return board[1][i];//#*
		
	
	//判断对角线,反还* #
	if (board[0][0] == board[1][1] && board[0][0] == board[2][2] && board[2][2] != ' ')
	
		return board[1][1];
	
	if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[2][0] != ' ')
	
		return board[1][1];
	

	//判断平局
	if (1 == is_full(board, row, col))
	
		return 'Q';
	

	return 'C';

判断是否平局

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;

在game.h文件中要写上is_win的声明!
为什么不写上is_full的声明呢?
因为is_full在is_win中直接调用了

char is_win(char board[ROW][COL], int row, int col);

最后再game()中加上这个:
玩家每走一步,打印一次棋盘,做一次是否获胜的判断,如果反还的值不是’C’,那么跳出while循环,做出判断,是平局?玩家赢?还是电脑赢?

while (1)
	
		Player_move(board,ROW,COL);
		DisplayBoard(board, ROW, COL);
		ret = is_win(board, ROW, COL);
		if (ret != 'C')
		
			break;
		
		Computer_Move(board, ROW, COL);
		DisplayBoard(board, ROW, COL);
		ret = is_win(board, ROW, COL);
		if (ret != 'C')
		
			break;
		
	
	if (ret == '*')
	
		printf("玩家赢\\n");
	
	else if (ret == '#')
	
		printf("电脑赢\\n");
	
	else
	
		printf("平局\\n");
	

第五步:测试看看

其实每写一块代码,都需要进行这样的操作。
玩家赢:

平局:

电脑赢:

目前电脑下棋的算法还不够智能,对于输赢的判断还停留在初步阶段,后续会有改进。

以上是关于数组的应用实例1:三子棋的主要内容,如果未能解决你的问题,请参考以下文章

《三子棋》C语言数组应用 --n皇后问题雏形

还有这事?——一小时学会三子棋(优化版)

手把手带你搞定C语言实现三子棋游戏,让你的代码有趣起来(超详细教程,从思路到代码,快码起来!)

三子棋(会堵棋)

C语言初阶《三子棋》

三子棋游戏设计及代码实现