【扫雷】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;
}

上一篇:mysql之savepoint


下一篇:回溯法练习