[游戏制作]-C语言实现井字棋(三子棋)游戏简单版

制作目录

  • 构思整个制作结构
    • 什么是井字棋?
    • 如何用C语言知识思考?
  • 开发(制作)流程
    • 主程序设计
    • 游戏程序设计
  • 游戏效果
  • 制作感悟与心得

构思整个制作结构

什么是井字棋?

我相信很多人都玩过这个经典的小游戏,它规则简单且有趣,但是为了严谨性和便于思路梳理,我还是再赘述一下这个游戏/doge
[游戏制作]-C语言实现井字棋(三子棋)游戏简单版_第1张图片
这是一张井字棋盘,显然,棋盘是一个九宫格,有9个落棋点。游戏开始,O方或者X方玩家任意一方先落子在棋盘上,每次一颗占一格,双方交替进行,直至有一方玩家的棋子横纵斜“三星一线”时,取得游戏胜利,当然也会出现平局。

如何用C语言知识思考?

由上文知,我们的井字棋需要一个棋盘,我们必须先制作一个棋盘,在C语言中,像九宫格这样的规则平面方格图形,我们都有一个很棒的工具可以实现,那就是二维数组。同时利用数组可以存值的特性,棋子和棋盘的问题我们就都有了下手的地方。
有了棋盘和棋子,我们就可以开始玩我们的游戏了,玩家可以通过输入一个二维坐标把值传到数组里来下棋,而电脑则随机下棋即可(由于现在能力有限,我只能做出这样的“人工智障”,“人工智障不会根据你下的棋来判断它应该如何落子,而是通过随机值下棋”。等日后程度更深的时候,我会回来把它完善!)。
整体的思路好像已经很清楚了,至于实现的细节,我觉得在过程中提及会更好。

开发(制作)流程

主程序设计

  • 首先,我们应该打印一个菜单来提示用户,这么做可以让整个程序更像一个游戏一些。
    那么我们就在我们的主程序(demo.c文件)中写一个菜单函数,菜单函数meun()只需要做一个打印显示给用户即可。
void meun()
{
	printf("**************************\n");
	printf("********  1.play  ********\n");
	printf("********  0.exit  ********\n");
	printf("**************************\n");
}
  • 用户进行选择,玩游戏-play或者退出游戏-exit,我们的游戏可以由玩家决定是否要重复玩下去,因此我们使用循环来进行整个游戏的外部过程。
    玩家通过键入“1”或者“0”来进行选择,而使用1和0两个数字还有一个好处,我们可以通过C语言中0为假,非0为真这样的特性,很方便地写一个循环。
    由于是键入一个数字进行选择,我们使用switch-case语句,这样我们的主函数就实现了。
int main()
{
	do
	{
		meun();
		printf("请选择:");
		scanf("%d", &choose);
		switch (choose)
		{
		case 1:       //玩家键入“1”,玩游戏,进入我们的游戏函数game()
			game();
			break;    
		case 0:
			printf("退出游戏\n");
			break;
		default:     //玩家键入了非“1”,“0”的数字,需要重新键入
			printf("选择有误,请重新选择!\n");
			break;
		}
	} while (choose);   //选择0-1的好处体现了

	return 0;
}

游戏程序设计

  • 设计完我们的主程序,我们就要着手我们的重点——游戏主体了
    像上文中提到的,我们自定义并设计game()函数;
    首先,根据我们的思考,我们应该先创建一个二维数组,根据棋盘特点,我们容易知道这个二维数组应该是3行3列,为了落子(棋子我设计的是‘O’和·‘X’)和棋盘更好的显示,我们都应该将这个数组定义为char类型。
char board[3][3];

我相信很多初学者者会如是编写,其实我们在初学的时候就应该养成一个好习惯,尽量少放一些固定的需求变量进入代码,这样当我们需要修改这些变量来改变游戏参数的时候会很麻烦,这里我推荐再创建一个头文件game.h来定义一些参数,当我们需要使用这些参数时,只需要在对应的程序中引用这个头文件即可。同时,修改这些参数也会十分方便。

