You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
245 lines
7.4 KiB
245 lines
7.4 KiB
#!/usr/bin/env python3
|
|
from __future__ import annotations
|
|
from typing import Callable, Iterable, Optional, List, Set
|
|
import collections
|
|
import enum
|
|
import json
|
|
import sys
|
|
|
|
|
|
# Třídy reprezentující hru
|
|
|
|
class Direction(enum.Enum):
|
|
UP = 0
|
|
LEFT = 1
|
|
DOWN = 2
|
|
RIGHT = 3
|
|
STAY = None
|
|
|
|
def combine(self, other) -> Direction:
|
|
"""Otoč směr podle jiného otočení."""
|
|
|
|
return Direction((self.value + other.value) % 4)
|
|
|
|
def invert(self) -> Direction:
|
|
"""Získej opačný směr."""
|
|
|
|
if self == Direction.STAY:
|
|
return self
|
|
return self.combine(Direction.DOWN)
|
|
|
|
def __str__(self) -> str:
|
|
return self.name.lower()
|
|
|
|
|
|
class Team:
|
|
home: Field
|
|
|
|
def __init__(self, id: int, is_me: bool) -> None:
|
|
self.id = id
|
|
self.is_me = is_me
|
|
self.protected_fields: List[Field] = []
|
|
self.occupied: List[Field] = []
|
|
self.members: List[Member] = []
|
|
|
|
def __eq__(self, other: object) -> bool:
|
|
if not isinstance(other, Team):
|
|
return NotImplemented
|
|
return (self.id) == (other.id)
|
|
|
|
def __hash__(self) -> int:
|
|
return hash(self.id)
|
|
|
|
|
|
class Member:
|
|
def __init__(self, field: Field, team: Team, id: int) -> None:
|
|
self.field = field
|
|
self.team = team
|
|
self.id = id
|
|
self.action = Direction.STAY
|
|
|
|
|
|
class Field:
|
|
members: List[Member]
|
|
|
|
def __init__(self, i: int, j: int, hill: bool,
|
|
home_for_team: Optional[Team],
|
|
occupied_by_team: Optional[Team],
|
|
protected_for_team: Optional[Team],) -> None:
|
|
self.i = i
|
|
self.j = j
|
|
self.hill = hill
|
|
self.home_for_team: Optional[Team] = home_for_team
|
|
self.occupied_by_team: Optional[Team] = occupied_by_team
|
|
self.protected_for_team: Optional[Team] = protected_for_team
|
|
|
|
def __eq__(self, other: object) -> bool:
|
|
if not isinstance(other, Field):
|
|
return NotImplemented
|
|
return (self.i, self.j) == (other.i, other.j)
|
|
|
|
def __hash__(self) -> int:
|
|
return hash((self.i, self.j))
|
|
|
|
def get_neighbour_field(self, direction: Direction) -> Field:
|
|
"""Další políčko v daném směru."""
|
|
|
|
if direction == Direction.UP:
|
|
neighbour_i = self.i - 1
|
|
neighbour_j = self.j
|
|
elif direction == Direction.DOWN:
|
|
neighbour_i = self.i + 1
|
|
neighbour_j = self.j
|
|
elif direction == Direction.LEFT:
|
|
neighbour_i = self.i
|
|
neighbour_j = self.j - 1
|
|
elif direction == Direction.RIGHT:
|
|
neighbour_i = self.i
|
|
neighbour_j = self.j + 1
|
|
else:
|
|
neighbour_i = self.i
|
|
neighbour_j = self.j
|
|
# zajisti, aby souřadnice byly v rozsahu herní plochy
|
|
neighbour_i %= len(state.world)
|
|
neighbour_j %= len(state.world[0])
|
|
return state.world[neighbour_i][neighbour_j]
|
|
|
|
def is_accessible(self, team: Team) -> bool:
|
|
"""Jestli daný tým může vstoupit na políčko."""
|
|
|
|
if self.hill:
|
|
return False
|
|
if (self.protected_for_team is not None and
|
|
self.protected_for_team != team):
|
|
return False
|
|
return True
|
|
|
|
|
|
class State:
|
|
"""Celý herní stav včetně parsování a vypisování."""
|
|
|
|
def __init__(self, state: dict) -> None:
|
|
self.teams = [Team(i, i == state["team_id"])
|
|
for i in range(state["teams_count"])]
|
|
self.world = self._parse_world(state["state"]["world"])
|
|
self.my_team: Team = self.teams[state["team_id"]]
|
|
self.round_number: int = state["round"]
|
|
self.time_to_response: Optional[int] = state["time_to_response"]
|
|
|
|
def _parse_world(self, world: dict) -> List[List[Field]]:
|
|
def get_team(team_id: int) -> Optional[Team]:
|
|
return None if team_id is None else self.teams[team_id]
|
|
|
|
fields = []
|
|
for i, row in enumerate(world):
|
|
fields_row = []
|
|
for j, field in enumerate(row):
|
|
parsed_field = Field(
|
|
i, j,
|
|
field["hill"],
|
|
get_team(field["home_for_team"]),
|
|
get_team(field["occupied_by_team"]),
|
|
get_team(field["protected_for_team"]),
|
|
)
|
|
if parsed_field.home_for_team:
|
|
parsed_field.home_for_team.home = parsed_field
|
|
if parsed_field.occupied_by_team:
|
|
parsed_field.occupied_by_team.occupied.append(parsed_field)
|
|
if parsed_field.protected_for_team:
|
|
parsed_field.protected_for_team.protected_fields.append(parsed_field)
|
|
members = []
|
|
for member in field["members"]:
|
|
member_team = self.teams[member["team"]]
|
|
m = Member(
|
|
parsed_field,
|
|
member_team,
|
|
member["id"],
|
|
)
|
|
members.append(m)
|
|
member_team.members.append(m)
|
|
parsed_field.members = members
|
|
fields_row.append(parsed_field)
|
|
|
|
fields.append(fields_row)
|
|
return fields
|
|
|
|
def build_turn(self) -> dict:
|
|
"""Získej slovník s příkazy pro server."""
|
|
|
|
for team in self.teams:
|
|
if not team.is_me:
|
|
for member in team.members:
|
|
if member.action is not Direction.STAY:
|
|
raise Exception("Akce přiřazena cizímu týmu.")
|
|
return {
|
|
"members": [
|
|
{"id": member.id, "action": str(member.action)}
|
|
for member in self.my_team.members
|
|
]
|
|
}
|
|
|
|
|
|
state: State
|
|
|
|
# Algoritmy
|
|
|
|
|
|
def find_fields(predicate: Callable[[Field], bool]) -> List[Field]:
|
|
"""Najdi políčka splňující danou podmínku."""
|
|
|
|
result = []
|
|
for row in state.world:
|
|
for field in row:
|
|
if predicate(field):
|
|
result.append(field)
|
|
return result
|
|
|
|
|
|
def pathfind(member: Member, goal: Field) -> Direction:
|
|
"""Najdi nejkratší cestu od vojáka k cílovému políčku.
|
|
|
|
Vrátí první krok, který má voják udělat.
|
|
Pokud žádná cesta neexistuje, vrátí STAY.
|
|
"""
|
|
|
|
# speciální případ: voják už je v cíli
|
|
if member.field == goal:
|
|
return Direction.STAY
|
|
# BFS od cíle k vojákovi
|
|
# tento směr umožní rychle zjistit, že cesta neexistuje,
|
|
# a také jednoduše získat první krok
|
|
explored: Set[Field] = set()
|
|
queue = collections.deque([goal])
|
|
dirs = [Direction.UP, Direction.LEFT, Direction.DOWN, Direction.RIGHT]
|
|
while queue:
|
|
field = queue.popleft()
|
|
for direction in dirs:
|
|
neighbour = field.get_neighbour_field(direction)
|
|
if (neighbour in explored or
|
|
not neighbour.is_accessible(member.team)):
|
|
continue
|
|
# pokud jsme našli vojáka, vrátíme první krok
|
|
if neighbour == member.field:
|
|
return direction.invert()
|
|
queue.append(neighbour)
|
|
explored.add(neighbour)
|
|
return Direction.STAY
|
|
|
|
|
|
# Strategie
|
|
|
|
|
|
def main() -> None:
|
|
global state
|
|
state = State(json.loads(sys.stdin.read()))
|
|
|
|
# TODO: zde doplňte svou herní strategii
|
|
# příklad: všechny vojáky posílám nahoru
|
|
for member in state.my_team.members:
|
|
member.action = Direction.UP
|
|
|
|
print(json.dumps(state.build_turn()))
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|
|
|