初阶C语言如何实现扫雷?看完这个,闭着眼都能写!

扫雷游戏(初级)

背景介绍

扫雷,这个游戏想必绝大多数人都听过或者玩过,其游戏流程就是选择扫雷点,然后逐步探索雷区,当在没有触发地雷的情况下探索玩整个区域,就算游戏通关

那么,要怎么利用C语言来简单实现扫雷游戏呢?

接下来,请先浏览扫雷的基本网格图

该项目介绍的是:利用初阶C语言实现扫雷游戏

在这之前我们需要先回顾以下扫雷游戏的规则以及流程特色:

奥,在这之前,还请您对这个表格保留一个影响,因为接下来会涉及到两个网格,或者说是两个二维数组,一个数组用于执行游戏设定操作,另一个数组用于呈现游戏效果

它们分别是:

游戏操作数组 put_mines[][] ,顾名思义,对吧

效果呈现数组 show_mines[][] , 呈现游戏效果

规则:

踩到雷,游戏失败

把除了雷区以外的地方都探索完毕,游戏通关

流程:因为只涉及到了初阶C语言知识,所以所有的地区都需要通过每一次输入坐标来实现

//在C进阶甚至C++中,将会介绍更加高效的游玩方式以及更加丰富有趣的游戏设计!

一,项目分析环节

//这个环节就是介绍这个扫雷项目是怎么实现的,它是一个怎样的思维方式?是怎么一步步的去呈现这个项目的 

1,游戏流程

如下图,选取一个地址,进行探索

我们选取... 这个位置 <4,5>

程序反馈如下:

可以发现, 这个位置是非雷区,而且,其周围3X3范围内都是非雷区,因为在该项目中,我们会通过创建一个侦测器,把探索坐标周围3X3地区的地雷数量统计出来,并反馈到我们所探索的地区。

接着,我们探索 和 这两个区域 <6,6> 和 <6,7>

很不幸,我们在 <6,7> 这个位置踩雷了,游戏结束!

2,设计思路

①,创建 ” 棋盘 “

在游戏流程中我们以及大致了解了游戏的玩法以及部分效果展示,接下来,我们来梳理一下整体的设计思路!

首先,在初阶C语言中,我们需要通过二维数组来实现棋盘的组建,在此基础上,进行一系列擦欧总和编译

所以我们首先要创建一个大小合适的二维数组。

这里就称之为 arr[9][9]

因为在二维数组中,我们是使用 ‘ * ’ 来表示为揭秘区域,所以也用 ‘ 1 ’ 来表示地雷区域,所以,使用 char 来定义数组

可以发现,9X9就算地雷所放置的全部范围,但是我们还要实现选取一个坐标,自动探索其周围3X3空间的地雷情况,所以9X9的网格是不够用的。应该采用11X11,并且,为了便于观察和选取坐标,我们可以把行数和列数重新调整,这就得到了完整的游戏棋盘

如图我们创建了一个11X11的棋盘,途中标出的只是游玩时候将会打印到屏幕上的内容,虽然创建了11X11的棋盘,但是我们实际上只是在9X9范围内操作,所以就需要定义两套变量,为了方便使用,我们可以采用全局宏定义

我们定义 11X11 的行和列为 ROWS 和 COLS

定义9X9 的行和列为 ROW 和 COL 。它们之间关系为

ROW = COL = 9

ROWS = ROW + 2

COLS = COL + 2

②,初始化棋盘

在①中我们进行了棋盘的创建,自定义一个函数,用于初始化棋盘,其大致内容,就是见谅两层循环,外层行循环,内层列循环,行每执行一次,列执行11次,如此就能得到一个11X11 的棋盘了,创建完成后,我们需要在创建一个自定义函数来打印棋盘,而打印钱,只需要打印9X9范围就行

因为我们是在这个范围内尽心扫雷游戏,11X11的创建只是为了程序在搜索探索雷区的时候不发生越界访问

其中的红色方框,模拟了某一次排查地雷的时候,勘测器工作的范围

侦测器,就是说,我们最后需要对我们的地雷进行排查,而这个时候需要使用侦测器来帮助我们探索所选择区域周围3X3区域范围内的地雷数量,以帮助我们顺利完成地雷的排查工作

