1.游戏简介
《扫雷》是一款大众类的益智小游戏。游戏目标是在最短的时间内根据点击格子出现的数字找出所有非雷格子,同时避免踩雷,踩到一个雷即全盘皆输。
2.扫雷基本逻辑
- 生成雷区
- 打印棋盘
- 玩家扫雷
- 玩家标记雷
- 判断输赢
2.1 打印棋盘
2.1.1棋盘初始化
棋盘的形式可以用二维数组来表示,要先创建一个9*9的二维数组并初始化
keys:为了后面程序书写方便,我们用两个11*11的二维数组来表示雷区和玩家界面,用11*11方便计算坐标周围八个位置雷的数量,用mine[][]表示雷区,用show[][]表示玩家界面.
代码如下:`
//初始化棋盘 void InitBoard(char board[ROWS][COLS], int rows, int cols,char flag) { for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { board[i][j] = flag; } } }
2.1.2展示棋盘
初始化完成后,我们需要将棋盘展示给玩家,玩家界面用’ * '表示,该位置没有被排除.
代码如下:
//展示棋盘 void DisBoard(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 <= row; j++) { printf("%c ", board[i][j]); } printf("\n"); } }
初始化后的玩家界面如下图所示:
2.2 生成雷区
每次游玩游戏之前都要随机生成雷区,我们需要随机生成扫雷的坐标,因此可以用rand()函数随机生成x,y坐标;并且用字符’1’代表雷区,字符’0’代表安全区.
keys:在正确使用rand()之前,要在main()函数里使用srand()函数来保证坐标随机性.
下面为生成雷区的代码:
//生成雷区 '1'为雷 void Mine(char board[ROWS][COLS], int row, int col) { //x,y为随机生成坐标 int x = 0; int y = 0; int count = 0; //记录生成雷的数量 while (1) { x = (rand() % row) + 1; y = (rand() % col) + 1; if (board[x][y] == '0')//该坐标没有雷,生成雷 { board[x][y] = '1'; count++; if (count == GAMELEVEL) { break; } } } }`
效果如下图所示**:1代表雷,0代表安全区**
3. 玩家扫雷
玩家扫雷主要分为五个步骤:
- 接收玩家扫雷的坐标.
- 判断该位置是否为雷,若是雷,踩雷,游戏结束
- 该位置不是雷,将该位置展开,并计算周围雷区的数量
- 若周围没有雷,自动展开周围位置.
- 实现标记雷
3.1 接收坐标
由于我们用11*11二维数组,导致第0行,第10行,没有用到,1~9行棋盘恰好为我们输入坐标的逻辑.因此直接接收坐标即可.
3.2 判断是否踩雷
接收坐标(x,y)后,我们用mine[x][y],去判断是不是雷,然后在show[x][y]展开,如果踩雷游戏直接结束.
3.3 计算周围雷数量
用遍历的方法,计算坐标(x,y)的数量,并且将数量标记在show[x][y],用数量来推测雷的位置.
3.4自动展开
若排查坐标(x,y)周围没有雷,则将周围坐标展开,并再次判断周围坐标的四周有没有雷,有雷则不展开.没有雷,再次将其四周展开.因此我们可以用递归的逻辑实现这个功能.
3.5 标记雷(‘!’)
为了方便我们后续继续排雷,我们支持标记雷的功能,输入(-x,-y),即可标记雷.
3.6代码逻辑
//玩家排雷逻辑 void Player(char show[ROWS][COLS], char mine[ROWS][COLS], int row, int col) { //输入坐标 int x = 0; int y = 0; int protect = 1; while (1) { printf("请输入需要排雷的坐标: "); scanf("%d %d", &x, &y); while (getchar()!='\n'); //判断坐标是否合法 if ((x >= 1 && x <= row) && (y >= 1 && y <= col)) { //该位置没有被排查过 if (show[x][y] == '*') { //该位置有雷 if (mine[x][y] == '1') { if (protect==1) { printf("你已踩雷,请再次排雷!"); continue; } else { DisBoard(mine, ROW, COL);//打印雷区 printf("很遗憾,你被炸死!\n"); break; } } else //该位置没有雷,自动排雷 { protect = 0; AutoFindMine(show, mine, x, y); if (IsWin(show, mine, ROW, COL)) { system("cls"); DisBoard(show, ROW, COL); //打印棋盘 } else { DisBoard(mine, ROW, COL); //打印雷区 printf("恭喜你,找到了所有的雷!\n"); break; } } } else //该位置被排查过 { printf("该位置已经被排查过,请重输!\n"); continue; } } else if((-x >= 1 && -x <= row) && (-y >= 1 && -y <= col)) //标记 { MarkMine(show,mine ,-x, -y); } else { printf("输入坐标不在范围内,请重输!\n"); } } } //计算雷的数量 int FindMineNum(char mine[ROWS][COLS], int x, int y) { return (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]-'0'*8); } //自动翻开周围雷区 void AutoFindMine(char show[ROWS][COLS], char mine[ROWS][COLS], int x, int y) { //坐标传进来,排八个坐标 if (x >= 1 && x <= ROW && y >= 1 && y <= COL) { //没有被排查过 //if (show[x][y] == '*') //{ int ret = FindMineNum(mine, x, y); if (ret == 0) //该坐标周围没雷,自动翻开用‘0’表示 { show[x][y] = ' '; } else { show[x][y] = '0' + ret; return; } //} if (show[x - 1][y - 1] == '*') { AutoFindMine(show, mine, x - 1, y - 1); } if (show[x - 1][y] == '*') { AutoFindMine(show, mine, x - 1, y); } if (show[x - 1][y] == '*') { AutoFindMine(show, mine, x - 1, y); } if (show[x][y-1] == '*') { AutoFindMine(show, mine, x, y - 1); } if (show[x][y + 1] == '*') { AutoFindMine(show, mine, x, y + 1); } if (show[x+1][y - 1] == '*') { AutoFindMine(show, mine, x + 1, y - 1); } if (show[x + 1][y] == '*') { AutoFindMine(show, mine, x + 1, y); } if (show[x + 1][y+1] == '*') { AutoFindMine(show, mine, x + 1, y + 1); } } //雷区标记函数 '!'标记 void MarkMine(char show[ROWS][COLS], char mine[ROWS][COLS], int x, int y) { //该位置为 * 才可以标记为雷 int mark_count = GAMELEVEL; int mark = 0; printf("1:标记雷\n"); printf("0:取消标记雷\n"); printf("请输入1/0: "); scanf("%d", &mark); switch (mark) { case 1: { if (show[x][y] == '*')//未排过雷 { if (mark_count != 0) //标记数小于雷数 { show[x][y] = '!'; mark_count--; DisBoard(show, ROW, COL); } else { printf("已经标记%d个雷,不能继续标记!", GAMELEVEL); } } else { printf("该位置已经被排查,不能标记!\n"); } break; } case 0: { if (show[x][y] == '!') { show[x][y] = '*'; mark_count++; DisBoard(show, ROW, COL); } else { printf("该位置不能被取消,请重新标记"); } break; } } }
效果如下图所示:
4.判断输赢
当show[][]数组的所有非雷的位置均被翻开,扫雷即成功.
代码如下所示:
//判断输赢函数 int IsWin(char show[ROWS][COLS], char mine[ROWS][COLS],int row,int col) { //如果空格加数字等于ROW*COL-GAMELEVEL 扫雷成功! int i = 0; int j = 0; int iswin = 0; for (i = 1; i <= row; i++) { for (j = 1; j <= col; j++) { if (show[i][j] == ' ' || (show[i][j] >= '0' && show[i][j] <= '9')) { iswin++; } } } if (iswin == (row * col - GAMELEVEL))//游戏结束 { return 0; } else { return 1;//游戏继续 } }
5. 总工程
5.1 game.h
#pragma once #include<stdio.h> #include<stdlib.h> #include<time.h> #include<Windows.h> #define ROW 9 #define COL 9 #define ROWS ROW+2 #define COLS COL+2 #define GAMELEVEL 10 //初始化棋盘 void InitBoard(char board[ROWS][COLS],int rows, int cols,char flag); //展示棋盘 void DisBoard(char board[ROWS][COLS], int row, int col); //生成雷区 '1'为雷 void Mine(char board[ROWS][COLS], int row, int col); //扫雷 void Player(char show[ROWS][COLS], char mine[ROWS][COLS],int row, int col); //计算雷数量 int FindMineNum(char mine[ROWS][COLS], int x, int y); //自动翻开周围雷区 void AutoFindMine(char show[ROWS][COLS], char mine[ROWS][COLS],int x, int y); //雷区标记 void MarkMine(char show[ROWS][COLS], char mine[ROWS][COLS], int x, int y); //判断输赢函数 int IsWin(char show[ROWS][COLS], char mine[ROWS][COLS], int row, int col);
5.2 text.c
#define _CRT_SECURE_NO_WARNINGS 1 #include"game.h" //菜单 void menu() { printf("*****************************\n"); printf("******* 1.Play ********\n"); printf("******* 0.Exit ********\n"); printf("*****************************\n"); } void game() { char show[ROWS][COLS] = { 0 }; // 展示给用户的界面 char mine[ROWS][COLS] = { 0 }; //记录雷区 //初始化数组 InitBoard(show,ROWS,COLS,'*'); //初始化棋盘 '*' InitBoard(mine, ROWS, COLS,'0');//初始化雷区 '0' Mine(mine, ROW, COL); //生成雷区 DisBoard(show, ROW, COL); //打印棋盘 //DisBoard(mine, ROW, COL); //展示雷区 Player(show, mine, ROW, COL); //玩游戏 } int main() { srand((unsigned int)time(NULL)); int inpute = 0; do { menu(); //打印菜单 scanf("%d", &inpute); switch (inpute) { case 1: game();//扫雷游戏 break; case 0: printf("退出游戏\n"); break; default: printf("输入错误,请重输!\n"); } } while (inpute); }
5.3 game.c
#define _CRT_SECURE_NO_WARNINGS 1 #include"game.h" //初始化棋盘 void InitBoard(char board[ROWS][COLS], int rows, int cols,char flag) { for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { board[i][j] = flag; } } } //展示棋盘 void DisBoard(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 <= row; j++) { printf("%c ", board[i][j]); } printf("\n"); } } //生成雷区 '1'为雷 void Mine(char board[ROWS][COLS], int row, int col) { //x,y为随机生成坐标 int x = 0; int y = 0; int count = 0; //记录生成雷的数量 while (1) { x = (rand() % row) + 1; y = (rand() % col) + 1; if (board[x][y] == '0')//该坐标没有雷,生成雷 { board[x][y] = '1'; count++; if (count == GAMELEVEL) { break; } } } } //玩游戏 void Player(char show[ROWS][COLS], char mine[ROWS][COLS], int row, int col) { //输入坐标 int x = 0; int y = 0; int protect = 1; while (1) { printf("请输入需要排雷的坐标: "); scanf("%d %d", &x, &y); while (getchar()!='\n'); //判断坐标是否合法 if ((x >= 1 && x <= row) && (y >= 1 && y <= col)) { //该位置没有被排查过 if (show[x][y] == '*') { //该位置有雷 if (mine[x][y] == '1') { if (protect==1) { //Mine(mine,ROW,COL);//重新生成雷区再排 //DisBoard(show, ROW, COL); //再次打印新的棋盘 printf("你已踩雷,请再次排雷!"); continue; } else { DisBoard(mine, ROW, COL);//打印雷区 printf("很遗憾,你被炸死!\n"); break; } } else //该位置没有雷,自动排雷 { protect = 0; AutoFindMine(show, mine, x, y); if (IsWin(show, mine, ROW, COL)) { system("cls"); DisBoard(show, ROW, COL); //打印棋盘 } else { DisBoard(mine, ROW, COL); //打印雷区 printf("恭喜你,找到了所有的雷!\n"); break; } } } else //该位置被排查过 { printf("该位置已经被排查过,请重输!\n"); continue; } } else if((-x >= 1 && -x <= row) && (-y >= 1 && -y <= col)) //标记 { MarkMine(show,mine ,-x, -y); } else { printf("输入坐标不在范围内,请重输!\n"); } } } //自动翻开周围雷区 void AutoFindMine(char show[ROWS][COLS], char mine[ROWS][COLS], int x, int y) { //坐标传进来,排八个坐标 if (x >= 1 && x <= ROW && y >= 1 && y <= COL) { //没有被排查过 //if (show[x][y] == '*') //{ int ret = FindMineNum(mine, x, y); if (ret == 0) //该坐标周围没雷,自动翻开用‘0’表示 { show[x][y] = ' '; } else { show[x][y] = '0' + ret; return; } //} if (show[x - 1][y - 1] == '*') { AutoFindMine(show, mine, x - 1, y - 1); } if (show[x - 1][y] == '*') { AutoFindMine(show, mine, x - 1, y); } if (show[x - 1][y] == '*') { AutoFindMine(show, mine, x - 1, y); } if (show[x][y-1] == '*') { AutoFindMine(show, mine, x, y - 1); } if (show[x][y + 1] == '*') { AutoFindMine(show, mine, x, y + 1); } if (show[x+1][y - 1] == '*') { AutoFindMine(show, mine, x + 1, y - 1); } if (show[x + 1][y] == '*') { AutoFindMine(show, mine, x + 1, y); } if (show[x + 1][y+1] == '*') { AutoFindMine(show, mine, x + 1, y + 1); } } // 该坐标没有被排查过,排查过退出递归 //先计算该坐标周围有没有雷,没有雷,赋空格。有雷退出递归 } //计算雷的数量 int FindMineNum(char mine[ROWS][COLS], int x, int y) { return (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]-'0'*8); } //雷区标记函数 '!'标记 void MarkMine(char show[ROWS][COLS], char mine[ROWS][COLS], int x, int y) { //该位置为 * 才可以标记为雷 int mark_count = GAMELEVEL; int mark = 0; printf("1:标记雷\n"); printf("0:取消标记雷\n"); printf("请输入1/0: "); scanf("%d", &mark); switch (mark) { case 1: { if (show[x][y] == '*')//未排过雷 { if (mark_count != 0) //标记数小于雷数 { show[x][y] = '!'; mark_count--; DisBoard(show, ROW, COL); } else { printf("已经标记%d个雷,不能继续标记!", GAMELEVEL); } } else { printf("该位置已经被排查,不能标记!\n"); } break; } case 0: { if (show[x][y] == '!') { show[x][y] = '*'; mark_count++; DisBoard(show, ROW, COL); } else { printf("该位置不能被取消,请重新标记"); } break; } } } //判断输赢函数 int IsWin(char show[ROWS][COLS], char mine[ROWS][COLS],int row,int col) { //如果空格加数字等于ROW*COL-GAMELEVEL 扫雷成功! int i = 0; int j = 0; int iswin = 0; for (i = 1; i <= row; i++) { for (j = 1; j <= col; j++) { if (show[i][j] == ' ' || (show[i][j] >= '0' && show[i][j] <= '9')) { iswin++; } } } if (iswin == (row * col - GAMELEVEL))//游戏结束 { return 0; } else { return 1;//游戏继续 } }
源代码链接: link
以上就是扫雷的简单版本,还有很多地方需要优化,后续会继续更新,如果有更多问题,希望大佬在评论区指点!💖