目录
返回该坐标周围一圈8个坐标的雷的个数count_mine()
判断棋盘上还有多少个“雷”count_show_mine()
扫雷介绍
扫雷游戏就是要把所有非地雷的格子揭开即胜利,踩到地雷格子就算失败。 游戏主区域由很多个方格组成, 使用鼠标左键随机点击一个方格,方格即被打开并显示出方格中的数字,方格中数字则表示其周围的8个方格隐藏了几颗雷。
在C语言中我们用输入坐标的方式代替鼠标点击。
本文未介绍功能:(不是不想介绍,只是博主还未掌握能力,主要还是因为懒)
- 交互难度选择功能
- 标记雷功能
- 标记雷后选择坐标展开功能
实现思路
首先我们使用三个文件包含整个程序
- Mine.h
- Mine.c
- test.c
Mine.h头文件包含代码实现所需所有头文件及全局变量
Mine.c源文件包含代码实现的主要函数
test.c源文件负责测试代码
实现流程
- 打印交互菜单
- 初始化棋盘
- 布置雷
- 扫雷(递归展开)
- 判断胜利
- 胜利游戏结束,否则循环第4步
在定义棋盘的二维数组时我们需要定义两个数组;一个数组mine用来放置雷的位置(不显示),另一个数组show用来展示棋面!
代码分解
-
棋盘定义
//全局变量行和列
#define ROW 3
#define COL 3
//避免数组越界,在原有棋盘的基础上周围多一行和列
#define ROWS ROW+2
#define COLS COL+2
-
交互菜单menu()
//交互菜单
void menu()
{
printf("-----------------------\n");
printf("------ 【扫雷】 ------\n");
printf("------ 1.开始 ------\n");
printf("------ 0.退出 ------\n");
printf("-----------------------\n");
}
-
初始化棋盘init_board()
这里我们把show棋盘初始化为 '*'
把mine棋盘初始化为 '0'
//初始化棋盘
void init_board(char board[ROWS][COLS], int row, int col, char ch)
{
int i = 0;
int j = 0;
for (i = 0; i < ROWS; i++)
{
for (j = 0; j < COLS; j++)
{
board[i][j] = ch;
}
}
}
-
打印棋盘display()
//打印棋盘
void display(char show[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
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 ", show[i][j]);
}
printf("\n");
}
printf("\n");
}
-
布置雷set_mine()
//雷的数量
#define COUNT 1
//布置雷
void set_mine(char mine[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int count = COUNT;
while (count)
{
//获取随机数在棋盘上生成雷('1')
x = rand() % row + 1;
y = rand() % col + 1;
if (mine[x][y] == '0')
{
mine[x][y] = '1';
count--;
}
}
}
-
返回该坐标周围一圈8个坐标的雷的个数count_mine()
注意这里函数的返回类型是 static
//返回该坐标周围一圈雷的个数
static count_mine(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';//ASCII值与数字字符相差'0'
}
-
递归展开super_open_mine()
递归展开条件:
- 该坐标处不是雷, != '1'
- 该坐标周围八个坐标没有雷,count = 0
- 该坐标没有被展开过,!= ' '
//递归展开
void super_open_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{
//获取该坐标周围八个坐标雷数
int count = count_mine(mine, x, y);
if (count == 0 && show[x][y] != ' ')
{
show[x][y] = ' ';
if (x - 1 >= 0 && x <= ROW && y >= 0 && y <= COL && show[x - 1][y] == '*')
{
super_open_mine(mine, show, x - 1, y);
}
if (x + 1 >= 0 && x + 1 <= ROW && y >= 0 && y <= COL && show[x + 1][y] == '*')
{
super_open_mine(mine, show, x + 1, y);
}
if (x >= 0 && x <= ROW && y - 1 >= 0 && y - 1 <= COL && show[x][y - 1] == '*')
{
super_open_mine(mine, show, x, y - 1);
}
if (x >= 0 && x <= ROW && y + 1 >= 0 && y + 1 <= COL && show[x][y + 1] == '*')
{
super_open_mine(mine, show, x, y + 1);
}
}
else
{
show[x][y] = count + '0';
}
}
-
排查雷sweep_mine()
//排查雷
int sweep_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int count = 0;
printf("请输入扫雷坐标(行.列):>");
scanf("%d.%d", &x, &y);
if ((x >= 1 && x <= row) && (y >= 1 && y <= col))
{
if (mine[x][y] == '1')
{
return 1;
}
else
{
super_open_mine(mine, show, x, y);
display(show, ROW, COL);
return 0;
}
}
else
{
printf("输入坐标非法,请重新输入!\a\n\n");
}
return 0;
}
-
判断棋盘上还有多少个“雷”count_show_mine()
//判断棋盘上还有多少个“雷”
int count_show_mine(char show[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
int count = 0;
for (i = 1; i <= row; i++)
{
for (j = 1; j <=col; j++)
{
if (show[i][j] == '*')
{
count++;
}
}
}
return count;
}
-
游戏实现函数game()
//游戏实现函数
void game()
{
char mine[ROWS][COLS] = { 0 };
char show[ROWS][COLS] = { 0 };
init_board(mine, ROWS, COLS, '0');//mine初始化为'0'
init_board(show, ROWS, COLS, '*');//show初始化为'*'
set_mine(mine, ROW, COL);
//display(mine, ROW, COL);
display(show, ROW, COL);
while (1)
{
int ret = sweep_mine(mine, show, ROW, COL);
if (ret)
{
printf("\n你被炸死了,游戏结束!\n\a");
display(mine, ROW, COL);
break;
}
int key = count_show_mine(show, ROW, COL);
if (key == COUNT)
{
printf("WIN!\a\n");
break;
}
}
}
-
测试函数test()
//测试函数
void test()
{
srand((unsigned int)time(NULL));
int choose = -1;
do
{
menu();
scanf("%d", &choose);
switch (choose)
{
case 1:
printf("\n游戏开始:\n");
game();
break;
case 0:
printf("游戏结束...\n\a");
break;
default:
printf("输入有误,请重新输入!\n\a");
break;
}
} while (choose);
}
-
主调函数main()
//主调函数
int main(void)
{
test();
return 0;
}
代码总览
- Mine.h
#pragma once
#include <stdio.h>//printf(),scanf()函数头文件
#include <stdlib.h>//获取时间戳头文件
#include <time.h>//时间头文件
//全局变量行和列
#define ROW 3
#define COL 3
//避免数组越界,在原有棋盘的基础上周围多一行和列
#define ROWS ROW+2
#define COLS COL+2
//雷的数量
#define COUNT 1
//函数声明
void menu();
void init_board();
void display();
void set_mine();
int sweep_mine();
- Mine.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "Mine.h"
//交互菜单
void menu()
{
printf("-----------------------\n");
printf("------ 【扫雷】 ------\n");
printf("------ 1.开始 ------\n");
printf("------ 0.退出 ------\n");
printf("-----------------------\n");
}
//初始化棋盘
void init_board(char board[ROWS][COLS], int row, int col, char ch)
{
int i = 0;
int j = 0;
for (i = 0; i < ROWS; i++)
{
for (j = 0; j < COLS; j++)
{
board[i][j] = ch;
}
}
}
//打印棋盘
void display(char show[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
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 ", show[i][j]);
}
printf("\n");
}
printf("\n");
}
//布置雷
void set_mine(char mine[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int count = COUNT;
while (count)
{
//获取随机数在棋盘上生成雷('1')
x = rand() % row + 1;
y = rand() % col + 1;
if (mine[x][y] == '0')
{
mine[x][y] = '1';
count--;
}
}
}
//返回该坐标周围一圈雷的个数
static count_mine(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';//ASCII值与数字字符相差'0'
}
//递归展开
void super_open_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{
int count = count_mine(mine, x, y);
if (count == 0 && show[x][y] != ' ')
{
show[x][y] = ' ';
if (x - 1 >= 0 && x <= ROW && y >= 0 && y <= COL && show[x - 1][y] == '*')
{
super_open_mine(mine, show, x - 1, y);
}
if (x + 1 >= 0 && x + 1 <= ROW && y >= 0 && y <= COL && show[x + 1][y] == '*')
{
super_open_mine(mine, show, x + 1, y);
}
if (x >= 0 && x <= ROW && y - 1 >= 0 && y - 1 <= COL && show[x][y - 1] == '*')
{
super_open_mine(mine, show, x, y - 1);
}
if (x >= 0 && x <= ROW && y + 1 >= 0 && y + 1 <= COL && show[x][y + 1] == '*')
{
super_open_mine(mine, show, x, y + 1);
}
}
else
{
show[x][y] = count + '0';
}
}
//排查雷
int sweep_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int count = 0;
printf("请输入扫雷坐标(行.列):>");
scanf("%d.%d", &x, &y);
if ((x >= 1 && x <= row) && (y >= 1 && y <= col))
{
if (mine[x][y] == '1')
{
return 1;
}
else
{
super_open_mine(mine, show, x, y);
display(show, ROW, COL);
return 0;
}
}
else
{
printf("输入坐标非法,请重新输入!\a\n\n");
}
return 0;
}
//判断棋盘上还有多少个“雷”
int count_show_mine(char show[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
int count = 0;
for (i = 1; i <= row; i++)
{
for (j = 1; j <=col; j++)
{
if (show[i][j] == '*')
{
count++;
}
}
}
return count;
}
- test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "Mine.h"
//游戏实现函数
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);
//display(mine, ROW, COL);
display(show, ROW, COL);
while (1)
{
int ret = sweep_mine(mine, show, ROW, COL);
if (ret)
{
printf("\n你被炸死了,游戏结束!\n\a");
display(mine, ROW, COL);
break;
}
int key = count_show_mine(show, ROW, COL);
if (key == COUNT)
{
printf("WIN!\a\n");
break;
}
}
}
//测试函数
void test()
{
srand((unsigned int)time(NULL));
int choose = -1;
do
{
menu();
scanf("%d", &choose);
switch (choose)
{
case 1:
printf("\n游戏开始:\n");
game();
break;
case 0:
printf("游戏结束...\n\a");
break;
default:
printf("输入有误,请重新输入!\n\a");
break;
}
} while (choose);
}
//主调函数
int main(void)
{
test();
return 0;
}