以下是初始化程序的参考模型:

③,打印棋盘

棋盘的打印,并非只是打印9X9的雷区,实际上我们还需要再用一行和一列来实现行号和列号的打印,这样打印出来的棋盘比较美观

以下是打印棋盘程序的参考模型:

那么现在我们就能够打印出这样的东西:

④,埋下地雷

在②和③中,我们成功创建了一个棋盘,尽管它看上去确实很简陋,关于这点我们将会在更全面版本的迅雷游戏中去进一步优化它

在创建好了棋盘后,我们要开始设计一些挑战因素,通过随机数rand,在9X9棋盘内随机生成一定数量的地雷,可以采用生成的随机数 余上 9,或者更加包容的设定,我们让随机数 余上 行数 和 列数, 这样,当行数和列数都为9,就能得到 0 ~ 8 的随机数,把所得到的随机数都进行 +1 输出,就能得到 1 ~ 9 的随机数,通过行得到的随机数设为x,通过列得到的随机数设为y,这样就得到了坐平面直角坐标系中的x和y。这样程序就能在9X9范围内随机放置地雷

当然,为了放置城西偶然的生成了两个相同的坐标,我们需要对每一个地雷的坐标进行判断,确保不会出现少雷的情况

参考程序如下:

如此,我们就通过while循环把雷成功埋进了棋盘,这里我们进行一次小小的测试:

以上是某一次游戏中,程序随机生成的地雷情况

⑤,排查地雷

我们在④中成功布置了地雷,现在让我们设计程序帮助我们把这些随机的地雷都找出来

对于地雷的排查,其实很简单,我们只需要随便选取一个坐标,只要不是运气太差,是不会在第一步就结束游戏的。

假如我们输入了坐标

那么,在放置了地雷的棋盘中,这里我们称之为put_mines[ROWS][COLS]

需要对这个坐标进行核实,看看这个位置上是否已经有了地雷,如果有地雷,则触发游戏失败的程序,如果没有,就继续进行下一步探索。

之前也提到过,我们还需要在某个已探索的3X3区域周围进行地雷侦测,将发现的地雷数量返回到我们所探索的那个地址

所以这里就需要创建一个自定义函数来计算地雷的数量,从而实现地雷的侦测

侦测地雷

那么,我们创建的是char字符数组,所以可以通过ASCII 码 的值雷进行地雷数量的确定

我们设定的非雷区就是 ‘ 0 ’ ,而雷区就是 ‘ 1 ’ ,

所以,只需要把 ‘ 1 ’ - ‘ 0 ’ ,就能得到1,再把这个1赋值给整型的一个计数变量,就能完成一个地雷的计数操作,那么我需要再了解已探索区域 周围3X3区域的内容:

所以我们只需要对着八个区域进行勘探就行了,可以参考以下计算模型来得出地雷数量:

计算原理,就是把八个区域的字符总大小求出来,再减去8个区域下 ‘ 0 ’ 字符的大小综合,也就是说,把勘探区域的字符大小,检测数来,减去八个区域再非雷情况下的字符大小,就能得到地雷数量。

⑥,设定输赢

游戏总的有个结局,输或者赢,都需要一个东西去结束它,在① ~ ⑤ 中,我们进行了棋盘的布置,地雷的布置,还进行了地雷的排查,现在是时候设定胜利条件了

我们可以在排查雷的时候设置一个变量 win, 每当我们进行一次成功的地雷排查,这个变量就自增一次,在这样的条件下,我们只需要设定,如果 变量 win 的数值大小,等于棋盘总区域数量减去雷区数量的数值大小,就说明我们已经顺利完成了所有地区的勘探任务,任务圆满完成,游戏成功通关!

可以参考以下程序来实现:

//以下就是我们对于扫雷游戏初阶的全部介绍环节,下面将会展示程序原码,并对其进行必要性的解释

二,项目原码

在这个项目中,我们将程序分类存放,以便于更改和编译,避免在一个源文件下编译,造成的混乱

我们将会设定两个 .c 源文件 , 一个 .h 头文件

它们分别是:

主函数测试文件 —— Minesweep_01_test.c

用于编译主函数,也就是主程序所运行的函数

自定义函数程序文件 —— Minesweep_01.c

