扫雷C语言如何实现(含递归展开)

Posted 水瓶water_ping

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了扫雷C语言如何实现(含递归展开)相关的知识,希望对你有一定的参考价值。

目录

扫雷介绍

实现思路

实现流程

代码分解

棋盘定义

交互菜单menu()

初始化棋盘init_board()

打印棋盘display()

布置雷set_mine()

返回该坐标周围一圈8个坐标的雷的个数count_mine()

递归展开super_open_mine()

排查雷sweep_mine()

判断棋盘上还有多少个“雷”count_show_mine()

游戏实现函数game()

测试函数test()

主调函数main()

代码总览


扫雷介绍


        扫雷游戏就是要把所有非地雷的格子揭开即胜利,踩到地雷格子就算失败。 游戏主区域由很多个方格组成, 使用鼠标左键随机点击一个方格,方格即被打开并显示出方格中的数字,方格中数字则表示其周围的8个方格隐藏了几颗雷。

在C语言中我们用输入坐标的方式代替鼠标点击。

本文未介绍功能:(不是不想介绍,只是博主还未掌握能力,主要还是因为懒

  1. 交互难度选择功能
  2. 标记雷功能
  3. 标记雷后选择坐标展开功能

实现思路


        首先我们使用三个文件包含整个程序

  • Mine.h
  • Mine.c
  • test.c

Mine.h头文件包含代码实现所需所有头文件及全局变量

Mine.c源文件包含代码实现的主要函数

test.c源文件负责测试代码

实现流程

  1. 打印交互菜单
  2. 初始化棋盘
  3. 布置雷
  4. 扫雷(递归展开)
  5. 判断胜利
  6. 胜利游戏结束,否则循环第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. 该坐标处不是雷, != '1'
  2. 该坐标周围八个坐标没有雷,count = 0
  3. 该坐标没有被展开过,!= ' '

//递归展开
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;

以上是关于扫雷C语言如何实现(含递归展开)的主要内容,如果未能解决你的问题,请参考以下文章

c语言扫雷(含递归清场)

c语言期中项目实战二—扫雷递归版,思路分析+代码注释

C语言项目2------------------扫雷(带for循环递归展开和标记功能)

“纯C”实现——扫雷游戏(递归实现展开功能)

几段代码,让你用递归解决C语言扩展排雷(扫雷)

C语言实现简易版扫雷