目录
👍前言
当我们拿到 扫雷 这样一个项目的时候
不应该着急地打开编译器开始写
而是先沉下心来,理清楚思路、想法
再加以实现
这样既能提高效率,又能保证质量。
👍思路
那么先给大家分享一下我的思路:
使用二维数组来实现
雷位置设置成1,安全位置设置成0;
扫一个位置,周围8格有几个雷就返回几;
但是这样就会有歧义:如果返回1,不能确定是扫到雷还是周围有一个雷
所以我们需要两个棋盘
一个棋盘show给玩家展示,默认初始化为*,扫后显示周围雷的数量
一个棋盘mine存放雷的位置,雷是1,非雷是0;
1.二维数组初始化InitBoard
2.随机设置雷Setmine
3.排查雷Findmine
4.判断输赢
5.打印Displayboard
*另外,为了使项目更加清晰,我决定让代码模块化:
text.c里实现游戏的底层逻辑
game.c里实现游戏的各个功能
game.h里实现函数的声明
👍实现
游戏底层逻辑:
1.因为游戏要重复性,又有选择分支,所以用do while 和switch实现
2.这里巧妙的是,input的值如果是0,既表示退出游戏,也表示退出循环,结束程序
int main()
{
int input = 0;
srand((unsigned int)time(NULL));
do
{
menu();
printf("请选择\n");
scanf("%d", &input);
switch (input)
{
case 1:
printf("------------棋盘----------\n");
game();
break;
case 0:
printf("退出游戏\n");
break;
default :
printf("选择错误\n");
}
} while (input);
return 0;
}
1.二维数组初始化InitBoard
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
{
//初始化数据
//mine为0
//show为*
int i = 0;
int j = 0;
for (i = 0;i < ROWS;i++)
{
for (j = 0;j < COLS;j++)
{
board[i][j] = set;
}
}
}
这里用到了头文件定义的全局变量
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2//为防止后面会越界访问
2.随机设置雷Setmine
棋盘初始化好后,就要给mine设置雷上去
void Setmine(char mine[ROWS][COLS], char minea[ROWS][COLS], int row, int col)
{
int count = MINE;
while (count)
{
int x = rand()%row + 1;
int y = rand()%col + 1;
if (mine[x][y] == '0')
{
mine[x][y] = '1';
count--;
}
}
}
当然,也别忘判断一个位置是否已经设置了雷
为了方便,这里在头文件设置了MINE雷的个数
#define MINE 10
3.排查雷Findmine
*注:如果排查过坐标是用continue结束本次循环,让玩家重新输入坐标
而如果被炸死,游戏结束,直接跳出循环
printf("请输入要排查的坐标:>\n");
scanf("%d %d", &x, &y);
if (x <= row && x >= 1 && y <= col && y >= 1)
{
if (show[x][y] != '*')
{
printf("坐标已排查过,请重新输入\n");
continue;
}
if (mine[x][y] == '1')
{
printf("很遗憾,你被炸死了\n");
Displayboard(mine, ROW, COL);
break;
}
else
{
//统计雷
int count = get_mine_count(mine,show, x, y);
show[x][y] = count + '0';
Displayboard(show, ROW, COL);
}
}
else
{
printf("坐标非法,请重新输入 ");
getchar();
}
这里统计雷,巧妙的是周围八个数字相加,刚好得到的就是改点附近雷的个数
(当然,直接相加得到的是字符,还要减去(8* '0' ))
int sum = mine[x - 1][y - 1] + mine[x - 1][y] + mine[x - 1][y + 1] +
mine[x][y - 1] + mine[x][y + 1] +
mine[x + 1][y - 1] + mine[x + 1][y] + mine[x + 1][y + 1] - 8 * '0';
return sum;
4.判断输赢
当然,排查雷肯定不止一次,所以要把它放进循环里
这样一来,循环的条件刚好就是输赢的条件
int is_win(char show[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
int count = 0;
for (i = 1;i <= row;i++)
{
for (j = 1;j <= col;j++)
{
if (show[i][j] == '*')
count++;
}
}
return count;
}
while (is_win(show, row, col) != MINE)
即:如果剩下没扫的*和雷的数量一样,就可以跳出循环判断为赢
5.打印Displayboard
打印的时候,为了方面找坐标,要打印1-9的数字对齐
void Displayboard(char board[ROWS][COLS], int row, int col)
{
//打印棋盘
int i = 0;
int j = 0;
for (i = 0; i<=col; i++)
{
printf("%d ", i);//先打印1-9的列坐标,同时打印0是为了占角位置
}
printf("\n");
for (i = 1;i <= row; i++)
{
printf("%d ", i);
for (j = 1;j <= col; j++)
{
printf("%c ", board[i][j]);
}
printf("\n");
}
}
👍总结:
1.理清思路很重要
2.合理规划,仔细判断
3.只用眼睛看代码,代码永远不是你的。只有自己写的代码才是自己的,自己动手多练习
最终代码
game.h
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define MINE 10
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 mine[ROWS][COLS], int row, int col);
void Findmine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
game.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
{
//初始化数据
//mine为0
//show为*
int i = 0;
int j = 0;
for (i = 0;i < ROWS;i++)
{
for (j = 0;j < COLS;j++)
{
board[i][j] = set;
}
}
}
void Displayboard(char board[ROWS][COLS], int row, int col)
{
//打印棋盘
int i = 0;
int j = 0;
for (i = 0; i<=col; i++)
{
printf("%d ", i);
}
printf("\n");
for (i = 1;i <= row; i++)
{
printf("%d ", i);
for (j = 1;j <= col; j++)
{
printf("%c ", board[i][j]);
}
printf("\n");
}
}
void Setmine(char mine[ROWS][COLS], int row, int col)
{
int count = MINE;
while (count)
{
int x = rand()%row + 1;
int y = rand()%col + 1;
if (mine[x][y] == '0')
{
mine[x][y] = '1';
count--;
}
}
}
static int get_mine_count(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{
int sum = mine[x - 1][y - 1] + mine[x - 1][y] + mine[x - 1][y + 1] +
mine[x][y - 1] + mine[x][y + 1] +
mine[x + 1][y - 1] + mine[x + 1][y] + mine[x + 1][y + 1] - 8 * '0';
return sum;
}
int is_win(char show[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
int count = 0;
for (i = 1;i <= row;i++)
{
for (j = 1;j <= col;j++)
{
if (show[i][j] == '*')
count++;
}
}
return count;
}
void Findmine(char mine[ROWS][COLS], char show[ROWS][COLS], char minea[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
while (is_win(show, row, col) != MINE)
{
printf("请输入要排查的坐标:>\n");
scanf("%d %d", &x, &y);
if (x <= row && x >= 1 && y <= col && y >= 1)
{
if (show[x][y] != '*')
{
printf("坐标已排查过,请重新输入\n");
continue;
}
if (mine[x][y] == '1')
{
printf("很遗憾,你被炸死了\n");
Displayboard(mine, ROW, COL);
break;
}
else
{
//统计雷
int count = get_mine_count(mine,show, x, y);
show[x][y] = count + '0';
Displayboard(show, ROW, COL);
}
}
else
{
printf("坐标非法,请重新输入 ");
getchar();
}
}
if (is_win(show, row, col) == MINE)
{
printf("恭喜你赢了!!\n");
printf("恭喜你赢了!!\n");
printf("恭喜你赢了!!\n");
}
}
text.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
void menu()
{
printf("***********扫雷游戏**********\n");
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, '*');
//打印
//Displayboard(mine, ROW, COL);
Displayboard(show, ROW, COL);
//布置雷
Setmine(mine,minea, ROW, COL);
//Displayboard(mine, ROW, COL);
//排查雷
Findmine(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");
game();
break;
case 0:
printf("退出游戏\n");
break;
default :
printf("选择错误\n");
}
} while (input);
return 0;
}
👍扩展
我们在玩扫雷游戏时,发现扫出来结果是0,周围没有雷,还要一个一个扫很麻烦
所以我们要实现一个功能能实现从一个坐标向周围辐射自动扫雷,直到遇见不为0
向外辐射有条件
1.没有超出边界
2.扫过的位置不能再扫
3.遇见非0停下
*注:既然扫过不能再扫,我们就需要再加一个二维数组来表示是否扫过的状态
而且这个二维数组还要有雷的位置
那就直接加
char minea[ROWS][COLS] = { 0 };
InitBoard(minea, ROWS, COLS, '0');
另外,设置雷的时候再给minea设置同样的雷就行
实现
首先,因为扫结果必须是0才得向外辐射,所以我们直接在 统计雷 那里实现
static int get_mine_count(char mine[ROWS][COLS], char show[ROWS][COLS], char minea[ROWS][COLS], int x, int y)
{
int sum = mine[x - 1][y - 1] + mine[x - 1][y] + mine[x - 1][y + 1] +
mine[x][y - 1] + mine[x][y + 1] +
mine[x + 1][y - 1] + mine[x + 1][y] + mine[x + 1][y + 1] - 8 * '0';
if (sum == 0 && x>0 && x < ROWS && y>0 && y<COLS &&minea[x][y] != ' ')
{
minea[x][y] = ' ';
show[x][y] = get_mine_count(mine, show, minea, x, y)+'0';
show[x][y+1] = get_mine_count(mine, show, minea, x, y+1)+'0';
show[x][y-1] = get_mine_count(mine, show, minea, x, y-1)+'0';
show[x+1][y] = get_mine_count(mine, show, minea, x+1, y)+'0';
show[x+1][y+1] = get_mine_count(mine, show, minea, x+1, y+1)+'0';
show[x+1][y-1] = get_mine_count(mine, show, minea, x+1, y-1)+'0';
show[x-1][y] = get_mine_count(mine, show, minea, x-1, y)+'0';
show[x-1][y+1] = get_mine_count(mine, show, minea, x-1, y+1)+'0';
show[x-1][y-1] = get_mine_count(mine, show, minea, x-1, y-1)+'0';
}
if (sum == 0 && x > 0 && x < ROWS && y>0 && y < COLS)
{
return ' '-'0';
}
return sum;
}
*注:我们使用递归实现,判断条件就是
1.没有超出边界
2.扫过的位置不能再扫
扫过的位置我们要在minea设置为空格,表示扫过
然后对每个位置分别递归
返回值就是周围雷的个数,赋给show的该位置
(加' 0 '让返回的ing变成char字符)
如果递归完,周围没有雷,返回一个 ‘ ’ (' '-'0')给show该位置,方便看
否则就返回雷的数量sum
👍扫雷游戏就到此,由于作者水平有限,欢迎提出意见或建议