🪐🪐🪐欢迎来到程序员餐厅💫💫💫
今日主菜:扫雷小游戏,
主厨:邪王真眼
所属专栏: C语言知识点
主厨的主页:Chef‘s blog
引言:
已经学会数组的朋友注意了,现在的你已经有能力写出两个小游戏了,一个是扫雷,一个是三子棋,上次咱们搞定了三子棋代码,今天这扫雷又送上了门。
本章你可能会用到的知识:
- 随机数的生成:C语言实现随机数
- 数组的使用
- 函数的创建与调用
- 递归
1. 扫雷游戏要求
- 游戏可以通过菜单实现继续玩或者退出游戏
- 默认棋盘为9×9的格子,但可以修改
- 默认随机布置雷,默认雷的个数为9,但可以修改
- 关于排查雷
(1) 如果位置不是雷,就显⽰周围有⼏个雷,并且循环展开。
(2)如果位置是雷,就炸死游戏结束
(3)把所有⾮雷都找出来,排雷成功,游戏结束(4)玩家可以对认为是雷的位置进行可能是雷或肯定是雷的标记。
2 游戏的分析和设计
那如果这个位置布置雷,我们就存放1,没有布置雷就存放0.
排查(8,6)这个坐标时,我们访问周围的⼀圈8个⻩⾊位置,最下⾯的三 个坐标就会越界,因此我们在设计的时候,给数组扩大⼀圈,雷还是布置在中间的9*9的坐标上,周围⼀圈不去布置雷就行,这样就解决了越界的问题。所以我们将存放数据的数组创建成11*11 是⽐较合适。
- 问题:假设我们排查了某 ⼀个位置后,这个坐标处不是雷,这个坐标的周围有1个雷,那我们需要将排查出的雷的数量信息记录下来,那这个雷的个数信息存放在哪⾥呢?如果存放在布 置雷的数组中,这样雷的信息和雷的个数信息就可能或产⽣混淆和打印上的困难。
- 解决:这⾥我们肯定有办法解决,⽐如:雷和⾮雷的信息不要使⽤数字,使⽤某些字符就行,但是这样做棋盘上有雷和非雷的信息,还有排查出的雷的个数信息,就⽐较混杂,不够⽅便。我们采⽤另外⼀种⽅案,我们专门给⼀个棋盘(对应⼀个数组mine)存放布置好的雷的信息,再 给另外⼀个棋盘(对应另外⼀个数组show)存放排查出的雷的信息。这样就互不⼲扰了,
- 补充:同时为了保持神秘,show开始时初始化为字符 '*',mine数组最开始也初始化为字符'0',布置雷改成'1'。如下如:
3.多文件操作
为了方便代码的管理和保证游戏实现逻辑的清晰性,函数的声明与定义是分开写的,所以我们将采用多文件管理的模式。
(1)创建头文件game.h,包含所有头文件(其他源文件只需引用它即可),以及所有游戏功能的函数接口。
(2)创建源文件game.c,负责所有游戏功能对应函数的具体代码实现。
(3)创建源文件main.c,负责调用函数实现来游戏。
4. 简易菜单的实现
4.1功能介绍
1.玩家可以通过选择1进入游戏,0退出游戏。
2.选错的话提醒玩家,重新选择。
3.告诉玩家游戏规则
4.2功能实现
void menu(void)
{
printf("***************************\n");
printf("*** 1 开始游戏 ********\n");
printf("*** 0 结束游戏 ********\n");
printf("***************************\n");
}
void rules()
{
printf("游戏规则如下:\n");
printf("1.你可以通过排雷和标记逐渐逼近胜利条件\n");
printf("2.对同一坐标标记1次是!,标记2次是?,第3次恢复*\n");
printf("3.标记!代表肯定是雷,标记?代表可能是雷\n");
printf("4.标记!的次数最多为雷的个数\n");
printf("5.如果选中雷,则游戏失败\n");
printf("6.当成功标记所有雷,或者排查完所有格子,游戏胜利\n");
}
int main()
{
srand((unsigned int)time(NULL));
rules();
do
{
menu();
again:
printf("请选择->");
int input;
scanf("%d", &input);
gets(brr);
switch (input)
{
case 1:
printf("----------------扫雷游戏----------------\n");
game();
break;
case 0:
printf("结束游戏\n");
return 0;
default:
printf(" 选择错误,重新选择\n");
goto again;
}
} while (1);
return 0;
}
4.3功能展示
5. 游戏功能实现
5.1 预定义信息
(1)要求
- 1.包含需要的头文件
- 2.包含需要的宏定义
- 3.包含需要的函数接口
(2)代码实现
#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
#define L 9
#define W 9
#define LS L+2
#define WS L+2
#define NUM 9
void intiboard(char arr[LS][WS], int ls, int ws, char a);
void printboard(char arr[LS][WS], int l, int w);
void setmine(char arr[LS][WS], int l, int w,int a,int b);
void mark(char arr[LS][WS], int l, int w);
int conuts(char arr1[LS][WS], int x, int y);
void judge(char arr1[LS][WS], char arr2[LS][WS], int l, int w, int num,int x,int y);
int iswin(char arr1[LS][WS], char arr2[LS][WS], int l, int w);
5.2 初始化棋盘
(1)要求
1. mine棋盘初始化为全‘0’。
2. show棋盘初始化为全‘*’。
(2)实现
-
void intiboard(char arr[LS][WS], int ls, int ws, char a) { for (int i = 0; i < ls; i++) { for (int j = 0; j < ws; j++) { arr[i][j] = a; } } }
5.3 打印棋盘
(1)要求
1. 打印出棋盘中的元素。
2. 利用---,|模拟出棋盘框。
3. 显示出每行,每列的序号。
(2)实现
void printboard(char arr[LS][WS], int l, int w) { printf("----------------扫雷ing----------------\n"); for (int i = 0; i <= l; i++) printf(" %d ", i); printf("\n"); printf(" |---|---|---|---|---|---|---|---|---|"); printf("\n"); for (int i = 1; i <= l; i++) { printf(" %d |", i); for (int j = 1; j <= w; j++) { printf(" %c |", arr[j][i]); }printf("\n"); printf(" |---|---|---|---|---|---|---|---|---|"); printf("\n"); } }
(3)效果展示
5.4 埋雷
(1)要求
1.在mine棋盘中随机布置9个雷,雷为‘0’。
2.重复位置不能布置。
3.在第一次输入坐标后在埋雷,防止第一次直接炸死(这个实现不在这个函数里)
(2)实现
void setmine(char arr[LS][WS], int l, int w)
{
int num = NUM;
while (num)
{
int x, y;
a: x = rand() % l + 1;
y = rand() % l + 1;
if (arr[x][y] == '0')
{
arr[x][y] = '1';
num--;
}
else
goto a;
}
}
效果展示
5.5 排雷和标记
(1)要求·:
- 每次玩家都可以先进行排雷和标记。
- 玩家输入的值不合法时提醒玩家重新输入
- 对于同一个坐标,第一次标记为!代表肯定是雷;第二次标记为?代表可能是雷;第三次 标记恢复‘*’。
- 排雷时,玩家输入要排除的坐标,如果是雷,则游戏结束;如果不是雷,则统计周围雷的个数。
- 如果周围没有雷,则向周围展开至有雷为止。
(2)递归排雷的实现
}
int conuts(char arr1[LS][WS], int x, int y)
{
int count = 0;
for (int i = x - 1; i <= x + 1; i++)
{
for (int j = y - 1; j <= y + 1; j++)
{
if (arr1[i][j] == '1')
count++;
}
}
return count;
}
void judge(char arr1[LS][WS], char arr2[LS][WS], int l, int w, int num, int x, int y)
{
if (x > L || y > L || x < 1 || y < 1)
{
return;
}
if (arr1[x][y] == '1')
return;
int count = 0;//这个格子一圈的雷,你找到的非雷
if (flag == 0)
{
arr2[x][y] = ' ';
flag++;
setmine(arr1, L, W, x, y);
count = conuts(arr1, x, y);
if (!count)
{
arr2[x][y] = ' ';
if (arr2[x - 1][y] != ' ' && (arr2[x][y] - '0' > 9 || arr2[x][y] - '0' < 1))
judge(arr1, arr2, l, w, num, x - 1, y);
if (arr2[x - 1][y - 1] != ' ' && (arr2[x][y] - '0' > 9 || arr2[x][y] - '0' < 1))
judge(arr1, arr2, l, w, num, x - 1, y - 1);
if (arr2[x - 1][y + 1] != ' ' && (arr2[x][y] - '0' > 9 || arr2[x][y] - '0' < 1))
judge(arr1, arr2, l, w, num, x - 1, y + 1);
if (arr2[x][y - 1] != ' ' && (arr2[x][y] - '0' > 9 || arr2[x][y] - '0' < 1))
judge(arr1, arr2, l, w, num, x, y - 1);
if (arr2[x][y + 1] != ' ' && (arr2[x][y] - '0' > 9 || arr2[x][y] - '0' < 1))
judge(arr1, arr2, l, w, num, x, y + 1);
if (arr2[x + 1][y - 1] != ' ' && (arr2[x][y] - '0' > 9 || arr2[x][y] - '0' < 1))
judge(arr1, arr2, l, w, num, x + 1, y - 1);
if (arr2[x + 1][y] != ' ' && (arr2[x][y] - '0' > 9 || arr2[x][y] - '0' < 1))
judge(arr1, arr2, l, w, num, x + 1, y);
if (arr2[x + 1][y + 1] != ' ' && (arr2[x][y] - '0' > 9 || arr2[x][y] - '0' < 1))
judge(arr1, arr2, l, w, num, x + 1, y + 1);
}
else
arr2[x][y] = count + '0';
z++;
}
else
{
count = conuts(arr1, x, y);
if (!count)
{
arr2[x][y] = ' ';
if (arr2[x - 1][y] != ' ' &&(arr2[x][y] - '0' > 9 || arr2[x][y] - '0' < 1))
judge(arr1, arr2, l, w, num, x - 1, y);
if (arr2[x - 1][y - 1] != ' ' && (arr2[x][y] - '0' > 9 || arr2[x][y] - '0' < 1))
judge(arr1, arr2, l, w, num, x - 1, y - 1);
if (arr2[x - 1][y + 1] != ' ' && (arr2[x][y] - '0' > 9 || arr2[x][y] - '0' < 1))
judge(arr1, arr2, l, w, num, x - 1, y + 1);
if (arr2[x][y - 1] != ' ' && (arr2[x][y] - '0' > 9 || arr2[x][y] - '0' < 1))
judge(arr1, arr2, l, w, num, x, y - 1);
if (arr2[x][y + 1] != ' ' &&( arr2[x][y] - '0' > 9 || arr2[x][y] - '0' < 1))
judge(arr1, arr2, l, w, num, x, y + 1);
if (arr2[x + 1][y - 1] != ' ' &&( arr2[x][y] - '0' > 9 || arr2[x][y] - '0' < 1))
judge(arr1, arr2, l, w, num, x + 1, y - 1);
if (arr2[x + 1][y] != ' ' && (arr2[x][y] - '0' > 9 || arr2[x][y] - '0' < 1))
judge(arr1, arr2, l, w, num, x + 1, y);
if (arr2[x + 1][y + 1] != ' ' && (arr2[x][y] - '0' > 9 || arr2[x][y] - '0' < 1))
judge(arr1, arr2, l, w, num, x + 1, y + 1);
}
else
arr2[x][y] = count + '0';
z++;
}
}
(3)标记的实现
void mark(char arr[LS][WS], int l, int w)
{
printf("标记雷输入1,不标记输入0\n");
int a = 0;
scanf("%d", &a);
gets(brr);
if (a)
{
printf("请输入你要标记的坐标->\n");
int x, y;
again: scanf("%d %d", &x, &y);
gets(brr);
if (x > l || y > l || x < 1 || y < 1)
{
printf("输入错误,请重新输入\n");
goto again;
}
else if (arr[x][y] == '*')
arr[x][y] = '!';
else if (arr[x][y] == '!')
arr[x][y] = '?';
else if (arr[x][y] == '?')
arr[x][y] = '*';
}
}
5.6 胜利条件
(1)要求
成功排查完除雷外所有格子,游戏胜利。
(2)实现
int iswin(char arr1[LS][WS], char arr2[LS][WS], int l, int w)
{
int num = 0;
for(int i=1;i<=l;i++)
for (int j = 1; j <= w; j++)
{
if (arr2[i][j] == ' ' && arr1[i][j] == '1')
return -1;
if (arr2[i][j]-'0'<=9&&arr2[i][j]-'0'>=0||arr2[i][j]==' ')
num++;
}
return num;
}
6. 游戏实现
6.1函数调用分析顺序
1. 初始化棋盘。
2. 先让玩家输入一个排雷坐标再埋雷,防止第一次就被炸死。
3. 玩家选择标记还是埋雷。
4. 判断玩家是否被炸死,判断玩家是否胜利。
5.如果步骤4未成功执行,就重复执行步骤3,4。
6.2 实现
void game()
{
char arr1[LS][WS] = { 0 };
char arr2[LS][WS] = { 0 };
intiboard(arr2, LS, WS, '*');
intiboard(arr1, LS, WS, '0');
printboard(arr2, L, W);
int flag0 = 1;
do
{
int x, y;
printf("请输入坐标->");
again: scanf("%d %d", &x, &y);
gets(brr);
if (x > L || y > L || x < 1 || y < 1)
{
printf("输入坐标不再棋盘内,请重新输入\n");
goto again;
}
else if (arr2[x][y] == ' ' || arr2[x][y] - '0' <= 9 && arr2[x][y] - '0' >= 1)
{
printf("此位置已被排雷,请重新输入\n");
goto again;
}
judge(arr1, arr2, L, W, NUM, x, y);
int flag0=iswin( arr1, arr2, L, W);
if (flag0 == -1)
{
printf("你被炸死了,游戏结束");
break;
}
if (L * L - flag0 == NUM)
{
printf("恭喜你,顺利通关\n");
break;
}
mark( arr2, L, W);
printboard(arr2, L, W);
} while (1);
}
7. 源码
(1)game.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"源.h"
char brr[100];
void menu(void)
{
printf("***************************\n");
printf("*** 1 开始游戏 ********\n");
printf("*** 0 结束游戏 ********\n");
printf("***************************\n");
}
void rules()
{
printf("游戏规则如下:\n");
printf("1.你可以通过排雷和标记逐渐逼近胜利条件\n");
printf("2.对同一坐标标记1次是!,标记2次是?,第3次恢复*\n");
printf("3.标记!代表肯定是雷,标记?代表可能是雷\n");
printf("4.标记!的次数最多为雷的个数\n");
printf("5.如果选中雷,则游戏失败\n");
printf("6.当成功标记所有雷,或者排查完所有格子,游戏胜利\n");
}
void game()
{
char arr1[LS][WS] = { 0 };
char arr2[LS][WS] = { 0 };
intiboard(arr2, LS, WS, '*');
intiboard(arr1, LS, WS, '0');
printboard(arr2, L, W);
int flag0 = 1;
do
{
int x, y;
printf("请输入坐标->");
again: scanf("%d %d", &x, &y);
gets(brr);
if (x > L || y > L || x < 1 || y < 1)
{
printf("输入坐标不再棋盘内,请重新输入\n");
goto again;
}
else if (arr2[x][y] == ' ' || arr2[x][y] - '0' <= 9 && arr2[x][y] - '0' >= 1)
{
printf("此位置已被排雷,请重新输入\n");
goto again;
}
judge(arr1, arr2, L, W, NUM, x, y);
int flag0=iswin( arr1, arr2, L, W);
if (flag0 == -1)
{
printf("你被炸死了,游戏结束");
break;
}
if (L * L - flag0 == NUM)
{
printf("恭喜你,顺利通关\n");
break;
}
mark( arr2, L, W);
printboard(arr2, L, W);
} while (1);
}
int main()
{
srand((unsigned int)time(NULL));
rules();
do
{
menu();
again:
printf("请选择->");
int input;
scanf("%d", &input);
gets(brr);
switch (input)
{
case 1:
printf("----------------扫雷游戏----------------\n");
game();
break;
case 0:
printf("结束游戏\n");
return 0;
default:
printf(" 选择错误,重新选择\n");
goto again;
}
} while (1);
return 0;
}
(2)源.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"源.h"
int flag = 0, z = 0;
char brr[100];
void intiboard(char arr[LS][WS], int ls, int ws, char a)
{
for (int i = 0; i < ls; i++)
{
for (int j = 0; j < ws; j++)
{
arr[i][j] = a;
}
}
}
void printboard(char arr[LS][WS], int l, int w)
{
printf("----------------扫雷ing----------------\n");
for (int i = 0; i <= l; i++)
printf(" %d ", i);
printf("\n");
printf(" |---|---|---|---|---|---|---|---|---|");
printf("\n");
for (int i = 1; i <= l; i++)
{
printf(" %d |", i);
for (int j = 1; j <= w; j++)
{
printf(" %c |", arr[j][i]);
}printf("\n");
printf(" |---|---|---|---|---|---|---|---|---|");
printf("\n");
}
}
void setmine(char arr[LS][WS], int l, int w,int a,int b)
{
int num = NUM;
while (num)
{
int x, y;
a: x = rand() % l + 1;
y = rand() % l + 1;
if (arr[x][y] == '0'&&(a!=x||b!=y))
{
arr[x][y] = '1';
num--;
}
else
goto a;
}
}
int conuts(char arr1[LS][WS], int x, int y)
{
int count = 0;
for (int i = x - 1; i <= x + 1; i++)
{
for (int j = y - 1; j <= y + 1; j++)
{
if (arr1[i][j] == '1')
count++;
}
}
return count;
}
void judge(char arr1[LS][WS], char arr2[LS][WS], int l, int w, int num, int x, int y)
{
if (x > L || y > L || x < 1 || y < 1)
{
return;
}
if (arr1[x][y] == '1')
return;
int count = 0;//这个格子一圈的雷,你找到的非雷
if (flag == 0)
{
arr2[x][y] = ' ';
flag++;
setmine(arr1, L, W, x, y);
//printboard(arr1, 9, 9);
count = conuts(arr1, x, y);
if (!count)
{
arr2[x][y] = ' ';
if (arr2[x - 1][y] != ' ' && (arr2[x][y] - '0' > 9 || arr2[x][y] - '0' < 1))
judge(arr1, arr2, l, w, num, x - 1, y);
if (arr2[x - 1][y - 1] != ' ' && (arr2[x][y] - '0' > 9 || arr2[x][y] - '0' < 1))
judge(arr1, arr2, l, w, num, x - 1, y - 1);
if (arr2[x - 1][y + 1] != ' ' && (arr2[x][y] - '0' > 9 || arr2[x][y] - '0' < 1))
judge(arr1, arr2, l, w, num, x - 1, y + 1);
if (arr2[x][y - 1] != ' ' && (arr2[x][y] - '0' > 9 || arr2[x][y] - '0' < 1))
judge(arr1, arr2, l, w, num, x, y - 1);
if (arr2[x][y + 1] != ' ' && (arr2[x][y] - '0' > 9 || arr2[x][y] - '0' < 1))
judge(arr1, arr2, l, w, num, x, y + 1);
if (arr2[x + 1][y - 1] != ' ' && (arr2[x][y] - '0' > 9 || arr2[x][y] - '0' < 1))
judge(arr1, arr2, l, w, num, x + 1, y - 1);
if (arr2[x + 1][y] != ' ' && (arr2[x][y] - '0' > 9 || arr2[x][y] - '0' < 1))
judge(arr1, arr2, l, w, num, x + 1, y);
if (arr2[x + 1][y + 1] != ' ' && (arr2[x][y] - '0' > 9 || arr2[x][y] - '0' < 1))
judge(arr1, arr2, l, w, num, x + 1, y + 1);
}
else
arr2[x][y] = count + '0';
z++;
}
else
{
count = conuts(arr1, x, y);
if (!count)
{
arr2[x][y] = ' ';
if (arr2[x - 1][y] != ' ' &&(arr2[x][y] - '0' > 9 || arr2[x][y] - '0' < 1))
judge(arr1, arr2, l, w, num, x - 1, y);
if (arr2[x - 1][y - 1] != ' ' && (arr2[x][y] - '0' > 9 || arr2[x][y] - '0' < 1))
judge(arr1, arr2, l, w, num, x - 1, y - 1);
if (arr2[x - 1][y + 1] != ' ' && (arr2[x][y] - '0' > 9 || arr2[x][y] - '0' < 1))
judge(arr1, arr2, l, w, num, x - 1, y + 1);
if (arr2[x][y - 1] != ' ' && (arr2[x][y] - '0' > 9 || arr2[x][y] - '0' < 1))
judge(arr1, arr2, l, w, num, x, y - 1);
if (arr2[x][y + 1] != ' ' &&( arr2[x][y] - '0' > 9 || arr2[x][y] - '0' < 1))
judge(arr1, arr2, l, w, num, x, y + 1);
if (arr2[x + 1][y - 1] != ' ' &&( arr2[x][y] - '0' > 9 || arr2[x][y] - '0' < 1))
judge(arr1, arr2, l, w, num, x + 1, y - 1);
if (arr2[x + 1][y] != ' ' && (arr2[x][y] - '0' > 9 || arr2[x][y] - '0' < 1))
judge(arr1, arr2, l, w, num, x + 1, y);
if (arr2[x + 1][y + 1] != ' ' && (arr2[x][y] - '0' > 9 || arr2[x][y] - '0' < 1))
judge(arr1, arr2, l, w, num, x + 1, y + 1);
}
else
arr2[x][y] = count + '0';
z++;
}
}
void mark(char arr[LS][WS], int l, int w)
{
printf("标记雷输入1,不标记输入0\n");
int a = 0;
scanf("%d", &a);
gets(brr);
if (a)
{
printf("请输入你要标记的坐标->\n");
int x, y;
again: scanf("%d %d", &x, &y);
gets(brr);
if (x > l || y > l || x < 1 || y < 1)
{
printf("输入错误,请重新输入\n");
goto again;
}
else if (arr[x][y] == '*')
arr[x][y] = '!';
else if (arr[x][y] == '!')
arr[x][y] = '?';
else if (arr[x][y] == '?')
arr[x][y] = '*';
}
}
int iswin(char arr1[LS][WS], char arr2[LS][WS], int l, int w)
{
int num = 0;
for(int i=1;i<=l;i++)
for (int j = 1; j <= w; j++)
{
if (arr2[i][j] == ' ' && arr1[i][j] == '1')
return -1;
if (arr2[i][j]-'0'<=9&&arr2[i][j]-'0'>=0||arr2[i][j]==' ')
num++;
}
return num;
}
(3)源.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
#define L 9
#define W 9
#define LS L+2
#define WS L+2
#define NUM 9
void intiboard(char arr[LS][WS], int ls, int ws, char a);
void printboard(char arr[LS][WS], int l, int w);
void setmine(char arr[LS][WS], int l, int w,int a,int b);
void mark(char arr[LS][WS], int l, int w);
int conuts(char arr1[LS][WS], int x, int y);
void judge(char arr1[LS][WS], char arr2[LS][WS], int l, int w, int num,int x,int y);
int iswin(char arr1[LS][WS], char arr2[LS][WS], int l, int w);
结语:
恭喜你,看完了这~么长的文章,熟能生巧,看完记得练习哦,
ps:本章部分图片来源《比特就业课课件》。