C 语 言 --- 扫 雷 游 戏(初 阶 版)

C 语 言 --- 扫 雷 游 戏 初 阶 版

💻作 者 简 介:曾 与 你 一 样 迷 茫,现 以 经 验 助 你 入 门 C 语 言

💡个 人 主 页:@笑口常开xpr 的 个 人 主 页

📚系 列 专 栏:C 启新程

✨代 码 趣 语:每 个 人 都 应 该 学 习 编 程,因 为 它 教 会 你 如 何 思 考。

💪代 码 千 行,始 于 坚 持,每 日 敲 码,进 阶 编 程 之 路。

📦gitee 链 接:gitee

在 编 程 的 世 界 里,每 一 行 代 码 都 可 能 隐 藏 着 无 限 的 可 能 性 。你 是 否 想 过,一 个 小 小 的 程 序 究 竟 能 改 变 什 么?它 可 以 是 解 决 复 杂 问 题 的 工 具 ,也 可 以 是 实 现 梦 想 的 桥 梁。今 天,就 让 我 们 一 起 走 进 C 语 言 扫 雷 的 世 界,探 索 它 的 无 限 潜 力。

网页版扫雷游戏

代 码 全 貌 与 功 能 介 绍


整 个 游 戏 项 目 由 三 个 主 要 文 件 构 成:game.h、test.c 和 game.c。这 种 多 文 件 的 架 构 设 计,有 助 于 将 不 同 功 能 模 块 分 离,提 高 代 码 的 可 读 性、可 维 护 性 与 可 扩 展 性 。
game.h
game.h 包 含 了 游 戏 所 需 的 头 文 件 引 用、常 量 定 义 以 及 函 数 声 明。
test.c
test.c 是游戏的主逻辑文件,负责处理用户输入和游戏流程的控制;
game.c
game.c 则实现了游戏的具体功能函数。
下 面 展 示完整代码
读 者 可 以 将 这 段 代 码 复 制 到 自 己 的 编 译 器 中 运 行:
game.h

javascript 复制代码
#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 board[ROWS][COLS], char show[ROWS][COLS], int row, int col);

test.c

javascript 复制代码
#include "game.h"
void menu()
{
	printf("**********************************\n");
	printf("**********    1.play     *********\n");
	printf("**********    0.exit     *********\n");
	printf("**********************************\n");
}

void game()
{
	//需要存放布置雷的信息,需要存放排查出的雷的信息 -- 需要2个二维数组
	//排查时,防止坐标越界,要给数组行,列各加2
	char mine[ROWS][COLS] = { 0 };//布置好的雷的信息
	char show[ROWS][COLS] = { 0 };//排查出的雷的信息

	//1.初始化棋盘
	InitBoard(mine, ROWS, COLS, '0');
	InitBoard(show, ROWS, COLS, '*');

	//2.打印棋盘
	//DisPlayBoard(mine, ROW, COL);
	DisPlayBoard(show, ROW, COL);

	//3.布置雷
	SetMine(mine, ROW, COL);
	//DisPlayBoard(mine, ROW, COL);

	//4.排查雷
	FindMine(mine, show, ROW, COL);
}
void test()
{
	srand((unsigned int)time(NULL));
	int input = 0;
	do
	{	//打印菜单
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("扫雷游戏\n");
			//实现扫雷游戏的逻辑
			game();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("选择错误,请重新选择\n");
			break;
		}
	} while (input);
}
int main()
{
	//实现游戏的主逻辑
	test();
	return 0;
}

game.c

javascript 复制代码
#define _CRT_SECURE_NO_WARNINGS 1 

#include "game.h"

void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < rows; i++)
	{
		for (j = 0; j < cols; j++)
		{
			board[i][j] = set;
		}
	}
}
void DisPlayBoard(char board[ROWS][COLS], int row, int col)
{
	int i = 0;
	int j = 0;
	printf("----------扫雷----------\n");
	for (i = 0; i <= col; i++)
	{
		if (i == 0)
		{
			printf("  ");
			continue;
		}
		printf("%d ", i);
	}
	printf("\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 SetMine(char board[ROWS][COLS], int row, int col)
{
	int count = EASY_COUNT;
	while (count)
	{
		//1.产生随机的坐标1~9
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		//2.在坐标中放置雷
		if (board[x][y] == '0')
		{
			board[x][y] = '1';
			count--;
		}
	}
}
int get_mine_count(char mine[ROWS][COLS], int x, int y)
{
	return (mine[x - 1][y - 1] + mine[x - 1][y] + mine[x - 1][y + 1]
		+ mine[x][y - 1] + mine[x][y + 1] + mine[x + 1][y - 1]
		+ mine[x + 1][y] + mine[x + 1][y + 1] - 8 * '0');
}


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 (show[x][y] != '*')
			{
				printf("该坐标被排查过了\n");
				continue;
			}
			if (mine[x][y] == '1')
			{
				printf("很遗憾,你被炸死了\n");
				DisPlayBoard(mine, ROW, COL);
				break;
			}
			else
			{
				//统计一下坐标
				int n = get_mine_count(mine, x, y);
				show[x][y] = n + '0';
				DisPlayBoard(show, ROW, COL);
				win++;
			}
		}
		else
		{
			printf("坐标非法,请重新输入\n");
		}
	}
	if (win == (row * col - EASY_COUNT))
	{
		printf("恭喜你,排雷成功\n");
		DisPlayBoard(mine, ROW, COL);
	}
}

