Browse Source

add peace keeping action

olinox14 3 years ago
parent
commit
ddfc7aef2f
1 changed files with 67 additions and 14 deletions
  1. 67 14
      cultist_war/main.py

+ 67 - 14
cultist_war/main.py

@@ -195,12 +195,13 @@ class ActionMove(Action):
 
 
 class ActionShoot(Action):
-    def __init__(self, unit, target):
+    def __init__(self, unit, target, message=''):
         self.unit = unit
         self.target = target
+        self.message = message
 
     def __repr__(self):
-        return f"<ActionShoot: {self.unit.id} to {self.target.id}>"
+        return f"<ActionShoot: {self.unit.id} to {self.target.id} ({self.message})>"
 
     def exec(self):
         print(f"{self.unit.id} SHOOT {self.target.id}")
@@ -322,7 +323,7 @@ class Grid(BaseClass):
                 key=(lambda pos: pos in self.index and self.index[pos].neutral),
                 limit=min(4, len(self.neutrals))
             )
-            log(conversion_path)
+            log(f"conversion : {conversion_path}")
         self.conversion_path = conversion_path
 
     def update(self):
@@ -352,8 +353,8 @@ class Grid(BaseClass):
                 for pos in covers:
                     action = ActionMove(self.cult_leader, pos, f'take cover (t: {current_threat})')
 
-                    interest = self.conversion_path and self.conversion_path.steps and pos in [s.pos for s in
-                                                                                               self.conversion_path.steps]
+                    interest = bool(self.conversion_path and self.conversion_path.steps and pos in [s.pos for s in
+                                                                                               self.conversion_path.steps])
 
                     priority = k0_protect_cult_leader
                     priority += k_protect_threat_level * current_threat
@@ -401,7 +402,7 @@ class Grid(BaseClass):
 
                     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))
+                    # log(self.line_of_sight(a.pos, u.pos))
                     actions.put(priority, action)
                 else:
                     # la cible est hors de portée, mais elle est plus faible et sans soutien
@@ -409,11 +410,9 @@ class Grid(BaseClass):
                     pass
 
         # Position
-        k0_position = 50
+        k0_position = 30
         k_advantage = 40
-        k_position_distance = 10
-        k_position_heat = -2
-        k_position_danger = 10
+        k_position_distance = 3  # on veut privilégier les plus près, mais pas non plus décourager ceux de l'arrière...
 
         advantage = len(self.owned_units) > len(self.opponent_units) + len(self.neutrals)
 
@@ -426,23 +425,75 @@ class Grid(BaseClass):
 
             else:
                 # l'unité semble en sécurité, go to front-line
-                nearest_frontline = self.discover(a, key=lambda x: self.threat[x] == 1, limit=1)
+                nearest_frontline = self.discover(a, key=lambda x: self.can_move_on(a, x) and self.threat[x] == 1, limit=1)
                 if not nearest_frontline:
-                    log(f"<!> {u.id} can not join nearest frontline")
+                    log(f"<!> {a.id} can not join nearest frontline")
                     continue
 
                 path, target = nearest_frontline[0]
                 if path:
-                    log(f"{a.id} - {path} - {target}")
+                    # log(f"{a.id} - {path} - {target}")
                     # already in place
                     priority = k0_position
                     priority += k_position_distance * len(path)
-                    priority += k_position_danger * sum(self.threat[p] for p in path)
                     priority += k_advantage * advantage
 
                     action = ActionMove(a, path[0], f'go to frontline {target} by {path}')
                     actions.put(priority, action)
 
+        # Shoot neutral units:
+        k_shoot_dangerous_neutral_threat = 2
+        k_shoot_dangerous_neutral_distance = 8
+        dangerous_neutral_distance_limit = 3
+
+        if self.opponent_cult_leader:
+
+            discovered_by_opponent = self.discover(self.opponent_cult_leader, key=lambda x: x in self.index and self.index[x].neutral, limit=3)
+            neutrals_threaten = {}
+            for path, target_pos in discovered_by_opponent:
+                if len(path) > dangerous_neutral_distance_limit:
+                    continue
+                if target_pos not in neutrals_threaten or neutrals_threaten[target_pos] < len(path):
+                    neutrals_threaten[target_pos] = len(path)
+
+            threaten_neutrals_from_leader = {}
+            if self.cult_leader:
+                discovered_by_leader = self.discover(self.cult_leader, key=lambda x: x in neutrals_threaten, limit=3)
+                for path, target_pos in discovered_by_leader:
+                    if target_pos not in threaten_neutrals_from_leader or threaten_neutrals_from_leader[target_pos] < len(path):
+                        threaten_neutrals_from_leader[target_pos] = len(path)
+
+            log(f"Nearest from opp. leader: {neutrals_threaten}")
+            log(f"Nearest from own  leader: {threaten_neutrals_from_leader}")
+
+            lost_causes = {}
+            for target, dist in neutrals_threaten.items():
+                if target not in threaten_neutrals_from_leader or threaten_neutrals_from_leader[target] <= neutrals_threaten[target]:
+                    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é
+
+            # 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 pos, dist in lost_causes.items():
+                    if not pos in self.index:
+                        log(f"<!> {pos} is not in the index!!")
+                        continue
+
+                    u = self.index[pos]
+
+                    shooting_distance = self.shooting_distance(a.pos, u.pos)
+                    if not shooting_distance:
+                        continue
+
+                    if shooting_distance <= u.SHOOTING_RANGE:
+                        # la cible est à portée
+                        action = ActionShoot(a, u, f"peace keeping, shoot at {u.id}")
+
+                        priority = k_shoot_dangerous_neutral_distance * shooting_distance
+                        priority += k_shoot_dangerous_neutral_threat * dist
+                        # log(self.line_of_sight(a.pos, u.pos))
+                        actions.put(priority, action)
+
         # TODO: action 'take cover' pour les unités aussi
         # TODO: action 'peace-keeping': tirer sur les neutres qu'on ne pourra pas convertir avant l'ennemi
         # TODO: action 'intercept': une unité se place entre un tireur ennemi et le leader; en dernier recours
@@ -601,6 +652,8 @@ class Grid(BaseClass):
                     return paths
 
                 if current != unit.pos and key(pos):
+                    # TODO: actuellement, l'algo pourra retourner x chemins différents vers la même cible
+                    #       il faudrait retourner les trois meilleurs chemins vers x cibles (selon 'limit')
                     path = current.ancestors[1:] + [current]
                     paths.append((path, pos))