首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >手把手教你用C语言实现进阶扫雷游戏(附完整源码+模块详解)

手把手教你用C语言实现进阶扫雷游戏(附完整源码+模块详解)

作者头像
码途随笔
发布2026-01-12 19:49:35
发布2026-01-12 19:49:35
2520
举报

一、项目概述

本文详细解析一个完整的C语言扫雷游戏实现,采用模块化编程思想,分为game.htest.cgame.c三个文件。代码具备完整的游戏功能:难度选择、棋盘初始化、地雷布置、玩家交互、递归展开等核心玩法。

二、项目架构设计

2.1 文件结构说明
代码语言:javascript
复制
扫雷游戏项目结构:
├── game.h      // 头文件:函数声明和全局变量声明
├── test.c      // 主测试文件:游戏流程控制
└── game.c      // 游戏核心功能实现
2.2 全局变量设计
代码语言:javascript
复制
// game.h中声明
extern int ROW;     // 实际行数
extern int COL;     // 实际列数
extern int ROWS;    // 扩展行数(ROW+2)
extern int COLS;    // 扩展列数(COL+2)
extern int Mine;    // 地雷数量

设计理念:采用扩展棋盘设计,实际游戏区域为(ROW × COL),但存储为(ROWS × COLS),避免边界判断时的数组越界问题。

三、核心模块功能详解

3.1 难度选择模块 (DiffChoice)
代码语言:javascript
复制
void DiffChoice() {
    // 提供三种难度选择
    // 1. 简单:9x9棋盘,10个雷
    // 2. 中等:16x16棋盘,40个雷
    // 3. 困难:30x16棋盘,99个雷
}

创新点

  • 使用system("cls")清屏提供更好的交互体验
  • 统一设置ROWS = ROW + 2COLS = COL + 2,简化后续边界处理
3.2 棋盘初始化模块 (InitBoard)
代码语言:javascript
复制
void InitBoard(char arr[][18], char set) {
    // 初始化整个扩展棋盘为指定字符set
    // 雷区棋盘用'0'初始化
    // 显示棋盘用'*'初始化
}

关键技术:二维数组作为函数参数传递时,需要指定第二维的大小。这里固定为18,因为最大列数为16+2=18。

3.3 地雷布置模块 (SetMine)
代码语言:javascript
复制
void SetMine(char mine[][18]) {
    // 使用随机数生成地雷位置
    // 地雷用字符'1'表示,非雷区用'0'表示
    // 确保不重复布置地雷
}

算法亮点

  • 使用srand((unsigned int)time(NULL))确保随机性
  • 通过mine[x][y] == '0'检查避免重复布置
3.4 显示模块 (Display)
代码语言:javascript
复制
void Display(char arr[][18]) {
    // 显示游戏棋盘
    // 打印行列号便于玩家定位
    // 显示已标记雷数/总雷数
}

用户体验优化

  • 使用%-3d%-3c格式化输出,对齐棋盘
  • 显示当前标记的旗帜数量,增强游戏信息透明度
3.5 地雷检测模块 (GetMineCount)
代码语言:javascript
复制
static int GetMineCount(char mine[][18], int x, int y) {
    // 计算坐标(x,y)周围8个格子的地雷总数
    // 利用字符'0'和'1'的ASCII码差值计算
}

巧妙的实现

代码语言:javascript
复制
count += mine[i][j] - '0';  // 字符'1' - '0' = 1,字符'0' - '0' = 0
3.6 递归展开模块 (ExpandMine)
代码语言:javascript
复制
void ExpandMine(char mine[][18], char show[][18], int x, int y, int* win) {
    // 递归展开周围没有地雷的区域
    // 实现扫雷游戏的核心"空白展开"功能
}

递归逻辑

  1. 如果当前位置周围地雷数为0,标记为空格,并递归检查周围8个格子
  2. 如果周围有地雷,显示地雷数量(1-8)
  3. 每次成功展开,win计数器递增
3.7 标记功能模块 (MineMake)
代码语言:javascript
复制
void MineMake(char show[][18], int x, int y) {
    // 标记/取消标记可能的地雷位置
    // 用'F'表示标记的旗帜
    // 限制标记数量不超过总雷数
}

