c语言:扫雷

一、【扫雷】

二、【游戏逻辑】

1.游戏的初始化

2.雷区的翻转

3.误区

三、【代码实现】 

1.zcx.c 

1.1 面板显示

1.2 主函数 main()

1.3 实现扫雷游戏的各功能的引用

2.zdy.c

2.1 初始化棋盘函数 csh

2.2 打印棋盘函数 dyqb

2.3 布置雷函数 bzl

2.4 判断周围存在几个雷函数 zwjgl

2.5 排查雷函数 pcl

3.zdy.h

3.1 常量定义

3.2 函数声明

四、【完整代码】

1.zcx.c

2.zdy.h

3.zdy.c


一、【扫雷】

《扫雷》是一款经典的单人电脑游戏,旨在揭开一个方块网格,并避免触发隐藏的地雷。每个方块可能包含数字(表示周围地雷数量)或者地雷。玩家需要利用数字信息来推断哪些方块是安全的,并标记可能包含地雷的方块

二、【游戏逻辑】

1.游戏的初始化

      扫雷游戏的基础模式是 9×9 的雷区,为在c语言中方便实现,可以用 ' * ' 号表示未翻开的雷区,其中用字符 ' 1 ' 表示地雷,字符 ‘ 0 ’ 表示不存在地雷的区域,并用二维数组进行存储。

2.雷区的翻转

对于雷区的反转,可以通过两张二维表进行实现,分别将雷的信息,排查出雷的信息分别存储到不同二维表中。

排查表

     

雷表

在格子未被翻转前,用字符 ‘*’ 来对其进行遮盖,而格子翻转以后,该格子则显示周围的 8 个格子存在的地雷的个数。

3.误区

   扫雷游戏规则,规定了,若翻转的格子不是地雷,则显示该格子周围 8 个格子存在地雷的个数,但若选择的是边缘的格子,对雷区边界的格子执行这一操作可能会导致数组的越界访问

    为实现 9×9 的扫雷游戏,创建一个9×9的二维数组并不合适,既然对边界元素进行操作时,会导致数组越界访问,那直接将二维数组扩大一圈,使用时,我们继续使用原来的范围,对外圈不进行操作

三、【代码实现】 

为提高代码的可阅读性和让代码尽量简洁,便于维护而分成三个文件。

1.zcx.c 

1.1 面板显示

void mb()
{
	printf("******************\n");
	printf("*****1.开始 1*****\n");
	printf("*****2.结束 0*****\n");
	printf("******************\n");
}

这个函数用于打印游戏菜单,显示给玩家选择开始游戏或结束游戏

1.2 主函数 main()

int main()
{
    int a = 0;
    srand((unsigned int)time(NULL));
    do {
        mb();
        printf("请选择:");
        scanf("%d", &a);
        switch (a)
        {
            case 1:
                printf("-----扫雷开始-----\n");
                dt(); // 调用 dt() 函数来执行扫雷游戏逻辑
                break;
            case 0:
                printf("游戏结束\n");
                break;
            default:
                printf("输入错误,请重新输入\n");
                break;
        }
    } while (a);
    return 0;
}

主函数首先初始化随机数种子,然后进入一个循环中。在每次循环中,程序会打印菜单(使用 mb() 函数),然后等待用户输入选择。根据用户选择不同,程序会执行相应的操作:如果选择开始游戏,则调用 dt() 函数;如果选择结束,则打印"游戏结束";如果输入错误,则提示用户重新输入。

1.3 实现扫雷游戏的各功能的引用

//打印扫雷的画面
void dt()
{
	char nqb[HANGS][LIES] = { 0 };//存放雷的信息
	char wqb[HANGS][LIES] = { 0 };//存放排查出雷的信息显示
	//初始化棋盘
	csh(nqb, HANGS, LIES, '0');//'0'
	csh(wqb, HANGS, LIES, '*');//'*'

	//打印棋盘
	/*dyqb(wqb, HANG, LIE);*/

	//布置雷
	bzl(nqb, HANG, LIE);
	dyqb(wqb, HANG, LIE);

	//排查雷
	pcl(nqb, wqb, HANG, LIE);

}

 将初始化棋盘需要存储打印的字符,作为实参传递给函数,简化了存储字符的过程。

