初阶扫雷(超详解)

图片来源于网络

✨博客主页:小钱编程成长记
🎈博客专栏:C语言小游戏
🎈推荐相关博文:初阶三子棋(超详解)

初阶扫雷

  • 1.游戏介绍
  • 2.基本思路
  • 3.实现前的准备
  • 4.实现步骤
    • 4.1 打印菜单
    • 4.2 初始化扫雷棋盘
    • 4.3 打印扫雷棋盘
    • 4.4 布置雷
    • 4.5 扫雷
    • 4.6 优化棋盘显示
  • 5.游戏代码
  • 6.总结

1.游戏介绍

《扫雷》是一款大众类的益智小游戏,于1992年发行。游戏目标是在最短的时间内根据点击格子出现的数字找出所有非雷格子,同时避免踩雷,踩到一个雷即全盘皆输。我们今天实现的是最基础的9*9的扫雷。> 扫雷网页版链接
在这里插入图片描述

2.基本思路

  1. 先实现并打印菜单。
  2. 初始化扫雷棋盘(两个,一个用来放置雷和排雷,另一个用来显示雷的数量)。
  3. 打印扫雷棋盘(两个,一个用来放置雷和排雷,另一个用来显示雷的数量)。
  4. 布置雷(1表示雷,0表示不是雷)。
  5. 扫雷并判断输赢:
    判断输入的坐标是否是雷:
    若是,则排雷失败,游戏结束;
    若不是,则统计周围8个坐标有几个雷并显示在当前坐标上,游戏继续;
    若不是雷的坐标都被排查完,则排雷成功,游戏结束。

3.实现前的准备

在本工程中,代码较多,并且有很多自定义函数。我们一般将代码进行拆分,主程序放在test.c源文件中,函数定义放在game.c源文件中,函数声明或宏等放到game.h头文件中。

将代码拆分的好处:

  1. 多人协作
  2. 代码保护

4.实现步骤

4.1 打印菜单

打印菜单和三子棋一样。如果我们想要多次游玩,则菜单要放进循环里,在菜单里选择开始游戏或者退出游戏。菜单中的选择我们通常用switch语句,菜单的循环我们通常用do … while循环。

//test.c
#include <stdio.h>
//菜单
void menu()
{printf("*******************\n");printf("******1.play ******\n");printf("******0.exit ******\n");printf("*******************\n");
}
int main()
{int input = 0;do{menu();printf("请输入>:");scanf("%d", &input);switch (input){case 1:printf("开始游戏\n");game();break;case 0:printf("退出游戏\n");break;default:printf("输入错误,请重新输入\n");break;}} while (input);return 0;
}

在这里插入图片描述

4.2 初始化扫雷棋盘

主程序框架写好了,我们现在开始写游戏具体程序。因为棋盘有很多行和列,我们想到二维数组可以表现多行多列。

注意: 为了可以方便修改棋盘的大小,我们可以用#define定义行和列,并将他们放到game.h头文件,只要在主程序中声明一下gane.h就可以使用头文件中的所有内容。

我们用1表示雷,用0表示不是雷的格。如果数组只设置9行9列,那边上和角上的格子在计算周围8个格子有几个雷时就会越界。在这里插入图片描述
为了防止越界,我们要在周围再加一圈变成11*11
在这里插入图片描述
因为在创建二维数组时全都默认为0,所以这里最外面一圈在初始化时放不放0都行,但一定不能放 1(雷),这里并不属于真正的棋盘,只是为了防越界。

如果我们在这个棋盘中的某一个格子里显示周围8个格中有几个雷。若周围只有一个雷,那这个格子上显示的1就会和是雷的那些格子上的1相混淆,就分不清哪个是雷,哪个是统计的周围的雷的数量。
在这里插入图片描述

  • 因此,我们要再初始化一个棋盘,专门用来存放一个格子周围8个格子中的雷的数量,用来展示给玩家看(那就用9 * 9就行了),先把它全都初始化为 *,我们把这个棋盘称为展示棋盘(show)
  • 存放雷的棋盘是给我们程序员看的,用来让程序排雷的,先把它全初始化为0,我们称它为排雷棋盘(mine)

