业精于勤,荒于嬉;行du成于思,毁于随。
扫雷游戏规则 把所有非地雷的格子揭开即胜利,踩到地雷格子即失败。
游戏流程
电脑打印游戏菜单(1.开始游戏 0.退出游戏)——>玩家选择(开始游戏——>进入游戏函数)——>电脑打印出雷盘——>玩家输入需要排雷的坐标——>电脑打印出排完雷后的雷盘(可能性1:被炸死;可能性2:坐标安全,系统显示出周围八个格子内的地雷总数)——>继续排雷——>游戏胜利
扫雷游戏的双层数组 前文有写到三(多)子棋小游戏,三子棋游戏只需要一个数组就够了,但是对于扫雷游戏的实现,一个数组是不够的,需要创建两个数组。
两个二维数组 第一个数组,存放雷的分布信息,面向游戏设计者创建,称为雷盘布置数组,简称布雷数组,如下所示。
1 2 3 4 5 6 7 8 9 10 0 1 2 3 4 5 6 7 8 9 1 0 0 0 0 0 1 0 1 0 2 0 0 0 0 0 0 1 0 0 3 0 1 0 0 0 0 1 1 0 4 0 0 0 0 0 0 0 0 0 5 0 0 0 0 1 0 0 0 0 6 0 0 0 0 0 0 0 0 0 7 0 0 0 0 1 0 0 0 0 8 0 1 0 0 0 0 1 0 0 9 0 0 0 0 0 0 0 0 0
第二个数组,存放排雷后的雷盘中雷的分布个数信息,面向玩家创建,称为玩家数组,如下所示。
1 2 3 4 5 6 7 8 9 10 0 1 2 3 4 5 6 7 8 9 1 * * * * * * * * *2 * * * * * * * * *3 * * 1 * * * * * *4 * * * * * * * * *5 * * * * * * * * *6 * * * * * * * * *7 * * * * * * * * *8 * * * * * * * * *9 * * * * * * * * *
布雷数组技巧的设计 程序设计的时候,需要判断玩家坐标周围八个格子中雷的总个数,那么这就带来一个问题:
玩家选择边角最外环的一层坐标
和玩家选择内环坐标
判断方法不同。有两种解决方案。
第一种:加if判断
如果是最外面的一圈的就单独拉出来判断其周围的雷的总数,但是这又导致了一个问题,四个角和每条棱的判断方法各不相同,所以这个解决方案很繁琐。
第二种:巧妙地让布雷数组膨胀一圈
假设让布置雷的雷盘是11X11规格的,但是只在9X9的格子里布雷,那么对于每个9X9格子里的坐标,判断周围格子里雷的总数的算法都是一样的。(把巧妙打在公屏上!!!)
程序设计 创建三个文件,game.h
游戏的头文件functions.c
游戏所用到功能函数 game.c
游戏框架 三个文件。
头文件 头文件的书写是一步一步需要什么写什么建立而来的,这里先把所有的宏定义、库函数引用、函数声明放这里,后面用到会有解释。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #define _CRT_SECURE_NO_WARNINGS 1 #define ROW 9 #define COL 9 #define ROWS ROW+2 #define COLS COL+2 #define EASY_COUNT 10 #include <stdio.h> #include <stdlib.h> #include <time.h> void test (void ) ;void menu (void ) ;void game (void ) ;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[ROW][COL], int row, int col) ;void FindMine (char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col) ;
游戏实现框架 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 #define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h> #include "game.h" int main (void ) { test(); return 0 ; } void test (void ) { int input = 0 ; do { menu(); printf ("请选择:>>>" ); scanf ("%d" , &input); switch (input) { case 1 : game(); break ; case 2 : printf ("退出游戏\n" ); input = 0 ; break ; default : printf ("输入值无效\n" ); break ; } } while (input); } void menu (void ) { printf ("************************************\n" ); printf ("*****1.开始游戏 0.退出游戏******\n" ); printf ("************************************\n" ); } void game (void ) { char mine[ROWS][COLS] = { 0 }; char show[ROWS][COLS] = { 0 }; InitBoard(mine, ROWS, COLS, '0' ); InitBoard(show, ROWS, COLS, '*' ); SetMine(mine, ROW, COL); DisplayBoard(mine, ROW, COL); DisplayBoard(show, ROW, COL); FindMine(mine, show, ROW, COL); }
功能函数 InitBoard 需要对两个数组都进行初始化,但是初始化的分辨元素不一样,所以这里加入一个参数调用set。
1 2 3 4 5 6 7 8 9 10 11 12 13 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 ; } } }
DisplayBoard 初始化函数写好之后可以先写雷盘打印函数进行核验,所以下介绍DisplayBoard函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 void DisplayBoard (char board[ROWS][COLS], int row, int col) { int i = 0 ; int j = 0 ; printf ("\n" ); for (i = 0 ; i <= row; 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" ); } printf ("\n" ); }
SetMine 这里就要布置雷了,采用随机值的方法进行布雷,传递整个数组,但控制雷的分布只出现在内环9*9的网格里。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 void SetMine (char board[ROWS][COLS], int row, int col) { int count = EASY_COUNT; srand((unsigned int )time(NULL )); while (count) { int x = rand() % row + 1 ; int y = rand() % col + 1 ; if (board[x][y] == '0' ) { board[x][y] = '1' ; count--; } } }
FindMine 进行扫雷,如果玩家踩雷就被炸死,未踩雷,系统报告周围雷的个数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 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 >= 1 && x <= row && y >= 1 && y <= col) { if (mine[x][y] == '1' ) { printf ("\n啊哦,你被炸死了!!!\n" ); DisplayBoard(mine, row, col); break ; } else { int count = (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' ; show[x][y] = count + '0' ; DisplayBoard(show, row, col); win++; } } else { printf ("非法输入!!!" ); } } }
完整程序 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #define _CRT_SECURE_NO_WARNINGS 1 #define ROW 3 #define COL 3 #define ROWS ROW+2 #define COLS COL+2 #define EASY_COUNT 3 #include <stdio.h> #include <stdlib.h> #include <time.h> void test (void ) ;void menu (void ) ;void game (void ) ;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) ;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 #define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h> #include "game.h" int main (void ) { test(); return 0 ; } void test (void ) { int input = 0 ; do { menu(); printf ("请选择:>>>" ); scanf ("%d" , &input); switch (input) { case 1 : game(); break ; case 2 : printf ("退出游戏\n" ); input = 0 ; break ; default : printf ("输入值无效\n" ); break ; } } while (input); } void menu (void ) { printf ("************************************\n" ); printf ("*****1.开始游戏 0.退出游戏******\n" ); printf ("************************************\n" ); } void game (void ) { char mine[ROWS][COLS] = { 0 }; char show[ROWS][COLS] = { 0 }; InitBoard(mine, ROWS, COLS, '0' ); InitBoard(show, ROWS, COLS, '*' ); SetMine(mine, ROW, COL); DisplayBoard(mine, ROW, COL); DisplayBoard(show, ROW, COL); FindMine(mine, show, ROW, COL); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 #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 ; printf ("\n" ); for (i = 0 ; i <= row; 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" ); } printf ("\n" ); } void SetMine (char board[ROWS][COLS], int row, int col) { int count = EASY_COUNT; srand((unsigned int )time(NULL )); while (count) { int x = rand() % row + 1 ; int y = rand() % col + 1 ; if (board[x][y] == '0' ) { board[x][y] = '1' ; count--; } } } 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 >= 1 && x <= row && y >= 1 && y <= col) { if (mine[x][y] == '1' ) { printf ("\n啊哦,你被炸死了!!!\n" ); DisplayBoard(mine, row, col); break ; } else { int count = (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' ; show[x][y] = count + '0' ; DisplayBoard(show, row, col); win++; } } else { printf ("非法输入!!!" ); } } }
游戏测试 这里将ROW
和COL
更改成3
,EASY_COUNT
改成了5
进行测试。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 ************************************ *****1. 开始游戏 0. 退出游戏****** ************************************ 请选择:>>>1 0 1 2 3 1 1 1 0 2 1 0 1 3 0 0 1 0 1 2 3 1 * * *2 * * *3 * * *请输入排查雷的坐标:>>>1 1 啊哦,你被炸死了!!! 0 1 2 3 1 1 1 0 2 1 0 1 3 0 0 1 ************************************ *****1. 开始游戏 0. 退出游戏****** ************************************ 请选择:>>>1 0 1 2 3 1 0 1 1 2 1 0 1 3 0 1 0 0 1 2 3 1 * * *2 * * *3 * * *请输入排查雷的坐标:>>>2 2 0 1 2 3 1 * * *2 * 5 *3 * * *请输入排查雷的坐标:>>>1 1 0 1 2 3 1 2 * *2 * 5 *3 * * *请输入排查雷的坐标:>>>3 1 0 1 2 3 1 2 * *2 * 5 *3 2 * *请输入排查雷的坐标:>>>3 3 0 1 2 3 1 2 * *2 * 5 *3 2 * 2 恭喜你,历经千辛万苦排雷成功! ************************************ *****1. 开始游戏 0. 退出游戏****** ************************************ 请选择:>>>
写在后面 总的来说,这个程序除游戏基本框架(菜单框架等等)外,只用了四个功能函数,实现了基本的扫雷程序,但是游戏还是非常鸡肋
的,在真正的扫雷游戏中,当玩家选择的坐标周围八个坐标均没有雷时雷盘会之间展开,这还是很必要的,因为当扫雷的棋盘很大时,一个一个的选择坐标实属太没有游戏体验了。
加入递归展开后,游戏胜利判断条件也需要改变,相对复杂,所以写在了下一篇博客,欢迎各位大佬检阅、批评和指正,非常感谢!!!