|
@@ -232,7 +232,7 @@ class Grid(BaseClass):
|
|
|
self.index = {}
|
|
self.index = {}
|
|
|
self.units = {}
|
|
self.units = {}
|
|
|
self.threat = {}
|
|
self.threat = {}
|
|
|
- self.conversion_path = []
|
|
|
|
|
|
|
+ self.conversion_path = ConversionPath()
|
|
|
|
|
|
|
|
self.cult_leader = None
|
|
self.cult_leader = None
|
|
|
self.opponent_cult_leader = None
|
|
self.opponent_cult_leader = None
|
|
@@ -315,7 +315,7 @@ class Grid(BaseClass):
|
|
|
self.threat[(x, y)] = threat
|
|
self.threat[(x, y)] = threat
|
|
|
|
|
|
|
|
def compute_conversion_path(self):
|
|
def compute_conversion_path(self):
|
|
|
- conversion_path = []
|
|
|
|
|
|
|
+ conversion_path = ConversionPath()
|
|
|
|
|
|
|
|
if self.cult_leader and self.neutrals:
|
|
if self.cult_leader and self.neutrals:
|
|
|
conversion_path = self.get_conversion_path(
|
|
conversion_path = self.get_conversion_path(
|
|
@@ -387,37 +387,81 @@ class Grid(BaseClass):
|
|
|
|
|
|
|
|
# Shoot opponent units
|
|
# Shoot opponent units
|
|
|
k_shoot_opponent_cultist = 8
|
|
k_shoot_opponent_cultist = 8
|
|
|
- k_shoot_opponent_cult_leader = 4
|
|
|
|
|
|
|
+ k_shoot_opponent_leader = 4
|
|
|
k_shoot_movement_needed = 15
|
|
k_shoot_movement_needed = 15
|
|
|
|
|
+ k0_shoot_in_danger = -10
|
|
|
|
|
+ k_shoot_sacrifice = 50
|
|
|
|
|
+ k0_runaway = 10
|
|
|
|
|
+ k_runaway_threat = 10
|
|
|
|
|
|
|
|
for a in self.allied_cultists:
|
|
for a in self.allied_cultists:
|
|
|
|
|
+ targets = {}
|
|
|
for u in self.opponent_units:
|
|
for u in self.opponent_units:
|
|
|
- shooting_distance = self.shooting_distance(a.pos, u.pos)
|
|
|
|
|
- if not shooting_distance:
|
|
|
|
|
|
|
+ line_of_sight = self.line_of_sight(a.pos, u.pos)
|
|
|
|
|
+ if not len(line_of_sight):
|
|
|
continue
|
|
continue
|
|
|
|
|
|
|
|
- if shooting_distance <= u.SHOOTING_RANGE:
|
|
|
|
|
- # la cible est à portée
|
|
|
|
|
- action = ActionShoot(a, u)
|
|
|
|
|
-
|
|
|
|
|
- priority = (k_shoot_opponent_cult_leader if type(
|
|
|
|
|
- u) is CultLeader else k_shoot_opponent_cultist) * shooting_distance
|
|
|
|
|
- # log(self.line_of_sight(a.pos, u.pos))
|
|
|
|
|
- actions.put(priority, action)
|
|
|
|
|
|
|
+ if len(line_of_sight) <= u.SHOOTING_RANGE:
|
|
|
|
|
+ targets[u] = line_of_sight
|
|
|
else:
|
|
else:
|
|
|
- # la cible est hors de portée, mais elle est plus faible et sans soutien
|
|
|
|
|
|
|
+ # la cible est hors de portée, la poursuivre si plus faible et sans soutien?
|
|
|
# TODO: implémenter
|
|
# TODO: implémenter
|
|
|
pass
|
|
pass
|
|
|
|
|
|
|
|
|
|
+ if not targets:
|
|
|
|
|
+ continue
|
|
|
|
|
+
|
|
|
|
|
+ # Il est probable que l'unité se fera tirer dessus par l'ennemi le plus proche: quel résultat?
|
|
|
|
|
+ dist_to_nearest_target = min([len(line) for line in targets.values()])
|
|
|
|
|
+ damages_received = Unit.SHOOTING_MAX_DAMAGE - dist_to_nearest_target
|
|
|
|
|
+ unit_in_danger = damages_received >= a.hp or (len(targets) > 1 and (damages_received + 3) >= a.hp)
|
|
|
|
|
+
|
|
|
|
|
+ for u, line in targets.items():
|
|
|
|
|
+ dist = len(line)
|
|
|
|
|
+ damages = Unit.SHOOTING_MAX_DAMAGE - dist
|
|
|
|
|
+ target_die = damages >= u.hp
|
|
|
|
|
+
|
|
|
|
|
+ priority = 0
|
|
|
|
|
+
|
|
|
|
|
+ if unit_in_danger and not target_die:
|
|
|
|
|
+ # run away!
|
|
|
|
|
+ covers = [n for n in self.neighbors(*a.pos)
|
|
|
|
|
+ if self.threat[n] < self.threat[a.pos] and self.can_move_on(a, n)]
|
|
|
|
|
+
|
|
|
|
|
+ for pos in covers:
|
|
|
|
|
+ action = ActionMove(a, pos, f'run away!')
|
|
|
|
|
+
|
|
|
|
|
+ priority += k0_runaway
|
|
|
|
|
+ priority += k_runaway_threat * self.threat[pos]
|
|
|
|
|
+
|
|
|
|
|
+ actions.put(priority, action)
|
|
|
|
|
+ continue
|
|
|
|
|
+
|
|
|
|
|
+ elif unit_in_danger and target_die and len(targets) > 1:
|
|
|
|
|
+ # bof, un échange, pas sûr que ça puisse être intéressant...
|
|
|
|
|
+ priority += k_shoot_sacrifice
|
|
|
|
|
+
|
|
|
|
|
+ k = k_shoot_opponent_leader if u is self.opponent_cult_leader else k_shoot_opponent_cultist
|
|
|
|
|
+ priority += k * dist
|
|
|
|
|
+ priority += k0_shoot_in_danger * unit_in_danger
|
|
|
|
|
+
|
|
|
|
|
+ action = ActionShoot(a, u, f'shoot from {a.pos} ({line})')
|
|
|
|
|
+
|
|
|
|
|
+ actions.put(priority, action)
|
|
|
|
|
+
|
|
|
# Position
|
|
# Position
|
|
|
k0_position = 30
|
|
k0_position = 30
|
|
|
k_advantage = 40
|
|
k_advantage = 40
|
|
|
k_position_distance = 3 # on veut privilégier les plus près, mais pas non plus décourager ceux de l'arrière...
|
|
k_position_distance = 3 # on veut privilégier les plus près, mais pas non plus décourager ceux de l'arrière...
|
|
|
|
|
+ hp_min = 5
|
|
|
|
|
|
|
|
advantage = len(self.owned_units) > len(self.opponent_units) + len(self.neutrals)
|
|
advantage = len(self.owned_units) > len(self.opponent_units) + len(self.neutrals)
|
|
|
|
|
|
|
|
for a in self.allied_cultists:
|
|
for a in self.allied_cultists:
|
|
|
|
|
|
|
|
|
|
+ if a.hp < hp_min:
|
|
|
|
|
+ continue
|
|
|
|
|
+
|
|
|
if self.threat[a.pos]:
|
|
if self.threat[a.pos]:
|
|
|
# l'unité est déjà dans une zone à risque
|
|
# l'unité est déjà dans une zone à risque
|
|
|
# TODO: envisager un retrait
|
|
# TODO: envisager un retrait
|
|
@@ -472,22 +516,22 @@ class Grid(BaseClass):
|
|
|
lost_causes[target] = (dist - threaten_neutrals_from_leader.get(target, 0)) # la distance retenue est la différence entre la distance entre la cible
|
|
lost_causes[target] = (dist - threaten_neutrals_from_leader.get(target, 0)) # la distance retenue est la différence entre la distance entre la cible
|
|
|
# et le leader ennemi, et celle entre la cible et le leader allié
|
|
# et le leader ennemi, et celle entre la cible et le leader allié
|
|
|
|
|
|
|
|
- # TODO: faire un algo pour identifier les neutres que le leader ennemi pourrait atteindre dans les prochains rounds, et avant notre leader
|
|
|
|
|
for a in self.allied_cultists:
|
|
for a in self.allied_cultists:
|
|
|
for pos, dist in lost_causes.items():
|
|
for pos, dist in lost_causes.items():
|
|
|
- if not pos in self.index:
|
|
|
|
|
|
|
+ if pos not in self.index:
|
|
|
log(f"<!> {pos} is not in the index!!")
|
|
log(f"<!> {pos} is not in the index!!")
|
|
|
continue
|
|
continue
|
|
|
|
|
|
|
|
u = self.index[pos]
|
|
u = self.index[pos]
|
|
|
|
|
|
|
|
- shooting_distance = self.shooting_distance(a.pos, u.pos)
|
|
|
|
|
|
|
+ line_of_sight = self.line_of_sight(a.pos, u.pos)
|
|
|
|
|
+ shooting_distance = len(line_of_sight)
|
|
|
if not shooting_distance:
|
|
if not shooting_distance:
|
|
|
continue
|
|
continue
|
|
|
|
|
|
|
|
if shooting_distance <= u.SHOOTING_RANGE:
|
|
if shooting_distance <= u.SHOOTING_RANGE:
|
|
|
# la cible est à portée
|
|
# la cible est à portée
|
|
|
- action = ActionShoot(a, u, f"peace keeping, shoot at {u.id}")
|
|
|
|
|
|
|
+ action = ActionShoot(a, u, f"peace keeping, shoot from {a.pos} ({line_of_sight})")
|
|
|
|
|
|
|
|
priority = k_shoot_dangerous_neutral_distance * shooting_distance
|
|
priority = k_shoot_dangerous_neutral_distance * shooting_distance
|
|
|
priority += k_shoot_dangerous_neutral_threat * dist
|
|
priority += k_shoot_dangerous_neutral_threat * dist
|
|
@@ -550,7 +594,9 @@ class Grid(BaseClass):
|
|
|
x0, y0 = start
|
|
x0, y0 = start
|
|
|
x1, y1 = target
|
|
x1, y1 = target
|
|
|
|
|
|
|
|
- if y0 > y1:
|
|
|
|
|
|
|
+ reversed = y0 > y1
|
|
|
|
|
+
|
|
|
|
|
+ if reversed:
|
|
|
# on fait toujours de bas en haut, du coup on inverse au besoin
|
|
# on fait toujours de bas en haut, du coup on inverse au besoin
|
|
|
x0, y0, x1, y1 = x1, y1, x0, y0
|
|
x0, y0, x1, y1 = x1, y1, x0, y0
|
|
|
|
|
|
|
@@ -564,13 +610,8 @@ class Grid(BaseClass):
|
|
|
x, y = x0, y0
|
|
x, y = x0, y0
|
|
|
|
|
|
|
|
while 1:
|
|
while 1:
|
|
|
- line.append((x, y))
|
|
|
|
|
-
|
|
|
|
|
- if x == x1 and y == y1:
|
|
|
|
|
- break
|
|
|
|
|
-
|
|
|
|
|
e2 = 2 * err
|
|
e2 = 2 * err
|
|
|
- if e2 > (-1 * dy):
|
|
|
|
|
|
|
+ if e2 > -1 * dy:
|
|
|
err -= dy
|
|
err -= dy
|
|
|
x += sx
|
|
x += sx
|
|
|
|
|
|
|
@@ -578,6 +619,16 @@ class Grid(BaseClass):
|
|
|
err += dx
|
|
err += dx
|
|
|
y += sy
|
|
y += sy
|
|
|
|
|
|
|
|
|
|
+ if x == x1 and y == y1:
|
|
|
|
|
+ break
|
|
|
|
|
+
|
|
|
|
|
+ line.append((x, y))
|
|
|
|
|
+
|
|
|
|
|
+ if reversed:
|
|
|
|
|
+ line = line[::-1]
|
|
|
|
|
+
|
|
|
|
|
+ line = [start] + line + [target]
|
|
|
|
|
+
|
|
|
return line
|
|
return line
|
|
|
|
|
|
|
|
def line_of_sight(self, from_, to_):
|
|
def line_of_sight(self, from_, to_):
|