目录
扫雷游戏介绍及规则
扫雷游戏是一款经典的益智小游戏,玩家需要在由数字标记的方块矩阵中找出所有隐藏的地雷,同时避免踩到它们。游戏开始时,矩阵中大部分方块都是隐藏的,玩家需要逐个点击来翻开它们。每个方块上的数字表示其周围八个方向上的地雷数量,玩家需要利用这些数字来推理出地雷的位置。如果成功找出所有地雷而不踩到任何一个,那么游戏就胜利了。如果踩到地雷,游戏就会立即结束。
预期功能
-
初始化游戏界面:游戏开始时,应能生成一个预设大小的扫雷界面,该界面由一定数量的格子组成,每个格子初始状态为未翻开。
-
设置地雷:在界面上随机布置一定数量的地雷,同时计算每个非地雷格子周围的地雷数,并在界面上以数字形式显示。
-
扫雷游戏(排雷):玩家应能够通过键盘输入坐标来翻开格子,显示格子内容(数字或地雷)。如果翻开的是地雷格子,游戏结束;如果翻开的是数字格子,根据数字显示周围地雷数。
-
游戏胜利与失败判定:当玩家成功找出所有地雷并翻开所有非地雷格子时,游戏胜利;如果玩家翻开了地雷格子,则游戏失败。游戏应能正确判断并显示游戏结果。
-
重新开始或退出游戏:游戏结束后,程序应提供选项供用户选择重新开始新的一局或退出游戏。
游戏设计与代码实现
游戏初始界面及多次游戏实现
使用 printf 打印出简单的游戏初始界面 menu(),使用 do-while循环 和 switch 选择语句,当玩家输入的值 input 等于 1 时便开始游戏,游戏结束后,因为 input 值为 1,非零为真,循环会再次执行,便可以再次选则是否进行下一场游戏;input 等于 0 时,零为假,循环终止,退出游戏;其它值可以设置为,选择错误,请重新输入。
代码实现
//打印游戏初始界面
void menu()
{
printf("************************\n");
printf("******** 1.play ********\n");
printf("******** 0.exit ********\n");
printf("************************\n");
}
int main()
{
int input = 0;
do
{
menu();
printf("请选择:》");
//玩家输入,1 开始游戏,0 退出游戏,其它重新输入
scanf("%d", &input);
switch (input)
{
case 1:
game();//选择1,调用游戏函数,开始游戏
break;
case 0:
printf("退出游戏。\n");
break;
default:
printf("选择错误,请重新选择!\n");
break;
}
} while (input);
return 0;
}
初始化游戏界面
定义两个数组,一个是玩家可以看到雷的信息的游戏矩阵;一个是玩家看不到,放置雷的后台矩阵。
游戏开始后,我们可以使用两个符号代表已经翻开的格子和未被翻开的格子,本文使用是 ‘ * ‘ 未翻开,放在玩家可以看到的游戏矩阵里;’ 0 ‘ 已经翻开,放在玩家看不到,放置雷的后台矩阵里。利用循环将游戏界面的那个矩阵依次赋值为 ’ * ' 或 ‘ 0 ‘ 即可。
代码实现
test.c
char mine[ROWS][COLS];
char show[ROWS][COLS];
InitBoard(mine, ROWS, COLS, '0');
InitBoard(show, ROWS, COLS, '*');
game.c
//初始化游戏界面
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
{
int i = 0;
for (i = 0; i <rows; i++)
{
int j = 0;
for (j = 0; j <cols; j++)
{
board[i][j] = set;
}
}
}
打印游戏界面
因为游戏界面已经被初始化了,所以这里使用循环直接将它们打印出来即可。
代码实现
//打印游戏界面
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
int i = 0;
printf("----扫雷游戏----\n");
for (i = 0; i <=col; i++)
{
printf("%d ", i);
}
printf("\n");
for (i = 1; i <=row; i++)
{
printf("%d ", i);
int j = 0;
for (j = 1; j <=col; j++)
{
printf("%c ", board[i][j]);
}
printf("\n");
}
}
放置雷
利用随机函数产生的数作为雷的坐标,用循环将后台矩阵此处坐标的 ’ 0 ‘ 替换成雷的符号,
雷的符号设置为 ’ 1 ‘。
代码实现
void SetMine(char board[ROWS][COLS], int row, int col)
{
int count = EASY_COUNT;
while (count)
{
int x = rand() % row + 1;
int y = rand() % col + 1;
if (board[x][y] == '0')
{
board[x][y] = '1';
count--;
}
}
}
排雷及游戏判定
玩家输入要排查的坐标后,先用 if 检查数值是否合规,输入的坐标值需要可以在游戏界面矩阵上面可以找到;然后检查坐标位置是否是雷,当坐标符号和雷的符号 ’1 ‘一致时,则游戏结束。或者将所有不是雷的坐标排查完,剩下的未被排查到的坐标全是雷,游戏结束。
代码实现
//排雷,玩家开始游戏,输入需要排查的坐标
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int win = 0;
while (win<row*col-EASY_COUNT)
{
printf("请输入要排查的坐标:》");
scanf("%d %d", &x, &y);
//检查输入的坐标是否合规
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (mine[x][y] == '1')
{
printf("很遗憾,你被炸死了\n");
DisplayBoard(mine, ROW, COL);
break;
}
else
{
int count = GetMineCount(mine, x, y);
show[x][y] = count + '0';
DisplayBoard(show, ROW, COL);
win++;
}
}
else
{
printf("坐标非法,请重新输入\n");
}
}
//把所有雷找到,游戏获胜
if (win == row * col - EASY_COUNT)
{
printf("恭喜你!排雷成功!\n");
DisplayBoard(mine, ROW, COL);
}
}
计算周围雷的数量
如图,横坐标为 x ,纵坐标为 y ,当我们要计算出 q 坐标周围雷的数量的时候,可以发现它周围的格子的坐标是可以被算出来的,假如 q 的坐标是(x, y),那么它的左上角为((x-1),(y-1)),上方为(x,(y-1)),右上角为((x+1),(y-1)),以此类推可以算出它周围的所有坐标。
因为在前面设置了雷的符号为 ’ 1 ‘,所当我们找到 q 坐标处周围的所有坐标时,将周围所有坐标处的值 ’ 1 ‘ 相加,它们的和就是 q 坐标处雷的数量,在将和赋值给 q ,打印矩阵,我们就可以看到 q 处坐标周围雷的数量。
代码实现
//计算坐标周围的雷的数量
int GetMineCount(char mine[ROWS][COLS], int x, int y)
{
return (mine[x - 1][y] + mine[x - 1][y + 1] + mine[x][y + 1] + mine[x + 1][y + 1]
+ mine[x + 1][y] + mine[x + 1][y - 1] + mine[x][y - 1] + mine[x - 1][y - 1] - '0' * 8);
}
(在定义游戏矩阵的行数和列数时,程序真正的创建的矩阵行数和列数要大于游戏矩阵,不然当坐标处于边界时,(x-1)、(y-1)的值便会越界。比如本文的游戏矩阵为 9X9,程序真实创建的矩阵为 11X11)
//扫雷游戏界面的行数和列数
#define ROW 9
#define COL 9
//在我们计算坐标周围的雷的数量的时候防止越界,实际使用的数组
#define ROWS ROW+2
#define COLS COL+2
完整的源代码及注释
头文件 game . h
头文件提供函数和类的声明
#pragma once
#include <stdio.h>
//随机函数 srand 的头文件
#include <stdlib.h>
//时间函数 time 的头文件
#include <time.h>
//雷的数量
#define EASY_COUNT 10
//扫雷游戏界面的行数和列数
#define ROW 9
#define COL 9
//在我们计算坐标周围的雷的数量的时候防止越界,实际使用的数组
#define ROWS ROW+2
#define COLS COL+2
//初始化游戏界面
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set);
//打印游戏界面
void DisplayBoard(char board[ROWS][COLS], int row, int col);
//在游戏界面上放置雷
void SetMine(char board[ROWS][COLS], int row, int col);
//开始游戏,开始排雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
源文件提供函数和类的实现
源文件 test . c
#define _CRT_SECURE_NO_WARNINGS
//引用头文件
#include "game.h"
//打印游戏初始界面
void menu()
{
printf("************************\n");
printf("******** 1.play ********\n");
printf("******** 0.exit ********\n");
printf("************************\n");
}
//游戏函数
void game()
{
char mine[ROWS][COLS];
char show[ROWS][COLS];
//初始化界面
InitBoard(mine, ROWS, COLS, '0');
InitBoard(show, ROWS, COLS, '*');
//打印游戏界面
//DisplayBoard(mine, ROW, COL);
DisplayBoard(show, ROW, COL);
//放置雷
SetMine(mine, ROW, COL);
//这里可以看到放置雷后的游戏界面,能看到雷在棋盘上的位置
//DisplayBoard(mine, ROW, COL);
//排雷
FindMine(mine,show, ROW, COL);
}
int main()
{
int input = 0;
//随机函数,使每次游戏雷的坐标不同
srand((unsigned int)time(NULL));
do
{
menu();
printf("请选择:》");
//玩家输入,1 开始游戏,0 退出游戏,其它重新输入
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出游戏。\n");
break;
default:
printf("选择错误,请重新选择!\n");
break;
}
} while (input);
return 0;
}
源文件 game . c
#define _CRT_SECURE_NO_WARNINGS
#include "game.h"
//初始化游戏界面
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
{
int i = 0;
for (i = 0; i <rows; i++)
{
int j = 0;
for (j = 0; j <cols; j++)
{
board[i][j] = set;
}
}
}
//打印游戏界面
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
int i = 0;
printf("----扫雷游戏----\n");
for (i = 0; i <=col; i++)
{
printf("%d ", i);
}
printf("\n");
for (i = 1; i <=row; i++)
{
printf("%d ", i);
int j = 0;
for (j = 1; j <=col; j++)
{
printf("%c ", board[i][j]);
}
printf("\n");
}
}
//放置雷
void SetMine(char board[ROWS][COLS], int row, int col)
{
int count = EASY_COUNT;
while (count)
{
int x = rand() % row + 1;
int y = rand() % col + 1;
if (board[x][y] == '0')
{
board[x][y] = '1';
count--;
}
}
}
//计算坐标周围的雷的数量
int GetMineCount(char mine[ROWS][COLS], int x, int y)
{
return (mine[x - 1][y] + mine[x - 1][y + 1] + mine[x][y + 1] + mine[x + 1][y + 1]
+ mine[x + 1][y] + mine[x + 1][y - 1] + mine[x][y - 1] + mine[x - 1][y - 1] - '0' * 8);
}
//排雷,玩家开始游戏,输入需要排查的坐标
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int win = 0;
while (win<row*col-EASY_COUNT)
{
printf("请输入要排查的坐标:》");
//玩家输入想要排查的坐标
scanf("%d %d", &x, &y);
//检查输入的坐标是否合规
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (mine[x][y] == '1')
{
printf("很遗憾,你被炸死了\n");
DisplayBoard(mine, ROW, COL);
break;
}
else
{
int count = GetMineCount(mine, x, y);
show[x][y] = count + '0';
DisplayBoard(show, ROW, COL);
win++;//当雷的数量为 10 ,矩阵为 9x9 时,win 值为 89 时,游戏获胜
}
}
else
{
printf("坐标非法,请重新输入\n");
}
}
//把所有不是雷的位置找到,游戏获胜
if (win == row * col - EASY_COUNT)
{
printf("恭喜你!排雷成功!\n");
DisplayBoard(mine, ROW, COL);
}
}