扫雷游戏的功能说明


  • 使 用 控 制 台 实 现 经 典 的 扫 雷 游 戏
  • 游 戏 可 以 通 过 菜 单 实 现 继 续 玩 或 者 退 出 游 戏
  • 扫 雷 的 棋 盘 是 9 * 9 的 格 子
    默 认 随 机 布 置 10 个 雷
  • 可 以 排 查 雷
    如 果 位 置 不 是 雷,就 显 示 周 围 有 几 个 雷
    如 果 位 置 是 雷,就 炸 死 游 戏 结 束
    把 除 10 个 雷 之 外 的 所 有 非 雷 都 找 出 来,排 雷 成 功,游 戏 结 束

游 戏 效 果 展 示


游 戏 启 动 后,玩 家 看 到 简 洁 的 主 菜 单,选 择 1 开 始 游 戏,反 之,选 择 0 退 出 游 戏。

此 时,空 棋 盘 呈 现 眼 前,9 × 9 的 棋 盘 格 等 待 着 玩 家 进 行 排 查 雷。

玩 家 输 入 下 棋 坐 标 后,程 序 会 验 证 坐 标 合 法 性。若 坐 标 合 法 且 对 应 位 置 没 有 雷,将 会 显 示 棋 盘 等 待 玩 家 下 一 次 排 查 雷;如 果 坐 标 是 雷,玩 家 会 收 到 " 很 遗 憾 ,你 被 炸 死 了 " 的 提 示; 若 坐 标 不 合 法 ,玩 家 会 收 到 " 坐 标 非 法 " 或 " 该 坐 标 被 排 查 过 了" 的 提 示,需 重 新 输 入 坐 标。

玩 家 不 断 根 据 游 戏 规 则 排 查 雷 直 到 雷 全 部 排 查 完 ,在 这 个 过 程 中,程 序 不 断 检 查 玩 家 输 入 的 坐 标 是 否 是 雷 以 及 雷 是 否 全 部 排 查 完。若 玩 家 把 雷 全 部 排 查 完,程 序 立 即 宣 告 " 恭 喜 你,排 雷 成 功 ",游 戏 结 束。


游 戏 代 码 详 解


game.h

javascript 复制代码
#pragma once

防 止 重 复 包 含
#pragma once 指 令 确 保 这 个 头 文 件 在 整 个 项 目 中 只 会 被 包 含 一 次。在 大 型 项 目 中,若 多 个 源 文 件 包 含 同 一 个 头 文 件,若 不 加 以 限 制,头 文 件 中 的 内 容 会 被 重 复 编 译,导 致 错 误。使 用 此 指 令 可 避 免 这 种 情 况。

javascript 复制代码
#include <stdio.h>​
#include <stdlib.h>

引 入 标 准 库
#include <stdio.h> 引 入 标 准 输 入 输 出 库,使 得 代 码 能 够 使 用 如 printf(用 于 输 出 信 息)和 scanf( 用 于 获 取 用 户 输 入)等 函 数。#include <stdlib.h> 引 入 标 准 库,为 rand 函 数( 随 机 布 置 雷 时 用 到 )等 提 供 支 持。

javascript 复制代码
#define ROW 9
#define COL 9

#define ROWS ROW + 2
#define COLS COL + 2

//雷的数量
#define EASY_COUNT 10

常 量 定 义
定 义 了 游 戏 棋 盘 的 大 小 ROW 和 COL 为 9,为 了 防 止 排 查 雷 时 数 组 越 界,定 义 了 ROWS 和 COLS 为 ROW + 2 和 COL + 2。同 时,还 定 义 了 初 级 难 度 下 雷 的 数 量 EASY_COUNT 为 10。

