目录

前言

一、扫雷是什么?

二、代码详解(模块化)

1.创建文件

2.test.c

3.game.c

4.game.h

总结

test.c

game.c

game.h



前言

相信大家过年玩游戏玩王者都玩腻了吧,就算没有玩腻,也被各种队友坑得再也没有玩游戏的欲望了吧。真要玩游戏,还得看单机游戏。

扫雷才是永远的神!


一、扫雷是什么?

在写程序之前,我们必须先要了解扫雷游戏的原理:我们随便打开一个扫雷游戏:

我们可以看到:许多小正方形组成了一个大的正方形平面,我们点击其中一个正方形,如果他不是雷,就会显示他周围的八个小正方形中有几个雷:

如图,我们翻开箭头所指的小正方形,该小方块中的数字为1,即表示他的周围8个小方块中有一个是雷。

而当我们点到雷时,游戏就结束了

注意:熟悉扫雷的兄弟们可能知道,有的扫雷程序还能够标记小红旗(即你认为哪个地方有雷,就在哪个地方右键,插一朵小红旗),但是我们这里实现的扫雷程序只是一个初级版本,故我们不写这部分代码

注意:熟悉扫雷的兄弟们可能知道,有的扫雷程序会在你点击一个雷以后,自动为你翻开一大片,但是我们这里实现的扫雷程序只是一个初级版本,故我们不写这部分代码

注意:熟悉扫雷的兄弟们可能知道,有的扫雷程序会有一个难度的选择,涉及到版面大小的变化和雷的数量的变化,但是我们这里实现的扫雷程序只是一个初级版本,故我们不写这部分代码

以上完整版的代码,将在进阶篇中再写出来。

在这里,我们写一个9*9,无拓展,无红旗标记功能的扫雷程序!

二、代码详解(模块化)

1.创建文件

我们这里尝试使用模块化的方式进行代码的书写,即创建三个文件,分别是:

test.c:用来测试整个代码的逻辑,存放主函数的地方

game.c:用来书写test.c中需要使用到的自定义函数

game.h:由于test.c和game.c不在同一个文件中,所以需要函数声明后才能使用,我们就把声明放在这个文件,以后需要用到game.c中的函数的时候,我们只需要引用该头文件即可(也可以叫做函数的封装)


2.test.c

首先:我们参照之前的三子棋的开局:->点这里

(这里我们的srand函数是种下随机数种子,我们先不管,但是肯定会用到)

这是我们制作大多数游戏都需要写的一段代码:

int main(void)
{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;
}

接着,我们可以把menu函数写了: 这个基本上没有什么难度的,就是根据我们上面的逻辑写一个菜单:

按1,即进入游戏主体

按0,即退出游戏

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

接下来,我们开始重头戏:game()函数的主体(逻辑)部分

首先,我们想一下,如果我们后面写函数来计算某个坐标周围八个方块有多少个是雷时,有一点特殊情况,那就是,当我们要“扫雷”的那个坐标在边角的时候,他周围可能没有那么多数组元素,到时候再写代码就会出现错误

那么有什么方法可以规避呢?

加入我们的棋盘是9*9,那我们可以再创建一个数组(11*11)来装雷,这样就完美解决了边角问题:

如图,这样我们就把我们真正看到的“边角”放在了中间。

1、于是,我们创建两个数组:mine 和show数组,其中mine数组拿来装雷,大小为11*11,show数组拿来显示雷的个数,大小为9*9

创建好以后,我们要初始化我们的数组,将mine数组中的每个元素初始化为 0 ,来表示没有雷(类似的,将雷设为 1 ,但是一开始是没有雷的,需要我们后期去放入)

然后我们将show数组中的每一个元素都初始化为 *

即达到这样的效果:

2、初始化好了以后,我们还要打印棋盘

3、布置雷

4、排查雷

