Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Let AI consider fighter vs planet mechanics #1434

Merged
merged 8 commits into from Apr 6, 2017
15 changes: 10 additions & 5 deletions default/python/AI/AIstate.py
Expand Up @@ -456,11 +456,14 @@ def update_system_status(self):
sys_status['enemies_supplied'] = enemy_supply.get(sys_id, [])
sys_status['enemies_nearly_supplied'] = enemy_near_supply.get(sys_id, [])
my_ratings_list = []
my_ratings_against_planets_list = []
for fid in sys_status['myfleets']:
this_rating = self.get_rating(fid, True, self.get_standard_enemy()) # DONE!
this_rating = self.get_rating(fid, True, self.get_standard_enemy())
my_ratings_list.append(this_rating)
my_ratings_against_planets_list.append(self.get_rating(fid, against_planets=True))
if sys_id != INVALID_ID:
sys_status['myFleetRating'] = my_ratings_list and CombatRatingsAI.combine_ratings_list(my_ratings_list) or 0
sys_status['myFleetRating'] = CombatRatingsAI.combine_ratings_list(my_ratings_list)
sys_status['myFleetRatingVsPlanets'] = CombatRatingsAI.combine_ratings_list(my_ratings_against_planets_list)
sys_status['all_local_defenses'] = CombatRatingsAI.combine_ratings(sys_status['myFleetRating'], sys_status['mydefenses']['overall'])
sys_status['neighbors'] = set(dict_from_map(universe.getSystemNeighborsMap(sys_id, self.empireID)))

Expand Down Expand Up @@ -584,18 +587,19 @@ def has_target(self, mission_type, target):
return True
return False

