扫雷游戏的实现( 9 * 9)

一、 文件的创建

首先要创建3个文件,作用分别如下:

game.h 

头文件:负责将一些变量存储,方便后期的修改和调用——调用的方式:

#include "game.h"
//注意,这是自定义的函数,所以要用到双引号而不是<>,<>是库函数调用时用到的符号

game.c

源文件:为了避免游戏的主程序过于冗长,把游戏中所需要的程序单独放在game.c里,这样可以使得游戏的主程序看起来更加清晰。

test.c

存放游戏主程序的文件

二、游戏思路的梳理以及变量的定义

1、思路的梳理

先定义一个函数,用来囊括游戏的所有进程,我们定义他为 Game 函数

下面是Game函数内部的构建

首先,我们定义‘0’为没有雷的地方,‘1’为有雷的地方。

这个步骤就需要利用到二维数组了,我们的目的是要做出来 9 * 9的扫雷棋盘。所以就需要用到

9 * 9的二维数组,而是用到了 11 * 11的二维数组。但实际上并非如此,为什么呢?

在扫雷的过程中,点击某一个地方(在c语言里用的是输入坐标的形式),如果是雷,则游戏结束。

而如果不是雷呢?那我们就需要计算点击(或输入)的地方周围有多少个雷(方法会在后面介绍到)。

但是如果你点击(或者输入的地方)在扫雷棋盘的边缘呢(如图一)?周围你并没有定义数组,这样计算起来会比较麻烦。

为了避免这个问题,我们直接创建 11 * 11的二维数组,在判断的过程中用到 11 * 11的部分,而在游戏的过程中的时候显示只用显示 9 * 9的部分即可,这样可以很方便地解决许多问题。

   9 * 9 扫雷棋盘

                                   图1

这样,我们就能开始定义了。

但是在这里又出现了一个问题:我们需要把雷的信息体现在屏幕上。如果我们定义没有雷是‘0’的话,假设检测到雷的数量是1,那么我们把‘1’替换到这个位置,那么这个‘1’到底是显示的雷的数量,还是这里原本就有一个雷?

针对这个问题,我们就需要设计两个扫雷棋盘,一个用来打印在屏幕上,一个用来放置、检测雷的数量。

对于打印在屏幕上扫雷棋盘、判断雷的扫雷棋盘我们都定义为char类型

这个函数只是需要执行指令,而不需要有返回值,所以我们定义他为viod类型

对于印在屏幕上扫雷棋盘,我们定义为 char mine

对于判断雷的扫雷棋盘,我们定义为    char show

下一步:既然知道了我们应该用二维数组 char 和函数 InitBoard 来实现,我们就需要对数组进行初始化。

在这里,我定义一个函数为 InitBoard 用来初始化,为了确认该数组是否完成初始化,我们用另一个函数 DisplayBoard 来实现数组的打印。

下面就是构建布置雷(SetMine)和排查雷(FindMine)

大概处理好游戏的内容的时候,我们开始构建扫雷游戏的主体

2. 程序的构建

首先在test.c写一个程序的框架

用 menu函数来显示给用户的提示

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

(1)我们需要一个初始界面来让用户选择是‘开始游戏’还是‘退出游戏’。

接下来就需要有我们的提示和用户的输入了,这几行代码直接在主程序中完成就可以。

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

这里让用户输入0 / 1是为了方便让下面的do---while循环结构中的while判断是否可以继续运行。

如果用户输入1: 为真,执行语句

                      0: 为假,不再执行上面的循环

利用scanf让用户输入后,在do while 循环里嵌套switch语句用来执行开始游戏、退出游戏。

如果用户选择‘1’即开始游戏,则执行 game函数后再跳出。

接下来讲解game函数是如何构建的

void game()
{
	//构建一个11*11的棋盘
	char mine[ROWS][COLS];
	char show[ROWS][COLS];	
	//初始化键盘
	InitBoard(mine, ROWS, COLS, '0');
	InitBoard(show, ROWS, COLS, '*');
	//打印初始化内容
	DisplayBoard(show, ROW, COL);
	//布置雷
	SetMine(mine, ROW, COL);
	//排查雷
	FindMine(mine, show, ROW, COL);
}

