C语言简单扫雷

阿林说:点赞,观看,养成好习惯!

 

 1.扫雷游戏的分析

敲出一个简单的扫雷游戏,要先对游戏的设计和实现进行思考

1.以9*9的扫雷棋盘为例子,我们需要随机布置雷、排查雷 ,那就需要存储这些信息,而棋盘是个二维的形状,,我们很容易想到使用二维数组来存储这些信息,棋盘在没有排查的时候,我们用*这个符号指代每一个格子,那么,棋盘就用字符二维数组进行存储,如图:

这样的棋盘没有标坐标,不方便扫雷,我们给他标上坐标

 

2.棋盘需要实现展示功能,还要储存随机布置雷的位置,那我们就用两个棋盘来存储这些信息,一个用来展示棋盘,分辨扫过雷的位置和没有扫雷的位置,另一个棋盘存储雷的排布,为了展示雷的棋盘和储存雷的棋盘的类型保持一致,那也用字符类型的数组来存储,并且这个棋盘不会展示出来

判断有没有放置雷就像真假一般,我们很自然的使用 0为假,1为真,而因为是字符数组,我们就用'0'和'1'来代替无雷和有雷

 

3.在玩游戏的时候,我们都知道,我们扫了雷的地方如果没有雷,就统计这个格子周围有几个雷,如果设置成9*9的棋盘,在边缘的格子不是雷,需要统计它周围有几个雷的时候,显然会越界访问,那我们就将棋盘给它加一条“边”,这条边不布置雷,也不显示出来,这样就可以简单地解决问题了

因此,我们在设置棋盘的时候,设置11行11列,又因为每个棋盘都是11行11列,11这个数字多次出现,这个数字又可以改变棋盘的大小,我们定义它为一个数,后续需要修改时也很方便

#define ROW 9   //行(用于展示)
#define COL 9    //列

#define ROWS ROW + 2  (用于布置)
#define COLS COL + 2

我们也将9这个数字定义一下,后续需要修改棋盘大小时方便修改

而给棋盘加条边,相当于行和列各加2,

所以定义ROWS和COLS分别为ROW+2,COL+2

4.最后一个稍微难的点就是统计一个格子周围雷的数量了,我们给排雷的坐标设为(a,b),则用

a-1,b-1,a,b,a+1,b+1这几个数就可以描述周围的坐标了 

 

用于统计的代码如下

arr[a - 1][b - 1] + arr[a - 1][b] + arr[a - 1][b + 1]
+ arr[a][b - 1] + arr[a][b + 1]
+ arr[a + 1][b - 1] + arr[a + 1][b] + arr[a + 1][b + 1] - 7 * '0'; 

因为我用的是字符1和字符0去描述雷的信息的,所以要统计雷的数量,还要减去7个字符0

即-7 * ' 0 ' 

5.判定游戏结束

5.1第一种游戏结束的情况就是踩雷了

if (mine[a][b] == '1')
{
    printf("雷的位置(1为雷):\n");
    _printf(mine);//踩雷后展示所有雷的位置,让玩家“死”而无憾
    printf("你被炸死了,游戏结束\n\n");   //这里可以写上当玩家扫雷失败时,对玩家说的话

    //还可以对玩家进行惩罚~(手动狗头)
    return 1;
}

这里的(a,b)是玩家要扫雷的地方,_printf()这个函数是用来打印棋盘的函数,参数是哪个棋盘,(二维数组) 

(下面的代码在文章最后有)

