#!/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()