【C语言】三子棋及多子棋

大家好!今天我们学习用C语言来实现三子棋及多子棋的小游戏!

目录

1. 三子棋

1.1 设计思路

1.2 具体实现

1.2.1 设计游戏菜单

1.2.2 设计棋盘

1.2.2.1 设计棋盘大小

1.2.2.2 初始化棋盘

1.2.2.3 打印棋盘

1.2.3 设计下棋步骤

1.2.3.1 玩家下棋

 1.2.3.2 电脑下棋

1.2.3.3 设计判断输赢的函数

1.3 完整代码

1.3.1 game.h

1.3.2 game.c

1.3.3 test.c

 2. 多子棋

2.1 思路分析和代码改进

 2.2 完整代码

2.2.1 game.h

2.2.2 game.c

2.2.3 test.c

3. 总结


1. 三子棋

1.1 设计思路

首先说明本人使用的编译器是Visual Studio 2022。在本次设计三子棋及多子棋小游戏中,我准备将主体代码用一个头文件game.h和两个源文件game.c和test.c实现。其中game.h包含游戏函数的声明,game.c是游戏函数的实现,而test.c是用来测试游戏函数的。

我的设计思路如下:

(1)首先设计一个游戏菜单,用户可以通过游戏菜单的说明进行相应的游戏操作

(2)用二维数组创建一个棋盘,将棋盘初始化并且打印棋盘

(3)用二维数组记录每次玩家落子和电脑落子(用随机数解决)的状态

(4)每次玩家落子和电脑落子后,打印棋盘,判断是玩家赢电脑赢平局,还是继续游戏

1.2 具体实现

1.2.1 设计游戏菜单

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

我们在test.c文件中写一个menu()函数,使用printf()函数打印菜单,玩家输入1时开始游戏,输入0时退出游戏。

因为要使用printf()函数,所以我们在game.h中引入头文件stdio.h。

#include<stdio.h>

因为玩家玩好一局游戏后,可能还想继续玩。所以这里我采用了do-while结构,同时用了switch语句来实现用户做出相应的选择后执行相应的功能。

int main()
{
	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 (input);
	return 0;
}

1.2.2 设计棋盘

1.2.2.1 设计棋盘大小

这里我们用二维数组创建一个棋盘,棋盘的大小(行和列)用宏定义实现。使用宏定义能改变棋盘的大小,也为后面实现多子棋做铺垫。

#define ROW 3
#define COL 3

我们将二维数组先初始化为0,在test.c文件中写一个game()函数,在game()函数中实现整个游戏功能。

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

同时我们对原先main()函数中的switch语句中的case 1:部分进行修改——将printf("开始游戏\n")更改成game()函数。

int main()
{
	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);
	return 0;
}

我们之后就要不断扩展game()函数的功能。

1.2.2.2 初始化棋盘

我们将棋盘初始化为空格,提供落子空间。

在game.h文件中进行InitBoard()的函数声明:

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

在game.c文件中进行InitBoard()的函数实现:

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

同时在test.c文件的game()函数中增加相应内容:

void game()
{
	char board[ROW][COL] = { 0 };
	InitBoard(board, ROW, COL);
}
1.2.2.3 打印棋盘

这里我们要设计一个棋盘,我的想法是实现一个如下图的棋盘。

 要实现这样一个棋盘,我们可以把问题简化成打印数据打印分割线

 

 首先我们在game.h文件中进行DisplayBoard()的函数声明:

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

 在game.c文件中进行DisplayBoard()的函数实现:

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

	for (int i = 0; i < row; i++)
	{
		//1. 打印数据
		for (int j = 0; j < col; j++)
		{
			printf(" %c ", board[i][j]);
			if (j < col - 1)
				printf("|");
		}
		printf("\n");
		//2. 打印分割线
		if (i < row - 1)
		{
			for (int j = 0; j < col; j++)
			{
				printf("---");
				if (j < col - 1)
					printf("|");
			}
			printf("\n");
		}
	}
}

 

当玩家选择1时就能看到如图的棋盘。

我们同时在test.c文件中的game()函数中增加相应内容:

void game()
{
	char board[ROW][COL] = { 0 };
	InitBoard(board, ROW, COL);
	DisplayBoard(board, ROW, COL);
}

1.2.3 设计下棋步骤

1.2.3.1 玩家下棋