游戏机制:玩家可以通过标记功能记录可疑的地雷位置,增强策略性。

3.8 主游戏逻辑模块 (FindMine)
代码语言:javascript
复制
void FindMine(char mine[][18], char show[][18]) {
    // 游戏主循环:处理玩家输入
    // 提供两种操作:1-排查雷 2-标记雷
    // 包含首点击保护机制
}

首点击保护机制

代码语言:javascript
复制
if (first_click) {
    first_click = 0;
    if (mine[x][y] == '1') {  // 如果第一次点击就是雷
        // 将雷移到其他安全位置
        // 确保玩家不会一开始就失败
    }
}

胜利条件:当win(已排查的安全格子数)等于ROW*COL - Mine(总格子数减去雷数)时,游戏胜利。

四、源代码

4.1 game.h

代码语言:javascript
复制
#pragma once
#include<stdio.h>
#include<Windows.h>
#include<stdlib.h>
#include<time.h>
extern int ROW;
extern int COL;
extern int ROWS;
extern int COLS;
extern int Mine;

void DiffChoice();

void InitBoard(char arr[][18], char set);

void SetMine(char mine[][18]);

void Display(char arr[][18]);

void FindMine(char mine[][18], char show[][18]);

4.2 game.c

代码语言:javascript
复制
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
int ROW = 0;
int COL = 0;
int ROWS = 0;
int COLS = 0;
int Mine = 0;
int flag = 0;

void menu1()
{
	system("cls");
	printf("****************************\n");
	printf("***1. 简单9x9棋盘,10个雷*****\n");
	printf("***2. 简单16x16棋盘,40个雷***\n");
	printf("***3. 简单30x16棋盘,90个雷***\n");
}
void DiffChoice()
{
	int input1 = 0;
	do
	{
		menu1();
		scanf("%d", &input1);
		switch (input1)
		{
		case 1:
			ROW = 9;
			COL = 9;
			Mine = 10;
			break;
		case 2:
			ROW = 16;
			COL = 16;
			Mine = 40;
			break;
		case 3:
			ROW = 30;
			COL = 16;
			Mine = 99;
			break;
		default:
			printf("输入错误,重新输入\n");
			break;
		}
		ROWS = ROW + 2;
		COLS = COL + 2;
	} while (input1 != 1 && input1 != 2 && input1 != 3);
}
void InitBoard(char arr[][18], char set)
{
	int i = 0;
	for (i = 0; i < ROWS; i++)
	{
		int j = 0;
		for (j = 0; j < COLS; j++)
		{
			arr[i][j] = set;
		}
	}
}

void Display(char arr[][18])
{
	printf("------扫雷游戏-------         已标记雷数:%d/%d\n", flag, Mine);
	int i = 0;
	printf("   ");
	for (i = 1; i <= COL; i++)
	{
		printf("%-3d", i);
	}
	printf("\n");
	for (i = 1; i <= ROW; i++)
	{
		int j = 0;
		printf("%-3d", i);
		for (j = 1; j <= COL; j++)
		{
			printf("%-3c", arr[i][j]);
		}
		printf("\n");
	}
}

void SetMine(char mine[][18])
{
	int count = Mine;
	while (count)
	{
		int x = rand() % ROW + 1;
		int y = rand() % COL + 1;
		if (mine[x][y] == '0')
		{
			mine[x][y] = '1';
			count--;
		}
	}
}
static int  GetMineCount(char mine[][18], int x, int y)
{
	int count = 0;
	int i = 0;
	for (i = x - 1; i <= x + 1; i++)
	{
		int j = 0;
		for (j = y - 1; j <= y + 1; j++)
		{
			count += mine[i][j] - '0';
		}
	}
	return count;
}

void MineMake(char show[][18], int x, int y)
{
	if (show[x][y] == '*')
	{
		if (flag < Mine)
		{
			show[x][y] = 'F';
			flag++;
			Display(show);
		}
		else
		{
			printf("旗帜用完了\n");
		}
	}
	else if (show[x][y] == 'F')
	{
		show[x][y] = '*';
		flag--;
		Display(show);
	}
	else
	{
		printf("该坐标已被排查\n");
	}
}

