|
|
@ -7,36 +7,42 @@ import json |
|
|
|
import sys |
|
|
|
|
|
|
|
|
|
|
|
class Action(enum.Enum): |
|
|
|
UP = enum.auto() |
|
|
|
DOWN = enum.auto() |
|
|
|
LEFT = enum.auto() |
|
|
|
RIGHT = enum.auto() |
|
|
|
STAY = enum.auto() |
|
|
|
|
|
|
|
def invert(self) -> Action: |
|
|
|
INVERSIONS = { |
|
|
|
Action.UP: Action.DOWN, |
|
|
|
Action.DOWN: Action.UP, |
|
|
|
Action.LEFT: Action.RIGHT, |
|
|
|
Action.RIGHT: Action.LEFT, |
|
|
|
Action.STAY: Action.STAY, |
|
|
|
} |
|
|
|
return INVERSIONS[self] |
|
|
|
# Třídy reprezentující hru |
|
|
|
|
|
|
|
class Direction(enum.Enum): |
|
|
|
UP = 0 |
|
|
|
LEFT = 1 |
|
|
|
DOWN = 2 |
|
|
|
RIGHT = 3 |
|
|
|
STAY = None |
|
|
|
|
|
|
|
def combine(self, other) -> Direction: |
|
|
|
return Direction((self.value + other.value)%4) |
|
|
|
|
|
|
|
def invert(self) -> Direction: |
|
|
|
if self == Direction.STAY: |
|
|
|
return self |
|
|
|
return self.combine(Direction.DOWN) |
|
|
|
|
|
|
|
def __str__(self) -> str: |
|
|
|
return self.name.lower() |
|
|
|
|
|
|
|
|
|
|
|
class State: |
|
|
|
def __init__(self, state: dict) -> None: |
|
|
|
self.world = parse_world(state["state"]["world"]) |
|
|
|
self.my_team_id: int = state["team_id"] |
|
|
|
self.round_number: int = state["round"] |
|
|
|
self.time_to_response: Optional[int] = state["time_to_response"] |
|
|
|
class Team: |
|
|
|
home: 'Field' |
|
|
|
|
|
|
|
def __init__(self, id: int, is_me: bool) -> None: |
|
|
|
self.id: int = id |
|
|
|
self.is_me = is_me |
|
|
|
self.protected_fields: List[Field] = [] |
|
|
|
self.occupied: List[Field] = [] |
|
|
|
self.members: List[Member] = [] |
|
|
|
|
|
|
|
state: State |
|
|
|
def __eq__(self, other: Field) -> bool: |
|
|
|
return (self.id) == (other.id) |
|
|
|
|
|
|
|
def __hash__(self) -> int: |
|
|
|
return hash(self.id) |
|
|
|
|
|
|
|
|
|
|
|
class Member: |
|
|
@ -44,22 +50,22 @@ class Member: |
|
|
|
self.field = field |
|
|
|
self.team = team |
|
|
|
self.id = id |
|
|
|
self.action = Action.STAY |
|
|
|
self.action = Direction.STAY |
|
|
|
|
|
|
|
|
|
|
|
class Field: |
|
|
|
members: List[Member] |
|
|
|
|
|
|
|
def __init__(self, i: int, j: int, hill: bool, |
|
|
|
home_for_team: Optional[int], |
|
|
|
occupied_by_team: Optional[int], |
|
|
|
protected_for_team: Optional[int],) -> None: |
|
|
|
self.i = i |
|
|
|
self.j = j |
|
|
|
self.hill = hill |
|
|
|
self.home_for_team = home_for_team |
|
|
|
self.occupied_by_team = occupied_by_team |
|
|
|
self.protected_for_team = protected_for_team |
|
|
|
home_for_team: Optional[Team], |
|
|
|
occupied_by_team: Optional[Team], |
|
|
|
protected_for_team: Optional[Team],) -> None: |
|
|
|
self.i: int = i |
|
|
|
self.j: int = j |
|
|
|
self.hill: bool = 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: Field) -> bool: |
|
|
|
return (self.i, self.j) == (other.i, other.j) |
|
|
@ -67,17 +73,17 @@ class Field: |
|
|
|
def __hash__(self) -> int: |
|
|
|
return hash((self.i, self.j)) |
|
|
|
|
|
|
|
def get_neighbour_field(self, action: Action) -> Field: |
|
|
|
if action == Action.UP: |
|
|
|
def get_neighbour_field(self, action: Direction) -> Field: |
|
|
|
if action == Direction.UP: |
|
|
|
neighbour_i = self.i - 1 |
|
|
|
neighbour_j = self.j |
|
|
|
elif action == Action.DOWN: |
|
|
|
elif action == Direction.DOWN: |
|
|
|
neighbour_i = self.i + 1 |
|
|
|
neighbour_j = self.j |
|
|
|
elif action == Action.LEFT: |
|
|
|
elif action == Direction.LEFT: |
|
|
|
neighbour_i = self.i |
|
|
|
neighbour_j = self.j - 1 |
|
|
|
elif action == Action.RIGHT: |
|
|
|
elif action == Direction.RIGHT: |
|
|
|
neighbour_i = self.i |
|
|
|
neighbour_j = self.j + 1 |
|
|
|
else: |
|
|
@ -88,7 +94,7 @@ class Field: |
|
|
|
neighbour_j %= len(state.world[0]) |
|
|
|
return state.world[neighbour_i][neighbour_j] |
|
|
|
|
|
|
|
def is_accessible(self, team: int) -> bool: |
|
|
|
def is_accessible(self, team: Team) -> bool: |
|
|
|
"""If this field is accessible for the given team.""" |
|
|
|
|
|
|
|
if self.hill: |
|
|
@ -98,19 +104,69 @@ class Field: |
|
|
|
return False |
|
|
|
return True |
|
|
|
|
|
|
|
# Celý stav hry včetně parsování a vypisování |
|
|
|
|
|
|
|
def main() -> None: |
|
|
|
global state |
|
|
|
state = State(json.loads(sys.stdin.read())) |
|
|
|
my_members = find_team_members(state.my_team_id) |
|
|
|
class State: |
|
|
|
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"] |
|
|
|
|
|
|
|
# TODO: set actions for all members |
|
|
|
# example: all members go up |
|
|
|
for member in my_members: |
|
|
|
member.action = Action.UP |
|
|
|
def _parse_world(self, world: dict) -> List[List[Field]]: |
|
|
|
def get_team(team_id): |
|
|
|
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"]: |
|
|
|
m = Member( |
|
|
|
parsed_field, |
|
|
|
self.teams[member["team"]], |
|
|
|
member["id"], |
|
|
|
) |
|
|
|
members.append(m) |
|
|
|
m.team.members.append(m) |
|
|
|
parsed_field.members = members |
|
|
|
fields_row.append(parsed_field) |
|
|
|
|
|
|
|
fields.append(fields_row) |
|
|
|
return fields |
|
|
|
|
|
|
|
|
|
|
|
def build_turn(self) -> dict: |
|
|
|
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 |
|
|
|
] |
|
|
|
} |
|
|
|
|
|
|
|
print(json.dumps(build_turn(my_members))) |
|
|
|
state: State |
|
|
|
|
|
|
|
# Algoritmy |
|
|
|
|
|
|
|
def find_fields(predicate: Callable[[Field], bool]) -> List[Field]: |
|
|
|
"""Find all fields that satisfy the given predicate.""" |
|
|
@ -123,19 +179,7 @@ def find_fields(predicate: Callable[[Field], bool]) -> List[Field]: |
|
|
|
return result |
|
|
|
|
|
|
|
|
|
|
|
def find_team_members(team_id: int) -> List[Member]: |
|
|
|
"""Find all members that belong to the given team.""" |
|
|
|
|
|
|
|
result = [] |
|
|
|
for row in state.world: |
|
|
|
for field in row: |
|
|
|
for member in field.members: |
|
|
|
if member.team == team_id: |
|
|
|
result.append((member)) |
|
|
|
return result |
|
|
|
|
|
|
|
|
|
|
|
def pathfind(member: Member, goal: Field) -> Action: |
|
|
|
def pathfind(member: Member, goal: Field) -> Direction: |
|
|
|
"""Find the shortest path from member position to goal. |
|
|
|
|
|
|
|
Returns the first action to take. |
|
|
@ -148,7 +192,7 @@ def pathfind(member: Member, goal: Field) -> Action: |
|
|
|
queue = collections.deque([goal]) |
|
|
|
while queue: |
|
|
|
field = queue.popleft() |
|
|
|
for action in Action: |
|
|
|
for action in Direction: |
|
|
|
neighbour = field.get_neighbour_field(action) |
|
|
|
if (neighbour in explored or |
|
|
|
not neighbour.is_accessible(member.team)): |
|
|
@ -157,43 +201,20 @@ def pathfind(member: Member, goal: Field) -> Action: |
|
|
|
return action.invert() |
|
|
|
queue.append(neighbour) |
|
|
|
explored.add(neighbour) |
|
|
|
return Action.STAY |
|
|
|
|
|
|
|
|
|
|
|
def build_turn(members: Iterable[Member]) -> dict: |
|
|
|
return { |
|
|
|
"members": [ |
|
|
|
{"id": member.id, "action": str(member.action)} |
|
|
|
for member in members |
|
|
|
] |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
def parse_world(world: dict) -> List[List[Field]]: |
|
|
|
fields = [] |
|
|
|
for i, row in enumerate(world): |
|
|
|
fields_row = [] |
|
|
|
for j, field in enumerate(row): |
|
|
|
parsed_field = Field( |
|
|
|
i, j, |
|
|
|
field["hill"], |
|
|
|
field["home_for_team"], |
|
|
|
field["occupied_by_team"], |
|
|
|
field["protected_for_team"], |
|
|
|
) |
|
|
|
members = [] |
|
|
|
for member in field["members"]: |
|
|
|
members.append(Member( |
|
|
|
parsed_field, |
|
|
|
member["team"], |
|
|
|
member["id"], |
|
|
|
)) |
|
|
|
parsed_field.members = members |
|
|
|
fields_row.append(parsed_field) |
|
|
|
|
|
|
|
fields.append(fields_row) |
|
|
|
return fields |
|
|
|
return Direction.STAY |
|
|
|
|
|
|
|
# main (zde doplňte kód) |
|
|
|
|
|
|
|
def main() -> None: |
|
|
|
global state |
|
|
|
state = State(json.loads(sys.stdin.read())) |
|
|
|
|
|
|
|
# TODO: set actions for all yours members |
|
|
|
# example: all members go up |
|
|
|
for member in state.teams[1].members: |
|
|
|
member.action = Direction.UP |
|
|
|
|
|
|
|
print(json.dumps(state.build_turn())) |
|
|
|
|
|
|
|
if __name__ == '__main__': |
|
|
|
main() |
|
|
|