C语言实战项目—扫雷小程序

扫雷游戏是微软自带的一款小游戏。扫雷游戏的玩法是,以9*9棋盘为例,棋盘上随机分布着10个地雷,玩家在棋盘上进行点击,如果被点击的格子是地雷,则玩家被炸“死”,游戏结束;如果被点击的格子上没有地雷,与被点击的格子相邻的格子(被点击格子的上下左右还有斜向,共八个格子)有地雷,则在被点击的格子上显示这些地雷的总数,如果与被点击的格子相邻的八个格子都没有地雷,则棋盘自动展开,直到与展开的格子相邻的格子有地雷才停止。此时最后被展开的格子显示其相邻格子共有的地雷数。

除上述功能外,如果玩家认为某个格子有地雷,则在该格子上插入旗子。后期如果确定某个插了旗子的格子没有地雷,则可以将旗子拔掉。

扫雷是一款集运气与逻辑的游戏,一般为了保证可玩性,不至于刚上来就游戏结束,所以还需确保玩家点击的第一步不是雷。

下面将使用C语言实现上述功能。因为初学C语言,对许多功能了解的不够深入,代码写的难免有瑕疵,如有问题,还望批评指正。

本例使用Win10系统下的 Visual Studio 2019 进行编写的,对于所有使用scanf()函数的文档,需在首行加入如下代码(必须是在首行,加入其他行无效):

#define _CRT_SECURE_NO_WARNINGS 1

游戏框架

对于任意游戏,都需要菜单来让玩家进行选择,其次还有游戏的主体部分。所以建立三个文件,分别为”game.c”(用于存放游戏中各个部分功能的函数),”game.h”(用于声明”game.c”中的函数),以及主文件”main.c”.

菜单部分

游戏开始,首先在屏幕上打印菜单,供玩家进行选择。程序根据玩家的选择进行下一步操作。菜单功能使用menu()函数进行实现,用户输入则用scanf()函数来接收。规定:菜单打印两个选项,当用户键入1时,则开始进行游戏(游戏部分由game()函数实现);当用户键入0时,则退出程序;若用户键入其他字符,则提示用户输入有误,需重新出入。因此可以使用switch()函数来实现此部分功能。具体实现代码如下:

1//扫雷游戏 2#include "game.h" 3 4int main(void) 5{ 6 int input; 7 srand((unsigned int)time(NULL)); 8 do 9 { 10 menu(); 11 printf("请选择:>"); 12 scanf("%d", &input); 13 switch (input) 14 { 15 case 1: 16 game(); 17 break; 18 case 0: 19 printf("退出游戏。\n"); 20 break; 21 default: 22 printf("输入错误,请重新输入.\n"); 23 break; 24 } 25 } while (input); 26 return 0; 27}

其中menu()代码如下:

1void menu() 2{ 3 printf("********************\n"); 4 printf("*1.play******0.exit*\n"); 5 printf("********************\n"); 6}

游戏部分

首先要生成两个棋盘,其中一个为后台,玩家看不见,用于存储地雷的信息和判断游戏的输赢。一个为对玩家显示的棋盘,供玩家进行操作。后台棋盘使用数组mineboard()实现,对玩家显示的棋盘用showboard()实现。

生成棋盘后,则需对棋盘进行初始化,规定:后台的棋盘用字符0表示没有地雷,用字符1表示有地雷;玩家的棋盘用*表示没有被点击,用#表示插入旗子。初始化则将后台的棋盘全部置为没有地雷,玩家棋盘全部置为*。初始化棋盘使用InitBoard()实现。

初始化棋盘后需要在后台棋盘上布置地雷同时向玩家展示玩家棋盘,布置地雷使用随机值布置10个地雷。布置地雷使用SetMine()函数,展示棋盘使用DisplayBoard()函数。

布置地雷后,开始游戏,为确保玩家第一次点击(由于程序限制,本例全部使用坐标输入代替玩家点击)不踩到地雷,所以需要对第一次点击进行判断,如果第一次刚好点到地雷,则将此处改为没有地雷,并在其他位置随机生成地雷,以确保地雷总数不变。此功能使用First_SafeMine()函数实现。

从第二步开始,每次都询问玩家的操作(是点击还是插旗还是拔旗)。同时进行输赢判断。从第二部开始为真正排雷过程,使用FindMine()函数进行实现。

输赢判断包括以下几个方面:

(1)玩家点到了地雷,游戏直接结束;

(2)玩家运气足够好,在棋盘上插得旗子数等于地雷数,并且这些旗子下刚好都有地雷,玩家胜利,游戏结束;