2.zdy.c

这段代码是扫雷游戏的一部分实现,包括了初始化棋盘、打印棋盘、布置雷、判断周围存在几个雷以及排查雷等功能。

2.1 初始化棋盘函数 csh

void csh(char qqb[HANGS][LIES], int hangs, int lies, char cszf)
{
    for (int x = 0; x < HANGS; x++) 
    {
        for (int y = 0; y < LIES; y++) 
        {
            qqb[x][y] = cszf;
        }
    }
}

这个函数用于初始化棋盘,将所有格子的状态设置为指定的字符。

2.2 打印棋盘函数 dyqb

void dyqb(char wqb[HANGS][LIES], int hang, int lie)
{
    // 打印行号
    for (int i = 0; i <= HANG; i++)
    {
        printf("%d", i);
    }
    printf("\n");

    // 打印每行的内容(包括列号和格子状态)
    for (int x = 1; x <= HANG; x++)
    {
        printf("%d", x); // 打印列号
        for (int y = 1; y <= LIE; y++)
        {
            printf("%c", wqb[x][y]); // 打印格子状态
        }
        printf("\n");
    }
}

这个函数用于打印棋盘,显示每个格子的位置,并打印首行首列所对应的数字,方便格子的翻转实现

2.3 布置雷函数 bzl

void bzl(char nqb[HANGS][LIES], int hang, int lie) 
{
    // 随机布置地雷
    int lei = LEI; // 设置地雷数量
    srand((unsigned int)time(NULL)); // 使用当前时间作为随机种子

    while (lei) 
   {
        int x = rand() % hang + 1; // 随机生成行坐标
        int y = rand() % lie + 1;  // 随机生成列坐标
        
        if (nqb[x][y] == '0') 
        {                     // 如果该位置没有地雷,则放置地雷并减少剩余地雷数量
            nqb[x][y] = '1';
            lei--;
        }
    }
}
  • 在循环开始之前,通过srand((unsigned int)time(NULL))设置了随机数种子。这样可以确保每次运行程序时都会得到不同的随机序列。
  • 在循环内部,使用 rand() 函数生成一个0到hang-1之间的随机整数,并加1得到x坐标;再生成一个0到lie-1之间的随机整数,并加1得到y坐标。
  • 然后检查所选位置是否已经有地雷(即nqb[x][y]是否等于'0'),如果是,则将该位置设为地雷('1'),并将剩余地雷数量减一。

2.4 判断周围存在几个雷函数 zwjgl

int zwjgl(char nqb[HANGS][LIES], int x, int y)
{
	return nqb[x - 1][y - 1] + nqb[x - 1][y] + nqb[x - 1][y + 1] + nqb[x][y - 1]
	+ nqb[x][y + 1] + nqb[x + 1][y - 1] + nqb[x + 1][y] + nqb[x + 1][y + 1] - 8 * '0';
}

  

  • 在这段代码中,通过将当前位置(x,y)及其周围八个格子的值相加,并减去8乘以字符'0'(即48),得到了周围地雷的数量。
  • 这是因为在ASCII码中,数字字符'0'到'9'依次对应的ASCII码值是48到57。因此减去8乘以字符'0'实际上就是将字符转换为对应的数字值。

2.5 排查雷函数 pcl