javascript 复制代码
//实现扫雷游戏中的函数声明
//初始化棋盘
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 board[ROWS][COLS], char show[ROWS][COLS], int row, int col);

函 数 声 明
声 明 了 四 个 关 键 函 数,分 别 是 初 始 化 棋 盘 函 数 InitBoard、打 印 棋 盘 函 数 DisPlayBoard、布 置 雷 函 数 SetMine 和 排 查 雷 函 数 FindMine。


test.c


javascript 复制代码
#define _CRT_SECURE_NO_WARNINGS 1 

消 除 安 全 警 告
用 于 消 除 因 使 用 了 一 些 不 安 全 的 函 数( 如 scanf)而 产 生 的 编 译 警 告。在 较 新 的 编 译 器 中,为 了 安 全 考 虑,对 一 些 函 数 的 使 用 会 给 出 警 告 , 通 过 定 义 这 个 宏 可 屏 蔽 这 些 警 告。

javascript 复制代码
void menu()​
{​
    printf("*******************************\n");​
    printf("************  1.play  *********\n");​
    printf("************  0.exit  *********\n");​
    printf("*******************************\n");​
}​

menu 函 数
menu 函 数 用 于 打 印 游 戏 主 菜 单。通 过 printf 函 数 输 出 一 个 包 含" 1.play "(开 始 游 戏)和 "0.exit"( 退 出 游 戏 )选 项 的 菜 单,为 玩 家 提 供 操 作 选 择 界 面。

javascript 复制代码
void game()
{
	//需要存放布置雷的信息,需要存放排查出的雷的信息 -- 需要2个二维数组
	//排查时,防止坐标越界,要给数组行,列各加2
	char mine[ROWS][COLS] = { 0 };//布置好的雷的信息
	char show[ROWS][COLS] = { 0 };//排查出的雷的信息

	//1.初始化棋盘
	InitBoard(mine, ROWS, COLS, '0');
	InitBoard(show, ROWS, COLS, '*');

	//2.打印棋盘
	//DisPlayBoard(mine, ROW, COL);
	DisPlayBoard(show, ROW, COL);

	//3.布置雷
	SetMine(mine, ROW, COL);
	//DisPlayBoard(mine, ROW, COL);

	//4.排查雷
	FindMine(mine, show, ROW, COL);
}​

game 函 数
game 函 数 按 照 游 戏 的 逻 辑 顺 序,依 次 完 成 了 棋 盘 初 始 化、显 示 初 始 棋 盘、布 置 雷 以 及 让 玩 家 进 行 扫 雷 操 作 等 关 键 步 骤,是 实 现 整 个 扫 雷 游 戏 功 能 的 核 心 函 数。

javascript 复制代码
void test()
{
    srand((unsigned int)time(NULL));
    int input = 0;
    do
    {   //打印菜单
        menu();
        printf("请选择:>");
        scanf("%d", &input);
        switch (input)
        {
        case 1:
            printf("扫雷游戏\n");
            //实现扫雷游戏的逻辑
            game();
            break;
        case 0:
            printf("退出游戏\n");
            break;
        default:
            printf("选择错误,请重新选择\n");
            break;
        }
    } while (input);
}​

test 函 数
test 函 数 通 过 srand 初 始 化 随 机 数 种 子,然 后 进 入 一 个 循 环,显 示 游 戏 菜 单,根 据 用 户 输 入 选 择 开 始 游 戏 或 退 出 游 戏。选 择 开 始 游 戏 时 调 用 game 函 数,game 函 数 依 次 调 用 上 述 功 能 函 数 来 完 成 游 戏 的 初 始 化、布 置 雷 和 排 查 雷 等 操 作。

javascript 复制代码
int main()​
{​
    test();​
    return 0;​
}

main 函 数
main 函 数 作 为 程 序 入 口,调 用 test() 函 数 启 动 游 戏 流 程,最 后 return 0; 表 示 程 序 正 常 结 束 。


game.c


javascript 复制代码
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
{
    int i = 0;
    int j = 0;
    for (i = 0; i < rows; i++)
    {
        for (j = 0; j < cols; j++)
        {
            board[i][j] = set;
        }
    }
}

初 始 化 棋 盘( InitBoard 函 数 )
该 函 数 接 受 棋 盘 数 组、行 数、列 数 和 初 始 化 字 符 作 为 参 数,通 过 两 层 循 环 将 棋 盘 数 组 的 每 个 元 素 初 始 化 为 指 定 字 符。

