目录
Part1 扫雷游戏初步分析
Part2 扫雷游戏设计难点及其解决方案
难点一:逻辑混乱
难点二:越界访问
Part3 扫雷游戏代码分析
1. 游戏菜单打印
2. 游戏实现
(1)函数信息的声明
(2)棋盘的初始化
(3)打印棋盘
(4)雷的布置
(5)查找功能与游戏成功条件
Part4 扫雷游戏整体代码
(1)test.c
(2)game.h
(3)game.c
Part1 扫雷游戏初步分析
游戏要求:
1.
使?控制台实现经典的扫雷游戏2.
游戏可以通过菜单实现继续玩或者退出游戏3.
扫雷的棋盘是9*9的格?4.
默认随机布置10个雷5.
可以排查雷如果位置不是雷,就显?周围有?个雷
如果位置是雷,就炸死游戏结束
把除10个雷之外的所有雷都找出来,排雷成功,游戏结束
游戏效果:
1.游戏开始进入选择界面,通过选项选择进入或退出游戏
选择1-->进入游戏 选择0-->退出游戏 选择其他数值-->输入错误,重新输入
2.进入游戏后,选择要排查的位置,若选择的位置有雷,则游戏失败,显示整体棋盘雷的布置
3.若选择的位置没有雷,则显示以排查位置为中心的3*3格子中雷的总个数,当整个棋盘排查完毕后,游戏成功,显示整体棋盘雷的布置
Part2 扫雷游戏设计难点及其解决方案
设置思路:
扫雷的过程中,布置的雷和排查出的雷的信息都需要存储,所以我们需要?定的数据结构来存储这些信息。在9*9的棋盘上布置雷的信息和排查雷,我们?先想到的就是创建?个9*9的数组来存放信息
布置雷的棋盘上,‘*’表示有雷,‘0’表示没有雷
显示排查出的雷的信息的棋盘上,‘0’~‘8’表示排查雷位置3*3格子里雷的个数
难点一:逻辑混乱
当我们排查出雷的信息后需要将信息展示给用户,如果将信息放置在布置雷的数组中,这样雷的信息和雷的信息的个数就可能产生混淆和打印上的困难。
解决方案:
所以不如换个思路,直接将雷的信息和排查出雷的信息放在两个不同的棋盘上,也就是创建两个不同的数组,一个用来放置布置的雷的信息,一个用来放置排查出的雷的信息。程序实现的时候,把排查的位置对应到存放雷的数组中,在存放雷的数组中实现雷的排查,将排查好的雷的信息传到放置排查出雷的信息的数组当中,再将放置排查出雷的信息的数组打印出来。
难点二:越界访问
若将存放雷的信息的数组设置成9*9的格式,对于中间内部8*8的雷的排查是可以实现的,但是四周17个雷的排查会存在问题,若还是按照以排查位置为中心的3*3格子的排查方式,会出现越界访问的情况。
解决方案:为了防?越界,我们在设计的时候,给数组扩??圈,雷还是布置在中间的9*9的坐标上,周围?圈不去布置雷就?,这样就解决了越界的问题。所以我们将存放数据的数组创建成11*11是?较合适。
Part3 扫雷游戏代码分析
1. 游戏菜单打印
功能:
玩家可以通过选择1进入游戏,0退出游戏
输入错误提醒玩家重新选择
代码:
void menu() { printf("********************* "); printf("***** 1.start ***** "); printf("***** 0.exit ***** "); printf("********************* "); } int main() { int input = 0; srand((unsigned int)time(NULL)); do { menu(); scanf_s("%d", &input); srand((unsigned int)time(NULL)); switch (input) //采用while和switch语句完成 { case 1 : game(); goto finish; case 0 : printf("退出游戏 "); break; default : printf("输入错误,请重新输入: "); } } while (input); finish: return 0; }
2. 游戏实现
由于整体代码行数较多,为了便于书写和逻辑的部署,扫雷游戏采用三个文件
test.c //?件中写游戏的测试逻辑
game.c //?件中写游戏中函数的实现等
game.h //?件中写游戏需要的数据类型和函数声明等
(1)函数信息的声明
#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 COUNT 10 //雷的数量
(2)棋盘的初始化
功能:
设置两个数组分别用来存储布置的雷和查找出来的雷的信息
其中将存储布置的雷的数组设置为mine数组,存储查找出来的雷的信息的数组设置为show数组,将mine数组全部初始化为‘0’,将show数组全部初始化位‘*’
代码:
//test.c中的game函数 char Mine[ROWS][COLS] = { 0 }; char Show[ROWS][COLS] = { 0 }; //初始化棋盘 InitBoard(Mine, ROWS, COLS, '0'); InitBoard(Show, ROWS, COLS, '*'); //game.c文件 void InitBoard(char board[ROWS][COLS], int rows, int cols, char set) { int i = 0; for (i = 0; i < rows; i++) { int j = 0; for (j = 0; j < cols; j++) { board[i][j] = set; } } }
(3)打印棋盘
功能:
将初始化完成的存放排查雷的信息的数组打印到屏幕上
棋盘的每一行和每一列都有序号,以方便后续玩家操作
代码:
//game.c文件 void DisplayBoard(char board[ROWS][COLS], int row, int col) { printf("开始扫雷游戏—————— "); int i = 0; for (i = 0; i < 10; i++) { printf("%d ", i); } //打印行标 printf(" "); for (i = 1; i <= row; i++) //采用二次循环的方式,打印出9*9的棋盘 { int j = 0; printf("%d ", i); //打印列标 for (j = 1; j <= col; j++) { printf("%c ", board[i][j]); } printf(" "); } }
(4)雷的布置
功能:
在布置雷的期棋盘中随机选择给定个数个位置布置雷
将雷设置为‘0’
代码:
//game.c文件 void SetBoard(char board[ROWS][COLS], int row, int col) { int count = COUNT; while (count) { int x = (rand() % row) + 1; int y = (rand() % col) + 1; if (board[x][y] == '0') { board[x][y] = '1'; count--; } } }
重点:
rand()函数可用来生成随机数,但实际上是一个伪随机数,它的运作原理有一定的内部逻辑,是每一次重新生成的随机数一致,为了避免这个问题,可以采用srand函数,它初始化随机种子,会提供一个种子,这个种子会对应一个随机数,再将这个种子设置成与时间相关
也就是在test.c文件中包含srand((unsigned int )time(NULL));并包含头文件stdlib.h和math.h
(5)查找功能与游戏成功条件
功能:
选择某个在棋盘范围内的位置,若选择的位置不属于棋盘范围,提醒玩家选择错误,重新输入
选择的位置在棋盘范围内,若此位置为雷,则告知玩家,你被炸死了,游戏失败;若此处不为雷,则告知玩家以此位置为中心的9*9的格子中雷的个数
游戏成功条件:
若所有雷都被排查出来则游戏成功
代码:
//game.c文件 int GetMineCount(char board[ROWS][COLS], int x, int y) { return(board[x][y - 1] + board[x][y + 1] + board[x - 1][y - 1] + board[x - 1][y] + board[x - 1][y + 1] + board[x + 1][y - 1] + board[x + 1][y] + board[x + 1][y + 1 ] - 8 * '0'); } void FindBoard(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 - COUNT) { printf("请输入要查找的雷: "); scanf_s("%d %d", &x, &y); if (x >= 1 && x <= row && y >= 1 && y <= col) { if (mine[x][y] == '1') { printf("你被炸死了: "); DisplayBoard(mine, ROW, COL); break; } else { int count = GetMineCount(mine, x, y); show[x][y] = '0'+count; DisplayBoard(show, row, col); win++; } } else { printf("输入错误,请重新输入: "); } } if (win == row * col - COUNT) { printf("恭喜你成功通关: "); DisplayBoard(mine, row, col); } }
Part4 扫雷游戏整体代码
(1)test.c
#include "game.h" void game() { char Mine[ROWS][COLS] = { 0 }; char Show[ROWS][COLS] = { 0 }; //初始化棋盘 InitBoard(Mine, ROWS, COLS, '0'); InitBoard(Show, ROWS, COLS, '*'); //打印棋盘 DisplayBoard(Show, ROW, COL); //布置棋盘 SetBoard(Mine, ROW, COL); //DisplayBoard(Mine, ROW, COL); //查找棋盘 FindBoard(Mine, Show, ROW, COL); } void menu() { printf("********************* "); printf("***** 1.start ***** "); printf("***** 0.exit ***** "); printf("********************* "); } int main() { int input = 0; srand((unsigned int)time(NULL)); do { menu(); scanf_s("%d", &input); srand((unsigned int)time(NULL)); switch (input) { case 1 : game(); goto finish; case 0 : printf("退出游戏 "); break; default : printf("输入错误,请重新输入: "); } } while (input); finish: return 0; }
(2)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 COUNT 10 //雷的数量 void InitBoard(char board[ROWS][COLS], int rows, int cols, char set); void DisplayBoard(char borad[ROWS][COLS], int row, int col); void SetBoard(char board[ROWS][COLS], int row, int col); void FindBoard(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
(3)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 = 0; for (j = 0; j < cols; j++) { board[i][j] = set; } } } void DisplayBoard(char board[ROWS][COLS], int row, int col) { printf("开始扫雷游戏—————— "); int i = 0; for (i = 0; i < 10; i++) { printf("%d ", i); } printf(" "); for (i = 1; i <= row; i++) { int j = 0; printf("%d ", i); for (j = 1; j <= col; j++) { printf("%c ", board[i][j]); } printf(" "); } } void SetBoard(char board[ROWS][COLS], int row, int col) { int count = 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 board[ROWS][COLS], int x, int y) { return(board[x][y - 1] + board[x][y + 1] + board[x - 1][y - 1] + board[x - 1][y] + board[x - 1][y + 1] + board[x + 1][y - 1] + board[x + 1][y] + board[x + 1][y + 1 ] - 8 * '0'); } void FindBoard(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 - COUNT) { printf("请输入要查找的雷: "); scanf_s("%d %d", &x, &y); if (x >= 1 && x <= row && y >= 1 && y <= col) { if (mine[x][y] == '1') { printf("你被炸死了: "); DisplayBoard(mine, ROW, COL); break; } else { int count = GetMineCount(mine, x, y); show[x][y] = '0'+count; DisplayBoard(show, row, col); win++; } } else { printf("输入错误,请重新输入: "); } } if (win == row * col - COUNT) { printf("恭喜你成功通关: "); DisplayBoard(mine, row, col); } }