在game.h文件中进行PlayerMove()的函数声明:

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

玩家下棋时,落子在棋盘上,所以我们需要设置x,y两个变量表示玩家落子的位置。落子需要在二维数组不越界的条件下,所以我使用了if语句判断坐标是否合法,在坐标合法的情况下,如果棋盘对应位置没有落子(即为空格),玩家就可依落子。我们将玩家的落子标记为'*'。因为要贴近人们的习惯,所以我将左上角的坐标标成1 1而不是0 0。因为玩家要多次落子,所以我们在最外面加一个while(1)的循环,当玩家落子完后,我们就break退出循环。

在game.c文件中进行PlayerMove()的函数实现:

void PlayerMove(char board[ROW][COL], int row, int col)
{
	int x = 0;
	int y = 0;
	printf("玩家下棋>:\n");
	while (1)
	{
		printf("请输入下棋的坐标,中间使用空格>:");
		scanf("%d %d", &x, &y);
		//坐标合法
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (board[x - 1][y - 1] == ' ')//可以落子,
			{							   //这里用x-1.y-1是因为规则定义左上角为1 1,
				board[x - 1][y - 1] = '*'; //而数组存储的第一个元素下标是0 0,所以行和列都需减去1
				break;
			}
			else//不能落子
			{
				printf("坐标被占有,不能落子,重新输入坐标\n");
			}
		}
		else//非法
		{
			printf("坐标非法,重新输入\n");
		}
	}
}

同时我们想知道玩家落子完后棋盘的状态,我们可以在玩家每次落子后打印棋盘的状态。所以在test.c的game()函数中调用PlayerMove()函数和DisplayBoard()函数。

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

	PlayerMove(board, ROW, COL);  //玩家下棋
	DisplayBoard(board, ROW, COL); //打印棋盘
}

选择1后,玩家如果输入1 1,则在棋盘的左上角就会出现*f符号。

 1.2.3.2 电脑下棋

在game.h文件中进行ComputerMove()的函数声明:

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

电脑下棋时,落子在棋盘上,所以我们也需要设置x,y两个变量表示玩电脑落子的位置。这里我使用了rand()随机数函数,通过rand()%row,获得0~row-1的值,通过rand()%col,获得0~col-1的值,从而确保落子的位置不越界,棋子合法落在棋盘上。从我们将玩家的落子标记为'#'。因为电脑要多次落子,所以我们在最外面加一个while(1)的循环,当电脑落子完后,我们就break退出循环。

因为要使用rand()函数,所以我们先简单了解下这个函数,这个函数的头文件是stdlib.h。

这个函数会返回一个范围在0~RAND_MAX(至少是32767)的伪随机整数。

在调用rand()函数产生随机数前,我们可以先使用srand()函数设置随机数种子(seed)。 如果没有设置随机数种子, rand()在调用时会自动设随机数种子为1。随机数种子相同,每次产生的随机数也会相同。

srand()函数也包含在stdlib.h头文件中。

 我们还需要使用time()函数,该函数的头文件是time.h。

 time()函数功能是获取当前时间,并将其转换为time_t类型的值。

其中,time_t *time 是一个指向time_ t 类型变量的指针,用于存储获取到的时间值。如果t为NULL,则表示不需要存储时间值,只需要返回当前时间的time_t类型值。

因为我们的程序中需要使用rand()、srand()、time()这三个函数,所以我们要在game.h中引入相应的头文件。

#include<stdlib.h>
#include<time.h>

我们需要在test.c的main函数中添加代码:

srand((unsigned int)time(NULL));
/*用于生成随机数种子。
srand()函数接受一个unsigned int整型参数,它被用来初始化随机数生成器的种子。
time(NULL)函数返回当前时间的秒数,并将其转换为无符号整数值,然后作为种子传递给srand函数。
这样做的目的是使每次程序执行时都生成不同的随机序列。*/

接下来我们在game.c文件中进行DisplayBoard()的函数实现:

void ComputerMove(char board[ROW][COL], int row, int col)
{
	int x = 0; //0~row-1
	int y = 0; //0~col-1
	printf("电脑下棋:>\n");
	while (1)
	{
		x = rand() % row;
		y = rand() % col;
		if (board[x][y] == ' ')
		{
			board[x][y] = '#';
			break;
		}
	}
}