void _printf(char arr[ROWS][COLS])
{
    for (int i = 0; i <= ROW; i++)
    {
        printf("%d ", i);//用来给棋盘加上坐标
    }
    printf("\n");
    for (int i = 1; i <= ROW; i++)
    {
        printf("%d ", i);//用来给棋盘加上坐标
        for (int j = 1; j <= COL; j++)
        {
            printf("%c ", arr[i][j]);
        }
        printf("\n");
    }
    printf("\n");

 

5.2第二种情况就是玩家将雷以外的空格全扫光了

我们可以用棋盘格子总数减去雷的数量来得到雷以外的所有空格数cot,用while(cot)

判断游戏的进行还是结束

当玩家每扫出一个空格这个数就加减1,直到减少到0,退出循环,玩家胜利,游戏结束

(下面的代码在文章最后有)

    while (cot)
    {
        printf("请输入你要猜的位置>");
        scanf("%d%d", &a, &b);
        if (a > 0 && a <= ROW && b > 0 && b <= ROW)
        {
            if (mine[a][b] == '1')
            {
                printf("雷的位置(1为雷):\n");
                _printf(mine);//踩雷后展示所有雷的位置,让玩家“死”而无憾
                printf("你被炸死了,游戏结束\n\n");
                return 1;
            }
            else
            {
                show[a][b] = getnum(mine, a, b);
                system("cls");
                _printf(show);
                cot--;
            }
        }
        else
            printf("输入错误,请重新输入\n");//只对数字有效,使用符号会进入死循环
    }
    printf("恭喜你,扫雷成功!\n");

 

2.游戏逻辑设计想象 

1.我们要打印出一个简易菜单,说明进入和退出游戏的操作是什么,为了游戏逻辑简单易读,定义一个菜单函数来负责打印菜单

void menu1()
{
    printf("****************\n");
    printf("**** 1.play ****\n");
    printf("**** 0.exit ****\n");
    printf("****************\n");
}  

2.用户输入一个数,选择1:进入游戏,选择0:退出游戏,选择0和1以外的其他内容时,提示选择错误,请重新输入,并且用户可再次选择 

那就意味着不仅要输入,还要判断这个数并提供相应的作用,并且可以再次输入

可以多次输入就用do - while()循环来实现,条件为scanf(),不仅可以多次输入,还可使得游戏不断进行,里面再嵌套一个switch函数用来使不同的输入值,实现不同的功能

(下面的代码在文章最后有)

int main()
{
    srand((unsigned)time(NULL));//设置时间种子,使得随机布雷
    int input = 0;
    do
    {
        menu1();
        printf("请选择>");
        scanf("%d", &input);
        switch (input)
        {
        case 0:
            printf("退出\n");
            break;
        case 1:
            printf("扫雷开始\n");
            int ret=game();
            break;
        default :
            printf("选择错误,请重新选择\n");
            break;
        }
    } while (input);
    return 0;

 3.代码实现分析

在上面的代码中,我们为了游戏逻辑的清晰,将游戏主体的实现放在game()这个函数里面,接下来,我们给他写上代码,完成游戏的设计 

1.显示游戏菜单 

提示玩家输入何值进行游戏或退出 

void menu1()
{
    printf("****************\n");
    printf("**** 1.play ****\n");
    printf("**** 0.exit ****\n");
    printf("****************\n");

因为对于玩家而言,选择play就是进入游戏,选择play时要向玩家再次提示输入内容的限制

因为我们的棋盘是有限的坐标的范围也是有限的,玩家有可能输入的不是坐标,而是符号,

会令玩家无法正常游戏,因此有必要提示 

printf("注意:\n  

                1.不可输入字母\n  

                2.每输入一个数要用空格隔开\n  

                3.每次必须输入两个数\n");
// 这里为了方便阅读而分成三行,实际上不可分成三行

 

2.布置棋盘 

char mine[ROWS][COLS] = { 0 };  //放置雷的棋盘
char show[ROWS][COLS] = { 0 }; //用于展示的棋盘

 

3.棋盘初始化

展示的棋盘全用 * 初始化

放雷的棋盘先不放置雷,即用 '0'进行初始化 

我们用一个函数来完成棋盘的初始化,则:

函数的第一个参数为哪个棋盘(二维数组),第二个参数为初始化的内容(一个是*,一个是’0‘)

void set(char arr[ROWS][COLS], char s) //函数声明
{
    for (int i = 0; i < ROWS; i++)
    {
        for (int j = 0; j < COLS; j++)
        {
            arr[i][j] = s;
        }
    }
}

set(mine, '0');  //给雷的棋盘初始化
set(show, '*');  //给展示的棋盘初始化

4. 布置雷

使用随机数随机一个坐标,用一个数设置要放雷的数count,循环放置

如果这个位置没有雷,放一个雷,count-1

如果这个地方已经有雷,count不变,再次进入布置雷的循环,直到count为0

void setmine(char arr[ROWS][COLS], int count)//放置雷的函数
{
    while (count)
    {
        int a = rand() % ROW + 1;
        int b = rand() % COL + 1;
        if (arr[a][b] != '1')
        {
            arr[a][b] = '1';
            count--;
        }
    }

5.屏幕上打印展示的棋盘

_printf(show);

6.待玩家输入一个坐标,进行排雷 

需要注意的是如果输入两个错误的数字,会提示错误,输入一个正确数字、一个错误数字

会使得坐标不按玩家想要的坐标进行排雷,因为上一次的一个坐标合法,这次再获取一个合法数字

即可组成一个坐标。遇到这种情况,只需要输入一个合法数就恢复正常了,但是不能保证这个坐标底下不是雷(合法数就是符合棋盘范围的数)

scanf("%d%d", &a, &b); 

7.打印棋盘

1.先判断这个位置是不是雷,是雷,游戏结束,不是雷,统计周围的雷

2.因为棋盘是一个一个打印出来的,排查的位置越多 ,打印的棋盘也越多,为了界面美观,

使用函数system("cls")进行清屏,先清屏,再打印

 show[a][b] = getnum(mine, a, b);//统计周围的雷的数量
system("cls");//清屏
_printf(show);//打印棋盘
cot--;

8.游戏胜利 

在不断获取非雷位置,最终会获胜,需要使这局游戏停下 

while (cot)
{

        xxxxxxxx    //省略这里的代码,看整体结构

        xxxxxxxx    //省略这里的代码,看整体结构
    printf("恭喜你,扫雷成功!\n");

9.落入循环,玩家进行下一 次的选择

 

4.所有代码

在一个工程里,往往会用多个位置来写代码,将头文件、游戏实现、游戏逻辑,分别放置  

 

game.h 

 头文件

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
#include<windows.h>
#define ROW 9       //行
#define COL 9       //列
#define num 15      //雷的数量
#define ROWS ROW + 2
#define COLS COL + 2
void set(char arr[ROWS][COLS], char s);
void setmine(char arr[ROWS][COLS], int count);
void _printf(char arr[ROWS][COLS]);
char getnum(char arr[ROWS][COLS], int a, int b);

 

game.c 

 实现游戏的各个函数主体

#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
void set(char arr[ROWS][COLS], char s)//初始化函数声明
{
	for (int i = 0; i < ROWS; i++)
	{
		for (int j = 0; j < COLS; j++)
		{
			arr[i][j] = s;
		}
	}
}
void setmine(char arr[ROWS][COLS], int count)//放置雷的函数
{
	while (count)
	{
		int a = rand() % ROW + 1;
		int b = rand() % COL + 1;
		if (arr[a][b] != '1')
		{
			arr[a][b] = '1';
			count--;
		}
	}
}
void _printf(char arr[ROWS][COLS])
{
	for (int i = 0; i <= ROW; i++)
	{
		printf("%d ", i);//用来给棋盘加上坐标
	}
	printf("\n");
	for (int i = 1; i <= ROW; i++)
	{
		printf("%d ", i);//用来给棋盘加上坐标
		for (int j = 1; j <= COL; j++)
		{
			printf("%c ", arr[i][j]);
		}
		printf("\n");
	}
	printf("\n");
}
char getnum(char arr[ROWS][COLS], int a, int b)
{
	return arr[a - 1][b - 1] + arr[a - 1][b] + arr[a - 1][b + 1]
		+ arr[a][b - 1] + arr[a][b + 1]
		+ arr[a + 1][b - 1] + arr[a + 1][b] + arr[a + 1][b + 1] - 7 * '0';
}

 

test.c 

 游戏逻辑、游戏实现

#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
void menu1()
{
	printf("****************\n");
	printf("**** 1.play ****\n");
	printf("**** 0.exit ****\n");
	printf("****************\n");
}
int game()
{

	char mine[ROWS][COLS] = { 0 };
	char show[ROWS][COLS] = { 0 };
	set(mine, '0');//给雷的棋盘初始化
	set(show, '*');//给展示的棋盘初始化
	int count = num;
	int c = ROW - 1;
	setmine(mine, count);
	int cot = c * c - count;
	printf("注意:\n   1.不可输入字母\n   2.每输入一个数要用空格隔开\n   3.每次必须输入两个数\n");
	int a = 0;
	int b = 0;
	_printf(show);
	while (cot)
	{
		printf("请输入你要猜的位置>");
		scanf("%d%d", &a, &b);
		if (a > 0 && a <= ROW && b > 0 && b <= ROW)
		{
			if (mine[a][b] == '1')
			{
				printf("雷的位置(1为雷):\n");
				_printf(mine);//踩雷后展示所有雷的位置,让玩家“死”而无憾
				printf("你被炸死了,游戏结束\n\n");
				return 1;
			}
			else
			{
				show[a][b] = getnum(mine, a, b);//统计周围的雷的数量
				system("cls");//清屏
				_printf(show);//打印棋盘
				cot--;
			}
		}
		else
			printf("输入错误,请重新输入\n");//只对数字有效,使用符号会进入死循环
	}
	printf("恭喜你,扫雷成功!\n");
}
int main()
{
	srand((unsigned)time(NULL));//设置时间种子,使得随机布雷
	int input = 0;
	do
	{
		menu1();
		printf("请选择>");
		scanf("%d", &input);
		switch (input)
		{
		case 0:
			printf("退出\n");
			break;
		case 1:
			printf("扫雷开始\n");
			int ret=game();
			break;
		default :
			printf("选择错误,请重新选择\n");
			break;
		}
	} while (input);
	return 0;
}

 

最后再提示: 

需要注意的是如果输入两个错误的数字,会提示错误,输入一个正确数字、一个错误数字

会使得坐标不按玩家想要的坐标进行排雷,因为上一次的一个坐标合法,这次再获取一个合法数字

即可组成一个坐标。遇到这种情况,只需要输入一个合法数就恢复正常了,但是不能保证这个坐标底下不是雷(合法数就是符合棋盘范围的数)

 

阿林说:都看到这里了,点点关注点点赞呗! 

“谢谢您的观看!”

 

 

  • 43
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 9
    评论
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值