(3)玩家将棋盘上没有地雷的区域全部点开,玩家胜利,游戏结束。

对于玩家的操作,又分为以下几部分:

玩家点击:以输入坐标的形式代替点击,需判断坐标是否合法,如果坐标超出了棋盘范围,则提示玩家坐标超出范围并等待玩家重新输入;如果玩家输入的坐标已经被排查过(包括该位置为空,该位置上有数字,该位置上有旗子),则提示玩家该位置已经被排查过,并让玩家对照此三种情况进行检查然后等待玩家重新输入;如果坐标没有被排查过,则判断该位置是否有地雷,如果有,则游戏直接结束;如果没有地雷,则需判断该位置相邻棋盘是否有地雷,如果没有,则棋盘自动展开,直到相邻格子有地雷为止;如果相邻棋盘有地雷,则在该位置显示地雷数。如果棋盘已全部排查完,则提示玩家成功并打印后台棋盘然后退出游戏。

其中判断相邻棋盘是否有地雷用GetMineCount()函数实现,棋盘自动展开用OpenMine()实现,判断棋盘是否排查完使用Countmine()函数实现。

插入旗子:同玩家点击一样,需判断坐标是否合法以及插旗位置是否已经被排查过。此外,需判断插入旗子位置对应的真雷数,如果旗子数等于真雷数并且旗子对应的位置都是真雷,则认为玩家胜利。判断旗子对应的真雷数使用TrueMineCount()函数实现。

拔掉旗子:首先要判断棋盘是否有旗子,没有旗子则提示玩家棋盘上没有旗子同时退出拔旗操作,如果有旗子则判断坐标的合法性以及拔旗位置是否已经排查过或拔旗位置没有旗子。同时也需要判断剩余旗子位置对应的真雷数,如果旗子数等于真雷数并且旗子对应的位置都是真雷,则认为玩家胜利。

代码实现

下面则对上述各个函数进行实现。

在统计某处雷的个数时,对于边界还需判断是否越界,这样每次判断都会麻烦。因此把棋盘设置的比实际玩的棋盘大一圈,这一圈上不放置地雷,统计时可以省去判断边界的麻烦。

1.初始化棋盘:遍历棋盘,对后台的棋盘全部置0,对玩家看到的棋盘置*。函数可以接收棋盘和棋盘大小,同时接收相应的字符。

1//初始化棋盘 2void InitBoard(char board[ROWS][COLS], int cols, int rows, char set) 3{ 4 int i, j; 5 for (= 0; i < cols; i++) 6 { 7 for (= 0; j < rows; j++) 8 { 9 board[i][j] = set; 10 } 11 } 12}

2.显示棋盘:遍历玩家棋盘,将玩家棋盘的内容输出在屏幕上。为防止坐标轴与棋盘上信息混在一起,将其分开打印。

1//显示棋盘 2void DisplayBoard(char board[ROWS][COLS], int col, int row) 3{ 4 int i, j; 5 printf("    "); 6 for (= 1; j <= col; j++) 7 { 8 printf("%d ", j); 9 } 10 printf("\n\n"); 11 for (= 1; i <= col; i++) 12 { 13 printf("%d   ",i); 14 for (= 1; j <= row; j++) 15 { 16 printf("%c ",board[i][j]); 17 } 18 printf("\n"); 19 } 20}

生成的棋盘如下图所示:

![5X]Q0[%]6V66NAKGEU7}KD.png

      3.初始化地雷位置:使用随机函数在后台棋盘上生成10个地雷,规定字符1是地雷,字符0是安全地带。

1//初始化地雷的位置 2void SetMine(char board[ROWS][COLS], int col, int row) 3{ 4 int x, y; 5 int count = EASY_CONST; 6 while (count) 7 { 8= rand() % col + 1; 9= rand() % row + 1; 10 if (board[x][y] == '0') 11 { 12 board[x][y] = '1'; 13 count--; 14 } 15 } 16}

4.确保第一个不是地雷

1//保证第一个不踩雷 2void First_SafeMine(char mineboard[ROWS][COLS], char showboard[ROWS][COLS], int row, int col) 3{ 4 int x = 0; 5 int y = 0; 6 int ret = 1; 7 int count = 0; 8 int a, b; 9 printf("请输入要排查的坐标:"); 10 scanf("%d%d", &x, &y); 11 if (mineboard[x][y] == '1')//如果为雷,则改为没有雷 12 { 13 mineboard[x][y] = '0'; 14 while (ret)//随机生成一个雷 15 { 16= rand() % ROW + 1; 17= rand() % COL + 1; 18 if (mineboard[a][b] == '0') 19 { 20 mineboard[a][b] = '1'; 21 } 22 ret--; 23 } 24 } 25 count = GetMineCount(mineboard, x, y);//在上述步骤完成后,需统计第一个坐标周围雷的个数 26 showboard[x][y] = count + '0'; 27 OpenMine(mineboard, showboard, row, col, x, y);//周围没有雷需要自动展开棋盘 28 DisplayBoard(showboard, row, col); 29}

    5.统计某位置处周围雷的个数。

    雷在棋盘上是字符1,所以需判断字符1的个数。字符1在内存中存放的实际上是ASCII码值,所以可以用一个整型变量来统计有几个字符1.