为了方便和排雷棋盘共用一个初始化函数,将展示棋盘初始化11 * 11也可以,只要打印时打印的是9 * 9就行。
在这里插入图片描述

//game.c
#include <stdio.h>
#define ROW 9
#define COL 9
#define ROWS ROW + 2
#define COLS COL + 2
//初始化
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set);//game.c
#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;}}
}//test.c
#include "game.h"
//游戏
void game()
{char mine[ROWS][COLS] ;//定义char show[ROWS][COLS] ;//初始化排雷棋盘InitBoard(mine, ROWS, COLS, '0');//打印排雷棋盘//初始化展示棋盘InitBoard(show, ROWS, COLS, '*');//打印展示棋盘}

4.3 打印扫雷棋盘

那么多的行和列,玩家在玩时不容易看坐标是哪行哪列,所以我们可以把行和列对应的数字也打印出来。打印时也可以自由发挥一下给棋盘加一些装饰。

//game.h
//打印
void DisplayBoard(char board[ROWS][COLS], int row, int col);//game.c
#include "game.h"
//打印扫雷棋盘
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{int i = 0;int j = 0;printf("******* 扫雷 ******\n");for (j = 0; j <= col; j++){printf("%d ", j);}printf("\n");for (i = 1; i <= row; i++){printf("%d ", i);for (j = 1; j <= col; j++){printf("%c ", board[i][j]);}printf("\n");}printf("******* 扫雷 ******\n");
}//test.c
#include "game.h"
//游戏
void game()
{char mine[ROWS][COLS] ;//定义char show[ROWS][COLS] ;//初始化排雷棋盘InitBoard(mine, ROWS, COLS, '0');//打印排雷棋盘DisplayBoard(mine, ROW, COL);//初始化展示棋盘InitBoard(show, ROWS, COLS, '*');//打印展示棋盘DisplayBoard(show, ROW, COL);
}

在这里插入图片描述

4.4 布置雷

我们制作的是9 * 9版本的扫雷,所以我们希望能在排雷棋盘中随机生成10个雷,就是找到10个随机的坐标,那么就需要产生随机数。用rand可以产生随机数。
让电脑下棋需要先让电脑产生随机的坐标,那我们需要用rand产生随机数。
注意: 只用rand产生的是伪随机数,要想让rand产生真随机数,就需要先用srand为rand产生随机的种子,给srand()的()中输入的是随机数,srand产生的就是随机的种子。时间戳(需要头文件time.h)是一个随着时间的变化而变化的值,给srand()中输入时间戳->srand( (unsigne int)time(NULL) ),得到的就是随机的种子。
rand和srand都需要头文件stdlib.h

//game.h
#include <time.h>
#include <stdlib.h>
//雷的总数
#define EASY_COUNT 10
//布置雷
void SetMine(char board[ROWS][COLS], int rows, int cols);//game.c
#include "game.h"
//布置雷
void SetMine(char board[ROWS][COLS], int row, int col)
{int c = EASY_COUNT;while (c){int a = rand() % row + 1;int b = rand() % col + 1;board[a][b] = '1';c--;}
}//test.c
#include "game.h"
//产生随机的种子,用于rand产生真随机数
srand((unsigned int)time(NULL));
//布置雷
SetMine(mine, ROW, COL);
//打印排雷棋盘
DisplayBoard(mine, ROW, COL);

在这里插入图片描述

4.5 扫雷

  • 玩家输入的坐标一定要在我们设置的二维数组的范围内,如果不在则重新输入;

  • 玩家输入的坐标一定要是没有排查过的,若是排查过的,则需重新输入;

  • 如果输入的坐标在排雷棋盘中是1,则排雷失败,本局游戏结束;

  • 如果排查雷的次数正好等于不是雷的格子的数量,则排雷成功,本局游戏结束;

  • 如果输入一个坐标不是雷(游戏继续,要再打印一次展示棋盘为下一次排雷做准备),我们要计算这个坐标周围8个格中的雷的数量,
    -> 1. 只需要将周围8个格中的数据加在一起就行。因为雷为1,不是雷为0,这也是我们 之前这样初始化的好处。注意: 字符本身不参与计算,参与计算的是字符对应的ASCII码值,如:‘1’-‘0’=1(整型) 3+‘0’=‘3’
    -> 2. 还有一种通用方法,就是一个个的判断周围坐标是不是雷,如果是则用计数器(count)加1。周围坐标如下:
    在这里插入图片描述

//game.h
//扫雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int rows, int cols);//game.c
#include "game.h"
//通用方法:计算坐标周围8个格中雷的数量
//static int GetMineCount(char mine[ROWS][COLS], int x, int y)
//{
//	int count = 0;
//	if (mine[x - 1][y-1] == '1')
//		count++;
//	if (mine[x - 1][y] == '1')
//		count++;
//	if (mine[x - 1][y + 1] == '1')
//		count++;
//	if (mine[x][y - 1] == '1')
//		count++;
//	if (mine[x][y + 1] == '1')
//		count++;
//	if (mine[x + 1][y - 1] == '1')
//		count++;
//	if (mine[x + 1][y] == '1')
//		count++;
//	if (mine[x + 1][y + 1] == '1')
//		count++;
//	return count;
//}//计算坐标周围8个格中雷的数量
static int GetMineCount(char mine[ROWS][COLS], int x, int y)//把函数放到静态库,使函数失去外部连接属性,只能在本源文件中使用。
{return mine[x - 1][y - 1] + mine[x - 1][y] + 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 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");break;}else if (show[x][y] != '*'){printf("该坐标已被排查了,请重新输入\n");}else{   //不是雷,就统计x,y有多少雷?int c = GetMineCount(mine, x, y);show[x][y] = c + '0';DisplayBoard(show, ROW, COL);win++;}}elseprintf("坐标非法,请重新输入\n");}if (win == row * col - EASY_COUNT){printf("排雷成功\n");}
}//test.c
#include "game.h"
//扫雷
FindMine(mine, show, ROW, COL);

4.6 优化棋盘显示

玩家每次排雷时,上一次的棋盘并未消失。这会使打印的棋盘越来越多,不美观。
我们可以在每次排雷(输入坐标)后都清空一次屏幕,这样屏幕就只会显示一个棋盘,更加美观。
使用system(“cls”)可以清空屏幕,需要头文件stdlib.h
在这里插入图片描述
在这里插入图片描述

5.游戏代码

game.h

#define _CRT_SECURE_NO_WARNINGS#include <stdio.h>
#include <time.h>
#include <stdlib.h>#define ROW 9
#define COL 9
#define ROWS ROW + 2
#define COLS COL + 2
#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 rows, int cols);

game.c

#define _CRT_SECURE_NO_WARNINGS
#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;int j = 0;printf("******* 扫雷 ******\n");for (j = 0; j <= col; j++){printf("%d ", j);}printf("\n");for (i = 1; i <= row; i++){printf("%d ", i);for (j = 1; j <= col; j++){printf("%c ", board[i][j]);}printf("\n");}printf("******* 扫雷 ******\n");
}//布置雷
void SetMine(char board[ROWS][COLS], int row, int col)
{int c = EASY_COUNT;while (c){int a = rand() % row + 1;int b = rand() % col + 1;board[a][b] = '1';c--;}
}//通用方法:计算坐标周围8个格中雷的数量
//static int GetMineCount(char mine[ROWS][COLS], int x, int y)
//{
//	int count = 0;
//	if (mine[x - 1][y-1] == '1')
//		count++;
//	if (mine[x - 1][y] == '1')
//		count++;
//	if (mine[x - 1][y + 1] == '1')
//		count++;
//	if (mine[x][y - 1] == '1')
//		count++;
//	if (mine[x][y + 1] == '1')
//		count++;
//	if (mine[x + 1][y - 1] == '1')
//		count++;
//	if (mine[x + 1][y] == '1')
//		count++;
//	if (mine[x + 1][y + 1] == '1')
//		count++;
//	return count;
//}//计算坐标周围8个格中雷的数量
static int GetMineCount(char mine[ROWS][COLS], int x, int y)//把函数放到静态库,使函数失去外部连接属性,只能在本源文件中使用。
{return mine[x - 1][y - 1] + mine[x - 1][y] + 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 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");break;}else if (show[x][y] != '*'){printf("该坐标已被排查了,请重新输入\n");}else{   //不是雷,就统计x,y有多少雷?int c = GetMineCount(mine, x, y);show[x][y] = c + '0';DisplayBoard(show, ROW, COL);win++;}}elseprintf("坐标非法,请重新输入\n");}if (win == row * col - EASY_COUNT){printf("排雷成功\n");}
}

