一、实验要求:
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按钮寻找路径,也可以自己通过键盘按键手动走迷宫。
以上即为迷宫的实现记录。