项目三:基于A*搜索算法的迷宫游戏开发

一、实验要求:

1.要求随机生成一个迷宫,并求解迷宫;
2.要求游戏支持玩家走迷宫,和系统走迷宫路径两种模式。玩家走迷宫,通过键盘方向键控制,并在行走路径上留下痕迹;系统走迷宫路径要求基于A*算法实现,输出走迷宫的最优路径并显示。
3.设计交互友好的游戏图形界面。

二、迷宫问题具体分析

从起点出发,沿着某一方向前进,如果可以走通,则继续前进,否则原路退回,寻找其他路径。在迷宫中路径的选择方向只有四种,分别是上,下,左,右,我们可以通过键盘的W,A,S,D分别进行控制。我们分别用三种颜色对于迷宫中各种对象进行标记,走迷宫的对象为一种颜色,道路和墙壁为另外两种颜色。当走通一步时,我们应将此网格的颜色转变为通路应该有的颜色。根据这样的方法一直走到终点即为成功。

三、此开发过程中使用A*算法进行路径寻找

下面详细介绍一下A算法
A
算法是一种静态路网中求解最短路径最有效的直接搜索方法,也是许多其他问题的常用启发式算法。
公式表示为: f(n)=g(n)+h(n),
其中,f(n)是从初始状态经由状态n到目标状态的代价估计,g(n)是在状态空间中从初始状态到状态n的实际代价,h(n)是从状态n到目标状态的最佳路径的估计代价。
(对于路径搜索问题,状态就是图中的节点,代价就是距离>h(n)的选取保证找到最短路径(最优解的)条件,关键在于估价函数f(n)的选取(或者说h(n)的选取)。

A*算法在迷宫中的详细实现

1、Open和Closed表
A算法有两个重要的数据列表:一个记录下所有被考虑来寻找最短路径的方格(称为open列表)和一个记录下不会再被考虑的方格(称为closed 列表)。
在A
算法中,我们从起点开始,依次检查它的相邻方格,选取相邻方格然后继续向外扩展直到找到目的地,根据评价值选择方格。
2、把起点加入 open list 。
3、遍历 open list ,查找 F 值最小的节点,把它作为当前要处理的节点。把这个节点移到 close list 。对于当前方格的 8 个相邻方格的每一个方格做如下判断:
①如果它是不可抵达的或者它在 close list 中,忽略它。
②如果它不在 open list 中,把它加入 open list ,并且把当前方格设置为它的父亲,记录该方格的 F , G 和 H 值。
③如果它已经在 open list 中,检查这条路径 ( 即经由当前方格到达它那里 ) 是否更好,用 G 值作参考。更小的 G 值表示这是更好的路径。如果是这样,把它的父亲设置为当前方格,并重新计算它的 G 值和 F 值。
④把终点加入到了 open list 中,此时路径已经找到了,或者查找终点失败,并且 open list 是空的,此时没有路径时停止。
4、保存路径。

四、结构框架

在这里插入图片描述

五、prime算法随机生成迷宫

算法详解:首先我们将点分为两个集合,一部分是可以作为通路的点,一部分为未添加到通路的点。随机选择一个起点,我将它标记为红色,判断它隔着蓝色格子对面的格子是不是预定义的黄色,如果是,则将其标记为红色代表通路,将两个格子之间的蓝色格子也标记为红色代表通路。然后继续以最后标记的红色格子重复刚刚的操作。
如果对面的格子不是黄色,则将蓝色格子设置为灰色,确定为障碍物,然后继续寻找另一个蓝色的格子,重复以上操作。

# random prim algorithm
def randomPrim(map, width, height):			
	startX, startY = (randint(0, width-1), randint(0, height-1))  # 随机生成一个起点坐标
	print("start(%d, %d)" % (startX, startY))
	map.setMap(2*startX+1, 2*startY+1, MAP_ENTRY_TYPE.MAP_EMPTY)
	
	checklist = []
	checklist.append((startX, startY))
	while len(checklist):
		# 从列表中随机选择一个入口
		entry = choice(checklist)	
		if not checkAdjacentPos(map, entry[0], entry[1], width, height, checklist):
			# 如果它正好也是未被访问过的点,就将它从列表中移出
			checklist.remove(entry)
# 将未设置为通路的点全部设置为墙
def doRandomPrim(map):
	# set all entries of map to wall
	map.resetMap(MAP_ENTRY_TYPE.MAP_BLOCK)	
	randomPrim(map, (map.width-1)//2, (map.height-1)//2)

六、A*算法设计

算法详解:使用F=G+H来预估下一步应该怎么走,H表示待检测结点到目标节点B的估计移动开销,G代表的是从初始位置Start沿着已生成的路径到指定待检测节点的移动开销,估价函数F为他们的取值求和,选择F值最小的节点确定为要移动的节点(当F值相同时我们可以选择跟着open列表中最近添加的方块走)。寻路过程中将待检测节点放入open list,已检测过的节点放入close list。最终close列表中存放的就是最短路径。

# 计算曼哈顿距离
	def calHeuristic(pos, dest):
		return abs(dest.x - pos[0]) + abs(dest.y - pos[1])

	# 计算消耗
	def getMoveCost(location, pos):
		if location.x != pos[0] and location.y != pos[1]:
			return 1.4
		else:
			return 1

	# 判断节点是否在open集或close集中
	def isInList(list, pos):
		if pos in list:
			return list[pos]
		return None
	
	# 添加节点的邻节点
	def addAdjacentPositions(map, location, dest, openlist, closedlist):
		poslist = getPositions(map, location)
		for pos in poslist:
			# 如果该节点已经在close列表中,什么都不做
			if isInList(closedlist, pos) is None:
				findEntry = isInList(openlist, pos)
				h_cost = calHeuristic(pos, dest)
				g_cost = location.g_cost + getMoveCost(location, pos)
				if findEntry is None :
					# 如果该节点不在open表中,将它添加到open表
					openlist[pos] = SearchEntry(pos[0], pos[1], g_cost, g_cost+h_cost, location)
				elif findEntry.g_cost > g_cost:
					# 如果此位置在open表中并且代价大于当前位置,那么更新代价和以前的节点位置
					findEntry.g_cost = g_cost
					findEntry.f_cost = g_cost + h_cost
					findEntry.pre_entry = location
	
	# 寻找open表中代价最小的节点,如果open表为空返回none
	def getFastPosition(openlist):
		fast = None
		for entry in openlist.values():
			if fast is None:
				fast = entry
			elif fast.f_cost > entry.f_cost:
				fast = entry
		return fast

	openlist = {}
	closedlist = {}
	location = SearchEntry(source[0], source[1], 0.0)
	dest = SearchEntry(dest[0], dest[1], 0.0)
	openlist[source] = location
	while True:
		location = getFastPosition(openlist)
		if location is None:
			# 没有找到有效路径
			print("can't find valid path")
			break;
		
		if location.x == dest.x and location.y == dest.y:
			break
		
		closedlist[location.getPos()] = location
		openlist.pop(location.getPos())
		addAdjacentPositions(map, location, dest, openlist, closedlist)
		
	# 在地图中标记找到的路径
	while location is not None:
		map.setMap(location.x, location.y, MAP_ENTRY_TYPE.MAP_PATH)
		location = location.pre_entry	
七、实现效果

首先点击Random Prim按钮,再点击start随机生成迷宫。
在这里插入图片描述
点击Source and des按钮随机设置起点和终点
在这里插入图片描述
最后点击A* search按钮寻找路径,也可以自己通过键盘按键手动走迷宫。
在这里插入图片描述
以上即为迷宫的实现记录。

  • 6
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值