同时我们想知道电脑落子完后棋盘的状态,我们可以在电脑每次落子后打印棋盘的状态。所以在test.c的game()函数中调用PlayerMove()函数和DisplayBoard()函数。

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

	PlayerMove(board, ROW, COL);  //玩家下棋
	DisplayBoard(board, ROW, COL); //打印棋盘
	
	ComputerMove(board, ROW, COL);  //电脑下棋
	DisplayBoard(board, ROW, COL);  //打印棋盘
}

1.2.3.3 设计判断输赢的函数

当玩家和电脑下完棋时,我们要判断输赢。这里我们使用'*'表示玩家赢,'#'表示电脑赢,'Q'表示平局,'C'表示继续游戏。
在game.h文件中进行IsWin()的函数声明:

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

三子棋游戏的目标是在一个3x3的棋盘上先将自己的棋子连成一条直线的玩家获胜。在判断输赢时,需要检查横向、纵向和对角线上是否存在连续的三个相同棋子。如果以上三种情况都不存在,而且棋盘已经放满,那么游戏结束,平局。

我们先在game.h中声明判断棋盘是否放满的IsFull()函数:

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

要判断棋盘是否放满,非常简单,我们只要使用两层for循环遍历一下棋盘,如果有棋盘格的状态为空格,这说明没有放。当完整遍历完后,如果所有棋盘格的状态都不会空格,说明放满了。

我们在game.c文件中实现IsFull()函数:

int IsFull(char board[ROW][COL], int row, int col)
{
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			if (board[i][j] == ' ')
				return 0;
		}
	}
	return 1;
}

我们通过for循环分别遍历行和列判断是否有三个棋子连成一条直线,同时通过if语句判断主对角线和副对角线是否有三个棋子连成一条直线。若以上都没有,我们检查一下棋盘是否放满,若放满则是平局,否则我们继续游戏。

在game.c文件中实现判断输赢的函数IsWin():

char IsWin(char board[ROW][COL], int row, int col)
{
	//赢
	//行
	for (int 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 (int 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 (IsFull(board, row, col))
	{
		return 'Q';
	}
	//继续
	return 'C';
}

因为玩家和电脑要依次下棋直到分出输赢,所以我们在外层要加一个while(1)的循环。同时我们需要使用一个变量ret来记录游戏进程和游戏结果,如果ret值为'C'表示继续游戏,否则退出游戏。通过最后ret的值判断输赢。如果ret值为'*'表示玩家赢,ret值为'#'表示电脑赢,ret值为'Q'表示平局。

所以t我们在test.c文件中实现game()函数:

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

1.3 完整代码

1.3.1 game.h

#pragma once

#include<stdio.h>
#include<stdlib.h>
#include<time.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);
//玩家下棋
void PlayerMove(char board[ROW][COL], int row, int col);
//电脑随机下棋
void ComputerMove(char board[ROW][COL], int row, int col);
//判断棋盘是否放满
int IsFull(char board[ROW][COL], int row, int col);
//判断输赢
//玩家赢 - '*'
//电脑赢 - '#'
//平局 - 'Q'
//继续 - 'C
char IsWin(char board[ROW][COL], int row, int col);

1.3.2 game.c

#define _CRT_SECURE_NO_WARNINGS 1

#include"game.h"

//初始化棋盘为空格
void InitBoard(char board[ROW][COL], int row, int col)
{
	for (int i = 0; i < row; i++)
		for (int 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++)
	{
		//1. 打印数据
		for (int j = 0; j < col; j++)
		{
			printf(" %c ", board[i][j]);
			if (j < col - 1)
				printf("|");
		}
		printf("\n");
		//2. 打印分割线
		if (i < row - 1)
		{
			for (int j = 0; j < col; j++)
			{
				printf("---");
				if (j < col - 1)
					printf("|");
			}
			printf("\n");
		}
	}
}

//玩家下棋
void PlayerMove(char board[ROW][COL], int row, int col)
{
	int x = 0;
	int y = 0;
	printf("玩家下棋>:\n");
	while (1)
	{
		printf("请输入下棋的坐标,中间使用空格>:");
		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");
		}
	}
}

//电脑随机下棋
void ComputerMove(char board[ROW][COL], int row, int col)
{
	int x = 0; //0~row-1
	int y = 0; //0~col-1
	printf("电脑下棋:>\n");
	while (1)
	{
		x = rand() % row;
		y = rand() % col;
		if (board[x][y] == ' ')
		{
			board[x][y] = '#';
			break;
		}
	}
}

//判断棋盘是否放满
int IsFull(char board[ROW][COL], int row, int col)
{
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			if (board[i][j] == ' ')
				return 0;
		}
	}
	return 1;
}

