摘要:每成功排一次雷,我们都要展示雷盘当场上剩下的格子数等于雷数时,游戏胜利,玩家踩雷时,游戏结束,所以我们这是一个判断输赢的函数。
前言:写完三子棋后,慢慢地熟悉了这种写小游戏的过程,之前对于代码的分区存储都还是懵懵的,现在终于有点得心应手啦,话不多说,这次我写的是扫雷,但是博主水平有限,目前还不能做到图形化界面,日后有能力时定来修改这篇文章,实现一个带有图形化界面的扫雷!
先来看看扫雷的样子
与三子棋一样,我们可以把要排雷的雷盘?(是这个名字不?)看成一个二维数组,当我们点击格子时,格子会显示周围一圈的雷数。先要有一个二维数组来放我们的雷,但我们不可能把这个数组展示给玩家看(这样直接开透视了呀)。我们得再创建一个二维数组展示给玩家,这个数组开始时全是’ * ’ ,当玩家输入一个坐标时,’ * ’ 会变成周围一圈的雷数。
经过梳理,游戏的逻辑就清晰了:开始时我们要初始化两个数组,一个用来放雷(我们叫这个数组mine),另一个用来向玩家展示(我们叫它show)。接着我们会放置地雷,把show数组打印给玩家看,玩家输入坐标,开始排雷。每成功排一次雷,我们都要展示雷盘当场上剩下的格子数等于雷数时,游戏胜利,玩家踩雷时,游戏结束,所以我们这是一个判断输赢的函数。大概的过程我们就理清楚了。
还是像三子棋一样,我们把整个游戏分成三个部分:
test.c — > 程序的运行逻辑
game.c — > 游戏的实现逻辑
game.c — > 存放游戏相关函数的代码与库函数的头文件
接着是游戏代码
int main(){ srand((unsigned int)time(NULL));//设置随机数种子,用来随机生成地雷 test(); return 0;}
为了让代码看起来整洁,在主函数中调用test函数
test函数是游戏运行的逻辑
void test(){ int input = 0; do { menu();//开始游戏前打印菜单 printf("请选择 > 1/0/n"); scanf("%d", &input);//input变量存放玩家的选择 switch (input)//根据input的值switch进入不同的分支 { case 1: game();//游戏逻辑的实现函数 break; case 0: printf("退出游戏/n"); default : printf("输入错误,请重新输入/n"); break; } } while (input);//玩家可以反复玩游戏,玩家选0时退出游戏}
代码实现与三子棋的类似,这次写这部分代码时就很熟练了。
游戏菜单(这个就和三子棋一模一样了)
void menu(){ printf("***********************/n"); printf("***** 1.开始游戏 ****/n"); printf("***** 0.退出游戏 ****/n"); printf("***********************/n");}
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); //打印展示棋盘 DisplayBoard(show, ROW, COL); //玩家排雷 Find(mine, show, ROW, COL);}
game函数就是我们刚刚梳理的游戏逻辑,接着我们就要一个个实现这些函数了。
首先呢,与三子棋不一样的是,我们这次要初始化两个棋盘,但是两个棋盘的初始化内容是不一样的,这就要求我们的初始化函数要有相对的独立性来实现对不同数组的初始化。要怎么做呢?我们可以在函数设置四个参数
我们来看看函数的实现
//初始化棋盘void InitBoard(char board[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++) { board[i][j] = set;//将所有元素初始化 } }}
这里解释为什么定义ROWS,COLS,ROW,COL这四个宏。
mine数组中,除了雷是用1表示,其他的元素都是用0表示,计算雷数时,把周围八个元素的值相加,得到的数刚好就是雷数。但是我们对边界上的元素进行排雷时,会出先数组的越界访问,产生意想不到的结果,这当然不是我们想要的。所以,对数组增加两行与两列(假设二维数组是9 * 9的,增加后变为11 * 11),对11 * 11的数组初始化,但我们使用的是9 * 9 的数组,在计算雷数时就不会出现错误了。
所以,我们把ROWS与COLS设置成11,ROW与COL设值成9。
初始化后,我们就要放地雷了。
//放置地雷//这里有个小细节,我们的数组大小是ROWS * COLS//但放地雷时只能在ROW * COL大小的数组中void SetMine(char board[ROWS][COLS], int row, int col){ int x = 0; int y = 0; int count = CON; //CON又是一个宏,指的是我们要放置的雷数 //CON在头文件中定义 //用count变量接收CON常量的值,放一次雷count减一 while (count) { //利用随机数产生坐标 x = rand() % row + 1; y = rand() % col + 1; //坐标的值可能相同,所以要判断坐标是否合理 if (board[x][y] == "0") { board[x][y] = "1"; count--;//当count为0时,循环结束 } }}
由于我们要用到rand函数,所以在主函数中,我们调用了srand函数设置随机数种子。
展示雷盘的时候,要是直接把雷盘打印出来,玩家输入坐标时对行号与列号可能会不太确定(毕竟数量有点多嘛),
看得清不?哈哈哈,所以我们要对行与列加上序号,方便玩家确定坐标
//同时打印行号与列号//我们展示的雷盘大小是ROW * COLvoid DisplayBoard(char board[ROWS][COLS], int row, int col){ int i = 0; int j = 0; //列号的打印 for (i = 0; i <= 9; i++) { printf("%d ", i); } printf("/n"); for (i = 1; i <= row; i++) { //行号的打印 //在打印行之前先打印行号 printf("%d ", i); for (j = 1; j <= col; j++) { printf("%c ", board[i][j]); } printf("/n"); }}
这样就好认多了
接着就到了玩家进行排雷了,通过玩家输入坐标,程序把对应的数组元素赋值成周围一圈的雷数,要是踩到雷了,提示玩家玩得太菜 游戏结束。但这又有一个细节,我们创建的数组是字符数组,放置的是字符,把字符相加得到的是对应的ASCII码值。所以我们把相加后的值减去八个字符0的ASCII码值,得到的就是雷数了。我写了一个num函数来计算雷数。
//num函数返回类型是整形int num(char board[ROWS][COLS], int x, int y){ //返回八个周围元素的相加值,但返回的得是整形,所以减去八个字符0 return board[x - 1][y] + board[x - 1][y - 1] + board[x][y - 1] + board[x + 1][y - 1] + board[x + 1][y] + board[x + 1][y + 1] + board[x][y + 1] + board[x - 1][y + 1] - 8 * "0";}
接着是玩家排雷的函数
void Find(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col){ int x = 0; int y = 0; //设置变量接收玩家输入的值 int win = 0; //用win变量判断玩家是否胜利 //win初始值为0,每排一次雷win加一 while (win < ROW * COL - CON) { printf("请输入坐标/n"); scanf("%d %d", &x, &y); if (x >= 1 && x <= 9 && y >= 1 && y <= 9 && show[x][y] == "*") //判断玩家输入的坐标在雷盘中并且没有对坐标进行过扫雷 { if (mine[x][y] == "1") { printf("很遗憾,你被炸死了/n"); DisplayBoard(mine, ROW, COL); //玩家被炸死后展示雷盘 break; } else { //计算雷的数量 int n = num(mine, x, y); //把展示雷盘对应的元素赋值成周围雷数 show[x][y] = n + "0"; //但展示雷盘是字符数组,雷数要加上字符0 DisplayBoard(show, ROW, COL); //每次排雷展示雷盘 win++; } } else printf("输入错误,请重新输入/n"); } //当win等于雷盘大小减去雷数时,玩家排雷成功,游戏结束 if (win == ROW * COL - CON) { printf("恭喜你,排雷成功/n"); }}
至此所有的代码我们就完成了,接着是所有文件代码的整体展示
#include"game.h"void InitBoard(char board[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++) { board[i][j] = set; } }}//同时打印行号与列号void DisplayBoard(char board[ROWS][COLS], int row, int col){ int i = 0; int j = 0; //列号的打印 for (i = 0; i <= 9; i++) { printf("%d ", i); } printf("/n"); for (i = 1; i <= row; i++) { //行号的打印 printf("%d ", i); for (j = 1; j <= col; j++) { printf("%c ", board[i][j]); } printf("/n"); }}void SetMine(char board[ROWS][COLS], int row, int col){ int x = 0; int y = 0; int count = CON; while (count) { x = rand() % row + 1; y = rand() % col + 1; if (board[x][y] == "0") { board[x][y] = "1"; count--; } }}int num(char board[ROWS][COLS], int x, int y){ return board[x - 1][y] + board[x - 1][y - 1] + board[x][y - 1] + board[x + 1][y - 1] + board[x + 1][y] + board[x + 1][y + 1] + board[x][y + 1] + board[x - 1][y + 1] - 8 * "0";}void Find(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 - CON) { printf("请输入坐标/n"); scanf("%d %d", &x, &y); if (x >= 1 && x <= 9 && y >= 1 && y <= 9) { if (mine[x][y] == "1") { printf("恭喜你,你被炸死了/n"); DisplayBoard(mine, ROW, COL); break; } else { //计算雷的数量 int n = num(mine, x, y); show[x][y] = n + "0"; DisplayBoard(show, ROW, COL); win++; } } else printf("输入错误,请重新输入/n"); } if (win == ROW * COL - CON) { printf("恭喜你,排雷成功/n"); }}
#include #include #include #define ROWS 11#define COLS 11#define ROW 9#define COL 9#define CON 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 Find(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
#include"game.h"void menu(){ printf("***********************/n"); printf("***** 1.开始游戏 ****/n"); printf("***** 0.退出游戏 ****/n"); printf("***********************/n");}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); //打印展示棋盘 DisplayBoard(show, ROW, COL); //玩家排雷 Find(mine, show, ROW, COL);}void test(){ int input = 0; do { menu(); printf
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/123356.html
摘要:展示雷盘和初始化雷盘不一样,展示雷盘只需要用即可,并不需要将都展示出来,只是为了我们更好的计算扫雷的位置周围的雷的数量。 目录 1、需求分析 2、程序架构 3、代码实现(分函数呈现) (1)主函数代码实现 分析: 异常处理: (2)游戏主函数实现 分析: (3)初始化函数的实现 分析: (4...
摘要:新人小白的第一篇博客,有什么不好之处望多提意见。这个扫雷小游戏主要是基于二维数组,循环与基本的函数知识等。请输入坐标提示玩家输入坐标。换行是为了看着好看,要不然打印出来的数组会变形的。用来接收判断输赢的函数的返回值。 新人小白的第一篇博客,有什么不好之处望多提意见。 ...
摘要:设计实现扫雷游戏大致思路创建文件想法实现设计一个函数,实现建议菜单循环和分支选择游戏选项创造一个扫雷版面版面的大小最后是要可控的如何存放雷和版面的信息呢考虑排查雷时候的思路,我们要判断该位置周围个格子里面是否有雷初始化 ...
阅读 2016·2021-11-15 11:38
阅读 2049·2019-08-30 15:55
阅读 2183·2019-08-30 15:52
阅读 3169·2019-08-30 14:01
阅读 2685·2019-08-30 12:47
阅读 1131·2019-08-29 13:17
阅读 1062·2019-08-26 13:55
阅读 2630·2019-08-26 13:46