用C语言写个控制台扫雷游戏附完整代码
用C语言写个控制台扫雷游戏(附完整代码)
前言
扫雷游戏大家应该都玩过,虽然win10以后更新了,但还是win7版的好用,在代码写烦了来上几把换换脑子还是不错滴。
下面用c语言模仿win7版的写一个控制台扫雷游戏。
一、对控制台的控制
游戏中要对控制台的大小、文字颜色、光标大小、光标位置、鼠标输入等需要进行一些设置,下面列出所需要的一些函数。
1.控制台大小
控制台大小通过mode命令设置。这个命令中的大小是指行和列的字符数而非像素。
//设置控制台大小
void SetSize(unsigned uCol, unsigned uLine)
{
char cmd[64];
sprintf(cmd, "mode con cols=%d lines=%d", uCol, uLine);
system(cmd);
}
2.控制台颜色
控制台颜色设置详情可查看:
//更改文字颜色
// color为每一种颜色所代表的数字,范围是0~15
void setColor(WORD color)
{
HANDLE HOutput = GetStdHandle(STD_OUTPUT_HANDLE); //获取标准输出的句柄
SetConsoleTextAttribute(HOutput, color);
}
3.控制台光标样式
更改光标样式要用到结构体CONSOLE_CURSOR_INFO 和控制台API函数SetConsoleCursorInfo,通过这个结构体和函数可以设置光标的可见性和大小。
// 设置光标样式
void SetCursorType(BOOL bVisible)
{
HANDLE HOutput = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_CURSOR_INFO curInfo;
curInfo.bVisible = bVisible; //更改光标显示状态,TRUE显示光标,FALSE隐藏光标
curInfo.dwSize = 100; // 光标大小1到100之间。 范围从完全填充单元格到单元底部的水平线条。
SetConsoleCursorInfo(HOutput, &curInfo);
}
4.控制台光标位置
这个函数用的比较多,用来定位要输出的字符的位置。
//设置光标位置,起点从1开始
void MoveCursorTo(int nCols, int nRows)
{
COORD crdLocation = {nCols, nRows};
HANDLE HOutput = GetStdHandle(STD_OUTPUT_HANDLE); //获取标准输出的句柄
SetConsoleCursorPosition(HOutput, crdLocation); //设置光标位置
}
5.获取控制台光标位置字符
这个函数用的比较多,用来定位要输出的字符的位置。
/// 提取出窗口中第x行y列的位置的字符
/// @return 返回指定位置显示的字符,空白处返回空格
char GetCursorStr(int x, int y)
{
COORD pos = {x, y};
char str = 0;
DWORD read;
// HANDLE HOutput = GetStdHandle(STD_OUTPUT_HANDLE); //获取标准输出的句柄
ReadConsoleOutputCharacterA(HOutput, &str, 1, pos, &read);
return str;
}
6.控制台显示位置
通过API函数MoveWindow设置窗口的位置。在设置窗口位置之前,要先取得控制台窗口大小和屏幕大小,通过计算得出屏幕居中的位置。
//居中显示控制台
BOOL MoveConsoleWindow()
{
//获取屏幕大小
HDC hdc = GetDC(NULL);
int cx = GetDeviceCaps(hdc, DESKTOPHORZRES); //窗口左侧位置
int cy = GetDeviceCaps(hdc, DESKTOPVERTRES); //窗口顶部位置
ReleaseDC(NULL, hdc);
//获取控制台大小
HWND HOutput = GetConsoleWindow();
RECT rect;
GetWindowRect(HOutput, &rect);
int w = rect.right - rect.left; //窗口宽度
int h = rect.bottom - rect.top; //窗口高度
cx = (cx - w) / 2;
cy = (cy - h) / 2;
return MoveWindow(HOutput, cx, cy, w, h, TRUE);
}
7.控制台鼠标操作设置
除了上面,还需要对控制台的鼠标操作进行屏蔽。
#define DISABLE_QUICK_EDIT_MODE 0x01
#define DISABLE_INSERT_MODE 0x02
#define DISABLE_MOUSE_INPUT 0x03
#define DISABLE_ALL (DISABLE_QUICK_EDIT_MODE | DISABLE_INSERT_MODE | DISABLE_MOUSE_INPUT)
//执行控制台相关设置
void CloseConsoleMode(UINT uTag)
{
HANDLE HInput = GetStdHandle(STD_INPUT_HANDLE);//这里用标准输入设备
DWORD mode;
GetConsoleMode(HInput, &mode);
if (uTag & DISABLE_QUICK_EDIT_MODE)
mode &= ~ENABLE_QUICK_EDIT_MODE; //移除快速编辑模式
if (uTag & DISABLE_INSERT_MODE)
mode &= ~ENABLE_INSERT_MODE; //移除插入模式
if (uTag & DISABLE_MOUSE_INPUT)
mode &= ~ENABLE_MOUSE_INPUT; //移除鼠标输入
SetConsoleMode(HInput, mode);
}
二、游戏界面
1.游戏难度选择界面
win版的扫雷有3种难度和自定义难度,这里通过选择界面选择游戏难度。
先看看效果
1.1 基本定义
定义结构体保存游戏的格子数量和地雷数量。
#define CONSOLEWIDTH 80 //控制台宽度
#define CONSOLEHEIGHT 40 //控制台高度
#define DEFCOLOR 0x7 //控制台默认文字颜色
typedef struct _point
{
short x;
short y;
} Point;
//格子大小
typedef struct _MapSize
{
Point MapSize; //格子数量
short Mine_Count; //地雷数量
} Map_Size;
//按照win扫雷定义尺寸和雷数
Map_Size defMapSize[3] = {{{9, 9}, 10}, {{16, 16}, 40}, {{30, 16}, 99}};
1.2显示标题信息
- 使用setColor更改文字颜色
- sprintf给buf输出字符宽度控制居中显示。
- 显示输出后文字颜色改回默认颜色
void PrintName()
{
system("cls");
setColor(0x2);
char buf[100];
int l = (CONSOLEWIDTH - 20) / 2 + 20;
sprintf(buf, "\n%%%ds\n", l);
printf(buf, "欢迎来到扫雷小游戏");
sprintf(buf, "%%%ds\n", (CONSOLEWIDTH - 16) / 2 + 16);
printf(buf, "-- by Apull --");
setColor(DEFCOLOR);
}
1.3显示难度选项
- 不同难度使用不同颜色显示
- 游戏界面中横向格子之间用空格隔开,因此在选定难度后,横向格子数量需要翻倍,更改为MapSize.MapSize.x * 2 - 1
//难度选择
void Choose()
{
MapSize = defMapSize[0];
PrintName();
printf("\t\t选择游戏难度:\n");
setColor(0xA);
printf("\t\t 1、初级:%dx%d,共%d个地雷\n", defMapSize[0].MapSize.x, defMapSize[0].MapSize.y, defMapSize[0].Mine_Count);
setColor(0xE);
printf("\t\t 2、中级:%dx%d,共%d个地雷\n", defMapSize[1].MapSize.x, defMapSize[1].MapSize.y, defMapSize[1].Mine_Count);
setColor(0xc);
printf("\t\t 3、高级:%dx%d,共%d个地雷\n", defMapSize[2].MapSize.x, defMapSize[2].MapSize.y, defMapSize[2].Mine_Count);
setColor(0x9);
printf("\t\t 4、自定义难度\n");
setColor(DEFCOLOR);
printf("\t\t 0、退出\n");
printf("\t\t输入你的选择(回车默认选择1): ");
char ch;
BOOL inputOK = TRUE;
while (inputOK)
{
ch = _getch();
switch (ch)
{
case '1':
case Key_ENTER:
MapSize = defMapSize[0];
inputOK = FALSE;
break;
case '2':
MapSize = defMapSize[1];
inputOK = FALSE;
break;
case '3':
MapSize = defMapSize[2];
inputOK = FALSE;
break;
case '4':
printf("\n");
MapSize.MapSize.y = OptMines("\t\t 高度:", 9, 24);
MapSize.MapSize.x = OptMines("\t\t 宽度:", 9, 30);
MapSize.Mine_Count = OptMines("\t\t 雷数:", 10, 668);
inputOK = FALSE;
break;
case '0':
case Key_ESC:
EXIT(0);
break;
default:
break;
}
}
MapSize.MapSize.x = MapSize.MapSize.x * 2 - 1;
}
1.4 选择自定义难度
- 为了防止输入错误后的无限循环,这里使用字符串接收输入,再转换成int类型。
- 设定地雷数量是所有格子数量的20%
- 输入错误使用红色文字提示
//自定义难度
int OptMines(char *str, int min, int max)
{
int x;
char buf[20] = {0};
if (max > 30) // max > 30 表示输入的是雷数
{
max = (int)(MapSize.MapSize.x * MapSize.MapSize.y * 0.2); //地雷数量是所有格子数的20%
}
while (TRUE)
{
printf(str);
printf("(%d-%d):", min, max);
gets_s(buf, 20);
if (buf[0] == '0')
EXIT(0);
x = atoi(buf);
if (x < min || x > max)
{
setColor(0x04);
printf("\t\t输入超出范围,请重新输入。\n");
setColor(DEFCOLOR);
}
else
return x;
}
}
2 游戏界面
2.1 基本定义
2.1.1 定义地雷格子结构
结构体 struct _mine 中定义一个格子要显示的字符、周围8个方向上地雷数量以及格子本身的状态。
//定义地雷格子状态
enum Mine_STATU
{
HIDDEN_SAFE,
VISIBLE_SAFE,
HIDDEN_MINE
};
//地雷格子定义
typedef struct _mine
{
char ch;
char hasMines;
enum Mine_STATU statu;
} Mine;
#define FLAG '@' //标记的地雷
#define MAPNORMAL 'o' //正常格子
#define MINEICO '*' //地雷
2.1.2 填充地雷格子
使用二维数组 minefield 存储每个格子,根据难度选择给二维数组分配内存大小。
Mine **minefield = NULL; //格子数组
//生成格子数组,并填充显示字符
Mine **initMapArr()
{
Mine **minefield = (Mine **)calloc(MapSize.MapSize.y, sizeof(Mine *));
if (minefield == NULL)
{
printf("内存分配错误!");
EXIT(-1);
}
for (int i = 0; i < MapSize.MapSize.y; i++)
{
minefield[i] = (Mine *)calloc(MapSize.MapSize.x, sizeof(Mine));
if (minefield == NULL)
{
printf("内存分配错误!");
EXIT(-1);
}
for (int j = 0; j < MapSize.MapSize.x; j++)
{
minefield[i][j].ch = (j % 2) ? ' ' : MAPNORMAL;
}
}
return minefield;
}
2.1.3 产生地雷
- 使用动态数组 MinesPos 保存地雷所在位置
- 用随机数产生地雷
- 检索重复项,有重复的则重新生成地雷。
Point *MinesPos = NULL; //地雷所在位置
Point *FindMinesPos = NULL; //标记的地雷所在位置
int MineCount = 0; //地雷总数
//产生地雷
void init_Mine(Point *point, int MineCount)
{
Point pot;
for (int i = 0, j; MineCount > i;)
{
do
{
pot.x = rand() % MapSize.MapSize.x;
} while (pot.x % 2 != 0);
pot.y = rand() % MapSize.MapSize.y;
for (j = 0; j < i; j++)
{
if (point[j].x == pot.x && point[j].y == pot.y)
break;
}
if (i == j)
{
point[i].x = pot.x;
point[i].y = pot.y;
i++;
}
}
}
2.1.4 将地雷放置到格子中
数组 MinesPos 中保存着地雷的坐标,直接在格子中把这些坐标标记为地雷。
//写入雷的位置
void init_Map(Mine **minefield, Point *point, int MineCount)
{
for (int i = 0; i < MineCount; i++)
{
minefield[point[i].y][point[i].x].statu = HIDDEN_MINE;
minefield[point[i].y][point[i].x].hasMines = -1;
}
}
2.1.5 计算周围地雷数量
- 按行列扫描所有格子
- 对一个格子的周围8个位置的地雷数量进行统计
- 将统计数量保存到格子的 hasMines 属性中
//计算周围地雷数量
void calMine(Mine **minefield)
{
int count;
for (int i = 0; i < MapSize.MapSize.y; i++)
{
for (int j = 0; j < MapSize.MapSize.x; j += 2)
{
if (minefield[i][j].statu == HIDDEN_MINE)
continue;
count = 0;
if (i > 0 && j > 1 && minefield[i - 1][j - 2].statu == HIDDEN_MINE) //左上角
count++;
if (i > 0 && minefield[i - 1][j].statu == HIDDEN_MINE) //上面
count++;
if (i > 0 && j < MapSize.MapSize.x - 2 && minefield[i - 1][j + 2].statu == HIDDEN_MINE) //右上角
count++;
if (j < MapSize.MapSize.x - 1 && minefield[i][j + 2].statu == HIDDEN_MINE) //右面
count++;
if (i < MapSize.MapSize.y - 1 && j < MapSize.MapSize.x - 2 && minefield[i + 1][j + 2].statu == HIDDEN_MINE) //右下角
count++;
if (i < MapSize.MapSize.y - 1 && minefield[i + 1][j].statu == HIDDEN_MINE) //下面
count++;
if (i < MapSize.MapSize.y - 1 && j > 0 && minefield[i + 1][j - 2].statu == HIDDEN_MINE) //左下角
count++;
if (j > 0 && minefield[i][j - 2].statu == HIDDEN_MINE) //左面
count++;
minefield[i][j].hasMines = count;
}
}
}
2.1.6 其他变量
Point StatuRow; //状态显示行位置
clock_t start_t, end_t; //记录游戏时间
//格子范围,限定地雷显示和操作的范围
int top = 0, left = 0, right = 0, bottom = 0;
2.2 显示游戏介绍
显示游戏介绍,并返回显示的行数。
// 输出游戏介绍
int intro()
{
int line = 3; //加上之前的标题信息2行
line += 7;
StatuRow.y = line - 2;
StatuRow.x = left;
PrintName(); //显示标题信息
printf("\t\t游戏方法介绍:\n");
setColor(0xE);
printf("\t\t W、S、A、D/↑、↓、←、→");
setColor(DEFCOLOR);
printf(":控制光标上下左右移动\n");
setColor(0xE);
printf("\t\t PageUP、PageDown、Home、End");
setColor(DEFCOLOR);
printf(":控制光标跳到四边位置\n");
setColor(0xE);
printf("\t\t 空格");
setColor(DEFCOLOR);
printf(":打开当前格子");
setColor(0xE);
printf(" R");
setColor(DEFCOLOR);
printf(":回到选择界面");
setColor(0xE);
printf(" ESC");
setColor(DEFCOLOR);
printf(":结束游戏\n");
ShowStatu();
printf("\n\n");
return line;
}
2.3 显示游戏状态
显示游戏信息和游戏时间,光标移动到要输出的位置,输出内容覆盖就内容,完成状态刷新。
在输出状态的时候要移动光标,游戏过程中会看到光标跳到状态显示位置后有会跳回格子中,为避免这种情况,在开始输出状态时使用 SetCursorType 隐藏光标,输出完毕后再显示光标
//显示游戏状态
void ShowStatu()
{
static int total_t = 0;
end_t = clock();
total_t = (end_t - start_t) / CLOCKS_PER_SEC;
SetCursorType(FALSE);
MoveCursorTo(StatuRow.x, StatuRow.y);
printf("\t\t共有雷数:%-3d 剩余雷数:%-3d 用时:%ds ", MapSize.Mine_Count, MineCount, total_t);
SetCursorType(TRUE);
}
2.4 显示地雷格子
- 根据上面显示的信息行数得到地图格子要显示的起始行
- 根据上面 基本定义 的宽度 CONSOLEWIDTH 计算居中显示的起始列
- 每一行光标移动到左边起始列后输出行
//确定绘制地雷格子范围
void DrawMine()
{
left = right = bottom = 0;
top = intro();
drawMap(minefield);
right = MapSize.MapSize.x + left;
bottom += MapSize.MapSize.y + top - 1;
MoveCursorTo(left, top);
start_t = clock(); //开始计时
}
//绘制地雷格子和外框,设置各边范围
void drawMap(Mine **minefield)
{
int l = (CONSOLEWIDTH - MapSize.MapSize.x) / 2 - 4;
if (l % 2 == 0)
l++;
int t = top;
MoveCursorTo(l, t++);
printf("┌");
for (int i = 0; i <= MapSize.MapSize.x; i++)
printf("─");
printf("┐\n");
MoveCursorTo(l, t++);
// 输出中间部分
for (int i = 0; i < MapSize.MapSize.y; i++)
{
printf("│ ");
for (int j = 0; j < MapSize.MapSize.x; j++)
{
printf("%c", minefield[i][j].ch);
}
printf("│\n");
MoveCursorTo(l, t++);
}
printf("└");
for (int i = 0; i <= MapSize.MapSize.x; i++)
printf("─");
printf("┘\n");
top += 1;
left = l + 2;
}
至此地雷界面绘制基本完成,下面来看看操作部分。
三、游戏操作
1 游戏按键输入
1.1 定义按键
定义出后面要用到达按键变量
//按键定义
enum KB_KEY
{
Key_FN = 0xE0, // 功能键标志
Key_Up = 0x48, // 向上方向键
Key_Down = 0x50, // 向下方向键
Key_Left = 0x4b, // 向左方向键
Key_Right = 0x4d, // 向右方向键
Key_Home = 0x47, // Home键
Key_End = 0x4f, // End键
Key_PageUp = 0x49, // PageUp键
Key_PageDown = 0x51, // PageDown键
Key_ESC = 0x1B, // ESC键
Key_ENTER = 0xD, // 回车键
Key_SPACE = 0x20, // 空格键
Key_A = 'A',
Key_a = 'a',
Key_D = 'D',
Key_d = 'd',
Key_W = 'W',
Key_w = 'w',
Key_S = 'S',
Key_s = 's',
Key_R = 'R',
Key_r = 'r'
} KEY;
1.2 读取按键输入
- 需要注意的是方向键的输入,方向键是先输入0xE0或0,再输入对应的键值,因此输入方向键的输入需要读取2次才能确定。
- 使用 row 和 col 记录当前光标位置,列使用奇数位,因此列的移动要±2。
- 检测移动后光标会不会超出现实范围,如果有超出情况则取消移动。
- 回车 标记地雷
- 空格 点开地雷格子
- R 结束游戏回到选择界面
- ESC 退出游戏
int row = top; //行
int col = left; //列
char ch;
int chInput = 0;
int isGame = 1; //游戏继续标志
int x, y;
while (isGame)
{
if (_kbhit() != 0) //当_kbhit返回值不为0的时候,代表用户有按键按下。
{
chInput = _getch();
if (chInput == Key_FN || chInput == 0) // 方向键读2次
chInput = _getch();
switch (chInput) //控制操作
{
case 'a':
case 'A':
case Key_Left:
col -= 2;
if (col <= left)
col = left;
break;
case 'd':
case 'D':
case Key_Right:
col += 2;
if (col >= right)
col = right - 1;
break;
case 'w':
case 'W':
case Key_Up:
row--;
if (row <= top)
row = top;
break;
case 's':
case 'S':
case Key_Down:
row++;
if (row >= bottom)
row = bottom;
break;
case Key_SPACE: //空格点开
...
break;
case Key_ENTER: //标记地雷
...
break;
case Key_Home: //跳到第一列
col = left;
break;
case Key_End: //跳到最后一列
col = right - 1;
break;
case Key_PageUp: //跳到第一行
row = top;
break;
case Key_PageDown: //跳到最后一行
row = bottom;
break;
case Key_ESC: //退出游戏
isGame = 0;
break;
case Key_R:
case Key_r: //复位到选择界面
init();
DrawMine();
break;
default: //其他输入字符不动作
break;
}
MoveCursorTo(col, row);
}
Sleep(150);
ShowStatu();
MoveCursorTo(col, row);
}
2 标记地雷
- 按 回车 标记地雷。
- 读取光标处的字符,光标处是未点开格子则标记地雷。
- 标记的地雷坐标保存到 FindMinesPos 数组中。
- 如果该位置已标记则取消标记,同时从 FindMinesPos 数组中删除。
- 标记后判断所有的地雷是否都已标记,如果都已标记则判赢
- 标记后光标右移到下一个地雷格子
case Key_ENTER: //标记地雷
if (flagMine(row, col))
GameOver(WIN_GAME);
col += 2;
if (col >= right)
col = right - 1;
break;
//标记地雷
BOOL flagMine(int row, int col)
{
char ch;
BOOL iswin = FALSE;
ch = GetCursorStr(col, row); //获取光标处字符
if (ch == FLAG && MineCount < MapSize.Mine_Count) //切换已标记
{
FindMinesPos[MineCount].x = FindMinesPos[MineCount].y = -1;
setColor(DEFCOLOR);
putchar(MAPNORMAL);
MineCount++;
}
else if (ch == MAPNORMAL && MineCount > 0)
{
MineCount--;
FindMinesPos[MineCount].x = col - left;
FindMinesPos[MineCount].y = row - top;
setColor(0xC);
putchar(FLAG);
if (MineCount == 0)
{
int i;
for (i = 0; i < MapSize.Mine_Count; i++) // 确定是否标记了所有的雷
{
if (findMine4Pos(FindMinesPos[i].x, FindMinesPos[i].y) == FALSE)
break;
}
iswin = i == MapSize.Mine_Count;
}
}
setColor(DEFCOLOR);
ShowStatu(); //更新标记的雷数
MoveCursorTo(col, row);
return iswin;
}
3 点开地雷操作
3.1 点开地雷格子
- 按 空格 点开地雷格子
- 读取光标位置字符,如果是已标记地雷则结束点开操作。
- 如果点开的位置是地雷,游戏结束,显示游戏结束画面
- 点开位置安全,则自动点开相连的安全区域。
- 自动点开后判断是否一点开所有安全区,判断胜负。
- 点开后光标右移到下一个地雷格子
case Key_SPACE: //空格点开
x = col - left;
y = row - top;
ch = GetCursorStr(col, row);
if (ch == FLAG) //忽略点开已标记地雷
break;
if (minefield[y][x].statu == HIDDEN_MINE) //点开雷,游戏结束
{
isGame = GameOver(LOSS_GAME);
row = top;
col = left;
continue;
}
else //自动点开连续的安全区
{
SetCursorType(FALSE);
OpenMine(y, x);
SetCursorType(TRUE);
if (isWin())
{
isGame = GameOver(WIN_GAME);
row = top;
col = left;
continue;
}
}
col += 2;
if (col >= right)
col = right - 1;
break;
3.2 自动展开安全区域
- 递归对当前格子周围的安全区域进行展开操作
- 递归到边界或已点开区域终止
- 点开时使用不用的颜色显示格子周围地雷数。
- 格子周围地雷数为0的显示为 空格’ ’
//自动打开安全区
void OpenMine(int row, int col)
{
int x = 0;
x = col + left;
int y = 0;
y = row + top;
char ch = 0;
if (row < 0 || row >= MapSize.MapSize.y)
return;
if (col < 0 || col >= MapSize.MapSize.x)
return;
if (minefield[row][col].statu == VISIBLE_SAFE)
return;
if (minefield[row][col].hasMines == 0)
ch = ' ';
else if (minefield[row][col].hasMines > 0)
ch = minefield[row][col].hasMines + '0';
else
return;
if (minefield[row][col].hasMines >= 0)
{
MoveCursorTo(x, y);
int n = minefield[row][col].hasMines;
switch (n)
{
case 1:
setColor(0x9); //淡蓝色
break;
case 2:
setColor(0x2); //绿色
break;
case 3:
setColor(0xC); //淡红色
break;
case 4:
setColor(0x1); //蓝色
break;
case 5:
setColor(0x4); //红色
break;
case 6:
setColor(0xD); //淡紫色
break;
case 7:
setColor(0x5); //紫色
break;
case 8:
setColor(0x6); //黄色
break;
}
putchar(ch);
setColor(DEFCOLOR);
minefield[row][col].ch = ch;
minefield[row][col].statu = VISIBLE_SAFE;
if (ch != ' ')
return;
}
if (row > 1)
OpenMine(row - 1, col);
if (col < MapSize.MapSize.x)
OpenMine(row, col + 2);
if (row < MapSize.MapSize.y)
OpenMine(row + 1, col);
if (col >= 2 && col % 2 == 0)
OpenMine(row, col - 2);
}
四、游戏胜负
1 胜负判定
- 游戏过程中如果点开了地雷,则直接判负。
- 比对所有未点开的地雷格子和地雷数组的坐标,如果刚好符合则判赢。
//检查标记地雷是否都正确
BOOL isWin()
{
BOOL iswin = FALSE;
int cnt = 0;
for (int i = 0; i < MapSize.MapSize.y; i++)
{
for (int j = 0; j < MapSize.MapSize.x; j++)
{
if (minefield[i][j].ch == MAPNORMAL)
{
if (findMine4Pos(j, i) == FALSE)
{
i = MapSize.MapSize.y + 1;
break;
}
cnt++;
}
}
}
iswin = cnt == MapSize.Mine_Count;
return iswin;
}
2 胜负界面
- 外部函数判断胜负后传递胜负结果变量给 GameOver 函数。
- 根据参数使用不同颜色显示不同提示。
- 光标定位到地雷格子往下2行位置输出胜负信息。
- 根据提示返回是否继续游戏
//胜负结果变量
enum GAME_RESULT
{
WIN_GAME,
LOSS_GAME
};
//游戏结束画面
BOOL GameOver(enum GAME_RESULT statu)
{
MoveCursorTo(0, bottom + 2);
if (statu == WIN_GAME)
{
// system("color 2F");//改变背景为绿色
showMine(TRUE);
setColor(0x02);
printf("\t 恭喜,你找出了所有的地雷!\n");
}
if (statu == LOSS_GAME)
{
// system("color 4F"); //改变背景为红色
showMine(FALSE);
setColor(0x04);
printf("\t 哦吼,踩到雷了!\n");
}
setColor(DEFCOLOR);
BOOL rst = FALSE;
char ch;
printf("\n\t 是否重新开始游戏(Y/N): ");
fflush(stdin);
do
{
ch = _getch();
if (ch == 'Y' || ch == 'y' || ch == Key_ENTER)
{
init();
DrawMine();
rst = TRUE;
break;
}
else if (ch == 'N' || ch == 'n' || ch == Key_ESC)
{
rst = FALSE;
break;
}
} while (TRUE);
return rst;
}
胜利界面
失败界面
五、总结
没有总结
完整代码已上传到GitCode,需要的移步