排雷棋盘打印出来是给我们程序员看的,方便我们调试,给玩家玩时可以注释掉。
随机数的种子不需要每局都获取,所以我们可以把它放进main函数里。
test.c

#define _CRT_SECURE_NO_WARNINGS
#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] ;//初始化排雷棋盘InitBoard(mine, ROWS, COLS, '0');//打印排雷棋盘//DisplayBoard(mine, ROW, COL);//初始化展示棋盘InitBoard(show, ROWS, COLS, '*');//打印展示棋盘DisplayBoard(show, ROW, COL);//布置雷SetMine(mine, ROW, COL);//打印排雷棋盘//DisplayBoard(mine, ROW, COL);//扫雷FindMine(mine, show, ROW, COL);}
int main()
{int input = 0;srand((unsigned int)time(NULL));//产生随机的种子,用于rand产生真随机数do{menu();printf("请输入>:");scanf("%d", &input);switch (input){case 1:printf("开始游戏\n");game();break;case 0:printf("退出游戏\n");break;default:printf("输入错误,请重新输入\n");break;}} while (input);return 0;
}

6.总结

好啦,这就是初阶扫雷的全部内容了,大家可以跟着操作起来,一起进步。由于我目前能力有限,写的扫雷代码还是有很大的优化空间,比如不能自动将周围没有雷的格子跳过,不能标记雷等。大家有什么问题也可以在评论区多多交流,感谢大家的阅读!

