根据我不久前发布的扫雷基础版本,我做了一个扫雷完整版,有可选难度,未使用变长数组,可自定义游戏。具体内容写在代码和注释里。
创建test文件,写好整个程序的框架。这样可以让我们写代码具有明确的目的性。
#include "game.h"
//用于标记第一步不会踩雷
int FIRSTMINE = 0;
void game(int rows, int cols, int counts)
{
//创建两个二维数组mine和show
char** mine = mine_creat(rows, cols);
char** show = show_creat(rows, cols);
InitBoard(mine, rows, cols, '0');
InitBoard(show, rows, cols, '*');
system("cls");
DisplayBoard(show, rows, cols);
SetMine(mine, rows, cols, counts);
FindMine(mine, show, rows, cols, counts);
//释放内存
for (int i = 0; i < rows; i++)
{
free(mine[i]);
}
free(mine);
//释放内存
for (int i = 0; i < rows; i++)
{
free(show[i]);
}
free(show);
}
int main()
{
int input1;
int input2;
int ROW4, COL4, COUNT4;
do {
// 打印菜单
print_menu1();
// 获取用户选择
printf("请选择是否进行游戏:>>");
scanf("%d", &input1);
// 检查用户选择是否合法
if (input1 == 1 || input1 == 0) {
do {
system("cls");
// 打印子菜单
print_menu2();
// 获取用户选择
printf("请选择游戏难度:>>");
scanf("%d", &input2);
// 根据用户选择执行相应操作,值得注意的是,当game函数结束时,我们需要把FIRSTMINE重新赋值为零,否则第一步不会踩雷只能维持一局游戏
switch (input2) {
case 1:
game(ROW1, COL1, COUNT1);
FIRSTMINE = 0;
break;
case 2:
game(ROW2, COL2, COUNT2);
FIRSTMINE = 0;
break;
case 3:
game(ROW3, COL3, COUNT3);
FIRSTMINE = 0;
break;
case 4:
printf("请输入尺寸 X Y 和雷的数量 N :");
scanf("%d %d %d", &ROW4, &COL4, &COUNT4);
ROW4 += 2;
COL4 += 2;
game(ROW4, COL4, COUNT4);
FIRSTMINE = 0;
break;
default:
printf("选项错误,请重新选择!!!\n");
}
} while (input2 != 1 && input2 != 2 && input2 != 3 && input2 != 4);
}
else {
printf("选项错误,请重新选择!!!\n");
}
} while (input1);
return 0;
}
创建game.h文件,同时我们定义了三种难度下的棋盘的尺寸以及对应的雷的数量。当然了,自定义模式下的尺寸和雷的数量是交给用户决定的。接下来我们规定了每一种函数的参数以及其返回类型和我们希望的函数所具备的功能。
#define _CRT_SECURE_NO_WARNINGS
#pragma once
#define ROW1 11
#define COL1 11
#define COUNT1 10
#define ROW2 18
#define COL2 18
#define COUNT2 40
#define ROW3 18
#define COL3 32
#define COUNT3 99
#include <stdio.h>
#include <time.h>
#include <windows.h>
#include <stdbool.h>
#include <stdlib.h>
//打印菜单一
void print_menu1();
//打印菜单二
void print_menu2();
//通过动态内存创建mine数组,并初始化为 '0'
char** mine_creat(int x, int y);
//通过动态内存创建show数组 ,并初始化为 '0'
char** show_creat(int x, int y);
// 初始化棋盘
void InitBoard(char** p, int rows, int cols, char ch);
// 展示棋盘
void DisplayBoard(char** board, int rows, int cols);
// 设置雷
void SetMine(char** board, int rows, int cols, int counts);
//玩家指定坐标进行排雷或标雷
void FindMine(char** mine_board, char** show_board, int rows, int cols, int counts);
//游戏的实现
void game(int rows, int cols, int counts);
创建game.c文件
#include "game.h"
//用于标记第一步不会踩雷
extern FIRSTMINE;
//打印菜单一
void print_menu1()
{
printf("***************************\n");
printf("****** 1. 开始游戏 ******\n");
printf("****** 0. 退出游戏 ******\n");
printf("***************************\n");
}
//打印菜单二
void print_menu2()
{
printf("***************************\n");
printf("****** 1. 初级难度 ******\n");
printf("****** 2. 中级难度 ******\n");
printf("****** 3. 高级难度 ******\n");
printf("****** 4. 自由定义 ******\n");
printf("***************************\n");
}
//通过动态内存创建mine数组,并初始化为 '0'
char** mine_creat(int x, int y)
{
char** mine = (char**)malloc(x * sizeof(char*));
for (int i = 0; i < x; i++)
{
mine[i] = (char*)malloc(y * sizeof(char));
for (int j = 0; j < y; j++)
{
mine[i][j] = '0';
}
}
return mine;
}
//通过动态内存创建show数组 ,并初始化为 '0'
char** show_creat(int x, int y)
{
char** show = (char**)malloc(x * sizeof(char*));
for (int i = 0; i < x; i++)
{
show[i] = (char*)malloc(y * sizeof(char));
for (int j = 0; j < y; j++)
{
show[i][j] = '0';
}
}
return show;
}
//初始化棋盘,初始化为 ch
void InitBoard(char** board, int row, int col, char ch)
{
for (int i = 0; i < row; ++i)
{
for (int j = 0; j < col; ++j)
{
board[i][j] = ch;
}
}
}
// 展示棋盘
void DisplayBoard(char** board, int rows, int cols)
{
int i = 0;
int j = 0;
printf("游戏提示,需要扫开所有的非雷区域后才可以胜利哦!\n\n");
//打印横坐标
printf(" ");
for (j = 1; j < cols - 1; ++j)
{
if (j < 9)
{
printf(" %d ", j);
}
else if (j == 9)
{
printf(" %d ", j);
}
else
{
printf("%d ", j);
}
}
printf("\n");
//打印横向上边界
printf("-----");
for (i = 1; i < cols - 1; ++i)
{
printf("---");
}
printf("\n");
for (i = 1; i < rows - 1; ++i)
{
if (i <= 9)
{
printf("%d | ", i);
}
else
{
printf("%d| ", i);
}
for (j = 1; j < cols - 1; ++j)
{
if (j == cols - 2)
{
printf(" %c |", board[i][j]);
}
else if (j == 1)
{
printf("%c ", board[i][j]);
}
else
{
printf(" %c ", board[i][j]);
}
}
printf("\n");
}
//打印横向下边界
printf("-----");
for (i = 1; i < cols - 1; ++i)
{
printf("---");
}
printf("\n");
}
//放置雷
void SetMine(char** mine_board, int rows, int cols, int counts)
{
// 设置的雷的数量
while (counts)
{
int x = rand() % rows - 1;
int y = rand() % cols - 1;
if (x > 0 && y > 0 && x <= rows - 1 && y <= cols - 1)
{
if (mine_board[x][y] == '0') // 确保该位置没有雷
{
mine_board[x][y] = 'M'; // 将该位置设置为雷
counts--;
}
}
}
}
// 检查周围的雷的数量
static int GetMineCount(char** mine_board, int rows, int cols, int x, int y)
{
int count = 0;
int i = 0;
int j = 0;
for (i = x - 1; i <= x + 1; ++i)
{
for (j = y - 1; j <= y + 1; ++j)
{
if (i >= 0 && i < rows && j >= 0 && j < cols && mine_board[i][j] == 'M')
{
count++;
}
}
}
return count;
}
//实现扫雷的递归展开
static void OpenBoard(char** mine_board, char** show_board, int rows, int cols, int x, int y)
{
int i = 0;
int j = 0;
int count = GetMineCount(mine_board, rows, cols, x, y);
//如果点位(x,y)周围的雷的数量是零,则进入该语句
if (count == 0)
{
show_board[x][y] = ' ';//防止重复递归导致栈溢出
for (i = x - 1; i <= x + 1; i++)
{
for (j = y - 1; j <= y + 1; j++)
{
if (i > 0 && j > 0 && i < rows - 1 && j < cols - 1 && (show_board[i][j] == '*') && (show_board[i][j] != '#') && (mine_board[i][j] != '1'))
OpenBoard(mine_board, show_board, rows, cols, i, j);//递归,连续展开
}
}
}
else
{
show_board[x][y] = count + '0';
}
}
//统计全局当中还未被排开的雷的总数
static int Number(char** board, int rows, int cols)
{
int i = 0;
int j = 0;
int count = 0;
for (i = 1; i < rows - 1; i++)
{
for (j = 1; j < cols - 1; j++)
{
if (board[i][j] == '*' || board[i][j] == '#')//未被展开的点和已经被标记的点都要算上去
{
count++;
}
}
}
return count;
}
//玩家指定坐标进行排雷或标雷
void FindMine(char** mine_board, char** show_board, int rows, int cols, int counts)
{
int m = 0;
int x = 0;
int y = 0;
int x0 = 0;
int y0 = 0;
int number = Number(show_board, rows, cols);
while (number > counts)
{
printf("要扫雷请输入1,要标雷请输入0,请输入:>>");
scanf("%d", &m);
if (m == 0)
{
printf("已进入标记模式,请输入要标雷的位置坐标(格式:x y):");
scanf("%d %d", &x0, &y0);
if (x0 >= 0 && x0 < rows - 1 && y0 >= 0 && y0 < cols - 1)
{
show_board[x0][y0] = '#';
system("cls");
DisplayBoard(show_board, rows, cols);
}
else
{
printf("坐标超出范围,请重新输入。\n");
}
// 每次排雷结束后计算还未被扫到的数量,当未扫到的数量等于雷的数量,则游戏成功
number = Number(show_board, rows, cols);
if (number == counts)
break;
}
else if (m == 1)
{
printf("请输入要排查的坐标(格式: x y ):");
scanf("%d %d", &x, &y);
// 如果第一步是雷,则把第一个雷清除掉,重新再放置一个雷,放置结束后把标志FIRSTMINE赋值为1
if ((mine_board[x][y] == 'M') && (FIRSTMINE == 0))
{
mine_board[x][y] = '0';
SetMine(mine_board, rows, cols, counts);
FIRSTMINE = 1;
}
FIRSTMINE = 1;//第一步结束后把标志FIRSTMINE赋值为1
if (x >= 0 && x < rows - 1 && y >= 0 && y < cols - 1)
{
if (mine_board[x][y] == 'M') // 踩到雷,游戏结束
{
show_board[x][y] = mine_board[x][y];
printf("很遗憾,你踩到了雷!游戏结束。\n");
break;
}
else
{
show_board[x][y] = mine_board[x][y];
if (mine_board[x][y] == '0') // 如果排雷的位置是0,翻开周围的位置
{
OpenBoard(mine_board, show_board, rows, cols, x, y);
}
system("cls");
DisplayBoard(show_board, rows, cols);
}
}
else
{
printf("坐标超出范围,请重新输入。\n");
}
// 每次排雷结束后计算还未被扫到的数量,当未扫到的数量等于雷的数量,则游戏成功
number = Number(show_board, rows, cols);
}
else
{
printf("输入错误,请重新输入。\n");
}
}
if (number == counts)
{
printf("\n恭喜你游戏成功!!!!\n");
}
}
游戏的运行: