编程小游戏之三子棋

发布于:2022-11-09 ⋅ 阅读:(14) ⋅ 点赞:(0) ⋅ 评论:(0)

       三子棋是一个民间的益智小游戏,游戏分为双方对战,双方依次在9宫格棋盘上摆放棋子,率先将自己的三个棋子连成一条线的一方则视为胜利者。下面将说明如何利用C语言在我们的计算机上简单的实现三子棋。


 1.逻辑框架设计

       在vs上创建两个.c文件test.c和game.c,再创建一个头文件game.h。我们在test.c文件中写游戏的逻辑框架,在game.c文件中写游戏细节,在game.h文件中进行声明。

       在test.c文件中建立主函数,并设计调用test()函数。

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

       继续写test()函数,首先我们采用do while循环建立程序的框架,游戏的开头需要一个游戏菜单,所以设计一个menu()函数放在循环的开头。

void menu()
{
	printf("**********************************\n");
	printf("********       1.PLAY     ********\n");
	printf("********       0.EXIT     ********\n");
	printf("**********************************\n");
}

       再设计printf()scanf()函数引导玩家,后面采用switch case 语句把玩家输入的数input分为三种情况:输入1进入游戏,输入0退出游戏,输入其他发生错误重新输入。该循环的条件语句为input,如果input为0则程序结束。效果如下:

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

}

2.游戏内容设计

      基本的框架建立好之后,我们要对玩家输入1的情况下的开始游戏进行设计,定义一个game()函数用来写游戏的细节。首先我们需要在game()中创建一个的数组char board[ROW][COL],分别表示9宫格的9个位置,ROW表示行数,COL表示列数,虽然我们设计的是三子棋,但是为了通用性这里用ROW COL 来代替3 3,再将ROW COL在game.h中定义为3 3,这样有利于对ROW COL进行修改。

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

        然后我们对board数组进行初始化,因为不进行初始化它的值是随机的。在game.c文件中定义一个init_board()函数,将所有元素都初始化为" "。此处我们采用两个for循环简单的解决这个问题。再在test.c的game()中调用这个函数。

void init_board(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] = ' ';
}

注意:调用game.c中的函数需要在game.h中进行声明。

#define ROW 3
#define COL 3

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

       下面就是将我们的棋盘打印出来啦,首先在game.c中定义一个is_print()函数,我们需要打印一个九宫格,设想通过三个for循环实现,其中需要注意两个细节,一个是在行棋盘的打印中需要打印3个" %c "和2个“|”,而不是3个"|",所以j<col-1。一个是在列棋盘的打印中需要打印2个“---”而不是3个,所以i<row-1。

void is_pirnt(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++)
		{
			printf(" %c ", board[i][j]);
			if (j < col - 1)
				printf("|");
		}
		printf("\n");
		if (i < row - 1)
		{
			for (j = 0; j < col; j++)
			{
				printf("---");
				if (j < col - 1)
					printf("|");
			}
		}
		printf("\n");
	}
}

最终棋盘打印的效果是这样的

棋盘设计好之后,就开始我们的下棋环节。

       第一步,玩家下棋。在game.c中定义一个player_move()函数,在game.h中声明此函数,最后在game()函数中调用。我们需要玩家输入一个坐标把棋下在想下的地方,注意玩家输入的坐标与计算机中真正的坐标有所不同,我们需要对输入的这个坐标-1。搞清楚逻辑后开始设计,首先限制输入的这个坐标的范围,如果超出了范围就打印“坐标错误”,同时还要注意输入的这个坐标有没有被占用,如果被占用需要重新输入,所以我们使用嵌套的两个if语句和一个while循环来实现。