点赞收藏加关注,C语言学习不迷路!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/106085.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

jvm 程序计算器 程序计数器是否溢出 程序计数器是做什么的 java程序计数器会内存溢出吗 程序计数器作用与用处 jvm内存模型 jvm合集(一)

1. jvm内存模型&#xff1a; 内存模型&#xff1a; 程序计数器 堆 栈 本地方法栈 方法区 2. java代码编译为class文件&#xff0c;由类加载器加载到jvm&#xff0c;然后由解释器,jit即时编译到机器码&#xff0c;机器码再到cpu执行 3. 程序计数器&#xff1a; 是一块较小的内存…

解决Ubuntu无法安装pycairo和PyGObject

环境&#xff1a;虚拟机Ubuntu20.04&#xff0c;vscode无法安装pycairo和PyGObject 虚拟机Ubuntu20.04&#xff0c;vscode中运行Anaconda搭建的vens 的Python3.8.10 首先在vscode中点击ctrlshiftp&#xff0c;选择Python3.8.10的环境&#xff0c;自动激活Python 最近在搞无人…

使用本地mysql+linux实现mysql主从同步

1.配置linux 保证linux已经安装好了mysql1.1修改该linux配置文件 vim /etc/my.cnf1.2重启linux的mysql systemctl restart mysqld1.3使用账户密码登录linux中的mysql,查看是否配置成功 mysql> show master status;若显示有FIile和Posttion就表示注linux的主节点配置成功…