1//统计坐标周围雷的个数 2int GetMineCount(const char mineboard[ROWS][COLS], int x, int y) 3{ 4 return mineboard[- 1][+ 1] + mineboard[x][+ 1] + mineboard[+ 1][+ 1] + 5 mineboard[- 1][y] + mineboard[+ 1][y] + 6 mineboard[- 1][- 1] + mineboard[x][- 1] + mineboard[+ 1][- 1] - 8 * '0'; 7}

6.棋盘自动展开:棋盘不展开的条件是某位置的周围有雷。所以先要判断是否有雷。如果雷的个数为0就展开,采用递归来实现。

1//展开无雷区:如果该坐标周围没有雷就自动展开。 2void OpenMine(const char mineboard[ROWS][COLS], char showboard[ROWS][COLS], int col, int row,int x,int y) 3{ 4 int ret = 0; 5 ret=GetMineCount(mineboard, x, y); 6 if (ret == 0) 7 { 8 showboard[x][y] = ' '; 9 if (> 0 && y + 1 <= col && showboard[x][+ 1] == '*') 10 { 11 OpenMine(mineboard, showboard, col, row, x, y + 1); 12 } 13 if (- 1 > 0 && y + 1 <= col && showboard[- 1][+ 1] == '*') 14 { 15 OpenMine(mineboard, showboard, col, row, x - 1, y + 1); 16 } 17 if (- 1 > 0 && y >0 && showboard[- 1][y] == '*') 18 { 19 OpenMine(mineboard, showboard, col, row, x - 1, y); 20 } 21 if (- 1 > 0 && y - 1 >0 && showboard[- 1][- 1] == '*') 22 { 23 OpenMine(mineboard, showboard, col, row, x - 1, y - 1); 24 } 25 if (> 0 && y - 1 > 0 && showboard[x][- 1] == '*') 26 { 27 OpenMine(mineboard, showboard, col, row, x, y - 1); 28 } 29 if (+ 1 <= row && y - 1 > 0 && showboard[+ 1][- 1] == '*') 30 { 31 OpenMine(mineboard, showboard, col, row, x + 1, y - 1); 32 } 33 if (+ 1 <= row && y > 0 && showboard[+ 1][y] == '*') 34 { 35 OpenMine(mineboard, showboard, col, row, x + 1, y); 36 } 37 if (+ 1 <=row && y + 1 <= col && showboard[+ 1][+ 1] == '*') 38 { 39 OpenMine(mineboard, showboard, col, row, x + 1, y + 1); 40 } 41 } 42 else 43 { 44 showboard[x][y] = '0' + ret; 45 } 46}

7.统计棋盘上旗子对应真雷的个数

1//统计棋盘上旗子下对应的真雷个数 2int TrueMineCount(const char mineboard[ROWS][COLS],const char showboard[ROWS][COLS], int cols, int rows,int* pcount) 3{ 4 int i, j; 5 int count = 0; 6 if (*pcount > 0) 7 { 8 for (= 0; i < cols; i++) 9 { 10 for (= 0; j < rows; j++) 11 { 12 if (showboard[i][j] == '#') 13 { 14 if (mineboard[i][j] == '1') 15 { 16 count++; 17 } 18 } 19 } 20 } 21 } 22 return count; 23}

8.统计棋盘上除(已排除的区域)和(雷区)以外的其他区域大小

1//统计棋盘上已排除的区域 2int Countmine(char showboard[ROWS][COLS],int col ,int row) 3{ 4 int i,j; 5 int check; 6 int win = col * row - EASY_CONST; 7 for (= 1; i <= row; i++) 8 { 9 for (= 1; j <= row; j++) 10 { 11 check = showboard[i][j] - '0'; 12 if (showboard[i][j] == ' ' || (check > 0 && check < 9)) 13 { 14 win--; 15 } 16 } 17 } 18 return win; 19}

    9.排雷实现,具体步骤看代码注释

