|
|
@@ -184,6 +184,8 @@ class Grid(Base):
|
|
|
|
|
|
for s in self.owned_ships:
|
|
|
s.allies = [other for other in self.owned_ships if other is not s]
|
|
|
+ for s in self.ennemy_ships:
|
|
|
+ s.allies = [other for other in self.ennemy_ships if other is not s]
|
|
|
|
|
|
self.update_moving_costs()
|
|
|
|
|
|
@@ -513,6 +515,12 @@ class Ship(Entity):
|
|
|
MAX_SPEED = 2
|
|
|
SCOPE = 10
|
|
|
|
|
|
+ SLOW_DOWN = 1
|
|
|
+ SPEED_UP = 2
|
|
|
+ TURN_LEFT = 3
|
|
|
+ TURN_RIGHT = 4
|
|
|
+ MOVES = [SLOW_DOWN, SPEED_UP, TURN_LEFT, TURN_RIGHT]
|
|
|
+
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
super().__init__(*args, **kwargs)
|
|
|
self.x, self.y = 0, 0
|
|
|
@@ -621,8 +629,6 @@ class Ship(Entity):
|
|
|
positions[i + 1] = []
|
|
|
|
|
|
for p in positions[i]:
|
|
|
- if self.moving_cost(*p.pos) >= 1000:
|
|
|
- continue
|
|
|
|
|
|
pos, d, speed = p.pos, p.d, p.speed
|
|
|
|
|
|
@@ -643,15 +649,20 @@ class Ship(Entity):
|
|
|
positions[i + 1].append(Position(Grid.next_cell(*pos, d, repeat=speed + 1), d, speed + 1))
|
|
|
|
|
|
# slow down
|
|
|
- if speed > 0:
|
|
|
+ if speed > 1:
|
|
|
positions[i + 1].append(Position(Grid.next_cell(*pos, d, repeat=speed - 1), d, speed - 1))
|
|
|
|
|
|
+ # we voluntary ignore the case where a ship at speed 1 would slow down,
|
|
|
+ # as it is not expected to be a standard behaviour for a ship
|
|
|
+
|
|
|
# agregate
|
|
|
proba = {}
|
|
|
for i, plst in positions.items():
|
|
|
proba[i] = {}
|
|
|
for p in plst:
|
|
|
for c in Ship.get_area(*p.pos, p.d):
|
|
|
+ if self.moving_cost(*c) >= 1000:
|
|
|
+ continue
|
|
|
proba[i][c] = proba[i].get(c, 0) + 10
|
|
|
|
|
|
# involve the moving cost
|
|
|
@@ -662,7 +673,7 @@ 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
|
|
|
-
|
|
|
+
|
|
|
return proba
|
|
|
|
|
|
def guess_next_positions(self, in_=3):
|
|
|
@@ -693,53 +704,75 @@ class Ship(Entity):
|
|
|
def can_move(self):
|
|
|
return self.can_move_fwd() or self.can_turn_left() or self.can_turn_left()
|
|
|
|
|
|
- def move(self, *args, **kwargs):
|
|
|
- try:
|
|
|
- self._move(*args, **kwargs)
|
|
|
- return True
|
|
|
- except DidNotAct:
|
|
|
- return False
|
|
|
-
|
|
|
- def _move(self, path):
|
|
|
-
|
|
|
+ def move(self, path):
|
|
|
+
|
|
|
if path is None:
|
|
|
if self.can_move():
|
|
|
log(f"(!) broken: automove to {self.goto}")
|
|
|
self.auto_move(*self.goto)
|
|
|
- return
|
|
|
+ return True
|
|
|
else:
|
|
|
- raise DidNotAct()
|
|
|
+ return False
|
|
|
elif not path:
|
|
|
- raise DidNotAct()
|
|
|
+ return False
|
|
|
+
|
|
|
+ planned = self._plan_move(path)
|
|
|
|
|
|
- # flags represent direction changes of end of the path
|
|
|
+ if planned is None:
|
|
|
+ future_pos = self.get_next_cell()
|
|
|
+ future_d = self.orientation
|
|
|
+ else:
|
|
|
+ new_speed = self.speed
|
|
|
+ if planned == Ship.SPEED_UP:
|
|
|
+ new_speed += 1
|
|
|
+ elif planned == Ship.SLOW_DOWN:
|
|
|
+ new_speed -= 1
|
|
|
+ future_pos = path[new_speed]
|
|
|
+ future_d = path[new_speed].orientation
|
|
|
+
|
|
|
+ future_area = self.get_area(*future_pos, future_d)
|
|
|
+ if any(self.moving_cost(*c) >= 1000 for c in future_area):
|
|
|
+ log("/!\ Danger: planned move would lead to collision")
|
|
|
+
|
|
|
+ if planned == Ship.SPEED_UP:
|
|
|
+ self.speed_up()
|
|
|
+ elif planned == Ship.SLOW_DOWN:
|
|
|
+ self.slow_down()
|
|
|
+ elif planned == Ship.TURN_LEFT:
|
|
|
+ self.turn_left()
|
|
|
+ elif planned == Ship.TURN_RIGHT:
|
|
|
+ self.turn_right()
|
|
|
+ else:
|
|
|
+ return False
|
|
|
+
|
|
|
+ return True
|
|
|
+
|
|
|
+ def _plan_move(self, path):
|
|
|
+
|
|
|
+ # flags represent direction changes or end of the path
|
|
|
last_flag = len(path) - 1
|
|
|
- flags = (i for i, n in enumerate(path) if n.orientation != self.orientation)
|
|
|
- next_flag = next(flags, last_flag)
|
|
|
- afternext_flag = next(flags, last_flag)
|
|
|
+ next_flag = next((i for i, n in enumerate(path) if n.orientation != self.orientation), last_flag)
|
|
|
+ afternext_flag = next((i for i, n in enumerate(path[next_flag:]) if n.orientation != path[next_flag].orientation), last_flag)
|
|
|
|
|
|
if not self.speed:
|
|
|
diff = Grid.diff_directions(self.orientation, path[0].orientation)
|
|
|
|
|
|
if diff > 0 and self.last_action == "STARBOARD" or diff < 0 and self.last_action == "PORT":
|
|
|
# special: avoid the starting hesitation
|
|
|
- self.speed_up()
|
|
|
+ return Ship.SPEED_UP
|
|
|
|
|
|
if diff and next_flag == 0:
|
|
|
# start, with a direction change
|
|
|
if diff > 0:
|
|
|
if self.can_turn_left():
|
|
|
- self.turn_left()
|
|
|
- return
|
|
|
+ return Ship.TURN_LEFT
|
|
|
elif diff < 0:
|
|
|
if self.can_turn_right():
|
|
|
- self.turn_right()
|
|
|
- return
|
|
|
+ return Ship.TURN_RIGHT
|
|
|
else:
|
|
|
# start straight
|
|
|
if self.can_move_fwd():
|
|
|
- self.speed_up()
|
|
|
- return
|
|
|
+ return Ship.SPEED_UP
|
|
|
|
|
|
elif self.speed == self.MAX_SPEED:
|
|
|
|
|
|
@@ -747,33 +780,27 @@ class Ship(Entity):
|
|
|
# drift
|
|
|
diff = Grid.diff_directions(self.orientation, path[next_flag].orientation)
|
|
|
if diff > 0:
|
|
|
- self.turn_left()
|
|
|
- return
|
|
|
+ return Ship.TURN_LEFT
|
|
|
elif diff < 0:
|
|
|
- self.turn_right()
|
|
|
- return
|
|
|
+ return Ship.TURN_RIGHT
|
|
|
|
|
|
if (self.speed + 1) >= next_flag:
|
|
|
# next direction change or target will be passed at current speed
|
|
|
- self.slow_down()
|
|
|
- return
|
|
|
+ return Ship.SLOW_DOWN
|
|
|
|
|
|
elif self.speed == 1:
|
|
|
|
|
|
if self.speed == next_flag:
|
|
|
diff = Grid.diff_directions(self.orientation, path[next_flag].orientation)
|
|
|
if diff > 0:
|
|
|
- self.turn_left()
|
|
|
- return
|
|
|
+ return Ship.TURN_LEFT
|
|
|
elif diff < 0:
|
|
|
- self.turn_right()
|
|
|
- return
|
|
|
+ return Ship.TURN_RIGHT
|
|
|
|
|
|
elif next_flag > 3 or (next_flag > 2 and afternext_flag >= (next_flag + 2)):
|
|
|
- self.speed_up()
|
|
|
- return
|
|
|
+ return Ship.SPEED_UP
|
|
|
|
|
|
- raise DidNotAct()
|
|
|
+ return None
|
|
|
|
|
|
def fire_at_will(self, *args, **kwargs):
|
|
|
try:
|
|
|
@@ -792,7 +819,7 @@ class Ship(Entity):
|
|
|
for ally in allies:
|
|
|
avoid += ally.mobility_zone
|
|
|
|
|
|
- next_positions = target.guess_next_positions(in_=3)
|
|
|
+ next_positions = target.guess_next_positions(in_=4)
|
|
|
for t, next_pos in next_positions.items():
|
|
|
dist = Grid.manhattan(self.prow, next_pos)
|
|
|
if dist > self.SCOPE:
|
|
|
@@ -800,10 +827,10 @@ class Ship(Entity):
|
|
|
if next_pos in avoid:
|
|
|
continue
|
|
|
|
|
|
- dt = (1 + round(dist / 3)) # time for tha cannonball to reach this pos
|
|
|
+ dt = 1 + (1 + round(dist / 3)) # time for the cannonball to reach this pos (including fire turn)
|
|
|
|
|
|
- if (dt + 1) == t:
|
|
|
- log(f"precise shoot: {next_pos}")
|
|
|
+ if dt == t:
|
|
|
+ log(f"[x] precise shoot: dt={dt}, pos={next_pos}")
|
|
|
ship.fire(*next_pos)
|
|
|
return
|
|
|
|
|
|
@@ -982,14 +1009,13 @@ while True:
|
|
|
if new_path:
|
|
|
ship.path += new_path
|
|
|
else:
|
|
|
- break
|
|
|
-
|
|
|
+ break
|
|
|
|
|
|
for ship in grid.owned_ships:
|
|
|
log(f"---- ship {ship.id} ---")
|
|
|
log(f"ship: {ship}")
|
|
|
- log(f"obj: {ship.objective}; next: {ship.objectives_next}; target: {ship.target_ennemy}")
|
|
|
- log(f"goto: {ship.goto}")
|
|
|
+ log(f"obj: {ship.objective}; next: {ship.objectives_next}")
|
|
|
+ log(f"target: {ship.target_ennemy}")
|
|
|
log(f"path: {ship.path}")
|
|
|
|
|
|
### Process
|