一.扫雷游戏分析和设计
1.扫雷游戏的功能说明
- 使用控制台实现经典的扫雷游戏
- 游戏可以通过菜单实现继续玩或者退出游戏
- 扫雷的棋盘是9*9的格子
- 默认随机布置10颗雷
- 可以排查雷,分为以下几种情况
- 如果位置不是雷,就显示周围有几个雷
- 如果位置是雷,直接炸死,游戏结束
- 把除10个雷之外的所有非雷的位置都找出来,那么排雷成功,游戏结束
游戏的界面如下:
初始界面
排雷界面
排雷失败界面
二、游戏的分析和设计
1.数据结构的分析我们要创建棋盘,所以需要用到2维数组(我这里创建的是一个9*9的棋盘),我们这里创造的char类型的2维数组。
但是我们创建的数组是char a[11][11]是11*11,因为当我们要排查的雷是边界值时,统计该位置周围(3*3的格子)的地雷个数会出错。赋值要赋11*11范围的,打印只需要9*9的就可以了。接着就要埋雷了,是利用随机数生成的原理埋下10个雷的坐标。
接着分析,我们在棋盘上布置了雷,棋盘上雷的信息(1)和非雷的信息(0),假设我们排查了个位置后,这个坐标处不是雷,这个坐标的周围有1个雷,那我们需要将排查出的雷的数量信息记存储,并打印出来,作为排雷的重要参考信息的。那这个雷的个数信息存放在哪里呢?如果存放在置雷的数组中,这样雷的信息和雷的个数信息就可能或产生混淆和打印上的困难。
这里我们肯定有办法解决,比如:雷和非雷的信息不要使用数字,使用某些字符就行,这样就避免突了,但是这样做棋盘上有雷和非雷的信息,还有排查出的雷的个数信息,就比较混杂,不够方便这里我们采用另外一种方案,我们专门给一个棋盘(对应一个数组mine) 存放布置好的雷的信息,给另外一个棋盘(对应另外一个数组show)存放排查出的雷的信息。这样就互不干扰了,把雷布置mine数组,在mine数组中排查雷,排查出的数据存放在show数组,并且打印show数组的信息给后排查参考。
同时为了保持神秘,显示数组开始时初始化为字符'*,为了保持两个数组的类型一致,可以使用同套函数处理,我的数组最开始也初始化为字符0',布置雷改成1。如下图: -
mine数组布置雷后的状态
show输出初始化的状态
代码如下:char mine[11][11]={0};//用来存放布置好的雷的信息 char show[11][11]={0};//用来存放排查出的雷的个数信息
2.文件结构设计由于把扫雷游戏只写到一个源文件中看起来和检查会很麻烦,所以我把它分成了两个源文件和
一个头文件。1.test.c 文件中写游戏的测试逻辑
2.game.c 文件中写游戏中函数的实现,具体用到的函数
3.game.h 文件中写游戏需要的数据类型和函数声明
三、扫雷游戏代码实现
game.h#pragma once //游戏需要的类和函数声明 #include<stdio.h> #include<stdlib.h> #include<time.h> #define EASY_COUNT 10 #define ROW 9 #define COL 9 #define ROWS ROW+2 #define COLS COL+2 //初始化棋盘 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 FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
game.c
#include"game.h" //初始化棋盘 void InitBoard(char board[ROWS][COLS], int rows, int cols, char set) { int i = 0; for (i = 0;i < rows;i++) { int j; for (j = 0;j < cols;j++) { board[i][j] = set; } } } //打印棋盘 void DisplayBoard(char board[ROWS][COLS], int row, int col) { int i = 0; printf("\t--------扫雷游戏--------\t\n"); for (i = 0;i <= col;i++) { printf("%d", i); } printf("\n"); for (i = 1;i <= row;i++) { printf("%d", i); int j = 0; for (j = 1;j <= col;j++) { printf("%c", board[i][j]); } printf("\n"); } } //布置雷 void SetMine(char board[ROWS][COLS], int row, int col) { //布置10个雷 //生成随机的坐标,布置雷 int count = EASY_COUNT; while (count) { int x = rand() % row + 1; int y = rand() % col + 1; if (board[x][y] == '0') { board[x][y] = '1'; count--; } } } int GetMineCount(char mine[ROWS][COLS], int x, int y)//用来统计除了mine[x][y]剩下8个地雷的个数 { int sum = 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]; return sum - 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;//这里的win是排雷次数,也就相当于计数器 while (win < row * col - EASY_COUNT) { printf("请输入要排查的坐标:>"); scanf("%d %d", &x, &y); if (x >= 1 && x <= row && y >= 1 && y <= col) { if (mine[x][y] == '1') { printf("很遗憾,你被炸死了,游戏结束\n"); DisplayBoard(mine, ROW, COL); break; } else { //该位置不是雷,就统计这个坐标周围有几个雷 int count = GetMineCount(mine, x, y); show[x][y] = count + '0'; DisplayBoard(show, ROW, COL); win++; } } else { printf("坐标非法,请重新输入\n"); } }if (win == row * col - EASY_COUNT) { printf("恭喜你,排雷成功\n"); DisplayBoard(mine, ROW, COL); } }
test.c
#include "game.h" void menu() { printf("*************************\t\n"); printf("******* 1.play *******\t\n"); printf("******* 0.exit *******\t\n"); printf("*************************\t\n"); } void game() { char mine[ROWS][COLS];//存放布置好的雷 char show[ROWS][COLS];//存放排查出的雷的信息 //初始化棋盘 //1.mine数组最开始全是'0' //2.show数组最开始全是'*' InitBoard(mine, ROWS, COLS, '0'); InitBoard(show, ROWS, COLS, '*'); //打印棋盘 DisplayBoard(show, ROW, COL);//布置雷 SetMine(mine, ROW, COL);//排查雷 FindMine(mine, show, ROW, COL); } int main() { int input = 0; srand((unsigned int)time(NULL)); //利用一个生成随机数(但是一个编译器只能生成一个固定值,所以又要引入一个时间这个变量) do { menu(); printf("请选择:>"); scanf("%d", &input); switch (input) { case 1: game(); break; case 0: printf("退出游戏\n"); break; default: printf("选择错误,重新选择\n"); break; } } while (input); return 0; }