|
@ -1,7 +1,8 @@ |
|
|
#!/usr/bin/env python3 |
|
|
#!/usr/bin/env python3 |
|
|
from __future__ import annotations |
|
|
from __future__ import annotations |
|
|
from enum import Enum |
|
|
from enum import Enum |
|
|
from typing import Callable, Iterable, List, Optional |
|
|
from typing import Callable, Iterable, Optional, List, Set |
|
|
|
|
|
import collections |
|
|
import json |
|
|
import json |
|
|
import sys |
|
|
import sys |
|
|
|
|
|
|
|
@ -13,6 +14,17 @@ class Action(Enum): |
|
|
RIGHT = "right" |
|
|
RIGHT = "right" |
|
|
STAY = None |
|
|
STAY = None |
|
|
|
|
|
|
|
|
|
|
|
def invert(self) -> Action: |
|
|
|
|
|
if self == Action.UP: |
|
|
|
|
|
return Action.DOWN |
|
|
|
|
|
if self == Action.DOWN: |
|
|
|
|
|
return Action.UP |
|
|
|
|
|
if self == Action.LEFT: |
|
|
|
|
|
return Action.RIGHT |
|
|
|
|
|
if self == Action.RIGHT: |
|
|
|
|
|
return Action.LEFT |
|
|
|
|
|
return Action.STAY |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class State: |
|
|
class State: |
|
|
def __init__(self, state: dict) -> None: |
|
|
def __init__(self, state: dict) -> None: |
|
@ -44,6 +56,12 @@ class Field: |
|
|
self.home_for_team = home_for_team |
|
|
self.home_for_team = home_for_team |
|
|
self.occupied_by_team = occupied_by_team |
|
|
self.occupied_by_team = occupied_by_team |
|
|
|
|
|
|
|
|
|
|
|
def __eq__(self, other: Field) -> bool: |
|
|
|
|
|
return (self.i, self.j) == (other.i, other.j) |
|
|
|
|
|
|
|
|
|
|
|
def __hash__(self) -> int: |
|
|
|
|
|
return hash((self.i, self.j)) |
|
|
|
|
|
|
|
|
def get_neighbour_field(self, action: Action) -> Field: |
|
|
def get_neighbour_field(self, action: Action) -> Field: |
|
|
if action == Action.UP: |
|
|
if action == Action.UP: |
|
|
neighbour_i = self.i - 1 |
|
|
neighbour_i = self.i - 1 |
|
@ -111,6 +129,31 @@ def find_team_members(team_id: int) -> List[Member]: |
|
|
return result |
|
|
return result |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def pathfind(member: Member, goal: Field) -> Action: |
|
|
|
|
|
"""Find the shortest path from member position to goal. |
|
|
|
|
|
|
|
|
|
|
|
Returns the first action to take. |
|
|
|
|
|
If there is no path, returns Action.STAY. |
|
|
|
|
|
""" |
|
|
|
|
|
|
|
|
|
|
|
# BFS from goal to member |
|
|
|
|
|
# this direction makes it easier to realize that no path exists |
|
|
|
|
|
explored: Set[Field] = set() |
|
|
|
|
|
queue = collections.deque([goal]) |
|
|
|
|
|
while queue: |
|
|
|
|
|
field = queue.popleft() |
|
|
|
|
|
for action in Action: |
|
|
|
|
|
neighbour = field.get_neighbour_field(action) |
|
|
|
|
|
if (neighbour in explored or |
|
|
|
|
|
not neighbour.is_accessible(member.team)): |
|
|
|
|
|
continue |
|
|
|
|
|
if field == member.field: |
|
|
|
|
|
return action.invert() |
|
|
|
|
|
queue.append(neighbour) |
|
|
|
|
|
explored.add(field) |
|
|
|
|
|
return Action.STAY |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def build_turn(members: Iterable[Member]) -> dict: |
|
|
def build_turn(members: Iterable[Member]) -> dict: |
|
|
return { |
|
|
return { |
|
|
"members": [ |
|
|
"members": [ |
|
|