如图,经过前面的思路梳理,我们不难写出game函数的框架。

但是为什么多出来 ROWS COLS ROW COL这几个变量呢?

让我先对变量进行一个解释:

ROWS:判断雷棋盘的行数

COLS:判断雷棋盘的列数

ROW:显示棋盘的行数

COL:显示棋盘的列数

为了我们后续改变难度容易一些,所以用变量代替数字,方便替换以及观察

为了能让在test.c文件和game.c文件都能使用以上变量,我们不妨在game.h里定义这几个变量,在game.c和test.c里引用即可。

game.h如下:

#include <time.h>

#define ROWS ROW + 2
#define COLS COL + 2

#define ROW 9
#define COL 9

#define EASY_COUNT 10
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set);

void DisplayBoard(char board[ROWS][COLS], int row, int col);

void SetMine(char board[ROWS][COLS], int row, int col);

void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);

time.h文件和Easy_Count会在稍后解释。

此时需要在test.c中添加

#include "game.h"

现在开始构建game函数的内部

函数初始化棋盘:InitBoard

void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
{
	int i = 0;
	for (i = 0; i < rows; i++)
	{
		int j = 0;
		for (j = 0; j < cols; j++)
		{
			board[i][j] = set;
		}
	}
}

实参需要传递四个:

mine / show, ROWS, COLS, '0' / '*'

注意:为什么这里写‘0’和‘*’?

因为在游戏开始的时候,玩家看到的是没有雷布置的一张图,我们用星号‘*’表示。

而需要安放雷的我们用‘0’表示。

所以形参写为了:

char board[ROWS][COLS], int rows, int cols, char set

下面就是利用两个for循环遍历两个数组实现完全初始化。

打印初始化棋盘:DisplayBoard

void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
	int i = 0;
	printf("******扫雷游戏*****\n");
	for (i = 0; i <= col; i++)
	{
		printf("%d ", i);		
	}
	printf("\n");
	for (i = 1; i <= row; i++)
	{
		printf("%d ", i);
		int j = 0;
		for (j = 1; j <= col; j++)
		{
			printf("%c ", board[i][j]);
		}
		printf("\n");
	}

}

看代码可以知道,我这里写了3个for循环。为什么要三个呢?

第一个for循环目的就是要打印行号和列号,方便读者寻找和输入坐标排雷。

剩下两个for循环是为了遍历数组打印字符。

布置雷函数:SetMine

void SetMine(char board[ROWS][COLS], int row, int col)
{
	int count = EASY_COUNT;
	while (count)
	{
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		if (board[x][y] == '0')
		{
			board[x][y] = '1';
			count--;
		}
	}

}

注:EasyCount在这里代表雷的个数,已经在game.h中定义过了。

里面用到了rand函数,所以我们要在test.c文件中添加这一行代码:

srand((unsigned int)time(NULL));

在这里利用了time函数,是为了让种子时刻变化,保证出现的随机值每次会有变化。这里用系统现在的时间减去1970年1月1日的返回值。如此,为了确保x,y变量在 0 - 9之间,利用

rand() % ROW / COL + 1来取 0 - 9的值

这里就出现了1个问题:如果在这10次中有两个一样的坐标会怎么样呢?

那这里就需要一个if函数来判断要存储雷的这个地方是不是已经有雷了。如果没有雷,则放雷

且让count--,直到count为0,跳出循环,不再布置雷。这样,这个函数至少循环了10次。

排查雷的函数:FindMine  标记雷的函数:NumberMine

