|
|
@@ -4,6 +4,7 @@
|
|
|
'''
|
|
|
import heapq
|
|
|
import sys
|
|
|
+import time
|
|
|
|
|
|
|
|
|
# TODO:
|
|
|
@@ -12,12 +13,15 @@ import sys
|
|
|
# * avoid getting blocked by a side-by-side with an ennemy
|
|
|
# * use a queue to choose the best shoot instead of a strict equality
|
|
|
# * why do mines explode when turning around?
|
|
|
+# * improve next_pos_proba: eliminate next positions if not passables
|
|
|
|
|
|
debug = True
|
|
|
|
|
|
+t0 = time.time()
|
|
|
+
|
|
|
def log(*msg):
|
|
|
if debug:
|
|
|
- print(*msg, file=sys.stderr)
|
|
|
+ print("{} - ".format(str(time.time() - t0)[1:5]), *msg, file=sys.stderr)
|
|
|
|
|
|
current_turn = 0
|
|
|
|
|
|
@@ -101,7 +105,7 @@ class BaseObjective(Base):
|
|
|
|
|
|
class GetBarrel(BaseObjective):
|
|
|
def _compute_interest(self):
|
|
|
- self.interest = 6 * self.distance + 9 * self.alignment + 3 * self.target.dispersal + self.target.mine_threat ** 2 - 36 * self.target.ennemy_near
|
|
|
+ self.interest = 6 * self.distance + 12 * self.alignment + 3 * self.target.dispersal + self.target.mine_threat ** 2 - 24 * self.target.ennemy_near
|
|
|
|
|
|
class Attack(BaseObjective):
|
|
|
def _compute_interest(self):
|
|
|
@@ -119,19 +123,36 @@ class PathNode(tuple):
|
|
|
return f"<{self[0]}, {self[1]}, c:{self.cost}, o:{self.orientation}>"
|
|
|
|
|
|
class Grid(Base):
|
|
|
- def __init__(self):
|
|
|
- self.w = 23
|
|
|
- self.h = 21
|
|
|
+ w = 23
|
|
|
+ h = 21
|
|
|
+ _neighbors = {}
|
|
|
+ _next_cell = {}
|
|
|
|
|
|
- self._neighbors = {}
|
|
|
- for x in range(-1, self.w + 1):
|
|
|
- for y in range(-1, self.h + 1):
|
|
|
- self.cache_neighbors(x, y)
|
|
|
-
|
|
|
+ def __init__(self):
|
|
|
self.load_entities({})
|
|
|
|
|
|
+ @classmethod
|
|
|
+ def preload(cls):
|
|
|
+ cls._neighbors = {}
|
|
|
+ for x in range(-1, cls.w + 1):
|
|
|
+ for y in range(-1, cls.h + 1):
|
|
|
+ cls.cache_neighbors(x, y)
|
|
|
+
|
|
|
+ cls._next_cell = {}
|
|
|
+ for x in range(0, cls.w):
|
|
|
+ for y in range(0, cls.h):
|
|
|
+ cls.cache_next_cell(x, y)
|
|
|
+
|
|
|
+ for x in range(0, cls.w):
|
|
|
+ for y in range(0, cls.h):
|
|
|
+ Ship.cache_area(x, y)
|
|
|
+
|
|
|
+ @classmethod
|
|
|
+ def contains(cls, x, y):
|
|
|
+ return 0 <= x < cls.w and 0 <= y < cls.h
|
|
|
+
|
|
|
def __contains__(self, key):
|
|
|
- return 0 <= key[0] < self.w and 0 <= key[1] < self.h
|
|
|
+ return self.contains(*key)
|
|
|
|
|
|
def __iter__(self):
|
|
|
for item in ((x, y) for x in range(self.w) for y in range(self.h)):
|
|
|
@@ -145,7 +166,7 @@ class Grid(Base):
|
|
|
ghost_mines = []
|
|
|
if hasattr(self, "mines"):
|
|
|
for m in self.mines:
|
|
|
- if not m.pos in [e.pos for e in entities.values() if type(e) is Mine]:
|
|
|
+ if not m.id in entities:
|
|
|
if all((self.manhattan(m.pos, ship.pos) > 5) for ship in self.owned_ships):
|
|
|
m.ghost = True
|
|
|
ghost_mines.append(m)
|
|
|
@@ -186,7 +207,7 @@ class Grid(Base):
|
|
|
s.allies = [other for other in self.ennemy_ships if other is not s]
|
|
|
|
|
|
for s in self.ships:
|
|
|
- s.next_positions = s.next_pos_proba(4)
|
|
|
+ s.next_pos_proba(2)
|
|
|
|
|
|
self.update_moving_costs()
|
|
|
|
|
|
@@ -198,27 +219,33 @@ class Grid(Base):
|
|
|
|
|
|
for s in self.owned_ships:
|
|
|
|
|
|
- s._can_move = {c: (s.moving_cost(*c) < 1000) for c in [s.front, s.front_left, s.left, s.front_right,
|
|
|
- s.right, s.back_left, s.back_right]}
|
|
|
+ s._can_move = {c: (s.moving_cost(*c) < 1000)
|
|
|
+ for c in [s.front, s.front_left, s.left, s.front_right,
|
|
|
+ s.right, s.back_left, s.back_right]}
|
|
|
+
|
|
|
s.objectives = ObjectivesQueue()
|
|
|
s.ennemies = ObjectivesQueue()
|
|
|
|
|
|
for b in self.barrels:
|
|
|
obj = GetBarrel(b)
|
|
|
- obj.eval(s.next_pos if s.speed else s.prow, s.orientation)
|
|
|
+ obj.eval(s.pos, s.orientation)
|
|
|
s.objectives.put(obj)
|
|
|
|
|
|
for e in self.ennemy_ships:
|
|
|
obj = Attack(e)
|
|
|
- obj.eval(s.next_pos, s.orientation)
|
|
|
+ obj.eval(s.pos, s.orientation)
|
|
|
s.ennemies.put(obj)
|
|
|
|
|
|
+
|
|
|
def at(self, x, y):
|
|
|
try:
|
|
|
return self.index[(x, y)]
|
|
|
except KeyError:
|
|
|
return None
|
|
|
|
|
|
+ def borderline(self, x, y):
|
|
|
+ return x == -1 or y == -1 or x == self.w or y == self.h
|
|
|
+
|
|
|
def collision_at(self, x, y):
|
|
|
e = self.at(x, y)
|
|
|
return type(e) in [Mine, Ship, Cannonball] or not (x, y) in self.__iter__()
|
|
|
@@ -260,26 +287,22 @@ class Grid(Base):
|
|
|
if other is ship:
|
|
|
continue
|
|
|
dist = self.manhattan(ship.pos, other.pos)
|
|
|
- if dist > 8:
|
|
|
+ if dist > 6:
|
|
|
continue
|
|
|
- if not other.speed:
|
|
|
- for c in other.area:
|
|
|
+ for c in self.zone(other.pos, 3):
|
|
|
+ ship._moving_costs[c] += 25
|
|
|
+ next_positions = other.next_pos_proba()
|
|
|
+ for c, proba in next_positions[1].items():
|
|
|
+ if proba > 20 and not c in ship.area:
|
|
|
ship._moving_costs[c] += 1000
|
|
|
- else:
|
|
|
- for c in self.zone(other.next_pos, 4):
|
|
|
- if c in other.next_positions[1]:
|
|
|
- if other.next_positions[1][c] > 10:
|
|
|
- ship._moving_costs[c] += 1000
|
|
|
- else:
|
|
|
- ship._moving_costs[c] += 100
|
|
|
- else:
|
|
|
- ship._moving_costs[c] += 20
|
|
|
+ else:
|
|
|
+ ship._moving_costs[c] += 100
|
|
|
|
|
|
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):
|
|
|
+ for x, y in self.zone(target_pos, 8):
|
|
|
if ship.moving_cost(x, y) > 100:
|
|
|
continue
|
|
|
if self.manhattan((x, y), target_pos) <= 2:
|
|
|
@@ -323,14 +346,10 @@ class Grid(Base):
|
|
|
xa, ya = from_
|
|
|
xb, yb = to_
|
|
|
return abs(xa - xb) + abs(ya - yb)
|
|
|
-
|
|
|
- def zone(self, center, radius):
|
|
|
- buffer = frozenset([center])
|
|
|
- for _ in range(0, radius):
|
|
|
- current = buffer
|
|
|
- for x, y in current:
|
|
|
- buffer |= frozenset(self.abs_neighbors(x, y))
|
|
|
- return [c for c in buffer if 0 <= c[0] < self.w and 0 <= c[1] < self.h]
|
|
|
+
|
|
|
+ @classmethod
|
|
|
+ def zone(cls, center, radius):
|
|
|
+ return [(x, y) for x in range(0, cls.w) for y in range(0, cls.h) if cls.manhattan(center, (x, y)) <= radius]
|
|
|
|
|
|
@staticmethod
|
|
|
def closest(from_, in_):
|
|
|
@@ -384,12 +403,12 @@ class Grid(Base):
|
|
|
d -= 6
|
|
|
return d
|
|
|
|
|
|
- @staticmethod
|
|
|
- def next_cell(x, y, d, repeat=1):
|
|
|
- for _ in range(repeat):
|
|
|
- dx, dy = Grid.directions(y)[d]
|
|
|
- x, y = x + dx, y + dy
|
|
|
- return x, y
|
|
|
+# @staticmethod
|
|
|
+# def next_cell(x, y, d, repeat=1):
|
|
|
+# for _ in range(repeat):
|
|
|
+# dx, dy = Grid.directions(y)[d]
|
|
|
+# x, y = x + dx, y + dy
|
|
|
+# return x, y
|
|
|
|
|
|
@staticmethod
|
|
|
def symetry(d):
|
|
|
@@ -399,16 +418,38 @@ class Grid(Base):
|
|
|
def abs_neighbors(x, y):
|
|
|
return ((x + dx, y + dy) for dx, dy in Grid.directions(y))
|
|
|
|
|
|
- def cache_neighbors(self, xc, yc):
|
|
|
- self._neighbors[(xc, yc)] = [(x, y) for x, y in Grid.abs_neighbors(xc, yc) if 0 <= x < self.w and 0 <= y < self.h]
|
|
|
+ @classmethod
|
|
|
+ def cache_neighbors(cls, xc, yc):
|
|
|
+ cls._neighbors[(xc, yc)] = [(x, y) for x, y in Grid.abs_neighbors(xc, yc) if 0 <= x < cls.w and 0 <= y < cls.h]
|
|
|
|
|
|
- def neighbors(self, x, y):
|
|
|
+ @classmethod
|
|
|
+ def neighbors(cls, x, y):
|
|
|
try:
|
|
|
- return self._neighbors[(x, y)]
|
|
|
+ return cls._neighbors[(x, y)]
|
|
|
except KeyError:
|
|
|
- self.cache_neighbors(x, y)
|
|
|
- return self._neighbors[(x, y)]
|
|
|
-
|
|
|
+ cls.cache_neighbors(x, y)
|
|
|
+ return cls._neighbors[(x, y)]
|
|
|
+
|
|
|
+ @classmethod
|
|
|
+ def abs_next_cell(cls, x, y, d):
|
|
|
+ dx, dy = Grid.directions(y)[d]
|
|
|
+ return x + dx, y + dy
|
|
|
+
|
|
|
+ @classmethod
|
|
|
+ def cache_next_cell(cls, x, y):
|
|
|
+ for d, dv in enumerate(Grid.directions(y)):
|
|
|
+ dx, dy = dv
|
|
|
+ cls._next_cell[(x, y, d)] = (x + dx, y + dy)
|
|
|
+
|
|
|
+ @classmethod
|
|
|
+ def next_cell(cls, x, y, d, repeat=1):
|
|
|
+ for _ in range(repeat):
|
|
|
+ try:
|
|
|
+ x, y = cls._next_cell[(x, y, d)]
|
|
|
+ except KeyError:
|
|
|
+ x, y = cls.abs_next_cell(x, y, d)
|
|
|
+ return x, y
|
|
|
+
|
|
|
def rotate(self, center, coordinates, rotations):
|
|
|
if coordinates == [center] or rotations % 6 == 0:
|
|
|
return coordinates
|
|
|
@@ -456,7 +497,7 @@ class Grid(Base):
|
|
|
if previous != origin or incl_start:
|
|
|
path.insert(0, previous)
|
|
|
previous = previous.parent
|
|
|
- return inertia_path + path
|
|
|
+ return inertia_path + path, iteration
|
|
|
|
|
|
neighbors = self.neighbors(*current)
|
|
|
|
|
|
@@ -464,11 +505,10 @@ class Grid(Base):
|
|
|
|
|
|
if (x, y) == current.parent:
|
|
|
continue
|
|
|
-
|
|
|
+
|
|
|
iteration += 1
|
|
|
if break_on > 0 and iteration >= break_on:
|
|
|
broken = True
|
|
|
- break
|
|
|
|
|
|
moving_cost = moving_costs.get((x, y), 1000)
|
|
|
if moving_cost >= 1000:
|
|
|
@@ -480,7 +520,8 @@ class Grid(Base):
|
|
|
# change direction one degree at a time
|
|
|
continue
|
|
|
|
|
|
- if any(moving_costs.get(c, 1000) >= 1000 for c in Ship.get_area(x, y, d)):
|
|
|
+ area = Ship.get_area(x, y, d)
|
|
|
+ if any(moving_costs.get(c, 1000) >= 1000 for c in area):
|
|
|
continue
|
|
|
|
|
|
cost = current.cost + moving_cost + diff * 10
|
|
|
@@ -495,8 +536,8 @@ class Grid(Base):
|
|
|
node.cost = cost
|
|
|
node.orientation = d
|
|
|
nodes.put(node, priority)
|
|
|
- else:
|
|
|
- return None
|
|
|
+
|
|
|
+ return None, iteration
|
|
|
|
|
|
class Entity(Base):
|
|
|
def __init__(self, ent_id):
|
|
|
@@ -530,9 +571,11 @@ class Ship(Entity):
|
|
|
SPEED_UP = 2
|
|
|
TURN_LEFT = 3
|
|
|
TURN_RIGHT = 4
|
|
|
- MOVES = [SLOW_DOWN, SPEED_UP, TURN_LEFT, TURN_RIGHT]
|
|
|
+ MOVES = [SPEED_UP, TURN_LEFT, TURN_RIGHT, SLOW_DOWN]
|
|
|
COMMANDS = {SLOW_DOWN: "SLOWER", SPEED_UP: "FASTER", TURN_LEFT: "PORT", TURN_RIGHT: "STARBOARD", None: "NONE"}
|
|
|
|
|
|
+ areas = {}
|
|
|
+
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
super().__init__(*args, **kwargs)
|
|
|
self.x, self.y = 0, 0
|
|
|
@@ -550,6 +593,7 @@ class Ship(Entity):
|
|
|
self.last_action = ""
|
|
|
self.allies = []
|
|
|
self._moving_costs = {}
|
|
|
+ self.cached_next_pos_proba = {}
|
|
|
|
|
|
self.objectives = ObjectivesQueue()
|
|
|
self.ennemies = ObjectivesQueue()
|
|
|
@@ -566,6 +610,21 @@ class Ship(Entity):
|
|
|
def __repr__(self):
|
|
|
return f"<Ship {self.id}: pos=({self.x}, {self.y}), orientation={self.orientation}, speed={self.speed}, blocked={self.blocked_since}, last_fire={self.last_fire}, next_pos={self.next_pos}, area={self.area}>"
|
|
|
|
|
|
+ @classmethod
|
|
|
+ def cache_area(cls, x, y):
|
|
|
+ for d in range(3):
|
|
|
+ area = [Grid.next_cell(x, y, d), (x, y), Grid.next_cell(x, y, d + 3)]
|
|
|
+ Ship.areas[(x, y, d)] = area
|
|
|
+ Ship.areas[(x, y, d + 3)] = list(reversed(area))
|
|
|
+
|
|
|
+ @classmethod
|
|
|
+ def get_area(cls, x, y, d):
|
|
|
+ try:
|
|
|
+ return list(Ship.areas[(x, y, d)])
|
|
|
+ except KeyError:
|
|
|
+ log(f"Error: area key missing {(x, y, d)}")
|
|
|
+ return []
|
|
|
+
|
|
|
def update(self, x, y, *args):
|
|
|
previous_state = self.state()
|
|
|
previous_traject = self.traject()
|
|
|
@@ -623,22 +682,17 @@ class Ship(Entity):
|
|
|
def get_pos_in(cls, current, speed, orientation, in_=1):
|
|
|
return Grid.next_cell(*current, orientation, repeat=speed * in_)
|
|
|
|
|
|
- @classmethod
|
|
|
- def get_area(cls, x, y, orientation):
|
|
|
- prow = Grid.next_cell(x, y, orientation)
|
|
|
- stern = Grid.next_cell(x, y, Grid.add_directions(orientation, 3))
|
|
|
- return [prow, (x, y), stern]
|
|
|
-
|
|
|
def get_next_pos(self, in_=1):
|
|
|
return self.get_pos_in(self.pos, self.speed, self.orientation, in_)
|
|
|
|
|
|
def next_pos_proba(self, in_=1):
|
|
|
|
|
|
+ if in_ in self.cached_next_pos_proba:
|
|
|
+ return {k: v for k, v in self.cached_next_pos_proba.items() if k <= in_}
|
|
|
+
|
|
|
# guess next positions
|
|
|
positions = {0: [Position(self.pos, self.orientation, self.speed)]}
|
|
|
-
|
|
|
for i in range(in_):
|
|
|
-
|
|
|
positions[i + 1] = []
|
|
|
|
|
|
for p in positions[i]:
|
|
|
@@ -681,7 +735,7 @@ class Ship(Entity):
|
|
|
if self.moving_cost(*c) >= 1000:
|
|
|
continue
|
|
|
proba[i][c] = proba[i].get(c, 0) + 10
|
|
|
-
|
|
|
+
|
|
|
# involve the moving cost
|
|
|
for i in proba:
|
|
|
for c in proba[i]:
|
|
|
@@ -690,10 +744,12 @@ class Ship(Entity):
|
|
|
# if ship is blocked, current area is more accurate
|
|
|
for c in self.area:
|
|
|
proba[i][c] = proba[i].get(c, 0) + 40 * self.blocked_since
|
|
|
-
|
|
|
+
|
|
|
+ self.cached_next_pos_proba = proba
|
|
|
+
|
|
|
return proba
|
|
|
|
|
|
- def guess_next_positions(self, in_=3):
|
|
|
+ def guess_next_positions(self, in_=1):
|
|
|
proba = self.next_pos_proba(in_)
|
|
|
best = {}
|
|
|
for i in proba:
|
|
|
@@ -710,10 +766,12 @@ class Ship(Entity):
|
|
|
return self._moving_costs.get((x, y), 1000)
|
|
|
|
|
|
def can_turn_left(self):
|
|
|
- return self._can_move[self.left] and self._can_move[self.back_right]
|
|
|
+ return (self._can_move[self.left] or grid.borderline(*self.left)) \
|
|
|
+ and (self._can_move[self.back_right] or grid.borderline(*self.right))
|
|
|
|
|
|
def can_turn_right(self):
|
|
|
- return self._can_move[self.right] and self._can_move[self.back_left]
|
|
|
+ return (self._can_move[self.right] or grid.borderline(*self.right)) \
|
|
|
+ and (self._can_move[self.back_left] or grid.borderline(*self.back_left))
|
|
|
|
|
|
def can_move_fwd(self):
|
|
|
return self._can_move[self.front]
|
|
|
@@ -721,20 +779,44 @@ class Ship(Entity):
|
|
|
def can_move(self):
|
|
|
return self.can_move_fwd() or self.can_turn_left() or self.can_turn_left()
|
|
|
|
|
|
+ def area_after_moving(self, move):
|
|
|
+ new_speed = self.speed
|
|
|
+ new_orientation = self.orientation
|
|
|
+ if move == Ship.SPEED_UP:
|
|
|
+ new_speed += 1
|
|
|
+ elif move == Ship.SLOW_DOWN:
|
|
|
+ new_speed -= 1
|
|
|
+ elif move == Ship.TURN_LEFT:
|
|
|
+ new_orientation = Grid.add_directions(self.orientation, 1)
|
|
|
+ elif move == Ship.TURN_RIGHT:
|
|
|
+ new_orientation = Grid.add_directions(self.orientation, -1)
|
|
|
+
|
|
|
+ new_pos = self.get_next_cell(new_speed)
|
|
|
+ return self.get_area(*new_pos, new_orientation)
|
|
|
+
|
|
|
def move(self, path):
|
|
|
-
|
|
|
+ broken = False
|
|
|
+
|
|
|
if path:
|
|
|
planned = self._plan_move(path)
|
|
|
|
|
|
if path is None:
|
|
|
if self.can_move():
|
|
|
- if self.can_move_fwd():
|
|
|
- planned = Ship.SPEED_UP
|
|
|
- elif self.can_turn_left():
|
|
|
- planned = Ship.TURN_LEFT
|
|
|
- elif self.can_turn_right():
|
|
|
- planned = Ship.TURN_RIGHT
|
|
|
- log(f"(!) broken: automove ({planned})")
|
|
|
+ available = {}
|
|
|
+
|
|
|
+ broken = True
|
|
|
+ if not self.speed and self.can_move_fwd():
|
|
|
+ available[Ship.SPEED_UP] = 0
|
|
|
+ if self.can_turn_left():
|
|
|
+ available[Ship.TURN_LEFT] = 0
|
|
|
+ if self.can_turn_right():
|
|
|
+ available[Ship.TURN_RIGHT] = 0
|
|
|
+
|
|
|
+ for m in available:
|
|
|
+ available[m] = sum([self.moving_cost(*c) for c in self.area_after_moving(m)])
|
|
|
+
|
|
|
+ planned = min(available.items(), key=lambda x: x[1])[0]
|
|
|
+ log(f"(!) broken: automove ({Ship.COMMANDS[planned]})")
|
|
|
else:
|
|
|
log(f"(!) broken: can not move")
|
|
|
return False
|
|
|
@@ -745,21 +827,8 @@ class Ship(Entity):
|
|
|
available_moves = [next_move] + [m for m in Ship.MOVES if m != planned]
|
|
|
|
|
|
for move in available_moves:
|
|
|
+ new_area = self.area_after_moving(move)
|
|
|
|
|
|
- new_speed = self.speed
|
|
|
- new_orientation = self.orientation
|
|
|
- if move == Ship.SPEED_UP:
|
|
|
- new_speed += 1
|
|
|
- elif move == Ship.SLOW_DOWN:
|
|
|
- new_speed -= 1
|
|
|
- elif move == Ship.TURN_LEFT:
|
|
|
- new_orientation = Grid.add_directions(self.orientation, 1)
|
|
|
- elif move == Ship.TURN_RIGHT:
|
|
|
- new_orientation = Grid.add_directions(self.orientation, -1)
|
|
|
-
|
|
|
- new_pos = self.get_next_cell(new_speed)
|
|
|
- new_area = self.get_area(*new_pos, new_orientation)
|
|
|
-
|
|
|
# special: extra-grid cells are not consider as collisions since a part of the ship can go there
|
|
|
if any((self.moving_cost(*c) >= 1000 and c in grid) for c in new_area):
|
|
|
log(f"/!\ Danger: planned move <{Ship.COMMANDS[move]}> would lead to collision")
|
|
|
@@ -767,8 +836,13 @@ class Ship(Entity):
|
|
|
next_move = move
|
|
|
break
|
|
|
else:
|
|
|
+# if broken:
|
|
|
+# log("* No collision-free move was found, try automove")
|
|
|
+# self.auto_move(*self.goto)
|
|
|
+# return True
|
|
|
+# else:
|
|
|
log("* No collision-free move was found, go to the initial one")
|
|
|
-
|
|
|
+
|
|
|
if next_move == Ship.SPEED_UP:
|
|
|
self.speed_up()
|
|
|
elif next_move == Ship.SLOW_DOWN:
|
|
|
@@ -832,7 +906,7 @@ class Ship(Entity):
|
|
|
elif diff < 0:
|
|
|
return Ship.TURN_RIGHT
|
|
|
|
|
|
- elif next_flag > 3 or (next_flag > 2 and afternext_flag >= (next_flag + 2)):
|
|
|
+ elif next_flag > 3:
|
|
|
return Ship.SPEED_UP
|
|
|
|
|
|
return None
|
|
|
@@ -850,8 +924,7 @@ class Ship(Entity):
|
|
|
for ally in allies:
|
|
|
avoid += ally.mobility_zone
|
|
|
|
|
|
- next_positions = target.guess_next_positions(in_=4)
|
|
|
- log(next_positions)
|
|
|
+ next_positions = target.guess_next_positions(4)
|
|
|
for t, next_pos in next_positions.items():
|
|
|
dist = Grid.manhattan(self.prow, next_pos)
|
|
|
if dist > self.SCOPE:
|
|
|
@@ -865,7 +938,7 @@ class Ship(Entity):
|
|
|
log(f"[x] precise shoot: dt={dt}, pos={next_pos}")
|
|
|
ship.fire(*next_pos)
|
|
|
return True
|
|
|
-
|
|
|
+
|
|
|
# give a try
|
|
|
next_pos = next_positions[2]
|
|
|
if not next_pos in avoid:
|
|
|
@@ -952,10 +1025,9 @@ map_entity = {"SHIP": Ship,
|
|
|
"MINE": Mine,
|
|
|
"CANNONBALL": Cannonball}
|
|
|
|
|
|
+Grid.preload()
|
|
|
grid = Grid()
|
|
|
|
|
|
-
|
|
|
-
|
|
|
### *** Main Loop ***
|
|
|
|
|
|
while True:
|
|
|
@@ -964,17 +1036,21 @@ while True:
|
|
|
|
|
|
# <--- get input
|
|
|
my_ship_count, entity_count = int(input()), int(input())
|
|
|
+ ent_input = [input().split() for _ in range(entity_count)]
|
|
|
+ # --->
|
|
|
+
|
|
|
+ log(f"### TURN {current_turn}")
|
|
|
+ log(">> Load input")
|
|
|
+ # <--- load input
|
|
|
previous_ent, entities = grid.entities, {}
|
|
|
- for _ in range(entity_count):
|
|
|
- ent_id, ent_type, *data = input().split()
|
|
|
+ for e in ent_input:
|
|
|
+ ent_id, ent_type, *data = e
|
|
|
ent_id = int(ent_id)
|
|
|
entities[ent_id] = grid.entities.get(ent_id, map_entity[ent_type](ent_id))
|
|
|
entities[ent_id].update(*data)
|
|
|
- # --->
|
|
|
-
|
|
|
+
|
|
|
grid.load_entities(entities)
|
|
|
-
|
|
|
- log(f"### TURN {current_turn}")
|
|
|
+ # --->
|
|
|
|
|
|
# log(f"Owned Ships: {grid.owned_ships}")
|
|
|
log(f"Ennemy Ships: {grid.ennemy_ships}")
|
|
|
@@ -991,7 +1067,6 @@ while True:
|
|
|
while not all(s.objective for s in grid.owned_ships):
|
|
|
try:
|
|
|
acquired = sorted([(s, s.objectives.get()) for s in grid.owned_ships if not s.objective], key= lambda x: x[1].interest)
|
|
|
-
|
|
|
for s, o in acquired:
|
|
|
if not s.objective and not any(al.objective.target is o.target for al in s.allies if al.objective):
|
|
|
s.objective = o
|
|
|
@@ -1017,14 +1092,15 @@ while True:
|
|
|
log("ERROR: No target")
|
|
|
continue
|
|
|
|
|
|
- ship.path = grid.path(ship.pos,
|
|
|
+ ship.path, its = grid.path(ship.pos,
|
|
|
ship.orientation,
|
|
|
ship.goto,
|
|
|
moving_costs=ship._moving_costs,
|
|
|
inertia=ship.speed,
|
|
|
limit=(max_it - it_consumed))
|
|
|
+ it_consumed += its
|
|
|
|
|
|
- if ship.objective and ship.path:
|
|
|
+ if ship.objective and ship.path and ship.path[-1] == ship.goto:
|
|
|
while ship.objectives and len(ship.path) < 10:
|
|
|
pos, d = ship.path[-1], ship.path[-1].orientation
|
|
|
|
|
|
@@ -1033,11 +1109,14 @@ while True:
|
|
|
|
|
|
ship.objectives_next.append(current_obj)
|
|
|
|
|
|
- new_path = grid.path(pos, d,
|
|
|
+ new_path, its = grid.path(pos, d,
|
|
|
current_obj.target.pos,
|
|
|
ship._moving_costs,
|
|
|
- limit=(max_it - it_consumed)) or []
|
|
|
- if new_path:
|
|
|
+ limit=(max_it - it_consumed))
|
|
|
+
|
|
|
+ it_consumed += its
|
|
|
+
|
|
|
+ if new_path and new_path[-1] == current_obj.target.pos:
|
|
|
ship.path += new_path
|
|
|
else:
|
|
|
break
|