//1.布置雷
//2.扫雷:输入坐标,是雷就炸死,不是雷就告诉你这个坐标周围八个坐标上总共有多少个雷,直到把所有非雷的位置全部都找出来,游戏结束,扫雷成功
//拓展:标记雷;炸开一片:1.该坐标不是雷 2.该坐标周围八个坐标也不是雷 3.该坐标未被排查过,这个时候就会展开一片
void game()//游戏的主体
{char mine[ROWS][COLS] = { 0 };//最开始最好都是'0',用来存放布置好的雷的信息char show[ROWS][COLS] = { 0 };//最开始是'*',用来存放排查出的雷的信息//初始化棋盘init_board(mine, ROWS, COLS,'0');init_board(show, ROWS, COLS,'*');//打印棋盘//show_board(mine, ROW, COL);//布置雷set_mine(mine,ROW,COL);show_board(show,ROW,COL);//排查雷find_mine(mine,show,ROW,COL);
}

总体上的游戏布局就是这样。


3.game.c

注意:

我们开始写game()中的代码:

首先,我们为了方便后期棋盘大小的改动,我们可以定义几个数:

#define ROW 9//即9*9的棋盘,show[][]数组
#define COL 9
#define ROWS ROW+2//即11*11的棋盘。用于mine[][]数组
#define COLS COL+2
#define EASY_COUNT 10 //简单模式中,雷的个数,我们暂且定为10个

定义以后,我们就统统用我们的定义来代替相应的数字,如果后期我们想要更改游戏的难度,也只需要更改这里的数字即可

一、void init_board(char arr[ROWS][COLS], int rows, int cols,char set)

这是我们的初始化棋盘函数,前面三个参数肯定是需要的,我们要做的是将两个数组中的每个元素设为我们设置的“set”,简单的两个for循环即可轻松实现

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

二、void show_board(char arr[ROWS][COLS], int row, int col)

这是我们的展示棋盘(不是展示mine数组,而是show数组,即展示的是 * 和数字,而不是0和1)的函数,需要的三个参数如上,分别是数组名,数组的长度,注意,我们在test.c文件中,传入的参数是ROW和COL,为9,故此时的形式参数row和col也为9,但这并不妨碍我们将形式数组设置为11(其实我们创建的两个数组的大小都是11*11的,只不过有一个数组没有将所有元素都打印出来而已)的大小,因为我们最后压根没有使用他的所有元素

我们可以看出,如果我们直接这样打印,玩家可能会很麻烦,因为每次输入坐标都需要一个个数,所以我们最好给玩家一张坐标纸,以及一条分割线

效果如图。

如果我们想要实现这样的表格,我们就需要先把第一排打印出来:

    int i = 0;printf("-----------扫雷--------------\n");for (i = 0; i <= col; i++)//标识符{printf("%d ",i);}printf("\n" );

这样我们就得到了0-9的数字排列

之后看竖排:不难发现,首先是要打印该行的行序号,之后打印相应坐标数组中的元素(*)

而且用户看到的坐标也应该是从1开始,而不是从0开始

for (i = 1; i <= row; i++){printf("%d ",i );for (j = 1; j <= col; j++){printf("%c ",arr[i][j] );}printf("\n");}printf("-----------扫雷--------------\n");

合起来便是:

void show_board(char arr[ROWS][COLS], int row, int col)
{int i = 0;int j = 0;printf("-----------扫雷--------------\n");for (i = 0; i <= col; i++)//标识符{printf("%d ",i);}printf("\n" );for (i = 1; i <= row; i++){printf("%d ",i );for (j = 1; j <= col; j++){printf("%c ",arr[i][j] );}printf("\n");}printf("-----------扫雷--------------\n");
}

三、void set_mine(char mine[ROWS][COLS], int row, int col)

这是用来布置雷的,很显然,我们又需要得到随机数了->随机数,其中有详细地解释随机数的写法。

