你真的明白井字棋整个过程吗

时间:2022-11-16 07:55:50

前沿

井字棋其实也叫三子棋,但是我们这边叫井字棋。废话不多少 ,直接开整
首先我们要创建三个文件:
你真的明白井字棋整个过程吗
test.c——是测试的逻辑,写出来之后能让你把整个游戏制作思路表现出来。
game.h
game.c ——是游戏的实现,具体写代码的地方。

简单的运行流程

首先我们先在test.c中写主函数:

int main()
{
	test();
		return 0;
}

然后我们可以在test()函数中写出主要运行流程

1.打印菜单

先在test()函数中增加menu()——用来打印菜

void test()
{
	do
	{
		menu();
	} while ();
}

做一个简单的菜单表示

void menu()
{
	printf("***************************\n");
	printf("********   1. play   ******\n");
	printf("********   0. eixt   ******\n");
	printf("***************************\n");
}

这样就把菜单表示出来了

2.对菜单选项进行表示

void test()
{
	int input = 0;
	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("井字棋\n");
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("选择错误\n");
			break;
		}
	} while (1);
}

不多废话,直接运行起来看看效果
你真的明白井字棋整个过程吗
这个整个框架就搭建起来了,但是我们输入1:玩游戏,只出现井字棋。这是不够的哦。
你真的明白井字棋整个过程吗 这样才算是进入了游戏门槛了。

3. 对游戏棋盘进行表示

所以我们单独打印一个中文数字是不行的,那就再加一个函数game();
首先我们存储数据,是需要一个3*3的数组。

我们初始化一下棋盘为全空格,与上图相对应

void game()			
{
	char board[3][3];
	init_board(board, 3, 3);
}

这样我们就需要在game.h——文件中引用
并且不局限于三子棋,可以五子棋,甚至n子棋了。
这是因为我们数组的行与列是可以无限延伸的

#pragma once

#define ROW 3
#define COL 3

这里大写ROW 代表行 COL 代表列 后面的数字你写多少就表示多少哦
所以我们在game()函数中就让ROW COL 替换 3 3

void game()
{
	char board[ROW][COL];
	init_board(board, ROW, COL);
}

同时我们也要在game.h头文件中表示

#pragma once

#define ROW 3
#define COL 3

初始化棋盘

我们首先在game.h中声明函数
//初始化棋盘
void init_board(char board[ROW][COL], int row, int col);

后面具体操作就要在game.c中操作了

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

小插曲:因为我们是三个文件同时写的,且都要引用头文件game.h
所以我们就把其他需要引用的东西写在头文件中,这样就简单明了了

打印棋盘

初始化之后,我们要表示出来,就要打印了,这些具体操作就放在game.c中写了
同时其他两个文件,也要表示表示,你懂的,哈哈

void print_board(char board[ROW][COL], int row, int col)
{
	int i = 0;
	for (i = 0; i < row; i++)
	{
		int j = 0;
		for (j = 0; j < col; j++)
		{
			printf("%c", board[i][j]);
		}
		printf("\n");
	}
}

这里我们使用了两个循环,是为了行与列都打印出来。
你真的明白井字棋整个过程吗
我们这个打印似乎缺少点什么了,我们的边界呢?所以是要优化一下的

void print_board(char board[ROW][COL], int row, int col)
{
	int i = 0;
	for (i = 0; i < row; i++)
	{
		printf(" %c | %c | \n", board[i][0], board[i][1], board[i][2]);
		if (i < row - 1)
			printf("---|---|---\n");
	}
}
   来运行一波

你真的明白井字棋整个过程吗

这个不就可以了呀, 但是如果要玩五子棋呢?
简单因为我们有变量 ROW COL 直接在头文件换数字不就可以了

你真的明白井字棋整个过程吗

看看这个是啥玩意,五子棋不像三子棋,三子棋不像井字棋
这就露出鸡脚了吧! 所以为了追求完美还是要继续优化

