|
|
@@ -12,6 +12,8 @@ import sys
|
|
|
# * find a way to change direction without slowing down if possible
|
|
|
# * avoid getting blocked by a side-by-side with an ennemy
|
|
|
# * priorize targetting blocked ennemies
|
|
|
+# * improve the 'avoid' part of the 'fire_at_will' method
|
|
|
+# * use a queue to choose the best shoot instead of a strict equality
|
|
|
|
|
|
debug = True
|
|
|
|
|
|
@@ -279,7 +281,6 @@ class Grid(Base):
|
|
|
|
|
|
shooting_spots.put((x, y), interest)
|
|
|
|
|
|
- log(shooting_spots.items)
|
|
|
return shooting_spots.get()
|
|
|
|
|
|
# geometrical algorithms
|
|
|
@@ -345,9 +346,9 @@ class Grid(Base):
|
|
|
@staticmethod
|
|
|
def add_directions(d1, d2):
|
|
|
d = d2 + d1
|
|
|
- if d <= -3:
|
|
|
+ if d < 0:
|
|
|
d += 6
|
|
|
- elif d > 3:
|
|
|
+ elif d > 5:
|
|
|
d -= 6
|
|
|
return d
|
|
|
|
|
|
@@ -490,6 +491,12 @@ class Entity(Base):
|
|
|
return self.id < other.id
|
|
|
|
|
|
|
|
|
+class Position(Base):
|
|
|
+ def __init__(self, pos, d, speed):
|
|
|
+ self.pos = pos
|
|
|
+ self.d = d
|
|
|
+ self.speed = speed
|
|
|
+
|
|
|
class Ship(Entity):
|
|
|
MAX_SPEED = 2
|
|
|
SCOPE = 10
|
|
|
@@ -592,45 +599,67 @@ class Ship(Entity):
|
|
|
def get_next_pos(self, in_=1):
|
|
|
return self.get_pos_in(self.pos, self.speed, self.orientation, in_)
|
|
|
|
|
|
- def guess_next_pos(self):
|
|
|
- proba = {}
|
|
|
-
|
|
|
- # wait (or fire or mine)
|
|
|
- for c in self.next_area:
|
|
|
- proba[c] = proba.get(c, 10)
|
|
|
+ def next_pos_proba(self, in_=1):
|
|
|
+
|
|
|
+ # guess next positions
|
|
|
+ positions = {0: [Position(self.pos, self.orientation, self.speed)]}
|
|
|
|
|
|
- # turn left
|
|
|
- area = self.get_area(*self.pos, Grid.add_directions(self.orientation, 1))
|
|
|
- for c in area:
|
|
|
- proba[c] = proba.get(c, 0) + 10
|
|
|
+ for i in range(in_):
|
|
|
|
|
|
- # turn right
|
|
|
- area = self.get_area(*self.pos, Grid.add_directions(self.orientation, -1))
|
|
|
- for c in area:
|
|
|
- proba[c] = proba.get(c, 0) + 10
|
|
|
+ positions[i + 1] = []
|
|
|
|
|
|
- # speed up
|
|
|
- if self.speed < self.MAX_SPEED:
|
|
|
- area = self.get_area(*self.get_pos_in(self.pos, self.speed + 1, self.orientation), self.orientation)
|
|
|
- for c in area:
|
|
|
- proba[c] = proba.get(c, 0) + 10
|
|
|
-
|
|
|
- # slow down
|
|
|
- if self.speed > 0:
|
|
|
- area = self.get_area(*self.get_pos_in(self.pos, self.speed - 1, self.orientation), self.orientation)
|
|
|
- for c in area:
|
|
|
- proba[c] = proba.get(c, 0) + 10
|
|
|
+ for p in positions[i]:
|
|
|
+ if self.moving_cost(*p.pos) >= 1000:
|
|
|
+ continue
|
|
|
+
|
|
|
+ pos, d, speed = p.pos, p.d, p.speed
|
|
|
+
|
|
|
+ # next pos with inertia
|
|
|
+ inertia = Grid.next_cell(*pos, d, repeat=speed)
|
|
|
+
|
|
|
+ # wait (or fire or mine)
|
|
|
+ positions[i + 1].append(Position(inertia, d, speed))
|
|
|
+
|
|
|
+ # turn left
|
|
|
+ positions[i + 1].append(Position(inertia, Grid.add_directions(d, 1), speed))
|
|
|
+
|
|
|
+ # turn right
|
|
|
+ positions[i + 1].append(Position(inertia, Grid.add_directions(d, -1), speed))
|
|
|
+
|
|
|
+ # speed up
|
|
|
+ if speed < self.MAX_SPEED:
|
|
|
+ positions[i + 1].append(Position(Grid.next_cell(*pos, d, repeat=speed + 1), d, speed + 1))
|
|
|
+
|
|
|
+ # slow down
|
|
|
+ if speed > 0:
|
|
|
+ positions[i + 1].append(Position(Grid.next_cell(*pos, d, repeat=speed - 1), d, speed - 1))
|
|
|
+
|
|
|
+ # agregate
|
|
|
+ proba = {}
|
|
|
+ for i, plst in positions.items():
|
|
|
+ proba[i] = {}
|
|
|
+ for p in plst:
|
|
|
+ for c in Ship.get_area(*p.pos, p.d):
|
|
|
+ proba[i][c] = proba[i].get(c, 0) + 10
|
|
|
+
|
|
|
+ # involve the moving cost
|
|
|
+ for i in proba:
|
|
|
+ for c in proba[i]:
|
|
|
+ proba[i][c] -= (self.moving_cost(*c) // 2)
|
|
|
+
|
|
|
+ # 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):
|
|
|
+ proba = self.next_pos_proba(in_)
|
|
|
+ best = {}
|
|
|
+ for i in proba:
|
|
|
+ best[i] = max(proba[i].items(), key=lambda x: x[1])[0]
|
|
|
+ return best
|
|
|
|
|
|
- for c in proba:
|
|
|
- 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])
|
|
|
-
|
|
|
- return best[0]
|
|
|
-
|
|
|
def get_next_cell(self, in_=1):
|
|
|
return Grid.next_cell(self.x, self.y, self.orientation, repeat=in_)
|
|
|
|
|
|
@@ -661,8 +690,6 @@ class Ship(Entity):
|
|
|
|
|
|
def _move(self, path):
|
|
|
|
|
|
- log(self._can_move, self.can_turn_left(), self.can_turn_right(), self.can_move_fwd())
|
|
|
-
|
|
|
if path is None:
|
|
|
if self.can_move():
|
|
|
log(f"(!) broken: automove to {self.goto}")
|
|
|
@@ -753,41 +780,30 @@ class Ship(Entity):
|
|
|
for ally in allies:
|
|
|
avoid += ally.mobility_zone
|
|
|
|
|
|
- dist = Grid.manhattan(self.prow, target.next_pos)
|
|
|
- if dist <= 4:
|
|
|
- # precise algo
|
|
|
- shoot_at = target.guess_next_pos()
|
|
|
- log(f"most probable position: {shoot_at}")
|
|
|
- ship.fire(*shoot_at)
|
|
|
+ next_positions = target.guess_next_positions(in_=3)
|
|
|
+ for t, next_pos in next_positions.items():
|
|
|
+ dist = Grid.manhattan(self.prow, next_pos)
|
|
|
+ if dist > self.SCOPE:
|
|
|
+ continue
|
|
|
+ if next_pos in avoid:
|
|
|
+ continue
|
|
|
|
|
|
- elif dist <= self.SCOPE:
|
|
|
-
|
|
|
- # anticipate
|
|
|
- next_positions = [target.get_next_pos(i) for i in range(1, 3)]
|
|
|
- for i, p in enumerate(next_positions):
|
|
|
- turn = i + 1
|
|
|
-
|
|
|
- if p in avoid:
|
|
|
- continue
|
|
|
-
|
|
|
- dist_p = Grid.manhattan(self.prow, p)
|
|
|
-
|
|
|
- if dist_p > self.SCOPE:
|
|
|
- continue
|
|
|
-
|
|
|
- if (1 + round(dist_p / 3)) == turn:
|
|
|
- log(f"Precision: {p}, {dist_p}, {turn}")
|
|
|
- ship.fire(*p)
|
|
|
- return
|
|
|
+ dt = (1 + round(dist / 3)) # time for tha cannonball to reach this pos
|
|
|
|
|
|
- # give a try
|
|
|
- next_pos = next_positions[0]
|
|
|
+ if dt == t:
|
|
|
+ log(f"precise shoot: {next_pos}")
|
|
|
+ ship.fire(*next_pos)
|
|
|
+ return
|
|
|
+
|
|
|
+ # give a try
|
|
|
+ next_pos = next_positions[2]
|
|
|
+ if not next_pos in avoid:
|
|
|
dist_p = Grid.manhattan(self.prow, next_pos)
|
|
|
if dist_p <= self.SCOPE:
|
|
|
- ship.fire(*p)
|
|
|
+ ship.fire(*next_pos)
|
|
|
+ return
|
|
|
|
|
|
- else:
|
|
|
- raise DidNotAct()
|
|
|
+ raise DidNotAct()
|
|
|
|
|
|
def can_mine(self):
|
|
|
return self.last_mining is None or (current_turn - self.last_mining) >= 4
|
|
|
@@ -961,8 +977,6 @@ while True:
|
|
|
log(f"goto: {ship.goto}")
|
|
|
log(f"path: {ship.path}")
|
|
|
|
|
|
-
|
|
|
-
|
|
|
### Process
|
|
|
log("# Processing")
|
|
|
|