//判断输赢
char IsWin(char board[ROW][COL], int row, int col)
{
	//赢
	//行
	for (int 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 (int 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 (IsFull(board,row,col))
	{
		return 'Q';
	}
	//继续
	return 'C';
}

1.3.3 test.c

#define _CRT_SECURE_NO_WARNINGS 1

#include"game.h"

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

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

int main()
{
	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);
	return 0;
}

 2. 多子棋

2.1 思路分析和代码改进

前面实现三子棋的判断输赢中我们使用了如下代码:

char IsWin(char board[ROW][COL], int row, int col)
{
	//赢
	//行
	for (int 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 (int 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 (IsFull(board, row, col))
	{
		return 'Q';
	}
	//继续
	return 'C';
}

我们发现这段代码有一定的局限性,当是n子棋(n>3)时,上述代码就不适用了,所以我们需要进行改进。

(1)对于行的判断,我们可以用i和j两个变量进行两重for循环,外层用i遍历行数,内层用j从0遍历到列数-1,两两判断一行中两个相邻的数组元素,如果不相等或者有没放棋子的棋盘格,我们使用break退出。每次遍历完一行时,进行判断,如果j==列数-1,说明这一行有n个棋子相连。

(2)对于列的判断,我们可以用j和i两个变量进行两重for循环,外层用j遍历列数,内层用i从0遍历到行数-1,两两判断一列中两个相邻的数组元素,如果不相等或者有没放棋子的棋盘格,我们使用break退出。每次遍历完一列时,进行判断,如果i==行数-1,说明这一列有n个棋子相连。

(3)对于主对角线的判断,我们可以用i和j两个变量进行两重for循环,外层用i遍历从0到行数-1,内层用j从0遍历到1,两两判断主对角线上两个相邻的数组元素,如果不相等或者有没放棋子的棋盘格,我们使用break退出。当遍历完主对角线时,进行判断,如果j==列数-1,说明主对角线有n个棋子相连。

 (4)对于副对角线的判断,我们可以用i和j两个变量进行两重for循环,外层用i遍历从0到行数-1,内层用j从列数-1遍历到1,两两判断副对角线上两个相邻的数组元素,如果不相等或者有没放棋子的棋盘格,我们使用break退出。当遍历完副对角线时,进行判断,如果i==行数-1,说明副对角线有n个棋子相连。

char IsWin(char board[ROW][COL], int row, int col)
{
	//赢
	//行
	int i = 0;
	int j = 0;
	for (i = 0; i < row; i++)
	{
		j = 0;
		for (j = 0; j < col - 1; j++)
		{
			if (board[i][j] != board[i][j + 1] || board[i][j] == ' ')
				break;
		}
		if (j == col - 1)
			return board[i][j];
	}
	//列
	i = 0;
	j = 0;
	for (j = 0; j < col; j++)
	{
		i = 0;
		for (i = 0; i < row - 1; i++)
		{
			if (board[i][j] != board[i + 1][j] || board[i][j] == ' ')
				break;
		}
		if (i == row - 1)
			return board[i][j];
	}
	//主对角线
	i = 0;
	j = 0;
	for (i = 0; i < row - 1; i++)
	{
		j = 0;
		for (j = 0; j < col - 1; j++)
		{
			if (board[i][j] != board[i + 1][j + 1] || board[i][j] == ' ')
				break;
		}
		if (j == col - 1)
			return board[i][j];
	}

	//副对角线
	i = 0;
	j = 0;
	for (i = 0; i < row - 1; i++)
	{
		j = 0;
		for (j = col - 1; j > 0; j--)
		{
			if (board[i][j] != board[i + 1][j - 1] || board[i][j] == ' ')
				break;
		}
		if (i == row - 1)
			return board[i][j];
	}
	//平局
	if (IsFull(board, row, col))
	{
		return 'Q';
	}
	//继续
	return 'C';
}

为了方便测试,我们此时宏定义ROW为5,COL为5

#define ROW 5
#define COL 5

 2.2 完整代码

2.2.1 game.h

#pragma once

#include<stdio.h>
#include<stdlib.h>
#include<time.h>

#define ROW 5
#define COL 5

//初始化棋盘
void InitBoard(char board[ROW][COL], int row, int col);
//打印棋盘
void DisplayBoard(char board[ROW][COL], int row, int col);
//玩家下棋
void PlayerMove(char board[ROW][COL], int row, int col);
//电脑随机下棋
void ComputerMove(char board[ROW][COL], int row, int col);
//判断棋盘是否放满
int IsFull(char board[ROW][COL], int row, int col);
//判断输赢
//玩家赢 - '*'
//电脑赢 - '#'
//平局 - 'Q'
//继续 - 'C
char IsWin(char board[ROW][COL], int row, int col);

2.2.2 game.c

#define _CRT_SECURE_NO_WARNINGS 1

#include"game.h"

//初始化棋盘为空格
void InitBoard(char board[ROW][COL], int row, int col)
{
	for (int i = 0; i < row; i++)
		for (int 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++)
	{
		//1. 打印数据
		for (int j = 0; j < col; j++)
		{
			printf(" %c ", board[i][j]);
			if (j < col - 1)
				printf("|");
		}
		printf("\n");
		//2. 打印分割线
		if (i < row - 1)
		{
			for (int j = 0; j < col; j++)
			{
				printf("---");
				if (j < col - 1)
					printf("|");
			}
			printf("\n");
		}
	}
}

//玩家下棋
void PlayerMove(char board[ROW][COL], int row, int col)
{
	int x = 0;
	int y = 0;
	printf("玩家下棋>:\n");
	while (1)
	{
		printf("请输入下棋的坐标,中间使用空格>:");
		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");
		}
	}
}

//电脑随机下棋
void ComputerMove(char board[ROW][COL], int row, int col)
{
	int x = 0; //0~row-1
	int y = 0; //0~col-1
	printf("电脑下棋:>\n");
	while (1)
	{
		x = rand() % row;
		y = rand() % col;
		if (board[x][y] == ' ')
		{
			board[x][y] = '#';
			break;
		}
	}
}


int IsFull(char board[ROW][COL], int row, int col)
{
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			if (board[i][j] == ' ')
				return 0;
		}
	}
	return 1;
}