安装wps后,回收站右键菜单出现“恢复误删文件”,通过注册表的方式去掉。

免费的才是最贵的。垃圾流氓软件。 这个东西点开会给你下载一个叫金山数据恢复大师的看起来不知道多少年的老古董。 win R 输入regedit打开注册表按照路径寻找&#xff1a;HKEY_CLASSES_ROOT\CLSID{645FF040-5081-101B-9F08-00AA002F954E}\shellex\ContextMenuHandlers 大功…

贝wa儿歌(安卓)

本次贝wa儿歌为大家提供的是特殊版本的&#xff0c;软件需要注册登录才能才能使用VIP权限&#xff0c;安装好后&#xff0c;打开如果提示更新请点击取消&#xff0c;一定不要更新哦。 贝瓦儿歌不仅只是儿歌&#xff0c;其实有很多分类的&#xff0c;包括有国学&#xff0c;动画…

时序分解 | MATLAB实现基于EWT经验小波变换的信号分解分量可视化

时序分解 | MATLAB实现基于EWT经验小波变换的信号分解分量可视化 目录 时序分解 | MATLAB实现基于EWT经验小波变换的信号分解分量可视化效果一览基本介绍程序设计参考资料 效果一览 基本介绍 EWT经验小波变换 包含频谱相关系数 可直接运行 Matlab代码 1.可自由设置分量个数&…

【软考】系统架构设计师 - 知识扩展 - “区块链技术“

目录 一 简介&#x1f451; 1 比特币❤️ 2 区块链的特点❤️ 3 共识算法❤️ 二 练习题&#x1f451; 三 扩展&#x1f451; 1 哈希算法❤️ 2 哈希指针❤️ 3 UTXO❤️ 4 参考资料❤️ 一 简介&#x1f451; 1 比特币❤️ 比特币底层采用了区块链技术。 比特币交易…

vue3:22、vue-router的使用

import { createRouter, createWebHistory } from vue-router//history模式&#xff1a;createWebHistory //hash模式&#xff1a;createWebHashHistory//vite中的环境变量 import.meta.env.BASE_URL 就是vite.config.js中的base配置项 const router createRouter({history:…

行业报告 | 智慧三角:长三角掀起AI产业热潮

原创 | 文 BFT机器人 产业集群是指在特定地理区域内&#xff0c;一群相关产业相互依存、相互关联、相互支持&#xff0c;形成密集的产业网络和价值链条的现象&#xff0c;这些相关产业可能涵盖整个产业链的不同环节&#xff0c;从原材料供应到产品研发、生产、销售和服务等多个…

使用LambdaQueryWrapper再也不担心字段拼写错误了 [MyBatis-Plus系列] - 第485篇

历史文章&#xff08;文章累计480&#xff09; 《国内最全的Spring Boot系列之一》 《国内最全的Spring Boot系列之二》 《国内最全的Spring Boot系列之三》 《国内最全的Spring Boot系列之四》 《国内最全的Spring Boot系列之五》 《国内最全的Spring Boot系列之六》 E…

释放数据的潜力:用梯度上升法解锁主成分分析(PCA)的神奇

文章目录 &#x1f340;引言&#x1f340;什么是主成分分析&#xff08;PCA&#xff09;&#xff1f;&#x1f340;传统PCA vs 梯度上升PCA&#x1f340;PCA的优化目标&#x1f340;代码实现&#x1f340;求解第一主成分&#x1f340;求解第二主成分 &#x1f340;在sklearn中封…

分类算法系列⑤:决策树

目录 1、认识决策树 2、决策树的概念 3、决策树分类原理 基本原理 数学公式 4、信息熵的作用 5、决策树的划分依据之一&#xff1a;信息增益 5.1、定义与公式 5.2、⭐手动计算案例 5.3、log值逼近 6、决策树的三种算法实现 7、API 8、⭐两个代码案例 8.1、决策树…