文章目录
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);
}
具体实现
初始化棋盘
// 初始化棋盘
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 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--;
}
}
}
打印棋盘
// 打印棋盘
void PrintBoard(char board[ROWS][COLS], int row, int col)
{
int i = 0;
// 打印列标
for (i = 0; i <= row; i++)
{
printf(" %-3d", i);
}
printf("\n\n");
// 打印每一行
for (i = 1; i <= row; i++)
{
int j = 0;
printf("%2d", i); // 打印行标
for (j = 1; j <= col; j++)
{
printf("%4c", board[i][j]);
}
printf("\n\n");
}
}
排雷
// 排查雷
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);
}
}
}
}
完整代码
#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;
}
#define _CRT_SECURE_NO_WARNINGS 1
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <Windows.h>
// 定义棋盘大小 -- 不建议超过 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);
#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("\n\n");
// 打印每一行
for (i = 1; i <= row; i++)
{
int j = 0;
printf("%2d", i); // 打印行标
for (j = 1; j <= col; j++)
{
printf("%4c", board[i][j]);
}
printf("\n\n");
}
}
// 设置雷
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");
}