void player_move(char board[ROW][COL], int row, int col)
{
	int i = 0;
	int j = 0;
	printf("玩家下棋\n");
	printf("请输入坐标:>");
	scanf("%d %d", &i, &j);
	while (1)
	{
		if (i >= 1 && i <= row && j >= 1 && j <= col)//限制范围
		{
			if (board[i - 1][j - 1] == ' ')//坐标-1
			{
				board[i - 1][j - 1] = '*';
				break;
			}
			else
				printf("已被占用,请重新输入\n");
		}
		else
			printf("坐标错误\n");
	}

  玩家下棋之后需要在game()函数中调用一次打印棋盘is_print()函数,把棋盘打印出来。

         第二步,电脑下棋。同样在game.c中定义computer_move()函数,在game.h中声明,在game()中调用。由于电脑没有自主意识,所以我们把电脑下棋的坐标设计为随机值,使用rand()函数对坐标进行初始化,想要实现rand()函数必须先实现一次srand()函数,此处用到时间戳的知识,由于srand((unsigned int)time(NULL))只需要执行一次,所以我们把它放入test()函数中,并且在game.h中包含time.h头文件。(NULL是空指针的意思)

        坐标初始化之后,同样建立一个while循环,与玩家下棋部分类似,如果随机值产生的坐标没有被占用,就将棋子下入该坐标,跳出循环。如果被占用就一直循环到有空位置为止。

void computer_move(char board[ROW][COL], int row, int col)
{
	printf("电脑下棋\n");
	while (1)
	{
		int i = rand() % ROW;// %ROW COL 的目的是把随机值限制在0~ROW-1和0~COL-1
		int j = rand() % COL;
		if (board[i][j] == ' ')
		{
			board[i][j] = '#';
			break;
		}
	}
}

电脑下棋之后在game()函数中再调用一次is_print()函数。

       第三步,判断胜利。在game.c中定义一个is_win()函数,在game.h中声明,在game()中定义一个整型来接受is_win()的返回值。三子棋的游戏规则是率先将自己的棋子连成一条线的那一方胜利,所有有几种胜利的可能性,一是行连成一条线,二是列连成一条线,三是两条对角线连成一条线,由此我们可以写出这四种情况

char is_win(char board[ROW][COL], int row, int col)
{
	int i = 0;
	int j = 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 (j = 0; j < col; j++)
	{
		if (board[0][j] == board[1][j] && board[1][j] == board[2][j] && board[0][j] != ' ')
			return board[0][j];
	}
	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];
}

除了胜利,还有可能平局,平局就是把棋盘下满了也没有胜利者,所以我们在game.c中定义一个is_full()函数,此函数不需要在game.h中声明,因为不需要把它写入test.c中。在is_full()中对整个数组进行遍历,如果有空位置说明没有满返回0,没有空位则返回1。
 

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()中

char is_win(char board[ROW][COL], int row, int col)
{
	int i = 0;
	int j = 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 (j = 0; j < col; j++)
	{
		if (board[0][j] == board[1][j] && board[1][j] == board[2][j] && board[0][j] != ' ')
			return board[0][j];
	}
	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 == 1)
		return 'Q';//用Q表示平局
	return 'C';//用C表示继续
}

此时我们再看test.c中的game()

void game()
{
	char ret = 0;
	char board[ROW][COL];
	init_board(board,ROW,COL);
	is_pirnt(board, ROW, COL);
	player_move(board, ROW, COL);
	is_pirnt(board, ROW, COL);
	computer_move(board, ROW, COL);
	is_pirnt(board, ROW, COL);
	ret = is_win(board, ROW, COL);
}

我们注意到这里玩家和电脑只下了一次棋,所以我们给它们套上一个循环,让它们一直下棋。

void game()
{
	char ret = 0;
	char board[ROW][COL];
	init_board(board,ROW,COL);
	is_pirnt(board, ROW, COL);
	while (1)
	{
		player_move(board, ROW, COL);
		is_pirnt(board, ROW, COL);
		computer_move(board, ROW, COL);
		is_pirnt(board, ROW, COL);
		ret = is_win(board, ROW, COL);
	}
}

然后我们发现判断输赢不能总是在电脑下完棋之后,玩家下完棋之后也需要判断输赢,并且加上if语句用来判定循环是否继续。

