C语言----小游戏三子棋-创新互联

相信大家或多或少都听说过五子棋,三子棋也是类似,即达成每行或每列中有三颗连续棋子,亦或是在对角线上有三颗连续的棋子即为胜利。今天,我们就来实现一下。

目前创新互联建站已为1000多家的企业提供了网站建设、域名、虚拟主机成都网站托管、企业网站设计、安岳网站维护等服务,公司将坚持客户导向、应用为本的策略,正道将秉承"和谐、参与、激情"的文化,与客户和合作伙伴齐心协力一起成长,共同发展。

目录

一、文件的准备

二、功能的实现

1.菜单与用户的选择

2.游戏的函数

3.棋盘的初始化

4.打印棋盘

5.下棋->玩家下棋

6.下棋->电脑下棋

7.判断结果

a.当一行中三个相等时

b.当一列中三个相等时

c.当对角线上相等时

d.判断平局

8.程序补全


一、文件的准备

为了后期代码的封装,我们将测试代码,函数代码和函数的声明分开存放到不同的文件中。如下:

test.c        ——        用于存放测试游戏功能实现的代码

game.c     ——        用于存放游戏相关的函数的具体内容

game.h      ——        用于存放游戏中功能函数的声明以及相关类型的定义

为了便于区分,将在后文的代码演示处首行以注释方式写明所属文件。

二、功能的实现         1.菜单与用户的选择

市面上的游戏的选项有很多,我们只完成最基础的玩游戏和退出。

我们创建无返回值的menu作为菜单,由于只用来测试,写在test.c中。

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

为了使用户有较好的游戏体验,我们使用循环来判断,只有当用户选择退出游戏才会退出,选择的不是玩游戏或者退出就提示错误并重新输入。

// test.c
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;
}
2.游戏的函数

当用户输入1选择进入游戏后,就需要进入具体的游戏程序中,我们在play的选项后创建Game函数用来存放游戏代码。Game的使用在上文的代码段中已有体现,接下来只讲声明与定义。

我们将Game函数也存放在test.c中,基础代码如下:

// test.c
void Game()
{
	char board[ROW][COL] = { 0 };

	InitBoard(board, ROW, COL);

	PrintBoard(board, ROW, COL);

	...

}

下棋,必不可少的就是棋盘。

我们创建一个二维数组来存放棋盘,棋盘的大小就设定为3X3,在二维数组中即为每行3个元素,共3行。

为了方便后期代码的维护,我们使用#define自定义行和列,并存放在我们编写的game.h中,方便调用。

// game.h

#include#include#include#include#define ROW 3
#define COL3

为了方便,已经后文需要引用的头文件一起添加,后文有关内容将仅做提示。

特别注意:由于自定义类型在别的文件中需要引用,也为了减少代码内容,另外两个.c需要引用头文件game.h,如下:

// test.c        game.c
#include "game.h"

创建字符类型二维数组board作为棋盘,行和列分别使用已定义的ROW和COL,Game函数中已有体现。

3.棋盘的初始化

在开始下棋之前,需要先清理棋盘,程序中也是一样,我们需要对棋盘进行初始化,创建InitBoard用于实现初始化。

由于自此之后绝大部分函数都涉及到具体的游戏内功能,故函数的使用均在Game函数中,有使用方式特别的会单独指出;函数的声明存放到头文件game.h中;函数的具体内容存放到game.c中,在此仅作演示,之后的声明和定义将省略。

声明:

// game.h
void InitBoard(char board[ROW][COL], int row, int col);

定义:

// game.c
void InitBoard(char board[ROW][COL], int row, int col)
{
	;
}

具体的内容在之后会有体现。

我们将棋盘上的落棋点,也就是二维数组中的元素初始化为空格字符,即' ',可以使用嵌套的for循环来实现,具体如下:

// 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] = ' ';
		    }
	    }
}
4.打印棋盘

在下棋之前,必须要能看到棋盘的内容,所以在初始化后就需要打印出来,方便观察。

都初始化为空格,直接打印肯定不方便观察,而且下棋也需要使用其他符号作为每个格子的分界,我们使用“-”和”|“作为分界,且分隔符与单元格并不是一一对应,而是只在两个各自之间才需要分割,所以循环的条件就是行数/列数 减去1,这里类似于数组元素和下标的关系。

代码如下:

// contact.c

void PrintBoard(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]);
			if(j

打印出来的棋盘样式如图:

5.下棋->玩家下棋

我们创建PlayMove函数用作玩家下棋,并将玩家可以出的棋子定义为'*':

给出玩家下棋的提示,使用printf打印。

在二维数组中行和列的下标均是从0开始,但是玩家并不一定知道下标这个概念,进而输入为从1开始的任意数值,我们就需要判断输入玩家输入的坐标是否在现有的棋盘内,使用if判断,由于有多个条件,我们使用C语言逻辑运算符中的与&&连接判断条件。

程序设计为人机对战,当用户输入的坐标是处于棋盘上的时候,我们还需要对所输入的坐标是否已有用户或者电脑使用过此坐标。若为我们初始化的' '就是没有被使用过,就将'*'置入该坐标中。代码如下:

// game.c
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");
		}
	}
}
6.下棋->电脑下棋

