扫雷游戏大家不陌生吧,最近新出了一款千人同图!百万地雷!全球同屏!《一起来扫雷》在steam商城上架。今天就突发奇想,我们是不是也能通过C语言自己写一个扫雷游戏呢,当然开始咱就不要求那么高,先做个简易版满足一下虚荣心,后续呢大家感兴趣我们再去优化它,将它美化封装成我们记忆中的扫雷发给朋友玩。
既然是手把手,那我们就要将每一步拆分开揉碎了来讲,需要看最终代码的可以直接滑到底部。
很多同学在第一步犯难了,我们要如何下手,在写代码前我们要了解扫雷的规则和扫雷长什么样
扫雷棋盘是一个9乘9的格子,地雷的数量是10个
好了,跨出我们的第一步吧,先写个菜单,没压力吧~
#include <stdio.h>
//用函数封装一个菜单
void menu()
{
printf("* * * * * * * * * *\n");
printf("* * * 1. play * * *\n");
printf("* * * 0. exit * * *\n");
printf("* * * * * * * * * *\n");
}
int main()
{
int input = 0;
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;
}
万事开头难,我们已经开了一个很好的头了,现在要写游戏主体了
我们是否能把这样的布局想象成这样的一个表格呢,地雷是1,没有地雷是0,那么如果还没布置地雷的时候系统盘全部都是0
当我们有了系统盘这个表格,我们是不是可以把他想象成一个二维数组里面存放着1和0
接下来我们是不是要考虑排雷,假设我们排的是绿点,那么绿点是不是要告诉我们他周围地雷的个数呀
那假设我们需要排这个坐标怎么办,这个坐标旁边的8个坐标有5个坐标越界了,这就不好排查了
所以为了防止在排查雷的时候有越界的可能性,我们可以选择把系统盘扩大一圈
那我们已经开始排查了,是不是需要再去建立一个盘用于展示给玩家呢,在玩家还没排查的时候展示盘全部都是“ * ”
我们将系统盘和展示盘都想象成二维数组,是不是就能开始写代码了,先建立数组嘛
void game()
{
char system[11][11];//系统盘
char show[11][11];//展示盘
}
下一步我们需要初始化我们的数组,展示盘在玩家还没排查的时候展示盘全部都是‘ * ’,系统盘在还没布置地雷的时候系统盘全部都是‘ 0 ’,我们把这一步封装一个函数吧,就叫他 Init_Board 吧
//初始化数组函数
//函数会传来系统盘或展示盘,形参就叫board吧
//函数会传来要初始化成什么,形参就叫set吧
void Init_Board(char board[11][11], int row, int col, char set)
{
int i = 0;
int j = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
board[i][j] = set;
}
}
}
//游戏主体函数
void game()
{
char system[11][11];//系统盘
char show[11][11];//展示盘
//将数组,行数,列数,还有你想初始化成什么,传给初始化函数
Init_Board(system, 11, 11,'0');//系统盘我想初始化成字符0
Init_Board(show, 11, 11,'*');//展示盘我想初始化成字符*
}
写了这老些代码,你是不是心里总有点慌慌的,担心自己会不会有哪里错了,那我们就写一个打印棋Display_Board ,看一看我们初始化后的棋盘是不是我们想要的样子。这个函数后面也能用上的
//打印棋盘函数
void Display_Board(char board[11][11],int row,int col)
{
//传过来的是我们原本的数组,但我们只需要打印中间的那9行9列,所以从下标为1开始打印
//至于周围那一圈我们不会打印的
int i = 0;
int j = 0;
for (i = 1; i <= row; i++)
{
for (j = 1; j <= col; j++)
{
printf("%c ", board[i][j]);
}
printf("\n");
}
}
//游戏主体函数
void game()
{
char system[11][11];
char show[11][11];
Init_Board(system, 11, 11,'0');
Init_Board(show, 11, 11,'*');
//这里我们只需要打印9行9列,我们没必要把11行11列的棋盘展示给玩家看对吧
Display_Board(system, 9, 9);
Display_Board(show, 9, 9);
}
然后我们再把x轴和y轴打印出来,方便玩家玩游戏的时候定位,大家可以根据自己的格式美化自己的棋盘
//打印棋盘函数
void Display_Board(char board[11][11],int row,int col)
{
int i = 0;
int j = 0;
printf("--------扫雷--------\n ");//美化
for(i = 1; i <= col; i++)//把y坐标轴打印出来
{
printf("%d ", i);
}
printf("\n\n");//美化
for (i = 1; i <= row; i++)
{
printf("%d ", i);//把x坐标轴打印出来
for (j = 1; j <= col; j++)
{
printf("%c ", board[i][j]);
}
printf("\n");
}
printf("--------扫雷--------\n");//美化
}
现在我们就可以好好欣赏我们的棋盘了,当然现在只是看看,检测一下代码是否能正常运行,现在还没轮到将棋盘展示给玩家,我们需要先布置我们的地雷。
那么思考一个问题,我们需要把地雷布置在哪个盘上呢?系统盘还是展示盘?毋庸置疑要布置在系统盘
#include <stdio.h>
//需要注意传过来的数组还是11行11列
void Set_Mine(char board[11][11], int row, int col)
{
int count = 10;//我们一共需要布置10颗地雷
//每布置一颗地雷需要布置的地雷就减一,所需布置的地雷为0则不进入循环
while (count)
{
//假设成功布置地雷
count--;
}
}
//游戏主体函数
void game()
{
char system[11][11];
char show[11][11];
Init_Board(system, 11, 11,'0');
Init_Board(show, 11, 11,'*');
//我们要把地雷布置在系统盘所以要把系统盘这个数组传过去吧
//并且我们不需要在9行9列的范围外布置地雷
Set_Mine(system,9,9);
}
那布置地雷具体要怎么实现呢,还记得猜数字游戏嘛,我们地雷的位置肯定要随机的才有趣,这时候就用到我们的rand函数了。
int x = rand();
int y = rand();
好了现在x下标轴和y下标轴都生成了随机数,但还没有限定rand生成随机数的范围,而我们需要的下标范围是1-9.
int x = rand()%row+1;
int y = rand()%col+1;
但需要注意的是有没有可能随机数刚刚好好随机到相同的坐标,在同一个地方埋了两颗雷,也可能吧,这样我们棋盘上地雷的总数不就少了一颗嘛。所以我们就要判断如果这里没有雷我才能布置地雷,我们在系统盘上地雷是用字符‘1’来表示的,所以雷只能布置在不是‘1’的地方。
void Set_Mine(char board[11][11], int row, int col)
{
int count = 10;
while (count)
{
int x = rand() % row + 1;
int y = rand() % col + 1;
if (board[x][y] != '1')//判断随机位置是不是已经有雷了
{
board[x][y] = '1';
count--;
}
}
}
那调用了rand是不是还需要调用srand,和猜数字游戏一样的,srand只用调用一次所以我们放在主函数,注意包含头文件和强制类型转换成unsigned int类型
#include <stdlib.h>
#include <time.h>
int main()
{
srand((unsigned int)time(NULL));
}
做到这一步我们的雷也布置好了,离成功越来越近了,我们可以利用我们自己写的Display_Board来看一下代码是否在系统盘正确布置好地雷,没有问题刚好是10颗地雷。
接下来就到了玩家排查雷了 ,当玩家选择了一个坐标是不是有两种可能性1. 踩雷 2.没踩雷
踩雷:是不是直接就game over了,可以返回菜单了
没踩雷:我们是不是要在展示盘上告诉玩家这个位置周围有多少颗地雷呢
有了以上逻辑我们就可以封装一个排查地雷的函数,就叫Find_Mine吧
Find_Mine(char system[11][11],char show[11][11],int row,int col)
{
int x = 0;
int y = 0;
while(1)
{
printf("请输入要排查的坐标");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)//限定玩家的输入范围
{
if (system[x][y] == '1')//如果排查的位置刚好是雷
{
printf("很遗憾,此处是雷,游戏失败");
//给玩家看看所有雷的位置
Display_Board(system, 9, 9);
break;
}
else//如果排查的位置是安全的
{
Get_Mine_Count();//再封装一个函数用于计算排查位置周围一圈有多少地雷
}
}
else
{
printf("输入坐标有误\n");
}
}
}
接下来我们怎么统计排查坐标周围一圈地雷的个数
有没有突然豁然开朗,那就写代码吧~噢,还要注意我们系统盘内的0和1是字符
那当我们把周围一圈所有坐标都减去一个字符‘0’,再把他们都加起来是不是就能得到希望的答案了
int Get_Mine_Count(char system[11][11],int x,int y)
{
return system[x - 1][y] + system[x - 1][y - 1] + system[x][y - 1] + system[x + 1][y - 1] + system[x + 1][y] + system[x + 1][y + 1] + system[x][y + 1] + system[x - 1][y + 1] - 8 * '0';
}
计算出了周围有几个雷我们是不是就要把这个数字放到我们的展示盘给玩家看。
void Find_Mine(char system[11][11],char show[11][11],int row,int col)
{
int x = 0;
int y = 0;
while(1)
{
printf("请输入要排查的坐标");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (system[x][y] == '1')
{
printf("很遗憾,此处是雷,游戏失败");
Display_Board(system, 9, 9);
break;
}
else
{
int num = Get_Mine_Count(system, x, y);
show[x][y] = num + '0';//刚刚减去了一个字符0,现在是不是要加上一个字符0呀
//打印展示盘给玩家看
Display_Board(show, 9, 9);
}
}
else
{
printf("输入坐标有误\n");
}
}
}
现在我们其实就可以玩儿了,但我们只设定了失败条件,却没有设定成功条件。我们的目标是找到所有的安全地区对吧,那一共是九乘九的棋盘,只布置了10颗雷,81-10=71,是不是我们只要找到了71块安全区我们就获胜了。
void Find_Mine(char system[11][11],char show[11][11],int row,int col)
{
int x = 0;
int y = 0;
int win = 0;//建立一个胜利条件的变量
while(win<71)//win到达71获得胜利
{
printf("请输入要排查的坐标");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (system[x][y] == '1')
{
printf("很遗憾,此处是雷,游戏失败");
Display_Board(system, 9, 9);
break;
}
else
{
win++;//每一个安全,胜利条件变量加一
printf("还需要排查%d个安全坐标\n",71-win);//提示一下玩家
int num = Get_Mine_Count(system, x, y);
show[x][y] = num + '0';
Display_Board(show, 9, 9);
}
}
else
{
printf("输入坐标有误\n");
}
}
if (win == 71)
{
printf("恭喜你,游戏成功!\n");
Display_Board(system, 9, 9);//把雷的位置打印给玩家
}
}
终于我们的代码写完了,简易版的扫雷也做出来了!!!
下面是完整版的代码:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
//封装菜单函数
void menu()
{
printf("* * * * * * * * * *\n");
printf("* * * 1. play * * *\n");
printf("* * * 0. exit * * *\n");
printf("* * * * * * * * * *\n");
}
//棋盘初始化函数
void Init_Board(char board[11][11], int row, int col, char set)
{
int i = 0;
int j = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
board[i][j] = set;
}
}
}
//布置地雷函数
void Set_Mine(char board[11][11], int row, int col)
{
int count = 10;
while (count)
{
int x = rand() % row + 1;
int y = rand() % col + 1;
if (board[x][y] != '1')
{
board[x][y] = '1';
count--;
}
}
}
//打印棋盘函数
void Display_Board(char board[11][11], int row, int col)
{
int i = 0;
int j = 0;
printf("--------扫雷--------\n ");
for (i = 1; i <= col; i++)
{
printf("%d ", i);
}
printf("\n\n");
for (i = 1; i <= row; i++)
{
printf("%d ", i);
for (j = 1; j <= col; j++)
{
printf("%c ", board[i][j]);
}
printf("\n");
}
printf("--------扫雷--------\n");
}
//统计周围地雷数函数
int Get_Mine_Count(char system[11][11],int x,int y)
{
return system[x - 1][y] + system[x - 1][y - 1] + system[x][y - 1] + system[x + 1][ y - 1] + system[x + 1][y] + system[x + 1][y + 1] + system[x][y + 1] + system[x - 1][y + 1] - 8 * '0';
}
//排查地雷函数
void Find_Mine(char system[11][11],char show[11][11],int row,int col)
{
int x = 0;
int y = 0;
int win = 0;
while(win<71)
{
printf("请输入要排查的坐标");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (system[x][y] == '1')
{
printf("很遗憾,此处是雷,游戏失败");
Display_Board(system, 9, 9);
break;
}
else
{
win++;
printf("还需要排查%d个安全坐标\n",71-win);
int num = Get_Mine_Count(system, x, y);
show[x][y] = num + '0';
Display_Board(show, 9, 9);
}
}
else
{
printf("输入坐标有误\n");
}
}
if (win == 71)
{
printf("恭喜你,游戏成功!\n");
Display_Board(system, 9, 9);
}
}
//游戏主体函数
void game()
{
char system[11][11];
char show[11][11];
Init_Board(system, 11, 11,'0');
Init_Board(show, 11, 11,'*');
Set_Mine(system,9,9);
Display_Board(system, 9, 9);
Display_Board(show, 9, 9);
Find_Mine(system, show, 9, 9);
}
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;
}
当然游戏做成这么一大片肯定是不行的,我们最好是新建一个头文件(game.h)和一个源文件(game.c)
game.h:
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <windows.h>
#define Easy_Mode 10 //简单模式的地雷数量
#define Easy_Row 9
#define Easy_Col 9
//棋盘初始化函数
void Init_Board(char board[Easy_Row + 2][Easy_Col + 2], int row, int col, char set);
//打印棋盘函数
void Display_Board(char board[Easy_Row + 2][Easy_Col + 2], int row, int col);
//布置地雷函数
void Set_Mine(char board[Easy_Row + 2][Easy_Col + 2], int row, int col);
//排查地雷函数
void Find_Mine(char system[Easy_Row + 2][Easy_Col + 2], char show[Easy_Row + 2][Easy_Col + 2], int row, int col);
main.c:
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
//封装菜单函数
void menu()
{
printf("* * * * * * * * * *\n");
printf("* * * 1. play * * *\n");
printf("* * * 0. exit * * *\n");
printf("* * * * * * * * * *\n");
}
//游戏主体函数
void game()
{
char system[Easy_Row+2][Easy_Col+2];
char show[Easy_Row+2][Easy_Col+2];
Init_Board(system, Easy_Row + 2, Easy_Col+2,'0');
Init_Board(show, Easy_Row + 2, Easy_Col+2,'*');
Set_Mine(system, Easy_Row, Easy_Col);
Display_Board(system, Easy_Row, Easy_Col);
Display_Board(show, Easy_Row, Easy_Col);
Find_Mine(system, show, Easy_Row, Easy_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;
}
game.c:
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
//棋盘初始化函数
void Init_Board(char board[Easy_Row + 2][Easy_Col + 2], int row, int col, char set)
{
int i = 0;
int j = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
board[i][j] = set;
}
}
}
//打印棋盘函数
void Display_Board(char board[Easy_Row + 2][Easy_Col + 2], int row, int col)
{
int i = 0;
int j = 0;
printf("--------扫雷--------\n ");
for (i = 1; i <= col; i++)
{
printf("%d ", i);
}
printf("\n\n");
for (i = 1; i <= row; i++)
{
printf("%d ", i);
for (j = 1; j <= col; j++)
{
printf("%c ", board[i][j]);
}
printf("\n");
}
printf("--------扫雷--------\n");
}
//布置地雷函数
void Set_Mine(char board[Easy_Row + 2][Easy_Col + 2], int row, int col)
{
int count = Easy_Mode;//地雷数设置为简单模式
while (count)
{
int x = rand() % row + 1;
int y = rand() % col + 1;
if (board[x][y] != '1')
{
board[x][y] = '1';
count--;
}
}
}
//统计周围地雷数函数
int Get_Mine_Count(char system[Easy_Row + 2][Easy_Col + 2], int x, int y)
{
return system[x - 1][y] + system[x - 1][y - 1] + system[x][y - 1] + system[x + 1][y - 1] + system[x + 1][y] + system[x + 1][y + 1] + system[x][y + 1] + system[x - 1][y + 1] - 8 * '0';
}
//排查地雷函数
void Find_Mine(char system[Easy_Row + 2][Easy_Col + 2], char show[Easy_Row + 2][Easy_Col + 2], int row, int col)
{
int x = 0;
int y = 0;
int win = 0;
while (win < Easy_Row * Easy_Col - Easy_Mode)//这边用常量计算出来
{
printf("请输入要排查的坐标");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (system[x][y] == '1')
{
printf("\n很遗憾,此处是雷,游戏失败\n");
Display_Board(system, 9, 9);
printf("即将返回主菜单");
Sleep(5000);//让游戏失败后,玩家冷静一下
break;
}
else
{
win++;
printf("\n还需要排查%d个安全坐标\n", Easy_Row * Easy_Col - Easy_Mode - win);
int num = Get_Mine_Count(system, x, y);
show[x][y] = num + '0';
Display_Board(show, 9, 9);
}
}
else
{
printf("输入坐标有误\n");
}
}
if (win == Easy_Row * Easy_Col - Easy_Mode)
{
printf("恭喜你,游戏成功!\n");
Display_Board(system, 9, 9);
}
}
完美,是不是也没那么难,只是把基础的C语言技巧结合,遇到困难主要还是思路堵塞,不过跟着Mr.狠人过了一遍是不是就轻车熟路了。求赞三连转发分享,“扫雷游戏进阶版”马上带给大家。