
C语言扫雷
整体框架游戏框架具体实现
初始化棋盘设置雷打印棋盘排雷获取雷的数量判断第一次是否踩雷标记雷判断是否赢展开一片 完整代码
C语言扫雷扫雷的代码实现和三子棋差不多,主要差别在于游戏代码的实现
分成三个文件进行编写 test.c 、game.h 、game.c
test.c :用于实现总体框架,及测试代码
game.h :用于符号定义、函数声明、头文件引用
game.c :用实现游戏代码
整体框架首先打印菜单供玩家进行选择
选择1: 进入游戏,游戏结束,break 跳出多分支 此时 input 值为 1,循环继续。实现了玩了一把不过瘾再玩一把的需求选择0: 打印提示 break 跳出多分支 此时 input 值为 0,循环停止。程序结束选择其它:打印提示 break 跳出多分支 此时 input 值为 非0,循环继续
// 创建菜单 -- 可自行修改为想要的样式
void menu()
{
printf("**************************n");
printf("******** 1.play ********n");
printf("******** 0.exit ********n");
printf("**************************n");
}
int main()
{
int input = 0;
srand((unsigned int)time(NULL)); // 用于设置随机数种子
do
{
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case 1:
system("cls"); // 清屏 - 可以加在自己想要的地方
game();
break;
case 0:
printf("退出游戏n");
break;
default:
printf("选择错误,请重输n");
break;
}
} while (input);
return 0;
}
游戏框架
理清游戏的执行顺序
定义两个数组 mine 数组用于存储雷的信息,show 数组用于显示给玩家观看,采用宏定义方式便于以后扩展。初始化两个数组,将 mine 数组初始化为 '0' ,show 数组初始化为 '*' 。设置雷,将雷的信息存储到 mine 数组中。打印棋盘,棋盘存储在 show 数组中。排雷的具体细节
void game()
{
char mine[ROWS][COLS] = { 0 }; // 用于存储雷的位置
char show[ROWS][COLS] = { 0 }; // 用于显示给用户
// 初始化数组
InitBoard(mine, ROWS, COLS, '0');
InitBoard(show, ROWS, COLS, '*');
// 设置雷
SetMine(mine, ROW, COL);
//PrintBoard(mine, ROW, COL); // 如果需要答案将这一行注释去掉
PrintBoard(show, ROW, COL); // 打印棋盘供玩家开始游戏
// 排雷
FindMine(mine, show, ROW, COL);
}
具体实现
初始化棋盘
将棋盘设置为指定的初始化格式
show 数组全部初始化为 '0'
mine 数组全部初始化为 '*'
// 初始化棋盘
void InitBoard(char board[ROWS][COLS], int row, int col, char set)
{
int i = 0;
for (i = 0; i < row; i++)
{
int j = 0;
for (j = 0; j < col; j++)
{
board[i][j] = set; // 初始为指定的格式
}
}
}
设置雷
采用随机数来生成雷 – 将为雷的坐标设置为 '1'
// 设置雷
void SetMine(char board[ROWS][COLS], int row, int col)
{
int count = EASY_COUNT;
while (count)
{
int x = rand() % row + 1; // 1 ~ 9
int y = rand() % col + 1; // 1 ~ 9
if ('0' == board[x][y])
{
board[x][y] = '1';
count--;
}
}
}
打印棋盘
将棋盘打印出来,供玩家开始扫雷,格式可自行设计,这里保证了在 1 ~ 99 大小棋盘不会出现错位情况,棋盘大小不建议超过 70 不然程序容易崩溃
// 打印棋盘
void PrintBoard(char board[ROWS][COLS], int row, int col)
{
int i = 0;
// 打印列标
for (i = 0; i <= row; i++)
{
printf(" %-3d", i);
}
printf("nn");
// 打印每一行
for (i = 1; i <= row; i++)
{
int j = 0;
printf("%2d", i); // 打印行标
for (j = 1; j <= col; j++)
{
printf("%4c", board[i][j]);
}
printf("nn");
}
}
排雷
排雷实现的主要实现,本文扫雷主要采用三个输入来进行游戏 (横纵坐标及是否标记 – 1 为标记此坐标为雷,其余数字为不标记,正常揭开),同时实现了第一次绝对不会踩雷,一下展开一大片等功能
// 排查雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int flag = 0; // 用于判断是否标记
int first = 1; // 用于保证第一次不是雷,且仅执行一次
while (IsWin(show, ROW, COL) != 1)
{
printf("请输入要排查的坐标:>");
scanf("%d %d %d", &x, &y, &flag);// 采用三个输入,横纵坐标及是否标记雷为 1 标记为雷 其它数字为不标记正常打开
if (x >= 1 && x <= row && y >= 1 && y <= col) // 判断输入是否合法
{
if (1 == flag) // 判断是否标记雷
{
SignMine(show, x, y);
continue;
}
// 判断第一次是否踩雷
if (1 == first)
{
IsFirstStepMine(mine, x, y);
first = 0;
}
if ('1' == mine[x][y])
{
printf("很遗憾,你被雷炸死了!n");
PrintBoard(mine, ROW, COL);
break;
}
else
{
system("cls"); // 清屏
show[x][y] = '0' + GetMineCount(mine, x, y); // 将九宫内雷数,显示给用户
// 展开一片
if ('0' == show[x][y])
CleanMore(mine, show, x, y);
PrintBoard(show, ROW, COL);
}
}
else
{
printf("输入坐标不合法,请重输!n");
}
}
if (IsWin(show, ROW, COL) == 1)
printf("恭喜,你赢了!n");
}
获取雷的数量
这里存在的问题是如何处理边角,无法对附近九宫格进行遍历,主要有两个方法 - 本文采用第二种
对坐标进行判断,如果越界不予理会,返回合法坐标内的雷数为了防止越界问题,我们也可以直接在棋盘外再加上一圈,原本 9 * 9 的棋盘变为 11 * 11 的棋盘,这样就不会发生越界问题了
// 获取九宫格内雷的数量
static int GetMineCount(char mine[ROWS][COLS], int x, int y)
{
int count = 0;
int i = 0;
for (i = x - 1; i <= x + 1; i++)
{
int j = 0;
for (j = y - 1; j <= y + 1; j++)
{
count += mine[i][j];
}
}
return count - 9 * '0'; // 棋盘存储的为字符,减去字符 0 转化为数字
}
判断第一次是否踩雷
如果第一次的坐标是雷,首先寻找一个不为雷的坐标将雷设置在这,再将原坐标为雷的坐标,置为非雷
// 判断第一次是否踩雷
static void IsFirstStepMine(char mine[ROWS][COLS], int x, int y)
{
while ('1' == mine[x][y])
{
int m = rand() % ROW + 1;
int n = rand() % COL + 1;
if ('0' == mine[m][n])
{
mine[m][n] = '1';
mine[x][y] = '0';
}
}
}
标记雷
由于使用键盘 *** 作并不便捷,故只设置了一层标记
标记指定坐标,将其置为 '?' , 再次标记,还原为 '*'
// 标记雷
static SignMine(char show[ROWS][COLS], int x, int y)
{
if ('*' == show[x][y])
show[x][y] = '?';
else if ('?' == show[x][y])
show[x][y] = '*';
system("cls");
PrintBoard(show, ROW, COL);
}
判断是否赢
遍历棋盘,统计未被排查及已标记雷的数量,看是否与所设置雷数相同,相同即为赢
// 判断是否赢
static int IsWin(char show[ROWS][COLS], int row, int col)
{
int count = 0;
int i = 0;
for (i = 1; i <= row; i++)
{
int j = 0;
for (j = 1; j <= col; j++)
{
if ('*' == show[i][j] || '?' == show[i][j]) // 统计被标记及未被排查雷个数
count++; // 等于总的雷数,获胜
}
}
return count == EASY_COUNT;
}
展开一片
如果九宫格内没有雷,则揭开其余格子,再对揭开的格子进行判断,是否有雷,没有雷则继续展开,采用递归实现
// 利用递归实现一下展开一片
static void CleanMore(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{
int i = 0;
show[x][y] = ' '; // 为了美观将 '0' 全部用 ' ' 代替
for (i = x - 1; i <= x + 1; i++)
{
int j = 0;
for (j = y - 1; j <= y + 1; j++)
{
if (i < 1 || i > ROW || j < 1 || j > COL) // 不合法坐标
continue;
if ('*' == show[i][j]) // 对未展开的进行展开,已展开跳过
{
show[i][j] = '0' + GetMineCount(mine, i, j);
if ('0' == show[i][j])
CleanMore(mine, show, i, j);
}
}
}
}
完整代码
test.c
#include "game.h"
// 打印菜单 -- 可自行设计
void menu()
{
printf("************************n");
printf("******** 1.play ********n");
printf("******** 0.exit ********n");
printf("************************n");
}
void game()
{
char mine[ROWS][COLS] = { 0 }; // 用于存储雷的位置
char show[ROWS][COLS] = { 0 }; // 用于显示给用户
// 初始化数组
InitBoard(mine, ROWS, COLS, '0');
InitBoard(show, ROWS, COLS, '*');
// 设置雷
SetMine(mine, ROW, COL);
//PrintBoard(mine, ROW, COL); // 如果需要答案将这一行注释去掉
PrintBoard(show, ROW, COL);
// 排雷
FindMine(mine, show, ROW, COL);
}
int main()
{
int input = 0;
srand((unsigned int)time(NULL)); // 设置随机数种子
do
{
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case 1:
system("cls");
game();
break;
case 0:
printf("退出游戏n");
break;
default:
printf("选择错误,请重输n");
break;
}
} while (input);
return 0;
}
game.h
#define _CRT_SECURE_NO_WARNINGS 1 #pragma once #include#include #include #include // 定义棋盘大小 -- 不建议超过 60 ~ 70,不然程序会崩溃 #define ROW 9 #define COL 9 // 扩展棋盘大小,防止越界 #define ROWS ROW + 2 #define COLS COL + 2 // 定义雷数 -- 雷的数量不能超过棋盘大小 #define EASY_COUNT 10 // 初始化数组 void InitBoard(char board[ROWS][COLS], int row, int col, char set); // 打印数组 void PrintBoard(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);
game.c
#include "game.h"
// 初始化棋盘
void InitBoard(char board[ROWS][COLS], int row, int col, char set)
{
int i = 0;
for (i = 0; i < row; i++)
{
int j = 0;
for (j = 0; j < col; j++)
{
board[i][j] = set; // 初始为指定的格式
}
}
}
// 打印棋盘
void PrintBoard(char board[ROWS][COLS], int row, int col)
{
int i = 0;
// 打印列标
for (i = 0; i <= row; i++)
{
printf(" %-3d", i);
}
printf("nn");
// 打印每一行
for (i = 1; i <= row; i++)
{
int j = 0;
printf("%2d", i); // 打印行标
for (j = 1; j <= col; j++)
{
printf("%4c", board[i][j]);
}
printf("nn");
}
}
// 设置雷
void SetMine(char board[ROWS][COLS], int row, int col)
{
int count = EASY_COUNT;
while (count)
{
int x = rand() % row + 1; // 1 ~ 9
int y = rand() % col + 1; // 1 ~ 9
if ('0' == board[x][y])
{
board[x][y] = '1';
count--;
}
}
}
// 获取九宫格内雷的数量
static int GetMineCount(char mine[ROWS][COLS], int x, int y)
{
int count = 0;
int i = 0;
for (i = x - 1; i <= x + 1; i++)
{
int j = 0;
for (j = y - 1; j <= y + 1; j++)
{
count += mine[i][j];
}
}
return count - 9 * '0';
}
// 判断第一次是否踩雷
static void IsFirstStepMine(char mine[ROWS][COLS], int x, int y)
{
while ('1' == mine[x][y])
{
int m = rand() % ROW + 1;
int n = rand() % COL + 1;
if ('0' == mine[m][n])
{
mine[m][n] = '1';
mine[x][y] = '0';
}
}
}
// 利用递归实现一下展开一片
static void CleanMore(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{
int i = 0;
show[x][y] = ' '; // 为了美观将 '0' 全部用 ' ' 代替
for (i = x - 1; i <= x + 1; i++)
{
int j = 0;
for (j = y - 1; j <= y + 1; j++)
{
if (i < 1 || i > ROW || j < 1 || j > COL) // 不合法坐标
continue;
if ('*' == show[i][j]) // 对未展开的进行展开
{
show[i][j] = '0' + GetMineCount(mine, i, j);
if ('0' == show[i][j])
CleanMore(mine, show, i, j);
}
}
}
}
// 判断是否赢
static int IsWin(char show[ROWS][COLS], int row, int col)
{
int count = 0;
int i = 0;
for (i = 1; i <= row; i++)
{
int j = 0;
for (j = 1; j <= col; j++)
{
if ('*' == show[i][j] || '?' == show[i][j]) // 统计被标记及未被排查雷个数
count++; // 等于总的雷数,获胜
}
}
return count == EASY_COUNT;
}
// 标记雷
static SignMine(char show[ROWS][COLS], int x, int y)
{
if ('*' == show[x][y])
show[x][y] = '?';
else if ('?' == show[x][y])
show[x][y] = '*';
system("cls");
PrintBoard(show, ROW, COL);
}
// 排查雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int flag = 0; // 用于判断是否标记
int first = 1; // 用于保证第一次不是雷,且仅执行一次
while (IsWin(show, ROW, COL) != 1)
{
printf("请输入要排查的坐标:>");
scanf("%d %d %d", &x, &y, &flag);
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (1 == flag)
{
SignMine(show, x, y);
continue;
}
// 判断第一次是否踩雷
if (1 == first)
{
IsFirstStepMine(mine, x, y);
first = 0;
}
if ('1' == mine[x][y])
{
printf("很遗憾,你被雷炸死了!n");
PrintBoard(mine, ROW, COL);
break;
}
else
{
system("cls"); // 清屏
show[x][y] = '0' + GetMineCount(mine, x, y);
// 展开一片
if ('0' == show[x][y])
CleanMore(mine, show, x, y);
PrintBoard(show, ROW, COL);
}
}
else
{
printf("输入坐标不合法,请重输!n");
}
}
if (IsWin(show, ROW, COL) == 1)
printf("恭喜,你赢了!n");
}
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)