【从零开始的C语言】初阶 数组应用之 扫雷
文章目录
- 前言
- 一、扫雷的逻辑。
- 二、文件说明
- 1.game.h
- 2.test.c
- 3.game.c
- 初始化雷区
- 打印雷区
- 电脑随机布置雷
- 递归展开雷区
- 总结
前言
前面刚做好了数组的实战练习之三子棋,学习完二维数组的初步内容之后,还可以再做一个控制台小程序就是扫雷。
一、扫雷的逻辑。
扫雷大部分人应该都玩过,但win10之后的版本好像都不自带扫雷啦,还是说一下扫雷的逻辑吧。
对于9*9的雷区,我们布置10个雷,雷的位置由电脑随机确定。我们玩家需要去做的,就是输入坐标排查出所有雷的位置。过程中如果恰好输入了雷的坐标,提示我们被炸死了。当10个雷全被排查完后,提示我们排雷成功了。
二、文件说明
1.game.h
包含函数定义与符号常量的定义。
#pragma once#include <stdio.h>
#include <stdlib.h>
#include <time.h>#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define EDDITION 10//初始化雷盘
//初始化时,将mine雷盘初始化为字符 '0' ,将show雷盘初始化为字符 '*'
void Initboard(char board[ROWS][COLS], int rows, int cols, char set);//打印雷盘
void Printboard(char board[ROWS][COLS], int rows, int cols);//电脑随机生成雷并将其放到雷盘mine数组里,将雷的位置设为字符 '1',是有讲究的
void Setmine(char board[ROWS][COLS], int rows, int cols);//玩家扫雷
void Findmine(char board1[ROWS][COLS], char board2[ROWS][COLS], int rows, int cols);
2.test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"void game()
{char mine[ROWS][COLS] = { 0 };char show[ROWS][COLS] = { 0 };Initboard(mine, ROWS, COLS, '0');Initboard(show, ROWS, COLS, '*');Setmine(mine, ROW, COL);Printboard(show, ROW, COL);Findmine(mine, show, ROW, COL);
}void menu()
{printf("************************\n");printf("*******1. to play*******\n");printf("*******1. to quit*******\n");printf("************************\n");}void test()
{srand((unsigned int)time(NULL));menu();int input = 0;do {printf("请输入:>(1 or 0)");scanf("%d", &input);switch (input){case 1:printf("游戏开始!\n");game();break;case 0:printf("游戏结束!\n");break;default:printf("输入错误,请重新输入!\n");break;}} while (input);
}int main()
{test();return 0;
}
和三子棋一样,通过函数的嵌套调用来实现完扫雷游戏的过程。值得一提的是,我们在定义99数组时并不是真正定义99的数组,二十11*11,这是为了在后面排雷的时候防止数组越界而做的准备。
3.game.c
这个文件才是扫雷程序的主要部分,包含所有函数定义以及逻辑。
先看整体,后面将每一个函数拆出来一个一个分析。
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"//初始化雷盘
//为了方便,将整个游戏的二维数组雷盘分成两部分
//一个雷盘叫mine,用来布置雷,该雷盘不向玩家展示
//一个雷盘叫show,用来向玩家展示排雷的信息,即雷的分布情况
void Initboard(char board[ROWS][COLS], int rows, int cols, char set)
{for (int i = 0; i < rows; i++){for (int j = 0; j < cols; j++){board[i][j] = set;}}
}//打印雷盘
void Printboard(char board[ROWS][COLS], int rows, int cols)
{printf("---------------------\n"); //为了好看for (int i = 0; i <=cols ; i++){printf(" %d", i); }printf("\n");for (int i = 1; i <= rows; i++){printf(" %d|", i);for (int j = 1; j <= cols; j++){printf("%c ", board[i][j]);}printf("\n");}printf("---------------------\n"); //为了好看
}//让电脑随机生成雷并把雷放到雷区里(就是放到mine数组里)
void Setmine(char board[ROWS][COLS], int rows, int cols)
{int count = EDDITION;while (count){int x = rand() % rows+1;int y = rand() % cols+1;if (board[x][y] == '0'){board[x][y] = '1';count--;}}
}//低配版,当排查的位置周围没有雷的时候不能展开周围的空间
//int get_mine_count(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');
//}//高配版
//用递归实现展开一片的操作
// mine show
void Set_count_got(char board1[ROWS][COLS], char board2[ROWS][COLS], int x, int y)
{int num = (board1[x - 1][y] + board1[x - 1][y - 1] + board1[x][y - 1] +board1[x + 1][y - 1] + board1[x + 1][y] + board1[x + 1][y + 1] +board1[x][y + 1] + board1[x - 1][y + 1] - 8 * '0'); //将雷的位置设置成字符'1',不是雷的位置设置为字符'0'的好处就体现出来了if (x >= 1 && x <= ROW && y >= 1 && y <= COL && board2[x][y] == '*') //递归时候的坐标也要合法且该坐标没被排查过{if (num == 0) //周围一圈没有雷,则递归展开{board2[x][y] = ' '; //将该位置置为空格字符 ' 'for (int i = -1; i <= 1; i++) //捣鼓了半天想到-1 0 1的关系,才反应过来可以用循环代替一个个输坐标了,真的是上了大学脑子就不够用啦{ //board2在x,y位置处的周围八个坐标再检测一次for (int j = -1; j <= 1; j++){Set_count_got(board1, board2, x + i, y + j);}} }else{board2[x][y] = num + '0';}}
}//判断输赢 遍历数组就完事了 Win(char board2[ROWS][COLS], int rows, int cols)
int Win(char board2[ROWS][COLS], int row, int col)
{int num = 0;for (int i = 1; i <= row; i++){for (int j = 1; j <= col; j++)//排查地图上所有的坐标{if (board2[i][j] == '*')//当地图上只剩下十个未知的坐标{ //而玩家没有被炸死,证明扫雷成功了num++;}}}return num;
}//玩家扫雷
void Findmine(char board1[ROWS][COLS], char board2[ROWS][COLS], int rows, int cols)
{int x = 0;int y = 0;int count = rows * cols; //count要设置的足够大while(count > EDDITION){printf("请输入要排查的坐标:>");scanf("%d %d", &x, &y);if (x >= 1 && x <= rows && y >= 1 && y <= cols){if (board2[x][y] != '*'){printf("该坐标已被排查过,请重新输入!\n");continue;}if (board1[x][y] == '1'){printf("很遗憾,你被炸死了!\n");printf("雷区的布置是:\n");Printboard(board1, rows, cols); break;}else{Set_count_got(board1, board2, x, y);Printboard(board2, rows, cols);count = Win(board2, rows, cols);}}else{printf("坐标非法!请重新输入!\n");}}if (count == EDDITION){printf("排雷成功!\n");}
}
初始化雷区
void Initboard(char board[ROWS][COLS], int rows, int cols, char set)
{for (int i = 0; i < rows; i++){for (int j = 0; j < cols; j++){board[i][j] = set;}}
}
遍历二维数组并将字符放进去即可。
打印雷区
void Printboard(char board[ROWS][COLS], int rows, int cols)
{printf("---------------------\n"); //为了好看for (int i = 0; i <=cols ; i++){printf(" %d", i); }printf("\n");for (int i = 1; i <= rows; i++){printf(" %d|", i);for (int j = 1; j <= cols; j++){printf("%c ", board[i][j]);}printf("\n");}printf("---------------------\n"); //为了好看
}
电脑随机布置雷
void Setmine(char board[ROWS][COLS], int rows, int cols)
{int count = EDDITION;while (count){int x = rand() % rows+1;int y = rand() % cols+1;if (board[x][y] == '0'){board[x][y] = '1';count--;}}
}
和三子棋当中电脑下期一样,调用rand函数随机生成坐标。
递归展开雷区
void Set_count_got(char board1[ROWS][COLS], char board2[ROWS][COLS], int x, int y)
{int num = (board1[x - 1][y] + board1[x - 1][y - 1] + board1[x][y - 1] +board1[x + 1][y - 1] + board1[x + 1][y] + board1[x + 1][y + 1] +board1[x][y + 1] + board1[x - 1][y + 1] - 8 * '0'); //将雷的位置设置成字符'1',不是雷的位置设置为字符'0'的好处就体现出来了if (x >= 1 && x <= ROW && y >= 1 && y <= COL && board2[x][y] == '*') //递归时候的坐标也要合法且该坐标没被排查过{if (num == 0) //周围一圈没有雷,则递归展开{board2[x][y] = ' '; //将该位置置为空格字符 ' 'for (int i = -1; i <= 1; i++) //捣鼓了半天想到-1 0 1的关系,才反应过来可以用循环代替一个个输坐标了,真的是上了大学脑子就不够用啦{ //board2在x,y位置处的周围八个坐标再检测一次for (int j = -1; j <= 1; j++){Set_count_got(board1, board2, x + i, y + j);}} }else{board2[x][y] = num + '0';}}
}
这里体现出了将雷的位置设置为’1’的好处了,将数字字符减去字符0,则得到了该数字字符对应的数字。
这里用num接收,如果num为零,说明周围一圈没有雷,则将该位置置为空格字符,并且将周围一圈的坐标再次展开,直到递归的坐标周围有雷的时候停止递归并将周围雷的个数转化成对应的字符放入其中即可。
这里想了大半天想到可以用循环来取周围一圈的坐标,这样就可以不用一个个给它们写出来了。
真的是上了大学智商就退化了hh。
之后的玩家扫雷和判断输赢逻辑就很简单了,因为作者是大二大学牲,逃了很多课才有时间写博客,乐跑都快结束了我还差一大半,为了防止挂科只能抽出这么点时间写博客了,望大佬们见谅见谅!
总结
扫雷这个控制台小程序还是很简单的,可能递归的时候需要注意很多比如坐标不能是雷,周围不能有雷,且该坐标没被排查过,满足上述的条件才可以用递归展开一片。其他的并没有很复杂。
有很多不足的地方,望大佬们尽情指出,虚心接受别人的意见和听取别人的思维才是一个编程学习者应当具备的品质。
最后放张运行截图:
【从零开始的C语言】初阶 数组应用之 扫雷相关推荐
- 从零开始学C语言 : 初阶指针
目录: 1. 指针是什么 2. 指针和指针类型 3. 野指针 4. 指针运算 5. 指针和数组 6. 二级指针 7. 指针数组 1.指针是什么: 1. 指针是内存中一个最小单元的编号,也就 ...
- 【C语言初阶】——简易版·扫雷(9*9)【运行逻辑思维导图+细节讲解+源码】【初级】
目录 一.扫雷游戏的运行逻辑 二.代码逻辑讲解+源码 1.打印一个简易的游戏开始菜单 2.创建数组储存数据并初始化数组 代码逻辑讲解 源码 3.布置雷 代码逻辑讲解 源码 4.排雷 代码逻辑 源码 三 ...
- C语言初阶(18) | 数组详解
目录 1.数组的定义 2.一维数组 2.1一维数组的格式 2.2一维数组的初始化 2.3一维数组的使用 2.4一维数组再内存中的存储 3.二维数组 3.1二维数组的创建和初始化 3.2二维数组的使用 ...
- C语言初阶作业题-数组
C语言初阶作业题-数组 一.选择题 1.关于一维数组初始化,下面哪个定义是错误的?( ) A.int arr[10] = {1,2,3,4,5,6}; B.int arr[] = {1,2,3,4,5 ...
- C语言初阶_初识C语言(1)
凡是过往,皆为序章 期末考试结束了,真是感慨良多啊...... C语言编程题大家都做对了多少呢? 不管怎么样,都已经过去了.不管你学得怎么样,都可以在接下来的时间里,我们一起重新回顾一遍C语言初阶的知 ...
- C语言初阶——5.字符串
C语言初阶--5.字符串 1. 字符串操作 1.1 字符串遍历 可以通过数组方式遍历字符串. char* str="Hello World"; for(int i = 0;'\0' ...
- 五十分钟带你看遍C语言初阶语法(总纲)
C语言初阶语法(总纲) 此篇博客包含了前面C语言所有的基础语法,写这一期的博客不仅是为了记录自己的学习成果也是为了让更多的朋友能学到对自己有用的知识,如果总有一个知识点帮助到了你,给卑微博主点点关注加 ...
- C语言初阶——6.进制
C语言初阶--6.进制 1. 进制 百度百科: 进制也就是进位计数制,是人为定义的带进位的计数方法,对于任何一种进制-X进制,就表示每一位置上的数运算时都是逢X进一位. 十进制是逢十进一,十六进制是逢 ...
- C语言初阶总结 v1
目录 C 基础1⭐ 数据类型 计算机中的单位 常量与变量 字符串/转意字符/ 注释 C 基础2⭐ 判断语句 函数与数组 C语言操作符 C语言关键字 C语言定义宏与关键字 初始指针 ⭐⭐ 指针基础 拓展 ...
最新文章
- 初入股市之 Hello Stock
- python整除表达 mod_[零基础学python]啰嗦的除法
- python树莓派设备_Python+树莓派制作IoT(物联网)门控设备
- 经典网页设计:18个示例展示图片在网页中的使用
- 米农分享:浅谈好域名应具备的10大特点
- OpenCV——人脸检测
- composer安装thinkphp
- Java实例化后自动执行_Java的实例化顺序(程序执行顺序)
- acl的access-list命令使用详解
- 前端核心技术必备知识库精华资源强力推荐
- 钓鱼网站 (搬运自common craft )
- 网管员应该掌握好的学习方法
- C# 使用SHFileOperation 复制文件 注意引用方式
- 钉钉 消息防撤回 分析
- 无根树的Prufer序列
- 《孤尽班T31-04-工程结构规约》
- java web 注册登录_javaweb实现登录注册功能实例
- VLC_本地播放器(C#)
- linux 7.0 域名,RHEL 7.0已发布 CentOS 7 即将到来
- 代正通_ChromeBook怎么刷windows系统||Linux教程
热门文章
- .html .val .text 的区别
- unbuntu 18.04 配置静态IP
- box-shadow用法总结
- Python量化交易平台开发教程系列3-vn.py项目中API封装的编译
- everything搜到不到文件的设置问题
- 招商银行为什么选用MySQL
- 如何写综述论文全攻略
- AttributeError: type object ‘h5py.h5.H5PYConfig‘ has no attribute ‘__reduce_cython__‘的解决方案
- 如何调用API接口在线生成在微信可用的网址二维码
- 03-盒子模型与元素显示类型