本篇适用于C语言初学者,主要涉及对于函数,数组,分支循环的运用。
目录
设计思想:
总代码(改进后):
运行结果展示:
分布介绍:
声明:
代码主体部分:
功能模块实现:
初始化模块:
打印模块:
埋雷模块:
判断模块:
总结:
设计思想:
首先要有一个游戏菜单,输入1表示开始,0表示结束,其它数字则提示输入错误,请重新输入;其次要有雷盘,雷盘用二维数组表示,开始时要有初始化模块对二维数组初始化,要有埋雷模块放置雷,要有判断输赢及返回结果模块,要有打印模块向玩家展示雷盘,为了更容易实现这些模块,我们选择两个二维数组表示雷盘,其中一个埋雷,其中一个向玩家展示。为了方便计算周围雷的个数,空用 '0' 表示,;雷用 '1'表示。
总代码(改进后):
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define ROW 9
#define COL 9
#define ROWS ROW + 2
#define COLS COL + 2
#define Easy_Mine 10
void InitBorad(char borad[ROWS][COLS], int rows, int cols, char ch);
void DisplayBorad(char borad[ROWS][COLS], int row, int col);
void SetMine(char borad[ROWS][COLS], int row, int col);
void FindMine(char mine_borad[ROWS][COLS], char show_board[ROWS][COLS], int row, int col);
//初始化
void InitBorad(char borad[ROWS][COLS], int rows, int cols, char ch)
{
int i = 0;
for (i = 0; i < rows; i++)
{
int j = 0;
for (j = 0; j < cols; j++)
{
borad[i][j] = ch;
}
}
}
//打印拓展
void DisplayBorad(char borad[ROWS][COLS], int row, int col)
{
int i = 0;
//列标
for (i = 0; i <= col; i++)
{
printf(" %d |", i);
}
printf("\n");
for (i = 0; i <= col; i++)
{
printf("---|", i);
}
printf("\n");
for (i = 1; i <= row; i++)
{
//行标
printf(" %d |", i);
int j = 0;
for (j = 1; j <= col; j++)
{
printf(" %c |",borad[i][j]);
}
printf("\n");
for (j = 0; j <= col; j++)
{
printf("---|", i);
}
printf("\n");
}
printf("\n");
}
//放置雷
void SetMine(char borad[ROWS][COLS], int row, int col)
{
int count = Easy_Mine;
while (count)
{
int x = rand() % row + 1;
int y = rand() % col + 1;
if (borad[x][y] == '0')
{
borad[x][y] = '1';
count--;
}
}
}
//改进判断法
//递归实现扫雷的展开一片
void GetMineCount(char mine_borad[ROWS][COLS], char show_borad[ROWS][COLS], int x, int y, int* count)
{
if (x >= 1 && x <= ROW && y >= 1 && y <= COL && show_borad[x][y] == '*')
{
int i = x - 1;
int j = y - 1;
int sum = 0;
//计算周围有几个雷
for (i = x - 1; i <= x + 1; i++)
{
for (j = y - 1; j <= y + 1; j++)
{
sum = sum +(mine_borad[i][j] - '0');
}
}
//如果周围没有雷,将此坐标置为空格,递归查找周围的周围是否有雷...
if (sum == 0)
{
show_borad[x][y] = ' ';
for (i = x - 1; i <= x + 1; i++)
{
for (j = y - 1; j <= y + 1; j++)
{
//更正循环次数
(*count)--;
GetMineCount(mine_borad, show_borad, i, j, count);
}
}
}
//如果周围有雷,将此坐标字符该为对应雷的个数的字符
else
{
//更正循环次数
(*count)--;
show_borad[x][y] = sum + '0';
}
}
}
void FindMine(char mine_borad[ROWS][COLS], char show_board[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int count = row * col - Easy_Mine;
//实现主体
while (count)
{
printf("请输入坐标:>");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (mine_borad[x][y] == '1')
{
printf("嘭!你被炸死了!\n");
DisplayBorad(mine_borad, ROW, COL);
break;
}
else if (mine_borad[x][y] == '0' && show_board[x][y] == '*');
{
GetMineCount(mine_borad, show_board, x, y, &count);
//count = count_number(show_board, row, col);
//DisplayBorad(mine_borad, ROW, COL);
DisplayBorad(show_board, ROW, COL);
}
}
else
{
printf("输入坐标非法,请重新输入!\n");
}
}
if (count == 0)
{
printf("恭喜你,你赢了!\n");
DisplayBorad(mine_borad, row, col);
}
}
void menu()
{
printf("******************************\n");
printf("******** 1.paly **********\n");
printf("******** 0.exit **********\n");
printf("******************************\n");
}
void game()
{
//此二维数组用来放置雷
char mine[ROWS][COLS] = { 0 };
//此二维数组用于像玩家展示
char show[ROWS][COLS] = { 0 };
//初始化两个二维数组
InitBorad(mine, ROWS, COLS, '0');
//DisplayBorad(mine, ROW, COL);
InitBorad(show, ROWS, COLS, '*');
//放置雷
SetMine(mine, ROW, COL);
//打印棋盘
DisplayBorad(show, ROW, COL);
//DisplayBorad(mine, 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:
game();
break;
case 0:
printf("退出游戏!\n");
break;
default:
printf("选择错误,请重新选择!\n");
break;
}
} while (input);
return 0;
}
运行结果展示:
分布介绍:
声明:
如果想要更改雷盘大小,放置雷个数,只需对ROW , COL , Easy_Mine作相应的修改即可。
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
//标识符定义行与列
#define ROW 9
#define COL 9
//雷盘实际大小
#define ROWS ROW + 2
#define COLS COL + 2
//雷的个数
#define Easy_Mine 10
//主要函数声明
void InitBorad(char borad[ROWS][COLS], int rows, int cols, char ch);
void DisplayBorad(char borad[ROWS][COLS], int row, int col);
void SetMine(char borad[ROWS][COLS], int row, int col);
void FindMine(char mine_borad[ROWS][COLS], char show_board[ROWS][COLS], int row, int col);
代码主体部分:
关于作为9 * 9的雷盘为什么要选择11 * 11的二维数组表示,是为了防止越界访问,以及更容易地实现扫雷后的判断和返回结果。用字符0将要放置雷的二维数组初始化,用字符*将向玩家展示的二维数组初始化,用字符1表示雷。
//游戏菜单
void menu()
{
printf("******************************\n");
printf("******** 1.paly **********\n");
printf("******** 0.exit **********\n");
printf("******************************\n");
}
//实现主体
void game()
{
//此二维数组用来放置雷
char mine[ROWS][COLS] = { 0 };
//此二维数组用于像玩家展示
char show[ROWS][COLS] = { 0 };
//初始化两个二维数组
InitBorad(mine, ROWS, COLS, '0');
//DisplayBorad(mine, ROW, COL);
InitBorad(show, ROWS, COLS, '*');
//放置雷
SetMine(mine, ROW, COL);
//打印棋盘
DisplayBorad(show, ROW, COL);
//DisplayBorad(mine, 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:
game();
break;
case 0:
printf("退出游戏!\n");
break;
default:
printf("选择错误,请重新选择!\n");
break;
}
} while (input);
return 0;
}
功能模块实现:
初始化模块:
通过嵌套for循环用传过来的字符将传过来的数组初始化。
//初始化
void InitBorad(char borad[ROWS][COLS], int rows, int cols, char ch)
{
int i = 0;
for (i = 0; i < rows; i++)
{
int j = 0;
for (j = 0; j < cols; j++)
{
borad[i][j] = ch;
}
}
}
打印模块:
原始打印模块:
先利用for循环打印列标,再利用嵌套for循环打印行标及二维数组。代码及雷盘展示如下:
//打印
void DisplayBorad(char borad[ROWS][COLS], int row, int col)
{
int i = 0;
//列标
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 ", borad[i][j]);
}
printf("\n");
}
printf("\n");
}
雷盘:
改进打印模块:
原理如上,加以修饰,代码及雷盘展示如下:
//打印拓展
void DisplayBorad(char borad[ROWS][COLS], int row, int col)
{
int i = 0;
//列标
for (i = 0; i <= col; i++)
{
printf(" %d |", i);
}
printf("\n");
for (i = 0; i <= col; i++)
{
printf("---|", i);
}
printf("\n");
for (i = 1; i <= row; i++)
{
//行标
printf(" %d |", i);
int j = 0;
for (j = 1; j <= col; j++)
{
printf(" %c |",borad[i][j]);
}
printf("\n");
for (j = 0; j <= col; j++)
{
printf("---|", i);
}
printf("\n");
}
printf("\n");
}
雷盘:
埋雷模块:
要实现在雷盘上随机放置雷,需要srand()和time()库函数来确定随机数生成期起点,rand()库函数来生成随机数,将获得的随机数大小控制在1~9之间(雷盘逻辑大小),获得要放置雷的坐标后,找到与之对应的二维数组元素,将其置为字符1。(字符0表示不是雷,字符1表示雷)模块代码及放置雷的二维数组展示:
//放置雷
void SetMine(char borad[ROWS][COLS], int row, int col)
{
//要放置的雷的个数
int count = Easy_Mine;
while (count)
{
//获得要放置雷的坐标
int x = rand() % row + 1;
int y = rand() % col + 1;
//如果此坐标未放置雷,则放置雷,否则重新获得坐标
if (borad[x][y] == '0')
{
borad[x][y] = '1';
count--;
}
}
}
放置雷的二维数组:
判断模块:
原始判断模块;
此模块需要将真雷盘和假雷盘二维数组都传过去。玩家输入要扫雷的坐标,如果坐标不合法,提示输入错误,重新输入。如果坐标合法,与放置雷的二维数组的相应坐标对照,如果此坐标是雷,则输出玩家扫雷失败,并将埋雷的二维数组向玩家展示,游戏结束,如果此坐标不是雷,则判断此坐标周围一圈有无雷,如果无雷,返回0,有雷的话,返回雷的个数,并将向玩家展示的二维数组的对应的坐标更改为雷的个数,更正循环控制条件,继续游戏。当循环控制条件不再满足(及已将所有不是雷的坐标扫出)则获得游戏胜利,游戏结束。
//原始判断法
//返回此坐标周围雷的个数
int GetMineCount(char borad[ROWS][COLS], int x, int y)
{
//字符1~9减去一个字符0就可以得到整型数1~9,这也是选择用字符0和1表示是否有雷的原因
return (borad[x - 1][y] + borad[x - 1][y - 1] + borad[x][y - 1] + borad[x + 1][y - 1]
+ borad[x + 1][y] + borad[x + 1][y + 1] + borad[x][y + 1] + borad[x - 1][y + 1] - 8 * '0');
}
//判断输赢
void FindMine(char mine_borad[ROWS][COLS], char show_board[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
//循环控制条件
int count = row * col - Easy_Mine;
while (count)
{
printf("请输入坐标:>");
scanf("%d %d", &x, &y);
//判断输入坐标是否合法
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
//此坐标是雷
if (mine_borad[x][y] == '1')
{
printf("嘭!你被炸死了!\n");
//打印放置雷的二维数组
DisplayBorad(mine_borad, ROW, COL);
break;
}
//此坐标不是雷
else if (mine_borad[x][y] == '0' && show_board[x][y] != ' ');
{
//函数调用返回雷的个数
int ret = GetMineCount(mine_borad, x, y);
//将向玩家展示的二维数组的对应坐标更改为雷的个数(将整型数1~9加一个字符0就可转换成字符1~9)
show_board[x][y] = ret + '0';
//打印
DisplayBorad(show_board, ROW, COL);
//更正循环控制条件
count--;
}
}
else
{
printf("输入坐标非法,请重新输入!\n");
}
}
//游戏胜利判定
if (count == 0)
{
printf("恭喜你,你赢了!\n");
DisplayBorad(mine_borad, row, col);
}
}
运行展示:
改进判断模块:
上述判断模块跟扫雷游戏还是有些实质差异,在扫雷游戏中,当一个坐标周围一圈都没有雷时,就会为空,再对他周围的坐标的周围进行判断......因此需要对其进行改进,总体思想不变,仅需对判断坐标周围一圈雷数的子模块以及循环控制条件进行修改即可。当输入坐标周围一圈都没有雷时,将向玩家展示的二维数组的对应坐标置为空格,更改循环控制条件,并判断它周围坐标的周围是否有雷.....依此类推。这里通过循环和递归思想实现此功能。
//改进判断法
//递归实现扫雷的展开一片
void GetMineCount(char mine_borad[ROWS][COLS], char show_borad[ROWS][COLS], int x, int y, int* count)
{
//递归限制条件
if (x >= 1 && x <= ROW && y >= 1 && y <= COL && show_borad[x][y] == '*')
{
int i = x - 1;
int j = y - 1;
//此变量表示雷的个数
int sum = 0;
//计算周围有几个雷
for (i = x - 1; i <= x + 1; i++)
{
for (j = y - 1; j <= y + 1; j++)
{
sum = sum +(mine_borad[i][j] - '0');
}
}
//如果周围没有雷,将此坐标置为空格,递归查找周围的周围是否有雷...
if (sum == 0)
{
show_borad[x][y] = ' ';
for (i = x - 1; i <= x + 1; i++)
{
for (j = y - 1; j <= y + 1; j++)
{
//更正循环次数
(*count)--;
//递归
GetMineCount(mine_borad, show_borad, i, j, count);
}
}
}
//如果周围有雷,将此坐标字符改为对应雷的个数的字符
else
{
//更正循环次数
(*count)--;
show_borad[x][y] = sum + '0';
}
}
}
void FindMine(char mine_borad[ROWS][COLS], char show_board[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int count = row * col - Easy_Mine;
//实现主体
while (count)
{
printf("请输入坐标:>");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (mine_borad[x][y] == '1')
{
printf("嘭!你被炸死了!\n");
DisplayBorad(mine_borad, ROW, COL);
break;
}
else if (mine_borad[x][y] == '0' && show_board[x][y] == '*');
{
GetMineCount(mine_borad, show_board, x, y, &count);
//count = count_number(show_board, row, col);
//DisplayBorad(mine_borad, ROW, COL);
DisplayBorad(show_board, ROW, COL);
}
}
else
{
printf("输入坐标非法,请重新输入!\n");
}
}
if (count == 0)
{
printf("恭喜你,你赢了!\n");
DisplayBorad(mine_borad, row, col);
}
}
运行展示:
总结:
关于扫雷游戏的基本模块已经实现,可以开始游戏啦!当然,这个简易扫雷游戏还是可以继续优化的,比如增加标记功能等,这些模块的实现就需要大家自行去探索了,我就不过多阐述了。本期内容就到这里,如果帮到你的话,还请给个一键三连吧。