在一个9×9(初级)、16×16(中级)、16×30(高级)或自定义大小的方块矩阵中随机布置一定量的地雷(初级为10个,中级为40个,高级为99个),再由玩家逐个翻开方块,以找出所有地雷为最终游戏目标。如果玩家翻开的方块有地雷,则游戏结束。
游玩方式
先把过程现在前面,方便后续直接看着这个步骤写出代码
void menu()
{
printf("|-------------------------------|\n");
printf("|----------MineSweeper----------|\n");
printf("|*******************************|\n");
printf("|****** 1.play 0.exit ******|\n");
printf("|*******************************|\n");
printf("|是雷为 '1' --------- 非雷为 '0'|\n");
printf("|-------------------------------|\n");
}
为什么要创建两个棋盘?
解决方法
将二维数组扩大一圈
扩张棋盘的好处
棋盘定义
//玩家看到的期盼大小
#define ROW 9 //行
#define COL 9 //列
//棋盘的实际大小
#define ROWS ROW + 2
#define COLS COL + 2
char mine[ROWS][COLS] = { 0 }; //存放布置好的地雷的信息
char show[ROWS][COLS] = { 0 }; //存放排查出的地雷的信息
//初始化棋盘
void init_board(char board[ROWS][COLS], int rows, int cols,char set)
{
int i = 0;
int j = 0;
for (i = 0; i < rows; i++)
{
for (j = 0; j < cols; j++)
{
board[i][j] = set;
//board[i][j] = '0';如果这样弄函数就不具备通用性了
}
}
}
//打印棋盘
void display_board(char board[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
printf("\n----MineSweeper----\n");
//打印列号
for (j = 0; j <= col; j++)
{
printf("%d ", j);
}
putchar('\n');
//显示出的棋盘的行和列都从 1 开始
for (i = 1; i <= row; i++)
{
printf("%d ", i);//打印行号
for (j = 1; j <= col; j++)
{
printf("%c ", board[i][j]);
}
putchar('\n');
}
printf("----MineSweeper----\n");
}
效果展示
game.h
#define EASY_COUNT 10 //雷的数量
game.c
//布雷
void set_mine(char board[ROWS][COLS], int row, int col)
{
int count = EASY_COUNT;//10
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 find_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int win = 0;//找到非雷的个数
int operate = 0;
while (win < ROW * COL - EASY_COUNT)//游戏赢了就退出循环
{
putchar('\n');
printf("|------------------|\n");
printf("|--- 1.排查地雷 ---|\n");
printf("|--- 2.标记地雷 ---|\n");
printf("|--- 3.取消标记 ---|\n");
printf("|------------------|\n");
printf(" 请选择你的操作:");
scanf("%d", &operate);
if(1==operate)
{
display_board(show, ROW, COL);
printf("请输入要排查的坐标:");
scanf("%d %d", &x, &y);
//判断坐标是否合法
if ((x >= 1 && x <= row) && (y >= 1 && y <= col))
{
if (show[x][y] != '*')
{
printf("该位置已被排查过,请重新输入\n");
}
if ('1' == mine[x][y]) //如果是雷
{
printf("\n很遗憾,你被炸死了\n");
display_board(mine, ROW, COL);//挂了之后要展示雷的位置
break;
}
else //如果不是雷,就要统计周围有几个雷
{
spreed(mine, show, x, y); //展开条件1,该坐标不是雷
display_board(show, ROW, COL);
win++;
}
}
else
{
printf("坐标非法,请重新输入\n");
}
}
else if (2 == operate)
{
mark_mine(show, ROW, COL);
}
else if (3 == operate)
{
cancel_mark(show, ROW, COL);
}
}
if (ROW * COL - EASY_COUNT == win)
{
printf("\n恭喜你,排雷成功\n");
display_board(mine, ROW, COL);
}
}
游戏胜利条件
排查思路
但是又有个问题了
解决方法
//统计x,y周围有几个雷
int get_mine_count(char board[ROWS][COLS], int x, int y)
{
return (board[x - 1][y] +
board[x - 1][y - 1] +
board[x][y - 1] +
board[x + 1][y - 1] +
board[x + 1][y] +
board[x + 1][y + 1] +
board[x][y + 1] +
board[x - 1][y + 1] - 8 * '0');
}
当排查 x,y 坐标时的展开条件
代码实现
//展开非雷区
void spreed(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{
int i = 0;
int j = 0;
//限制在棋盘内展开,防止越界
if ((x >= 1 && x <= ROW) && (y >= 1 && y <= COL))
{
int count = get_mine_count(mine, x, y);
if (0 == count)//展开条件2,该坐标周围没有雷
{
show[x][y] = ' ';//现将该坐标置成空格
for (i = x - 1; i <= x + 1; i++)
{
for (j = y - 1; j <= y + 1; j++)
{
if ('*' == show[i][j])//展开条件3,该坐标没有被排查过
{
//满足条件,展开递归
spreed(mine, show, i, j);//将坐标从 x y 向外扩散
//递归要传过去的新坐标是 i j 别把 x y 传过去了 (恼)
}
}
}
}
else//周围有雷,结束展开
{
show[x][y] = count + '0';
}
}
}
效果演示
代码实现
//标记地雷
void mark_mine(char show[ROWS][COLS],int row, int col)
{
int x = 0;
int y = 0;
display_board(show, ROW, COL);
printf("请输入要标记的坐标:");
scanf("%d %d", &x, &y);
if ((x >= 1 && x <= row) && (y >= 1 && y <= col))
{
if ('!' == show[x][y])
{
printf("该坐标已被标记,请重新输入\n");
}
else if ('*' == show[x][y])
{
show[x][y] = '!';
printf("标记成功\n");
display_board(show, ROW, COL);
}
}
else
{
printf("坐标非法,请重新输入\n");
}
}
结果演示
//取消标记
void cancel_mark(char show[ROWS][COLS], int row, int col)
{
int i = 0;
int x = 0;
int y = 0;
int count = 0;
printf("请输入要取消多少个标记:");
scanf("%d", &count);
while(i < count)
{
printf("请输入要取消标记的坐标:");
scanf("%d %d", &x, &y);
display_board(show, ROW, COL);
if ((x >= 1 && x <= row) && (y >= 1 && y <= col))
{
//取消标记
if ('!' == show[x][y])
{
show[x][y] = '*';
printf("取消标记成功\n");
display_board(show, ROW, COL);
i++;//这玩意放的位置别搞错了
}
else if ('*' == show[x][y])
{
printf("该坐标未被标记,请重新输入作标\n");
}
}
else
{
printf("坐标非法,请重新输入\n");
}
}
}
#include "game.h"
void menu()
{
printf("|-------------------------------|\n");
printf("|----------MineSweeper----------|\n");
printf("|*******************************|\n");
printf("|****** 1.play 0.exit ******|\n");
printf("|*******************************|\n");
printf("|是雷为 '1' --------- 非雷为 '0'|\n");
printf("|-------------------------------|\n");
}
void game()//游戏的整个流程
{
int operate = 0;
char mine[ROWS][COLS] = { 0 }; //存放布置好的地雷的信息
char show[ROWS][COLS] = { 0 }; //存放排查出的地雷的信息
init_board(mine, ROWS, COLS, '0'); //mine 数组在没有布雷时,都是 '0'
init_board(show, ROWS, COLS, '*'); //show 数组在没有排查时,都是 '*'
set_mine(mine, ROW, COL); //将雷布置到中间的9*9的格子里去
display_board(show, ROW, COL); //只打印中间9*9的内容,不打印外圈
find_mine(mine, show, ROW, COL); //排雷
}
int main()
{
int input = 0;
srand((unsigned int)time(NULL)); //设置随机数的生成起点
do
{
menu();
printf("\n请选择是否开始游戏:");
scanf("%d", &input);
switch (input)
{
case 1:
printf("\n开始游戏\n"); game(); break;
case 0:
printf("\n退出游戏\n"); break;
default:
printf("\n输入错误\n"); break;
}
} while (input);
return 0;
}
#include
#include
#include
//玩家看到的棋盘大小
#define ROW 9 //行
#define COL 9 //列
//棋盘的实际大小
#define ROWS ROW + 2
#define COLS COL + 2
#define EASY_COUNT 10 //雷的数量
//初始化数组为指定的字符
void init_board(char board[ROWS][COLS], int rows, int cols,char set);
//打印棋盘信息
void display_board(char board[ROWS][COLS], int row, int col);
//布雷
void set_mine(char board[ROWS][COLS], int row, int col);
//排雷
void find_mine(char mine[ROWS][COLS],char show[ROWS][COLS],int row, int col);
//标记地雷
void mark_mine(char show[ROWS][COLS], int row, int col);
//取消标记
void cancel_mark(char show[ROWS][COLS], int row, int col);
//展开非雷区
void spreed(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y);
#include "game.h"
//初始化棋盘
void init_board(char board[ROWS][COLS], int rows, int cols,char set)
{
int i = 0;
int j = 0;
for (i = 0; i < rows; i++)
{
for (j = 0; j < cols; j++)
{
board[i][j] = set;
//board[i][j] = '0';如果这样弄函数就不具备通用性了
}
}
}
//打印棋盘
void display_board(char board[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
printf("\n----MineSweeper----\n");
//打印列号
for (j = 0; j <= col; j++)
{
printf("%d ", j);
}
putchar('\n');
//显示出的棋盘的行和列都从 1 开始
for (i = 1; i <= row; i++)
{
printf("%d ", i);//打印行号
for (j = 1; j <= col; j++)
{
printf("%c ", board[i][j]);
}
putchar('\n');
}
printf("----MineSweeper----\n");
}
//布雷
void set_mine(char board[ROWS][COLS], int row, int col)
{
int count = EASY_COUNT;//10
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--;//这个要放在这里面,不然雷的布置的数量和设置的数量不一致
}
}
}
//统计x,y周围有几个雷
int get_mine_count(char board[ROWS][COLS], int x, int y)
{
return (board[x - 1][y] +
board[x - 1][y - 1] +
board[x][y - 1] +
board[x + 1][y - 1] +
board[x + 1][y] +
board[x + 1][y + 1] +
board[x][y + 1] +
board[x - 1][y + 1] - 8 * '0');
}
//排雷
void find_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int win = 0;//找到非雷的个数
int operate = 0;
while (win < ROW * COL - EASY_COUNT)
{
putchar('\n');
printf("|------------------|\n");
printf("|--- 1.排查地雷 ---|\n");
printf("|--- 2.标记地雷 ---|\n");
printf("|--- 3.取消标记 ---|\n");
printf("|------------------|\n");
printf(" 请选择你的操作:");
scanf("%d", &operate);
if(1==operate)
{
display_board(show, ROW, COL);
printf("请输入要排查的坐标:");
scanf("%d %d", &x, &y);
//判断坐标是否合法
if ((x >= 1 && x <= row) && (y >= 1 && y <= col))
{
if (show[x][y] != '*')
{
printf("该位置已被排查过,请重新输入\n");
}
if ('1' == mine[x][y]) //如果是雷
{
printf("\n很遗憾,你被炸死了\n");
display_board(mine, ROW, COL);//挂了之后要展示雷的位置
break;
}
else //如果不是雷,就要统计周围有几个雷
{
//int count = get_mine_count(mine, x, y);
//show[x][y] = count + '0';
spreed(mine, show, x, y); //该坐标不是雷
display_board(show, ROW, COL);
win++;
}
}
else
{
printf("坐标非法,请重新输入\n");
}
}
else if (2 == operate)
{
mark_mine(show, ROW, COL);
}
else if (3 == operate)
{
cancel_mark(show, ROW, COL);
}
}
if (ROW * COL - EASY_COUNT == win)
{
printf("\n恭喜你,排雷成功\n");
display_board(mine, ROW, COL);
}
}
//标记地雷
void mark_mine(char show[ROWS][COLS],int row, int col)
{
int x = 0;
int y = 0;
display_board(show, ROW, COL);
printf("请输入要标记的坐标:");
scanf("%d %d", &x, &y);
if ((x >= 1 && x <= row) && (y >= 1 && y <= col))
{
if ('!' == show[x][y])
{
printf("该坐标已被标记,请重新输入\n");
}
else if ('*' == show[x][y])
{
show[x][y] = '!';
printf("标记成功\n");
display_board(show, ROW, COL);
}
}
else
{
printf("坐标非法,请重新输入\n");
}
}
//取消标记
void cancel_mark(char show[ROWS][COLS], int row, int col)
{
int i = 0;
int x = 0;
int y = 0;
int count = 0;
printf("请输入要取消多少个标记:");
scanf("%d", &count);
while(i < count)
{
printf("请输入要取消标记的坐标:");
scanf("%d %d", &x, &y);
display_board(show, ROW, COL);
if ((x >= 1 && x <= row) && (y >= 1 && y <= col))
{
//取消标记
if ('!' == show[x][y])
{
show[x][y] = '*';
printf("取消标记成功\n");
display_board(show, ROW, COL);
i++;
}
else if ('*' == show[x][y])
{
printf("该坐标未被标记,请重新输入作标\n");
}
}
else
{
printf("坐标非法,请重新输入\n");
}
}
}
//展开非雷区
void spreed(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{
int i = 0;
int j = 0;
//限制在棋盘内展开,防止越界
if ((x >= 1 && x <= ROW) && (y >= 1 && y <= COL))
{
int count = get_mine_count(mine, x, y);
if (0 == count) //该坐标周围没有雷
{
show[x][y] = ' ';
for (i = x - 1; i <= x + 1; i++)
{
for (j = y - 1; j <= y + 1; j++)
{
if ('*' == show[i][j]) //该坐标没有被排查过
{
spreed(mine, show, i, j); //将坐标从 x y 向外扩散
//递归要传过去的新坐标是 i j 别把 x y 传过去了 (恼)
}
}
}
}
else //周围有雷,结束展开
{
show[x][y] = count + '0';
}
}
}
被雷炸死
游戏胜利