用于编译我们自定义的函数

项目函数依托头文件 —— Minesweep_01.h

把我们定义的函数在这个文件中声明,然后再著文件中直接 #include 应用就行了

那么我们就从头文件开始

1,项目函数依托头文件 —— Minesweep_01.h

#pragma once
#include <stdio.h>
#include <windows.h>
#include <stdlib.h>
#include <time.h>
//设定侦测器工作范围,以及地雷的埋设范围
#define ROW 9
#define COL 9

//设定基础数组的行和列,建立基本的工作网格
#define ROWS ROW + 2
#define COLS COL + 2

//设定难度,及埋设地雷的数量
#define Easy_mod 10

//这里多定义了两个常量,将用于后续版本的优化使用
#define Normal_mod 15
#define Hard_mod 20

//这里设置该源文件为自定义函数的头文件
//初始化棋盘,当我们创建了一个11X11的工作棋盘,需要自定义一个函数来对其元素进行初始化
void InitBoard(char Board[ROWS][COLS], int rows, int cols, char set);

//初始化数组初始化11X11,这里只需要9X9棋盘,所以只打印9X9
void DispalyBoard(char Board[ROWS][COLS], int row, int col);

//布置地雷
void SetMine(char mines[ROWS][COLS], int row, int col);

//排查地雷
void FindMine(char put_mine[ROWS][COLS], char show_mine[ROWS][COLS], int row, int col);

2,自定义函数程序文件 —— Minesweep_01.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "Minesweep_01.h"
//自定义实现初始化数组
void InitBoard(char Board[ROWS][COLS], int rows, int cols, int set)
{
	for (int i = 0; i < rows; i++)
	{
		for (int j = 0; j < cols; j++)
		{
			Board[i][j] = set;
		}
	}
}
void DispalyBoard(char Board[ROWS][COLS], int row, int col)
{
	printf("---------* 扫雷游戏 *----------\n");
	//吧行和列号打印出来
	//列号
	for (int i = 0; i <= col; i++)
	{
		printf("%d  ", i);
	}
	printf("\n");
	//定义外层循环,每循环一次,就是一行
	for (int i = 1; i <= row; i++)
	{
		//行号
		printf("%d  ", i);
		for (int j = 1; j <= col; j++)
		{
			//定义内层循环,每外层循环一次,就是再一行内循环完一个列
			printf("%c  ", Board[i][j]);
		}
		printf("\n");
	}
	printf("---------* 扫雷游戏 *----------\n");
}
void SetMine(char mines[ROWS][COLS], int row, int col)
{
	//设置雷数
	int count = Easy_mod;
		//通过随机数产生地雷的坐标
		//随机数会很小或者很大,所以余上行或列就能得到0~8
		//0~8分别都 + 1 就能得到 0~9
		while (count)
		{
			int x = rand() % row + 1;
			int y = rand() % col + 1;
			if (mines[x][y] == '0')
			{
				//为了避免系统产生了重复的坐标
				mines[x][y] = '1';
				count--;
			}
		}
}
//这个函数只是用来统计地雷数量,只再这个源文件中使用,所以用static修饰避免问题
static int get_mines_count(char put_mine[ROWS][COLS], int x, int y)
{
	return put_mine[x - 1][y - 1] +
		put_mine[x - 1][y] +
		put_mine[x - 1][y + 1] +
		put_mine[x][y - 1] +
		put_mine[x][y + 1] +
		put_mine[x + 1][y - 1] +
		put_mine[x + 1][y] +
		put_mine[x + 1][y + 1] - 8 * '0';
}
void FindMine(char put_mine[ROWS][COLS], char show_mine[ROWS][COLS], int row, int col)
{
	//首先选择排查坐标
	printf("请输入你要排查的坐标:> ");
	int x = 0, y = 0;
	int win = 0;
	while (win<row*col-Easy_mod)
	{
		scanf("%d%d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			//如果输入的坐标是地雷坐标一样
			if (put_mine[x][y] == '1')
			{
				printf("很遗憾,你踩雷了,游戏失败!\n");
				//游戏失败以后,将布置了地雷的数组打印出来
				DispalyBoard(put_mine, row, col);
				printf("正在回到菜单,请稍后......\n");
				//让玩家有足够的时间观察
				Sleep(9000);
				system("cls");
				break;
			}
			//如果没有踩雷
			//就吧周围3X3的空间中的地雷数量统计并返回输出
			else
			{
				//把侦测器统计的地雷个数赋值给int类型的count
				int count = get_mines_count(put_mine, x, y);
				//将这个结果加上 ' 0 ',转换成字符,再赋值给字符数组中的勘探坐标处
				show_mine[x][y] = count + '0';
				//每进行一次成功的排查,就把胜利条件自增,以满足后续胜利条件
				win++;
				system("cls;");
				//显示排查信息
				DispalyBoard(show_mine, row, col);

			}
		}
		else
		{
			printf("输入的地址无效,请重新输入!\n");

		}
	}
	if (win == row * col - Easy_mod)
	{
		printf("\n");
		printf("恭喜你成功通关!\n");
		printf("\n");
		DispalyBoard(show_mine, row, col);
		//让系统休眠5秒,这样给玩家足够的时间回顾上局游戏
		Sleep(5000);
		system("cls");
	}
}

