今天我们来看看扫雷这个游戏如何用C实现:
思路挺容易,想要写的功能齐全还真有些费劲X_X||,不过这个游戏做起来个人感觉对函数调用以及各种基础语法的熟悉掌握都很有帮助。
思路:
先来说说扫雷都具有那些基础功能:
首先我们需要一个可以和玩家交互的界面:菜单的打印;还有雷区;然后是如何生成雷;其次,我们要将玩家输入的坐标进行判断并将结果显示在界面上(是没死,还是被炸了,又或者已经扫雷成功);最后为了不输电脑自带的扫雷,还要实现如何点一下出来一片的‘顺滑操作’…
函数模块及代码:
1.菜单还是printf搞定能用就行:
void menu()
{
printf("************************\n");
printf("******1,开始游戏********\n");
printf("******0,退出游戏********\n");
printf("************************\n");
}
2.初始化与打印雷区
打印雷区有一点需要特别注意:因为我们要在界面上显示出来方格周围有几个雷,而雷和安全快的我们用字符‘1’和‘0’区别,为了避免存储产生冲突,在这里我们使用两个二维数组分别存放:ture数组存放雷和安全块的位置,判断和运算都从这里抓取数据,并不打印出来;而show数组则用来给玩家显示我们用‘*’来初始化,所以界面是这个样子的:
void InitBoard(char arr[ROWS][COLS], int rows, int cols,char want)
{
memset(arr, want, rows*cols);
}
void PrintBoard(char arr[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
printf(" ");
for (j = 0; j <= col; j++)
{
printf("%d ", j);
}
printf("\n\n");
for (i = 1; i <= row; i++)
{
printf("%d ", i);
for (j = 1; j <= col; j++)
{
printf(" %c", arr[i][j]);
}
printf("\n");
}
printf("\n");
}
3.布雷
有雷才叫扫雷嘛!和n子棋一样,我在这里使用了srand和rand函数产生随机的坐标在这个坐标里放雷同样需要考虑输入合法性的问题:
void SetBom(char ture[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int count = BOM;
while (count)
{
x = (rand() % row) + 1;
y = (rand() % row) + 1;
if (ture[x][y] == '0')
{
ture[x][y] = '1';
count--;
}
}
}
static void SetBomforFrist_PyrMov(char ture[ROWS][COLS],int x,int y)
{
int count = 1;
while (count)
{
x = (rand() % ROW) + 1;
y = (rand() % COL) + 1;
if (ture[x][y] == '0')
{
ture[x][y] = '1';
count--;
}
}
}
4.玩家扫雷与连消
这里可是重头戏,扫雷的灵魂可就是在连消上面!玩家扫雷的判定胜负我就不赘述了,与n子棋大同小异,主要谈一谈连消的策略:
这个效果实现于Consecutive_show()函数,核心思想是在传入一个合法坐标(为空且周围无雷)后,以此坐标为中心向上下左右四个
//方向抓取距离为1的相邻格子再以这些格子为核心再次抓取距离唯一的相邻格子,循环往复达到覆盖整个棋盘的目的,因为步骤的相似性,我在这里使用了递归,当检测到周围存在地雷时,
//立刻将自身标记为周围雷的个数(CountBom()函数),在这里为了避免重复查找,我将每一个曾作为核心的格子都替换为字符空格,在接下来的查找过程中一旦检测到空格便不做处理直接跳过。
这里使用到了三个函数,来啊,小二,上代码!
static int CountBom(char ture[ROWS][COLS], int x, int y )
{
int count = 0;
//return (ture[x-1][y] + ture[x-1][y-1] + ture[x][y-1] + ture[x+1][y-1] + ture[x+1][y] + ture[x+1][y+1] + ture[x][y+1] + ture[x-1][y+1]) % '0';
if (ture[x - 1][y] != ' ')
{
count +=(ture[x-1][y]-'0');
}
if (ture[x - 1][y-1] != ' ')
{
count += (ture[x - 1][y-1] - '0');
}
if (ture[x][y-1] != ' ')
{
count += (ture[x][y-1] - '0');
}
if (ture[x+1][y-1] != ' ')
{
count += (ture[x+1][y-1] - '0');
}
if (ture[x+1][y] != ' ')
{
count += (ture[x+1][y] - '0');
}
if (ture[x+1][y+1] != ' ')
{
count += (ture[x+1][y+1] - '0');
}
if (ture[x][y+1] != ' ')
{
count += (ture[x][y+1] - '0');
}
if (ture[x - 1][y+1] != ' ')
{
count += (ture[x - 1][y+1] - '0');
}
return count;
}
static void Consecutive_show(char ture[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{
int blank = 0;
//Debug Log:blank = CountBom(ture, x, y) % 13;//'='-'0'的asc码值是13所以即使检测到' ='返回的值必然是'1'的个数加上13的倍数,blank得到的就是不含已检测过安全格的其他格得到的结果//不成熟,会损失数据。
blank = CountBom(ture, x, y);
if (blank == 0)
{
ture[x][y] = ' ';
show[x][y] = ' ';
if (x > 0 && x <= ROW && y>0 && y <= COL )
{
if (ture[x - 1][y] != ' ')
{
Consecutive_show(ture, show, x - 1, y);//上
}
if (ture[x][y-1] != ' ')
{
Consecutive_show(ture, show, x , y-1);//左
}
if (ture[x+1][y] != ' ')
{
Consecutive_show(ture, show, x +1, y);//下
}
if (ture[x][y+1] != ' ')
{
Consecutive_show(ture, show, x, y+1);//右
}
}
}
else
{
show[x][y] = blank + '0';
}
}
void PyrMov(char ture[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int flag = 1;
int x = 0;
int y = 0;
int count= 0;
int i = 0;
int j = 0;
int judge_Frist = 1;
while (flag)
{
for (i = 1; i <= row; i++)
{
for (j = 1; j <= col; j++)
{
if (show[i][j] == '*')
{
count++;
}
}
}
if (count >BOM)
{
printf("请输入坐标:>");
scanf("%d%d", &x, &y);
if (x > 0 && x <= row && y > 0 && y <= col)
{
/* if (count <(row*col-BOM-1))
{*/
//PrintBoard(ture, ROW, COL);
if (ture[x][y] == '1' && judge_Frist == 1)//为了提升玩家的游戏体验,第一次走如果踩到雷,调用SetBomforFrist_PyrMov()将这颗雷转移到其他地方;
{
judge_Frist = 0;
ture[x][y] = '0';
SetBomforFrist_PyrMov(ture, x, y);
Consecutive_show(ture, show, x, y);
PrintBoard(ture, ROW, COL);
}
else if (ture[x][y] == '1')
{
printf("踩到雷了,GameOver!\n\n");
flag = 0;
PrintBoard(ture, ROW, COL);
}
else
{
judge_Frist = 0;
//show[x][y] = CountBom(ture, x, y) + '0';
Consecutive_show(ture, show, x, y);
PrintBoard(show, ROW, COL);
}
//count++;
//}
/* else
{
printf("扫雷成功!厉害!\n\n");
flag = 0;
}*/
}
else
{
printf("输入非法坐标,请重新输入!/n");
}
count = 0;
}
else
{
printf("扫雷成功!厉害!\n\n");
flag = 0;
}
}
}
这样就达到了我所预想的顺滑效果:
真*点击就赢= =||(雷数设成1了)
头文件和源代码
SL.h
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<string.h>
#include<assert.h>
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define BOM 1
void InitBoard(char arr[ROWS][COLS], int rows, int cols, char want);
void PrintBoard(char arr[ROWS][COLS], int row, int col);
void SetBom(char ture[ROWS][COLS], int row, int col);
void PyrMov(char ture[ROWS][COLS], char show[ROWS][COLS], int row, int col);
test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"SL.h"
void game()
{
char ture[ROWS][COLS];
char show[ROWS][COLS];
InitBoard(ture, ROWS, COLS, '0');
SetBom(ture, ROW, COL);
InitBoard(show, ROWS, COLS, '*');
//PrintBoard(ture, ROW, COL);
PrintBoard(show, ROW, COL);
PyrMov(ture, show,ROW, COL);
}
void menu()
{
printf("************************\n");
printf("******1,开始游戏********\n");
printf("******0,退出游戏********\n");
printf("************************\n");
}
int main()
{
assert(BOM < ROW*COL);
srand((unsigned int)time(NULL));
int input = 0;
do
{
menu();
printf("请选择:\n");
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出游戏!\n");
break;
default:
printf("输入错误,请重新输入!\n");
}
}while (input);
system("pause");
return 0;
}
当然本小白水平有限,敲代码的过程中也来来回回debug了好多次,这里有几个个经常出错的地方,贴出来供大家参考:
//Debug log:
//***使用递归时添加限制条件不充分,导致程序经常性栈溢出!
//***频繁使用for以及while导致层次太深无法及时跳出,差点逼不得已请出goto大法,最后还是修改了整个函数结构。
好了扫雷就暂且分享到这里了,你如果还有什么新想法欢迎在下方评论区提出哦!