C语言实现扫雷:从思路到源码的详细教程
本文详细解析如何使用C语言从零开始实现经典的Windows扫雷游戏。文章涵盖了项目结构设计、二维数组的巧妙运用(通过扩大数组防止越界)、棋盘初始化与打印、随机布雷算法以及坐标排查逻辑,并提供了包含game.h、game.c和test.c的完整可运行代码。
设计思路与核心逻辑
在实现扫雷游戏之前,首先需要理清数据存储的方式。扫雷棋盘通常为9×9大小,若直接定义 char mine[9][9] 来存储地雷信息,在处理边界格子周围的雷数统计时,访问相邻8个位置极易发生数组越界。为了解决这一问题,我们将实际使用的棋盘定义为11×11的二维数组,即 ROWS (ROW + 2) 和 COLS (COL + 2),而玩家看到的区域仅为中间的9×9部分。这种“扩边”策略能有效避免越界检测逻辑。
同时,为了区分“地雷信息”和“玩家排查出的状态”,我们需要两个独立的数组:
char mine[11][11]:用于后台存储雷的位置,字符'0'表示无雷,'1'表示有雷。char show[11][11]:用于展示给玩家的界面,初始状态全部填充'*'表示未探索,当安全时该位置将显示周围一圈的雷数(数字字符)。
游戏的核心流程包括:初始化棋盘 → 随机布置地雷 → 玩家输入坐标排查 → 判定胜负。
具体操作顺序
第1步:构建游戏主框架与菜单
目的:提供重复游戏的循环机制和用户交互入口。
文件:test.c
动作:使用 do-while 循环包裹菜单和 switch-case 结构,实现选择“1”开始游戏、“0”退出游戏的功能。在游戏开始前调用 srand((unsigned int)time(NULL)) 以生成不同的随机序列。
void menu()
{
printf("****************************\n");
printf("******** 1. play *******\n");
printf("******** 0. exit *******\n");
printf("****************************\n");
}
int main()
{
int input = 0;
srand((unsigned int)time(NULL)); // 必须放在循环外
do {
menu();
printf("请选择(1/0):>");
scanf("%d", &input);
switch (input)
{
case 1: game(); break;
case 0: printf("退出游戏\n"); break;
default: printf("选择错误!\n"); break;
}
} while (input); // 输入非0值继续
return 0;
}
第2步:定义棋盘尺寸与头文件声明
目的:统一管理常量,方便后续修改棋盘难度。
文件:game.h
参数: #define ROW 9, #define COL 9 为实际大小;#define ROWS (ROW + 2), #define COLS (COL + 2) 为物理数组大小;#define EASY_COUNT 10 为地雷总数。
函数声明: 包括初始化 init_board、打印 show_board、布雷 set_mine 和排查 find_mine。
#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
// ... 函数原型声明 ...
第3步:初始化与打印棋盘
目的:准备空白的游戏面板。
文件:game.c
动作:遍历整个11×11的物理数组。对于mine数组填入字符'0',对于show数组填入字符'*'。打印函数只遍历1到ROW及1到COL的区域,并在输出两侧添加行号和列标以提升可读性。
// 打印效果示意
/*
------------扫雷------------
0 1 2 3 4 5 6 7 8 9
1 * * * * * * * * * *
2 * * * * * * * * * *
...
*/
第4步:随机布置地雷
目的:在mine数组中随机生成指定数量的'1'。
文件:game.c
动作:使用 while(count) 循环。每次 x = rand() % row + 1,y = rand() % col + 1 生成有效范围内的坐标。若该位置当前为'0',则置为'1'并将计数器减1,直至布完所有雷。此方法简单直观,但效率较低(存在重复生成的概率),对于小规模棋盘足够适用。
第5步:排查雷与计算周边雷数
目的:处理用户输入,更新show数组并判定输赢。
文件:game.c
动作:
1. 检查坐标合法性(1~9)。
2. 检查该坐标是否已被排查过(是否为'*')。
3. 若踩中雷(mine[x][y]=='1'),游戏结束,显示全图。
4. 若安全,计算周围8个格子的雷总数。由于mine数组存的是字符,利用ASCII码差值:sum = 周围字符相加 - 8*'0'。计算结果即为真实雷数。
5. 将 count+'0' 存入 show[x][y] 并打印。
6. 胜利条件:已找到的安全格子数等于总格子数减去雷数(win == row*col - EASY_COUNT)。
static int get_mine_count(char mine[ROWS][COLS], int x, int y)
{
return mine[x - 1][y] + mine[x - 1][y - 1] + mine[x][y - 1] +
mine[x + 1][y - 1] + mine[x + 1][y] + mine[x + 1][y + 1] +
mine[x][y + 1] + mine[x - 1][y + 1] - 8 * '0';
}
完整代码汇总
以下是整合后的三个主要源文件,可直接编译运行(需注意编译器对 _CRT_SECURE_NO_WARNINGS 的支持情况)。
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 init_board(char arr[ROWS][COLS], int rows, int cols, char set);
void show_board(char arr[ROWS][COLS], int row, int col);
void set_mine(char mine[ROWS][COLS], int row, int col);
void find_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
game.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
void init_board(char arr[ROWS][COLS], int rows, int cols, char set)
{
int i, j;
for (i = 0; i < rows; ++i)
for (j = 0; j < cols; ++j)
arr[i][j] = set;
}
void show_board(char arr[ROWS][COLS], int row, int col)
{
int i, j;
printf("------------扫雷------------\n");
for (i = 0; i <= col; ++i) printf("%d ", i); // 打印列标
printf("\n");
for (i = 1; i <= row; ++i)
{
printf("%d ", i); // 打印行号
for (j = 1; j <= col; ++j)
printf("%c ", arr[i][j]);
printf("\n");
}
printf("------------扫雷------------\n");
}
void set_mine(char mine[ROWS][COLS], int row, int col)
{
int count = EASY_COUNT, x, y;
while (count)
{
x = rand() % row + 1;
y = rand() % col + 1;
if ('0' == mine[x][y]) { mine[x][y] = '1'; --count; }
}
}
static int get_mine_count(char mine[ROWS][COLS], int x, int y)
{
return mine[x - 1][y] + mine[x - 1][y - 1] + mine[x][y - 1] +
mine[x + 1][y - 1] + mine[x + 1][y] + mine[x + 1][y + 1] +
mine[x][y + 1] + mine[x - 1][y + 1] - 8 * '0';
}
void find_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int x, y, 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])
{
if ('1' == mine[x][y])
{
printf("很遗憾,你被炸死了\n");
show_board(mine, row, col); break;
}
else
{
int count = get_mine_count(mine, x, y);
show[x][y] = count + '0';
show_board(show, row, col);
++win;
}
}
else printf("该坐标已被排查\n");
}
else printf("坐标非法,重新输入\n");
}
if (win == row * col - EASY_COUNT)
{
printf("恭喜你,排雷成功\n");
show_board(mine, row, col);
}
}
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][COLS] = {0};
init_board(mine, ROWS, COLS, '0');
init_board(show, ROWS, COLS, '*');
set_mine(mine, ROW, COL);
show_board(show, ROW, COL); // 不显示mine的内容
find_mine(mine, show, ROW, COL);
}
int main(){
int input = 0;
srand((unsigned int)time(NULL));
do {
menu();
printf("请选择(1/0):>");
scanf("%d", &input);
switch (input){
case 1: game(); break;
case 0: printf("退出游戏\n"); break;
default: printf("选择错误!\n"); break;
}
} while (input);
return 0;
}
以上就是C语言实现经典windows游戏扫雷的示例代码的详细内容,更多关于C语言扫雷游戏的资料请关注本站其它相关文章!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