我们需要在mine数组中,布置下10(EASY_COUNT)个雷,坐标x,y首先要满足大小在1~9之间,那么参照三子棋中的方法,只需要用一个随即是除9取余(0~8)加1(1~9),即可得到符合要求的x、y坐标,当然,每次的坐标不能重叠,所以还需要一步判断,看该坐标是否已经被占用,即该数组元素仍为0,我们才能“放置雷”

void set_mine(char mine[ROWS][COLS], int row, int col)
{int count = EASY_COUNT;int x = 0;int y = 0;while (count != 0){x = rand() % row + 1;//布置雷的坐标范围是1~9,为了出现1而不是0,就必须要最后+1,而不是%(row+1)y = rand() % row + 1;if (mine[x][y] == '0'){mine[x][y] = '1';count--;}}
}

四、int get_mine_count(char mine[ROWS][COLS], int x, int y)

这是我们用来数某个数组元素周围八个中有几个雷的。

我们回忆一下,我们在mine函数中是使用的0表示没有雷,1表示有雷,那该如何计算周围有几个雷呢?我们可以利用ASCll码的特点,即字符1减去字符0,结果是数字1,因此,我们可以周围八个元素加起来,再减去八个字符0,最后得到的数字,不就是我们想要的地雷的个数了吗?我们只需要返回该值即可

但是考虑到我们的show_board函数中,使用的是%c来替换arr[ i ][ j ]的,所以最后还需要我们将之转换为字符——即加一个字符‘0’(这一步我们可以放在后面进行)

int get_mine_count(char mine[ROWS][COLS], int x, int y)
{//'0' - '0' = 0//'1' - '0' = 1//字符相减其实是ASCLL码//这也是为什么使用0 和 1来表示非雷与雷!!!!return (mine[x - 1][y] + mine[x - 1][y - 1] + mine[x - 1][y + 1] + mine[x][y - 1] + mine[x][y + 1] + mine[x + 1][y - 1] + mine[x + 1][y] + mine[x + 1][y + 1]) - 8 * '0';
}

五、void find_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)

这是我们最核心的函数了,即开始排查雷了,由于我们放雷是用0 和1 表示的,所以我们使用if语句,如果是雷,就输入相应的文字提醒,如果不是雷,就调用上面写的 get_mine_count函数,注意,这个时候需要将数字转换为字符哈!

上述语句需要使用一个循环,只有当所有的雷都排查完了或者是排查到雷了,才会跳出这个循环

void find_mine(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("请输入要排查的坐标:>\n");scanf("%d %d", &x, &y);if (x >= 1 && x <= row && y >= 1 && y <= col){if (mine[x][y] == '1')//是雷,炸死了{printf("很遗憾,被炸死了\n");show_board(show, ROW, COL);break;}else{int count = get_mine_count(mine, x, y);//统计雷的个数show[x][y] = count + '0';//放的是字符,不是数字!.加'0'可以将数字转化成对应的字符show_board(show, ROW, COL);win++;} }else{printf("非法输入,请重新输入:>\n");}}if (win == row * col - EASY_COUNT){printf("恭喜你,排雷成功\n" );show_board(show, ROW, COL);}
}

4.game.h

这段代码用于引用头文件和函数的声明。这样便于代码的封装和团队的合作

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<time.h>#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define EASY_COUNT 10//初始化
void init_board(char arr[ROWS][COLS], int rows, int cols,char set);
//打印
void show_board(char arr[ROWS][COLS], int row, int col);
//布置雷
void set_mine(char mine[ROWS][COLS], int row, int col);
//排查雷
void find_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);

总结

模块化的写作是非常好用的,大家在练习编程时可以多多使用该方法,养成一个良好的习惯

完整代码:

目录

test.c