#pragma once
#include
#define ROW 3
#define LINE 3

game.h还可以帮助我们存放一些函数的声明,只需要在使用函数的地方引用它即可。同时我们还可以将别的多文件使用的头文件放进game.h里,这样做我们在使用它们时只需要引用我们的game.h即可,比如这里我将标准输入输出函数库的头文件就放入其中,非常好用~

  • 接下来我们需要初始化我们的棋盘
    设计一个初始化数组的函数Init_board(),我们需要把数组地址和它的大小信息传入函数。为了便于修改和查找,我们新建一个game.c的c程序,将游戏需要的功能函数全部放进去。
    初始化棋盘实现如下:
void Init_board(char board[ROW][LINE], int row, int line)
{
	int i = 0;
	for (i = 0; i < row; i++)
	{
		int j = 0;
		for (j = 0; j < line; j++)
			board[i][j] = ' ';
	}
}

遍历整个数组中的元素,并把数组中的元素全部打印空格

  • 游戏开始,我们需要看到一张棋盘,以便于我们落子,所以接下来设计打印棋盘的函数Show_board():
    实现如下:
void Show_board(char board[ROW][LINE], int row, int line)
{
	int i = 0;
	for (i = 0; i < row; i++)
	{
		int j = 0;
		for (j = 0; j < line; j++)
		{
			if (j != line - 1)
				printf(" %c |", board[i][j]);   //设计棋盘分割线我使用了两种不同写法,这是第一种,也是棋盘内容和纵向分割线
			else
				printf(" %c ", board[i][j]);
		}
		printf("\n");
		if (i != row - 1)                //这是第二种,也是水平分割线和从向分割线
		{
			for (j = 0; j < line; j++)
			{
				printf("---");
				if (j != line - 1)
					printf("|");
			}
			printf("\n");
		}

	}
}

这里的难点在于棋盘界面设计的美观性,起初我很是头疼如何设计出格子可以存放数据,后来受人指点才发觉可以将分割线当作数组元素中的一部分打印呈现,这样就能很好的划分棋盘且能存放数据。

打印出的效果是这样的:[游戏制作]-C语言实现井字棋(三子棋)游戏简单版_第2张图片
由于先前在game.h中定义了ROW和LINE,我们可以随时修改它来改变棋盘大小。

  • 做好了棋盘,接下来就可以“下棋”了。我们分别设计玩家走和电脑走的函数。在game.c中设计。在此之前,我们需要在game.h里声明我们自己写的所有函数,这样在主程序中引用这个自定义的头文件便可以直接使用。
#pragma once
#include
#define ROW 3
#define LINE 3
//声明函数:
void Init_board(char board[ROW][LINE], int row, int line);
void Playermove(char board[ROW][LINE], int row, int line);
void Computermove(char board[ROW][LINE], int row, int line);
void Show_board(char board[ROW][LINE], int row, int line);

玩家下棋:

void Playermove(char board[ROW][LINE], int row, int line)
{
	int i = 0;
	int j = 0;
	while (1)
	{
		printf("轮到你下了(输入落子坐标以继续):");
		scanf("%d %d", &i, &j);
		if ((i >= 1) && (i <= row) && (j >= 1) && (j <= line))
		{
			if (board[i - 1][j - 1] == ' ')
			{
				board[i - 1][j - 1] = 'o';
				break;
			}
			else
				printf("该位置已经有棋子占用!重新输入\n");
		}
		else
			printf("坐标非法!请重新输入\n");
	}
}

电脑下棋:

void Computermove(char board[ROW][LINE], int row, int line)
{
	int x = 0;
	int y = 0;
	printf("轮到电脑下:\n");
	while (1)
	{
		x = rand() % row;
		y = rand() % line;
		if (board[x][y] == ' ')
		{
			board[x][y] = 'x';
			break;
		}
	}
}