void game()
{
	char ret = 0;
	char board[ROW][COL];
	init_board(board,ROW,COL);
	is_pirnt(board, ROW, COL);
	while (1)
	{
		player_move(board, ROW, COL);
		is_pirnt(board, ROW, COL);
		ret=is_win(board, ROW, COL);
		if (ret != 'C')
			break;
		computer_move(board, ROW, COL);
		is_pirnt(board, ROW, COL);
		ret = is_win(board, ROW, COL);
		if (ret != 'C')
			break;
	}
}

最后当跳出循环的时候,对胜负情况进行打印。

void game()
{
	char ret = 0;
	char board[ROW][COL];
	init_board(board,ROW,COL);
	is_pirnt(board, ROW, COL);
	while (1)
	{
		player_move(board, ROW, COL);
		is_pirnt(board, ROW, COL);
		ret=is_win(board, ROW, COL);
		if (ret != 'C')
			break;
		computer_move(board, ROW, COL);
		is_pirnt(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");
}

这样,我们的三子棋代码就完成啦!

让我们看看效果吧。

test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
void game()
{
	char ret = 0;
	char board[ROW][COL];
	init_board(board,ROW,COL);
	is_pirnt(board, ROW, COL);
	while (1)
	{
		player_move(board, ROW, COL);
		is_pirnt(board, ROW, COL);
		ret=is_win(board, ROW, COL);
		if (ret != 'C')
			break;
		computer_move(board, ROW, COL);
		is_pirnt(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");
}
void menu()
{
	printf("**********************************\n");
	printf("********       1.PLAY     ********\n");
	printf("********       0.EXIT     ********\n");
	printf("**********************************\n");
}
void test()
{
	int input = 0;
	srand((unsigned int)time(NULL));
	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("选择错误\n");
			break;
		}
	} while (input);

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

game.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
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;
}
void init_board(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 is_pirnt(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++)
		{
			printf(" %c ", board[i][j]);
			if (j < col - 1)
				printf("|");
		}
		printf("\n");
		if (i < row - 1)
		{
			for (j = 0; j < col; j++)
			{
				printf("---");
				if (j < col - 1)
					printf("|");
			}
		}
		printf("\n");
	}
}
void player_move(char board[ROW][COL], int row, int col)
{
	int i = 0;
	int j = 0;
	printf("玩家下棋\n");
	printf("请输入坐标:>");
	scanf("%d %d", &i, &j);
	while (1)
	{
		if (i >= 1 && i <= row && j >= 1 && j <= col)
		{
			if (board[i - 1][j - 1] == ' ')
			{
				board[i - 1][j - 1] = '*';
				break;
			}
			else
				printf("已被占用,请重新输入\n");
		}
		else
			printf("坐标错误\n");
	}
}
void computer_move(char board[ROW][COL], int row, int col)
{
	printf("电脑下棋\n");
	while (1)
	{
		int i = rand() % ROW;
		int j = rand() % COL;
		if (board[i][j] == ' ')
		{
			board[i][j] = '#';
			break;
		}
	}
}
char is_win(char board[ROW][COL], int row, int col)
{
	int i = 0;
	int j = 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 (j = 0; j < col; j++)
	{
		if (board[0][j] == board[1][j] && board[1][j] == board[2][j] && board[0][j] != ' ')
			return board[0][j];
	}
	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 == 1)
		return 'Q';
	return 'C';
}

game.h

#include<stdio.h>
#include<time.h>
#define ROW 3
#define COL 3
//声明初始化数组函数
void init_board(char board[ROW][COL], int row, int col);
//声明打印棋盘函数
void is_pirnt(char board[ROW][COL], int row, int col);
//声明玩家下棋函数
void player_move(char board[ROW][COL], int row, int col);
//声明电脑下棋函数
void computer_move(char board[ROW][COL], int row, int col);
//声明是否胜利函数
char is_win(char board[ROW][COL], int row, int col);