目录
前言
正文开始
1.扫雷游戏的分析与设计
1.1扫雷游戏的功能说明
1.2游戏的分析和设计
1.2.1数据结构的分析
1.2.2文件结构设计
2.代码实现
2.1.1文件game.h
2.1.2文件game.c
2.1.3文件test.c
2.2讲解
2.2.1主体
2.2.2有关定义
2.2.3函数
1.InitBoard()初始化棋盘
2.SetMine()随机设置雷
3.GetMineCount()显示雷数
4.FindMine()排查雷
前言
扫雷,想必大家都很熟悉,一款经典的益智小游戏。在我们动手敲代码前,先分析一下它的逻辑。这里以最简单的9×9的棋盘大小为例。先先创建一个9×9大小的一个棋盘,然后在这个棋盘内随机布置10个雷,然后玩家要去排查雷。在排查的时候,如果排查的位置是雷,就爆炸?,GG了,如果不是雷就显示周围8个位置中,雷的数量。当我们把所有的不是雷的位置找出来,也就把雷全都排查出来了,游戏胜利✌?。大概就是这么一个逻辑。
这里附上网页版扫雷
正文开始
1.扫雷游戏的分析与设计
1.1扫雷游戏的功能说明
目标:
a.使用控制台实现经典的扫雷游戏。
(什么是控制台?控制台就是程序运行起来出现的黑色框框)
控制台
b.游戏可以通过菜单实现继续玩或退出游戏。
c.扫雷的棋盘是9×9的格子
d.默认随机布置10个雷
e.可以排查雷
如果位置是雷,就显示周围有几个雷如果位置是雷就爆炸游戏结束f.把10个雷之外的所有非雷都找出来,排雷成功,游戏结束。
游戏的界面:
初始界面
排雷界面
失败界面
1.2游戏的分析和设计
1.2.1数据结构的分析
扫雷的过程中,布置的雷和排查出的雷的数据(信息)都需要储存。数据要储存,要么要变量,要么要数组,因为我们的棋盘是一个9×9的格子,所以我们用一个9×9的二维数组来存放最合适。
如果这个位置布置雷,就存放1,如果不是雷,就存放0。(你可能有别的想法,为什么这里要用1表示雷呢,为什么不用别的?请带着疑问耐心地往下看)
但当我们在排查雷的时候,如果一个位置周围只有1个雷,那这个地方就显示1,这是不是就跟表示雷的1,产生歧义了?那我们怎么避免这个问题呢,我们专门创建一个9×9的棋盘放置布置的雷(对应一个数组mine)再创建一个9×9的棋盘来放置排查出的信息(对应数组show),这样就互不干扰了,把雷布置到mine数组,在mine数组中排查雷,排查出的的数据放在show数组中,并打印show数组的信息给玩家,同时为了保持神秘感,将show数组开始初始化为字符'*'
,将mine数组开始初始化为字符'0',布置雷改成'1'。
mine数组
show数组
但注意,在排查边界的位置时,如下图
它在棋盘上的周围只有3个格子,难道我们还要单独写代码专门来统计它周围的雷的数量吗?
我们不如在创建数组的时候扩大一圈,这样是不是方便多了,就是11×11大小的数组。
mine数组
show数组
1.2.2文件结构设计
我们将这个游戏分装成3个文件
game.h用来写游戏中需要的函数的声明个数据类型 game.c用来写游戏中函数的实现等 test.c用来写游戏的主了逻辑2.代码实现
2.1.1文件game.h
#pragma once#include <stdio.h>#include <stdlib.h>#include <time.h>#define EASY_COUNT 10#define ROW 9#define COL 9#define ROWS ROW+2#define COLS COL+2//菜单void menu();//游戏主体void game();//初始化棋盘 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);
2.1.2文件game.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 mine[ROWS][COLS];//存放布置好的雷 char show[ROWS][COLS];//存放排查出的雷的信息 //初始化棋盘 //1. mine数组最开始是全'0' //2. show数组最开始是全'*' InitBoard(mine, ROWS, COLS, '0');InitBoard(show, ROWS, COLS, '*');//打印棋盘 //DisplayBoard(mine, ROW, COL);DisplayBoard(show, ROW, COL);//1. 布置雷 SetMine(mine, ROW, COL);//DisplayBoard(mine, ROW, COL);//2. 排查雷 FindMine(mine, show, ROW, COL);}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){//布置10个雷 //⽣成随机的坐标,布置雷 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--;}}}int GetMineCount(char mine[ROWS][COLS], int x, int y){return (mine[x - 1][y] + mine[x - 1][y - 1] + mine[x][y - 1] + mine[x + 1][y - 1] + mine[x + 1][y] +mine[x + 1][y + 1] + mine[x][y + 1] + mine[x - 1][y + 1] - 8 * '0');}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 = GetMineCount(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);}}
2.1.3文件test.c
#include "game.h"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讲解
2.2.1主体
用do-while循环,使程序至少执行一次,输入 1,开始游戏;输入 0,退出游戏;输入其他值,提示重新输入,但它是非0值,循环继续。
2.2.2有关定义
我们引入一个概念——泛型编程,即一个程序适用于多种情形,大规模的减少程序代码的编写量。
我们用#define EASY_COUNT 10来设置雷的数量,并用类似的方法设置行数,列数,扩大后的行数,扩大后的列数。未来想要玩更大的棋盘时,只需要改变相应变量后的数字即可。
2.2.3函数
1.InitBoard()初始化棋盘
参数
数组,行数,列数,初始化的内容
因为mine数组和show数组大小完全一样,差异仅为初始化的内容,如果给它们各自设计一个函数未免太繁琐,不如在传参的时候,把想要初始化的内容一起传过去。
2.SetMine()随机设置雷
参数
数组,行数,列数
因为用define定义的变量不能被修改,我们语言单独创建一个变量来存放它的值。
设置雷需要用到一个库函数:rand(void)(头文件stdlib.h)用来生成随机值,但这个随机是一种伪随机,rand() 根据系统提供的伪随机数种子得到的,而每次执行他生成的随机值都是相同的,因为种子没有改变。为了解决这个问题,我们又需要用函数 srand(unsigned seed) (头文件stdlib.h),它是通过参数seed来改变系统提供给 rand() 的种子的,同样的,如果seed的不变,那么随机值也不会改变,那么我们引用第三个函数 time() (头文件time.h),它的返回值表示从CUT(Coordinated Universal Time)时间1970年1月1日00:00:00(称为UNIX系统的Epoch时间)到当前时刻的秒数,类型为time_t,需要强制类型转换为(unsigned int),我们不需要它做其他事,所以给它一个NULL(time(NULL))。这样我们就可以实现随机值的生成了。以9×9的棋盘为例,我们有9行9列,所以我们希望生成的随机数的范围是1~9,只需要将rand()%9(0~8)加1(1~9)就行了。
3.GetMineCount()显示雷数
参数
数组,行数,列数
前面我们用'1'表示雷,'0'表示非雷,在排查雷时,若要显示周围的雷数,只需要将周围的数字加起来就行了(注意我们存的是数字字符,最后要记得减去8个字符0的和)
4.FindMine()排查雷
参数
数组,数组,行数,列数
玩家将所有不是雷的位置找出来就赢了,比如9×9的棋盘,就有9*9-10=71个位置,我们就循环71次,
只要排完71个位置就胜利✌?,玩家选择想排查的位置,如果这个地方存的是1就爆炸,结束,如果是0就显示周围的雷数。
到这里今天分享就结束了,但扫雷还有很多可拓展的内容,日后有机会再给大家分享~~
欢迎大家到评论区留言~~