【C语言】扫雷小游戏详解
前言: 还记得大明湖畔的夏雨荷,电脑课上的扫雷吗?
———————————————————————————是 他 吗———————————————————————————
没错,就是他。你电脑课上偷偷玩过的扫雷,今天我们用C语言做一个扫雷小游戏。
《扫雷》是一款大众类的益智小游戏,于1992年发行。游戏目标是在最短的时间内根据点击格子出现的数字找出所有非雷格子,同时避免踩雷,踩到一个雷即全盘皆输。
接下来让我们进入代码的世界:
目录:
一、想法与构思
1.想法
首先想要做一个游戏就先明白游戏的规则和玩法,扫雷,就是在一个n*n的棋盘上操作,玩家通过点击格子出现的数字找出所有非雷格子,避免踩雷,当所以非雷的格子都被找出,那么玩家获胜,一旦踩到任意一颗雷,则玩家失败。
没错,这就是我们想写出来的游戏,那么接下来就是构思了↓
2.构思
写任意一个代码游戏的时候都要先构思,这样写出来的代码才是比较好的,能让你事半功倍。 ????????????????????? ? ? ? ? ———沃·兹基硕德
构思好了当你开始写你的代码的时候到那个地方不对劲的时候你也可以比较快速的找到错误并纠正他
废话不多说,开锤!
首先我们要确定我们代码中要实现的: (为了更好的说明我们制作一个简单难度的扫雷)
- 制作一个9 x 9的棋盘并初始化埋好雷。
- 制作一个9 x 9的棋盘显示信息。
- 输入坐标然后显示该坐标结果。
- 游戏结果判断。
完成构思接下来就进入我们的代码领域了。
二、代码与实现
1.代码分块
同样的,为了让整个代码更加可观,我们首先把代码分为三个板块: 1.test.c源文件 测试游戏和一些细节的补充。
2.game.h头文件 存放游戏函数的声明。
3.game.c源文件 整个游戏的实现。
2.test.c 源文件
首先我们来将代码的整体框架给写出来:
void menu()
{
printf("***********************************\n");
printf("*********** 1. play *********\n");
printf("*********** 0. exit *********\n");
printf("***********************************\n");
}
void game()
{
}
int main()
{
int input = 0;
do
{
menu();
printf("请选择>");
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("已退出游戏!");
break;
default:
printf("选择错误,请重新选择!\n");
break;
}
} while (input);
return 0;
}
然后我们要补充好game()的部分: 按照构思,我们应该要把棋盘写出来,但是在这里需要想一想,是不是两个棋盘更好一点,所以我们可以分别写两个棋盘,一个棋盘放置的是我们扫雷里面哪个下标下是有雷的,然后另一个棋盘作为给玩家进行游戏时看到的棋盘,是只会显示提示附近有几颗雷或者无雷的棋盘。
然后还有一点,我们将数组作为每一个格子里面存放是否有地雷的容器,那当边框坐标要读取旁边有多少地雷的时候,是不是也读取旁边的8个坐标呢,但是边框旁边没有呀,那我们可以在边框上再围上一层边,然后这一层边上的不放雷就好了,所以是加上2的边框。
———————————>
那么直接写?不不不,还记得我们区分板块吗,在这里我们只需要命名几个函数,然后在game.c上写就可以啦(如果你是自己写的时候建议一边命名函数一边写,然后调试过后再写下一个,这里为了给大家写思路更流程一些才这样写!)
代码如下:
#pragma once
#include <stdio.h>
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
void game()
{
char Mine[ROWS][COLS] = { 0 };
char Show[ROWS][ROWS] = { 0 };
InitBoard(Mine, ROWS, COLS, '0');
InitBoard(Show, ROWS, COLS, '*');
SetMine(Mine, ROW, COL);
DisplayBoard(Show, ROW, COL);
FindMine(Mine, Show, ROW, COL);
}
所以tese.c就是把我们的函数构思再完善一遍。然后每一个对应函数在game.h里面写,这样份块的优势就是清晰明了。
3.game.h头文件
头文件这里就没什么好说的了,就是把每一个函数都赋予声明,让它能正常运行,前面我们的test.c中game里面写了四个函数,我们只需要把他们分别声明,并且确定好函数的传参就可以了。
代码如下:
#pragma once
#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_COUNT 10
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set);
void DisplayBoard(char board[ROWS][COLS], int row, int col);
void SetMine(char board[ROWS][COLS], int row, int col);
void FindMine(char Mine[ROWS][COLS], char Show[ROWS][COLS], int row, int col);
然后接下来我们主要讲解的是game.c的思路。
4.game.c源文件
1.分函数实现
同样的,这里我们分为几个函数的讲解:
- InitBoard 初始化函数(把棋盘数据初始化)
- DisplayBoard 打印函数(进行游戏时打印给玩家看)
- SetMine 随机布雷函数(棋盘初始化后需要进行布雷)
- GetMineCount 返回雷数函数(负责在游戏进行时,玩家输入坐标返回周围雷的数字提示)
- FindMine 扫雷游戏函数(进行游戏时判断游戏是否继续)
接下来–
2.InitBoard 初始化函数
这是一个初始化函数,目的就是把我们之前设置的二维数组里面每一个坐标下的内容初始化,比如我们的后台得先把他的初始化成0,然后待会的布雷函数随机放十个1进去就可以了。
那初始化两个数组要放置的内容不同怎么办,我们可以把他们需要初始化的字符作为传承传过去。
代码如下:
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
{
int i = 0;
for (i = 0; i < rows; i++)
{
int j = 0;
for (j = 0; j < cols ; j++)
{
board[i][j] = set;
}
}
}
这样我们就把Mine二维数组中的数字全部换成了’ 0 ‘,而显示数组Show中就全部换成了’ * '。
3.DisplayBoard 打印函数
首先我们要思考我们的打印出来的是什么样子的一个界面给别人玩?
是这样子的: 还是这样子的:
还是这样子的:
很明显,三者之间最后一个比较神似扫雷一点吧?同时也有第二个的前置坐标提示,不用担心游戏还没玩多久眼睛就瞎了的第一种。
我们先写第二种: 代码如下:
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
int i = 0;
for (i = 0; i <= 9; i++)
{
if (i == 0)
{
printf("0 ");
}
else
printf("%d ", i);
}
printf("\n");
for (i = 1; i <= row; i++)
{
int j = 0;
printf("%d ", i);
for (j = 1; j <= col; j++)
{
printf("%c ", board[i][j]);
}
printf("\n");
}
}
然后就是弄的好看一点嘛,把周围放上边框,加上小游戏,把他移动到不那么靠左的地方。(优化的地方我会标注上说明)
代码如下:
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
int i = 0;
printf(" 扫雷小游戏 \n");
printf(" --------------------\n");
for (i = 0; i <= 9; i++)
{
if (i == 0)
{
printf(" |0 ");
}
else
printf("%d ", i);
}
printf("|");
printf("\n");
for (i = 1; i <= row; i++)
{
int j = 0;
printf(" |");
printf("%d ", i);
for (j = 1; j <= col; j++)
{
printf("%c ", board[i][j]);
}
printf("|");
printf("\n");
}
printf(" --------------------\n");
}
这样子就可以完好的打印出我们的显示界面了。
4.SetMine 随机布雷函数
随机布雷函数就比较简单了,我们已经初始化将后台表中所有数组内都变成了字符0了,那么我们只需要在数组范围内随机生成几个坐标然后把字符1替换进去就可以了。
void SetMine(char board[ROWS][COLS], int row, int col)
{
int count = EASY_COUNT;
while(count)
{
int x = rand() % row + 1;
int y = rand() % col + 1;
if (board[x][y] != '1')
{
board[x][y] = '1';
count--;
}
}
}
以上就是我们的布雷函数了,如果想要扩大界面布置更多的雷可以将数据改一下就可以啦。
5.GetMineCount 返回雷数函数
这个是镶嵌在第五个就是FindMine 扫雷游戏函数中的,但为了下面的函数大家更好理解,我们就先将这个函数写了。
返回雷的个数就是我需要统计我输入坐标周围的8个坐标中是否有雷,那么这里就显现出我们当初设置空为字符0,雷为字符1的优势了,当我们要知道附近雷的个数,可以直接用周围的ASCII码值来进行计算。
比如:当右边是地雷字符1的时候,我们只需要计算他减去字符0就可以得到存在1个地雷了,当不是地雷时相减就是存在0个地雷。所以
代码如下:
int GetMineCount(char Mine[ROWS][COLS],int x,int y)
{
int ret = 0;
int i = 0;
for (i = x - 1; i <= x + 1; i++)
{
int j = 0;
for (j = y - 1; j <= y + 1; j++)
{
ret = ret + Mine[i][j] - '0';
}
}
return ret;
}
6.FindMine 扫雷游戏函数
首先我们要明确这个函数要干什么:
1. 玩家输入坐标进行游戏 2. 判断玩家输入坐标是否在范围内 3. 判断玩家坐标是否为地雷 4. 判断玩家是否扫出所有地雷游戏胜利
那我们挨个实现:
代码如下:
void FindMine(char Mine[ROWS][COLS], char Show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int win = 0;
while (win < row * col - EASY_COUNT)
{
printf("请输入要排查的坐标:>");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <=col)
{
if (Mine[x][y] == '1')
{
printf("很遗憾,你被炸死了\n");
DisplayBoard(Mine, ROW, COL);
break;
}
else
{
int count = GetMineCount(Mine, x, y);
Show[x][y] = count + '0';
DisplayBoard(Show, ROW, COL);
win++;
}
}
else
{
printf("坐标非法,请重新输入:\n");
}
}
if (win == row * col - EASY_COUNT)
{
printf("恭喜你赢得胜利!\n");
DisplayBoard(Mine, ROW, COL);
}
}
然后这就是我们的扫雷函数啦。
三、完整代码
1.test.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 Mine[ROWS][COLS] = { 0 };
char Show[ROWS][ROWS] = { 0 };
InitBoard(Mine, ROWS, COLS, '0');
InitBoard(Show, ROWS, COLS, '*');
SetMine(Mine, ROW, COL);
DisplayBoard(Mine, ROW, COL);
DisplayBoard(Show, 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("已退出游戏!");
break;
default:
printf("选择错误,请重新选择!\n");
break;
}
} while (input);
return 0;
}
2.game.h
#pragma once
#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_COUNT 10
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set);
void DisplayBoard(char board[ROWS][COLS], int row, int col);
void SetMine(char board[ROWS][COLS], int row, int col);
void FindMine(char Mine[ROWS][COLS], char Show[ROWS][COLS], int row, int col);
3.game.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
{
int i = 0;
for (i = 0; i < rows; i++)
{
int j = 0;
for (j = 0; j < cols ; j++)
{
board[i][j] = set;
}
}
}
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
int i = 0;
printf(" 扫雷小游戏 \n");
printf(" --------------------\n");
for (i = 0; i <= 9; i++)
{
if (i == 0)
{
printf(" |0 ");
}
else
printf("%d ", i);
}
printf("|");
printf("\n");
for (i = 1; i <= row; i++)
{
int j = 0;
printf(" |");
printf("%d ", i);
for (j = 1; j <= col; j++)
{
printf("%c ", board[i][j]);
}
printf("|");
printf("\n");
}
printf(" --------------------\n");
}
void SetMine(char board[ROWS][COLS], int row, int col)
{
int count = EASY_COUNT;
while(count)
{
int x = rand() % row + 1;
int y = rand() % col + 1;
if (board[x][y] != '1')
{
board[x][y] = '1';
count--;
}
}
}
int GetMineCount(char Mine[ROWS][COLS],int x,int y)
{
int ret = 0;
int i = 0;
for (i = x - 1; i <= x + 1; i++)
{
int j = 0;
for (j = y - 1; j <= y + 1; j++)
{
ret = ret + Mine[i][j] - '0';
}
}
return ret;
}
void FindMine(char Mine[ROWS][COLS], char Show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int win = 0;
while (win < row * col - EASY_COUNT)
{
printf("请输入要排查的坐标:>");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <=col)
{
if (Mine[x][y] == '1')
{
printf("很遗憾,你被炸死了\n");
DisplayBoard(Mine, ROW, COL);
break;
}
else
{
int count = GetMineCount(Mine, x, y);
Show[x][y] = count + '0';
DisplayBoard(Show, ROW, COL);
win++;
}
}
else
{
printf("坐标非法,请重新输入:\n");
}
}
if (win == row * col - EASY_COUNT)
{
printf("恭喜你赢得胜利!\n");
DisplayBoard(Mine, ROW, COL);
}
}
这就是扫雷小游戏的C语言写法啦,小白制作不易,有错的地方还请xdm指正,互相关注,共同进步。
还有一件事:
|