|
@@ -8,10 +8,12 @@ import sys
|
|
|
|
|
|
|
|
# TODO:
|
|
# TODO:
|
|
|
# * add an esquive manoeuvre / try to avoid cannonballs
|
|
# * add an esquive manoeuvre / try to avoid cannonballs
|
|
|
-# * consider targeting rum barrels if an ennemy is nearer
|
|
|
|
|
|
|
+# * consider firing at rum barrels if an ennemy is nearer
|
|
|
# * compute first and second target instead of only one to anticipate the next move
|
|
# * 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
|
|
# * if an enemy is near a mine, shoot the mine instead of the ship
|
|
|
-# * compute the probabilities of presence of an ennemy on dirrefent coords at next turn
|
|
|
|
|
|
|
+# * compute the probabilities of presence of an ennemy on different coords at next turn
|
|
|
|
|
+# * find a way to change direction without slowing down if possible
|
|
|
|
|
+# * avoid getting blocked by a side-by-side with an ennemy
|
|
|
|
|
|
|
|
debug = True
|
|
debug = True
|
|
|
|
|
|
|
@@ -95,9 +97,7 @@ class GetBarrel(BaseObjective):
|
|
|
|
|
|
|
|
class Attack(BaseObjective):
|
|
class Attack(BaseObjective):
|
|
|
def _compute_interest(self):
|
|
def _compute_interest(self):
|
|
|
- self.interest = 7 * self.distance + 3 * self.alignment - 20 * self.target.blocked_since - 10 * self.target.same_traject_since
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
|
|
+ self.interest = 7 * self.distance + 3 * self.alignment + self.target.stock // 4 - 20 * self.target.blocked_since
|
|
|
|
|
|
|
|
class PathNode(tuple):
|
|
class PathNode(tuple):
|
|
|
def __new__(self, x, y, parent=None):
|
|
def __new__(self, x, y, parent=None):
|
|
@@ -185,7 +185,7 @@ class Grid(Base):
|
|
|
|
|
|
|
|
for b in self.barrels:
|
|
for b in self.barrels:
|
|
|
obj = GetBarrel(b)
|
|
obj = GetBarrel(b)
|
|
|
- obj.eval(s.next_pos, s.orientation)
|
|
|
|
|
|
|
+ obj.eval(s.next_pos if s.speed else s.prow, s.orientation)
|
|
|
s.objectives.put(obj)
|
|
s.objectives.put(obj)
|
|
|
|
|
|
|
|
for e in self.ennemy_ships:
|
|
for e in self.ennemy_ships:
|
|
@@ -268,7 +268,7 @@ class Grid(Base):
|
|
|
interest += 10
|
|
interest += 10
|
|
|
|
|
|
|
|
# priorize spots at distance 5 from active ship
|
|
# priorize spots at distance 5 from active ship
|
|
|
- interest -= 10 * abs(5 - self.manhattan((x, y), ship.pos))
|
|
|
|
|
|
|
+ interest -= 20 * abs(5 - self.manhattan((x, y), ship.pos))
|
|
|
|
|
|
|
|
shooting_spots.put((x, y), interest)
|
|
shooting_spots.put((x, y), interest)
|
|
|
|
|
|
|
@@ -352,6 +352,13 @@ class Grid(Base):
|
|
|
d -= 6
|
|
d -= 6
|
|
|
return d
|
|
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
|
|
@staticmethod
|
|
|
def symetry(d):
|
|
def symetry(d):
|
|
|
return d + 3 if d < 3 else d - 3
|
|
return d + 3 if d < 3 else d - 3
|
|
@@ -392,7 +399,6 @@ class Grid(Base):
|
|
|
nodes = Queue()
|
|
nodes = Queue()
|
|
|
break_on, iteration = limit, 0
|
|
break_on, iteration = limit, 0
|
|
|
|
|
|
|
|
-
|
|
|
|
|
origin = PathNode(*origin)
|
|
origin = PathNode(*origin)
|
|
|
origin.orientation = orientat0
|
|
origin.orientation = orientat0
|
|
|
|
|
|
|
@@ -422,7 +428,7 @@ class Grid(Base):
|
|
|
if break_on > 0 and iteration >= break_on:
|
|
if break_on > 0 and iteration >= break_on:
|
|
|
return None
|
|
return None
|
|
|
|
|
|
|
|
- moving_cost = moving_costs[(x, y)]
|
|
|
|
|
|
|
+ moving_cost = moving_costs.get((x, y), 1000)
|
|
|
if moving_cost >= 1000:
|
|
if moving_cost >= 1000:
|
|
|
continue
|
|
continue
|
|
|
|
|
|
|
@@ -432,10 +438,14 @@ class Grid(Base):
|
|
|
# change direction one degree at a time
|
|
# change direction one degree at a time
|
|
|
continue
|
|
continue
|
|
|
|
|
|
|
|
|
|
+ if any(moving_costs.get(c, 1000) >= 1000 for c in Ship.get_area(x, y, d)):
|
|
|
|
|
+ continue
|
|
|
|
|
+
|
|
|
cost = current.cost + moving_cost + diff * 10
|
|
cost = current.cost + moving_cost + diff * 10
|
|
|
- if diff != 0 and any(moving_costs[c] >= 1000 for c in neighbors):
|
|
|
|
|
- # a direction change here is dangerous
|
|
|
|
|
- cost += 50
|
|
|
|
|
|
|
+# if diff != 0 and any(moving_costs.get(c, 1000) >= 1000 for c in neighbors):
|
|
|
|
|
+# # a direction change here is dangerous
|
|
|
|
|
+# cost += 100
|
|
|
|
|
+
|
|
|
|
|
|
|
|
priority = cost + 10 * Grid.manhattan((x, y), target)
|
|
priority = cost + 10 * Grid.manhattan((x, y), target)
|
|
|
|
|
|
|
@@ -514,6 +524,8 @@ class Ship(Entity):
|
|
|
self.objective = None
|
|
self.objective = None
|
|
|
self.objective_next = None
|
|
self.objective_next = None
|
|
|
self.target_ennemy = None
|
|
self.target_ennemy = None
|
|
|
|
|
+
|
|
|
|
|
+ self.goto = None
|
|
|
self.path = []
|
|
self.path = []
|
|
|
|
|
|
|
|
self.area = Ship.get_area(self.x, self.y, self.orientation)
|
|
self.area = Ship.get_area(self.x, self.y, self.orientation)
|
|
@@ -588,29 +600,56 @@ class Ship(Entity):
|
|
|
|
|
|
|
|
# slow down
|
|
# slow down
|
|
|
if self.speed > 0:
|
|
if self.speed > 0:
|
|
|
- area = self.get_area(*self.get_pos_in(self.pos, self.speed + 1, self.orientation), self.orientation)
|
|
|
|
|
|
|
+ area = self.get_area(*self.get_pos_in(self.pos, self.speed - 1, self.orientation), self.orientation)
|
|
|
for c in area:
|
|
for c in area:
|
|
|
proba[c] = proba.get(c, 0) + 10
|
|
proba[c] = proba.get(c, 0) + 10
|
|
|
|
|
|
|
|
for c in proba:
|
|
for c in proba:
|
|
|
proba[c] -= self.moving_cost(*c)
|
|
proba[c] -= self.moving_cost(*c)
|
|
|
|
|
|
|
|
|
|
+ for c in self.area:
|
|
|
|
|
+ proba[c] = proba.get(c, 0) + 50 * self.blocked_since
|
|
|
|
|
+
|
|
|
best = max(proba.items(), key=lambda x: x[1])
|
|
best = max(proba.items(), key=lambda x: x[1])
|
|
|
|
|
|
|
|
return best[0]
|
|
return best[0]
|
|
|
|
|
|
|
|
def get_next_cell(self, in_=1):
|
|
def get_next_cell(self, in_=1):
|
|
|
- x, y = self.x, self.y
|
|
|
|
|
- for _ in range(in_):
|
|
|
|
|
- dx, dy = Grid.directions(y)[self.orientation]
|
|
|
|
|
- x, y = x + dx, y + dy
|
|
|
|
|
- return x, y
|
|
|
|
|
|
|
+ return Grid.next_cell(self.x, self.y, self.orientation, repeat=in_)
|
|
|
|
|
|
|
|
def in_current_direction(self, x, y):
|
|
def in_current_direction(self, x, y):
|
|
|
return self.orientation == Grid.direction_to(*self.pos, x, y)
|
|
return self.orientation == Grid.direction_to(*self.pos, x, y)
|
|
|
|
|
|
|
|
def moving_cost(self, x, y):
|
|
def moving_cost(self, x, y):
|
|
|
- return self._moving_costs[(x, y)]
|
|
|
|
|
|
|
+ return self._moving_costs.get((x, y), 1000)
|
|
|
|
|
+
|
|
|
|
|
+ def cant_move(self):
|
|
|
|
|
+
|
|
|
|
|
+ front = Grid.next_cell(*self.prow, self.orientation)
|
|
|
|
|
+ front_left = Grid.next_cell(*self.prow, Grid.add_directions(self.orientation, 1))
|
|
|
|
|
+ left = Grid.next_cell(*self.prow, Grid.add_directions(self.orientation, 2))
|
|
|
|
|
+ front_right = Grid.next_cell(*self.prow, Grid.add_directions(self.orientation, -1))
|
|
|
|
|
+ right = Grid.next_cell(*self.prow, Grid.add_directions(self.orientation, -2))
|
|
|
|
|
+
|
|
|
|
|
+ back_left = Grid.next_cell(*self.stern, Grid.add_directions(self.orientation, 2))
|
|
|
|
|
+ back_right = Grid.next_cell(*self.stern, Grid.add_directions(self.orientation, -2))
|
|
|
|
|
+
|
|
|
|
|
+ blocked = {c: (self.moving_cost(*c) >= 1000) for c in [front, front_left, left,
|
|
|
|
|
+ front_right, right,
|
|
|
|
|
+ back_left, back_right]}
|
|
|
|
|
+
|
|
|
|
|
+ if all(blocked[i] for i in [front, front_left, front_right, left, right]):
|
|
|
|
|
+ # surrounded
|
|
|
|
|
+ return True
|
|
|
|
|
+ elif (blocked[front_left] and blocked[left]) or (blocked[front_right] and blocked[right]):
|
|
|
|
|
+ # side by side
|
|
|
|
|
+ return True
|
|
|
|
|
+ elif blocked[front] and ((blocked[front_left] and blocked[back_right]) or (blocked[front_right] and blocked[back_left])):
|
|
|
|
|
+ # cannot go front or turn
|
|
|
|
|
+ return True
|
|
|
|
|
+
|
|
|
|
|
+ return False
|
|
|
|
|
+
|
|
|
|
|
|
|
|
def move(self, *args, **kwargs):
|
|
def move(self, *args, **kwargs):
|
|
|
try:
|
|
try:
|
|
@@ -619,40 +658,49 @@ class Ship(Entity):
|
|
|
except DidNotAct:
|
|
except DidNotAct:
|
|
|
return False
|
|
return False
|
|
|
|
|
|
|
|
- def _move(self, path):
|
|
|
|
|
|
|
+
|
|
|
|
|
+ def _move(self, path, danger=[]):
|
|
|
|
|
|
|
|
if path is None:
|
|
if path is None:
|
|
|
- log(f"(!) broken: automove to {goto}")
|
|
|
|
|
- ship.auto_move(*goto)
|
|
|
|
|
|
|
+ log(f"(!) broken: automove to {self.goto}")
|
|
|
|
|
+ self.auto_move(*self.goto)
|
|
|
return
|
|
return
|
|
|
elif not path:
|
|
elif not path:
|
|
|
raise DidNotAct()
|
|
raise DidNotAct()
|
|
|
|
|
|
|
|
- # <--- special: avoid blocking situations
|
|
|
|
|
- if current_turn > 1 and self.blocked_since >= 1:
|
|
|
|
|
- dx, dy = Grid.directions(self.y)[((self.orientation + 1) % 6)]
|
|
|
|
|
- if self.moving_cost(self.x + dx, self.y + dy) <= 50:
|
|
|
|
|
- self.turn_left()
|
|
|
|
|
- else:
|
|
|
|
|
- self.turn_right()
|
|
|
|
|
- return
|
|
|
|
|
- # --->
|
|
|
|
|
-
|
|
|
|
|
# speed shall be at 1 when arriving on the "flag"
|
|
# speed shall be at 1 when arriving on the "flag"
|
|
|
next_flag = next((i for i, n in enumerate(path) if n.orientation != self.orientation), None)
|
|
next_flag = next((i for i, n in enumerate(path) if n.orientation != self.orientation), None)
|
|
|
if next_flag is None:
|
|
if next_flag is None:
|
|
|
next_flag = len(path)
|
|
next_flag = len(path)
|
|
|
|
|
|
|
|
- if next_flag < (2 * self.speed):
|
|
|
|
|
- # the end of the path or a direction change is coming
|
|
|
|
|
- diff = Grid.diff_directions(self.orientation, path[0].orientation)
|
|
|
|
|
-
|
|
|
|
|
- # <--- special: avoid the left/right hesitation when stopped
|
|
|
|
|
- if diff and not self.speed and self.last_action in ["STARBOARD", "PORT"]:
|
|
|
|
|
|
|
+ diff = Grid.diff_directions(self.orientation, path[0].orientation)
|
|
|
|
|
+
|
|
|
|
|
+ if not self.speed:
|
|
|
|
|
+ if diff and next_flag == 0:
|
|
|
|
|
+ # start, with a direction change
|
|
|
|
|
+ if diff > 0:
|
|
|
|
|
+ self.turn_left()
|
|
|
|
|
+ return
|
|
|
|
|
+
|
|
|
|
|
+ elif diff < 0:
|
|
|
|
|
+ self.turn_right()
|
|
|
|
|
+ return
|
|
|
|
|
+ else:
|
|
|
|
|
+ # start, todo recto
|
|
|
self.speed_up()
|
|
self.speed_up()
|
|
|
return
|
|
return
|
|
|
- # --->
|
|
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
|
|
+ elif self.speed > 1 and next_flag <= (self.speed + 1):
|
|
|
|
|
+ # the end of the path or a direction change is coming
|
|
|
|
|
+ self.slow_down()
|
|
|
|
|
+ return
|
|
|
|
|
+
|
|
|
|
|
+ elif next_flag > self.MAX_SPEED + 1 and self.speed < self.MAX_SPEED:
|
|
|
|
|
+ # long path and no direction change coming: speed up
|
|
|
|
|
+ self.speed_up()
|
|
|
|
|
+ return
|
|
|
|
|
+
|
|
|
|
|
+ elif diff:
|
|
|
if diff > 0:
|
|
if diff > 0:
|
|
|
self.turn_left()
|
|
self.turn_left()
|
|
|
return
|
|
return
|
|
@@ -661,16 +709,6 @@ class Ship(Entity):
|
|
|
self.turn_right()
|
|
self.turn_right()
|
|
|
return
|
|
return
|
|
|
|
|
|
|
|
- elif self.speed > 1:
|
|
|
|
|
- self.slow_down()
|
|
|
|
|
- return
|
|
|
|
|
-
|
|
|
|
|
- else:
|
|
|
|
|
- if not self.speed or (next_flag > (2 * self.speed + 1) and self.speed < self.MAX_SPEED):
|
|
|
|
|
- # long path and no direction change coming: speed up
|
|
|
|
|
- self.speed_up()
|
|
|
|
|
- return
|
|
|
|
|
-
|
|
|
|
|
raise DidNotAct()
|
|
raise DidNotAct()
|
|
|
|
|
|
|
|
def fire_at_will(self, *args, **kwargs):
|
|
def fire_at_will(self, *args, **kwargs):
|
|
@@ -733,39 +771,37 @@ class Ship(Entity):
|
|
|
return self.last_fire is None or (current_turn - self.last_fire) >= 1
|
|
return self.last_fire is None or (current_turn - self.last_fire) >= 1
|
|
|
|
|
|
|
|
# --- Basic commands
|
|
# --- Basic commands
|
|
|
|
|
+ def _act(self, cmd, *args):
|
|
|
|
|
+ self.last_action = cmd
|
|
|
|
|
+ output = " ".join([cmd] + [str(a) for a in args])
|
|
|
|
|
+ log(f"ship {self.id}: {output}")
|
|
|
|
|
+ print(output)
|
|
|
|
|
+
|
|
|
def auto_move(self, x, y):
|
|
def auto_move(self, x, y):
|
|
|
- self.last_action = "MOVE"
|
|
|
|
|
- print(f"MOVE {x} {y}")
|
|
|
|
|
|
|
+ self._act("MOVE", x, y)
|
|
|
|
|
|
|
|
def speed_up(self):
|
|
def speed_up(self):
|
|
|
- self.last_action = "FASTER"
|
|
|
|
|
- print("FASTER")
|
|
|
|
|
|
|
+ self._act("FASTER")
|
|
|
|
|
|
|
|
def slow_down(self):
|
|
def slow_down(self):
|
|
|
- self.last_action = "SLOWER"
|
|
|
|
|
- print("SLOWER")
|
|
|
|
|
|
|
+ self._act("SLOWER")
|
|
|
|
|
|
|
|
def turn_right(self):
|
|
def turn_right(self):
|
|
|
- self.last_action = "STARBOARD"
|
|
|
|
|
- print("STARBOARD")
|
|
|
|
|
|
|
+ self._act("STARBOARD")
|
|
|
|
|
|
|
|
def turn_left(self):
|
|
def turn_left(self):
|
|
|
- self.last_action = "PORT"
|
|
|
|
|
- print("PORT")
|
|
|
|
|
|
|
+ self._act("PORT")
|
|
|
|
|
|
|
|
def wait(self):
|
|
def wait(self):
|
|
|
- self.last_action = "WAIT"
|
|
|
|
|
- print("WAIT")
|
|
|
|
|
|
|
+ self._act("WAIT")
|
|
|
|
|
|
|
|
def mine(self):
|
|
def mine(self):
|
|
|
self.last_mining = current_turn
|
|
self.last_mining = current_turn
|
|
|
- self.last_action = "MINE"
|
|
|
|
|
- print("MINE")
|
|
|
|
|
|
|
+ self._act("MINE")
|
|
|
|
|
|
|
|
def fire(self, x, y):
|
|
def fire(self, x, y):
|
|
|
self.last_fire = current_turn
|
|
self.last_fire = current_turn
|
|
|
- self.last_action = "FIRE"
|
|
|
|
|
- print(f"FIRE {x} {y}")
|
|
|
|
|
|
|
+ self._act("FIRE", x, y)
|
|
|
|
|
|
|
|
class Barrel(Entity):
|
|
class Barrel(Entity):
|
|
|
def __init__(self, *args, **kwargs):
|
|
def __init__(self, *args, **kwargs):
|
|
@@ -816,18 +852,12 @@ while True:
|
|
|
|
|
|
|
|
# <--- get input
|
|
# <--- get input
|
|
|
my_ship_count, entity_count = int(input()), int(input())
|
|
my_ship_count, entity_count = int(input()), int(input())
|
|
|
|
|
+ previous_ent, entities = grid.entities, {}
|
|
|
for _ in range(entity_count):
|
|
for _ in range(entity_count):
|
|
|
ent_id, ent_type, *data = input().split()
|
|
ent_id, ent_type, *data = input().split()
|
|
|
ent_id = int(ent_id)
|
|
ent_id = int(ent_id)
|
|
|
- seen.append(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}
|
|
|
|
|
|
|
+ entities[ent_id] = grid.entities.get(ent_id, map_entity[ent_type](ent_id))
|
|
|
|
|
+ entities[ent_id].update(*data)
|
|
|
# --->
|
|
# --->
|
|
|
|
|
|
|
|
grid.load_entities(entities)
|
|
grid.load_entities(entities)
|
|
@@ -860,10 +890,12 @@ while True:
|
|
|
for s in grid.owned_ships:
|
|
for s in grid.owned_ships:
|
|
|
if not s.objective:
|
|
if not s.objective:
|
|
|
continue
|
|
continue
|
|
|
|
|
+ if grid.manhattan(s.pos, s.objective.target.pos) > 5:
|
|
|
|
|
+ continue
|
|
|
after_that = ObjectivesQueue()
|
|
after_that = ObjectivesQueue()
|
|
|
- for b in grid.barrels:
|
|
|
|
|
|
|
+ for b in [o.target for o in s.objectives.items]:
|
|
|
obj = GetBarrel(b)
|
|
obj = GetBarrel(b)
|
|
|
- obj.eval(s.objective.target.pos)
|
|
|
|
|
|
|
+ obj.eval(s.objective.target.pos, s.orientation)
|
|
|
after_that.put(obj)
|
|
after_that.put(obj)
|
|
|
if after_that:
|
|
if after_that:
|
|
|
s.objective_next = after_that.get()
|
|
s.objective_next = after_that.get()
|
|
@@ -883,20 +915,19 @@ while True:
|
|
|
log(f"---- ship {ship.id} ---")
|
|
log(f"---- ship {ship.id} ---")
|
|
|
log(f"ship: {ship}")
|
|
log(f"ship: {ship}")
|
|
|
|
|
|
|
|
- if ship.objective:
|
|
|
|
|
- goto = ship.objective.target.pos
|
|
|
|
|
|
|
+ if ship.objective or (ship.target_ennemy and ship.target_ennemy.interest < 0):
|
|
|
|
|
+ ship.goto = ship.objective.target.pos
|
|
|
elif ship.target_ennemy:
|
|
elif ship.target_ennemy:
|
|
|
- goto = grid.shooting_spot(ship, ship.target_ennemy.target)
|
|
|
|
|
|
|
+ ship.goto = grid.shooting_spot(ship, ship.target_ennemy.target)
|
|
|
else:
|
|
else:
|
|
|
log("ERROR: No target")
|
|
log("ERROR: No target")
|
|
|
continue
|
|
continue
|
|
|
|
|
|
|
|
- log(f"goto: {goto}")
|
|
|
|
|
-
|
|
|
|
|
- ship.path = grid.path(ship.next_pos, ship.orientation, goto, ship._moving_costs, limit=6000 // len(grid.owned_ships))
|
|
|
|
|
|
|
+ log(f"goto: {ship.goto}")
|
|
|
|
|
+ ship.path = grid.path(ship.next_pos, ship.orientation, ship.goto, ship._moving_costs, limit=6000 // len(grid.owned_ships))
|
|
|
|
|
|
|
|
if ship.objective_next and ship.path:
|
|
if ship.objective_next and ship.path:
|
|
|
- ship.path += grid.path(goto,
|
|
|
|
|
|
|
+ ship.path += grid.path(ship.goto,
|
|
|
ship.path[-1].orientation,
|
|
ship.path[-1].orientation,
|
|
|
ship.objective_next.target.pos,
|
|
ship.objective_next.target.pos,
|
|
|
ship._moving_costs,
|
|
ship._moving_costs,
|
|
@@ -904,6 +935,10 @@ while True:
|
|
|
|
|
|
|
|
log(f"path: {ship.path}")
|
|
log(f"path: {ship.path}")
|
|
|
|
|
|
|
|
|
|
+ ### special: avoid cannonballs
|
|
|
|
|
+ danger = [c.pos for c in grid.cannonballs if c.countdown <= 1]
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
### Process
|
|
### Process
|
|
|
log("# Processing")
|
|
log("# Processing")
|
|
|
|
|
|
|
@@ -912,6 +947,11 @@ while True:
|
|
|
log("No target: wait")
|
|
log("No target: wait")
|
|
|
ship.wait()
|
|
ship.wait()
|
|
|
|
|
|
|
|
|
|
+ if ship.cant_move():
|
|
|
|
|
+ log("blocked... fire!")
|
|
|
|
|
+ if ship.fire_at_will(ship.target_ennemy.target, allies=grid.owned_ships):
|
|
|
|
|
+ continue
|
|
|
|
|
+
|
|
|
if ship.move(ship.path):
|
|
if ship.move(ship.path):
|
|
|
continue
|
|
continue
|
|
|
|
|
|