void print_board(char board[ROW][COL], int row, int col)
{
	int i = 0;
	for (i = 0; i < row; i++)
	{
		//printf(" %c | %c | \n", board[i][0], board[i][1], board[i][2]);
		int j = 0;
		for (j = 0; j < col; j++)
		{
			printf(" %c ", board[i][j]);
			if (j < col - 1)
				printf("|");
		}
		printf("\n");
		if (i < row - 1)
		{
			//printf("---|---|---\n");
			for (j = 0; j < col; j++)
			{
				printf("---");
				if (j < col - 1)
					printf("|");
			}
			printf("\n");
		}
	}
}

我们是依据这个个循环打印的哦你真的明白井字棋整个过程吗

玩家&电脑操作

现在所有的外部环境,背景都安排好了。就真的该上手操作了
同样在game.h和test.c中记得申明哦 废话不多说 开整

void player_move(char board[ROW][COL], int row, int col)
{
	printf("玩家下棋\n");
	while (1)
	{
		printf("请输入要下棋的坐标:>");
		int x = 0;
		int y = 0;
		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] = '*';
			}
			else
			{
				printf("该坐标被占用,请重新输入\n");
			}
		}
		else
		{
			printf("坐标非法\n");
		}
	}
}


在这里是不是脑瓜子嗡嗡的,所以这听我娓娓道来
首先我们要输入坐标,但是坐标要在范围内,否则就非法访问了
然后我们进入门槛后,再判断你输入的位置是不是已经有棋子在那里了
没有的话就直接坐标替换了


接下来就是电脑操作了
电脑下棋,随机生成坐标,只要坐标没有被占用,就下棋

void computer_move(char board[ROW][COL], int row, int col)
{
	printf("电脑下棋:\n");
	while (1)
	{
		int x = rand() % row;
		int y = rand() % col;

		if (board[x][y] == ' ')
		{
			board[x][y] = '#';
			break;
		}
	}
}

这就完成一个小任务
这里的rand()是随机函数,还有时间戳(NULL),所以我们就要添加一些东西了

你真的明白井字棋整个过程吗 更加精确

判断输赢

接下来总不能一直玩下去吧,我们还要判断输赢吧
判断输赢
判断输赢的代码要告诉我:电脑赢了?玩家赢了?平局?游戏继续?
电脑赢:#
玩家赢:*
平局:Q
游戏继续:C
我们先在test.c中把整体判断框架搞出来

		//判断输赢
		ret = is_win(board, ROW, COL);
		if (ret != 'C')
		{
			break;
		}
		computer_move(board, ROW, COL);
		print_board(board, ROW, COL);
		//判断输赢
ret = is_win(board, ROW, COL);
		if (ret != 'C')
		{
			break;
		}
	}
	if (ret == '*')
		printf("电脑赢了\n");
	else if (ret == '#')
		printf("玩家赢了\n");
	else if (ret == 'Q')
		printf("平局\n");
}

然后再对is_win函数在game.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][0];
		}
	}
	//判断三列
	for (i = 0; i < col; i++)
	{
		if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[0][i] != ' ')
		{
			return board[0][i];
		}
	}
	//对角线
	if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] != ' ')
	{
		return board[1][1];
	}
	if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[1][1] != ' ')
	{
		return board[1][1];
	}
	//平局?
	if (is_full(board, row, col) == 1)
	{
		return 'Q';
	}
	//继续
	//没有玩家或者电脑赢,也没有平局,游戏继续
	return 'C';
}


在这里玩家和电脑循环下棋,每下一步就需要is_win()函数进行判断
三行,三列,对角线是否有同棋
或者平局(棋盘填满)
这里我们就又又又要写一个is_full()函数了

static 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;
}

我希望is_full()这个函数只是为了支持is_win()函数的,只是在is_win()函数内部使用
就没有必要在头文件中申明了。
这就可以把它给隐藏起来了,那就需要 static 把那个函数放在静态库中了。
只要我不给你game.c文件 就你看着头文件是不可能想到这个的哦
结尾:我也是边学,边写,搞了半天。
希望后面的学习,更加熟练,更加通畅,效率更高。
这么长时间居然连如何使用编译器都不清楚。
连小叮当的任意门都做不到

仍需继续努力

守得云开见月明