扫雷小游戏(优化)
相信我!与其他扫雷博客不一样!!
-
- 代码思路
- 优化拓展雷
-
- ==注意==
- game.h
- game.c
- test.c
!
代码思路
传统的扫雷如上图所示,对于我们而言,如何写代码,从哪方面入手,我们可以思考一下传统扫雷是如何做到,(除了用鼠标点之外)其他我们都应该做到相同!
首先,明确一点,肯定是二维数组,至于是整形数组还是字符数组,还有待商榷,玩过传统扫雷的都知道,我们扫某个坐标时,当那个坐标是雷的时候,就game over了,当不是雷的时候,那上面是标了数字的,该数字X的含义是,除了X外其余八个坐标中一共有几个雷。
所以,假如我们只用一个二维数组,让其即表示雷,又可以表示坐标附近有几个雷,显然是做不到的,因此,我们可以用两个数组,一个设置雷,一个用于给用户展示,因为给用户展示要不是我们扫雷没扫到雷,要不就是直接爆掉了,而在玩的时候给用户展示的我们都可以用show数组,最终扫到雷的时候,我们可以给用户看我们设置雷的那个雷盘。
关于字符数组
为了保持未扫雷的神秘性,我们对于未扫的区域应该给一个字符’‘,至于雷,我们可以随便用任何字符或者数字,但是为了和之前的字符保持操作的一致性,我们对于雷应该用字符’1‘,不是雷区用字符’0‘,至于为什么用这个两个字符,是为了方便我们统计每个坐标附近有多少个雷,我们统计坐标附近有多少个雷的时候,直接这八个坐标的值相加后减去字符0就可以得到该坐标附近有几个雷了。
关于雷盘大小
无非就是一些边界问题,以99的扫雷为例子,当我们坐标是(1,1)的时候,左上角是没有坐标的,这个时候我们计算(1,1)附近雷的个数很难计算,为了保持操作的一致性,我们可以给行和列分别加上两行并且加的那两行不能布置雷,因此如果我们是9*9的扫雷,我们布置的行和列都应该是11
优化一加了system("cls");
这个函数表示的是“清空屏幕”,没错,就是清空屏幕,这个可以我们一种错觉,我们始终是在一块雷盘上扫雷
优化二
加上了拓展雷的功能,很多的扫雷之所以与传统扫雷玩起来感觉不同就是因为没有这个功能,何谓拓展?
类似于这样,我点一个空格,周围连续的只要是空格都会显示出来,这个功能的代码我可能写的比较搓,但是功能有了hh
优化拓展雷
//拓展雷void ExtendBoard3(char board[ROWS3][COLS3], char show[ROWS3][COLS3], int x, int y){if (used3[x][y])return;used3[x][y] = 1;if (board[x - 1][y - 1] == '0' &&board[x - 1][y] == '0' &&board[x - 1][y + 1] == '0' &&board[x][y - 1] == '0' &&board[x][y + 1] == '0' &&board[x + 1][y - 1] == '0' &&board[x + 1][y] == '0' &&board[x + 1][y + 1] == '0'){show[x - 1][y - 1] = '0' + SumMine3(board, x - 1, y - 1);show[x - 1][y] = '0' + SumMine3(board, x - 1, y);show[x - 1][y + 1] = '0' + SumMine3(board, x - 1, y + 1);show[x][y - 1] = '0' + SumMine3(board, x, y - 1);show[x][y + 1] = '0' + SumMine3(board, x, y + 1);show[x + 1][y - 1] = '0' + SumMine3(board, x + 1, y - 1);show[x + 1][y] = '0' + SumMine3(board, x + 1, y);show[x + 1][y + 1] = '0' + SumMine3(board, x + 1, y + 1);//下面函数的判断一定要写,因为下面的递归中还会调用SumMine3//函数,那么就有可能造成内存泄露if (x - 1 >= 1 && x - 1 <= ROW3 && y - 1 >= 1 && y - 1 <= ROW3)ExtendBoard3(board, show, x - 1, y - 1);if (x - 1 >= 1 && x - 1 <= ROW3 && y >= 1 && y <= ROW3)ExtendBoard3(board, show, x - 1, y);if (x - 1 >= 1 && x - 1 <= ROW3 && y + 1 >= 1 && y + 1 <= ROW3)ExtendBoard3(board, show, x - 1, y + 1);if (x >= 1 && x <= ROW3 && y - 1 >= 1 && y - 1 <= ROW3)ExtendBoard3(board, show, x, y - 1);if (x >= 1 && x <= ROW3 && y + 1 >= 1 && y + 1 <= ROW3)ExtendBoard3(board, show, x, y + 1);if (x + 1 >= 1 && x + 1 <= ROW3 && y - 1 >= 1 && y - 1 <= ROW3)ExtendBoard3(board, show, x + 1, y - 1);if (x + 1 >= 1 && x + 1 <= ROW3 && y >= 1 && y <= ROW3)ExtendBoard3(board, show, x + 1, y);if (x + 1 >= 1 && x + 1 <= ROW3 && y + 1 >= 1 && y + 1 <= ROW3)ExtendBoard3(board, show, x + 1, y + 1);}return;}
关于这个拓展雷的函数浅谈一下:
观察上面传统扫雷的图片可以知道,如果以我们正在扫雷的这个坐标为中心,如果其周围八个坐标都不是雷,那么便可以显示了,然后又以另外八个为中心,如果以他们各自为中心的九宫格都不是雷的话,那么继续拓展,这就是一个递归的过程,但是这边我们需要注意一个问题。
我们稍微不注意,就会造成无限递归的一个状态,因此必须创造一个条件,当该函数满足某个条件的时候才能执行,因此我又创造了一个used函数,每次游戏开始时,其值都会变成0。
游戏代码如下(我多加了两个不同难度的,只需要掌握一个就可以了,其他两个我也是直接ctrl+c+v的hh)
注意
代码我相当于写了三份一样的,因为时不同的雷盘大小的,其实只需要看game1()就可以了,写后面两份根本不用很长时间,只是为了方便自己玩hhh
game.h
game.h #define _CRT_SECURE_NO_WARNINGS 1#pragma once #include#include#include#define ROW1 9#define COL1 9#define ROWS1 ROW1+2#define COLS1 COL1+2#define Count1 10int used1[ROWS1][COLS1];void IninBoard(char board[ROWS1][COLS1], int rows, int cols, char set);//初始化棋盘void PrintBoard(char board[ROWS1][COLS1], int row, int col);//打印棋盘 只需要打印中间的九行就列void ArrangeMine(char board[ROWS1][COLS1], int row, int col);//布置雷void FindeMIne(char board[ROWS1][COLS1], char show[ROWS1][COLS1], int row, int col);//排查雷void ExtendBoard(char show[ROWS1][COLS1], int x, int y);//拓展雷#define ROW2 16#define COL2 16#define ROWS2 ROW2+2#define COLS2 COL2+2#define Count2 40int used2[ROWS2][COLS2];void IninBoard(char board[ROWS2][COLS2], int rows, int cols, char set);//初始化棋盘void PrintBoard(char board[ROWS2][COLS2], int row, int col);//打印棋盘 只需要打印中间的九行就列void ArrangeMine(char board[ROWS2][COLS2], int row, int col);//布置雷void FindeMIne(char board[ROWS2][COLS2], char show[ROWS2][COLS2], int row, int col);//排查雷void ExtendBoard(char show[ROWS2][COLS2], int x, int y);//拓展雷#define ROW3 16#define COL3 30#define ROWS3 ROW3+2#define COLS3 COL3+2#define Count3 99int used3[ROWS3][COLS3];void IninBoard(char board[ROWS3][COLS3], int rows, int cols, char set);//初始化棋盘void PrintBoard(char board[ROWS3][COLS3], int row, int col);//打印棋盘 只需要打印中间的九行就列void ArrangeMine(char board[ROWS3][COLS3], int row, int col);//布置雷void FindeMIne(char board[ROWS3][COLS3], char show[ROWS3][COLS3], int row, int col);//排查雷void ExtendBoard(char show[ROWS3][COLS3], int x, int y);//拓展雷
game.c
geme.c #define _CRT_SECURE_NO_WARNINGS 1#include"game.h"void IninBoard1(char board[ROWS1][COLS1], int rows, int cols, char set)//初始化{for (int i = 0; i < rows; i++)for (int j = 0; j < cols; j++)board[i][j] = set;}void PrintBoard1(char board[ROWS1][COLS1], int row, int col){//打印棋盘的时候最好把每行每列的行号以及列号打印上去for (int i = 0; i <= row; i++)printf("%2d ", i);printf("\n");for (int i = 1; i <= row; i++){printf("%2d ", i);for (int j = 1; j <= col; j++){printf("%2c ", board[i][j]);}printf("\n");}}void ArrangeMine1(char board[ROWS1][COLS1], int row, int col){int ret = Count1;while (ret){int x = rand() % row + 1;int y = rand() % col + 1;if (board[x][y] == '0'){board[x][y] = '1';ret--;}else{;}}}int SumMine1(char board[ROWS1][COLS1], int x, int y){return board[x - 1][y - 1] +board[x - 1][y] +board[x - 1][y + 1] +board[x][y - 1] +board[x][y + 1] +board[x + 1][y - 1] +board[x + 1][y] +board[x + 1][y + 1] - 8 * '0';}void ExtendBoard1(char board[ROWS1][COLS1],char show[ROWS1][COLS1], int x, int y){if (used1[x][y])return;used1[x][y] = 1;if (board[x-1][y-1]=='0'&&board[x-1][y] == '0'&&board[x-1][y+1] == '0'&&board[x][y-1] == '0'&&board[x][y+1] == '0'&&board[x+1][y-1] == '0'&&board[x+1][y] == '0'&&board[x+1][y+1] == '0'){show[x - 1][y - 1] = '0'+ SumMine1(board,x-1,y-1);show[x - 1][y] = '0' + SumMine1(board, x - 1, y);show[x - 1][y + 1] = '0' + SumMine1(board, x - 1, y + 1);show[x][y - 1] = '0' + SumMine1(board, x , y - 1);show[x][y + 1] = '0' + SumMine1(board, x , y + 1);show[x + 1][y - 1] = '0' + SumMine1(board, x + 1, y - 1);show[x + 1][y] = '0' + SumMine1(board, x + 1, y );show[x + 1][y + 1] = '0' + SumMine1(board, x + 1, y + 1);if(x-1>=1&&x-1<=ROW1&&y-1>=1&&y-1<=ROW1)ExtendBoard1(board, show, x - 1, y - 1);if (x - 1 >= 1 && x - 1 <= ROW1 && y >= 1 && y <= ROW1)ExtendBoard1(board, show, x - 1, y);if (x - 1 >= 1 && x - 1 <= ROW1 && y + 1 >= 1 && y + 1 <= ROW1)ExtendBoard1(board, show, x - 1, y + 1);if (x >= 1 && x <= ROW1 && y - 1 >= 1 && y - 1 <= ROW1)ExtendBoard1(board, show, x, y - 1);if (x >= 1 && x <= ROW1 && y +1 >= 1 && y + 1 <= ROW1)ExtendBoard1(board, show, x, y + 1);if (x + 1 >= 1 && x + 1 <= ROW1 && y - 1 >= 1 && y - 1 <= ROW1)ExtendBoard1(board, show, x + 1, y - 1);if (x + 1 >= 1 && x + 1 <= ROW1 && y >= 1 && y <= ROW1)ExtendBoard1(board, show, x + 1, y);if (x + 1 >= 1 && x + 1 <= ROW1 && y + 1 >= 1 && y + 1 <= ROW1)ExtendBoard1(board, show, x + 1, y + 1);}return;}void FindeMIne1(char board[ROWS1][COLS1], char show[ROWS1][COLS1], int row, int col)//排查雷{int sum = 0;while (1){int x = 0, y = 0;printf("请输入需要排查的坐标->\n");scanf("%d%d", &x, &y);//还应该判断输入的坐标是否合法if (x >= 1 && x <= ROW1 && y >= 1 && y <= COL1){if (board[x][y] == '1'){system("cls");printf("很遗憾!游戏到此结束!下面是雷的分布图’1‘表示雷\n");PrintBoard1(board, row, col);break;}else{int cur = SumMine1(board, x, y);//计算以x,y坐标为中心的雷总的个数show[x][y] = cur + '0';ExtendBoard1(board,show ,x, y);system("cls");PrintBoard1(show, row, col);sum++;}if (sum == row * col - Count1){system("cls");printf("恭喜你!排雷成功!下面是雷的分布图’1‘表示雷\n");PrintBoard1(board, row, col);break;}}else{printf("输入的坐标非法\n");}}}void IninBoard2(char board[ROWS2][COLS2], int rows, int cols, char set){for (int i = 0; i < rows; i++)for (int j = 0; j < cols; j++)board[i][j] = set;}void PrintBoard2(char board[ROWS2][COLS2], int row, int col){//打印棋盘的时候最好把每行每列的行号以及列号打印上去for (int i = 0; i <= row; i++)printf("%2d ", i);printf("\n");for (int i = 1; i <= row; i++){printf("%2d ", i);for (int j = 1; j <= col; j++){printf("%2c ", board[i][j]);}printf("\n");}}void ArrangeMine2(char board[ROWS2][COLS2], int row, int col){int ret = Count2;while (ret){int x = rand() % row + 1;int y = rand() % col + 1;if (board[x][y] == '0'){board[x][y] = '1';ret--;}else{;}}}int SumMine2(char board[ROWS2][COLS2], int x, int y){return board[x - 1][y - 1] +board[x - 1][y] +board[x - 1][y + 1] +board[x][y - 1] +board[x][y + 1] +board[x + 1][y - 1] +board[x + 1][y] +board[x + 1][y + 1] - 8 * '0';}void ExtendBoard2(char board[ROWS2][COLS2], char show[ROWS2][COLS2], int x, int y){if (used2[x][y])return;used2[x][y] = 1;if (board[x - 1][y - 1] == '0' &&board[x - 1][y] == '0' &&board[x - 1][y + 1] == '0' &&board[x][y - 1] == '0' &&board[x][y + 1] == '0' &&board[x + 1][y - 1] == '0' &&board[x + 1][y] == '0' &&board[x + 1][y + 1] == '0'){show[x - 1][y - 1] = '0' + SumMine2(board, x - 1, y - 1);show[x - 1][y] = '0' + SumMine2(board, x - 1, y);show[x - 1][y + 1] = '0' + SumMine2(board, x - 1, y + 1);show[x][y - 1] = '0' + SumMine2(board, x, y - 1);show[x][y + 1] = '0' + SumMine2(board, x, y + 1);show[x + 1][y - 1] = '0' + SumMine2(board, x + 1, y - 1);show[x + 1][y] = '0' + SumMine2(board, x + 1, y);show[x + 1][y + 1] = '0' + SumMine2(board, x + 1, y + 1);if (x - 1 >= 1 && x - 1 <= ROW2 && y - 1 >= 1 && y - 1 <= ROW2)ExtendBoard2(board, show, x - 1, y - 1);if (x - 1 >= 1 && x - 1 <= ROW2 && y >= 1 && y <= ROW2)ExtendBoard2(board, show, x - 1, y);if (x - 1 >= 1 && x - 1 <= ROW2 && y + 1 >= 1 && y + 1 <= ROW2)ExtendBoard2(board, show, x - 1, y + 1);if (x >= 1 && x <= ROW2 && y - 1 >= 1 && y - 1 <= ROW2)ExtendBoard2(board, show, x, y - 1);if (x >= 1 && x <= ROW2 && y + 1 >= 1 && y + 1 <= ROW2)ExtendBoard2(board, show, x, y + 1);if (x + 1 >= 1 && x + 1 <= ROW2 && y - 1 >= 1 && y - 1 <= ROW2)ExtendBoard2(board, show, x + 1, y - 1);if (x + 1 >= 1 && x + 1 <= ROW2 && y >= 1 && y <= ROW2)ExtendBoard2(board, show, x + 1, y);if (x + 1 >= 1 && x + 1 <= ROW2 && y + 1 >= 1 && y + 1 <= ROW2)ExtendBoard2(board, show, x + 1, y + 1);}return;}void FindeMIne2(char board[ROWS2][COLS2], char show[ROWS2][COLS2], int row, int col)//排查雷{int sum = 0;while (1){int x = 0, y = 0;printf("请输入需要排查的坐标->\n");scanf("%d%d", &x, &y);//还应该判断输入的坐标是否合法if (x >= 1 && x <= ROW2 && y >= 1 && y <= COL2){if (board[x][y] == '1'){system("cls");printf("很遗憾!游戏到此结束!下面是雷的分布图’1‘表示雷\n");PrintBoard2(board, row, col);break;}else{int cur = SumMine2(board, x, y);//计算以x,y坐标为中心的雷总的个数show[x][y] = cur + '0';ExtendBoard2(board, show, x, y);system("cls");PrintBoard2(show, row, col);sum++;}if (sum == row * col - Count2){system("cls");printf("恭喜你!排雷成功!下面是雷的分布图’1‘表示雷\n");PrintBoard2(board, row, col);break;}}else{printf("输入的坐标非法\n");}}}void IninBoard3(char board[ROWS3][COLS3], int rows, int cols, char set){for (int i = 0; i < rows; i++)for (int j = 0; j < cols; j++)board[i][j] = set;}void PrintBoard3(char board[ROWS3][COLS3], int row, int col){//打印棋盘的时候最好把每行每列的行号以及列号打印上去for (int i = 0; i <= col; i++)printf("%2d ", i);printf("\n");for (int i = 1; i <= row; i++){printf("%2d ", i);for (int j = 1; j <= col; j++){printf("%2c ", board[i][j]);}printf("\n");}}void ArrangeMine3(char board[ROWS3][COLS3], int row, int col){int ret = Count3;while (ret){int x = rand() % row + 1;int y = rand() % col + 1;if (board[x][y] == '0'){board[x][y] = '1';ret--;}else{;}}}int SumMine3(char board[ROWS3][COLS3], int x, int y){return board[x - 1][y - 1] +board[x - 1][y] +board[x - 1][y + 1] +board[x][y - 1] +board[x][y + 1] +board[x + 1][y - 1] +board[x + 1][y] +board[x + 1][y + 1] - 8 * '0';}void ExtendBoard3(char board[ROWS3][COLS3], char show[ROWS3][COLS3], int x, int y){if (used3[x][y])return;used3[x][y] = 1;if (board[x - 1][y - 1] == '0' &&board[x - 1][y] == '0' &&board[x - 1][y + 1] == '0' &&board[x][y - 1] == '0' &&board[x][y + 1] == '0' &&board[x + 1][y - 1] == '0' &&board[x + 1][y] == '0' &&board[x + 1][y + 1] == '0'){show[x - 1][y - 1] = '0' + SumMine3(board, x - 1, y - 1);show[x - 1][y] = '0' + SumMine3(board, x - 1, y);show[x - 1][y + 1] = '0' + SumMine3(board, x - 1, y + 1);show[x][y - 1] = '0' + SumMine3(board, x, y - 1);show[x][y + 1] = '0' + SumMine3(board, x, y + 1);show[x + 1][y - 1] = '0' + SumMine3(board, x + 1, y - 1);show[x + 1][y] = '0' + SumMine3(board, x + 1, y);show[x + 1][y + 1] = '0' + SumMine3(board, x + 1, y + 1);if (x - 1 >= 1 && x - 1 <= ROW3 && y - 1 >= 1 && y - 1 <= ROW3)ExtendBoard3(board, show, x - 1, y - 1);if (x - 1 >= 1 && x - 1 <= ROW3 && y >= 1 && y <= ROW3)ExtendBoard3(board, show, x - 1, y);if (x - 1 >= 1 && x - 1 <= ROW3 && y + 1 >= 1 && y + 1 <= ROW3)ExtendBoard3(board, show, x - 1, y + 1);if (x >= 1 && x <= ROW3 && y - 1 >= 1 && y - 1 <= ROW3)ExtendBoard3(board, show, x, y - 1);if (x >= 1 && x <= ROW3 && y + 1 >= 1 && y + 1 <= ROW3)ExtendBoard3(board, show, x, y + 1);if (x + 1 >= 1 && x + 1 <= ROW3 && y - 1 >= 1 && y - 1 <= ROW3)ExtendBoard3(board, show, x + 1, y - 1);if (x + 1 >= 1 && x + 1 <= ROW3 && y >= 1 && y <= ROW3)ExtendBoard3(board, show, x + 1, y);if (x + 1 >= 1 && x + 1 <= ROW3 && y + 1 >= 1 && y + 1 <= ROW3)ExtendBoard3(board, show, x + 1, y + 1);}return;}void FindeMIne3(char board[ROWS3][COLS3], char show[ROWS3][COLS3], int row, int col)//排查雷{int sum = 0;while (1){int x = 0, y = 0;printf("请输入需要排查的坐标->\n");scanf("%d%d", &x, &y);//还应该判断输入的坐标是否合法if (x >= 1 && x <= ROW3 && y >= 1 && y <= COL3){if (board[x][y] == '1'){system("cls");printf("很遗憾!游戏到此结束!下面是雷的分布图’1‘表示雷\n");PrintBoard3(board, row, col);break;}else{int cur = SumMine3(board, x, y);//计算以x,y坐标为中心的雷总的个数show[x][y] = cur + '0';ExtendBoard3(board, show, x, y);system("cls");PrintBoard3(show, row, col);sum++;}if (sum == row * col - Count3){system("cls");printf("恭喜你!排雷成功!下面是雷的分布图’1‘表示雷\n");PrintBoard3(board, row, col);break;}}else{printf("输入的坐标非法\n");}}}
test.c
test.c #define _CRT_SECURE_NO_WARNINGS 1#include"game.h"void game1(){char mine1[ROWS1][COLS1] = { 0 };//用来储存雷字符1表示雷字符0不表示雷char show1[ROWS1][COLS1] = { 0 };//用来计算雷//初始化棋盘IninBoard1(mine1, ROWS1, COLS1, '0');IninBoard1(show1, ROWS1, COLS1, '*'); //打印棋盘//布置雷ArrangeMine1(mine1, ROW1, COL1);PrintBoard1(mine1, ROW1, COL1);system("cls");PrintBoard1(show1, ROW1, COL1);//PrintBoard(mine, ROW, COL);FindeMIne1(mine1, show1, ROW1, COL1);}void game2(){char mine2[ROWS2][COLS2] = { 0 };//用来储存雷字符1表示雷字符0不表示雷char show2[ROWS2][COLS2] = { 0 };//用来计算雷//初始化棋盘IninBoard2(mine2, ROWS2, COLS2, '0');IninBoard2(show2, ROWS2, COLS2, '*');//打印棋盘//布置雷ArrangeMine2(mine2, ROW2, COL2);PrintBoard2(mine2, ROW2, COL2);system("cls");PrintBoard2(show2, ROW2, COL2);//PrintBoard(mine, ROW, COL);FindeMIne2(mine2, show2, ROW2, COL2);}void game3(){char mine3[ROWS3][COLS3] = { 0 };//用来储存雷字符1表示雷字符0不表示雷char show3[ROWS3][COLS3] = { 0 };//用来计算雷//初始化棋盘IninBoard3(mine3, ROWS3, COLS3, '0');IninBoard3(show3, ROWS3, COLS3, '*');//打印棋盘//布置雷ArrangeMine3(mine3, ROW3, COL3);PrintBoard3(mine3, ROW3, COL3);system("cls");PrintBoard3(show3, ROW3, COL3);//PrintBoard(mine, ROW, COL);FindeMIne3(mine3, show3, ROW3, COL3);}void menu(){printf("*\n");printf("*1,超级简单版本\n");printf("*2,进阶版本*\n");printf("*3,地狱模式*\n");printf("*0,EXIT*\n");}void test(){int input = 0;do{srand((unsigned)time(NULL));//随机数生成器menu();printf("请选择:>\n");scanf("%d", &input);switch (input){case 1:memset(used1, 0, sizeof(used1));game1();//扫雷break;case 2:memset(used2, 0, sizeof(used2));game2();break;case 3:memset(used3, 0, sizeof(used3));game3();break;case 0:printf("游戏结束!\n");break;default:printf("输入的值不合法,请重新输入\n");break;}} while (input);}int main(){test();return;}