A*をPythonで

C++でA*が必要なのだけど、実装経験がなかったのでまずはPythonでリファレンス実装を行った。
という背景があるので、setとかは使ってない。neighbor_nodes関数のタプルもなくしたいのだががががが。

参考にしたのは、wikipediaのA*。wikipedia英語版のA*の擬似コードをPythonで実装した。ほぼ一緒。
wikipedia日本語版のA*は擬似コード載ってないからどんまい。

距離の評価関数は、マンハッタン距離とユークリッド距離の2つ。
となりのノードは上下左右のみでok。

コード

テストはない。

import math

def astar(start, goal, neighbor_nodes, dist_between, heuristic_cost_estimate):
    closedset = []
    openset   = [start]
    came_from = {}
    g_score   = {}
    f_score   = {}
    g_score[start] = 0
    f_score[start] = g_score[start] + heuristic_cost_estimate(start, goal)
    while openset:
        current = min((f_score[node], node) for node in openset)[1]
        if current == goal:
            return reconstruct_path(came_from, goal)
        openset.remove(current)
	closedset.append(current)
        for neighbor in neighbor_nodes(current):
            tentative_g_score = g_score[current] + dist_between(current, neighbor)
            tentative_f_score = tentative_g_score + heuristic_cost_estimate(neighbor, goal)
            if neighbor in closedset and tentative_f_score >= f_score[neighbor]:
		continue
            if neighbor not in openset or tentative_f_score < f_score[neighbor]:
                came_from[neighbor] = current
		g_score[neighbor] = tentative_g_score
                f_score[neighbor] = tentative_f_score
                if neighbor not in openset:
                    openset.append(neighbor)

    return False

def reconstruct_path(came_from, current_node):
    path = [current_node]
    while current_node in came_from:
        current_node = came_from[current_node]
        path.append(current_node)
    return path

MAP_ROW = 5
MAP_COL = 5
map = [[1,1,1,1,1],
       [1,0,0,0,1],
       [1,0,0,0,1],
       [1,0,0,0,1],
       [1,0,0,0,1],
       [1,1,1,1,1]]

def is_movable(p):
    x, y = p
    if x < 0 or x > MAP_COL-1 or y < 0 or y > MAP_ROW-1:
        return False
    if map[y][x] == 1:
        return False
    return True

def neighbor_nodes(p):
    x, y = p
    neighbors = [(x-1, y), (x, y-1), (x+1, y), (x, y+1)]
    return filter(is_movable, neighbors)

def heuristic_cost_estimate(p1, p2):
    return manhattan_distance(p1, p2)

def manhattan_distance(p1, p2):
    return abs(p1[0]-p2[0]) + abs(p1[1]-p2[1])

def euclidean_distance(p1, p2):
    return math.sqrt((p1[0]-p2[0])**2 + (p1[1]-p2[1])**2)

if __name__ == '__main__':
    start = (1, 1)
    goal  = (3, 4)
    path  = astar(start, goal, neighbor_nodes, heuristic_cost_estimate, heuristic_cost_estimate)
    if path:
        for position in reversed(path):
            x,y = position
            print(x,y)

感想

実装に手間取った。この後C++に書き換える。このPythonコードkivyで使えるなーと思った。
kivyはTMXフォーマット対応からしないと、自分が作りたいもの作れないのがががが。

そういえば、kivyといえば、kivyアドベントカレンダーid:cheeseshopさんにより進行中なので、興味ある方は読んでみるよろし。

気が向いたらgithubにもpushしとく。

追記2

そういえばNLPでもA*使われることあったの思い出した!