Python 四大迷宫生成算法实现(3): 递归分割算法

递归分割算法简介

先介绍下算法使用的地图,地图最外围默认是一圈墙,其中白色单元是迷宫单元,黑色单元是墙。示例地图的宽度和长度都为11。

  • 第一个图用十字将地图分割成4个小矩阵,可以看到这个十字交点所在的位置(WALL_X, WALL_Y),在这个图中为(6,4),注意WALL_X, WALL_Y都必须是偶数。同时迷宫的长度和宽度必须为奇数。
    迷宫地图1
  • 第二图在4个小矩阵的相邻四条边上随机选择三条边来打通(每条边上去掉一个单元的墙),使得这4个小矩阵连通。
    迷宫地图2

这是一个递归算法,重复调用算法直到矩阵的长度或宽度<=1:
1 算法调用参数是一个矩阵的位置(x, y, width, height)
2 判断矩阵是否不能继续分割(长度或宽度<=1)

  • 如果能继续分割
    • 随机选择十字交点的位置(WALL_X, WALL_Y), 将这条十字所在的单元都设为墙,分割成4个更小的矩阵.
    • 十字所在的四条边中随机选择三条边来打通(每条边上去掉一个单元的墙)
    • 对于4个小矩阵递归调用算法
  • 否则,直接返回

关键代码介绍

保存基本信息的地图类

地图类用于保存和获取迷宫算法使用的基础地图信息。

  1. 先创建一个map类, 初始化参数设置地图的长度和宽度,并设置保存地图信息的二维数据map的值为0, 值为0表示该单元可以移动,值为1表示该单元是墙。定义在算法中用到的Enum 类MAP_ENTRY_TYPE 和 WALL_DIRECTION。
class MAP_ENTRY_TYPE(Enum):
	MAP_EMPTY = 0,
	MAP_BLOCK = 1,

class WALL_DIRECTION(Enum):
	WALL_LEFT = 0,
	WALL_UP = 1,
	WALL_RIGHT = 2,
	WALL_DOWN = 3,

class Map():
	def __init__(self, width, height):
		self.width = width
		self.height = height
		self.map = [[0 for x in range(self.width)] for y in range(self.height)]
  1. 在map类中添加将一个map单元设置为某个值的函数,判断某个单元是否是墙的函数,和是否是有效单元的函数。
	def setMap(self, x, y, value):
		if value == MAP_ENTRY_TYPE.MAP_EMPTY:
			self.map[y][x] = 0
		elif value == MAP_ENTRY_TYPE.MAP_BLOCK:
			self.map[y][x] = 1
	
	def isMovable(self, x, y):
		return self.map[y][x] != 1
	
	def isValid(self, x, y):
		if x < 0 or x >= self.width or y < 0 or y >= self.height:
			return False
		return True
  1. 在map类中添加一个显示地图的函数,可以看到,这边只是简单的打印出所有节点的值,值为1时打印’#"表示强,或1的意思上面已经说明,在后面显示寻路算法结果时,会使用到值2,表示一条从开始节点到目标节点的路径。
	def showMap(self):
		for row in self.map:
			s = ''
			for entry in row:
				if entry == 0:
					s += ' 0'
				elif entry == 1:
					s += ' #'
				else:
					s += ' X'
			print(s)

算法主函数介绍

doRecursiveDivision 函数 先调用setMap函数将地图最外围四条边都设置为墙。传入recursiveDivision 函数的参数是矩阵的位置(x, y, width, height),假如地图的宽度是31,长度是21,则传入参数为(1,1,29,19)。

def doRecursiveDivision(map):
	# draw four margin wall lines
	for x in range(0, map.width):
		map.setMap(x, 0, MAP_ENTRY_TYPE.MAP_BLOCK)
		map.setMap(x, map.height-1, MAP_ENTRY_TYPE.MAP_BLOCK)
	
	for y in range(0, map.height):
		map.setMap(0, y, MAP_ENTRY_TYPE.MAP_BLOCK)
		map.setMap(map.width-1, y, MAP_ENTRY_TYPE.MAP_BLOCK)
		
	recursiveDivision(map, 1, 1, map.width - 2, map.height - 2, MAP_ENTRY_TYPE.MAP_BLOCK)

recursiveDivision 函数就是上面算法主循环的实现。

