|
@@ -8,10 +8,9 @@ import time
|
|
|
|
|
|
|
|
|
|
|
|
|
# TODO:
|
|
# TODO:
|
|
|
-# * if an enemy is near a mine, shoot the mine instead of the ship
|
|
|
|
|
|
|
+# * improve the probability next pos
|
|
|
# * find a way to change direction without slowing down if possible
|
|
# * find a way to change direction without slowing down if possible
|
|
|
# * avoid getting blocked by a side-by-side with an ennemy
|
|
# * avoid getting blocked by a side-by-side with an ennemy
|
|
|
-# * use a queue to choose the best shoot instead of a strict equality
|
|
|
|
|
|
|
|
|
|
debug = True
|
|
debug = True
|
|
|
|
|
|
|
@@ -207,6 +206,16 @@ class Grid(Base):
|
|
|
for s in self.ships:
|
|
for s in self.ships:
|
|
|
s.next_pos_proba(2)
|
|
s.next_pos_proba(2)
|
|
|
|
|
|
|
|
|
|
+ self.next_to_mine = {}
|
|
|
|
|
+ for m in self.mines:
|
|
|
|
|
+ for n in self.neighbors(*m.pos):
|
|
|
|
|
+ self.next_to_mine[n] = m
|
|
|
|
|
+
|
|
|
|
|
+ self.next_to_barrel = {}
|
|
|
|
|
+ for b in self.barrels:
|
|
|
|
|
+ for n in self.neighbors(*b.pos):
|
|
|
|
|
+ self.next_to_barrel[n] = b
|
|
|
|
|
+
|
|
|
self.update_moving_costs()
|
|
self.update_moving_costs()
|
|
|
|
|
|
|
|
grav_center = self.barrels_gravity_center()
|
|
grav_center = self.barrels_gravity_center()
|
|
@@ -271,13 +280,12 @@ class Grid(Base):
|
|
|
elif x in (0, self.w - 1) or y in (0, self.h - 1):
|
|
elif x in (0, self.w - 1) or y in (0, self.h - 1):
|
|
|
base_costs[(x, y)] = 15 # borders are a little more expensive
|
|
base_costs[(x, y)] = 15 # borders are a little more expensive
|
|
|
|
|
|
|
|
- for m in self.mines:
|
|
|
|
|
- for n in self.neighbors(*m.pos):
|
|
|
|
|
- base_costs[n] += 30
|
|
|
|
|
|
|
+ for c in self.next_to_mine:
|
|
|
|
|
+ base_costs[c] += 30
|
|
|
for m in self.mines:
|
|
for m in self.mines:
|
|
|
base_costs[m.pos] += 1000
|
|
base_costs[m.pos] += 1000
|
|
|
for c in self.cannonballs:
|
|
for c in self.cannonballs:
|
|
|
- base_costs[c.pos] += (200 + (6 - c.countdown) * 200)
|
|
|
|
|
|
|
+ base_costs[c.pos] += (150 + (6 - c.countdown) * 200)
|
|
|
|
|
|
|
|
for ship in self.ships:
|
|
for ship in self.ships:
|
|
|
ship._moving_costs = {}
|
|
ship._moving_costs = {}
|
|
@@ -706,8 +714,7 @@ class Ship(Entity):
|
|
|
p = Position(inertia, d, speed, 30)
|
|
p = Position(inertia, d, speed, 30)
|
|
|
if self.moving_cost(*p.pos) >= 1000:
|
|
if self.moving_cost(*p.pos) >= 1000:
|
|
|
p.weight = 10
|
|
p.weight = 10
|
|
|
- positions[i + 1].append(p)
|
|
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
# turn left
|
|
# turn left
|
|
|
p = Position(inertia, Grid.add_directions(d, 1), speed)
|
|
p = Position(inertia, Grid.add_directions(d, 1), speed)
|
|
|
if not self.moving_cost(*p.pos) >= 1000:
|
|
if not self.moving_cost(*p.pos) >= 1000:
|
|
@@ -730,7 +737,6 @@ class Ship(Entity):
|
|
|
if not self.moving_cost(*p.pos) >= 1000:
|
|
if not self.moving_cost(*p.pos) >= 1000:
|
|
|
positions[i + 1].append(p)
|
|
positions[i + 1].append(p)
|
|
|
|
|
|
|
|
-
|
|
|
|
|
# we voluntary ignore the case where a ship at speed 1 would slow down,
|
|
# 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
|
|
# as it is not expected to be a standard behaviour for a ship
|
|
|
|
|
|
|
@@ -738,16 +744,15 @@ class Ship(Entity):
|
|
|
proba = {}
|
|
proba = {}
|
|
|
for i, plst in positions.items():
|
|
for i, plst in positions.items():
|
|
|
proba[i] = {}
|
|
proba[i] = {}
|
|
|
|
|
+ weights = {}
|
|
|
|
|
+ total_weight = sum([p.weight for p in plst])
|
|
|
for p in plst:
|
|
for p in plst:
|
|
|
for c in p.area:
|
|
for c in p.area:
|
|
|
- proba[i][c] = proba[i].get(c, 0) + p.weight
|
|
|
|
|
-
|
|
|
|
|
- # if ship is blocked, current area is more accurate
|
|
|
|
|
- if self.blocked_since:
|
|
|
|
|
- for i in proba:
|
|
|
|
|
- for c in self.area:
|
|
|
|
|
- proba[i][c] = proba[i].get(c, 0) + 30 * self.blocked_since
|
|
|
|
|
-
|
|
|
|
|
|
|
+ weights[c] = weights.get(c, 0) + p.weight
|
|
|
|
|
+
|
|
|
|
|
+ for c in weights:
|
|
|
|
|
+ proba[i][c] = 100 * weights[c] // total_weight
|
|
|
|
|
+
|
|
|
self.cached_next_pos_proba = proba
|
|
self.cached_next_pos_proba = proba
|
|
|
|
|
|
|
|
return proba
|
|
return proba
|
|
@@ -835,10 +840,10 @@ class Ship(Entity):
|
|
|
|
|
|
|
|
for move in available_moves:
|
|
for move in available_moves:
|
|
|
new_area = self.area_after_moving(move)
|
|
new_area = self.area_after_moving(move)
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
# special: extra-grid cells are not consider as collisions since a part of the ship can go there
|
|
# 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):
|
|
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")
|
|
|
|
|
|
|
+ log(f"/!\ Danger: planned move <{Ship.COMMANDS[move]}> would lead to collision ({new_area})")
|
|
|
else:
|
|
else:
|
|
|
next_move = move
|
|
next_move = move
|
|
|
break
|
|
break
|
|
@@ -848,20 +853,6 @@ class Ship(Entity):
|
|
|
self.next_move = next_move
|
|
self.next_move = next_move
|
|
|
self.next_area = new_area
|
|
self.next_area = new_area
|
|
|
return True
|
|
return True
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
- def move(self):
|
|
|
|
|
- if self.next_move == Ship.SPEED_UP:
|
|
|
|
|
- self.speed_up()
|
|
|
|
|
- elif self.next_move == Ship.SLOW_DOWN:
|
|
|
|
|
- self.slow_down()
|
|
|
|
|
- elif self.next_move == Ship.TURN_LEFT:
|
|
|
|
|
- self.turn_left()
|
|
|
|
|
- elif self.next_move == Ship.TURN_RIGHT:
|
|
|
|
|
- self.turn_right()
|
|
|
|
|
- else:
|
|
|
|
|
- return False
|
|
|
|
|
- return True
|
|
|
|
|
|
|
|
|
|
def _follow_path(self, path):
|
|
def _follow_path(self, path):
|
|
|
|
|
|
|
@@ -918,6 +909,19 @@ class Ship(Entity):
|
|
|
|
|
|
|
|
return None
|
|
return None
|
|
|
|
|
|
|
|
|
|
+ def move(self):
|
|
|
|
|
+ if self.next_move == Ship.SPEED_UP:
|
|
|
|
|
+ self.speed_up()
|
|
|
|
|
+ elif self.next_move == Ship.SLOW_DOWN:
|
|
|
|
|
+ self.slow_down()
|
|
|
|
|
+ elif self.next_move == Ship.TURN_LEFT:
|
|
|
|
|
+ self.turn_left()
|
|
|
|
|
+ elif self.next_move == Ship.TURN_RIGHT:
|
|
|
|
|
+ self.turn_right()
|
|
|
|
|
+ else:
|
|
|
|
|
+ return False
|
|
|
|
|
+ return True
|
|
|
|
|
+
|
|
|
def fire_at_will(self, *args, **kwargs):
|
|
def fire_at_will(self, *args, **kwargs):
|
|
|
return self._fire_at_will(*args, **kwargs)
|
|
return self._fire_at_will(*args, **kwargs)
|
|
|
|
|
|
|
@@ -930,27 +934,45 @@ class Ship(Entity):
|
|
|
avoid += ally.next_area
|
|
avoid += ally.next_area
|
|
|
|
|
|
|
|
next_positions = target.next_pos_proba(4)
|
|
next_positions = target.next_pos_proba(4)
|
|
|
- best_shots = Queue()
|
|
|
|
|
- for t, cells in next_positions.items():
|
|
|
|
|
- for c, proba in cells.items():
|
|
|
|
|
|
|
+
|
|
|
|
|
+ for t, probas in next_positions.items():
|
|
|
|
|
+
|
|
|
|
|
+ # include mines and barrels
|
|
|
|
|
+ mines_next = {}
|
|
|
|
|
+ for c, proba in probas.items():
|
|
|
|
|
+ if c in grid.next_to_mine:
|
|
|
|
|
+ mpos = grid.next_to_mine[c].pos
|
|
|
|
|
+ mines_next[mpos] = mines_next.get(mpos, 1) + proba
|
|
|
|
|
+ probas.update(mines_next)
|
|
|
|
|
+
|
|
|
|
|
+ barrels = [b.pos for b in grid.barrels]
|
|
|
|
|
+ for c in probas:
|
|
|
|
|
+ if c in barrels:
|
|
|
|
|
+ probas[c] += 20
|
|
|
|
|
+
|
|
|
|
|
+ shots = sorted(probas.items(), key=lambda x: x[1], reverse=True)
|
|
|
|
|
+ log(t, shots)
|
|
|
|
|
+
|
|
|
|
|
+ for c, proba in shots:
|
|
|
if c in avoid:
|
|
if c in avoid:
|
|
|
continue
|
|
continue
|
|
|
|
|
+ if proba < 20:
|
|
|
|
|
+ continue
|
|
|
dist = Grid.manhattan(self.prow, c)
|
|
dist = Grid.manhattan(self.prow, c)
|
|
|
if dist > self.SCOPE:
|
|
if dist > self.SCOPE:
|
|
|
continue
|
|
continue
|
|
|
-
|
|
|
|
|
- dt = 1 + (1 + round(dist / 3)) # time for the cannonball to reach this pos (including fire turn)
|
|
|
|
|
|
|
|
|
|
- interest = 30 * abs(dt - t) - proba # the lower the better
|
|
|
|
|
- best_shots.put(c, interest)
|
|
|
|
|
-
|
|
|
|
|
- try:
|
|
|
|
|
- shoot_at = best_shots.get()
|
|
|
|
|
- log(f"[x] precise shoot: pos={shoot_at}")
|
|
|
|
|
- ship.fire(*shoot_at)
|
|
|
|
|
- return True
|
|
|
|
|
- except IndexError:
|
|
|
|
|
- return False
|
|
|
|
|
|
|
+ # time for the cannonball to reach this pos (including fire turn)
|
|
|
|
|
+ delay = 1 + (1 + round(dist / 3))
|
|
|
|
|
+
|
|
|
|
|
+ if delay != t:
|
|
|
|
|
+ continue
|
|
|
|
|
+
|
|
|
|
|
+ log(f"[x] precise shoot: pos={c}")
|
|
|
|
|
+ ship.fire(*c)
|
|
|
|
|
+ return True
|
|
|
|
|
+
|
|
|
|
|
+ return False
|
|
|
|
|
|
|
|
def can_mine(self):
|
|
def can_mine(self):
|
|
|
return self.last_mining is None or (current_turn - self.last_mining) >= 4
|
|
return self.last_mining is None or (current_turn - self.last_mining) >= 4
|
|
@@ -1092,6 +1114,9 @@ while True:
|
|
|
log("# Planning")
|
|
log("# Planning")
|
|
|
|
|
|
|
|
for ship in grid.owned_ships:
|
|
for ship in grid.owned_ships:
|
|
|
|
|
+ log(f"---- ship {ship.id} ---")
|
|
|
|
|
+ log(f"ship: {ship}")
|
|
|
|
|
+
|
|
|
it_consumed = 0
|
|
it_consumed = 0
|
|
|
|
|
|
|
|
if ship.objective:
|
|
if ship.objective:
|
|
@@ -1133,9 +1158,6 @@ while True:
|
|
|
|
|
|
|
|
ship.plan_next_move()
|
|
ship.plan_next_move()
|
|
|
|
|
|
|
|
- for ship in grid.owned_ships:
|
|
|
|
|
- log(f"---- ship {ship.id} ---")
|
|
|
|
|
- log(f"ship: {ship}")
|
|
|
|
|
log(f"obj: {ship.objective}; next: {ship.objectives_next}")
|
|
log(f"obj: {ship.objective}; next: {ship.objectives_next}")
|
|
|
log(f"target: {ship.target_ennemy}")
|
|
log(f"target: {ship.target_ennemy}")
|
|
|
log(f"goto: {ship.goto}")
|
|
log(f"goto: {ship.goto}")
|