本文详细解析一个完整的C语言扫雷游戏实现,采用模块化编程思想,分为game.h、test.c、game.c三个文件。代码具备完整的游戏功能:难度选择、棋盘初始化、地雷布置、玩家交互、递归展开等核心玩法。
扫雷游戏项目结构:
├── game.h // 头文件:函数声明和全局变量声明
├── test.c // 主测试文件:游戏流程控制
└── game.c // 游戏核心功能实现// 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),避免边界判断时的数组越界问题。
DiffChoice)void DiffChoice() {
// 提供三种难度选择
// 1. 简单:9x9棋盘,10个雷
// 2. 中等:16x16棋盘,40个雷
// 3. 困难:30x16棋盘,99个雷
}创新点:
system("cls")清屏提供更好的交互体验ROWS = ROW + 2和COLS = COL + 2,简化后续边界处理InitBoard)void InitBoard(char arr[][18], char set) {
// 初始化整个扩展棋盘为指定字符set
// 雷区棋盘用'0'初始化
// 显示棋盘用'*'初始化
}关键技术:二维数组作为函数参数传递时,需要指定第二维的大小。这里固定为18,因为最大列数为16+2=18。
SetMine)void SetMine(char mine[][18]) {
// 使用随机数生成地雷位置
// 地雷用字符'1'表示,非雷区用'0'表示
// 确保不重复布置地雷
}算法亮点:
srand((unsigned int)time(NULL))确保随机性mine[x][y] == '0'检查避免重复布置Display)void Display(char arr[][18]) {
// 显示游戏棋盘
// 打印行列号便于玩家定位
// 显示已标记雷数/总雷数
}用户体验优化:
%-3d和%-3c格式化输出,对齐棋盘GetMineCount)static int GetMineCount(char mine[][18], int x, int y) {
// 计算坐标(x,y)周围8个格子的地雷总数
// 利用字符'0'和'1'的ASCII码差值计算
}巧妙的实现:
count += mine[i][j] - '0'; // 字符'1' - '0' = 1,字符'0' - '0' = 0ExpandMine)void ExpandMine(char mine[][18], char show[][18], int x, int y, int* win) {
// 递归展开周围没有地雷的区域
// 实现扫雷游戏的核心"空白展开"功能
}递归逻辑:
win计数器递增MineMake)void MineMake(char show[][18], int x, int y) {
// 标记/取消标记可能的地雷位置
// 用'F'表示标记的旗帜
// 限制标记数量不超过总雷数
}游戏机制:玩家可以通过标记功能记录可疑的地雷位置,增强策略性。
FindMine)void FindMine(char mine[][18], char show[][18]) {
// 游戏主循环:处理玩家输入
// 提供两种操作:1-排查雷 2-标记雷
// 包含首点击保护机制
}首点击保护机制:
if (first_click) {
first_click = 0;
if (mine[x][y] == '1') { // 如果第一次点击就是雷
// 将雷移到其他安全位置
// 确保玩家不会一开始就失败
}
}胜利条件:当win(已排查的安全格子数)等于ROW*COL - Mine(总格子数减去雷数)时,游戏胜利。
#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]);#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");
}
}#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();
}