javascript 复制代码
void DisPlayBoard(char board[ROWS][COLS], int row, int col)
{
    int i = 0;
    int j = 0;
    printf("----------扫雷----------\n");
    for (i = 0; i <= col; i++)
    {
        if (i == 0)
        {
            printf("  ");
            continue;
        }
        printf("%d ", i);
    }
    printf("\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");
}

打 印 棋 盘( DisPlayBoard 函 数 )
此 函 数 用 于 在 控 制 台 打 印 棋 盘。先 打 印 棋 盘 的 表 头( 列 号 ),然 后 逐 行 打 印 棋 盘 的 内 容,包 括 行 号 和 每 个 格 子 的 字 符。

javascript 复制代码
void SetMine(char board[ROWS][COLS], int row, int col)
{
    int count = EASY_COUNT;
    while (count)
    {
        //1.产生随机的坐标1~9
        int x = rand() % row + 1;
        int y = rand() % col + 1;
        //2.在坐标中放置雷
        if (board[x][y] == '0')
        {
            board[x][y] = '1';
            count--;
        }
    }
}​

布 置 雷( SetMine 函 数 )
该 函 数 通 过 随 机 生 成 坐 标,并 在 相 应 位 置 放 置 雷( 将 字 符 设 置 为 '1'),直 到 雷 的 数 量 达 到 EASY_COUNT。

javascript 复制代码
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 (show[x][y] != '*')
            {
                printf("该坐标被排查过了\n");
                continue;
            }
            if (mine[x][y] == '1')
            {
                printf("很遗憾,你被炸死了\n");
                DisPlayBoard(mine, ROW, COL);
                break;
            }
            else
            {
                //统计一下坐标
                int n = get_mine_count(mine, x, y);
                show[x][y] = n + '0';
                DisPlayBoard(show, ROW, COL);
                win++;
            }
        }
        else
        {
            printf("坐标非法,请重新输入\n");
        }
    }
    if (win == (row * col - EASY_COUNT))
    {
        printf("恭喜你,排雷成功\n");
        DisPlayBoard(mine, ROW, COL);
    }
}

排 查 雷( FindMine 函 数 )
玩 家 输 入 坐 标 后,函 数 会 检 查 坐 标 的 合 法 性 和 是 否 已 被 排 查 过 。如 果 坐 标 合 法 且 未 被 排 查,判 断 该 位 置 是 否 为 雷,若 是 则 游 戏 失 败,否 则 统 计 周 围 雷 的 数 量 并 显 示 在 显 示 棋 盘 上,直 到 所 有 非 雷 格 子 都 被 排 查 出 来 则 游 戏 胜 利。


总结


通 过 这 个 简 单 的 扫 雷 游 戏,我 们 不 仅 实 践 了 C 语 言 中 的 基 本 语 法,如 变 量 定 义、循 环 结 构、条 件 判 断 和 函 数 调 用,还 学 习 了 如 何 使 用 随 机 数 生 成 函 数 和 输 入 输 出 函 数。希 望 这 篇 博 客 能 帮 助 你 更 好 地 理 解 C 语 言 编 程,激 发 你 在 编 程 世 界 中 不 断 探 索 和 创 新 的 热 情 。

相关推荐
Answer_ism16 分钟前
【SpringMVC】SpringMVC拦截器,统一异常处理,文件上传与下载
java·开发语言·后端·spring·tomcat
-一杯为品-1 小时前
【小项目】四连杆机构的Python运动学求解和MATLAB图形仿真
开发语言·python·matlab
脑子慢且灵2 小时前
JavaIO流的使用和修饰器模式(直击心灵版)
java·开发语言·windows·eclipse·intellij-idea·nio
欣然~2 小时前
基于蒙特卡洛方法的网格世界求解
开发语言·python·信息可视化
海晨忆3 小时前
JS—事件委托:3分钟掌握事件委托
开发语言·javascript·ecmascript·事件委托·事件冒泡
froxy3 小时前
C++11 引入了的新特性与实例说明
开发语言·c++
珹洺3 小时前
Java-servlet(七)详细讲解Servlet注解
java·服务器·开发语言·hive·servlet·html
珊瑚里的鱼3 小时前
第一讲 | 解锁C++编程能力:基础语法解析
开发语言·c++·笔记·visualstudio·学习方法·visual studio
goTsHgo3 小时前
完整的类在JVM中的生命周期详解
java·开发语言·算法
大模型铲屎官3 小时前
玩转C#函数:参数、返回值与游戏中的攻击逻辑封装
开发语言·游戏·c#·编程·参数·函数·返回值