void pcl(char nqb[HANGS][LIES], char wqb[HANGS][LIES], int hang, int lie)
{
    int x = 0;
    int y = 0;
    int w = 0;

    while (w < HANG * LIE - LEI) // 当未排查的格子数量小于总格子数减去地雷数量时执行循环
    {
        printf("请输入排查的坐标:");
        scanf("%d %d", &x, &y); // 获取用户输入的坐标

        if (x >= 1 && x <= hang && y >= 1 && y <= lie) // 检查输入坐标是否合法
        {
            if (nqb[x][y] == '1') // 如果踩到地雷,则游戏失败并结束
            {
                printf("很遗憾,你被炸死了\n");
                dyqb(nqb, HANG, LIE); // 展示所有地雷位置
                break; // 结束游戏循环
            }
            else
            {
                if (wqb[x][y] != '*') // 如果该位置已经排查过,则提示无需再次排查
                {
                    printf("该位置已经排查,无需再次排查\n");
                }
                else // 否则进行排雷操作,并更新展示给玩家看的数组,并检查是否游戏胜利。
                {
                    int a = zwjgl(nqb, x, y); // 获取周围地雷数量
                    wqb[x][y] = a + '0'; // 更新展示给玩家看的数组
                    dyqb(wqb, HANG, LIE); // 打印更新后的棋盘状态(展示给玩家看)
                    w++; // 增加已排查格子数量计数器
                }
            }
        }
        else 
        {
            printf("坐标非法,请重新输入\n"); // 提示用户重新输入合法坐标
        }
    }

    if (w == HANG * LIE - LEI) 
    {
        printf("恭喜你排雷成功\n"); 
        // 如果已经成功排除所有非地雷格子,则输出恭喜信息并展示整个地雷分布情况。
        dyqb(nqb, HANG, LIE); 
    }
}

  1. 初始化变量

    • 初始化变量 x 和 y 为0,用于接收玩家输入的坐标。
    • 初始化变量 w 为0,用于计算已经排查过的格子数量。
  2. 循环排查

    • 使用 while 循环,条件是当未排查的格子数量小于总格子数减去地雷数量时执行循环。
    • 在循环内部,首先提示用户输入要排查的坐标,并使用 scanf 获取用户输入。
  3. 检查合法性

    • 检查用户输入坐标是否在合法范围内(即大于等于1且小于等于行数或列数)。
  4. 处理用户输入

    • 如果用户踩到地雷,则输出失败信息并展示所有地雷位置,并结束游戏循环。
    • 如果该位置已经排查过,则提示无需再次排查;否则进行排雷操作,并更新展示给玩家看的数组,并检查是否游戏胜利。
  5. 判断游戏结果

    • 如果已经成功排除所有非地雷格子,则输出恭喜信息并展示整个地雷分布情况。

3.zdy.h

这段代码是一个头文件,其中包含了一些函数的声明和预处理指令。

3.1 常量定义

//方便动态管理行列数
#define HANG 9
#define LIE  9

#define HANGS HANG+2
#define LIES  LIE+2

#define LEI 10

这里定义了一些常量,包括棋盘的行数和列数,以及雷的数量,方便动态设置。

3.2 函数声明

//初始化棋盘
void csh(char qqb[HANGS][LIES], int hangs, int lies,char cszf);
//打印棋盘
void dyqb(char wqb[HANGS][LIES], int hang, int lie);
//布置雷
void bzl(char nqb[HANGS][LIES], int hang, int lie);
//排查雷
void pcl(char nqb[HANGS][LIES], char wqb[HANGS][LIES], int hang, int lie);
//判断周围存在几个雷
int zwjgl(char nqb[HANGS][LIES], int x, int y);

这些是函数声明,用于告诉编译器这些函数的存在及其参数类型。在实际的源文件中需要实现这些函数,并且可以使用这个头文件来引入这些函数声明。

四、【完整代码】

1.zcx.c

#define _CRT_SECURE_NO_WARNINGS 1
//扫雷游戏

#include "zdy.h"
//面板
void mb()
{
	printf("******************\n");
	printf("*****1.开始 1*****\n");
	printf("*****2.结束 0*****\n");
	printf("******************\n");
}
//打印扫雷的画面
void dt()
{
	char nqb[HANGS][LIES] = { 0 };//存放雷的信息
	char wqb[HANGS][LIES] = { 0 };//存放排查出雷的信息显示
	//初始化棋盘
	csh(nqb, HANGS, LIES, '0');//'0'
	csh(wqb, HANGS, LIES, '*');//'*'

	//打印棋盘
	/*dyqb(wqb, HANG, LIE);*/

	//布置雷
	bzl(nqb, HANG, LIE);
	dyqb(wqb, HANG, LIE);

	//排查雷
	pcl(nqb, wqb, HANG, LIE);

}
int main()
{
	int a = 0;
	srand((unsigned int)time(NULL));
	do {
		mb();
		printf("请选择:");
		scanf("%d", &a);
		switch (a)
		{
		case 1:
			printf("-----扫雷开始-----\n");
			dt();
			break;
		case 0:
			printf("游戏结束\n");
			break;
		default:
			printf("输入错误,请重新输入\n");
			break;
		}
	} while (a);
	return 0;
}

