C语言——扫雷

扫雷是一款经典的小游戏,那如何使用C语言实现一个扫雷游戏呢?

一、全部源码

直接把全部源码放在开头,如有需要,直接拿走。

源码分为三个文件:

test.cpp/c

主函数的位置

#include "game.h"int main()
{int deep[ROW][COL];//深层,用来记录布置好的地雷和数字,及埋藏在下层的内容char face[ROW][COL];//表面,用来打印和记录玩家操作,表层,对玩家操作进行记录int menu1 = 0;//外层菜单,0为退出游戏,1为进入游戏int menu2 = 0;//内层菜单,0为返回上一步,1为简单难度,2为中等难度,3为大师难度do{GameMenu(1);//外菜单打印scanf("%d", &menu1);//接收选择switch(menu1){case 0://退出游戏printf("你已退出游戏!\n");break;case 1://内层菜单,选择难度GameMenu(2);//内层菜单打印scanf("%d", &menu2);//接收选择switch(menu2){case 0://返回上一界面break;case 1:GameEasy(deep, face);//简单难度break;case 2:GameMedium(deep, face);//中等难度break;case 3:GameExpert(deep, face);//大师难度break;case 4:GameNightmare(deep, face);//噩梦难度break;default:printf("ERROR!\n");break;}break;default :printf("ERROR!\n");break;}} while (menu1);//只要非零就一直循环,进行游戏,为0则退出游戏return 0;
}

gane.cpp/c

几乎所有游戏功能函数的位置

