START
我们平时玩的扫雷小游戏,可以通过C语言简易实现,当然,设计这个小游戏,需要具备严谨的逻辑,一步接一步的思维引领。
文章目录
思考扫雷步骤(思路)
多文件进行处理
设置菜单
在main函数中布置游戏实现框架
对游戏主体进行编写(game.c) + 声明自定义函数(game.h)
1. 思考扫雷步骤(思路)
首先,扫雷这个小程序,我们分析游戏规则得知:当我们排查雷的时候,如果所排查位置是雷的话,那我们就被炸死了;如果所排查处不是雷,为了降低游戏难度,我们可以显示所排查之处周围有多少个雷。
2. 多文件进行处理
由于扫雷代码偏长,为了使程序员编写条理井然有序,并可以多人进行对一个项目的编写,后续维修、检查Bug方便,因此分装成多个文件处理:main.c (扫雷小程序的主体框架)、game.c(扫雷游戏功能的实现)、game.h(扫雷游戏功能中自定义函数的声明)。
3. 设置菜单
扫雷小程序面向用户,美观且通俗易懂的菜单当然是必不可少的,由于菜单并不是很复杂,我们将菜单(menu)函数放在主函数中也是可以的。
void menu(){printf(" +---+---+---+---+---+---+---+---+---+\n");printf(" | <欢迎使用扫雷游戏> |\n");printf(" +---+---+---+---+---+---+---+---+---+\n");printf(" * * * * * * * * * * * * * * * * * * *\n");printf(" +---+---+---+---+---+---+---+---+---+\n");printf(" | 请选择你要执行的操作>> |\n");printf(" +---+---+---+---+---+---+---+---+---+\n");printf(" | 【1】开始游戏 |\n");printf(" +---+---+---+---+---+---+---+---+---+\n");printf(" | 【0】退出游戏 |\n");printf(" +---+---+---+---+---+---+---+---+---+\n"); }
通过这个菜单,我们将“1"设置为 开始游戏,并将”0“作为退出游戏。
4. 在main函数中布置游戏实现框架
Procedure_1 ——与菜单功能相结合
int main(){int input = 0;srand((unsigned int)time(NULL));do{menu();printf("请选择 ->");scanf("%d", &input);printf("\n");switch (input) {case 1:game();break;case 0:printf("退出游戏");printf("\n");break;default:printf("选择错误,请重新选择");printf("\n");break;}return 0;} while (input);}
当我们选择”1“的时候,进入游戏主体,若当我们选择”0“的时候,将退出游戏,如果选择其他数时,将提示”选择错误,请重新选择。通过while循环,将我们输入的数字进行反馈。
Procedure_2 ——主函数中调用game函数
1 . 定义两个棋盘
首先,我们想,在一个扫雷的棋盘中,棋盘里的数据用什么存储是“雷”和“非雷”,很容易会想到:“1"表示是”雷“,”0“表示"非雷”,但是问题来了,如果我们想呈现所排查的坐标周围有多少个雷的时候,数字逻辑就相互矛盾了,但是我们可以创建两个数组分开进行操作。
#define ROW 9#define COL 9
由于我们在排查时,发现当排查坐标在边缘角落时,有一些数据越界了,因此,我们将棋盘加个“边框”就能很好地解决这个问题。
#define ROWS ROW+2#define COLS COL+2
通过宏定义ROW,COL方便后续进行修改
char mine[ROWS][COLS];char show[ROWS][COLS];
定义mine数组为布置雷的数组,show数组为展示排查的数组
2 .初始化两个棋盘
自定义Initboard为初始化函数,将mine和show两个数组中数据分别初始化为“0”、“*”。主函数调用Initboard函数时,传数组时,记得传的是数组名(即首元素地址)。
Initboard(mine, ROWS, COLS, '0');Initboard(show, ROWS, COLS, '*');
3 . 打印棋盘
自定义Displayboard函数进行数组的打印,同时,主函数调用Displayboard函数时,传数组时,记得传的是数组名(即首元素地址)。
Displayboard(show, ROW, COL);
4 . 布置雷的位置
自定义Setmine函数进行对mine数组布置雷的坐标,当然,主函数调用Setmine函数时,传数组时,记得传的是数组名(即首元素地址)。
Setmine(mine, ROW, COL);
5 . 排查雷的位置
自定义Findmine函数,将从mine数组中排查中的数据通过show数组展现出来,因此,在调用Findmine函数时,应该传入mine和show这两个数组。
Findmine(mine, show, ROW, COL);
5. 对游戏主体进行编写(game.c) + 声明自定义函数(game.h)
Procedure_1 ——游戏框架(game.c)
< 接下来,在game.c中我们将我们上述的自定义函数逐个实现 >
1 . 初始化数组函数——Initboard
在Initboard函数中,形参有一个字符数组用于接收初始化的数组,行(rows)、(cols)以及想要初始化成的数据,即通过for循环嵌套for循环>>
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;}}}
2 . 呈现棋盘的函数——Displayboard
在Displayboard函数中,形参传入一个字符数组,所展示的棋盘不需要包括边框,因此形参传的是row和col。为了外观更美观,我们也可以通过for循环将行、列号打印出来>>
void Displayboard(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 <= row; j++){printf("%c ", board[i][j]);}printf("\n");}}
3 . 布置雷的函数——Setmine
同样,布置雷和展示雷一样,都不需要作用到边框,因此也是传row和col参数,现在,我们要布置雷的个数为NUMBER_MINE ,但是我们确保每次布置雷的位置都是随机的,这里我们需要使用随机种子数(rand),使用随机种子数,我们需要在game.h文件中定义#include<stdlib.h>、#include<time.h>这两个头文件,并且在main.c主体中添加srand((unsigned int)time(NULL));为了确保我们布置的雷满足我们设定的量,当我们布置一个雷的时候,可以做一个标记,即:count--
下面是代码展示>>
#include<stdlib.h>#include<time.h>
srand((unsigned int)time(NULL));
void Setmine(char board[ROWS][COLS], int row, int col) {int count = NUMBER_MINE;while (count){int x = rand() % row + 1;int y = rand() % col + 1;if (board[x][y] == '0'){board[x][y] = '1';count--;}}}
Tips: int x = rand() % row + 1; 即x的范围属于1~row
4 . 排查雷的函数——Findmine
首先,调用mine数组,排查所输入的坐标是否是雷;(1)当踩中雷的时候,游戏结束,并展示雷的坐标(Displayboard(mine, ROW, COL); (2) 当没有踩中时,我们可以再自定义一个Getminecount函数将该点周围8个坐标的雷统计出来并打印出来;(3)为确保游戏严谨性,我们还可以将输入不合法的形式进行提示-重新输入。(4)为了游戏顺利结束,我们可以将循环条件设置成输入次数小于row*col-布置的雷数>>
void Findmine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col) {int x = 0;int y = 0;int number = 0;while (number < row * col - NUMBER_MINE){printf("请输入你要排查的坐标:例如:3 6\n");printf("\n");scanf("%d %d", &x, &y);//排查雷的时候,有合理与不合理之分//合理>if (x >=1 && x <= row && y >=1 && y <= col){//当你踩中雷时if (mine[x][y] == '1'){printf("\a");printf("很遗憾,你被炸死了\n");Displayboard(mine, ROW, COL);break;}//当你踩的不是雷时else{int ret = Getminecount(mine, x, y);show[x][y] = ret + '0';Displayboard(show, ROW, COL);printf("加油,请继续排查!\n");printf("\n");printf("\a");number++;}}//当输入x,y不满足时,进行重新输入else {printf("输入非法,请重新输入\n");printf("\a");}}if (number == row * col - NUMBER_MINE) {printf("恭喜你,排雷成功!\n");printf("\a");Displayboard(mine, ROW, COL);}}
5 . 统计周围8个坐标的自定义函数——Getminecoun
该函数设置有返回值,由于“0”的Ascall码是48,“2”的Ascall码是50.因此,两字符相减,所得之值即数字2 ,将周围8个数的Ascall码相减,所得之值便是所求值。
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');}
Procedure_2 ——函数头文件(game.h)
将game.c中的函数声明,并包含一些头文件
#pragma once#include<stdio.h>#include<stdlib.h>#include<time.h>//通过#define 定义行数,列数,方便后续快速修改#define ROW 9#define COL 9#define ROWS ROW+2#define COLS COL+2//定义雷的个数#define NUMBER_MINE 10//将数组进行初始化void Initboard(char board[ROWS][COLS], int rows, int cols, int set);//打印展示需要呈现的数组void Displayboard(char board[ROWS][COLS],int row, int col);//通过随机种子数随机布置雷void Setmine(char board[ROWS][COLS], int row, int col);//在mine数组中检测是否是雷,并在show数组中显示详情void Findmine(char mine[ROWS][COLS],char show[ROWS][COLS], int row, int col);//计算周围雷的个数int Getminecount(char mine[ROWS][COLS], int x, int y);