# recursive division algorithm
def recursiveDivision(map, x, y, width, height, wall_value):	
	if width <= 1 or height <= 1:
		return
	
	#generate a row and a column wall index, they must be even number
	wall_x, wall_y = (getWallIndex(x, width), getWallIndex(y, height))
	
	#set horizontal and vertical lines to wall
	for i in range(x, x+width):
		map.setMap(i, wall_y, wall_value)
	for i in range(y, y+height):
		map.setMap(wall_x, i, wall_value)
	
	#create three holes
	generateHoles(map, x, y, width, height, wall_x, wall_y)
	
	recursiveDivision(map, x, y, wall_x - x, wall_y - y, wall_value)
	recursiveDivision(map, x, wall_y + 1, wall_x - x, y + height - wall_y -1, wall_value)
	recursiveDivision(map, wall_x + 1, y, x + width - wall_x -1, wall_y - y, wall_value)
	recursiveDivision(map, wall_x + 1, wall_y + 1, x + width - wall_x -1, y + height - wall_y -1, wall_value)

getWallIndex函数获取指定范围的随机偶数。
generateHoles函数,选择三条边打通,有个特别要注意的是,当外层的矩阵和十字墙相邻的单元是连通的情况下,比如下图所示的红色单元,这时候红色单元必须要打通,不然会导致迷宫不连通
在这里插入图片描述

	# start must be a odd number, wall_index must be a even number
	def getWallIndex(start, length):
		assert length >= 3
		wall_index = randint(start + 1, start + length - 2)
		if wall_index % 2 == 1:
			wall_index -= 1
		return wall_index
	
	# must check adjacent entry of four margin entries, 
	# if adjacent entry is movable, must set the margin entry as the hole
	def generateHoles(map, x, y, width, height, wall_x, wall_y):
		holes = []
		
		hole_entrys = [(randint(x, wall_x -1), wall_y), (randint(wall_x + 1, x + width -1), wall_y),
						(wall_x, randint(y, wall_y -1)), (wall_x, randint(wall_y + 1, y + height - 1))]
		margin_entrys = [(x, wall_y), (x+width-1, wall_y), (wall_x, y), (wall_x, y + height-1)]
		adjacent_entrys = [(x-1, wall_y), (x+width, wall_y), (wall_x, y - 1), (wall_x, y + height)]
		for i in range(4):
			adj_x, adj_y = (adjacent_entrys[i][0], adjacent_entrys[i][1])
			if map.isValid(adj_x, adj_y) and map.isMovable(adj_x, adj_y):
				map.setMap(margin_entrys[i][0], margin_entrys[i][1], MAP_ENTRY_TYPE.MAP_EMPTY)
			else:
				holes.append(hole_entrys[i])

		ignore_hole = randint(0, len(holes)-1)
		for i in range(0, len(holes)):
			if i != ignore_hole:
				map.setMap(holes[i][0], holes[i][1], MAP_ENTRY_TYPE.MAP_EMPTY)

代码的初始化

可以调整地图的长度,宽度,注意长度和宽度必须为奇数。

def run():
	WIDTH = 31
	HEIGHT = 21
	map = Map(WIDTH, HEIGHT)
	doRecursiveDivision(map)
	map.showMap()	
	
if __name__ == "__main__":
	run()

执行的效果图如下,迷宫中’#‘表示墙,空格’ '表示通道。
在这里插入图片描述

完整代码

使用python3.7编译

from random import randint, choice
from enum import Enum

class MAP_ENTRY_TYPE(Enum):
	MAP_EMPTY = 0,
	MAP_BLOCK = 1,

class WALL_DIRECTION(Enum):
	WALL_LEFT = 0,
	WALL_UP = 1,
	WALL_RIGHT = 2,
	WALL_DOWN = 3,

class Map():
	def __init__(self, width, height):
		self.width = width
		self.height = height
		self.map = [[0 for x in range(self.width)] for y in range(self.height)]
	
	def setMap(self, x, y, value):
		if value == MAP_ENTRY_TYPE.MAP_EMPTY:
			self.map[y][x] = 0
		elif value == MAP_ENTRY_TYPE.MAP_BLOCK:
			self.map[y][x] = 1
	
	def isMovable(self, x, y):
		return self.map[y][x] != 1
	
	def isValid(self, x, y):
		if x < 0 or x >= self.width or y < 0 or y >= self.height:
			return False
		return True

	def showMap(self):
		for row in self.map:
			s = ''
			for entry in row:
				if entry == 0:
					s += '  '
				elif entry == 1:
					s += ' #'
				else:
					s += ' X'
			print(s)

