|
|
|
#!/usr/bin/env python3
|
|
|
|
import argparse
|
|
|
|
import time
|
|
|
|
from typing import Callable, List, Optional, Tuple
|
|
|
|
from client import get_state, send_turn, logger, TIME_BEFORE_RETRY
|
|
|
|
|
|
|
|
|
|
|
|
parser = argparse.ArgumentParser()
|
|
|
|
parser.add_argument("--server", type=str, default="http://localhost:5000", help="Server address.")
|
|
|
|
parser.add_argument("--game", type=str, default="main", help="'main' or 'test_#'.")
|
|
|
|
parser.add_argument("--token", type=str, default=None, help="API token.")
|
|
|
|
parser.add_argument("--log_level", type=str, default="WARNING", choices=["ERROR", "WARNING", "INFO"])
|
|
|
|
# you can add your own arguments, the strategy function will get them
|
|
|
|
|
|
|
|
|
|
|
|
Coords = Tuple[int, int]
|
|
|
|
|
|
|
|
|
|
|
|
class Member:
|
|
|
|
def __init__(self, team: int, id: int, remaining_rounds: int):
|
|
|
|
self.team = team
|
|
|
|
self.id = id
|
|
|
|
self.remaining_rounds = remaining_rounds
|
|
|
|
self.action: Optional[str] = None
|
|
|
|
|
|
|
|
|
|
|
|
class Field:
|
|
|
|
def __init__(self, home_for_team: Optional[int],
|
|
|
|
occupied_by_team: Optional[int],
|
|
|
|
hill: bool, members: List[Member]):
|
|
|
|
self.home_for_team = home_for_team
|
|
|
|
self.occupied_by_team = occupied_by_team
|
|
|
|
self.hill = hill
|
|
|
|
self.members = members
|
|
|
|
|
|
|
|
|
|
|
|
def strategy(team_id: int, state: dict, args: argparse.Namespace) -> dict:
|
|
|
|
"""Finds the best move for the given state.
|
|
|
|
|
|
|
|
This function is called every round.
|
|
|
|
It should return a dict with actions for each of team's soldiers.
|
|
|
|
|
|
|
|
State format:
|
|
|
|
```yaml
|
|
|
|
{
|
|
|
|
map: [[{
|
|
|
|
home_for_team: Optional[int],
|
|
|
|
occupied_by_team: Optional[int],
|
|
|
|
hill: bool,
|
|
|
|
members: [{
|
|
|
|
type: "soldier",
|
|
|
|
team: int,
|
|
|
|
id: int,
|
|
|
|
remaining_rounds: int
|
|
|
|
}]
|
|
|
|
}]]
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
Turn format:
|
|
|
|
```yaml
|
|
|
|
{
|
|
|
|
members: [{
|
|
|
|
id: int,
|
|
|
|
action: Optional[str]
|
|
|
|
# None, "left", "right", "up", "down"
|
|
|
|
}]
|
|
|
|
}
|
|
|
|
```
|
|
|
|
"""
|
|
|
|
|
|
|
|
world = parse_world(state["map"])
|
|
|
|
home = find_fields(world, lambda f: f.home_for_team == team_id)[0][1]
|
|
|
|
members = find_members(world, lambda m: m.team == team_id)
|
|
|
|
|
|
|
|
# TODO: implement your strategy here
|
|
|
|
for member, coords in members:
|
|
|
|
member.action = "up"
|
|
|
|
return build_turn(members)
|
|
|
|
|
|
|
|
|
|
|
|
def find_fields(
|
|
|
|
world: List[List[Field]],
|
|
|
|
predicate: Callable[[Field], bool]
|
|
|
|
) -> List[Tuple[Field, Coords]]:
|
|
|
|
"""Finds all fields that satisfy the given predicate."""
|
|
|
|
|
|
|
|
result = []
|
|
|
|
for y, row in enumerate(world):
|
|
|
|
for x, field in enumerate(row):
|
|
|
|
if predicate(field):
|
|
|
|
result.append((field, (x, y)))
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
|
|
def find_members(
|
|
|
|
world: List[List[Field]],
|
|
|
|
predicate: Callable[[Member], bool]
|
|
|
|
) -> List[Tuple[Member, Coords]]:
|
|
|
|
"""Finds all members that satisfy the given predicate."""
|
|
|
|
|
|
|
|
result = []
|
|
|
|
for y, row in enumerate(world):
|
|
|
|
for x, field in enumerate(row):
|
|
|
|
for member in field.members:
|
|
|
|
if predicate(member):
|
|
|
|
result.append((member, (x, y)))
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
|
|
def build_turn(members: List[Member]) -> dict:
|
|
|
|
return {
|
|
|
|
"members": [
|
|
|
|
{"id": member.id, "action": member.action}
|
|
|
|
for member in members
|
|
|
|
]
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
def parse_world(world: dict) -> List[List[Field]]:
|
|
|
|
fields = []
|
|
|
|
for row in world:
|
|
|
|
fields_row = []
|
|
|
|
for field in row:
|
|
|
|
members = []
|
|
|
|
for member in field["members"]:
|
|
|
|
members.append(Member(
|
|
|
|
member["team"],
|
|
|
|
member["id"],
|
|
|
|
member["remaining_rounds"]
|
|
|
|
))
|
|
|
|
fields_row.append(Field(
|
|
|
|
field["home_for_team"],
|
|
|
|
field["occupied_by_team"],
|
|
|
|
field["hill"],
|
|
|
|
members
|
|
|
|
))
|
|
|
|
fields.append(fields_row)
|
|
|
|
return fields
|
|
|
|
|
|
|
|
|
|
|
|
def main(args: argparse.Namespace):
|
|
|
|
min_round = 0
|
|
|
|
logger.setLevel(args.log_level)
|
|
|
|
|
|
|
|
while True:
|
|
|
|
state, wait_time = get_state(min_round)
|
|
|
|
if state is None:
|
|
|
|
# retry later
|
|
|
|
time.sleep(wait_time)
|
|
|
|
continue
|
|
|
|
|
|
|
|
turn = strategy(state["team_id"], state["state"], args)
|
|
|
|
|
|
|
|
round = state["round"]
|
|
|
|
while not send_turn(turn, round):
|
|
|
|
# if there was a connection error, retry later
|
|
|
|
time.sleep(TIME_BEFORE_RETRY)
|
|
|
|
min_round = round + 1
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
args = parser.parse_args()
|
|
|
|
main(args)
|