#include "game.h"//用来打印菜单
void GameMenu(int x)
{switch(x){case 1://外层菜单printf("----------------------------\n");printf("------ 1 for start ---------\n");printf("------ 0 for exit ----------\n");printf("----------------------------\n");break;case 2://内层菜单printf("----------------------------\n");printf("------ 1 for Easy ----------\n");printf("------ 2 for Medium --------\n");printf("------ 3 for Expert --------\n");printf("------ 4 for Nightmare -----\n");printf("------ 0 for back ----------\n");printf("----------------------------\n");break;default :printf("ERROR!\n");break;}
}//用来初始化深层数组,difficulty用来传难度,不同难度初始化大小不同
void DeepBoardIntial(int deep[ROW][COL], int difficulty)
{int i = 0, j = 0;for (i = 0; i < difficulty; i++){for (j = 0; j < difficulty; j++){deep[i][j] = 0;//初始化为0,以便之后填充数字}}
}//用来初始化表面数组,difficulty用来传难度,不同难度初始化大小不同
void FaceBoardIntial(char face[ROW][COL],int difficulty)
{int i = 0, j = 0;for (i = 0; i < difficulty; i++){for (j = 0; j < difficulty; j++){face[i][j] = '_';//初始化为 _ ,表示未揭示的格子}}
}//打印内容,对应打印可视化面板,这个是打印内容
void ContentPrint(int deep[ROW][COL], char face[ROW][COL], int i, int j)
{if (face[i][j] == 'x')//只要是玩家揭示的,直接打印deep中的内容(如果是炸弹,通过IsGameWin函数判断,就直接失败了,不会打印){printf(" %d |", deep[i][j]);}else if (face[i][j] == 'L')//这里实现的是连续揭示,原版的一种操作,连续解释的赋值为 L ,方便实现当玩家探索一个没有雷的空块时,程序应该自动揭示与该空块相邻的所有空块,直到遇到数字(即与雷相邻的块)。这个特性是原版扫雷游戏的关键特点,加快游戏进程{if (deep[i][j] == 0)//只要数字是零,则直接打印空格,实现原版的周围八个格没有雷则打印空格{printf("   |");}else{printf(" %d |", deep[i][j]);//数字不是零则打印这个数字}}else{printf(" %c |",face[i][j]);//没有揭示的格子直接打印 _ 表示未揭示的格子}
}//打印可视化面板
void BoardPrint(int deep[ROW][COL], char face[ROW][COL],int difficulty)
{int i = 0, j = 0;printf("|");for (j = 0; j < difficulty; j++){printf("(%-2d|", j + 1);}printf("\n");printf("|");for (j = 0; j < difficulty; j++){printf("___|");}printf("___");printf("\n");for (i = 0; i < difficulty; i++){printf("|");for (j = 0; j < difficulty; j++){ContentPrint(deep,face,i,j);//只有这个是内容打印,其他是打印表格,为了更加美观}printf("(%-2d", i + 1);printf("\n");printf("|");for (j = 0; j < difficulty; j++){printf("___|");}printf("___");printf("\n");}
}//随即放置地雷
void RandomMineGenerate(int deep[ROW][COL],int difficulty,int minesnum)
{int x = 0;int y = 0;int i = 0;srand((unsigned int)time(NULL));//设置随机种子for(i = 0;i < minesnum;i++){x = rand() % difficulty;//产生0~8的数字,表示随机的行y = rand() % difficulty;//产生0~8的数字,表示随机的列if(deep[x][y] != MINE)//确保这个位置不是雷,只要不是已经是地雷的格子,则放一个地雷,防止重复放置导致的地雷数目减少{deep[x][y] = MINE;//将这个随机生成的坐标设为雷}else//如果是雷,则重新生成{i--;//i--可以实现此次作废,再来生成一次的效果}}
}//检查是否胜利,0为输,1为赢,2为继续
int IsGameWin(int deep[ROW][COL], char face[ROW][COL], int row, int col, int difficulty)
{if (deep[row][col] == MINE && face[row][col] == 'x')//只要是雷且玩家点击,返回0,即输{return 0;}int i = 0, j = 0;for (i = 0; i < difficulty; i++){for (j = 0; j < difficulty; j++){if (deep[i][j] != MINE && face[i][j] == '_')//只要还有非雷格子没探索,返回2,就继续{return 2;}}}return 1;//没有踩雷,且没有非雷格子未探索,返回1,则赢
}//连续揭示,点的是周围没有雷的格子,即数字零,周围就会连续揭示,实现原版操作
//连续揭示,原版的一种操作,连续解释的赋值为 L ,方便实现当玩家探索一个没有雷的空块时,程序应该自动揭示与该空块相邻的所有空块,直到遇到数字(即与雷相邻的块)。这个特性是原版扫雷游戏的关键特点,加快游戏进程
void LinkChange(int deep[ROW][COL], char face[ROW][COL], int i, int j,int difficulty)
{//由于下面使用了递归,必须加入一些条件使得递归不会一直递归下去if (!CheckIfValid(i, j, difficulty))//只要超出雷区范围,直接返回,函数返回值为void,直接return,不带任何值{return;}if (face[i][j] == 'L')//已经设置为连续揭示标志 L 的则直接返回{return;}if (deep[i][j] > 0 && deep[i][j] < 9)//在数字0旁边的非零数字,也设置为 L 并且返回{face[i][j] = 'L';return;}if (deep[i][j] == 9)//是地雷则直接返回{return;}if (deep[i][j] == 0)//一个位置为0,将这个位值设为 L 并且探索它周围八个格子,进行递归{face[i][j] = 'L';LinkChange(deep, face, i - 1, j - 1, difficulty);LinkChange(deep, face, i - 1, j, difficulty);LinkChange(deep, face, i - 1, j + 1, difficulty);LinkChange(deep, face, i, j - 1, difficulty);LinkChange(deep, face, i, j + 1, difficulty);LinkChange(deep, face, i + 1, j - 1, difficulty);LinkChange(deep, face, i + 1, j, difficulty);LinkChange(deep, face, i + 1, j + 1, difficulty);}
}//装填数字,计算非雷格子中应该是数字几,来表示周围雷的个数,装填在数组中
void FillinNum(int deep[ROW][COL],int difficulty)
{int i = 0, j = 0;for (i = 0; i < difficulty; i++){for (j = 0; j < difficulty; j++){if (deep[i][j] == MINE)//如果坐标(i,j)是雷,则将雷旁边的八个格子都加一{if (CheckIfValid(i - 1,j - 1,difficulty) && deep[i - 1][j - 1] != MINE )//判断格子是否是雷,并且判断坐标是否有效,格子有效且不是地雷才能加一{deep[i - 1][j - 1]++;}if (CheckIfValid(i - 1, j, difficulty) && deep[i - 1][j] != MINE){deep[i - 1][j]++;}if (CheckIfValid(i - 1, j + 1, difficulty) && deep[i - 1][j + 1] != MINE){deep[i - 1][j + 1]++;}if (CheckIfValid(i, j - 1, difficulty) && deep[i][j - 1] != MINE){deep[i][j - 1]++;}if (CheckIfValid(i, j + 1, difficulty) && deep[i][j + 1] != MINE){deep[i][j + 1]++;}if (CheckIfValid(i + 1, j - 1, difficulty) && deep[i + 1][j - 1] != MINE){deep[i + 1][j - 1]++;}if (CheckIfValid(i + 1, j, difficulty) && deep[i + 1][j] != MINE){deep[i + 1][j]++;}if (CheckIfValid(i + 1, j + 1, difficulty) && deep[i + 1][j + 1] != MINE){deep[i + 1][j + 1]++;}}}}
}//检查坐标是否有效
int CheckIfValid(int row, int col,int difficulty)
{if (row >= 0 && row < difficulty && col >= 0 && col < difficulty)//检查坐标是否在雷区内{return 1;//在雷区内为有效,返回1为有效}return 0;//不在雷区内为无效,返回0为无效
}//简单难度
void GameEasy(int deep[ROW][COL], char face[ROW][COL])
{DeepBoardIntial(deep, 9);//简单难度格子为 9 * 9 大小FaceBoardIntial(face, 9);RandomMineGenerate(deep, 9,9);//随机生成地雷FillinNum(deep, 9);//装填数字int row = 0, col = 0;//用来接收玩家输入行列int check = 0;//检查是否胜利,0为失败,1为胜利,2为继续do{system("cls");//清屏BoardPrint(deep, face, 9);//打印while(1){printf("输入行列>");scanf("%d %d", &row, &col);if (CheckIfValid(row - 1, col - 1,9) && face[row - 1][col - 1] != 'x' && face[row - 1][col - 1] != 'L')//有效且是没被揭开的则输入成功{face[row - 1][col - 1] = 'x';break;//输入成功则直接退出循环}else if (CheckIfValid(row - 1, col - 1, 9) && (face[row - 1][col - 1] == 'x' || face[row - 1][col - 1] == 'L'))//输入的坐标为已经揭示的格子,则重新输入{printf("已经揭示的格子,请不要重复操作!重新输入!\n");}else//不在范围内,重新输入{printf("范围错误!重新输入!\n");}}check = IsGameWin(deep, face,row - 1,col - 1,9);//检查是否胜利或失败以及游戏是否继续if(check == 2)//只要游戏继续,就判断是否连续揭示{if(deep[row - 1][col - 1] == 0)//揭示的格子是空格(即格子是0,周围八个格子没有地雷)则尝试连续揭示{LinkChange(deep, face, row - 1, col - 1,9);//对于这个输入的格子进行连续揭示}}} while (check == 2);//只要check为2则继续,则循环继续,则游戏继续,check为1或0(即赢或输)是退出循环if (check == 1)//check为1时胜利{printf("你赢了!\n");}else if(check == 0)//check为0时失败{printf("你输了!\n");}else//错误{printf("ERROR!\n");}
}//中等难度
void GameMedium(int deep[ROW][COL], char face[ROW][COL])
{DeepBoardIntial(deep, 16);//中等难度格子为 16 * 16 大小FaceBoardIntial(face, 16);RandomMineGenerate(deep, 16,32);//随机生成地雷FillinNum(deep, 16);//装填数字int row = 0, col = 0;//用来接收玩家输入行列int check = 0;//检查是否胜利,0为失败,1为胜利,2为继续do{system("cls");//清屏BoardPrint(deep, face, 16);//打印while (1){printf("输入行列>");scanf("%d %d", &row, &col);if (CheckIfValid(row - 1, col - 1, 16) && face[row - 1][col - 1] != 'x' && face[row - 1][col - 1] != 'L')//有效且是没被揭开的则输入成功{face[row - 1][col - 1] = 'x';break;//输入成功则直接退出循环}else if (CheckIfValid(row - 1, col - 1, 16) && (face[row - 1][col - 1] == 'x' || face[row - 1][col - 1] == 'L'))//输入的坐标为已经揭示的格子,则重新输入{printf("已经揭示的格子,请不要重复操作!重新输入!\n");}else//不在范围内,重新输入{printf("范围错误!重新输入!\n");}}check = IsGameWin(deep, face, row - 1, col - 1, 16);//检查是否胜利或失败以及游戏是否继续if (check == 2)//只要游戏继续,就判断是否连续揭示{if (deep[row - 1][col - 1] == 0)//揭示的格子是空格(即格子是0,周围八个格子没有地雷)则尝试连续揭示{LinkChange(deep, face, row - 1, col - 1, 16);//对于这个输入的格子进行连续揭示}}} while (check == 2);//只要check为2则继续,则循环继续,则游戏继续,check为1或0(即赢或输)是退出循环if (check == 1)//check为1时胜利{printf("你赢了!\n");}else if (check == 0)//check为0时失败{printf("你输了!\n");}else//错误{printf("ERROR!\n");}
}//大师难度
void GameExpert(int deep[ROW][COL], char face[ROW][COL])
{DeepBoardIntial(deep, 30);//大师难度格子为 30 * 30 大小FaceBoardIntial(face, 30);RandomMineGenerate(deep, 30,90);//随机生成地雷FillinNum(deep, 30);//装填数字int row = 0, col = 0;//用来接收玩家输入行列int check = 0;//检查是否胜利,0为失败,1为胜利,2为继续do{system("cls");//清屏BoardPrint(deep, face, 30);//打印while (1){printf("输入行列>");scanf("%d %d", &row, &col);if (CheckIfValid(row - 1, col - 1, 30) && face[row - 1][col - 1] != 'x' && face[row - 1][col - 1] != 'L')//有效且是没被揭开的则输入成功{face[row - 1][col - 1] = 'x';break;//输入成功则直接退出循环}else if (CheckIfValid(row - 1, col - 1, 30) && (face[row - 1][col - 1] == 'x' || face[row - 1][col - 1] == 'L'))//输入的坐标为已经揭示的格子,则重新输入{printf("已经揭示的格子,请不要重复操作!重新输入!\n");}else//不在范围内,重新输入{printf("范围错误!重新输入!\n");}}check = IsGameWin(deep, face, row - 1, col - 1, 30);//检查是否胜利或失败以及游戏是否继续if (check == 2)//只要游戏继续,就判断是否连续揭示{if (deep[row - 1][col - 1] == 0)//揭示的格子是空格(即格子是0,周围八个格子没有地雷)则尝试连续揭示{LinkChange(deep, face, row - 1, col - 1, 30);//对于这个输入的格子进行连续揭示}}} while (check == 2);//只要check为2则继续,则循环继续,则游戏继续,check为1或0(即赢或输)是退出循环if (check == 1)//check为1时胜利{printf("你赢了!\n");}else if (check == 0)//check为0时失败{printf("你输了!\n");}else//错误{printf("ERROR!\n");}
}//噩梦难度
void GameNightmare(int deep[ROW][COL], char face[ROW][COL])
{DeepBoardIntial(deep, 50);//噩梦难度格子为 50 * 50 大小FaceBoardIntial(face, 50);RandomMineGenerate(deep, 50, 250);//随机生成地雷FillinNum(deep, 50);//装填数字int row = 0, col = 0;//用来接收玩家输入行列int check = 0;//检查是否胜利,0为失败,1为胜利,2为继续do{system("cls");//清屏BoardPrint(deep, face, 50);//打印while (1){printf("输入行列>");scanf("%d %d", &row, &col);if (CheckIfValid(row - 1, col - 1, 50) && face[row - 1][col - 1] != 'x' && face[row - 1][col - 1] != 'L')//有效且是没被揭开的则输入成功{face[row - 1][col - 1] = 'x';break;//输入成功则直接退出循环}else if (CheckIfValid(row - 1, col - 1, 50) && (face[row - 1][col - 1] == 'x' || face[row - 1][col - 1] == 'L'))//输入的坐标为已经揭示的格子,则重新输入{printf("已经揭示的格子,请不要重复操作!重新输入!\n");}else//不在范围内,重新输入{printf("范围错误!重新输入!\n");}}check = IsGameWin(deep, face, row - 1, col - 1, 50);//检查是否胜利或失败以及游戏是否继续if (check == 2)//只要游戏继续,就判断是否连续揭示{if (deep[row - 1][col - 1] == 0)//揭示的格子是空格(即格子是0,周围八个格子没有地雷)则尝试连续揭示{LinkChange(deep, face, row - 1, col - 1, 50);//对于这个输入的格子进行连续揭示}}} while (check == 2);//只要check为2则继续,则循环继续,则游戏继续,check为1或0(即赢或输)是退出循环if (check == 1)//check为1时胜利{printf("你赢了!\n");}else if (check == 0)//check为0时失败{printf("你输了!\n");}else//错误{printf("ERROR!\n");}
}

game.cpp/c

函数声明

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <time.h>#define ROW 50//宏定义,方便更改
#define COL 50
#define MINE 9//宏定义,定义9为雷//菜单打印
void GameMenu(int x);//深层数组初始化
void DeepBoardIntial(int deep[ROW][COL],int difficulty);//表面数组初始化
void FaceBoardIntial(char face[ROW][COL],int difficulty);//连续揭示打印
void LinkChange(int deep[ROW][COL], char face[ROW][COL], int i, int j,int difficulty);//实际打印实现
void ContentPrint(int deep[ROW][COL], char face[ROW][COL], int i, int j);//面板打印
void BoardPrint(int deep[ROW][COL], char face[ROW][COL],int difficulty);//随机生成雷
void RandomMineGenerate(int deep[ROW][COL], int difficulty,int minesnum);//检查是否胜利
int IsGameWin(int deep[ROW][COL], char face[ROW][COL], int row, int col, int difficulty);//填入数字
void FillinNum(int deep[ROW][COL], int difficulty);//检查输入坐标是否有效
int CheckIfValid(int row, int col, int difficulty);//简单难度
void GameEasy(int deep[ROW][COL], char face[ROW][COL]);//中等难度
void GameMedium(int deep[ROW][COL], char face[ROW][COL]);//大师难度
void GameExpert(int deep[ROW][COL], char face[ROW][COL]);//噩梦难度
void GameNightmare(int deep[ROW][COL], char face[ROW][COL]);

可以自行运行测试,下面有测试图片以及一些问题的解决办法,在在第七大段。

二、面板

要想扫雷,首先要有可视化的面板,能显示相关的信息,例如显示出数字,表示周围八个格子中有几个雷。所以我们先实现面板,同时还要存储数据,存储雷的位置,以及记录玩家操作的位置。所以我们要定义两个数组,一个用来存储雷的位置和数字,一个用来打印面板和存储玩家操作位置。

	int deep[ROW][COL];//深层,用来记录布置好的地雷和数字,及埋藏在下层的内容char face[ROW][COL];//表面,用来打印和记录玩家操作,表层,对玩家操作进行记录

1、游戏菜单

同时我们还要有游戏菜单供玩家选择难度和退出游戏。两个菜单,一个外层一个内层。

//用来打印菜单
void GameMenu(int x)
{switch(x){case 1://外层菜单printf("----------------------------\n");printf("------ 1 for start ---------\n");printf("------ 0 for exit ----------\n");printf("----------------------------\n");break;case 2://内层菜单printf("----------------------------\n");printf("------ 1 for Easy ----------\n");printf("------ 2 for Medium --------\n");printf("------ 3 for Expert --------\n");printf("------ 4 for Nightmare -----\n");printf("------ 0 for back ----------\n");printf("----------------------------\n");break;default :printf("ERROR!\n");break;}
}

同时配合switch语句实现退出或开始游戏与难度选择:

GameMenu(1);//外菜单打印
scanf("%d", &menu1);//接收选择
switch(menu1)
{
case 0://退出游戏printf("你已退出游戏!\n");break;
case 1://内层菜单,选择难度GameMenu(2);//内层菜单打印scanf("%d", &menu2);//接收选择switch(menu2){case 0://返回上一界面break;case 1:GameEasy(deep, face);//简单难度break;case 2:GameMedium(deep, face);//中等难度break;case 3:GameExpert(deep, face);//大师难度break;case 4:GameNightmare(deep, face);//噩梦难度break;default:printf("ERROR!\n");break;}break;
default :printf("ERROR!\n");break;
}

2、初始始化面板

在定义了两个数组用来存储数据后,我们还要对这两个数组初始化,初始化的内容是对于整型数组直接初始化为0,方便后面数字打印;字符型数组初始化为 _ 用来代表没有被揭示的地块。

//用来初始化深层数组,difficulty用来传难度,不同难度初始化大小不同
void DeepBoardIntial(int deep[ROW][COL], int difficulty)
{int i = 0, j = 0;for (i = 0; i < difficulty; i++){for (j = 0; j < difficulty; j++){deep[i][j] = 0;//初始化为0,以便之后填充数字}}
}//用来初始化表面数组,difficulty用来传难度,不同难度初始化大小不同
void FaceBoardIntial(char face[ROW][COL],int difficulty)
{int i = 0, j = 0;for (i = 0; i < difficulty; i++){for (j = 0; j < difficulty; j++){face[i][j] = '_';//初始化为 _ ,表示未揭示的格子}}
}

3、面板打印

为了使面板可见,我们还需要一个面板打印函数,并且在两个数组发生变化时也能打印变化过的面板。也就是在玩家操作后数组内容进行了更新,打印的内容也要进行更新。

(1)表格打印

这里只是用来打印表格,使界面更美观。

//打印可视化面板
void BoardPrint(int deep[ROW][COL], char face[ROW][COL],int difficulty)
{int i = 0, j = 0;printf("|");for (j = 0; j < difficulty; j++){printf("(%-2d|", j + 1);}printf("\n");printf("|");for (j = 0; j < difficulty; j++){printf("___|");}printf("___");printf("\n");for (i = 0; i < difficulty; i++){printf("|");for (j = 0; j < difficulty; j++){ContentPrint(deep,face,i,j);//只有这个是内容打印,其他是打印表格,为了更加美观}printf("(%-2d", i + 1);printf("\n");printf("|");for (j = 0; j < difficulty; j++){printf("___|");}printf("___");printf("\n");}
}

(2)内容打印

这个是打印内容,实现扫雷过程中内容更新后的打印。

//打印内容,对应打印可视化面板,这个是打印内容
void ContentPrint(int deep[ROW][COL], char face[ROW][COL], int i, int j)
{if (face[i][j] == 'x')//只要是玩家揭示的,直接打印deep中的内容(如果是炸弹,通过IsGameWin函数判断,就直接失败了,不会打印){printf(" %d |", deep[i][j]);}else if (face[i][j] == 'L')//这里实现的是连续揭示,原版的一种操作,连续解释的赋值为 L ,方便实现当玩家探索一个没有雷的空块时,程序应该自动揭示与该空块相邻的所有空块,直到遇到数字(即与雷相邻的块)。这个特性是原版扫雷游戏的关键特点,加快游戏进程{if (deep[i][j] == 0)//只要数字是零,则直接打印空格,实现原版的周围八个格没有雷则打印空格{printf("   |");}else{printf(" %d |", deep[i][j]);//数字不是零则打印这个数字}}else{printf(" %c |",face[i][j]);//没有揭示的格子直接打印 _ 表示未揭示的格子}
}

两个函数配合完成打印工作。

三、游戏功能

1、地雷随机生成

//随即放置地雷
void RandomMineGenerate(int deep[ROW][COL],int difficulty,int minesnum)
{int x = 0;int y = 0;int i = 0;srand((unsigned int)time(NULL));//设置随机种子for(i = 0;i < minesnum;i++){x = rand() % difficulty;//产生0~8的数字,表示随机的行y = rand() % difficulty;//产生0~8的数字,表示随机的列if(deep[x][y] != MINE)//确保这个位置不是雷,只要不是已经是地雷的格子,则放一个地雷,防止重复放置导致的地雷数目减少{deep[x][y] = MINE;//将这个随机生成的坐标设为雷}else//如果是雷,则重新生成{i--;//i--可以实现此次作废,再来生成一次的效果}}
}

利用rand函数随机产生坐标将这个坐标设为地雷。

2、装填数字

根据这个格子周围八个格子有几个地雷,就把这个格子设为数字几。

//装填数字,计算非雷格子中应该是数字几,来表示周围雷的个数,装填在数组中
void FillinNum(int deep[ROW][COL],int difficulty)
{int i = 0, j = 0;for (i = 0; i < difficulty; i++){for (j = 0; j < difficulty; j++){if (deep[i][j] == MINE)//如果坐标(i,j)是雷,则将雷旁边的八个格子都加一{if (CheckIfValid(i - 1,j - 1,difficulty) && deep[i - 1][j - 1] != MINE )//判断格子是否是雷,并且判断坐标是否有效,格子有效且不是地雷才能加一{deep[i - 1][j - 1]++;}if (CheckIfValid(i - 1, j, difficulty) && deep[i - 1][j] != MINE){deep[i - 1][j]++;}if (CheckIfValid(i - 1, j + 1, difficulty) && deep[i - 1][j + 1] != MINE){deep[i - 1][j + 1]++;}if (CheckIfValid(i, j - 1, difficulty) && deep[i][j - 1] != MINE){deep[i][j - 1]++;}if (CheckIfValid(i, j + 1, difficulty) && deep[i][j + 1] != MINE){deep[i][j + 1]++;}if (CheckIfValid(i + 1, j - 1, difficulty) && deep[i + 1][j - 1] != MINE){deep[i + 1][j - 1]++;}if (CheckIfValid(i + 1, j, difficulty) && deep[i + 1][j] != MINE){deep[i + 1][j]++;}if (CheckIfValid(i + 1, j + 1, difficulty) && deep[i + 1][j + 1] != MINE){deep[i + 1][j + 1]++;}}}}
}

3、检查是否胜利

//检查是否胜利,0为输,1为赢,2为继续
int IsGameWin(int deep[ROW][COL], char face[ROW][COL], int row, int col, int difficulty)
{if (deep[row][col] == MINE && face[row][col] == 'x')//只要是雷且玩家点击,返回0,即输{return 0;}int i = 0, j = 0;for (i = 0; i < difficulty; i++){for (j = 0; j < difficulty; j++){if (deep[i][j] != MINE && face[i][j] == '_')//只要还有非雷格子没探索,返回2,就继续{return 2;}}}return 1;//没有踩雷,且没有非雷格子未探索,返回1,则赢
}

检查是否胜利,或者是否失败,以及游戏是否要继续。

4、连续揭示

连续揭示,原版的一种操作,连续解释的赋值为 L ,方便实现当玩家探索一个没有雷的空块时,程序应该自动揭示与该空块相邻的所有空块,直到遇到数字(即与雷相邻的块)。这个特性是原版扫雷游戏的关键特点,可以加快游戏进程。避免玩家只能一个坐标一个坐标探索浪费太多时间。

//连续揭示,点的是周围没有雷的格子,即数字零,周围就会连续揭示,实现原版操作
//连续揭示,原版的一种操作,连续解释的赋值为 L ,方便实现当玩家探索一个没有雷的空块时,程序应该自动揭示与该空块相邻的所有空块,直到遇到数字(即与雷相邻的块)。这个特性是原版扫雷游戏的关键特点,加快游戏进程
void LinkChange(int deep[ROW][COL], char face[ROW][COL], int i, int j,int difficulty)
{//由于下面使用了递归,必须加入一些条件使得递归不会一直递归下去if (!CheckIfValid(i, j, difficulty))//只要超出雷区范围,直接返回,函数返回值为void,直接return,不带任何值{return;}if (face[i][j] == 'L')//已经设置为连续揭示标志 L 的则直接返回{return;}if (deep[i][j] > 0 && deep[i][j] < 9)//在数字0旁边的非零数字,也设置为 L 并且返回{face[i][j] = 'L';return;}if (deep[i][j] == 9)//是地雷则直接返回{return;}if (deep[i][j] == 0)//一个位置为0,将这个位值设为 L 并且探索它周围八个格子,进行递归{face[i][j] = 'L';LinkChange(deep, face, i - 1, j - 1, difficulty);LinkChange(deep, face, i - 1, j, difficulty);LinkChange(deep, face, i - 1, j + 1, difficulty);LinkChange(deep, face, i, j - 1, difficulty);LinkChange(deep, face, i, j + 1, difficulty);LinkChange(deep, face, i + 1, j - 1, difficulty);LinkChange(deep, face, i + 1, j, difficulty);LinkChange(deep, face, i + 1, j + 1, difficulty);}
}

5、检查坐标是否有效

//检查坐标是否有效
int CheckIfValid(int row, int col,int difficulty)
{if (row >= 0 && row < difficulty && col >= 0 && col < difficulty)//检查坐标是否在雷区内{return 1;//在雷区内为有效,返回1为有效}return 0;//不在雷区内为无效,返回0为无效
}

四、游戏难度部分

1、简单难度

简单难度的格子为9*9大小,有9个地雷。

//简单难度
void GameEasy(int deep[ROW][COL], char face[ROW][COL])
{DeepBoardIntial(deep, 9);//简单难度格子为 9 * 9 大小FaceBoardIntial(face, 9);RandomMineGenerate(deep, 9);//随机生成地雷FillinNum(deep, 9);//装填数字int row = 0, col = 0;//用来接收玩家输入行列int check = 0;//检查是否胜利,0为失败,1为胜利,2为继续do{system("cls");//清屏BoardPrint(deep, face, 9);//打印while(1){printf("输入行列>");scanf("%d %d", &row, &col);if (CheckIfValid(row - 1, col - 1,9) && face[row - 1][col - 1] != 'x')//有效且是没被揭开的则输入成功{face[row - 1][col - 1] = 'x';break;//输入成功则直接退出循环}else if (CheckIfValid(row - 1, col - 1, 9) && (face[row - 1][col - 1] == 'x' || face[row - 1][col - 1] == 'L'))//输入的坐标为已经揭示的格子,则重新输入{printf("已经揭示的格子,请不要重复操作!重新输入!\n");}else//不在范围内,重新输入{printf("范围错误!重新输入!\n");}}check = IsGameWin(deep, face,row - 1,col - 1,9);//检查是否胜利或失败以及游戏是否继续if(check == 2)//只要游戏继续,就判断是否连续揭示{if(deep[row - 1][col - 1] == 0)//揭示的格子是空格(即格子是0,周围八个格子没有地雷)则尝试连续揭示{LinkChange(deep, face, row - 1, col - 1,9);//对于这个输入的格子进行连续揭示}}} while (check == 2);//只要check为2则继续,则循环继续,则游戏继续,check为1或0(即赢或输)是退出循环if (check == 1)//check为1时胜利{printf("你赢了!\n");}else if(check == 0)//check为0时失败{printf("你输了!\n");}else//错误{printf("ERROR!\n");}
}

2、中等难度

中等难度大小为16*16,有32个地雷。

//中等难度
void GameMedium(int deep[ROW][COL], char face[ROW][COL])
{DeepBoardIntial(deep, 16);//中等难度格子为 16 * 16 大小FaceBoardIntial(face, 16);RandomMineGenerate(deep, 16,32);//随机生成地雷FillinNum(deep, 16);//装填数字int row = 0, col = 0;//用来接收玩家输入行列int check = 0;//检查是否胜利,0为失败,1为胜利,2为继续do{system("cls");//清屏BoardPrint(deep, face, 16);//打印while (1){printf("输入行列>");scanf("%d %d", &row, &col);if (CheckIfValid(row - 1, col - 1, 16) && face[row - 1][col - 1] != 'x' && face[row - 1][col - 1] != 'L')//有效且是没被揭开的则输入成功{face[row - 1][col - 1] = 'x';break;//输入成功则直接退出循环}else if (CheckIfValid(row - 1, col - 1, 16) && (face[row - 1][col - 1] == 'x' || face[row - 1][col - 1] == 'L'))//输入的坐标为已经揭示的格子,则重新输入{printf("已经揭示的格子,请不要重复操作!重新输入!\n");}else//不在范围内,重新输入{printf("范围错误!重新输入!\n");}}check = IsGameWin(deep, face, row - 1, col - 1, 16);//检查是否胜利或失败以及游戏是否继续if (check == 2)//只要游戏继续,就判断是否连续揭示{if (deep[row - 1][col - 1] == 0)//揭示的格子是空格(即格子是0,周围八个格子没有地雷)则尝试连续揭示{LinkChange(deep, face, row - 1, col - 1, 16);//对于这个输入的格子进行连续揭示}}} while (check == 2);//只要check为2则继续,则循环继续,则游戏继续,check为1或0(即赢或输)是退出循环if (check == 1)//check为1时胜利{printf("你赢了!\n");}else if (check == 0)//check为0时失败{printf("你输了!\n");}else//错误{printf("ERROR!\n");}
}

3、大师难度

大师难度格子为30*30,有90个地雷。

//大师难度
void GameExpert(int deep[ROW][COL], char face[ROW][COL])
{DeepBoardIntial(deep, 30);//大师难度格子为 30 * 30 大小FaceBoardIntial(face, 30);RandomMineGenerate(deep, 30,90);//随机生成地雷FillinNum(deep, 30);//装填数字int row = 0, col = 0;//用来接收玩家输入行列int check = 0;//检查是否胜利,0为失败,1为胜利,2为继续do{system("cls");//清屏BoardPrint(deep, face, 30);//打印while (1){printf("输入行列>");scanf("%d %d", &row, &col);if (CheckIfValid(row - 1, col - 1, 30) && face[row - 1][col - 1] != 'x' && face[row - 1][col - 1] != 'L')//有效且是没被揭开的则输入成功{face[row - 1][col - 1] = 'x';break;//输入成功则直接退出循环}else if (CheckIfValid(row - 1, col - 1, 30) && (face[row - 1][col - 1] == 'x' || face[row - 1][col - 1] == 'L'))//输入的坐标为已经揭示的格子,则重新输入{printf("已经揭示的格子,请不要重复操作!重新输入!\n");}else//不在范围内,重新输入{printf("范围错误!重新输入!\n");}}check = IsGameWin(deep, face, row - 1, col - 1, 30);//检查是否胜利或失败以及游戏是否继续if (check == 2)//只要游戏继续,就判断是否连续揭示{if (deep[row - 1][col - 1] == 0)//揭示的格子是空格(即格子是0,周围八个格子没有地雷)则尝试连续揭示{LinkChange(deep, face, row - 1, col - 1, 30);//对于这个输入的格子进行连续揭示}}} while (check == 2);//只要check为2则继续,则循环继续,则游戏继续,check为1或0(即赢或输)是退出循环if (check == 1)//check为1时胜利{printf("你赢了!\n");}else if (check == 0)//check为0时失败{printf("你输了!\n");}else//错误{printf("ERROR!\n");}
}

4、噩梦难度

噩梦难度格子为50*50,地雷为250个。

//噩梦难度
void GameNightmare(int deep[ROW][COL], char face[ROW][COL])
{DeepBoardIntial(deep, 50);//噩梦难度格子为 50 * 50 大小FaceBoardIntial(face, 50);RandomMineGenerate(deep, 50, 250);//随机生成地雷FillinNum(deep, 50);//装填数字int row = 0, col = 0;//用来接收玩家输入行列int check = 0;//检查是否胜利,0为失败,1为胜利,2为继续do{system("cls");//清屏BoardPrint(deep, face, 50);//打印while (1){printf("输入行列>");scanf("%d %d", &row, &col);if (CheckIfValid(row - 1, col - 1, 50) && face[row - 1][col - 1] != 'x' && face[row - 1][col - 1] != 'L')//有效且是没被揭开的则输入成功{face[row - 1][col - 1] = 'x';break;//输入成功则直接退出循环}else if (CheckIfValid(row - 1, col - 1, 50) && (face[row - 1][col - 1] == 'x' || face[row - 1][col - 1] == 'L'))//输入的坐标为已经揭示的格子,则重新输入{printf("已经揭示的格子,请不要重复操作!重新输入!\n");}else//不在范围内,重新输入{printf("范围错误!重新输入!\n");}}check = IsGameWin(deep, face, row - 1, col - 1, 50);//检查是否胜利或失败以及游戏是否继续if (check == 2)//只要游戏继续,就判断是否连续揭示{if (deep[row - 1][col - 1] == 0)//揭示的格子是空格(即格子是0,周围八个格子没有地雷)则尝试连续揭示{LinkChange(deep, face, row - 1, col - 1, 50);//对于这个输入的格子进行连续揭示}}} while (check == 2);//只要check为2则继续,则循环继续,则游戏继续,check为1或0(即赢或输)是退出循环if (check == 1)//check为1时胜利{printf("你赢了!\n");}else if (check == 0)//check为0时失败{printf("你输了!\n");}else//错误{printf("ERROR!\n");}
}

五、主函数部分

主函数集成游戏的功能。

int main()
{int deep[ROW][COL];//深层,用来记录布置好的地雷和数字,及埋藏在下层的内容char face[ROW][COL];//表面,用来打印和记录玩家操作,表层,对玩家操作进行记录int menu1 = 0;//外层菜单,0为退出游戏,1为进入游戏int menu2 = 0;//内层菜单,0为返回上一步,1为简单难度,2为中等难度,3为大师难度do{GameMenu(1);//外菜单打印scanf("%d", &menu1);//接收选择switch(menu1){case 0://退出游戏printf("你已退出游戏!\n");break;case 1://内层菜单,选择难度GameMenu(2);//内层菜单打印scanf("%d", &menu2);//接收选择switch(menu2){case 0://返回上一界面break;case 1:GameEasy(deep, face);//简单难度break;case 2:GameMedium(deep, face);//中等难度break;case 3:GameExpert(deep, face);//大师难度break;case 4:GameNightmare(deep, face);//噩梦难度break;default:printf("ERROR!\n");break;}break;default :printf("ERROR!\n");break;}} while (menu1);//只要非零就一直循环,进行游戏,为0则退出游戏return 0;
}

六、头文件

头文件声明函数。

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <time.h>#define ROW 50//宏定义,方便更改
#define COL 50
#define MINE 9//宏定义,定义9为雷//菜单打印
void GameMenu(int x);//深层数组初始化
void DeepBoardIntial(int deep[ROW][COL],int difficulty);//表面数组初始化
void FaceBoardIntial(char face[ROW][COL],int difficulty);//连续揭示打印
void LinkChange(int deep[ROW][COL], char face[ROW][COL], int i, int j,int difficulty);//实际打印实现
void ContentPrint(int deep[ROW][COL], char face[ROW][COL], int i, int j);//面板打印
void BoardPrint(int deep[ROW][COL], char face[ROW][COL],int difficulty);//随机生成雷
void RandomMineGenerate(int deep[ROW][COL], int difficulty,int minesnum);//检查是否胜利
int IsGameWin(int deep[ROW][COL], char face[ROW][COL], int row, int col, int difficulty);//填入数字
void FillinNum(int deep[ROW][COL], int difficulty);//检查输入坐标是否有效
int CheckIfValid(int row, int col, int difficulty);//简单难度
void GameEasy(int deep[ROW][COL], char face[ROW][COL]);//中等难度
void GameMedium(int deep[ROW][COL], char face[ROW][COL]);//大师难度
void GameExpert(int deep[ROW][COL], char face[ROW][COL]);//噩梦难度
void GameNightmare(int deep[ROW][COL], char face[ROW][COL]);

七、一些问题和解决办法

1、打印显示问题

在游玩专家难度及以上的难度时,或者在游玩你自己改进的难度时(你可以通过更改宏ROW和COL的大小实现更大的格子范围),会发现打印出现下列状况:

打印出来的格子错位了。

这个问题可以通过全屏控制台解决,若还不行的话,可能是格子范围太大,可以通过使用Ctrl + 鼠标滚轮来对控制台缩放,Ctrl + 鼠标滚轮下滑可以缩放控制台,使得格子复位。在你不能看到完整的雷区时也可以使用这个方法。

2、输入设置

默认输入坐标是:

			scanf("%d %d", &row, &col);

输入坐标时中间要加一个空格或回车分开,例如

中间不能加其他字符,加其他字符会出错,你也可以改为:

			scanf("%d,%d", &row, &col);

这样就只能用 , 进行分隔了,不能用其他字符分隔了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/227915.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

x-cmd pkg | gum - 很好看的终端 UI 命令行工具

目录 简介首次用户功能特点Bubbles 与 Lip Gloss进一步探索 简介 gum 由 Charm 组织于 2022 年使用 Go 语言开发。旨在帮助用户编写 Shell 脚本与 dotfiles 时提供一系列快捷使用&#xff0c;可配置&#xff0c;可交互&#xff0c;美观的 Terminal UI 组件。 首次用户 使用 x…

常用的 MySQL 可视化客户端

数据库可视化客户端&#xff08;GUI&#xff09;让用户在和数据库进行交互时&#xff0c;能直观地查看、创建和修改对象&#xff0c;如&#xff1a;表、行和列。让数据库操作变得更方便了。 今天&#xff0c;我们来了解下目前市场上最常用的 MySQL 可视化客户端。 官方&#x…

详解数组的轮转

&#x1d649;&#x1d65e;&#x1d658;&#x1d65a;!!&#x1f44f;&#x1f3fb;‧✧̣̥̇‧✦&#x1f44f;&#x1f3fb;‧✧̣̥̇‧✦ &#x1f44f;&#x1f3fb;‧✧̣̥̇:Solitary-walk ⸝⋆ ━━━┓ - 个性标签 - &#xff1a;来于“云”的“羽球人”。…

c# listbox 添加图标和文字

给listbox 添加 DrawItem 事件 private void listBox1_DrawItem(object sender, DrawItemEventArgs e){int index e.Index;//获取当前要进行绘制的行的序号&#xff0c;从0开始。Graphics g e.Graphics;//获取Graphics对象。Rectangle bound e.Bounds;//获取当前要绘制的行的…

【设计模式】状态模式

文章目录 引例状态模式理论状态模式代码优化结合享元模式并发问题解决 策略模式 VS 状态模式 引例 交通信号灯系统的设计与实现 方案一 传统设计方案 定义交通灯颜色的枚举 public enum LightColor { Green,Red,Yellow }交通灯类TrafficLight&#xff0c;处理颜色转换等业务…

【AMD Xilinx】ZUBoard(3):通过AXI GPIO接收PL端的按键输入

【AMD Xilinx】ZUBoard&#xff08;3&#xff09;&#xff1a;通过AXI GPIO接收PL端的按键输入 一、本项目实现的功能二、Vivado工程1. 添加AXI GPIO2. 配置AXI GPIO3. 根据原理图查找对应管脚4. I/O Planning5.XDC 三、ARM代码1. 地址空间2. 函数说明3. 实际的C代码实现4. 运行…

VitulBox中Ubuntu虚拟机安装JAVA环境——备赛笔记——2024全国职业院校技能大赛“大数据应用开发”赛项

前言 在进行之后操作是请下载好JDK&#xff0c;之后的内容是以Ubuntu虚拟机中安装java环境续写。 提示&#xff1a;以下操作是在虚拟机hadoop用户下操作的&#xff0c;并为安装java环境作准备 一、更新APT 为了确保Hadoop安装过程顺利进行&#xff0c;建议用hadoop用户登录…

基于SpringBoot实现的前后端分离电影评分项目,功能:注册登录、浏览影片、热门影片、搜索、评分、片单、聊天、动态

一、项目介绍 本项目主要基于SpringBoot、Mybatis-plus、MySQL、Redis实现的影片评分项目。 本系统是前后端分离的&#xff0c;分别由三个子项目构成&#xff1a;java服务端、用户前端、管理员管理前端 关键词&#xff1a;springboot java vue mysql reids websocket 毕业设计…

2023年“中银杯”四川省职业院校技能大赛“云计算应用”赛项样题卷①

2023年“中银杯”四川省职业院校技能大赛“云计算应用”赛项&#xff08;高职组&#xff09; 样题&#xff08;第1套&#xff09; 目录 2023年“中银杯”四川省职业院校技能大赛“云计算应用”赛项&#xff08;高职组&#xff09; 样题&#xff08;第1套&#xff09; 模块一…

Java Log 学习笔记

参考文章&#xff1a; 1.Java 日志从入门到实战 2.Java日志框架的发展历史&#xff0c;你不想了解一下吗 背景 想自定义 logback 配置文件进行日志分级别记录到不同文件&#xff0c;遇到了几个问题&#xff08;使用的是 spring-boot 构建的项目&#xff0c;spring-boot 版本为…

多模态大模型-CogVLm 论文阅读笔记

多模态大模型-CogVLm 论文阅读笔记 COGVLM: VISUAL EXPERT FOR LARGE LANGUAGEMODELS 论文地址 :https://arxiv.org/pdf/2311.03079.pdfcode地址 : https://github.com/THUDM/CogVLM时间 : 2023-11机构 : zhipuai,tsinghua关键词: visual language model效果:&#xff08;2023…

MySQL基础入门(二)

多表内容 一对多 这个内容是黑马的入门问题&#xff0c;可以带大家思考一下这个怎么设计 我们要知道一个岗位可以对应很多用户&#xff0c;而一个用户只能对应一个岗位&#xff0c;这就属于一对多的类型 那么我们需要怎么将他们进行关联呢&#xff1f; 现在我们可以通过一个…

蓝桥杯C/C++程序设计——单词分析

题目描述 小蓝正在学习一门神奇的语言&#xff0c;这门语言中的单词都是由小写英文字母组 成&#xff0c;有些单词很长&#xff0c;远远超过正常英文单词的长度。小蓝学了很长时间也记不住一些单词&#xff0c;他准备不再完全记忆这些单词&#xff0c;而是根据单词中哪个字母出…

Zookeeper 实战 | Zookeeper 和Spring Cloud相结合解决分布式锁、服务注册与发现、配置管理

专栏集锦&#xff0c;大佬们可以收藏以备不时之需&#xff1a; Spring Cloud 专栏&#xff1a;http://t.csdnimg.cn/WDmJ9 Python 专栏&#xff1a;http://t.csdnimg.cn/hMwPR Redis 专栏&#xff1a;http://t.csdnimg.cn/Qq0Xc TensorFlow 专栏&#xff1a;http://t.csdni…

Media Encoder各版本安装指南

Media Encoder 链接地址如下&#xff1a; https://pan.baidu.com/s/1qOowHfm9rhrOOMljYgQCsA?pwd0531 1.鼠标右击【ME2023(64bit)】压缩包选择&#xff08;win11系统需先点击“显示更多选项”&#xff09;【解压到 ME2023(64bit)】。 2.打开解压后的文件夹&#xff0c;鼠标右…

16.综合项目实战

一、基础演练&#xff1a; 1、建库、建表 # 创建数据库 create database mysql_exampleTest; use mysql_exampleTest; # 学生表 CREATE TABLE Student( s_id VARCHAR(20), s_name VARCHAR(20) NOT NULL DEFAULT , s_birth VARCHAR(20) NOT NULL DEFAULT , s_sex VARC…

分布式存储考点梳理 + 高频面试题

欢迎来到分布式存储模环节&#xff0c;本文我将和你一起梳理面试中分布式系统的数据库的高频考点&#xff0c;做到温故知新。 面试中如何考察分布式存储 广义的分布式存储根据不同的应用领域&#xff0c;划分为以下的类别&#xff1a; 分布式协同系统 分布式文件系统 分布式…

运行时错误‘53’文件未找到:MathPage.WLL,安装MathType后Word不能复制粘贴问题的解决

两步解决&#xff1a; 1. 打开Word-->文件-->选项-->信任中心-->信任中心设置-->受信任位置&#xff0c;解决宏问题 添加如下受信任位置&#xff0c; 我的路径&#xff1a;C:\Program Files\Microsoft Office\root\Office16\STARTUP\ 2. 找到MathType下的MathT…

GitHub 一周热点汇总 第3期 (2023/12/24-12/30)

GitHub一周热点汇总第三期 (2023/12/24-12/30)&#xff0c;梳理每周热门的GitHub项目&#xff0c;了解热点技术趋势&#xff0c;掌握前沿科技方向&#xff0c;发掘更多商机。元旦就要到了&#xff0c;提前祝大家新年快乐。 #1 StreamDiffusion 项目名称&#xff1a;StreamDiff…

安装kafka

静态文件安装&#xff08;单机&#xff09; 解压到指定目录&#xff08;解压到 /usr&#xff09; tar -zxf kafka_2.11-2.2.0.tgz -C /usr/ 到指定的解压目录下 cd /usr/kafka_2.11-2.2.0/ 配置主机名 查看是否配置了HOSTNAME vim /etc/sysconfig/network 没有就新增 HOSTNA…