|
|
@@ -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))
|
|
|
|