2.zdy.h

#pragma once

#include<stdio.h>
#include<stdlib.h>
#include<time.h>
//方便动态管理行列数
#define HANG 9
#define LIE  9

#define HANGS HANG+2
#define LIES  LIE+2

#define LEI 10
//初始化棋盘
void csh(char qqb[HANGS][LIES], int hangs, int lies,char cszf);
//打印棋盘
void dyqb(char wqb[HANGS][LIES], int hang, int lie);
//布置雷
void bzl(char nqb[HANGS][LIES], int hang, int lie);
//排查雷
void pcl(char nqb[HANGS][LIES], char wqb[HANGS][LIES], int hang, int lie);
//判断周围存在几个雷
int zwjgl(char nqb[HANGS][LIES], int x, int y);

3.zdy.c

#define _CRT_SECURE_NO_WARNINGS 1

#include "zdy.h"
//初始化棋盘函数
void csh(char qqb[HANGS][LIES], int hangs, int lies, char cszf)
{
	for (int x = 0; x < HANGS; x++) 
	{
		for (int y = 0; y < LIES; y++) 
		{
			qqb[x][y] = cszf;
		}
	}
}
//打印棋盘函数
void dyqb(char wqb[HANGS][LIES], int hang, int lie)
{
	for (int i = 0; i <= HANG; i++)
	{
		printf("%d", i);
	}
	printf("\n");

	for (int x = 1; x <= HANG; x++)
	{
		printf("%d", x);
		for (int y = 1; y <= LIE; y++)
		{
			printf("%c", wqb[x][y]);
		}
		printf("\n");
	}
}
//布置雷函数
void bzl(char nqb[HANGS][LIES], int hang, int lie) 
{
	int lei = LEI;
	srand((unsigned int)time(NULL));

	while (lei) {
		int x = rand() % hang + 1;
		int y = rand() % lie + 1;
		if (nqb[x][y] == '0') 
		{
			nqb[x][y] = '1';
			lei--;
		}
	}
}

//判断周围存在几个雷
int zwjgl(char nqb[HANGS][LIES], int x, int y)
{
	return nqb[x - 1][y - 1] + nqb[x - 1][y] + nqb[x - 1][y + 1] + nqb[x][y - 1]
		+ nqb[x][y + 1] + nqb[x + 1][y - 1] + nqb[x + 1][y] + nqb[x + 1][y + 1] - 8 * '0';
}
//排查雷函数
void pcl(char nqb[HANGS][LIES], char wqb[HANGS][LIES], int hang, int lie)
{
	int x = 0;
	int y = 0;
	int w = 0;
	while (w<HANG*LIE-LEI)
	{
		printf("请输入排查的坐标:");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= hang && y >= 1 && y <= lie)
		{
			if (nqb[x][y] == '1')
			{
				printf("很遗憾,你被炸死了\n");
				dyqb(nqb, HANG, LIE);
				break;
			}
			else
			{
				if (wqb[x][y] != '*')
				{
					printf("该位置已经排查,无需再次排查\n");
				}
				else
				{
					int a = zwjgl(nqb, x, y);
					wqb[x][y] = a + '0';
					dyqb(wqb, HANG, LIE);
					w++;
				}
			}
		}
		else
		{
			printf("坐标非法,请重新输入\n");
		}
	}
	if (w == HANG * LIE - LEI) 
	{
		printf("恭喜你排雷成功\n");
		dyqb(nqb, HANG, LIE);
	}


}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值