目录
一、游戏初始化(Game Initialization)
#include <stdlib.h>
#include <time.h> // 用时间戳作为随机数种子
#define ROW 10
#define COL 10
#define MINE_COUNT 10
int board[ROW][COL] = { 0 }; // 创建一个二维数组来存储棋盘上每个格子的信息
void GameInit()
{
int i = 0; // 控制行
int j = 0; // 控制列
// 1. 特别是当游戏重新开始时,需要将棋盘上的每个格子的信息重新初始化为 0
for (i = 0; i < ROW; i++)
{
for (j = 0; j < COL; j++)
{
board[i][j] = 0;
}
}
// 2. 随机生成 MINE_COUNT 个雷
for (i = 0; i < MINE_COUNT; i++)
{
while (1)
{
int row = rand() % ROW; // row: 0 ~ ROW - 1
int col = rand() % COL; // col: 0 ~ COL - 1
if (board[row][col] == 0) // 避免在同一个格子上埋雷
{
board[row][col] = -1; // -1 表示这个格子上埋有雷
break;
}
}
}
// 3. 对以雷为中心九宫格进行 +1 操作,埋雷的格子除外
for (i = 0; i < ROW; i++)
{
for (j = 0; j < COL; j++)
{
// 第一步:找到埋雷的格子
if (board[i][j] == -1)
{
// 第二步:遍历以雷为中心的九宫格
for (int row = i - 1; row <= i + 1; row++)
{
for (int col = j - 1; col <= j + 1; col++)
{
// 第三步:检查坐标是否合法,即是否越界;然后检查是否有雷
if ((row >= 0 && row < ROW) && (col >= 0 && col < COL) && board[row][col] != -1)
{
board[row][col] += 1;
}
}
}
}
}
}
}
-
我们创建的二维数组是全局的,即便不初始化,也默认初始化为全 0。而在 GameInit 函数中,我们首先将棋盘上的每个格子的信息初始化为 0,其主要原因是当游戏重新开始时,需要重新埋雷,其他每个格子的信息也需要重新更新,所以这一步必不可少。
-
埋雷时随机生成的坐标可能相同,所以为了真正地生成 MINE_COUNT 个雷,我们首先需要对格子的信息进行判断,即 board[row][col] 是否等于 0。
-
对以雷为中心的九宫格进行 +1 操作,有雷的格子除外。
注意:在找到埋雷的格子后,首先要检查坐标是否合法,即是否越界,然后再检查是否有雷。&& 操作符控制求值顺序,可以避免越界访问的错误产生。
二、加密(Encrypt)
void Encrypt()
{
int i = 0; // 控制行
int j = 0; // 控制列
// 对每个格子进行加密操作,即每个格子的值 +20,当要打开格子时,再相应地 -20
for (i = 0; i < ROW; i++)
{
for (j = 0; j < COL; j++)
{
board[i][j] += 20; // board[i][j]:19 ~ 28(因为周围最多有 8 个雷)
}
}
}
扫雷初始界面如下所示:
我们不能直接将每个格子的信息绘制出来,所以在绘制游戏界面前,首先要先对每个格子进行加密操作。
三、加载图片(Load Image)
#include <graphics.h>
#include <stdio.h>
#define BLOCK_SIZE 50 // 每个格子的宽度为 50 px
IMAGE imgs[12]; // 数组的元素类型为保存图片的 IMAGE 对象
void LoadImg()
{
int i = 0;
for (i = 0; i < 12; i++)
{
char imgPath[50];
sprintf(imgPath, "./images/%d.jpg", i);
loadimage(&imgs[i], imgPath, BLOCK_SIZE, BLOCK_SIZE);
}
}
加载的 12 张图片:
sprintf 函数:
1.函数的原型: | int sprintf(char* buffer, const char* format[, argument...]); |
---|---|
2.函数的作用: | Write formatted data to a string. |
3.需要引入的头文件: | stdio.h |
四、 绘制界面(Draw the Interface)
void DrawInterface()
{
int i = 0; // 控制行
int j = 0; // 控制列
for (i = 0; i < ROW; i++)
{
for (j = 0; j < COL; j++)
{
if (board[i][j] == -1)
{
putimage(j * BLOCK_SIZE, i * BLOCK_SIZE, &imgs[9]); // 绘制雷
}
else if (board[i][j] >= 0 && board[i][j] <= 8)
{
putimage(j * BLOCK_SIZE, i * BLOCK_SIZE, &imgs[board[i][j]]); // 绘制数字 0 ~ 8
}
else if (board[i][j] >= 19 && board[i][j] <= 28)
{
putimage(j * BLOCK_SIZE, i * BLOCK_SIZE, &imgs[10]); // 绘制加密格子
}
else if (board[i][j] >= 39 && board[i][j] <= 48)
{
putimage(j * BLOCK_SIZE, i * BLOCK_SIZE, &imgs[11]); // 绘制红旗
}
}
}
}
注意:i 控制行,j 控制列,而 putimage 函数的第一个参数是绘制位置的 x 坐标,第二个参数是绘制位置的 y 坐标。
五、鼠标控制(Mouse Control)
int count = 0; // 用来记录鼠标左键按下的次数,其中包括了空白的展开
int IsOver = 0; // 用来判断游戏是否结束
void OpenBlank(int row, int col)
{
// 打开以空格,即 board[row][col] == 0,为中心的九宫格,并进行递归
for (int i = row - 1; i <= row + 1; i++)
{
for (int j = col - 1; j <= col + 1; j++)
{
// 首先检查坐标是否合法,然后打开不是雷的格子
if ((i >= 0 && i < ROW) && (j >= 0 && j < COL) &&
(board[i][j] > 19 && board[i][j] <= 28))
{
board[i][j] -= 20;
count++;
if (board[i][j] == 0)
{
OpenBlank(i, j);
}
}
}
}
}
void MouseControl()
{
ExMessage msg;
if (peekmessage(&msg))
{
int row = msg.y / BLOCK_SIZE;
int col = msg.x / BLOCK_SIZE;
switch (msg.message)
{
case WM_LBUTTONDOWN: // 鼠标左键按下消息
if (board[row][col] >= 19 && board[row][col] <= 28)
{
board[row][col] -= 20; // 打开加密格子
count++;
if (board[row][col] == -1)
{
IsOver = 1;
// 打开所有的雷
for (int i = 0; i < ROW; i++)
{
for (int j = 0; j < COL; j++)
{
if (board[i][j] == 19)
{
board[i][j] -= 20;
}
}
}
}
if (board[row][col] == 0)
{
OpenBlank(row, col);
}
}
break;
case WM_RBUTTONDOWN: // 鼠标右键按下消息
if (board[row][col] >= 19 && board[row][col] <= 28)
{
board[row][col] += 20; // 再次加密
}
else if (board[row][col] >= 39 && board[row][col] <= 48)
{
board[row][col] -= 20; // 解密
}
break;
}
}
}
六、判断游戏是否结束(Is Game Over)
void IsGameOver()
{
// 1. 判断游戏是否结束
if (IsOver)
{
HWND hWnd = GetHWnd();
int ret = MessageBox(NULL, "Game Over! Again?", "提示", MB_OKCANCEL);
if (ret == IDOK)
{
GameInit();
Encrypt();
IsOver = 0;
count = 0;
}
else if (ret == IDCANCEL)
{
exit(0);
}
}
// 2. 判断玩家是否获胜
if (count == ROW * COL - MINE_COUNT)
{
HWND hWnd = GetHWnd();
int ret = MessageBox(NULL, "You Are The Winner! Again?", "提示", MB_OKCANCEL);
if (ret == IDOK)
{
GameInit();
Encrypt();
count = 0;
}
else if (ret == IDCANCEL)
{
exit(0);
}
}
}
七、源代码
#define _CRT_SECURE_NO_WARNINGS 1
// 一、游戏初始化
#include <stdlib.h>
#include <time.h> // 用时间戳作为随机数种子
#include <graphics.h>
#include <stdio.h>
#define ROW 10
#define COL 10
#define MINE_COUNT 10
#define BLOCK_SIZE 50 // 每个格子的宽度为 50 px
int board[ROW][COL] = { 0 }; // 创建一个二维数组来存储棋盘上每个格子的信息
IMAGE imgs[12]; // 数组的元素类型为保存图片的 IMAGE 对象
int count = 0; // 用来记录鼠标左键按下的次数,其中包括了空白的展开
int IsOver = 0; // 用来判断游戏是否结束
// 一、游戏初始化
void GameInit()
{
int i = 0; // 控制行
int j = 0; // 控制列
// 1. 特别是当游戏重新开始时,需要将棋盘上的每个格子的信息重新初始化为 0
for (i = 0; i < ROW; i++)
{
for (j = 0; j < COL; j++)
{
board[i][j] = 0;
}
}
// 2. 随机生成 MINE_COUNT 个雷
for (i = 0; i < MINE_COUNT; i++)
{
while (1)
{
int row = rand() % ROW; // row: 0 ~ ROW - 1
int col = rand() % COL; // col: 0 ~ COL - 1
if (board[row][col] == 0) // 避免在同一个格子上埋雷
{
board[row][col] = -1; // -1 表示这个格子上埋有雷
break;
}
}
}
// 3. 对以雷为中心九宫格进行 +1 操作,有雷的格子除外
for (i = 0; i < ROW; i++)
{
for (j = 0; j < COL; j++)
{
// 第一步:找到埋雷的格子
if (board[i][j] == -1)
{
// 第二步:遍历以雷为中心的九宫格
for (int row = i - 1; row <= i + 1; row++)
{
for (int col = j - 1; col <= j + 1; col++)
{
// 第三步:检查坐标是否合法,即是否越界;然后检查是否有雷
if ((row >= 0 && row < ROW) && (col >= 0 && col < COL) && board[row][col] != -1)
{
board[row][col] += 1;
}
}
}
}
}
}
}
// 二、加密
void Encrypt()
{
int i = 0; // 控制行
int j = 0; // 控制列
// 对每个格子进行加密操作,即每个格子的值 +20,当要打开格子时,再相应地 -20
for (i = 0; i < ROW; i++)
{
for (j = 0; j < COL; j++)
{
board[i][j] += 20; // board[i][j]:19 ~ 28(周围最多有 8 个雷)
}
}
}
// 三、加载图片
void LoadImg()
{
int i = 0;
for (i = 0; i < 12; i++)
{
char imgPath[50];
sprintf(imgPath, "./images/%d.jpg", i);
loadimage(&imgs[i], imgPath, BLOCK_SIZE, BLOCK_SIZE);
}
}
// 四、绘制游戏界面
void DrawInterface()
{
int i = 0; // 控制行
int j = 0; // 控制列
for (i = 0; i < ROW; i++)
{
for (j = 0; j < COL; j++)
{
if (board[i][j] == -1)
{
putimage(j * BLOCK_SIZE, i * BLOCK_SIZE, &imgs[9]); // 绘制雷
}
else if (board[i][j] >= 0 && board[i][j] <= 8)
{
putimage(j * BLOCK_SIZE, i * BLOCK_SIZE, &imgs[board[i][j]]); // 绘制数字 0 ~ 8
}
else if (board[i][j] >= 19 && board[i][j] <= 28)
{
putimage(j * BLOCK_SIZE, i * BLOCK_SIZE, &imgs[10]); // 绘制加密格子
}
else if (board[i][j] >= 39 && board[i][j] <= 48)
{
putimage(j * BLOCK_SIZE, i * BLOCK_SIZE, &imgs[11]); // 绘制红旗
}
}
}
}
// 五、鼠标控制
void OpenBlank(int row, int col)
{
// 打开以空格,即 board[row][col] == 0,为中心的九宫格,并进行递归
for (int i = row - 1; i <= row + 1; i++)
{
for (int j = col - 1; j <= col + 1; j++)
{
// 首先检查坐标是否合法,然后打开不是雷的格子
if ((i >= 0 && i < ROW) && (j >= 0 && j < COL) &&
(board[i][j] > 19 && board[i][j] <= 28))
{
board[i][j] -= 20;
count++;
if (board[i][j] == 0)
{
OpenBlank(i, j);
}
}
}
}
}
void MouseControl()
{
ExMessage msg;
if (peekmessage(&msg))
{
int row = msg.y / BLOCK_SIZE;
int col = msg.x / BLOCK_SIZE;
switch (msg.message)
{
case WM_LBUTTONDOWN: // 鼠标左键按下消息
if (board[row][col] >= 19 && board[row][col] <= 28)
{
board[row][col] -= 20; // 打开加密格子
count++;
if (board[row][col] == -1)
{
IsOver = 1;
// 打开所有的雷
for (int i = 0; i < ROW; i++)
{
for (int j = 0; j < COL; j++)
{
if (board[i][j] == 19)
{
board[i][j] -= 20;
}
}
}
}
if (board[row][col] == 0)
{
OpenBlank(row, col);
}
}
break;
case WM_RBUTTONDOWN: // 鼠标右键按下消息
if (board[row][col] >= 19 && board[row][col] <= 28)
{
board[row][col] += 20; // 再次加密
}
else if (board[row][col] >= 39 && board[row][col] <= 48)
{
board[row][col] -= 20; // 解密
}
break;
}
}
}
// 六、判断游戏是否结束
void IsGameOver()
{
// 1. 判断游戏是否结束
if (IsOver)
{
HWND hWnd = GetHWnd();
int ret = MessageBox(NULL, "Game Over! Again?", "提示", MB_OKCANCEL);
if (ret == IDOK)
{
GameInit();
Encrypt();
IsOver = 0;
count = 0;
}
else if (ret == IDCANCEL)
{
exit(0);
}
}
// 2. 判断玩家是否获胜
if (count == ROW * COL - MINE_COUNT)
{
HWND hWnd = GetHWnd();
int ret = MessageBox(NULL, "You Are The Winner! Again?", "提示", MB_OKCANCEL);
if (ret == IDOK)
{
GameInit();
Encrypt();
count = 0;
}
else if (ret == IDCANCEL)
{
exit(0);
}
}
}
// 主函数
int main()
{
srand((unsigned int)time(NULL)); // 设置随机数种子
initgraph(COL * BLOCK_SIZE, ROW * BLOCK_SIZE, EW_SHOWCONSOLE);
GameInit();
Encrypt();
LoadImg();
/*
for (int i = 0; i < ROW; i++)
{
for (int j = 0; j < COL; j++)
{
printf("%2d ", board[i][j]);
}
printf("\n");
}
*/
BeginBatchDraw();
while (1)
{
MouseControl();
DrawInterface();
FlushBatchDraw();
IsGameOver();
}
EndBatchDraw();
closegraph();
return 0;
}
链接:百度网盘 请输入提取码
提取码:uwd1