大家好,欢迎来到IT知识分享网。
前言
今天我们来简单了解一下一款经典的游戏–扫雷!文末附上完整代码。
扫雷作为微软开发并发行的一款大众类的益智小游戏,于1992年发行。玩家的目标是在最短时间内识别出所有非雷区的格子,同时避免触碰到地雷。游戏区域由多个隐藏格子组成,每个格子可能隐藏着地雷或数字,数字表示周围八个格子中地雷的数量。玩家通过点击格子揭示内容,使用逻辑推理逐步排除雷区。游戏考验玩家的观察力和推理能力,任何一次误点地雷都会导致游戏失败。
扫雷分为初级,中级,高级等难度,玩家也可以自行定义雷的数量与方格的大小。免费在线扫雷 (minesweeper.cn)这是网页版的扫雷游戏,大家感兴趣可以点击进入。
游戏分析与设计:
1.数据结构分析
布置的雷和排查出的雷的信息都需要存储,所以我们需要⼀定的数据结构来存储这些信息。初阶的扫雷(如上图)大小是9*9, 因为我们需要在9*9的棋盘上布置雷的信息和排查雷,我们⾸先想到的就是创建⼀个9*9的数组来存放信息。
• 使⽤控制台实现经典的扫雷游戏
• 游戏可以通过菜单实现继续玩或者退出游戏
• 扫雷的棋盘是9*9的格⼦ • 默认随机布置10个雷
• 可以排查雷 ◦ 如果位置不是雷,就显⽰周围有⼏个雷
◦ 如果位置是雷,就炸死游戏结束
◦ 把除10个雷之外的所有⾮雷都找出来,排雷成功,游戏结束
2.⽂件结构设计
为了便于代码的创建,我们分成三个文件来实现。
代码实现
1.菜单的创建
void menu() { printf("*\n"); printf("* 1.play \n"); printf("* 0.exit \n"); printf("*\n"); } int main() { srand( (unsigned int) time ( NULL ) ); int input = 0 ; do { menu(); printf("请选择:"); scanf("%d", &input); switch (input) { case 1: printf("\n"); game(); break; case 0: printf("\n"); printf("退出\n"); break; default: printf("\n"); printf("输入有误,请重新输入\n"); break; } } while (input); return 0; }
细节分析:
创建menu函数来简单的放置我们的菜单栏,并添加选项1,0。1是开始游戏,而0是退出游戏! 使用do while循环,这样我们就能至少进行一次菜单栏操作,输入选项进入switch函数,为了让我们玩过一次后还能玩,当input等于0时,我们才结束循环退出!
2.初始化数组
game.h
#define ROW 9 #define COL 9 #define ROWS ROW+2 #define COLS COL+2 //ROW 行 COL 列
test.c
char mine[ROWS][COLS] = { 0 };//放布置好的雷‘0’ char show[ROWS][COLS] = { 0 };//放排查出的雷‘*’ //初始化数组 InitBoard(mine, ROWS, COLS,'0'); InitBoard(show, ROWS, COLS, '*');
game.c
#include "game.h" void InitBoard(char board[ROWS][COLS], int row, int col, char set) { int i, j; for (i = 0; i < row; i++) { for (j = 0; j < col; j++) { board[i][j] = set; } } }
细节分析:
在开始前,我们需要知道若要引用game.h的函数与声明,我们要在文件头加入#include “game.h”, 这样我们就只需要在game.h中放置函数声明与定义!
board有寄存的意思。
为什么在game.h中声明行列的数目?其实是为了自定义方格大小时便于修改!
相信大家注意到了,ROWS和COLS,为什么要创建这两个常量?下面我通过画图为大家分析
我们知道当选择A时,格子如果没有雷,则会显示周围八格的地雷数,如果没有雷则显示空格。那么如果选择C时,则显示周围五格,这将会大大提高我们的代码难度,难不成特地为边缘格子开发一个判断循环结构?所以我们就在9*9格子的基础上增加两行两列,也就是周围补上一圈!
如下图,D点也判断周围八格的地雷数!这样便于我们构造判断语句,即如果周围八格有地雷,则地雷数依次++,
所以在test.c中把数组mine和show传入函数InitBoard,当然了,我们在展示格子和雷的时候只需要9*9的范围。
3.打印棋盘
game.h
void DisplayBoard(char board[ROWS][COLS], int row, int col);
test.c
DisplayBoard(mine, ROW, COL); DisplayBoard(show, ROW, COL);
game.c
void DisplayBoard(char board[ROWS][COLS], int row, int col) { printf("-------扫雷-------\n"); printf("\n"); printf(" "); for (int i = 1; i <= row; i++) { printf("%d ", i); } printf("\n"); for (int i = 1; i <= row; i++) { printf("%d ", i); for (int j = 1; j <= col; j++) { printf("%c ", board[i][j]); } printf("\n"); } printf("-------扫雷-------\n"); }
细节分析:
前面我们讲到构造了一个11*11的二维数组,且提到了在打印棋盘时,我们其实只需要9*9的空间。
所以在test.c中我们传入ROW,COL,就是9。那么打印数组时,我们就从下标1开始,依次打印九个内容。那么为了美观,我们在头行头列加入数字便于我们知道这是哪行哪列,这也为我们后续的代码实现奠定基础!也可以加入扫雷字样。
4.布置地雷
game.h
#include<stdlib.h> #include<time.h> void SetMine(char board[ROWS][COLS], int row, int col);
test.c
//布置雷 SetMine(mine, ROW, COL);
game.c
void SetMine(char board[ROWS][COLS], int row, int col) { int count = EASY_COUNT; while (count) { //随机生成下标 int x = rand() % row + 1; int y = rand() % col + 1; if (board[x][y] != '1') { board[x][y] = '1'; count--; } } }
细节分析:
在这里我们用到了rand函数,这是一个生成伪随机数的函数,就是生成的数并不是真正随机,生成后再次打开,我们会发现数字与上次的一模一样,细心的你一定发现了,在菜单创建的主函数,有这么一串代码:srand( (unsigned int) time ( NULL ) );这便是生成随机数的关键。我们知道,在计算机中,时间是不断变化的,那么我们利用时间戳(大家可以搜看看)。time的参数如果是NULL,就能只返回时间的差值,这就是前面说的时间戳。srand函数能生成种子,而time让srand生成的是随机种子!
定义r=rand()的返回值,(由公式可知,如果要生成0-n的随机数,就让r%(n+1),即生成0-99的随机数,是r%100)现在我们要生成0-9的随机数,就是r%9+1.
rand函数与time函数需要头文件。
一切安排妥当后,我们就在数组中放入地雷,当这个位置不是雷时,我们就放入雷,并且雷的总数–。雷的数量在game.h中定义。
5.排查雷
关键的一步!
game.h
#define EASY_COUNT 10 void FindMine(char mine[ROWS][COLS],char show[ROWS][COLS], int row, int col);
test.c
//排查雷 FindMine(mine, show, ROW, COL);
game.c
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>0 && x<=row && y>0 && y<=col) { if (mine[x][y] == '1') { printf("炸了\n"); DisplayBoard(mine, ROW, COL); break; } else { win++; printf("还需排查%d个位置\n", row * col - EASY_COUNT-win); //统计x,y坐标周围的8个坐标中雷的个数 int c=GetMineCount(mine,x,y); show[x][y] = c + '0'; DisplayBoard(show, ROW, COL); } } else { printf("输入的坐标有误,请重新输入"); } } if (win == row * col - EASY_COUNT) { printf("恭喜你,排雷成功!!!\n"); DisplayBoard(mine, ROW, COL); } }
细节分析:
根据代码,我们容易知道,当我们输入坐标后,若该位置恰好有雷,那么游戏失败,退出。
否则,排查出附近八个位置雷的数目!
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'; }
因为相同字符相减为 0 ,所以方法就是让周围八个位置的相加再减去8* ‘ 0 ‘ ,下图是进阶算法。
循环的终止条件就是当win等于除了雷以外的格子数,而每次排查没踩到雷,win++。
每次排查后,都生成下一次的棋盘!
下面是效果图与源码!
效果图:
全部源码:
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 EASY_COUNT 10 void InitBoard(char board[ROWS][COLS], int row, int col, 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);
game.c
#define _CRT_SECURE_NO_WARNINGS 1 #include "game.h" void InitBoard(char board[ROWS][COLS], int row, int col, char set) { int i, j; for (i = 0; i < row; i++) { for (j = 0; j < col; j++) { board[i][j] = set; } } } void DisplayBoard(char board[ROWS][COLS], int row, int col) { printf("-------扫雷-------\n"); printf("\n"); printf(" "); for (int i = 1; i <= row; i++) { printf("%d ", i); } printf("\n"); for (int i = 1; i <= row; i++) { printf("%d ", i); for (int 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 count = EASY_COUNT; while (count) { //随机生成下标 int x = rand() % row + 1; int y = rand() % col + 1; if (board[x][y] != '1') { 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>0 && x<=row && y>0 && y<=col) { if (mine[x][y] == '1') { printf("炸了\n"); DisplayBoard(mine, ROW, COL); break; } else { win++; printf("还需排查%d个位置\n", row * col - EASY_COUNT-win); //统计x,y坐标周围的8个坐标中雷的个数 int c=GetMineCount(mine,x,y); show[x][y] = c + '0'; DisplayBoard(show, ROW, COL); } } else { printf("输入的坐标有误,请重新输入"); } } if (win == row * col - EASY_COUNT) { printf("恭喜你,排雷成功!!!\n"); DisplayBoard(mine, ROW, COL); } }
test.c
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include "game.h" void menu() { printf("*\n"); printf("* 1.play \n"); printf("* 0.exit \n"); printf("*\n"); } void game() { char mine[ROWS][COLS] = { 0 };//放布置好的雷‘0’ char show[ROWS][COLS] = { 0 };//放排查出的雷‘*’ //初始化面板 InitBoard(mine, ROWS, COLS,'0'); InitBoard(show, ROWS, COLS, '*'); //打印棋盘 //DisplayBoard(mine, ROW, COL); DisplayBoard(show, ROW, COL); //布置雷 SetMine(mine, ROW, COL); //DisplayBoard(mine, ROW, COL); //排查雷 FindMine(mine, show, ROW, COL); } int main() { srand( (unsigned int) time ( NULL ) ); int input = 0 ; do { menu(); printf("请选择:"); scanf("%d", &input); switch (input) { case 1: printf("\n"); game(); break; case 0: printf("\n"); printf("退出\n"); break; default: printf("\n"); printf("输入有误,请重新输入\n"); break; } } while (input); return 0; }
拓展:
作为一个经典的游戏,我们的代码还是不完善的,许多功能并没有实现,如:
1.在判断为雷的格子上插旗子标记!
2.加入排雷的时间显示!
3.如果排查位置不是雷,周围也没有雷,可以展开周围的一片!
4.选择游戏的难度!
。简单 9*9 10个雷
。中等 16*16 40个雷
。困难 30*16 99个雷
。自定义
小结:
本次分享就到此结束了,如果你有关于扫雷拓展与优化的想法,或者本文章中的错误与不足,欢迎在评论区中留言!!如果觉得小编写的不错,记得一键三连支持一下!!非常感谢!
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/157267.html