A*길찾기 소개(튜토리얼)
This is part 3 of a series on bot programming originally published on the Coder One blog.
Part 1: Getting started with the game environment
Part 2: Creating our first agent
A*길찾기(또는 A*검색) 알고리즘은 환경에서 두 점 사이의 가장 짧은 경로를 찾는 유행 기술이다.
본 강좌에서 우리는 Python에서 A*길 찾기 알고리즘을 실현하여 우리의 에이전트가 Dungeons and Data Structures environment의 목표 목적지로 내비게이션하는 것을 도울 것이다.서명Part 1을 해서 설치하거나 Part 2 간단한 로봇을 만들 수 있도록 하세요.
우리는 A*길찾기 알고리즘을 사용하여 기사가 목적지에 도착하는 가장 짧은 경로를 찾을 수 있도록 도울 것이다.
여기서 우리는 길을 찾는 배후의 이론을 깊이 연구하지 않을 것이다.대신 다음과 같은 유용한 리소스를 확인하십시오.
A* 탐색 개요
시작 노드가 제공된 경우 * 탐색 방법은 다음과 같습니다.
A*의 목표는 최소화입니다.
f(n)=g(n)+h(n)
어디:
n은 경로의 현재 노드
g(n)는 시작 노드에서 n까지의 경로의 비용이다
n(n): n에서 목표 단원으로 이동하는 비용을 추정하는 계발식 함수
우리 환경에서 f(n), g(n)와 h(n) 값의 예시.
What's the difference between A* and Dijkstra's algorithm?
The A* search algorithm can be considered an application of Dijkstra's algorithm, which adds heuristics to guide its search and achieve better performance. If you set the heuristic function h(n) equal to 0, the A* algorithm essentially boils down to Dijkstra's algorithm.
개시하다
목표는 에이전트가 환경 지하도와 데이터 구조에서 지정된 목적지로 성공적으로 이동하도록 돕는 것입니다.
Part 1 및 Part 2에 대한 관심이 계속되면 프로젝트를 손쉽게 관리할 수 있도록 재구성할 것입니다.
먼저 작업 디렉토리에 세 개의 파일을 생성합니다.
__init__.py
- 주 환경에서 호출from . import my_agent
def agent():
return my_agent.Agent()
my_agent.py
- 우리 대리인의 행위 논리를 소지from .helpers import *
class Agent:
def __init__(self):
'''
Place any initialization code for your agent here (if any)
'''
self.pending_actions = []
def next_move(self, game_state, player_state):
'''
This method is called each time your Agent is required to choose an action
'''
pass
helpers.py
- 모든 지원 함수와 클래스를 저장합니다보조 함수
우리는 일부 보조 함수를 정의할 것이다. 이 함수들은 뒤의 A*길 찾기 알고리즘에서 유용할 것이다.
helpers.py
파일에 추가합니다.Note: For consistency in the following sections, we'll refer to tiles as an (x,y) location of any tile on the map, and nodes as specific instances of the Node class object (which we'll cover later on). A node will contain a
location
property equivalent to a tile.
너의 이웃을 자유롭게 하다
다음에 검색할 배열의 목록을 가져오는 데 사용할 배열을 지정합니다.
# given our current location, return only surrounding tiles that are free
def get_free_neighbors(game_state, location):
x, y = location
neighbors = [{(x-1, y): 'l'}, {(x+1, y): 'r'}, {(x, y-1): 'd'}, {(x, y+1): 'u'}]
free_neighbors = []
for neighbor in neighbors:
for tile, direction in neighbor.items():
if game_state.is_in_bounds(tile):
if game_state.is_occupied(tile):
# check if this tile contains treasure or ammo
if game_state.entity_at(tile) == 't' or game_state.entity_at(tile) == 'a':
free_neighbors.append({tile:direction})
else:
free_neighbors.append({tile:direction})
return free_neighbors
보물을 얻다
우리는 우리의 목표 지점을 어떤 보물이 탄생하는 지점으로 만들 것이다.(또는
treasure
를 ammo
로 전환하여 스파이에게 지도 주위에서 탄약을 줍게 한다.# finds treasure, if any
def get_treasure(game_state):
treasure = game_state.treasure
if treasure:
return treasure[0] # return first treasure on the list
맨해튼 거리
앞서 언급한 바와 같이 A* 길찾기 알고리즘은 계발식 함수 $h(n)$가 인도합니다.우리가 사용할 계발식 함수는 맨해튼 거리입니다.
Source
맨해튼 거리는 현재 노드와 목적지 사이의 거리의 근사치이다. 왜냐하면 어떤 장애도 고려하지 않기 때문이다.완벽하지는 않지만 우리의 목적을 위해 역할을 발휘할 것이다.
맨해튼의 거리는 결코 우리 도로의 장애를 설명할 수 없다
def manhattan_distance(start, end):
distance = abs(start[0] - end[0]) + abs(start[1] - end[1])
return distance
A* 알고리즘
다음은 우리가 [2]에서 실현할 위조 코드입니다.
OPEN = priority queue containing START
CLOSED = empty set
while lowest rank in OPEN is not the GOAL:
current = remove lowest rank item from OPEN
add current to CLOSED
for neighbors of current:
cost = g(current) + movementcost(current, neighbor)
if neighbor in OPEN and cost less than g(neighbor):
remove neighbor from OPEN, because new path is better
if neighbor in CLOSED and cost less than g(neighbor): ⁽²⁾
remove neighbor from CLOSED
if neighbor not in OPEN and neighbor not in CLOSED:
set g(neighbor) to cost
add neighbor to OPEN
set priority queue rank to g(neighbor) + h(neighbor)
set neighbor's parent to current
reconstruct reverse path from goal to start
by following parent pointers
다음은 Dell의 구현입니다(link to full code.########################
### Pathfinder ###
########################
class Node:
def __init__(self, parent=None, location=None, action=None):
self.parent = parent
self.location = location
self.action = action
self.h = 0
self.g = 0
self.f = 0
def get_path(node):
path = []
while node.parent:
path.append(node)
node = node.parent
return path
def get_path_actions(path):
actions = []
for node in path:
actions.append(node.action)
return actions
def astar(game_state, start, target):
print("----A* STAR----")
path = []
# add starting node to open list
open_list = [Node(None, start, None)]
closed_list = []
# exit the loop early if no path can be found
# (the target is likely blocked off)
max_loops = 1000
counter = 0
# while lowest rank in OPEN is not the GOAL:
while len(open_list) > 0 and counter <= max_loops:
# find the node with the lowest rank
curr_node = open_list[0]
curr_index = 0
for index, node in enumerate(open_list):
if node.f < curr_node.f:
curr_node = node
curr_index = index
# check if this node is the goal
if curr_node.location == target:
print(f"~~~~~~~FOUND TARGET~~~~~~~")
path = get_path(curr_node)
return path
# current = remove lowest rank item from OPEN
# add current to CLOSED
del open_list[curr_index]
closed_list.append(curr_node)
# get neighbors of current
neighbors = get_free_neighbors(game_state, curr_node.location)
neighbor_nodes = []
for neighbor in neighbors:
for location, action in neighbor.items():
neighbor_nodes.append(Node(None, location, action))
# for neighbors of current:
for neighbor in neighbor_nodes:
# used for loop behavior
in_closed = False
in_open = False
# cost = g(current) + movementcost(current, neighbor)
cost = curr_node.g + 1
# if neighbor in OPEN and cost less than g(neighbor):
# remove neighbor from OPEN, because new path is better
for index, node in enumerate(open_list):
if neighbor.location == node.location and cost < neighbor.g:
del open_list[index]
in_open = True
# if neighbor in CLOSED and cost less than g(neighbor): ⁽²⁾
# remove neighbor from CLOSED
for index, node in enumerate(closed_list):
if neighbor.location == node.location and cost < neighbor.g:
del closed_list[index]
in_closed = True
# if neighbor not in OPEN and neighbor not in CLOSED:
# set g(neighbor) to cost
# add neighbor to OPEN
# set priority queue rank to g(neighbor) + h(neighbor)
# set neighbor's parent to current
if not in_open and not in_closed:
neighbor.g = cost
open_list.append(neighbor)
neighbor.h = manhattan_distance(neighbor.location, target)
neighbor.f = neighbor.g + neighbor.h
neighbor.parent = curr_node
counter += 1
print(f"---NO PATH FOUND---")
에 추가helpers.py
합니다.주의, 우리는
get_path_actions()
를 추가했다.이것은 우리의 대리인이 목적지에 도착하기 위해 취한 행동으로 돌아갈 것이다(예를 들어 ['u', 'l', ...]
우리 탐정에게 길찾기 기능을 추가하다
모든 것이 정상적인지 확인하려면
next_move()
파일의 my_agent.py
방법을 업데이트하십시오.def next_move(self, game_state, player_state):
print(f"tick: {game_state.tick_number}")
# treasure spawns randomly
treasure = get_treasure(game_state)
if len(self.pending_actions) > 0:
action = self.pending_actions.pop()
elif treasure:
path = astar(game_state, player_state.location, treasure)
if path:
actions = get_path_actions(path)
print(f"--ACTIONS: {actions}")
for action in actions:
self.pending_actions.append(action)
action = self.pending_actions.pop()
else:
action = ''
else:
action = ''
return action
Link to full code 터미널에서 다음을 실행합니다.
coderone-dungeon my_agent --interactive
--interactive
기가 기사 역할을 허락한다)만약 당신이 모든 것을 성공적으로 설치했다면, 당신의 대리인이 지도 주위에서 보물을 줍는 것을 보아야 합니다.
----A* STAR----
~~~~~~~FOUND TARGET~~~~~~~
--ACTIONS: ['d', 'd', 'd', 'r', 'r', 'd', 'r', 'r', 'r']
주의: 우리는 목표 노드에서 뒤로 경로를 구축했기 때문에 이 조작 목록도 반대로 되어 있습니다.이것은 아주 좋습니다. pop()
을 사용하기 때문에 목록의 끝에서부터 작업을 시작합니다.
Reference
이 문제에 관하여(A*길찾기 소개(튜토리얼)), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/joooyz/an-introduction-to-a-pathfinding-tutorial-f9o텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)