char IsWin(char board[ROW][COL], int row, int col)
{
	//赢
	//行
	int i = 0;
	int j = 0;
	for (i = 0; i < row; i++)
	{
		j = 0;
		for (j = 0; j < col - 1; j++)
		{
			if (board[i][j] != board[i][j + 1] || board[i][j] == ' ')
				break;
		}
		if (j == col - 1)
			return board[i][j];
	}
	//列
	i = 0;
	j = 0;
	for (j = 0; j < col; j++)
	{
		i = 0;
		for (i = 0; i < row - 1; i++)
		{
			if (board[i][j] != board[i + 1][j] || board[i][j] == ' ')
				break;
		}
		if (i == row - 1)
			return board[i][j];
	}
	//主对角线
	i = 0;
	j = 0;
	for (i = 0; i < row - 1; i++)
	{
		j = 0;
		for (j = 0; j < col - 1; j++)
		{
			if (board[i][j] != board[i + 1][j + 1] || board[i][j] == ' ')
				break;
		}
		if (j == col - 1)
			return board[i][j];
	}

	//副对角线
	i = 0;
	j = 0;
	for (i = 0; i < row - 1; i++)
	{
		j = 0;
		for (j = col - 1; j > 0; j--)
		{
			if (board[i][j] != board[i + 1][j - 1] || board[i][j] == ' ')
				break;
		}
		if (i == row - 1)
			return board[i][j];
	}
	//平局
	if (IsFull(board, row, col))
	{
		return 'Q';
	}
	//继续
	return 'C';
}

  2.2.3 test.c

#define _CRT_SECURE_NO_WARNINGS 1

#include"game.h"

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

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


int main()
{
	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);
	return 0;
}

3. 总结

到这里,我们的三子棋及多子棋小游戏的设计就结束了,通过设计该游戏,我增加了对二维数组和循环结构的理解,也熟悉了随机数函数的使用,学习使用了宏定义,提高了自定义函数的编写能力和分析问题的能力。

希望通过后续学习,不断提高代码能力和分析解决问题的能力。由于本人是大一学生,知识水平有限,本文如果有错误和不足之处,还望各位大佬多多指出并提出建议!

评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值