前言
看到这个图片相信大家肯定dna都动了把,联想起某年某月某日的上午或下午,是不是它救起了差点就要靠上厕所打发时间的你。但是在win11中,它却消失在了你的游戏分栏中,但是在今天,你可以通过一杯咖啡的时间来自己动手做出这个游戏。
(但由于本人技术力有限,完成度较差,希望各位海涵)
游戏实现
界面打印
这个和本人之前写过的三子棋大同小异,这里也不细说了,就直接上代码了
//自定义函数
void menu()
{
printf("*****************\n");
printf("**1: play ***\n");
printf("**0: exit ***\n");
printf("*****************\n");
printf("*****************\n");
printf("*****************\n");
printf("*****************\n");
printf("select>:\n");
//接收用户选择结果
int select()
{
int i = 0;
do
{
menu();
scanf("%d", &i);
switch (i)
{
case 1:
{
game();
break;
}
case 0:
break;
default:
{
printf("选择错误\n请重新选择>:");
break;
}
}
} while (i);
}
}
游戏函数部分
【游戏分为很多部分,而每个部分都有单独的函数组成,所以这里先讲了每个函数的实现,若想要整个游戏代码按照目录调制博客结尾即可】
雷区棋盘与给用户观看棋盘
引子
我们既要实现将可选择排雷的棋盘,在用户每次排查雷前和雷后将其现状和结果打印给玩家看,又要每次判断玩家输入的坐标是否是为雷,并且将周围的雷数量统计出来反映给玩家,从这里我们知道,一个数组是不够实现我们的要求的,所以这时可以定义两个数组,来实现我们的要求
思路整理:分成两个数组,一个是专门用来给玩家看的棋盘,一个则是布置了雷的位置与数量的雷区
(上为给玩家的棋盘,下为雷区)
实现
首先就是要定义两个数组,一个为可以为show,一个为mine,方便区分,
char show[row][col]={0};
char mine[row][col]={0};
在show中我用了’#‘,作为可排的区域
在mine中用了’0‘作为安全区,
看到这大家也明白要干啥了,就是数组的初始化
init(char arr[ROWS][COLS], char p)//p为初始化的目标
{
for (int i = 0; i < ROWS; i++)
{
for (int u = 0; u < COLS; u++)
{
arr[i][u] = p;
}
}
}
这里双循环将数组的每列每行都赋值成了p,成功将数组初始化。
打印棋盘部分
打印部分也很简单,即类似于赋值部分,运用双循环将数组每个元素打印即可
print(char arr[ROWS][COLS])
{
for (int i = 1; i <= ROW; i++)
{
for (int g = 0; g <= ROW; g++)
{
printf("%d ", arr[i][g]);
}
printf("\n");
}
}
实现结果
这里虽然也是粗略的实现了棋盘的打印,但是游戏是给玩家玩的,我们也希望玩家简单开心点,所以我们可以将每行每列打印出来,这样玩家也不用费事费力的去数个数
print(char arr[ROWS][COLS])
{
int q = 0;
for (int i = 1; i <= ROW; i++)
{
if (q == 0)
{
//打印行数
for (int g = 0; g <= ROW; g++)
{
//打印列数
printf("%d ", g);
}
printf("\n");
q++;
}
printf("%d ", i);
for (int u = 1; u <= COL; u++)
{
printf("%c ", arr[i][u]);
}
printf("\n");
}
}
结果
正是这些小细节,给玩家提高的游玩感受可是上了一个台阶。
雷的生成
这里我们可以自己设定一个雷数量的全局变量,方便玩家自己更改雷的数量,也提升了游戏的可变性
#define the_count 13
而生成雷的函数大家也应该能轻松想到用rand()函数来生成随机坐标,判断坐标是否已有雷,若无,则可以将mine数组中的元素赋值成’1‘,来表示雷。
而关于rand函数如何生成随机数字,则在先前的三子棋中有过详细讲解,这里也不细讲了
通过写一个循环,将循环次数设定为等于雷的个数,即可实现多雷的布置
代码实现
char boom(char arr[ROWS][COLS])
{
int i = 1;
while (i <= the_count)
{
int u = rand() % ROW + 1;
int l = rand() % COL + 1;
if (arr[u][l] == '0')
{
arr[u][l] = '1';
i++;
}
}
}
玩家排雷
这个函数可谓是整个游戏的重中之重了
输入
首先让玩家排雷,应该就是先完成输入部分,能让玩家输入一个坐标
int x=0;
int y=0;
scanf("%d %d",&y,&x)//由于二位数组中为arr[行][列]所以需要将x,y换下位置
让玩家输入坐标后,接下来应该就是判断玩家输入的坐标是否在棋盘内,或格式是否正确
if (x > 0 && x <= ROW && y > 0 && y <= COL)
判断完成后,接下来就应该对玩家输入的坐标作出反应了。
坐标处理
还记得我们在雷区中定义了‘0’为安全区,‘1’为雷吗,正好玩家输入的坐标即为数组的各个元素对应的位置,正好将雷区对应元素进行判断,是否为雷
1:若为雷,则游戏结束,打印棋盘,让玩家死个明白
2:若不为雷,则游戏继续,将坐标周围八格的雷的数量透露给玩家,打印在棋盘上
而将周围雷的数量统计可以再新建一个函数,统计完以后,让函数返回雷的个数整形,这个时候在新建一个变量用来接受,但是既然返回的值是整形,而棋盘的类型为char,这个时候我们可以动一下我们的脑经了,既然返回的是雷的个数,而char的数字的ascii码正好是连续编码,所以将
雷的数量+‘0’即可表示char类型的雷数量
这里我们思路透彻了,若玩家输入坐标不为雷,这个时候玩家应该可以继续进行排雷,所以我们应该设置一个循环,做到让玩家继续排雷
而循环需要停止条件,想到我们可以设置一个全局变量,来判断玩家排雷的次数,而排雷的次数达到所有区域数量-雷数这样即可结束循环,并宣布玩家胜利。
整体代码
int paicha = 0;
while (paicha <= ROW*COL-the_count)
{
int x = 0;
int y = 0;
scanf("%d %d", &y, &x);
if (x > 0 && x <= ROW && y > 0 && y <= COL)
{
if (mine[x][y] == '0')
{
char p = tongji(mine, x, y) + '0';
show[x][y] = p;
paicha++;
print(show);
}
else
{
printf("你被炸死了\n");
print(mine);
break;
}
}
if (paicha == the_count + 1)
{
printf("你赢了");
}
}
统计部分
由我们先前的分析可得
这个函数作用是统计雷的个数并将雷的个数以整形方式返回
而雷区类型正好为‘0’/‘1’
我们想想,如果我们将
8个格子的ascii码值总和相加并减去8ב0’的ascii码
即为整型值的雷的个数
这里便是我们为什么要用‘0’/‘1’表示雷的原因
而在设计统计的时候我们也暴露一个问题。
当我们用此种方式统计的时候
return mine[x + 1][y - 1] +
mine[x][y - 1] +
mine[x - 1][y - 1] +
mine[x + 1][y] +
mine[x - 1][y] +
mine[x + 1][y + 1] +
mine[x][y + 1] +
mine[x - 1][y + 1] - 8 * '0';
当我们统计棋盘边缘时,那y-1不就越界访问了码
所以我们在设置
雷区和棋盘两个数组的时候我们应该多设置两行让数组统计时不会越界
这样就很好的避免了这个问题
整体代码
头文件
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define ROWS 11
#define COLS 11
#define ROW 9
#define COL 9
#define the_count 13
char tongji(char mine[ROWS][COLS], int x, int y)
{
return mine[x + 1][y - 1] +
mine[x][y - 1] +
mine[x - 1][y - 1] +
mine[x + 1][y] +
mine[x - 1][y] +
mine[x + 1][y + 1] +
mine[x][y + 1] +
mine[x - 1][y + 1] - 8 * '0';
}
char boom(char arr[ROWS][COLS])
{
int i = 1;
while (i <= the_count)
{
int u = rand() % ROW + 1;
int l = rand() % COL + 1;
if (arr[u][l] == '0')
{
arr[u][l] = '1';
i++;
}
}
}
char print(char arr[ROWS][COLS])
{
int q = 0;
for (int i = 1; i <= ROW; i++)
{
if (q == 0)
{
for (int g = 0; g <= ROW; g++)
{
printf("%d ", g);
}
printf("\n");
q++;
}
printf("%d ", i);
for (int u = 1; u <= COL; u++)
{
printf("%c ", arr[i][u]);
}
printf("\n");
}
}
char init(char arr[ROWS][COLS], char p)
{
for (int i = 0; i < ROWS; i++)
{
for (int u = 0; u < COLS; u++)
{
arr[i][u] = p;
}
}
}
int game()
{
srand((unsigned)time(NULL));
char mine[ROWS][COLS] = { 0 };
char show[ROWS][COLS] = { 0 };
init(mine, '0');
init(show, '#');
print(show);
boom(mine);
int paicha = 0;
while (paicha <= ROW*COL-the_count)
{
int x = 0;
int y = 0;
scanf("%d %d", &y, &x);
if (x > 0 && x <= ROW && y > 0 && y <= COL)
{
if (mine[x][y] == '0')
{
char p = tongji(mine, x, y) + '0';
show[x][y] = p;
paicha++;
print(show);
}
else
{
printf("你被炸死了\n");
print(mine);
break;
}
}
if (paicha == the_count + 1)
{
printf("你赢了");
}
}
return 0;
}
void menu()
{
printf("*****************\n");
printf("**1: play ***\n");
printf("**0: exit ***\n");
printf("*****************\n");
printf("*****************\n");
printf("*****************\n");
printf("*****************\n");
printf("select>:\n");
}
int select()
{
int i = 0;
do
{
menu();
scanf("%d", &i);
switch (i)
{
case 1:
{
game();
break;
}
case 0:
break;
default:
{
printf("选择错误\n请重新选择>:");
break;
}
}
} while (i);
}
源文件部分
#include"lala.h"
int main()
{
select();
return 0;
}
后记
这个只是最简易的扫雷游戏
还有很多待优化的部分
比如想win中的扫雷一次可以点出一大片区域的功能,这些都是没有实现的,如果读者感兴趣可以自己去尝试一下
最后谢谢各位的观看