就像我开始说的一样,电脑下棋的操作是完全随机的,也就是我们常调侃的“人工智障”。那么如何随机下棋呢?我们需要随机给定下标,使用随机数生成函数rand(),对行列数字取模就能得到row-1和line-1范围的数字,正好符合了数组的性质。使用rand()必须先在主函数加入一段函数,并且包含两个新的头文件

#include  使用rand的库
#include    时间函数库,便于我们生成时刻变化的随机数
srand((unsigned int)time(NULL));  //这是随机数生成函数。它的使用方法我们可以参考msdn离线软件。
  • 我们知道,各自下一颗棋是无法结束游戏的,所以我们需要依次交替下棋,故而使用循环。我们先设置循环条件为1,后期再做修改。
while (1)
	{
		//玩家下棋:
		Playermove(board, ROW, LINE);
		Show_board(board, ROW, LINE);
	
			//电脑自动随机下棋:
		Computermove(board, ROW, LINE);
		Show_board(board, ROW, LINE);

	}
  • 接下来就是整个制作过程的最后一步了,我们需要设计一个函数来判断玩家赢还是电脑赢。
    而这个判断的过程应该加在哪儿呢?
    答案是每下一次棋以后,整个思路是:下棋–>判断–>赢(平局)(无结果)–>跳出循环,结束游戏(跳出循环,结束游戏并作说明)(继续下棋)。
    Judge()实现如下:
int Isfull(char board[ROW][LINE], int row, int line)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < line; j++)
		{
			if (board[i][j] == ' ')
				return 0;
		}
	}
	return 1;
}
char Judge(char board[ROW][LINE], int row, int line)
{
	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 < line; 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, LINE))
	{
		return 'Q';
	}
	//继续游戏:
	return 'C';
}

根据9宫格特点,写出实现横纵斜三星一线即可取胜的代码。

  • 我们对我们的游戏函数主体进行优化:最后的设计如下:
void game()
{
	//构建棋盘地基:
	char board[ROW][LINE];
	//棋盘初始化:
	Init_board(board, ROW, LINE);
	//打印棋盘界面:
	Show_board(board, ROW, LINE);
	char rez = 0;
	while (1)
	{
		//玩家下棋:
		Playermove(board, ROW, LINE);
		Show_board(board, ROW, LINE);
		rez = Judge(board, ROW, LINE);
		if (rez != 'C')
			break;
		//电脑自动随机下棋:
		Computermove(board, ROW, LINE);
		Show_board(board, ROW, LINE);
		rez = Judge(board, ROW, LINE);
		if (rez != 'C')
			break;
	}
	if (rez == 'o')
		printf("你赢了!\n");
	else if (rez == 'x')
		printf("你输了!\n");
	else
		printf("平局!\n");
}

玩家每下一次棋,打印一次棋盘并且判断输赢,是玩家赢还是电脑赢还是平局或者是无结果,游戏继续,同时下棋的过程设计为循环。

  • 到这儿我们的游戏已经设计完毕了,接下来我们看看效果:

游戏效果

[游戏制作]-C语言实现井字棋(三子棋)游戏简单版_第3张图片

可以看出,我们的电脑很笨,为了测试我们的代码功能的完整性
我们可以故意让着电脑再来一局:

[游戏制作]-C语言实现井字棋(三子棋)游戏简单版_第4张图片

在我的精心操作下,电脑成功赢得了比赛 ! (/doge)这至少证明我们的电脑也是会赢的,程序运行一切正常。

  • 到这儿,整个游戏的制作就告一段落

制作感悟与心得

这是我第一次进行一个完整的小游戏的制作,它功能简单却有我不少心血,日后待编程技术不断提高,我会持续更新这个系列!
通过此次制作,我发现很多时候程序员要做的思考的量要和实践操作相当,甚至大于操作,编程是逻辑的艺术,待我慢慢发掘~

你可能感兴趣的:(新手上线,小游戏,游戏,c++,c语言)