def get_rating(self, fleet_id, force_new=False, enemy_stats=None):
def get_rating(self, fleet_id, force_new=False, enemy_stats=None, against_planets=False):
"""Returns a dict with various rating info."""
if fleet_id in self.fleetStatus and not force_new and enemy_stats is None:
return self.fleetStatus[fleet_id].get('rating', 0)
else:
fleet = fo.getUniverse().getFleet(fleet_id)
if not fleet:
return {} # TODO: also ensure any info for that fleet is deleted
status = {'rating': CombatRatingsAI.get_fleet_rating(fleet_id, enemy_stats), # TODO
status = {'rating': CombatRatingsAI.get_fleet_rating(fleet_id, enemy_stats),
'ratingVsPlanets': CombatRatingsAI.get_fleet_rating_against_planets(fleet_id),
'sysID': fleet.systemID, 'nships': len(fleet.shipIDs)}
self.fleetStatus[fleet_id] = status
return status['rating']
return status['rating'] if not against_planets else status['ratingVsPlanets']

def update_fleet_rating(self, fleet_id):
self.get_rating(fleet_id, force_new=True)
Expand Down Expand Up @@ -663,6 +667,7 @@ def __clean_fleet_roles(self, just_resumed=False):
"""Removes fleetRoles if a fleet has been lost, and update fleet Ratings."""
for sys_id in self.systemStatus:
self.systemStatus[sys_id]['myFleetRating'] = 0
self.systemStatus[sys_id]['myFleetRatingVsPlanets'] = 0

universe = fo.getUniverse()
ok_fleets = FleetUtilsAI.get_empire_fleet_ids()
Expand Down
28 changes: 24 additions & 4 deletions default/python/AI/CombatRatingsAI.py
Expand Up @@ -166,15 +166,20 @@ def get_fighter_stats(self):
"""
return self._fighter_stats.get_stats()

def get_rating(self, enemy_stats=None):
def get_rating(self, enemy_stats=None, ignore_fighters=False):
"""Calculate a rating against specified enemy.

:param enemy_stats: Enemy stats to be rated against
:type enemy_stats: ShipCombatStats
:param ignore_fighters: If True, acts as if fighters are not launched
:type ignore_fighters: bool
:return: rating against specified enemy
:rtype: float
"""
# adjust base stats according to enemy stats
def _rating():
return my_total_attack * my_structure

my_attacks, my_structure, my_shields = self.get_basic_stats()
e_avg_attack = 1
if enemy_stats:
Expand All @@ -192,6 +197,9 @@ def get_rating(self, enemy_stats=None):
my_total_attack = sum(n*dmg for dmg, n in my_attacks.iteritems())
my_structure += my_shields

if ignore_fighters:
return _rating()

# consider fighter attacks
capacity, launch_rate, fighter_damage = self.get_fighter_stats()
launched_1st_bout = min(capacity, launch_rate)
Expand All @@ -205,7 +213,10 @@ def get_rating(self, enemy_stats=None):
fighters_shot_down = (1-survival_rate**2) * launched_1st_bout + (1-survival_rate) * launched_2nd_bout
damage_prevented = fighters_shot_down * e_avg_attack
my_structure += damage_prevented
return my_total_attack * my_structure
return _rating()

def get_rating_vs_planets(self):
return self.get_rating(ignore_fighters=True)

def get_stats(self, hashable=False):
""" Get all combat related stats of the ship.
Expand Down Expand Up @@ -238,15 +249,20 @@ def get_ship_combat_stats(self):
"""Returns list of ShipCombatStats of fleet."""
return list(self.__ship_stats)

def get_rating(self, enemy_stats=None):
def get_rating(self, enemy_stats=None, ignore_fighters=False):
"""Calculates the rating of the fleet by combining all its ships ratings.

:param enemy_stats: enemy to be rated against
:type enemy_stats: ShipCombatStats
:param ignore_fighters: If True, acts as if fighters are not launched
:type ignore_fighters: bool
:return: Rating of the fleet
:rtype: float
"""
return combine_ratings_list(map(lambda x: x.get_rating(enemy_stats), self.__ship_stats))
return combine_ratings_list(map(lambda x: x.get_rating(enemy_stats, ignore_fighters), self.__ship_stats))

def get_rating_vs_planets(self):
return self.get_rating(ignore_fighters=True)

def __get_stats_from_fleet(self):
"""Calculate fleet combat stats (i.e. the stats of all its ships)."""
Expand All @@ -271,6 +287,10 @@ def get_fleet_rating(fleet_id, enemy_stats=None):
return FleetCombatStats(fleet_id, consider_refuel=False).get_rating(enemy_stats)


def get_fleet_rating_against_planets(fleet_id):
return FleetCombatStats(fleet_id, consider_refuel=False).get_rating_vs_planets()


@cache_by_session
def _get_species_grades(species_name, grade_type):
spec_tags = []
Expand Down
12 changes: 12 additions & 0 deletions default/python/AI/FleetUtilsAI.py
Expand Up @@ -137,11 +137,23 @@ def get_fleets_for_mission(target_stats, min_stats, cur_stats, starting_system,
troop_capacity = count_troops_in_fleet(fleet_id)
if troop_capacity <= 0:
continue

# check if we need additional rating vs planets
this_rating_vs_planets = 0
if 'ratingVsPlanets' in target_stats:
this_rating_vs_planets = foAI.foAIstate.get_rating(fleet_id, against_planets=True)
if this_rating_vs_planets <= 0 and cur_stats.get('rating', 0) >= target_stats.get('rating', 0):
# we already have enough general rating, so do not add any more warships useless against planets
continue

# all checks passed, add ship to selected fleets and update the stats
fleet_list.append(fleet_id)
fleet_pool_set.remove(fleet_id)
this_rating = foAI.foAIstate.get_rating(fleet_id)
cur_stats['rating'] = CombatRatingsAI.combine_ratings(cur_stats.get('rating', 0), this_rating)
if 'ratingVsPlanets' in target_stats:
cur_stats['ratingVsPlanets'] = CombatRatingsAI.combine_ratings(cur_stats.get('ratingVsPlanets', 0),
this_rating_vs_planets)
if 'troopCapacity' in target_stats:
cur_stats['troopCapacity'] = cur_stats.get('troopCapacity', 0) + troop_capacity
# if we already meet the requirements, we can stop looking for more ships
Expand Down
4 changes: 2 additions & 2 deletions default/python/AI/InvasionAI.py
Expand Up @@ -156,10 +156,10 @@ def get_invasion_fleets():
del foAI.foAIstate.qualifyingTroopBaseTargets[pid]
continue
if (planet.currentMeterValue(fo.meterType.shield) > 0 and
this_sys_status.get('myFleetRating', 0) < 0.8 * this_sys_status.get('totalThreat', 0)):
(this_sys_status.get('myFleetRating', 0) < 0.8 * this_sys_status.get('totalThreat', 0) or
this_sys_status.get('myFleetRatingVsPlanets', 0) < this_sys_status.get('planetThreat', 0))):
# this system not secured, so ruling out invasion base troops for now
# don't immediately delete from qualifyingTroopBaseTargets or it will be opened up for regular troops
#del foAI.foAIstate.qualifyingTroopBaseTargets[pid]
continue
loc = foAI.foAIstate.qualifyingTroopBaseTargets[pid][0]
best_base_trooper_here = ProductionAI.get_best_ship_info(PriorityType.PRODUCTION_ORBITAL_INVASION, loc)[1]
Expand Down