//test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
void menu()
{printf("**********************\n");printf("********1.play********\n");printf("********0.exit********\n");printf("**********************\n");
}//1.布置雷
//2.扫雷:输入坐标,是雷就炸死,不是雷就告诉你这个坐标周围八个坐标上总共有多少个雷,直到把所有非雷的位置全部都找出来,游戏结束,扫雷成功
//拓展:标记雷;炸开一片:1.该坐标不是雷 2.该坐标周围八个坐标也不是雷 3.该坐标未被排查过,这个时候就会展开一片
void game()//游戏的主体
{char mine[ROWS][COLS] = { 0 };//最开始最好都是'0',用来存放布置好的雷的信息char show[ROWS][COLS] = { 0 };//最开始是'*',用来存放排查出的雷的信息//初始化棋盘init_board(mine, ROWS, COLS,'0');init_board(show, ROWS, COLS,'*');//打印棋盘//show_board(mine, ROW, COL);//布置雷set_mine(mine,ROW,COL);show_board(show,ROW,COL);//排查雷find_mine(mine,show,ROW,COL);
}int main(void)
{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

//game.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"void init_board(char arr[ROWS][COLS], int rows, int cols,char set)
{int i = 0;int j = 0;for (i = 0; i < rows; i++){for (j = 0; j < cols; j++){arr[i][j] = set;}}}void show_board(char arr[ROWS][COLS], int row, int col)
{int i = 0;int j = 0;printf("-----------扫雷--------------\n");for (i = 0; i <= col; i++)//标识符{printf("%d ",i);}printf("\n" );for (i = 1; i <= row; i++){printf("%d ",i );for (j = 1; j <= col; j++){printf("%c ",arr[i][j] );}printf("\n");}printf("-----------扫雷--------------\n");
}//布置雷
void set_mine(char mine[ROWS][COLS], int row, int col)
{int count = EASY_COUNT;int x = 0;int y = 0;while (count != 0){x = rand() % row + 1;//布置雷的坐标范围是1~9,为了出现1而不是0,就必须要最后+1,而不是%(row+1)y = rand() % row + 1;if (mine[x][y] == '0'){mine[x][y] = '1';count--;}}
}int get_mine_count(char mine[ROWS][COLS], int x, int y)
{//'0' - '0' = 0//'1' - '0' = 1//字符相减其实是ASCLL码//这也是为什么使用0 和 1来表示非雷与雷!!!!return (mine[x - 1][y] + mine[x - 1][y - 1] + mine[x - 1][y + 1] + mine[x][y - 1] + mine[x][y + 1] + mine[x + 1][y - 1] + mine[x + 1][y] + mine[x + 1][y + 1]) - 8 * '0';
}void find_mine(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("请输入要排查的坐标:>\n");scanf("%d %d", &x, &y);if (x >= 1 && x <= row && y >= 1 && y <= col){if (mine[x][y] == '1')//是雷,炸死了{printf("很遗憾,被炸死了\n");show_board(show, ROW, COL);break;}else{int count = get_mine_count(mine, x, y);//统计雷的个数show[x][y] = count + '0';//放的是字符,不是数字!.加'0'可以将数字转化成对应的字符show_board(show, ROW, COL);win++;} }else{printf("非法输入,请重新输入:>\n");}}if (win == row * col - EASY_COUNT){printf("恭喜你,排雷成功\n" );show_board(show, ROW, COL);}
}

game.h

//game.h
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<time.h>#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define EASY_COUNT 10//初始化
void init_board(char arr[ROWS][COLS], int rows, int cols,char set);
//打印
void show_board(char arr[ROWS][COLS], int row, int col);
//布置雷
void set_mine(char mine[ROWS][COLS], int row, int col);
//排查雷
void find_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);

手把手教你写个扫雷程序自己玩相关推荐

  1. C++ · 手把手教你写一个扫雷小游戏

    Hello,大家好,我是余同学.这两个月真是太忙了,无暇给大家更新文章- 暑假不是写了个扫雷小游戏吗(Link)?考虑到很多同学对代码没有透彻的理解,那么,这篇文章,我们来详细分析一下代码. 我们分为 ...

  2. 手把手教你写个小程序定时器管理库

    背景 凹凸曼是个小程序开发者,他要在小程序实现秒杀倒计时.于是他不假思索,写了以下代码: Page({init: function () {clearInterval(this.timer)this. ...

  3. qt定时器暂停与重新开始_手把手教你写个小程序定时器管理库

    背景 凹凸曼是个小程序开发者,他要在小程序实现秒杀倒计时.于是他不假思索,写了以下代码: Page({init: function () { clearInterval(this.timer)this ...

  4. java kafkastream_手把手教你写Kafka Streams程序

    一. 设置Maven项目 我们将使用Kafka Streams Maven Archetype来创建Streams项目结构: mvn archetype:generate \ -DarchetypeG ...

  5. 【Golang项目实战】手把手教你写一个备忘录程序|附源码——建议收藏

    博主简介:努力学习的大一在校计算机专业学生,热爱学习和创作.目前在学习和分享:数据结构.Go,Java等相关知识. 博主主页: @是瑶瑶子啦 所属专栏: Go语言核心编程 近期目标:写好专栏的每一篇文 ...

  6. 手把手教你写Kafka Streams程序

    个人名片: 因为云计算成为了监控工程师

  7. php注册程序,[PHP初级]手把手教你写注册程序 1

    [PHP初级]手把手教你写注册程序 1 实例内容 在此教程,我们将通过写一个用户注册程序,学习以下内容: 数据的传输与获取 信息的验证 pdo方式操作数据库 事务处理 前台显示文件:index.php ...

  8. 手把手教你写个微信小程序

    手把手教你写个微信小程序 很多人看完bmob快速入门,并完成了bmob的基本配置之后依然不知道如何下手去写自己的代码,那么跟着我一起来一步一步做个小程序吧. 工具:Bmob后端云 新建小程序项目 一. ...

  9. 程序之家系列教程之手把手教你写熊猫烧香病毒专杀工具

    (作者:chenhui530,论坛 http://chenhui530.com ) 前言       经过去年和熊猫烧香.威金等病毒的"斗争",我也累了,"程序之家病毒专 ...

最新文章

  1. js实现双向链表互联网机顶盒实战应用
  2. Scala代码案例: StdIn和if..else
  3. java 对象转json,java首字母小写,判断方法是否为javabean方法
  4. python学习笔记三 pickle序列化
  5. Angular Component ngOnInit和ngAfterViewInit调用时机的讨论
  6. php 社区,社区(phpmysql)一
  7. WF本质论第一章的代码
  8. CGContextRef:mask和layer绘图
  9. ssh框架的构成分析和代码构架小结 .
  10. 综述|视觉与惯导,视觉与深度学习SLAM
  11. MySQL 有这一篇就够(呕心狂敲37k字,只为博君一点赞!!!)
  12. Java实现杨辉三角形
  13. excel输入身份证号码变指数 及自动变数值如何解决?
  14. css如何把图片设置成梯形,如何用css创建流体梯形图像?
  15. 【Python turtle】使用turtle实现随机满天星星效果(完整代码+效果图)
  16. html 图像处理 灰度图和浮雕图类PS
  17. Linux_centos版初学(基础命令)
  18. Android设备获取mp3中的专辑封面信息
  19. 二次解析源码全kyuan
  20. 我用PhpWebshell抓肉鸡

热门文章

  1. MySQL修改表名注释
  2. 56. Merge Intervals(合并区间)解法(C++ 注释)
  3. SQL 窗口函数速查表
  4. k8s Failed to create pod sandbox错误处理
  5. 浙江省诸暨市诸暨荣怀学校四4班余波我家有一只可爱的小花猫
  6. pc控制iphone的软件_iPhone Share?这里有一款在 PC 端控制 iPhone 的工具
  7. 小程序 数据库 时间_我用两天时间搭了一个领外卖红包的小程序
  8. K3后台修改销售模块(销售订单、发货通知单、销售出库单、销售发票)销售部门名称语句
  9. 互联网+让农业也按耐不住了 村官涉足电商
  10. BUUCTF Reverse/[网鼎杯 2020 青龙组]jocker