1.分析问题需求和大致思路
在开始设计之前,我们先分析一下,扫雷游戏大家可能都玩过,基本的信息都是呈现在一个10*10的方格上的。开始时我们应该打印一个菜单,让玩家选择是否进行游戏。
然后是初始化的问题,用二维数组来的格子里面的信息的话,刚好非常契合,确定了用数组来存储以后,我们就可以思考一下怎么做初始化,我们可以用3种不同的符号来表示棋盘上的状态,没被玩家排查之前用#表示,在玩家查找以后要输出周围有多少炸弹,呈现出一个整数,呈现在屏幕上的是这些符号,因此我们在创建数组时就得注意用char型。具体的存储信息如下图:
在完成初始化之后,我们要生成随机的地雷位置,可以用rand函数和srand函数以及time函数来保证生成数的随机性。对于以上3个函数有不了解的小伙伴们欢迎点击博主的这篇文章,里面对以上函数有非常详细的介绍和讲解:C语言实现猜数字小游戏以及如何生成真正的随机数
在将地雷布置完成后,我们就可以构思扫雷部分的逻辑了,首先由玩家输入一个坐标,然后计算机进行判断,输入位置是否合法是否合理,在确认输入合理后,对玩家输入的坐标进行判断,如果有炸弹,就弹出失败的界面,然后退出游戏,如果没有炸弹,就使用一个函数来统计周围炸弹的数量并且打印出来,然后对以上做循环的封装处理,直到游戏胜利或者失败才跳出循环,完成循环后,我们就需要解决如何定义游戏胜利的条件,我们可以用所有的格子减去已经排除的格子,再减去剩余的炸弹数,也就是进行是否已经将所有格子排查的判断,在最后只剩10个炸弹时,结束游戏,跳出循环。
为了保证程序的健全性和可读性,我们采用多文件的方法实现扫雷小游戏。用game.h存储头文件信息以及宏定义和函数声明,用board.c文件存储游戏里面的基本逻辑函数,用game.c文件存储游戏的主要功能以及主体
2.游戏主体逻辑实现以及代码
这里#include包含的头文件是我们自定义的头文件,里面有我们需要的头文件和函数声明,首先我们定义一个input输入实现记录玩家对于是否开始游戏的选择,用do...while...循环来实现先运行循环体让玩家决定是否进入游戏,在判断玩家的输入,以便于让玩家重复输入以及重复的游玩游戏,其中的srand函数是生成随机数的种子,后文中会提及到,这里就不再继续赘述
include "game.h" void game() { char bomb[ROWS][COLS] = { 0 }; char show[ROWS][COLS] = { 0 }; //初始化棋盘 initboard(bomb, ROWS, COLS, '0'); initboard(show, ROWS, COLS, '#'); //打印棋盘 printboard(show, ROW, COL); //生成随机炸弹位置 setbombs(bomb, ROWS, COLS); //开始扫雷 findbombs(bomb, show, ROW, COL); } int main() { int input = 0; srand((unsigned int)time(NULL)); printf("请进行输入确认是否开始游戏\n"); do { mune();//打印菜单 scanf("%d", &input); switch(input) { case 1: printf("\t开始游戏\n"); game(); break; case 2: printf("\t退出游戏\n"); break; default: printf("非法输入,请重试\n"); break; } } while (input != 2); return 0; }
3.游戏的初始化
我们用俩个二维数组来做记录,一个用来保存我们实际上的地雷存储位置,一个用来打印在屏幕上让玩家观看,俩个数组同等大小,之所以使用11行和9行是为了方便后面判断周围的炸弹数,也是方便后面进行交换打印等等操作,但是实际上对玩家展示的只有1~9行和列,也就是最外边一圈不展示。这里利用传的参数(char ret)不同,来实现对俩个数组进行不同的初始化。至于ROW, ROWS, COL, COLS则是我们为了方便使用以及后期修改,对于行和列进行的宏定义
//定义行和列 #define ROW 9 #define COL 9 #define ROWS ROW+2 #define COLS COL+2
//初始化棋盘 initboard(bomb, ROWS, COLS, '0'); initboard(show, ROWS, COLS, '#');
//初始化 void initboard(char board[ROWS][COLS], int row, int col,char set) { for (int i = 0; i < row; i++) { for (int j = 0; j < col; j++) { board[i][j] = set; } } }
4.地雷位置的随机生成
还是这张图,我们是只需要使用中间部分的格子的,也就是1~9行以及1~9列,所以在放置炸弹时需要注意,不能放在外围去了
用rand函数对9取模,随机数的范围就成了0~8,加上1刚好就是1~9,将值付给x和y就得到了随机的坐标,while循环则可以反复的判断在放置地雷之前得先判断这个位置是否为空,再用count计数保证放置10个炸弹
//宏定义炸弹的个数 #define bomb_count 10 //生成随机炸弹位置 void setbombs(char board[ROWS][COLS],int row,int col);
//随机生成地雷位置 void setbombs(char board[ROWS][COLS], int row, int col) { int x = 0; int y = 0; int count = bomb_count; while (count > 0) { x = rand() % 9 + 1; y = rand() % 9 + 1; if (board[x + 1][y + 1] == '0') { board[x + 1][y + 1] = '1'; count--; } } }
5.打印棋盘
玩家是需要看见棋盘长什么样的,要给玩家提供一个可视化的界面,实现如下:
//打印棋盘 void printboard(char board[ROWS][COLS], int row, int col) { printf("------扫雷游戏------\n"); printf(" "); for (int i = 1; i <= col; 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"); }
6.扫雷
做好好一切准备工作后,就可以开始正式的扫雷了,首先让玩家输入一个坐标,用x和y进行保存记录,然后判断这个坐标是否在9*9的格子里面,如果不在就让玩家重新输入,可以用while循环实现重复的过程,拿到这个坐标以后判断是否踩雷,如果踩雷则弹出被炸死的信息,然后打印炸弹的位置,结束游戏,如果没有,则用一个函数来计算周围有多少个炸弹,定义个整数win来统计已经判断安全的位置,最后用所有的格子数减去炸弹数,再让win和这个数比较,就可以得到是否已经全部盘查完毕,如果是,就宣布游戏胜利,结束游戏,如果没有排查完,就继续游戏继续排查
//扫雷 void findbombs(char bombs[ROWS][COLS],char show[ROWS][COLS], int row, int col) { int x = 0; int y = 0; int win = 0; while (win<(row*col- bomb_count)) { printf("请输入你想排查的坐标: "); scanf("%d %d", &x, &y); if (x >= 1 && x <= row && y >= 1 && y <= col) { if (bombs[x][y] == '1') { printf("你被炸弹炸死了\n"); printboard(bombs, ROW, COL); break; } else { int count = countbombs(bombs, x, y); show[ x ][ y ] = count + '0'; printboard(show, ROW, COL); win++; } } else { printf("输入坐标不合法,请重新输入\n"); } } if (win == (row * col - bomb_count)) { printf("恭喜你,排掉了全部的炸弹,游戏胜利!!!\n"); printboard(bombs, ROW, COL); } }
这里我们定义一个函数来判断周围炸弹数量
//统计周围炸弹数量 int countbombs(char bombs[ROWS][COLS], int x, int y) { int ret = 0; ret=bombs[x - 1][y - 1] + bombs[x - 1][y] + bombs[x - 1][y + 1] + bombs[x][y - 1] + bombs[x][y + 1] + bombs[x + 1][y - 1] + bombs[x + 1][y] + bombs[x + 1][y + 1] - 8 * '0'; return ret; }
以上就完成了扫雷游戏的绝大部分内容,剩下的只需要稍微拼接,修补一下就可以得到完整的程序代码,以下完整的一下代码奉上
7.游戏的完整代码
1.头文件代码
#include<stdio.h> #include<windows.h> #include<stdlib.h> #include<time.h> //定义行和列 #define ROW 9 #define COL 9 #define ROWS ROW+2 #define COLS COL+2 #define bomb_count 10 //打印菜单 void mune(); //游戏逻辑主程序 void game(); //棋盘初始化 void initboard(char board[ROWS][COLS],int row,int col,char temp); //打印棋盘 void printboard(char board[ROWS][COLS], int row, int col); //生成随机炸弹位置 void setbombs(char board[ROWS][COLS],int row,int col); //扫雷 void findbombs(char bombs[ROWS][COLS], char show[ROWS][COLS], int row, int col); //统计周围炸弹数量 int countbombs(char bombs[ROWS][COLS], int x, int y);
2.游戏逻辑代码
#define _CRT_SECURE_NO_WARNINGS 1 #include "game.h" //打印菜单 void mune() { printf("--------------------------\n"); printf("---------1.Play ----------\n"); printf("--------------------------\n"); printf("---------2.Esc ----------\n"); printf("--------------------------\n"); } //初始化 void initboard(char board[ROWS][COLS], int row, int col,char set) { for (int i = 0; i < row; i++) { for (int j = 0; j < col; j++) { board[i][j] = set; } } } //打印棋盘 void printboard(char board[ROWS][COLS], int row, int col) { printf("------扫雷游戏------\n"); printf(" "); for (int i = 1; i <= col; 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 setbombs(char board[ROWS][COLS], int row, int col) { int x = 0; int y = 0; int count = bomb_count; while (count > 0) { x = rand() % 9 + 1; y = rand() % 9 + 1; if (board[x + 1][y + 1] == '0') { board[x + 1][y + 1] = '1'; count--; } } } //统计周围炸弹数量 int countbombs(char bombs[ROWS][COLS], int x, int y) { int ret = 0; ret=bombs[x - 1][y - 1] + bombs[x - 1][y] + bombs[x - 1][y + 1] + bombs[x][y - 1] + bombs[x][y + 1] + bombs[x + 1][y - 1] + bombs[x + 1][y] + bombs[x + 1][y + 1] - 8 * '0'; return ret; } //扫雷 void findbombs(char bombs[ROWS][COLS],char show[ROWS][COLS], int row, int col) { int x = 0; int y = 0; int win = 0; while (win<(row*col- bomb_count)) { printf("请输入你想排查的坐标: "); scanf("%d %d", &x, &y); if (x >= 1 && x <= row && y >= 1 && y <= col) { if (bombs[x][y] == '1') { printf("你被炸弹炸死了\n"); printboard(bombs, ROW, COL); break; } else { int count = countbombs(bombs, x, y); show[ x ][ y ] = count + '0'; printboard(show, ROW, COL); win++; } } else { printf("输入坐标不合法,请重新输入\n"); } } if (win == (row * col - bomb_count)) { printf("恭喜你,排掉了全部的炸弹,游戏胜利!!!\n"); printboard(bombs, ROW, COL); } }
3.游戏主体代码
#define _CRT_SECURE_NO_WARNINGS 1 #include "game.h" void game() { char bomb[ROWS][COLS] = { 0 }; char show[ROWS][COLS] = { 0 }; //初始化棋盘 initboard(bomb, ROWS, COLS, '0'); initboard(show, ROWS, COLS, '#'); //打印棋盘 printboard(show, ROW, COL); //生成随机炸弹位置 setbombs(bomb, ROWS, COLS); //printboard(bomb, ROW, COL); //开始扫雷 findbombs(bomb, show, ROW, COL); } int main() { int input = 0; srand((unsigned int)time(NULL)); printf("请进行输入确认是否开始游戏\n"); do { mune(); scanf("%d", &input); switch(input) { case 1: printf("\t开始游戏\n"); game(); break; case 2: printf("\t退出游戏\n"); break; default: printf("非法输入,请重试\n"); break; } } while (input != 2); return 0; }