codingame/challenge_coders_of_the_caribbean.py

283 lines
8.4 KiB
Python

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()