import math import random from enum import Enum from typing import List, Union, Sequence, TypeVar, Optional, Dict import sys BARREL = 'BARREL' SHIP = 'SHIP' MINE = 'MINE' CANNONBALL = 'CANNONBALL' DEFAULT_SHOOT_WAIT = 2 DEFAULT_MINE_WAIT = 4 class Action(Enum): MINE = 'MINE' MOVE = 'MOVE' FIRE = 'FIRE' WAIT = 'WAIT' SLOWER = 'SLOWER' class GameMap: def __init__(self, barrels: List['Barrel'] = None, my_ships: Dict[int, 'Ship'] = None, opponent_ships: List['Ship'] = None, mines: List['Mine'] = None, cannonballs: List['Cannonball'] = None): if opponent_ships is None: opponent_ships = [] if cannonballs is None: cannonballs = [] if mines is None: mines = [] if my_ships is None: my_ships = {} if barrels is None: barrels = [] self.opponent_ships = opponent_ships self.cannonballs = cannonballs self.mines = mines self.my_ships = my_ships self.barrels = barrels self.map = {} def reset(self): self.opponent_ships = [] self.cannonballs = [] self.mines = [] self.barrels = [] self.map = {} def add_opponent_ship(self, opponent: 'Ship'): self.opponent_ships.append(opponent) self._add_to_map(opponent) def add_my_ship(self, ship: 'Ship'): my_ship = self.my_ships.get(ship.id, None) if my_ship: my_ship.orientation = ship.orientation my_ship.speed = ship.speed my_ship.stock = ship.stock else: self.my_ships[ship.id] = ship self._add_to_map(ship) def add_mine(self, mine: 'Mine'): self.mines.append(mine) self._add_to_map(mine) def add_barrel(self, barrel: 'Barrel'): self.barrels.append(barrel) self._add_to_map(barrel) def add_cannonball(self, cannonball: 'Cannonball'): self.cannonballs.append(cannonball) self._add_to_map(cannonball) def _add_to_map(self, entity: 'Entity'): self.map[entity.coord] = entity class Tile: def __init__(self, x: int, y: int): self.y = y self.x = x @property def coord(self): return '{};{}'.format(self.x, self.y) def __repr__(self): return 'Tile(x={}, y={})'.format(self.x, self.y) class Entity(Tile): def __init__(self, game_map: GameMap, entity_id: int, x: int, y: int): super().__init__(x, y) self.game_map = game_map self.id = entity_id def distance(self, other: 'Entity') -> float: return ((self.x - other.x) ** 2 + (self.y - other.y) ** 2) ** 0.5 def nearest_barrel(self) -> Optional['Barrel']: return self.nearest(self.game_map.barrels) def nearest_opponent(self) -> Optional['Ship']: return self.nearest(self.game_map.opponent_ships) def nearest_mine(self) -> Optional['Mine']: return self.nearest(self.game_map.mines) def nearest(self, entities: Sequence['Entity']) -> Optional['Entity']: nearest = None best_dist = math.inf for entity in entities: dist = self.distance(entity) if dist < best_dist: best_dist = dist nearest = entity return nearest def __repr__(self): return 'Entity(id={}, x={}, y={})'.format(self.id, self.x, self.y) class Barrel(Entity): def __init__(self, game_map: GameMap, entity_id: int, x: int, y: int, quantity: int): super().__init__(game_map, entity_id, x, y) self.quantity = quantity def __repr__(self): return 'Barrel(id={}, x={}, y={}, quantity={})'.format(self.id, self.x, self.y, self.quantity) class Ship(Entity): def __init__(self, game_map: GameMap, entity_id: int, x: int, y: int, orientation: int, speed: int, stock: int, mine: bool = True): super().__init__(game_map, entity_id, x, y) self.orientation = orientation self.speed = speed self.stock = stock self.mine = mine self.mine_wait = DEFAULT_MINE_WAIT self.shoot_wait = DEFAULT_SHOOT_WAIT self.action = None # type: Optional[Action] self.target = None # type: Optional[Tile] def play_turn(self): if self.action is Action.MINE: self.mine_wait = DEFAULT_MINE_WAIT elif self.action is Action.FIRE: self.shoot_wait = DEFAULT_SHOOT_WAIT if self.target: print('{} {} {}'.format(self.action.value, self.target.x, self.target.y)) else: print(self.action.value) self.action = None self.target = None self.mine_wait -= 1 self.shoot_wait -= 1 def drop_mine(self): self.action = Action.MINE def wait(self): self.action = Action.WAIT def slower(self): self.action = Action.SLOWER def shoot(self, target: 'Entity'): self.action = Action.FIRE self.target = target def move(self, target: Tile): self.action = Action.MOVE self.target = target def __repr__(self): return ('Ship(id={}, x={}, y={}, orientation={}, speed={}, stock={}, mine={})' .format(self.id, self.x, self.y, self.orientation, self.speed, self.stock, self.mine)) class Mine(Entity): def __init__(self, game_map: GameMap, entity_id: int, x: int, y: int): super().__init__(game_map, entity_id, x, y) def __repr__(self): return 'Mine(id={}, x={}, y={})'.format(self.id, self.x, self.y) class Cannonball(Entity): def __init__(self, game_map: GameMap, entity_id: int, x: int, y: int, shooter_id: int, turns_before_impact: int): super().__init__(game_map, entity_id, x, y) self.turns_before_impact = turns_before_impact self.shooter_id = shooter_id def __repr__(self): return 'Barrel(id={}, x={}, y={}, shooter={}, before_impact={})'.format(self.id, self.x, self.y, self.shooter_id, self.turns_before_impact) def main(): random.seed() game_map = GameMap() while True: my_ship_count = int(input()) # the number of remaining ships entity_count = int(input()) # the number of entities (e.g. ships, mines or cannonballs) game_map.reset() # Fetch info for i in range(entity_count): entity_id, entity_type, x, y, arg_1, arg_2, arg_3, arg_4 = input().split() entity_id = int(entity_id) x = int(x) y = int(y) if entity_type == BARREL: rhum_quantity = int(arg_1) game_map.add_barrel(Barrel(game_map, entity_id, x, y, rhum_quantity)) elif entity_type == SHIP: orientation = int(arg_1) speed = int(arg_2) rhum_stock = int(arg_3) mine = int(arg_4) == 1 ship = Ship(game_map, entity_id, x, y, orientation, speed, rhum_stock, mine) if mine: game_map.add_my_ship(ship) else: game_map.add_opponent_ship(ship) elif entity_type == MINE: game_map.add_mine(Mine(game_map, entity_id, x, y)) elif entity_type == CANNONBALL: shooter_id = int(arg_1) turns_before_impact = int(arg_2) game_map.add_cannonball(Cannonball(game_map, entity_id, x, y, shooter_id, turns_before_impact)) perr(game_map.map) # Play for ship in game_map.my_ships.values(): rand = random.random() perr('rand', rand) perr('mine_wait', ship.mine_wait) perr('shoot_wait', ship.shoot_wait) if (ship.mine_wait > 0 and ship.shoot_wait > 0) or rand > 0.4: target = Tile(random.randint(0, 23), random.randint(0, 21)) if game_map.barrels: target = ship.nearest_barrel() ship.move(target) elif rand > 0.2: opponent = ship.nearest_opponent() ship.shoot(opponent) else: ship.drop_mine() ship.play_turn() def perr(*args): print(*args, file=sys.stderr, flush=True) if __name__ == '__main__': main()