void ExpandMine(char mine[][18], char show[][18], int x, int y, int* win)
{
	int count = GetMineCount(mine, x, y);
	if (count == 0)
	{
		show[x][y] = ' ';
		(*win)++;
		int i = 0;
		for (i = x - 1; i <= x + 1; i++)
		{
			int j = 0;
			for (j = y - 1; j <= y + 1; j++)
			{
				if (i > 0 && i <= ROW && j > 0 && j <= COL && mine[i][j] == '0' && show[i][j] == '*')
				{
					ExpandMine(mine, show, i, j, win);
				}
			}
		}
	}
	else
	{
		show[x][y] = count + '0';
		(*win)++;
	}
}
void FindMine(char mine[][18], char show[][18])
{
	int op = 0;
	int x = 0;
	int y = 0;
	int win = 0;

	while (win < ROW * COL - Mine)
	{
		printf("请选择: 1-排查雷   2-标记雷\n");
		while (scanf("%d", &op) != 1 || (op != 1 && op != 2))
		{
			printf("输入错误,请重新输入\n");
			while (getchar() != '\n');
		}

		while (getchar() != '\n');
		printf("输入坐标:>");
		while (scanf("%d%d", &x, &y) != 2)
		{
			printf("输入错误,请重新输入\n");
			while (getchar() != '\n');
		}
		while (getchar() != '\n');
		if (op == 1)
		{
			int first_click = 1;
			if (first_click)
			{
				first_click = 0;
				if (mine[x][y] == '1')
				{
					mine[x][y] = '0';
					int nx, ny;
					do
					{
						nx = rand() % ROW + 1;
						ny = rand() % ROW + 1;
					} while ((nx == x && ny == y) && mine[nx][ny] == '1');
					mine[nx][ny] = '1';
				}
			}
			if (x > 0 && x <= ROW && y > 0 && y <= COL)
			{
				if (mine[x][y] == '0')
				{
					if (show[x][y] == '*')
					{
						ExpandMine(mine, show, x, y, &win);
						Display(show);
					}
					else
					{
						printf("该坐标已被排查\n");
					}
				}
				else
				{
					printf("踩到雷了\n");
					Display(mine);
					break;
				}
			}
			else
			{
				printf("非法输入,坐标越界\n");
				continue;
			}
		}
		else
		{
			MineMake(show, x, y);
		}
	}
	if (win == ROW * COL - Mine)
	{
		printf("扫雷成功\n");
	}
}

4.3 test.c

代码语言:javascript
复制
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
void menu()
{
	printf("****************\n");
	printf("****1. play*****\n");
	printf("****0. exit*****\n");
}
void game()
{
	char show[32][18] = { '0'};
	char mine[32][18] = { '0'};
	DiffChoice();
	InitBoard(show, '*');
	InitBoard(mine, '0');
	SetMine(mine);
	//Display(mine);
	Display(show);
	FindMine(mine,show);

}
void test()
{
	int input = 0;
	do
	{
		menu();
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();
			break;
		case 0:
			printf("成功退出\n");
			break;
		default:
			printf("输入错误,请重新输入\n");
			break;
		}
	} while (input);
}
int main()
{
	srand((unsigned int)time(NULL));
	test();
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2026-01-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、项目概述
  • 二、项目架构设计
    • 2.1 文件结构说明
    • 2.2 全局变量设计
  • 三、核心模块功能详解
    • 3.1 难度选择模块 (DiffChoice)
    • 3.2 棋盘初始化模块 (InitBoard)
    • 3.3 地雷布置模块 (SetMine)
    • 3.4 显示模块 (Display)
    • 3.5 地雷检测模块 (GetMineCount)
    • 3.6 递归展开模块 (ExpandMine)
    • 3.7 标记功能模块 (MineMake)
    • 3.8 主游戏逻辑模块 (FindMine)
  • 四、源代码
    • 4.1 game.h
    • 4.2 game.c
    • 4.3 test.c
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档