我目前正在使用python 2.5編寫我的Python遊戲ika,它使用python 2.5Python A *實現
我決定爲AI使用A *尋路。然而,我發現它對我的需求來說太慢了(3-4名敵人可能會延遲這場比賽,但我希望在沒有問題的情況下提供4-5個)。我知道,像A *這樣複雜的搜索並不意味着用python編寫腳本,但我確信,我的探路者也是以錯誤的方式實現的。
我的問題是:我該如何加快這個算法? 我寫了我自己的二進制堆,並且有一些嘗試:除了某些函數內部的行。這些線路可能會產生大量開銷?有沒有更好的方法來維護公開列表?
爲了測試的目的,我提供了帶有圖形界面的算法(當探路者搜索完成後,它會在ika.txt文件中寫入尋找路徑的迭代次數和秒數。做一個完整的搜索,和S的確是一步一步) 圖形版本: http://data.hu/get/6084681/A_star.rar
而且,這裏是一個引擎收錄的版本: http://pastebin.com/9N8ybX5F
這裏是主要的代碼,我使用的尋路:
import ika
import time
class Node:
def __init__(self,x,y,parent=None,g=0,h=0):
self.x = x
self.y = y
self.parent = parent
self.g = g
self.h = h
def cost(self):
return self.g + self.h
def equal(self,node):
if self.x == node.x and self.y == node.y:
return True
else:
return False
class Emerald_Pathfinder:
def __init__(self):
pass
def setup(self,start,goal):
self.start = start
self.goal = goal
self.openlist = [None,start] # Implemented as binary heap
self.closedlist = {} # Implemented as hash
self.onopenlist = {} # Hash, for searching the openlist
self.found = False
self.current = None
self.iterations = 0
def lowest_cost(self):
pass
def add_nodes(self,current):
nodes = []
x = current.x
y = current.y
self.add_node(x+1,y,current,10,nodes)
self.add_node(x-1,y,current,10,nodes)
self.add_node(x,y+1,current,10,nodes)
self.add_node(x,y-1,current,10,nodes)
# Dont cut across corners
up = map.is_obstacle((x,y-1),x,y-1)
down = map.is_obstacle((x,y+1),x,y+1)
left = map.is_obstacle((x-1,y),x-1,y)
right = map.is_obstacle((x+1,y),x+1,y)
if right == False and down == False:
self.add_node(x+1,y+1,current,14,nodes)
if left == False and up == False:
self.add_node(x-1,y-1,current,14,nodes)
if right == False and up == False:
self.add_node(x+1,y-1,current,14,nodes)
if left == False and down == False:
self.add_node(x-1,y+1,current,14,nodes)
return nodes
def heuristic(self,x1,y1,x2,y2):
return (abs(x1-x2)+abs(y1-y2))*10
def add_node(self,x,y,parent,cost,list):
# If not obstructed
if map.is_obstacle((x,y),x,y) == False:
g = parent.g + cost
h = self.heuristic(x,y,self.goal.x,self.goal.y)
node = Node(x,y,parent,g,h)
list.append(node)
def ignore(self,node,current):
# If its on the closed list, or open list, ignore
try:
if self.closedlist[(node.x,node.y)] == True:
return True
except:
pass
# If the node is on the openlist, do the following
try:
# If its on the open list
if self.onopenlist[(node.x,node.y)] != None:
# Get the id number of the item on the real open list
index = self.openlist.index(self.onopenlist[(node.x,node.y)])
# If one of the coordinates equal, its not diagonal.
if node.x == current.x or node.y == current.y:
cost = 10
else:
cost = 14
# Check, is this items G cost is higher, than the current G + cost
if self.openlist[index].g > (current.g + cost):
# If so, then, make the list items parent, the current node.
self.openlist[index].g = current.g + cost
self.openlist[index].parent = current
# Now resort the binary heap, in the right order.
self.resort_binary_heap(index)
# And ignore the node
return True
except:
pass
return False
def resort_binary_heap(self,index):
m = index
while m > 1:
if self.openlist[m/2].cost() > self.openlist[m].cost():
temp = self.openlist[m/2]
self.openlist[m/2] = self.openlist[m]
self.openlist[m] = temp
m = m/2
else:
break
def heap_add(self,node):
self.openlist.append(node)
# Add item to the onopenlist.
self.onopenlist[(node.x,node.y)] = node
m = len(self.openlist)-1
while m > 1:
if self.openlist[m/2].cost() > self.openlist[m].cost():
temp = self.openlist[m/2]
self.openlist[m/2] = self.openlist[m]
self.openlist[m] = temp
m = m/2
else:
break
def heap_remove(self):
if len(self.openlist) == 1:
return
first = self.openlist[1]
# Remove the first item from the onopenlist
self.onopenlist[(self.openlist[1].x,self.openlist[1].y)] = None
last = self.openlist.pop(len(self.openlist)-1)
if len(self.openlist) == 1:
return last
else:
self.openlist[1] = last
v = 1
while True:
u = v
# If there is two children
if (2*u)+1 < len(self.openlist):
if self.openlist[2*u].cost() <= self.openlist[u].cost():
v = 2*u
if self.openlist[(2*u)+1].cost() <= self.openlist[v].cost():
v = (2*u)+1
# If there is only one children
elif 2*u < len(self.openlist):
if self.openlist[2*u].cost() <= self.openlist[u].cost():
v = 2*u
# If at least one child is smaller, than parent, swap them
if u != v:
temp = self.openlist[u]
self.openlist[u] = self.openlist[v]
self.openlist[v] = temp
else:
break
return first
def iterate(self):
# If the open list is empty, exit the game
if len(self.openlist) == 1:
ika.Exit("no path found")
# Expand iteration by one
self.iterations += 1
# Make the current node the lowest cost
self.current = self.heap_remove()
# Add it to the closed list
self.closedlist[(self.current.x,self.current.y)] = True
# Are we there yet?
if self.current.equal(self.goal) == True:
# Target reached
self.goal = self.current
self.found = True
print self.iterations
else:
# Add the adjacent nodes, and check them
nodes_around = self.add_nodes(self.current)
for na in nodes_around:
if self.ignore(na,self.current) == False:
self.heap_add(na)
def iterateloop(self):
time1 = time.clock()
while 1:
# If the open list is empty, exit the game
if len(self.openlist) == 1:
ika.Exit("no path found")
# Expand iteration by one
self.iterations += 1
# Make the current node the lowest cost
self.current = self.heap_remove()
# Add it to the closed list
self.closedlist[(self.current.x,self.current.y)] = True
# Are we there yet?
if self.current.equal(self.goal) == True:
# Target reached
self.goal = self.current
self.found = True
print "Number of iterations"
print self.iterations
break
else:
# Add the adjacent nodes, and check them
nodes_around = self.add_nodes(self.current)
for na in nodes_around:
if self.ignore(na,self.current) == False:
self.heap_add(na)
time2 = time.clock()
time3 = time2-time1
print "Seconds to find path:"
print time3
class Map:
def __init__(self):
self.map_size_x = 20
self.map_size_y = 15
self.obstructed = {} # Library, containing x,y couples
self.start = [2*40,3*40]
self.unit = [16*40,8*40]
def is_obstacle(self,couple,x,y):
if (x >= self.map_size_x or x < 0) or (y >= self.map_size_y or y < 0):
return True
try:
if self.obstructed[(couple)] != None:
return True
except:
return False
def render_screen():
# Draw the Character
ika.Video.DrawRect(map.start[0],map.start[1],map.start[0]+40,map.start[1]+40,ika.RGB(40,200,10),1)
# Draw walls
for x in range(0,map.map_size_x):
for y in range(0,map.map_size_y):
if map.is_obstacle((x,y),x,y) == True:
ika.Video.DrawRect(x*40,y*40,(x*40)+40,(y*40)+40,ika.RGB(168,44,0),1)
# Draw openlist items
for node in path.openlist:
if node == None:
continue
x = node.x
y = node.y
ika.Video.DrawRect(x*40,y*40,(x*40)+40,(y*40)+40,ika.RGB(100,100,100,50),1)
# Draw closedlist items
for x in range(0,map.map_size_x):
for y in range(0,map.map_size_y):
try:
if path.closedlist[(x,y)] == True:
ika.Video.DrawRect(x*40,y*40,(x*40)+20,(y*40)+20,ika.RGB(0,0,255))
except:
pass
# Draw the current square
try:
ika.Video.DrawRect(path.current.x*40,path.current.y*40,(path.current.x*40)+40,(path.current.y*40)+40,ika.RGB(128,128,128), 1)
except:
pass
ika.Video.DrawRect(mouse_x.Position(),mouse_y.Position(),mouse_x.Position()+8,mouse_y.Position()+8,ika.RGB(128,128,128), 1)
# Draw the path, if reached
if path.found == True:
node = path.goal
while node.parent:
ika.Video.DrawRect(node.x*40,node.y*40,(node.x*40)+40,(node.y*40)+40,ika.RGB(40,200,200),1)
node = node.parent
# Draw the Target
ika.Video.DrawRect(map.unit[0],map.unit[1],map.unit[0]+40,map.unit[1]+40,ika.RGB(128,40,200),1)
def mainloop():
while 1:
render_screen()
if mouse_middle.Pressed():
# Iterate pathfinder
if path.found == False:
path.iterateloop()
elif mouse_right.Pressed():
# Iterate pathfinder by one
if path.found == False:
path.iterate()
elif ika.Input.keyboard["A"].Pressed():
# Iterate pathfinder
if path.found == False:
path.iterateloop()
elif ika.Input.keyboard["S"].Pressed():
# Iterate pathfinder by one
if path.found == False:
path.iterate()
elif mouse_left.Position():
# Add a square to the map, to be obstructed
if path.iterations == 0:
x = mouse_x.Position()
y = mouse_y.Position()
map.obstructed[(int(x/40),int(y/40))] = True
# Mouse preview
x = mouse_x.Position()
y = mouse_y.Position()
mx = int(x/40)*40
my = int(y/40)*40
ika.Video.DrawRect(mx,my,mx+40,my+40,ika.RGB(150,150,150,70),1)
ika.Video.ShowPage()
ika.Input.Update()
map = Map()
path = Emerald_Pathfinder()
path.setup(Node(map.start[0]/40,map.start[1]/40),Node(map.unit[0]/40,map.unit[1]/40))
mouse_middle = ika.Input.mouse.middle
mouse_right = ika.Input.mouse.right
mouse_left = ika.Input.mouse.left
mouse_x = ika.Input.mouse.x
mouse_y = ika.Input.mouse.y
# Initialize loop
mainloop()
我感謝任何幫助! (對不起,任何拼寫錯誤,英文不是我的母語)
這可能是值得尋找一個現有的庫來做到這一點(這可能是一個用C編寫的擴展模塊),因爲這樣你不需要重新實現它,它會更快。顯然,如果你爲了學習而發展,那麼不要擔心。 –
此外,這個問題似乎是關於改進工作代碼,所以你可能想將它發佈在[Code Review](http://codereview.stackexchange.com)上。一般來說,請在問題中發佈代碼,而不是在外部鏈接它。 –
「用C編寫的擴展模塊」 有沒有關於此的任何教程,或者任何用於已寫入擴展的「數據庫」?我找不到任何好消息,如果這是值得的(遊戲尋路) –