# recursive division algorithm
def recursiveDivision(map, x, y, width, height, wall_value):
	# start must be a odd number, wall_index must be a even number
	def getWallIndex(start, length):
		assert length >= 3
		wall_index = randint(start + 1, start + length - 2)
		if wall_index % 2 == 1:
			wall_index -= 1
		return wall_index
	
	# must check adjacent entry of four margin entries, 
	# if adjacent entry is movable, must set the margin entry as the hole
	def generateHoles(map, x, y, width, height, wall_x, wall_y):
		holes = []
		
		hole_entrys = [(randint(x, wall_x -1), wall_y), (randint(wall_x + 1, x + width -1), wall_y),
						(wall_x, randint(y, wall_y -1)), (wall_x, randint(wall_y + 1, y + height - 1))]
		margin_entrys = [(x, wall_y), (x+width-1, wall_y), (wall_x, y), (wall_x, y + height-1)]
		adjacent_entrys = [(x-1, wall_y), (x+width, wall_y), (wall_x, y - 1), (wall_x, y + height)]
		for i in range(4):
			adj_x, adj_y = (adjacent_entrys[i][0], adjacent_entrys[i][1])
			if map.isValid(adj_x, adj_y) and map.isMovable(adj_x, adj_y):
				map.setMap(margin_entrys[i][0], margin_entrys[i][1], MAP_ENTRY_TYPE.MAP_EMPTY)
			else:
				holes.append(hole_entrys[i])

		ignore_hole = randint(0, len(holes)-1)
		for i in range(0, len(holes)):
			if i != ignore_hole:
				map.setMap(holes[i][0], holes[i][1], MAP_ENTRY_TYPE.MAP_EMPTY)

	if width <= 1 or height <= 1:
		return
	
	#generate a row and a column wall index, they must be even number
	wall_x, wall_y = (getWallIndex(x, width), getWallIndex(y, height))
	
	#set horizontal and vertical lines to wall
	for i in range(x, x+width):
		map.setMap(i, wall_y, wall_value)
	for i in range(y, y+height):
		map.setMap(wall_x, i, wall_value)
	
	#create three holes
	generateHoles(map, x, y, width, height, wall_x, wall_y)
	
	recursiveDivision(map, x, y, wall_x - x, wall_y - y, wall_value)
	recursiveDivision(map, x, wall_y + 1, wall_x - x, y + height - wall_y -1, wall_value)
	recursiveDivision(map, wall_x + 1, y, x + width - wall_x -1, wall_y - y, wall_value)
	recursiveDivision(map, wall_x + 1, wall_y + 1, x + width - wall_x -1, y + height - wall_y -1, wall_value)

def doRecursiveDivision(map):
	# draw four margin wall lines
	for x in range(0, map.width):
		map.setMap(x, 0, MAP_ENTRY_TYPE.MAP_BLOCK)
		map.setMap(x, map.height-1, MAP_ENTRY_TYPE.MAP_BLOCK)
	
	for y in range(0, map.height):
		map.setMap(0, y, MAP_ENTRY_TYPE.MAP_BLOCK)
		map.setMap(map.width-1, y, MAP_ENTRY_TYPE.MAP_BLOCK)
		
	recursiveDivision(map, 1, 1, map.width - 2, map.height - 2, MAP_ENTRY_TYPE.MAP_BLOCK)

def run():
	WIDTH = 31
	HEIGHT = 21
	map = Map(WIDTH, HEIGHT)
	doRecursiveDivision(map)
	map.showMap()	
	
if __name__ == "__main__":
	run()
  • 5
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
递归算法寻路是一种常见的图论算法,可以用于解决迷宫问题等。下面是用Python实现递归算法寻路的示例代码: ``` def find_path(maze, start, end): """ 递归算法寻路 :param maze: 迷宫矩阵,0表示可以通过,1表示障碍 :param start: 起点坐标,(x, y) :param end: 终点坐标,(x, y) :return: 找到的路径,如果没有则返回None """ # 判断当前位置是否越界或者是障碍 if not (0 <= start[0] < len(maze) and 0 <= start[1] < len(maze[0]) and maze[start[0]][start[1]] == 0): return None # 如果当前位置就是终点,则直接返回终点坐标 if start == end: return [end] # 标记当前位置已经访问过 maze[start[0]][start[1]] = 1 # 递归寻找上下左右四个方向能否到达终点 for next_pos in [(start[0] + 1, start[1]), (start[0] - 1, start[1]), (start[0], start[1] + 1), (start[0], start[1] - 1)]: # 递归寻找路径 path = find_path(maze, next_pos, end) if path is not None: # 如果找到了路径,则将当前位置添加到路径中,并返回路径 return [start] + path # 如果无法到达终点,则返回None return None ``` 使用示例: ``` maze = [ [0, 0, 0, 0, 0], [1, 1, 0, 1, 0], [0, 0, 0, 0, 0], [0, 1, 1, 1, 1], [0, 0, 0, 0, 0] ] start = (0, 0) end = (4, 4) path = find_path(maze, start, end) if path is None: print("无法到达终点") else: print("找到路径:", path) ``` 输出结果: ``` 找到路径: [(0, 0), (0, 1), (0, 2), (0, 3), (1, 3), (2, 3), (3, 3), (4, 3), (4, 4)] ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值