void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int win = 0;
	while (win < row * col - EASY_COUNT)
	{
		printf("其请输入你要查找的坐标:>");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (mine[x][y] == '1')
			{
				printf("恭喜你被炸死了,能力不行,速速开下一把\n");
				DisplayBoard(mine, ROW, COL);
				break;
			}
			else
			{
				int count = NumberMine(mine, x, y);  //计算周围的炸弹数量
				show[x][y] = count + '0';
				DisplayBoard(show, ROW, COL);
				win++;
			}
		}
		else
		{
			printf("你输入的数字有问题,请重新输入\n");
		}
	}
	if (win == ROW * COL - EASY_COUNT)
	{
		printf("恭喜你,游戏胜利\n");
		DisplayBoard(mine, ROW, COL);
	}
}
int NumberMine(char mine[ROWS][COLS], int x, int y)
{
	return((mine[x - 1][y - 1] + mine[x][y - 1] + mine[x + 1][y - 1] +
		mine[x - 1][y] + mine[x][y] + mine[x + 1][y] +
		mine[x - 1][y + 1] + mine[x][y + 1] + mine[x + 1][y + 1]) - '0' * 9);
}

首先我们要清楚获胜的条件:即  排查的数目   =  区域总数    -   雷的数目

设定一个win变量,如果没死,那么win++

为了保证严谨性,如果不小心输错了坐标,那么给出相应的提示。

而当我们排查的时候需要用到标记雷的函数:NumberMine

这个函数的原理是:

0的ACSCII值比1小一个,那么计算包括排查的地方周围 3 * 3的地方的值除以‘0’,得出来的结果就是有几个‘1’,即有几个雷。

这样整个扫雷游戏就结束了

完整代码如下:

game.h:

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

#define ROWS ROW + 2
#define COLS COL + 2

#define ROW 9
#define COL 9

#define EASY_COUNT 10
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set);

void DisplayBoard(char board[ROWS][COLS], int row, int col);

void SetMine(char board[ROWS][COLS], int row, int col);

void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);

test.c:

#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
void game()
{
	//构建一个11*11的棋盘
	char mine[ROWS][COLS];
	char show[ROWS][COLS];	
	//初始化键盘
	InitBoard(mine, ROWS, COLS, '0');
	InitBoard(show, ROWS, COLS, '*');
	//打印初始化内容
	DisplayBoard(show, ROW, COL);
	//布置雷
	SetMine(mine, ROW, COL);
	//排查雷
	FindMine(mine, show, ROW, COL);
}
void menu()
{
	printf("*******************\n");
	printf("***   1. play   ***\n");
	printf("***   0. exit   ***\n");
	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;
}

game.c:

#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"

void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
{
	int i = 0;
	for (i = 0; i < rows; i++)
	{
		int j = 0;
		for (j = 0; j < cols; j++)
		{
			board[i][j] = set;
		}
	}
}
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
	int i = 0;
	printf("******扫雷游戏*****\n");
	for (i = 0; i <= col; i++)
	{
		printf("%d ", i);		
	}
	printf("\n");
	for (i = 1; i <= row; i++)
	{
		printf("%d ", i);
		int j = 0;
		for (j = 1; j <= col; j++)
		{
			printf("%c ", board[i][j]);
		}
		printf("\n");
	}

}

void SetMine(char board[ROWS][COLS], int row, int col)
{
	int count = EASY_COUNT;
	while (count)
	{
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		if (board[x][y] == '0')	
		{
			board[x][y] = '1';
			count--;
		}
	}

}

void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int win = 0;
	while (win < row * col - EASY_COUNT)
	{
		printf("其请输入你要查找的坐标:>");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (mine[x][y] == '1')
			{
				printf("恭喜你被炸死了,能力不行,速速开下一把\n");
				DisplayBoard(mine, ROW, COL);
				break;
			}
			else
			{
				int count = NumberMine(mine, x, y);  //计算周围的炸弹数量
				show[x][y] = count + '0';
				DisplayBoard(show, ROW, COL);//为啥要这的了?
				win++;
			}
		}
		else
		{
			printf("你输入的数字有问题,请重新输入\n");
		}
	}
	if (win == ROW * COL - EASY_COUNT)
	{
		printf("恭喜你,游戏胜利\n");
		DisplayBoard(mine, ROW, COL);
	}
}
int NumberMine(char mine[ROWS][COLS], int x, int y)
{
	return((mine[x - 1][y - 1] + mine[x][y - 1] + mine[x + 1][y - 1] +
		mine[x - 1][y] + mine[x][y] + mine[x + 1][y] +
		mine[x - 1][y + 1] + mine[x][y + 1] + mine[x + 1][y + 1]) - '0' * 9);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值