1//排雷 2void FindMine(const char mineboard[ROWS][COLS], char showboard[ROWS][COLS], int col, int row) 3{ 4 int x = 0; 5 int y = 0; 6 int win = 0; 7 char input1; 8 int bool = 1; 9 int ret_trueminecount = 0; //用于接收旗子对应的真雷数 10 int flag_count = 0;        //用于统计棋盘上旗子的数量 11 do 12 { 13         //提示玩家进行选择模式 14 printf("请选择(输入a,b或c):\n"); 15 printf("a.排雷 b.插旗 c.取消插旗\n"); 16 //scanf("%s", &input1); 17 input1 = getchar(); 18 switch (input1) 19 { 20 case 'a': 21 { 22 printf("请输入要检查的坐标:>\n"); 23 int bool0 = 1; 24 //玩家输入正确坐标或者玩家胜利或者玩家失败退出循环 25 while (bool0)  26 { 27 scanf("%d %d", &x, &y); 28 if (> 0 && x <= row && y > 0 && y <= col)  //玩家输入的坐标在合法范围内 29 { 30 if (showboard[x][y] != '*')  //先判断是否已经排查 31 { 32 if (showboard[x][y] == '#') 33 { 34 printf("此处有旗子,请拔掉后再排查。\n"); 35 } 36 else 37 { 38 printf("\n此处已经排查过了,请重新输入。\n"); 39 } 40 } 41 else if (mineboard[x][y] == '1') //如果没有排查,判断是否踩到了雷 42 { 43 printf("\n很遗憾,你踩到了地雷,游戏结束!\n"); 44 DisplayBoard(mineboard, col, row); 45 bool = 0; 46 break; 47 } 48 else 49 { 50 int count = GetMineCount(mineboard, x, y);  //判断输入坐标的周围雷的个数 51 if (0 == count) 52 { 53 OpenMine(mineboard, showboard, COL, ROW, x, y); 54 bool0 = 0; 55 //判断棋盘上除真雷区域和已排查区域之外的格子数,如果为0,说明玩家排了所有的雷,win置1. 56 int ret_win = Countmine(showboard, COL, ROW);  57 if (ret_win == 0) 58 { 59 bool = 0; 60 win = 1; 61 break; 62 } 63 } 64 else 65 { 66 showboard[x][y] = count + '0'; 67 int ret_win = Countmine(showboard, COL, ROW); 68 bool0 = 0; 69 if (ret_win == 0) 70 { 71 bool = 0; 72 win = 1; 73 break; 74 } 75 } 76 DisplayBoard(showboard, COL, ROW); 77 } 78 } 79 //玩家坐标输入不在棋盘范围内,则提示玩家输入错误,重新输入 80 else 81 { 82 printf("\n您输入的坐标有误,请重新输入\n"); 83 } 84 } 85 break; 86 } 87 //插旗 88 case 'b': 89 { 90 int bool1 = 1; 91 while (bool1) 92 { 93 printf("请输入要插旗的坐标:>"); 94 scanf("%d %d", &x, &y); 95 if (> 0 && x <= row && y > 0 && y <= col) 96 { 97 if (showboard[x][y] == '*') 98 { 99 showboard[x][y] = '#'; 100 flag_count++;         //每插入一个旗子,旗子数+1 101 bool1 = 0;             102 ret_trueminecount = TrueMineCount(mineboard, showboard, COLS, ROWS, &flag_count); 103 //如果旗子数与对应的雷数相等,且旗子下都是真雷,则胜利,win置1. 104 if (flag_count==EASY_CONST && ret_trueminecount == EASY_CONST) 105 { 106 bool = 0; 107 win = 1; 108 } 109 } 110 else 111 { 112 printf("此处无法放置旗子。请按下列情况进行检查:\n(1)坐标是否有误;\n(2)此处已经有旗子;\n(3)此处是否已经排查过了。\n"); 113 } 114 } 115 else 116 { 117 printf("输入的坐标有误,请重新输入。\n"); 118 } 119 } 120 DisplayBoard(showboard, COL, ROW); 121 break; 122 } 123 124 //拔掉旗子 125 case 'c': 126 { 127 int bool2 = 1; 128 int ret = FlagCount(showboard, COLS, ROWS); 129 while (bool2) 130 { 131 if (ret == 0) 132 { 133 printf("\n棋盘上没有旗子;请选择排雷或插旗。\n"); 134 bool2 = 0; 135 } 136 else 137 { 138 printf("\n请输入要拔旗的坐标:>"); 139 scanf("%d %d", &x, &y); 140 if (> 0 && x <= row && y > 0 && y <= col) 141 { 142 if (showboard[x][y] == '#') 143 { 144 showboard[x][y] = '*'; 145 flag_count--;    //旗子拔掉后,旗子数要-1 146 bool2 = 0; 147 //如果旗子数与对应的雷数相等,且旗子下都是真雷,则胜利,win置1. 148 ret_trueminecount = TrueMineCount(mineboard, showboard, COLS, ROWS, &flag_count); 149 if (flag_count == EASY_CONST && ret_trueminecount == EASY_CONST) 150 { 151 bool = 0; 152 win = 1; 153 } 154 } 155 else 156 { 157 printf("\n此处无法移除旗子。请按下列情况进行检查:\n(1)坐标是否有误;\n(2)此处没有旗子;\n(3)此处旗子是否已经拔掉。\n"); 158 } 159 } 160 else 161 { 162 printf("输入的坐标有误,请重新输入。\n"); 163 } 164 } 165 } 166 DisplayBoard(showboard, COL, ROW); 167 break; 168 } 169 } 170 171 //根据上述代码,win等于1时,表示玩家排出了全部的雷。 172 if (win == 1) 173 { 174 printf("恭喜你,你已排出全部的雷。\n"); 175 bool = 0; 176 DisplayBoard(mineboard,COL,ROW); 177 } 178 }while ( bool == 1); 179}