3, 主函数测试文件 —— Minesweep_01_test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "Minesweep_01.h"
void menu()
{
	printf("******************************\n");
	printf("****  欢迎来到游戏:扫雷  ****\n");
	printf("****     1.  开始游戏     ****\n");
	printf("****     0.  退出游戏     ****\n");
	printf("******************************\n");
	printf("\n");
}
void game()
{
	//创建一个数组用于存放地雷
	char put_mine[ROWS][COLS];

	//创建一个数组用于显示地雷以及后续效果
	char show_mine[ROWS][COLS];

	//初始化数组
	InitBoard(put_mine, ROWS, COLS, '0');
	InitBoard(show_mine, ROWS, COLS, '*');

	//打印数组
	//为了使得游戏有可玩性,所以要把布置地雷的数组隐藏
	//DispalyBoard(put_mine, ROW, COL);
	DispalyBoard(show_mine, ROW, COL);

	//开始布置地雷
	SetMine(put_mine, ROW, COL);

	//为了确定是否正确产生了随机的地雷,这里编写打印布置地雷后的数组
	DispalyBoard(put_mine, ROW, COL);
	//
	//排查地雷
	FindMine(put_mine, show_mine, ROW, COL);
}
int main()
{
	int choose = 0;
	//随着事件产生随机数
	srand((unsigned int)time(NULL));
	do
	{
		menu();
		printf("请选择:\n");
		scanf("%d", &choose);
		switch (choose)
		{
		case 1:
		{
			printf("游戏即将开始!\n");
			game();
			break;
		}
	    	case 0:
	    	{
	    		printf("即将推出游戏!\n");
	    		break;
	    	}
	        	default:
	        	{
	        		printf("无效的输入,请重新输入!\n");
					break;;
	        	}
		}
	} while (choose);
	return 0;
}

----------以上就是本项目的全部原码----------

三,不足及期望

这个程序只是使用了C语言中最简单,最基础的部分来实现扫雷游戏,最后的结果并不是很能令人满意,这是因为初阶的程序是不足以构建复杂项目的,存在构建的可能性,但是这样做将会是费时费力的,所以本项目最大的期望,就是希望看到这里的你,在遇到这一类问题的时候有一个参考方向,有一个大致的规划

原本是准备加入难度选择环节和棋盘大小自定义环节,但是对于初阶C语言来说有些勉强了,关于以上的实现,将会在下一个版本的扫雷游戏中得以实现

扫雷游戏1.0版本不足:

难度单一,不能自动探索连贯的非雷区,每次探索都需要输入坐标,效率低下,且棋盘没有再一个表格框架中,显得很简陋,而且不利于观察和游戏

后续版本将会在以后进行优化,目前来说未来可能会解决的问题分别是:

1,增加自定义难度环节,可以让玩家自定义地雷数量

2,增加自定义棋盘环节,可以让玩家自定义棋盘大小

以上两点实现了玩家可以自定义游戏难度和流程长度

3,增加自动勘探功能,如果某一个已探索区域附近都是非雷区,那么将会把该坐标周围3X3范围内都清空

4,将会对于美观问题进一步优化

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值