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 def reset(self): self.opponent_ships = [] self.cannonballs = [] self.mines = [] self.barrels = [] class Tile: def __init__(self, x: int, y: int): self.y = y self.x = x 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: 'EntityType') -> 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['EntityType']) -> Optional['EntityType']: 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 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 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[TargetType] 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: 'EntityType'): self.action = Action.FIRE self.target = target def move(self, target: 'TargetType'): self.action = Action.MOVE self.target = target class Mine(Entity): def __init__(self, game_map: GameMap, entity_id: int, x: int, y: int): super().__init__(game_map, entity_id, x, 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 EntityType = TypeVar('EntityType', Entity, Barrel, Ship, Mine, Cannonball) TargetType = Union[EntityType, Tile] 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.barrels.append(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: my_ship = game_map.my_ships.get(entity_id, None) if my_ship: my_ship.orientation = orientation my_ship.speed = speed my_ship.stock = rhum_stock else: game_map.my_ships[entity_id] = ship else: game_map.opponent_ships.append(ship) elif entity_type == MINE: game_map.mines.append(Mine(game_map, entity_id, x, y)) elif entity_type == CANNONBALL: shooter_id = int(arg_1) turns_before_impact = int(arg_2) game_map.cannonballs.append(Cannonball(game_map, entity_id, x, y, shooter_id, turns_before_impact)) # 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()