|
|
@@ -5,9 +5,9 @@
|
|
|
import heapq
|
|
|
import sys
|
|
|
|
|
|
+
|
|
|
# TODO:
|
|
|
# * add an esquive manoeuvre / try to avoid cannonballs
|
|
|
-# * separate the main loop in two: planning, then acting
|
|
|
# * consider targeting rum barrels if an ennemy is nearer
|
|
|
# * compute first and second target instead of only one to anticipate the next move
|
|
|
# * if an enemy is near a mine, shoot the mine instead of the ship
|
|
|
@@ -23,19 +23,99 @@ current_turn = 0
|
|
|
class DidNotAct(Exception):
|
|
|
pass
|
|
|
|
|
|
+class Queue():
|
|
|
+ def __init__(self):
|
|
|
+ self.items = []
|
|
|
+
|
|
|
+ def put(self, item, priority):
|
|
|
+ heapq.heappush(self.items, (priority, item))
|
|
|
+
|
|
|
+ def get(self):
|
|
|
+ return heapq.heappop(self.items)[1]
|
|
|
+
|
|
|
+ @classmethod
|
|
|
+ def merge(cls, *args, reverse=False):
|
|
|
+ q = cls()
|
|
|
+ q.items = list(heapq.merge(*[a.items for a in args], key=lambda x: x[1], reverse=reverse))
|
|
|
+ return q
|
|
|
+
|
|
|
+class InterestQueue(Queue):
|
|
|
+ def __add__(self, other):
|
|
|
+ self.items += other.items
|
|
|
+ return self
|
|
|
+
|
|
|
+ def __bool__(self):
|
|
|
+ return bool(self.items)
|
|
|
+
|
|
|
+ def put(self, item):
|
|
|
+ heapq.heappush(self.items, item)
|
|
|
+
|
|
|
+ def get(self):
|
|
|
+ return heapq.heappop(self.items)
|
|
|
+
|
|
|
+ @classmethod
|
|
|
+ def merge(cls, *args, reverse=False):
|
|
|
+ q = cls()
|
|
|
+ q.items = list(heapq.merge(*[a.items for a in args], reverse=reverse))
|
|
|
+ return q
|
|
|
+
|
|
|
+class ObjectivesQueue(InterestQueue):
|
|
|
+ pass
|
|
|
+
|
|
|
+
|
|
|
class Base():
|
|
|
def __repr__(self):
|
|
|
return f"<{self.__class__.__name__}: {self.__dict__}>"
|
|
|
|
|
|
+class Objective(Base):
|
|
|
+ def __init__(self, ship, target):
|
|
|
+ self.ship = ship
|
|
|
+ self.target = target
|
|
|
+ self.interest = 0
|
|
|
|
|
|
-class Position(Base):
|
|
|
- def __init__(self, x, y):
|
|
|
- self.pos = (x, y)
|
|
|
+ def __lt__(self, other):
|
|
|
+ return self.interest < other.interest
|
|
|
|
|
|
-class ShootingSpot(Position):
|
|
|
- def __init__(self, *args):
|
|
|
- super().__init__(*args)
|
|
|
- self.interest = 0
|
|
|
+ def __repr__(self):
|
|
|
+ return f"<{self.__class__.__name__}({self.target.id})>"
|
|
|
+
|
|
|
+ def update_interest(self, from_= None):
|
|
|
+ # the lower the better
|
|
|
+ if from_ is None:
|
|
|
+ from_ = self.ship.next_pos
|
|
|
+ self.interest = Grid.manhattan(from_, self.target.pos)
|
|
|
+
|
|
|
+class GetBarrel(Objective):
|
|
|
+ def __init__(self, *args, **kwargs):
|
|
|
+ super().__init__(*args, **kwargs)
|
|
|
+ self.distance = 0
|
|
|
+ self.alignment = 0
|
|
|
+
|
|
|
+ def update_interest(self, from_= None, orientation=None):
|
|
|
+ if from_ is None:
|
|
|
+ from_ = self.ship.next_pos
|
|
|
+ if orientation is None:
|
|
|
+ orientation = self.ship.orientation
|
|
|
+ distance = Grid.manhattan(from_, self.target.pos)
|
|
|
+ alignment = abs(Grid.diff_directions(Grid.direction_to(*from_, *self.target.pos), orientation))
|
|
|
+
|
|
|
+ self.interest = 6 * distance + 9 * alignment + 3 * self.target.dispersal + self.target.mine_threat ** 2 - 36 * self.target.ennemy_near
|
|
|
+
|
|
|
+class Attack(Objective):
|
|
|
+ def __init__(self, *args, **kwargs):
|
|
|
+ super().__init__(*args, **kwargs)
|
|
|
+ self.distance = 0
|
|
|
+ self.alignment = 0
|
|
|
+
|
|
|
+ def update_interest(self, from_= None, orientation=None):
|
|
|
+ if from_ is None:
|
|
|
+ from_ = self.ship.next_pos
|
|
|
+ if orientation is None:
|
|
|
+ orientation = self.ship.orientation
|
|
|
+ distance = Grid.manhattan(from_, self.target.next_pos)
|
|
|
+ alignment = abs(Grid.diff_directions(Grid.direction_to(*from_, *self.target.next_pos), orientation))
|
|
|
+
|
|
|
+ self.interest = 7 * distance + 3 * alignment - 20 * self.target.blocked_since - 10 * self.target.same_traject_since
|
|
|
|
|
|
class PathNode(tuple):
|
|
|
def __new__(self, x, y, parent=None):
|
|
|
@@ -129,77 +209,88 @@ class Grid(Base):
|
|
|
wtotal += b.amount
|
|
|
return (wx // wtotal, wy // wtotal) if wtotal else None
|
|
|
|
|
|
- def pre_evaluate_barrels_interest(self):
|
|
|
+ def pre_eval_barrels(self):
|
|
|
grav_center = self.barrels_gravity_center()
|
|
|
for b in self.barrels:
|
|
|
b.dispersal = Grid.manhattan(grav_center, b.pos) if grav_center != None else 0
|
|
|
b.mine_threat = any(type(self.at(*c)) is Mine for c in self.neighbors(*b.pos))
|
|
|
-
|
|
|
- def evaluate_barrels_interest(self, ship):
|
|
|
+ b.ennemy_near = any(b.pos in e.next_area for e in self.ennemy_ships)
|
|
|
+
|
|
|
+ def eval_objectives(self):
|
|
|
+ objectives = {GetBarrel: {}, Attack: {}}
|
|
|
+
|
|
|
for b in self.barrels:
|
|
|
- b.distance = Grid.manhattan(ship.next_pos, b.pos)
|
|
|
- b.alignement = abs(Grid.diff_directions(Grid.direction_to(*ship.prow, *b.pos), ship.orientation))
|
|
|
- b.about_to_be_picked = any(b.pos in s.next_area for s in self.ennemy_ships)
|
|
|
-
|
|
|
- def evaluate_ennemies_interest(self, ship):
|
|
|
- for s in self.ennemy_ships:
|
|
|
- s.distance = Grid.manhattan(ship.next_pos, s.next_pos)
|
|
|
- s.alignement = abs(self.diff_directions(self.direction_to(*ship.prow, *s.next_pos), ship.orientation))
|
|
|
+ for s in self.owned_ships:
|
|
|
+ obj = GetBarrel(s, b)
|
|
|
+ obj.update_interest()
|
|
|
+ if not s in objectives[GetBarrel]:
|
|
|
+ objectives[GetBarrel][s] = ObjectivesQueue()
|
|
|
+ objectives[GetBarrel][s].put(obj)
|
|
|
+
|
|
|
+ for s in self.owned_ships:
|
|
|
+ objectives[Attack][s] = ObjectivesQueue()
|
|
|
+ for e in self.ennemy_ships:
|
|
|
+ obj = Attack(s, e)
|
|
|
+ obj.update_interest()
|
|
|
+ objectives[Attack][s].put(obj)
|
|
|
+
|
|
|
+ return objectives
|
|
|
|
|
|
- def pre_update_moving_costs(self):
|
|
|
- self.moving_costs = {}
|
|
|
+ def update_moving_costs(self):
|
|
|
+ base_costs = {}
|
|
|
|
|
|
for x in range(-1, self.w + 1):
|
|
|
for y in range(-1, self.h + 1):
|
|
|
if x in (0, self.w) or y in (0, self.h):
|
|
|
- self.moving_costs[(x, y)] = 15 # borders are a little more expensive
|
|
|
+ base_costs[(x, y)] = 15 # borders are a little more expensive
|
|
|
elif x in (-1, self.w + 1) or y in (-1, self.h + 1):
|
|
|
- self.moving_costs[(x, y)] = 1000 # out of the map
|
|
|
+ base_costs[(x, y)] = 1000 # out of the map
|
|
|
else:
|
|
|
- self.moving_costs[(x, y)] = 10 # base moving cost
|
|
|
+ base_costs[(x, y)] = 10 # base moving cost
|
|
|
|
|
|
for m in self.mines:
|
|
|
for n in self.neighbors(*m.pos):
|
|
|
- self.moving_costs[n] += 30
|
|
|
+ base_costs[n] += 30
|
|
|
for m in self.mines:
|
|
|
- self.moving_costs[m.pos] += 1000
|
|
|
+ base_costs[m.pos] += 1000
|
|
|
for c in self.cannonballs:
|
|
|
- self.moving_costs[c.pos] += (100 + (5 - c.countdown) * 200)
|
|
|
+ base_costs[c.pos] += (100 + (5 - c.countdown) * 200)
|
|
|
|
|
|
- def update_moving_costs(self, ship):
|
|
|
- for s in self.ships:
|
|
|
- if s is ship:
|
|
|
- continue
|
|
|
- dist = self.manhattan(ship.pos, s.pos)
|
|
|
- if dist > 8:
|
|
|
- continue
|
|
|
- for c in self.neighbors(*s.pos):
|
|
|
- self.moving_costs[c] += 100 * abs(3 - s.speed)
|
|
|
- for c in self.zone(s.next_pos, 4):
|
|
|
- self.moving_costs[c] += 20
|
|
|
-
|
|
|
- def shooting_spot(self, ship, targetted_ship):
|
|
|
- self.shooting_spots = []
|
|
|
- for x, y in self.zone(targetted_ship.next_pos, 10):
|
|
|
- if self.moving_costs[(x, y)] > 10:
|
|
|
+ for ship in self.ships:
|
|
|
+ ship._moving_costs = base_costs
|
|
|
+ for other in self.ships:
|
|
|
+ if other is ship:
|
|
|
+ continue
|
|
|
+ dist = self.manhattan(ship.pos, other.pos)
|
|
|
+ if dist > 8:
|
|
|
+ continue
|
|
|
+ for c in self.neighbors(*other.pos):
|
|
|
+ ship._moving_costs[c] += 100 * abs(3 - other.speed)
|
|
|
+ for c in self.zone(other.next_pos, 4):
|
|
|
+ ship._moving_costs[c] += 20
|
|
|
+
|
|
|
+ def shooting_spot(self, ship, target):
|
|
|
+ shooting_spots = Queue()
|
|
|
+ target_pos = target.next_pos if type(target) is Ship else target.pos
|
|
|
+
|
|
|
+ for x, y in self.zone(target_pos, 10):
|
|
|
+ if ship.moving_cost(x, y) > 10:
|
|
|
continue
|
|
|
- if self.manhattan((x, y), targetted_ship.next_pos) < 2:
|
|
|
+ if self.manhattan((x, y), target_pos) < 2:
|
|
|
continue
|
|
|
|
|
|
- spot = ShootingSpot(x, y)
|
|
|
-
|
|
|
- spot.interest -= self.moving_costs[(x, y)]
|
|
|
+ interest = 0 # the lower the better
|
|
|
|
|
|
# avoid cells too close from borders
|
|
|
if not (3 <= x <= (self.w - 3) and 3 <= y < (self.h - 3)):
|
|
|
- spot.interest -= 10
|
|
|
+ interest += 10
|
|
|
|
|
|
# priorize spots at distance 5 from active ship
|
|
|
- spot.interest += 10 * abs(5 - self.manhattan((x, y), ship.pos))
|
|
|
+ interest -= 10 * abs(5 - self.manhattan((x, y), ship.pos))
|
|
|
|
|
|
- self.shooting_spots.append(spot)
|
|
|
+ shooting_spots.put((x, y), interest)
|
|
|
|
|
|
- return max(self.shooting_spots, key= lambda x: x.interest)
|
|
|
+ return shooting_spots.get()
|
|
|
|
|
|
# geometrical algorithms
|
|
|
@staticmethod
|
|
|
@@ -306,17 +397,19 @@ class Grid(Base):
|
|
|
return result
|
|
|
|
|
|
# pathfinding
|
|
|
- def path(self, origin, orient0, target, incl_start=False, limit=10000):
|
|
|
- nodes = []
|
|
|
+ def path(self, origin, orientat0, target, moving_costs={}, incl_start=False, limit=10000):
|
|
|
+ nodes = Queue()
|
|
|
break_on, iteration = limit, 0
|
|
|
|
|
|
+
|
|
|
origin = PathNode(*origin)
|
|
|
- origin.orientation = orient0
|
|
|
- heapq.heappush(nodes, (0, origin))
|
|
|
+ origin.orientation = orientat0
|
|
|
+
|
|
|
+ nodes.put(origin, 0)
|
|
|
neighbors = []
|
|
|
|
|
|
while nodes:
|
|
|
- current = heapq.heappop(nodes)[1]
|
|
|
+ current = nodes.get()
|
|
|
|
|
|
if current == target:
|
|
|
path = []
|
|
|
@@ -338,7 +431,7 @@ class Grid(Base):
|
|
|
if break_on > 0 and iteration >= break_on:
|
|
|
return None
|
|
|
|
|
|
- moving_cost = self.moving_costs[x, y]
|
|
|
+ moving_cost = moving_costs[(x, y)]
|
|
|
if moving_cost >= 1000:
|
|
|
continue
|
|
|
|
|
|
@@ -349,7 +442,7 @@ class Grid(Base):
|
|
|
continue
|
|
|
|
|
|
cost = current.cost + moving_cost + diff * 10
|
|
|
- if diff != 0 and any(self.moving_costs[c] >= 1000 for c in neighbors):
|
|
|
+ if diff != 0 and any(moving_costs[c] >= 1000 for c in neighbors):
|
|
|
# a direction change here is dangerous
|
|
|
cost += 50
|
|
|
|
|
|
@@ -358,7 +451,7 @@ class Grid(Base):
|
|
|
node = PathNode(x, y, current)
|
|
|
node.cost = cost
|
|
|
node.orientation = d
|
|
|
- heapq.heappush(nodes, (priority, node))
|
|
|
+ nodes.put(node, priority)
|
|
|
else:
|
|
|
return None
|
|
|
|
|
|
@@ -375,6 +468,11 @@ class Entity(Base):
|
|
|
def pos(self):
|
|
|
return (self.x, self.y)
|
|
|
|
|
|
+ def __lt__(self, other):
|
|
|
+ # default comparison, used to avoid errors when used with queues and priorities are equals
|
|
|
+ return self.id < other.id
|
|
|
+
|
|
|
+
|
|
|
class Ship(Entity):
|
|
|
MAX_SPEED = 2
|
|
|
SCOPE = 10
|
|
|
@@ -394,7 +492,11 @@ class Ship(Entity):
|
|
|
self.blocked_since = 0
|
|
|
self.same_traject_since = 0
|
|
|
self.last_action = ""
|
|
|
+ self._moving_costs = {}
|
|
|
+
|
|
|
self.target = None
|
|
|
+ self.second_target = None
|
|
|
+ self.path = []
|
|
|
|
|
|
self.distance = 0
|
|
|
self.alignment = 0
|
|
|
@@ -409,7 +511,10 @@ class Ship(Entity):
|
|
|
super().update(x, y)
|
|
|
self.orientation, self.speed, self.stock, self.owned = map(int, args)
|
|
|
|
|
|
- self.target = None
|
|
|
+ self.objective = None
|
|
|
+ self.objective_next = None
|
|
|
+ self.objective_fallback = None
|
|
|
+ self.path = []
|
|
|
|
|
|
self.area = Ship.get_area(self.x, self.y, self.orientation)
|
|
|
self.prow, _, self.stern = self.area
|
|
|
@@ -464,15 +569,9 @@ class Ship(Entity):
|
|
|
def in_current_direction(self, x, y):
|
|
|
return self.orientation == Grid.direction_to(*self.pos, x, y)
|
|
|
|
|
|
- @property
|
|
|
- def interest(self):
|
|
|
- return 7 * self.distance + 3 * self.alignment - 20 * self.blocked_since - 10 * self.same_traject_since
|
|
|
+ def moving_cost(self, x, y):
|
|
|
+ return self._moving_costs[(x, y)]
|
|
|
|
|
|
- def acquire(self, target):
|
|
|
- self.target = target
|
|
|
- if type(target) is Barrel:
|
|
|
- target.aimed = True
|
|
|
-
|
|
|
def move(self, *args, **kwargs):
|
|
|
try:
|
|
|
self._move(*args, **kwargs)
|
|
|
@@ -480,7 +579,7 @@ class Ship(Entity):
|
|
|
except DidNotAct:
|
|
|
return False
|
|
|
|
|
|
- def _move(self, path, avoid=[]):
|
|
|
+ def _move(self, path):
|
|
|
|
|
|
if path is None:
|
|
|
log(f"(!) broken: automove to {goto}")
|
|
|
@@ -492,7 +591,7 @@ class Ship(Entity):
|
|
|
# <--- special: avoid blocking situations
|
|
|
if current_turn > 1 and self.blocked_since >= 1:
|
|
|
dx, dy = Grid.directions(self.y)[((self.orientation + 1) % 6)]
|
|
|
- if grid.moving_costs[self.x + dx, self.y + dy] <= 50:
|
|
|
+ if self.moving_cost(self.x + dx, self.y + dy) <= 50:
|
|
|
self.turn_left()
|
|
|
else:
|
|
|
self.turn_right()
|
|
|
@@ -627,12 +726,10 @@ class Barrel(Entity):
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
super().__init__(*args, **kwargs)
|
|
|
self.amount = 0
|
|
|
- self.distance = 0
|
|
|
+
|
|
|
self.dispersal = 0
|
|
|
- self.alignement = False
|
|
|
- self.mine_threat = 0
|
|
|
- self.about_to_be_picked = False
|
|
|
- self.aimed = False
|
|
|
+ self.mine_threat = False
|
|
|
+ self.ennemy_near = False
|
|
|
|
|
|
def __repr__(self):
|
|
|
return f"<Barrel {self.id}: pos=({self.x}, {self.y}), amount={self.amount}>"
|
|
|
@@ -640,16 +737,6 @@ class Barrel(Entity):
|
|
|
def update(self, x, y, *args):
|
|
|
super().update(x, y)
|
|
|
self.amount = int(args[0])
|
|
|
-# self.aimed = False
|
|
|
-
|
|
|
- @property
|
|
|
- def interest(self):
|
|
|
- # the lower the better
|
|
|
- return 7 * self.distance \
|
|
|
- + 3 * self.dispersal \
|
|
|
- + self.mine_threat ** 2 \
|
|
|
- + 7 * self.alignement \
|
|
|
- - 100 * self.about_to_be_picked
|
|
|
|
|
|
class Mine(Entity):
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
@@ -666,33 +753,6 @@ class Cannonball(Entity):
|
|
|
self.sender, self.countdown = int(args[0]), int(args[1])
|
|
|
|
|
|
|
|
|
-
|
|
|
-class Action(Base):
|
|
|
- def __init__(self, *args):
|
|
|
- self.args = args
|
|
|
-
|
|
|
- def resolve(self):
|
|
|
- raise NotImplementedError
|
|
|
-
|
|
|
-class Move(Action):
|
|
|
- pass
|
|
|
-
|
|
|
-class Fire(Action):
|
|
|
- pass
|
|
|
-
|
|
|
-class TurnLeft(Action):
|
|
|
- pass
|
|
|
-
|
|
|
-class TurnRight(Action):
|
|
|
- pass
|
|
|
-
|
|
|
-class SpeedUp(Action):
|
|
|
- pass
|
|
|
-
|
|
|
-class SlowDown(Action):
|
|
|
- pass
|
|
|
-
|
|
|
-
|
|
|
entities = {}
|
|
|
map_entity = {"SHIP": Ship,
|
|
|
"BARREL": Barrel,
|
|
|
@@ -707,94 +767,110 @@ while True:
|
|
|
current_turn += 1
|
|
|
|
|
|
# <--- get input
|
|
|
- if 1:
|
|
|
- my_ship_count, entity_count = int(input()), int(input())
|
|
|
- for _ in range(entity_count):
|
|
|
- ent_id, ent_type, *data = input().split()
|
|
|
- ent_id = int(ent_id)
|
|
|
- seen.append(ent_id)
|
|
|
+ my_ship_count, entity_count = int(input()), int(input())
|
|
|
+ for _ in range(entity_count):
|
|
|
+ ent_id, ent_type, *data = input().split()
|
|
|
+ ent_id = int(ent_id)
|
|
|
+ seen.append(ent_id)
|
|
|
+
|
|
|
+ if not ent_id in entities:
|
|
|
+ entities[ent_id] = map_entity[ent_type](ent_id)
|
|
|
|
|
|
- if not ent_id in entities:
|
|
|
- entities[ent_id] = map_entity[ent_type](ent_id)
|
|
|
-
|
|
|
- ent = entities[ent_id]
|
|
|
- ent.update(*data)
|
|
|
-
|
|
|
- entities = {k: v for k, v in entities.items() if k in seen}
|
|
|
- # --->
|
|
|
- # <--- test input
|
|
|
- else:
|
|
|
- ship = Ship(0)
|
|
|
- ship.update(3,3,0,1,0,1)
|
|
|
- ennemy = Ship(1)
|
|
|
- ennemy.update(1,1,0,1,0,0)
|
|
|
- barrel1 = Barrel(10)
|
|
|
- barrel1.update(8,2,40,0,0,0)
|
|
|
- barrel2 = Barrel(11)
|
|
|
- barrel2.update(4,2,50,0,0,0)
|
|
|
- mine = Mine(20)
|
|
|
- mine.update(10, 2)
|
|
|
- entities = {0: ship, 1: ennemy, 20: mine}
|
|
|
-# entities = {0: ship, 1: ennemy, 10: barrel1, 11: barrel2, 20: mine}
|
|
|
- seen = [0, 1]
|
|
|
+ ent = entities[ent_id]
|
|
|
+ ent.update(*data)
|
|
|
+
|
|
|
+ entities = {k: v for k, v in entities.items() if k in seen}
|
|
|
# --->
|
|
|
|
|
|
- # log(entities)
|
|
|
grid.load_entities(entities)
|
|
|
|
|
|
- log(f"### turn {current_turn}")
|
|
|
+ log(f"### TURN {current_turn}")
|
|
|
+
|
|
|
# log(f"Owned Ships: {grid.owned_ships}")
|
|
|
log(f"Ennemy Ships: {grid.ennemy_ships}")
|
|
|
- log(f"Barrels: {grid.barrels}")
|
|
|
+# log(f"Barrels: {grid.barrels}")
|
|
|
# log(f"Mines: {grid.mines}")
|
|
|
log(f"Cannonballs: {grid.cannonballs}")
|
|
|
|
|
|
- grid.pre_update_moving_costs()
|
|
|
- grid.pre_evaluate_barrels_interest()
|
|
|
|
|
|
- for ship in grid.owned_ships:
|
|
|
- log(f"---- ship {ship.id} ---")
|
|
|
- log(f"ship: {ship}")
|
|
|
- grid.update_moving_costs(ship)
|
|
|
- allies = [s for s in grid.owned_ships if s is not ship]
|
|
|
-
|
|
|
- target = None
|
|
|
-
|
|
|
- if grid.barrels:
|
|
|
- grid.evaluate_barrels_interest(ship)
|
|
|
- log("barrels interest: {}".format({b.pos: f"{b.interest} ({b.distance}/{b.dispersal}/{b.mine_threat}/{b.alignement})" for b in grid.barrels}))
|
|
|
- if grid.ennemy_ships:
|
|
|
- grid.evaluate_ennemies_interest(ship)
|
|
|
- log("ennemies interest: {}".format({s.pos: f"{s.interest} ({s.distance}/{s.alignement}/{s.blocked_since}/{s.same_traject_since})" for s in grid.ennemy_ships}))
|
|
|
+ ### Evaluate
|
|
|
+ grid.pre_eval_barrels()
|
|
|
+
|
|
|
+ objectives = grid.eval_objectives()
|
|
|
+
|
|
|
+ grid.update_moving_costs()
|
|
|
+
|
|
|
+ ### Acquire
|
|
|
+ log("# Acquiring")
|
|
|
+
|
|
|
+ if objectives[GetBarrel]:
|
|
|
+ # objectives are shared between allies by priority
|
|
|
+ merged_objs = ObjectivesQueue.merge(*[obj_lst for ship, obj_lst in objectives[GetBarrel].items() if ship.owned])
|
|
|
+ aimed = []
|
|
|
+
|
|
|
+ # first objective
|
|
|
+ obj = merged_objs.get()
|
|
|
+ while not all(s.objective for s in grid.owned_ships):
|
|
|
+ if not obj.ship.objective and not obj.target in aimed:
|
|
|
+ obj.ship.objective = obj
|
|
|
+ aimed.append(obj.target)
|
|
|
+ if not merged_objs:
|
|
|
+ break
|
|
|
+ obj = merged_objs.get()
|
|
|
+
|
|
|
+ del merged_objs, aimed
|
|
|
|
|
|
- allies_targets = [a.target for a in allies]
|
|
|
-
|
|
|
- targetted_barrel = next((b for b in sorted(grid.barrels, key=lambda x: x.interest) if not b in allies_targets and not b.pos in ship.next_area), None)
|
|
|
- targetted_ennemy = next((s for s in sorted(grid.ennemy_ships, key=lambda x: x.interest)), None)
|
|
|
+ if objectives[Attack]:
|
|
|
+ for ship in grid.owned_ships:
|
|
|
+ ship.objective_fallback = objectives[Attack][ship].get()
|
|
|
+ else:
|
|
|
+ log("ERROR: no objectives")
|
|
|
|
|
|
- if not targetted_barrel and not targetted_ennemy:
|
|
|
- log("(!) No target, wait")
|
|
|
- ship.wait()
|
|
|
- continue
|
|
|
+ for ship in grid.owned_ships:
|
|
|
+ log(f"Ship {ship.id}: obj: {ship.objective}; next: {ship.objective_next}; fallback: {ship.objective_fallback}")
|
|
|
|
|
|
- target = targetted_barrel or targetted_ennemy
|
|
|
+ ### Plan
|
|
|
+ log("# Planning")
|
|
|
|
|
|
- log(f"target ({target.__class__.__name__}): {target}")
|
|
|
- ship.acquire(target)
|
|
|
+ for ship in grid.owned_ships:
|
|
|
|
|
|
- if type(target) is Ship:
|
|
|
- goto = grid.shooting_spot(ship, target).pos
|
|
|
+ log(f"---- ship {ship.id} ---")
|
|
|
+ log(f"ship: {ship}")
|
|
|
+
|
|
|
+ if ship.objective:
|
|
|
+ goto = ship.objective.target.pos
|
|
|
+ elif ship.objective_fallback:
|
|
|
+ goto = grid.shooting_spot(ship, ship.objective_fallback.target)
|
|
|
else:
|
|
|
- goto = target.pos
|
|
|
+ log("ERROR: No target")
|
|
|
+ continue
|
|
|
+
|
|
|
log(f"goto: {goto}")
|
|
|
|
|
|
- path = grid.path(ship.next_pos, ship.orientation, goto, limit=6000 // len(grid.owned_ships))
|
|
|
- log(f"path: {path}")
|
|
|
+ ship.path = grid.path(ship.next_pos, ship.orientation, goto, ship._moving_costs, limit=6000 // len(grid.owned_ships))
|
|
|
+
|
|
|
+ if ship.objective_next and ship.path:
|
|
|
+ ship.path += grid.path(goto,
|
|
|
+ ship.path[-1].orientation,
|
|
|
+ ship.objective_next.target.pos,
|
|
|
+ ship._moving_costs,
|
|
|
+ limit=6000 // len(grid.owned_ships)) or []
|
|
|
+
|
|
|
+ log(f"path: {ship.path}")
|
|
|
|
|
|
- if ship.move(path):
|
|
|
+ ### Process
|
|
|
+ log("# Processing")
|
|
|
+
|
|
|
+ for ship in grid.owned_ships:
|
|
|
+ if not ship.objective and not ship.objective_fallback:
|
|
|
+ log("No target: wait")
|
|
|
+ ship.wait()
|
|
|
+
|
|
|
+ if ship.move(ship.path):
|
|
|
continue
|
|
|
-
|
|
|
- if ship.fire_at_will(targetted_ennemy, allies=grid.owned_ships):
|
|
|
+
|
|
|
+ # no movement was required, can fire
|
|
|
+ if ship.fire_at_will(ship.objective_fallback.target, allies=grid.owned_ships):
|
|
|
continue
|
|
|
|
|
|
log("ERROR: Did not act, wait")
|