ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

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

2021-11-17 21:58:00  阅读:197  来源: 互联网

标签:COLS ROWS 递归 show int mine C语言 扫雷 printf


目录

扫雷介绍

实现思路

实现流程

代码分解

棋盘定义

交互菜单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;
}

标签:COLS,ROWS,递归,show,int,mine,C语言,扫雷,printf
来源: https://blog.csdn.net/m0_61198445/article/details/121367239

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有