我们创建ComMove函数来表示电脑下棋,使用'#'作为电脑所下的棋子:

电脑下棋无法像玩家一样输入自己要下的,我们需要使用随机数来获取行和列。

C语言中,一般使用rand()函数来生成随机数,但随机数的范围太大,且多次连续运行时产生的结果相近或为统一结果,不适用于直接生成想要的随机数。

我们使用srand()函数对随机范围进行指定,在srand中则使用time函数获取每时每刻都在变化的时间(精确到秒)。

用获取得到的随机数在求与行/列的模,求出的随机数即在二维数组的范围内,只需判断所随机得到的坐标是否有棋子即可,为空表示可以将棋子落下,有内容则重新进行随机。

关于time函数仅作使用,不做过多描述,可自行查阅。

srand即为以下内容:

// test.c
srand((unsigned int)time(NULL));

rand函数需要引用头文件,time函数需要引用头文件

函数内容如下:

// game.c
void ComputerMove(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;
		}
	}
}
7.判断结果

是比赛就有胜利、失败以及平局。

三子棋中当一方棋子连成三点一线(包括对角线)时,即为胜利,另一方失败;

当棋盘下满仍无三颗同一方棋子连线时,即为平局

我们创建IsFull函数用来判断期盼是否已满,并返回1或0作为满或未满。

只需要对二维数组进行遍历,逐个判断是否为' ',即可确定棋盘是否已满。

如下:

// game.c
int IsFull(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;
}

然后就是判断棋盘上的棋子是否有一方满足胜利条件,创建IsWin函数来进行判断

a.当一行中三个相等时

使用for循环判断当棋盘中行数不变时,每一列的格子内的棋子都相同且不为空时,就返回相同的那种棋子;

b.当一列中三个相等时

与上一种判定方法类似,不同之处在于列不变,每一行相同且不为空,返回相同的棋子

c.当对角线上相等时

对角线相同有两种情况,一种是从左上到右下,即[0][0],[1][1],[2][2]相同且不为空;另一种是从右上到左下,即[0][2],[1][1],[2][0]相同且不为空。两种情况满足一种即返回相同的棋子。这里同样使用逻辑或&&对判断条件进行连接

d.判断平局

在创建判断棋盘是否已满的函数时,我们定义为了int类型,设定了返回值为0或1,在IsWin中,我们创建变量ret对IsFull的结果进行判断,若为1(表示棋盘已满)就返回'D',表示平局;若为0(棋盘未满)就返回'N'

函数代码如下:

// game.c
char IsWin(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][1] != ' ')
		{
			return  board[i][1];//
		}
	}

	for (i = 0; i< col; i++)
	{
		if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[1][i] != ' ')
		{
			return board[1][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];
	}

	int ret = IsFull(board, row, col);
	if (ret == 1)
	{
		return 'D';
	}
	return 'N';
}
8.程序补全

函数的功能和定义已经完成,接下来回到测试文件对逻辑进行补全。

下棋需要多次的玩家和电脑进行落子,故使用while循环进行轮流下棋,逐步显示当前棋盘的内容并判断是否满足胜利或者平局,有一方胜利或者棋盘已满便退出循环,不再落子。

然后就可以对返回值进行判断:

     如果返回的是表示玩家的棋子'*',就输出玩家胜利并打印棋盘;

     如果返回的是表示电脑的棋子'#',就打印电脑胜利并打印棋盘;

     若返回其他内容则表示棋盘已满,打印平局并打印棋盘。

代码如下:

// test.c
void Game()
{
	// 一个存放棋盘的3x3的二维数组
	char board[ROW][COL] = { 0 };
	// 初始化棋盘
	InitBoard(board, ROW, COL);

	// 打印棋盘
	PrintBoard(board, ROW, COL);

	char ret = 0;

	while (1)
	{
		//玩家下棋
		PlayerMove(board, ROW, COL);
		PrintBoard(board, ROW, COL);
		//判断玩家是否赢得游戏
		ret = IsWin(board, ROW, COL);
		if (ret != 'N')
			break;
		//电脑下棋
		ComMove(board, ROW, COL);
		PrintBoard(board, ROW, COL);
		//判断电脑是否赢得游戏
		ret = IsWin(board, ROW, COL);
		if (ret != 'N')
			break;
	}
	if (ret == '*')
	{
		printf("玩家赢了\n");
	}
	else if (ret == '#')
	{
		printf("电脑赢了\n");
	}
	else
	{
		printf("平局\n");
	}
	PrintBoard(board, ROW, COL);
}

至此,简单的三子棋游戏已经完成,具体的代码文件已上传,可点击下方链接进行查看:

GitCode-shiwuqing-game1

Gitee-shiwuqing-game1

你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧


文章题目:C语言----小游戏三子棋-创新互联
文章URL:http://ybzwz.com/article/ddddhc.html