最后游戏的主体部分代码如下:

1void game() 2{ 3 //存储雷的位置:生成两个棋盘,一个用于打印 4 char mineboard[COLS][ROWS] ; 5 char showboard[COLS][ROWS]; 6 7 //初始化棋盘 8 InitBoard(mineboard, COLS, ROWS, '0'); 9 InitBoard(showboard, COLS, ROWS, '*'); 10 11 //显示棋盘 12 DisplayBoard(showboard, COL, ROW); 13 14 //布置雷 15 SetMine(mineboard,COL,ROW); 16 //第一次点击 17 First_SafeMine(mineboard, showboard, ROW, COL); 18 //排雷 19 FindMine(mineboard, showboard, COL, ROW); 20}

总结

最后,对3个文件的代码进行汇总,如下所示。

头文件“game.h”:

1//头文件引用 2#include <stdio.h> 3#include <stdlib.h> 4#include <time.h> 5 6#ifndef __GAME_H__ 7#define __GAME_H__ 8 9//常量定义区 10#define EASY_CONST 10 11#define COL 9 12#define ROW 9 13#define COLS COL+2 14#define ROWS ROW+2 15 16//函数声明 17void menu(); 18void game(); 19void InitBoard(char board[ROWS][COLS], int cols,int rows, char set); 20void DisplayBoard(const char board[ROWS][COLS], int cols, int rows); 21void SetMine(char board[ROWS][COLS], int col, int row); 22void FindMine(const char mineboard[ROWS][COLS], char showboard[ROWS][COLS],int col,int row); 23int GetMineCount(const char mineboard[ROWS][COLS], int x, int y); 24void OpenMine(const char mineboard[ROWS][COLS], char showboard[ROWS][COLS], int col, int row, int x, int y); 25void First_SafeMine(char mineboard[ROWS][COLS], char showboard[ROWS][COLS], int row, int col); 26int TrueMineCount(const char mineboard[ROWS][COLS], const char showboard[ROWS][COLS], int cols, int rows, int* pcount); 27int Countmine(char showboard[ROWS][COLS], int col, int row); 28#endif

game.c文件代码:

1#define _CRT_SECURE_NO_WARNINGS 1 2 3#include "game.h" 4 5//菜单 6void menu() 7{ 8 printf("********************\n"); 9 printf("*1.play******0.exit*\n"); 10 printf("********************\n"); 11} 12 13//初始化棋盘 14void InitBoard(char board[ROWS][COLS], int cols, int rows, char set) 15{ 16 int i, j; 17 for (= 0; i < cols; i++) 18 { 19 for (= 0; j < rows; j++) 20 { 21 board[i][j] = set; 22 } 23 } 24} 25 26//显示棋盘 27void DisplayBoard(char board[ROWS][COLS], int col, int row) 28{ 29 int i, j; 30 printf("    "); 31 for (= 1; j <= col; j++) 32 { 33 printf("%d ", j); 34 } 35 printf("\n\n"); 36 for (= 1; i <= col; i++) 37 { 38 printf("%d   ",i); 39 for (= 1; j <= row; j++) 40 { 41 printf("%c ",board[i][j]); 42 } 43 printf("\n"); 44 } 45} 46 47//初始化地雷的位置 48void SetMine(char board[ROWS][COLS], int col, int row) 49{ 50 int x, y; 51 int count = EASY_CONST; 52 while (count) 53 { 54= rand() % col + 1; 55= rand() % row + 1; 56 if (board[x][y] == '0') 57 { 58 board[x][y] = '1'; 59 count--; 60 } 61 } 62} 63//保证第一个不踩雷 64void First_SafeMine(char mineboard[ROWS][COLS], char showboard[ROWS][COLS], int row, int col) 65{ 66 int x = 0; 67 int y = 0; 68 int ret = 1; 69 int count = 0; 70 int a, b; 71 printf("请输入要排查的坐标:"); 72 scanf("%d%d", &x, &y); 73 if (mineboard[x][y] == '1')//如果为雷,则改为没有雷 74 { 75 mineboard[x][y] = '0'; 76 while (ret)//随机生成一个雷 77 { 78= rand() % ROW + 1; 79= rand() % COL + 1; 80 if (mineboard[a][b] == '0') 81 { 82 mineboard[a][b] = '1'; 83 } 84 ret--; 85 } 86 } 87 count = GetMineCount(mineboard, x, y); 88 showboard[x][y] = count + '0'; 89 OpenMine(mineboard, showboard, row, col, x, y); 90 DisplayBoard(showboard, row, col); 91} 92 93//统计坐标周围雷的个数 94int GetMineCount(const char mineboard[ROWS][COLS], int x, int y) 95{ 96 return mineboard[- 1][+ 1] + mineboard[x][+ 1] + mineboard[+ 1][+ 1] + 97 mineboard[- 1][y] + mineboard[+ 1][y] + 98 mineboard[- 1][- 1] + mineboard[x][- 1] + mineboard[+ 1][- 1] - 8 * '0'; 99} 100 101//展开无雷区:如果该坐标周围没有雷就自动展开。 102void OpenMine(const char mineboard[ROWS][COLS], char showboard[ROWS][COLS], int col, int row,int x,int y) 103{ 104 int ret = 0; 105 ret=GetMineCount(mineboard, x, y); 106 if (ret == 0) 107 { 108 showboard[x][y] = ' '; 109 if (> 0 && y + 1 <= col && showboard[x][+ 1] == '*') 110 { 111 OpenMine(mineboard, showboard, col, row, x, y + 1); 112 } 113 if (- 1 > 0 && y + 1 <= col && showboard[- 1][+ 1] == '*') 114 { 115 OpenMine(mineboard, showboard, col, row, x - 1, y + 1); 116 } 117 if (- 1 > 0 && y >0 && showboard[- 1][y] == '*') 118 { 119 OpenMine(mineboard, showboard, col, row, x - 1, y); 120 } 121 if (- 1 > 0 && y - 1 >0 && showboard[- 1][- 1] == '*') 122 { 123 OpenMine(mineboard, showboard, col, row, x - 1, y - 1); 124 } 125 if (> 0 && y - 1 > 0 && showboard[x][- 1] == '*') 126 { 127 OpenMine(mineboard, showboard, col, row, x, y - 1); 128 } 129 if (+ 1 <= row && y - 1 > 0 && showboard[+ 1][- 1] == '*') 130 { 131 OpenMine(mineboard, showboard, col, row, x + 1, y - 1); 132 } 133 if (+ 1 <= row && y > 0 && showboard[+ 1][y] == '*') 134 { 135 OpenMine(mineboard, showboard, col, row, x + 1, y); 136 } 137 if (+ 1 <=row && y + 1 <= col && showboard[+ 1][+ 1] == '*') 138 { 139 OpenMine(mineboard, showboard, col, row, x + 1, y + 1); 140 } 141 } 142 else 143 { 144 showboard[x][y] = '0' + ret; 145 } 146} 147 148//统计棋盘上旗子下对应的真雷个数 149int TrueMineCount(const char mineboard[ROWS][COLS],const char showboard[ROWS][COLS], int cols, int rows,int* pcount) 150{ 151 int i, j; 152 int count = 0; 153 if (*pcount > 0) 154 { 155 for (= 0; i < cols; i++) 156 { 157 for (= 0; j < rows; j++) 158 { 159 if (showboard[i][j] == '#') 160 { 161 if (mineboard[i][j] == '1') 162 { 163 count++; 164 } 165 } 166 } 167 } 168 } 169 return count; 170} 171 172//统计棋盘上已排除的区域 173int Countmine(char showboard[ROWS][COLS],int col ,int row) 174{ 175 int i,j; 176 int check; 177 int win = col * row - EASY_CONST; 178 for (= 1; i <= row; i++) 179 { 180 for (= 1; j <= row; j++) 181 { 182 check = showboard[i][j] - '0'; 183 if (showboard[i][j] == ' ' || (check > 0 && check < 9)) 184 { 185 win--; 186 } 187 } 188 } 189 return win; 190} 191 192//排雷 193void FindMine(const char mineboard[ROWS][COLS], char showboard[ROWS][COLS], int col, int row) 194{ 195 int x = 0; 196 int y = 0; 197 int win = 0; 198 char input1; 199 int bool = 1; 200 int ret_trueminecount = 0; 201 int flag_count = 0; 202 do 203 { 204 //ret_trueminecount = TrueMineCount(mineboard, showboard, COLS, ROWS,&flag_count); 205 printf("请选择(输入a,b或c):\n"); 206 printf("a.排雷 b.插旗 c.取消插旗\n"); 207 //scanf("%s", &input1); 208 input1 = getchar(); 209 switch (input1) 210 { 211 case 'a': 212 { 213 printf("请输入要检查的坐标:>\n"); 214 int bool0 = 1; 215 while (bool0)  216 { 217 scanf("%d %d", &x, &y); 218 if (> 0 && x <= row && y > 0 && y <= col) 219 { 220 if (showboard[x][y] != '*') 221 { 222 if (showboard[x][y] == '#') 223 { 224 printf("此处有旗子,请拔掉后再排查。\n"); 225 } 226 else 227 { 228 printf("\n此处已经排查过了,请重新输入。\n"); 229 } 230 } 231 else if (mineboard[x][y] == '1') 232 { 233 printf("\n很遗憾,你踩到了地雷,游戏结束!\n"); 234 DisplayBoard(mineboard, col, row); 235 bool = 0; 236 break; 237 } 238 else 239 { 240 int count = GetMineCount(mineboard, x, y); 241 if (0 == count) 242 { 243 OpenMine(mineboard, showboard, COL, ROW, x, y); 244 bool0 = 0; 245 int ret_win = Countmine(showboard, COL, ROW); 246 if (ret_win == 0) 247 { 248 bool = 0; 249 win = 1; 250 break; 251 } 252 } 253 else 254 { 255 showboard[x][y] = count + '0'; 256 int ret_win = Countmine(showboard, COL, ROW); 257 bool0 = 0; 258 if (ret_win == 0) 259 { 260 bool = 0; 261 win = 1; 262 break; 263 } 264 } 265 DisplayBoard(showboard, COL, ROW); 266 } 267 } 268 else 269 { 270 printf("\n您输入的坐标有误,请重新输入\n"); 271 } 272 } 273 break; 274 } 275 case 'b': 276 { 277 int bool1 = 1; 278 while (bool1) 279 { 280 printf("请输入要插旗的坐标:>"); 281 scanf("%d %d", &x, &y); 282 if (> 0 && x <= row && y > 0 && y <= col) 283 { 284 if (showboard[x][y] == '*') 285 { 286 showboard[x][y] = '#'; 287 flag_count++; 288 bool1 = 0; 289 ret_trueminecount = TrueMineCount(mineboard, showboard, COLS, ROWS, &flag_count); 290 if (flag_count==EASY_CONST && ret_trueminecount == EASY_CONST) 291 { 292 bool = 0; 293 win = 1; 294 } 295 } 296 else 297 { 298 printf("此处无法放置旗子。请按下列情况进行检查:\n(1)坐标是否有误;\n(2)此处已经有旗子;\n(3)此处是否已经排查过了。\n"); 299 } 300 } 301 else 302 { 303 printf("输入的坐标有误,请重新输入。\n"); 304 } 305 } 306 DisplayBoard(showboard, COL, ROW); 307 break; 308 } 309 case 'c': 310 { 311 int bool2 = 1; 312 int ret = FlagCount(showboard, COLS, ROWS); 313 while (bool2) 314 { 315 if (ret == 0) 316 { 317 printf("\n棋盘上没有旗子;请选择排雷或插旗。\n"); 318 bool2 = 0; 319 } 320 else 321 { 322 printf("\n请输入要拔旗的坐标:>"); 323 scanf("%d %d", &x, &y); 324 if (> 0 && x <= row && y > 0 && y <= col) 325 { 326 if (showboard[x][y] == '#') 327 { 328 showboard[x][y] = '*'; 329 flag_count--; 330 bool2 = 0; 331 ret_trueminecount = TrueMineCount(mineboard, showboard, COLS, ROWS, &flag_count); 332 if (flag_count == EASY_CONST && ret_trueminecount == EASY_CONST) 333 { 334 bool = 0; 335 win = 1; 336 } 337 } 338 else 339 { 340 printf("\n此处无法移除旗子。请按下列情况进行检查:\n(1)坐标是否有误;\n(2)此处没有旗子;\n(3)此处旗子是否已经拔掉。\n"); 341 } 342 } 343 else 344 { 345 printf("输入的坐标有误,请重新输入。\n"); 346 } 347 } 348 } 349 DisplayBoard(showboard, COL, ROW); 350 break; 351 } 352 } 353 if (win == 1) 354 { 355 printf("恭喜你,你已排出全部的雷。\n"); 356 bool = 0; 357 DisplayBoard(mineboard,COL,ROW); 358 } 359 }while ( bool == 1); 360} 361 362//游戏主体部分 363void game() 364{ 365 //存储雷的位置:生成两个棋盘,一个用于打印 366 char mineboard[COLS][ROWS] ; 367 char showboard[COLS][ROWS]; 368 369 //初始化棋盘 370 InitBoard(mineboard, COLS, ROWS, '0'); 371 InitBoard(showboard, COLS, ROWS, '*'); 372 373 //显示棋盘 374 DisplayBoard(showboard, COL, ROW); 375 376 //布置雷 377 SetMine(mineboard,COL,ROW); 378 First_SafeMine(mineboard, showboard, ROW, COL); 379 //排雷 380 FindMine(mineboard, showboard, COL, ROW); 381}

main.c文件

1#define _CRT_SECURE_NO_WARNINGS 1 2//扫雷游戏 3#include "game.h" 4 5int main(void) 6{ 7 int input; 8 srand((unsigned int)time(NULL)); 9 do 10 { 11 menu(); 12 printf("请选择:>"); 13 scanf("%d", &input); 14 switch (input) 15 { 16 case 1: 17 game(); 18 break; 19 case 0: 20 printf("退出游戏。\n"); 21 break; 22 default: 23 printf("输入错误,请重新输入.\n"); 24 break; 25 } 26 } while (input); 27 return 0; 28}
点赞
收藏

评论区

加载中...

相关推荐

手把手教你使用JavaScript打造一款扫雷游戏

大家好,我是皮皮。扫雷大家都玩过,今天我们就是用JavaScript来打造扫雷游戏。废话不多说,直接看下效果;上图是失败后的结果。一、思路分析我们新建一个首页,在首页放置一个点击开始游戏的按钮,动态生成100个小格,即100div;然后通过点击div进行扫雷操作,然后扫雷成功或者失败显示对应的结果;二、静态页面搭建2.1结构层

Android RecyclerView使用GridLayoutManager间距设置

使用RecyclerView设置间距,需要重写RecyclerView.ItemDecoration这个类。有如下的效果图需要实现,间距只有中间的格子和底部的格式之间有。Paste\_Image.png实现方法很简单,因为这个效果是每一行有3个格子,只要每行的第一个格式左边间距为0即可以。其他都设置左边距和底部距离。代码如下:publ

Linux下的小游戏

Linux下的小游戏非常丰富,除了有扫雷、纸牌等Windows下常见小游戏外,还有一些富有Linux特色的游戏,如数独、gbrainy等智力游戏。我们今天要介绍的是为众人喜闻乐见、容易上手的小游戏:五子棋、中国象棋、麻将连连看、宝石迷阵。1.五子棋。在“Ubuntu软件中心”里搜索bovo,安装即可。点击“新建”,即可开始新游戏。右下角选择难度。

FZU2150 Fire Game

题目:_两个熊孩子在n\m的平地上放火玩,表示草,两个熊孩子分别选一个格子点火,火可以向上向下向左向右在有草的格子蔓延,点火的地方时间为0,蔓延至下一格的时间依次加一。求烧完所有的草需要的最少时间。如不能烧完输出1。_输入:_第一行,输入一个T,表示有T组测试数据。每组数据由一个n,m分别表示行列_1

Unity3D学习笔记(二十六):MVC框架下的背包系统(1)

MVC背包!(https://oscimg.oschina.net/oscnet/dd09a8a61054c24d33e9fd2e8c3850eab02.png)需求:1、背包格子的装备是可以拖动的2、装备栏的装备也是可以拖动的3、当背包格子的装备拖动到装备栏时,如果是装备类型和装备栏类型是一致的能装上4、背包的装备是按照顺序放在

200的大额人民币即将面世?央行:Yes!

点击上方蓝字关注我们!(https://oscimg.oschina.net/oscnet/2a1c2ac00bf54458a78c48a6c2e547d5.png)点击上方“印象python”,选择“星标”公众号重磅干货,第一时间送达!!(

C语言实战项目—扫雷小程序 - HelloWorld