def follow_vis_system_connections(start_system_id, home_system_id): universe = fo.getUniverse() empire_id = fo.empireID() exploration_list = [start_system_id] while exploration_list: cur_system_id = exploration_list.pop() if cur_system_id in graph_flags: continue graph_flags.add(cur_system_id) system = universe.getSystem(cur_system_id) if cur_system_id in foAI.foAIstate.visBorderSystemIDs: pre_vis = "a border system" elif cur_system_id in foAI.foAIstate.visInteriorSystemIDs: pre_vis = "an interior system" else: pre_vis = "an unknown system" system_header = "*** system %s;" % system if fo.currentTurn() < 50: visibility_turn_list = sorted(universe.getVisibilityTurnsMap( cur_system_id, empire_id).items(), key=lambda x: x[0].numerator) visibility_info = ', '.join('%s: %s' % (vis.name, turn) for vis, turn in visibility_turn_list) print "%s previously %s. Visibility per turn: %s " % ( system_header, pre_vis, visibility_info) status_info = [] else: status_info = [system_header] has_been_visible = universe.getVisibilityTurnsMap( cur_system_id, empire_id).get(fo.visibility.partial, 0) > 0 is_connected = universe.systemsConnected(cur_system_id, home_system_id, -1) # self.empire_id) status_info.append(" -- is%s partially visible" % ("" if has_been_visible else " not")) status_info.append(" -- is%s visibly connected to homesystem" % ("" if is_connected else " not")) if has_been_visible: sys_status = foAI.foAIstate.systemStatus.setdefault( cur_system_id, {}) foAI.foAIstate.visInteriorSystemIDs.add(cur_system_id) foAI.foAIstate.visBorderSystemIDs.discard(cur_system_id) neighbors = set( dict_from_map( universe.getSystemNeighborsMap(cur_system_id, empire_id)).keys()) sys_status.setdefault('neighbors', set()).update(neighbors) if neighbors: status_info.append(" -- has neighbors %s" % sorted(neighbors)) for sys_id in neighbors: if sys_id not in foAI.foAIstate.exploredSystemIDs: foAI.foAIstate.unexploredSystemIDs.add(sys_id) if (sys_id not in graph_flags) and ( sys_id not in foAI.foAIstate.visInteriorSystemIDs): foAI.foAIstate.visBorderSystemIDs.add(sys_id) exploration_list.append(sys_id) if fo.currentTurn() < 50: print '\n'.join(status_info) print "----------------------------------------------------------"
def follow_vis_system_connections(start_system_id, home_system_id): universe = fo.getUniverse() empire_id = fo.empireID() exploration_list = [start_system_id] while exploration_list: cur_system_id = exploration_list.pop() if cur_system_id in graph_flags: continue graph_flags.add(cur_system_id) system = universe.getSystem(cur_system_id) if cur_system_id in foAI.foAIstate.visBorderSystemIDs: pre_vis = "a border system" elif cur_system_id in foAI.foAIstate.visInteriorSystemIDs: pre_vis = "an interior system" else: pre_vis = "an unknown system" system_header = "*** system %s;" % system if fo.currentTurn() < 50: visibility_turn_list = sorted(universe.getVisibilityTurnsMap(cur_system_id, empire_id).items(), key=lambda x: x[0].numerator) visibility_info = ', '.join('%s: %s' % (vis.name, turn) for vis, turn in visibility_turn_list) print "%s previously %s. Visibility per turn: %s " % (system_header, pre_vis, visibility_info) status_info = [] else: status_info = [system_header] has_been_visible = universe.getVisibilityTurnsMap(cur_system_id, empire_id).get(fo.visibility.partial, 0) > 0 is_connected = universe.systemsConnected(cur_system_id, home_system_id, -1) # self.empire_id) status_info.append(" -- is%s partially visible" % ([" not", ""][has_been_visible])) status_info.append(" -- is%s visibly connected to homesystem" % ([" not", ""][is_connected])) if has_been_visible: sys_status = foAI.foAIstate.systemStatus.setdefault(cur_system_id, {}) foAI.foAIstate.visInteriorSystemIDs.add(cur_system_id) foAI.foAIstate.visBorderSystemIDs.discard(cur_system_id) neighbors = set(dict_from_map(universe.getSystemNeighborsMap(cur_system_id, empire_id)).keys()) sys_status.setdefault('neighbors', set()).update(neighbors) if neighbors: status_info.append(" -- has neighbors %s" % sorted(neighbors)) for sys_id in neighbors: if sys_id not in foAI.foAIstate.exploredSystemIDs: foAI.foAIstate.unexploredSystemIDs.add(sys_id) if (sys_id not in graph_flags) and (sys_id not in foAI.foAIstate.visInteriorSystemIDs): foAI.foAIstate.visBorderSystemIDs.add(sys_id) exploration_list.append(sys_id) if fo.currentTurn() < 50: print '\n'.join(status_info) print "----------------------------------------------------------"
def int_int_map_to_string(int_int_map): return str(dict_from_map(int_int_map))
def update_system_status(self): print 10 * "=", "Updating System Threats", 10 * "=" universe = fo.getUniverse() empire = fo.getEmpire() empire_id = fo.empireID() destroyed_object_ids = universe.destroyedObjectIDs(empire_id) supply_unobstructed_systems = set(empire.supplyUnobstructedSystems) min_hidden_attack = 4 min_hidden_health = 8 system_id_list = universe.systemIDs # will normally look at this, the list of all known systems # for use in debugging verbose = False # assess enemy fleets that may have been momentarily visible cur_e_fighters = {CombatRatingsAI.default_ship_stats().get_stats(hashable=True): [0]} # start with a dummy entry old_e_fighters = {CombatRatingsAI.default_ship_stats().get_stats(hashable=True): [0]} # start with a dummy entry enemy_fleet_ids = [] enemies_by_system = {} my_fleets_by_system = {} fleet_spot_position = {} saw_enemies_at_system = {} my_milship_rating = MilitaryAI.cur_best_mil_ship_rating() current_turn = fo.currentTurn() for fleet_id in universe.fleetIDs: fleet = universe.getFleet(fleet_id) if fleet is None: continue if not fleet.empty: # TODO: check if currently in system and blockaded before accepting destination as location this_system_id = (fleet.nextSystemID != INVALID_ID and fleet.nextSystemID) or fleet.systemID if fleet.ownedBy(empire_id): if fleet_id not in destroyed_object_ids: my_fleets_by_system.setdefault(this_system_id, []).append(fleet_id) fleet_spot_position.setdefault(fleet.systemID, []).append(fleet_id) else: dead_fleet = fleet_id in destroyed_object_ids if not fleet.ownedBy(-1) and (fleet.hasArmedShips or fleet.hasFighterShips): ship_stats = CombatRatingsAI.FleetCombatStats(fleet_id).get_ship_stats(hashable=True) e_f_dict = [cur_e_fighters, old_e_fighters][dead_fleet] # track old/dead enemy fighters for rating assessments in case not enough current info for stats in ship_stats: attacks = stats[0] if attacks: e_f_dict.setdefault(stats, [0])[0] += 1 partial_vis_turn = universe.getVisibilityTurnsMap(fleet_id, empire_id).get(fo.visibility.partial, -9999) if not dead_fleet: # TODO: consider checking death of individual ships. If ships had been moved from this fleet # into another fleet, we might have witnessed their death in that other fleet but if this fleet # had not been seen since before that transfer then the ships might also still be listed here. sys_status = self.systemStatus.setdefault(this_system_id, {}) sys_status['enemy_ship_count'] = sys_status.get('enemy_ship_count', 0) + len(fleet.shipIDs) if partial_vis_turn >= current_turn - 1: # only interested in immediately recent data saw_enemies_at_system[fleet.systemID] = True enemy_fleet_ids.append(fleet_id) enemies_by_system.setdefault(this_system_id, []).append(fleet_id) if not fleet.ownedBy(-1): self.misc.setdefault('enemies_sighted', {}).setdefault(current_turn, []).append(fleet_id) rating = CombatRatingsAI.get_fleet_rating(fleet_id, enemy_stats=CombatRatingsAI.get_empire_standard_fighter()) if rating > 0.25 * my_milship_rating: self.misc.setdefault('dangerous_enemies_sighted', {}).setdefault(current_turn, []).append(fleet_id) e_f_dict = [cur_e_fighters, old_e_fighters][len(cur_e_fighters) == 1] std_fighter = sorted([(v, k) for k, v in e_f_dict.items()])[-1][1] self.__empire_standard_enemy = std_fighter self.empire_standard_enemy_rating = self.get_standard_enemy().get_rating() # TODO: If no current information available, rate against own fighters # assess fleet and planet threats & my local fleets for sys_id in system_id_list: sys_status = self.systemStatus.setdefault(sys_id, {}) system = universe.getSystem(sys_id) if verbose: print "AIState threat evaluation for %s" % system # update fleets sys_status['myfleets'] = my_fleets_by_system.get(sys_id, []) sys_status['myFleetsAccessible'] = fleet_spot_position.get(sys_id, []) local_enemy_fleet_ids = enemies_by_system.get(sys_id, []) sys_status['localEnemyFleetIDs'] = local_enemy_fleet_ids if system: sys_status['name'] = system.name for fid in system.fleetIDs: if fid in destroyed_object_ids: # TODO: double check are these checks/deletes necessary? self.delete_fleet_info(fid) # this is safe even if fleet wasn't mine continue fleet = universe.getFleet(fid) if not fleet or fleet.empty: self.delete_fleet_info(fid) # this is safe even if fleet wasn't mine continue # update threats sys_vis_dict = universe.getVisibilityTurnsMap(sys_id, fo.empireID()) partial_vis_turn = sys_vis_dict.get(fo.visibility.partial, -9999) mob_ratings = [] # for mobile unowned monster fleets lost_fleet_rating = 0 enemy_ratings = [] monster_ratings = [] mobile_fleets = [] for fid in local_enemy_fleet_ids: fleet = universe.getFleet(fid) if not fleet: continue fleet_rating = CombatRatingsAI.get_fleet_rating(fid, enemy_stats=CombatRatingsAI.get_empire_standard_fighter()) if fleet.speed == 0: monster_ratings.append(fleet_rating) if verbose: print "\t immobile enemy fleet %s has rating %.1f" % (fleet, fleet_rating) else: if verbose: print "\t mobile enemy fleet %s has rating %.1f" % (fleet, fleet_rating) mobile_fleets.append(fid) if fleet.unowned: mob_ratings.append(fleet_rating) else: enemy_ratings.append(fleet_rating) enemy_rating = CombatRatingsAI.combine_ratings_list(enemy_ratings) monster_rating = CombatRatingsAI.combine_ratings_list(monster_ratings) mob_rating = CombatRatingsAI.combine_ratings_list(mob_ratings) if fleetsLostBySystem.get(sys_id, []): lost_fleet_rating = CombatRatingsAI.combine_ratings_list(fleetsLostBySystem[sys_id]) if not system or partial_vis_turn == -9999: # under current visibility rules should not be possible to have any losses or other info here, but just in case... if verbose: print "Have never had partial vis for system %d ( %s ) -- basing threat assessment on old info and lost ships" % (sys_id, sys_status.get('name', "name unknown")) sys_status.setdefault('local_fleet_threats', set()) sys_status['planetThreat'] = 0 sys_status['fleetThreat'] = int(max(CombatRatingsAI.combine_ratings(enemy_rating, mob_rating), 0.98 * sys_status.get('fleetThreat', 0), 1.1 * lost_fleet_rating - monster_rating)) sys_status['monsterThreat'] = int(max(monster_rating, 0.98 * sys_status.get('monsterThreat', 0), 1.1 * lost_fleet_rating - enemy_rating - mob_rating)) sys_status['enemy_threat'] = int(max(enemy_rating, 0.98 * sys_status.get('enemy_threat', 0), 1.1 * lost_fleet_rating - monster_rating - mob_rating)) sys_status['mydefenses'] = {'overall': 0, 'attack': 0, 'health': 0} sys_status['totalThreat'] = sys_status['fleetThreat'] sys_status['regional_fleet_threats'] = sys_status['local_fleet_threats'].copy() continue # have either stale or current info pattack = 0 phealth = 0 mypattack, myphealth = 0, 0 for pid in system.planetIDs: prating = self.assess_planet_threat(pid, sighting_age=current_turn - partial_vis_turn) planet = universe.getPlanet(pid) if not planet: continue if planet.owner == self.empireID: # TODO: check for diplomatic status mypattack += prating['attack'] myphealth += prating['health'] else: if [special for special in planet.specials if "_NEST_" in special]: sys_status['nest_threat'] = 100 pattack += prating['attack'] phealth += prating['health'] sys_status['planetThreat'] = pattack * phealth sys_status['mydefenses'] = {'overall': mypattack * myphealth, 'attack': mypattack, 'health': myphealth} if max(sys_status.get('totalThreat', 0), pattack * phealth) >= 0.6 * lost_fleet_rating: # previous threat assessment could account for losses, ignore the losses now lost_fleet_rating = 0 # TODO use sitrep combat info rather than estimating stealthed enemies by fleets lost to them # TODO also only consider past stealthed fleet threat to still be present if the system is still obstructed # TODO: track visibility across turns in order to distinguish the blip of visibility in (losing) combat, # which FO currently treats as being for the previous turn, partially superseding the previous visibility for that turn if not partial_vis_turn == current_turn: # (universe.getVisibility(sys_id, self.empire_id) >= fo.visibility.partial): sys_status.setdefault('local_fleet_threats', set()) sys_status['currently_visible'] = False # print "Stale visibility for system %d ( %s ) -- last seen %d, current Turn %d -- basing threat assessment on old info and lost ships"%(sys_id, sys_status.get('name', "name unknown"), partial_vis_turn, currentTurn) sys_status['fleetThreat'] = int(max(CombatRatingsAI.combine_ratings(enemy_rating, mob_rating), 0.98 * sys_status.get('fleetThreat', 0), 2.0 * lost_fleet_rating - max(sys_status.get('monsterThreat', 0), monster_rating))) sys_status['enemy_threat'] = int(max(enemy_rating, 0.98 * sys_status.get('enemy_threat', 0), 1.1 * lost_fleet_rating - max(sys_status.get('monsterThreat', 0), monster_rating))) sys_status['monsterThreat'] = int(max(monster_rating, 0.98 * sys_status.get('monsterThreat', 0))) # sys_status['totalThreat'] = ((pattack + enemy_attack + monster_attack) ** 0.8) * ((phealth + enemy_health + monster_health)** 0.6) # reevaluate this sys_status['totalThreat'] = max(CombatRatingsAI.combine_ratings_list([enemy_rating, mob_rating, monster_rating, pattack * phealth]), 2 * lost_fleet_rating, 0.98 * sys_status.get('totalThreat', 0)) else: # system considered visible #TODO: reevaluate as visibility rules change sys_status['currently_visible'] = True sys_status['local_fleet_threats'] = set(mobile_fleets) sys_status['fleetThreat'] = int(max(CombatRatingsAI.combine_ratings(enemy_rating, mob_rating), 2 * lost_fleet_rating - monster_rating)) # includes mobile monsters if verbose: print "enemy threat calc parts: enemy rating %.1f, lost fleet rating %.1f, monster_rating %.1f" % (enemy_rating, lost_fleet_rating, monster_rating) sys_status['enemy_threat'] = int(max(enemy_rating, 2 * lost_fleet_rating - monster_rating)) # does NOT include mobile monsters sys_status['monsterThreat'] = monster_rating sys_status['totalThreat'] = CombatRatingsAI.combine_ratings_list([enemy_rating, mob_rating, monster_rating, pattack * phealth]) sys_status['regional_fleet_threats'] = sys_status['local_fleet_threats'].copy() sys_status['fleetThreat'] = max(sys_status['fleetThreat'], sys_status.get('nest_threat', 0)) sys_status['totalThreat'] = max(sys_status['totalThreat'], sys_status.get('nest_threat', 0)) if partial_vis_turn > 0 and sys_id not in supply_unobstructed_systems: # has been seen with Partial Vis, but is currently supply-blocked sys_status['fleetThreat'] = max(sys_status['fleetThreat'], min_hidden_attack * min_hidden_health) sys_status['totalThreat'] = max(sys_status['totalThreat'], ((pattack + min_hidden_attack) ** 0.8) * ((phealth + min_hidden_health) ** 0.6)) if verbose and sys_status['fleetThreat'] > 0: print "%s intermediate status: %s" % (system, sys_status) enemy_supply, enemy_near_supply = self.assess_enemy_supply() # TODO: assess change in enemy supply over time # assess secondary threats (threats of surrounding systems) and update my fleet rating for sys_id in system_id_list: sys_status = self.systemStatus[sys_id] 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()) 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'] = 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))) for sys_id in system_id_list: sys_status = self.systemStatus[sys_id] neighbors = sys_status.get('neighbors', set()) this_system = fo.getUniverse().getSystem(sys_id) if verbose: print "Regional Assessment for %s with local fleet threat %.1f" % (this_system, sys_status.get('fleetThreat', 0)) jumps2 = set() jumps3 = set() jumps4 = set() for seta, setb in [(neighbors, jumps2), (jumps2, jumps3), (jumps3, jumps4)]: for sys2id in seta: setb.update(self.systemStatus.get(sys2id, {}).get('neighbors', set())) jump2ring = jumps2 - neighbors - {sys_id} jump3ring = jumps3 - jumps2 - neighbors - {sys_id} jump4ring = jumps4 - jumps3 - jumps2 - neighbors - {sys_id} sys_status['2jump_ring'] = jump2ring sys_status['3jump_ring'] = jump3ring sys_status['4jump_ring'] = jump4ring threat, max_threat, myrating, j1_threats = self.area_ratings(neighbors, ref_sys_name="neighbors %s" % this_system) if verbose else self.area_ratings(neighbors) sys_status['neighborThreat'] = threat sys_status['max_neighbor_threat'] = max_threat sys_status['my_neighbor_rating'] = myrating threat, max_threat, myrating, j2_threats = self.area_ratings(jump2ring, ref_sys_name="jump2 %s" % this_system) if verbose else self.area_ratings(jump2ring) sys_status['jump2_threat'] = threat sys_status['my_jump2_rating'] = myrating threat, max_threat, myrating, j3_threats = self.area_ratings(jump3ring) sys_status['jump3_threat'] = threat sys_status['my_jump3_rating'] = myrating threat_keys = ['fleetThreat', 'neighborThreat', 'jump2_threat'] # for local system includes both enemies and mobs sys_status['regional_threat'] = CombatRatingsAI.combine_ratings_list(map(lambda x: sys_status.get(x, 0), threat_keys)) # TODO: investigate cases where regional_threat has been nonzero but no regional_threat_fleets # (probably due to attenuating history of past threats) sys_status.setdefault('regional_fleet_threats', set()).update(j1_threats, j2_threats)
def issue_fleet_orders(self): """issues AIFleetOrders which can be issued in system and moves to next one if is possible""" # TODO: priority order_completed = True print "--------------" print "Checking orders for fleet %d (on turn %d)" % (self.target_id, fo.currentTurn()) print "\t Full Orders are:" for this_orders in self.orders: print "\t\t %s" % this_orders print "/t/t------" if AIFleetMissionType.FLEET_MISSION_INVASION in self.get_mission_types(): self._check_retarget_invasion() for fleet_order in self.orders: print " checking Order: %s" % fleet_order order_type = fleet_order.get_fleet_order_type() if order_type in [AIFleetOrderType.ORDER_COLONISE, AIFleetOrderType.ORDER_OUTPOST, AIFleetOrderType.ORDER_INVADE]: # TODO: invasion? if self._check_abort_mission(fleet_order): return self.check_mergers(context=str(fleet_order)) if fleet_order.can_issue_order(verbose=True): if order_type == AIFleetOrderType.ORDER_MOVE and order_completed: # only move if all other orders completed fleet_order.issue_order() elif order_type not in [AIFleetOrderType.ORDER_MOVE, AIFleetOrderType.ORDER_DEFEND]: fleet_order.issue_order() if not fleet_order.is_execution_completed(): order_completed = False else: # check that we're not held up by a Big Monster if order_type == AIFleetOrderType.ORDER_MOVE: this_system_id = fleet_order.get_target_target().target_id this_status = foAI.foAIstate.systemStatus.setdefault(this_system_id, {}) if this_status.get('monsterThreat', 0) > fo.currentTurn() * ProductionAI.curBestMilShipRating()/4.0: first_mission = self.get_mission_types()[0] if self.get_mission_types() else AIFleetMissionType.FLEET_MISSION_INVALID if (first_mission not in (AIFleetMissionType.FLEET_MISSION_ATTACK, AIFleetMissionType.FLEET_MISSION_MILITARY, AIFleetMissionType.FLEET_MISSION_HIT_AND_RUN, AIFleetMissionType.FLEET_MISSION_SECURE, ) or fleet_order != self.orders[-1] # if this move order is not this mil fleet's final destination, and blocked by Big Monster, release and hope for more effective reassignment ): print "Aborting mission due to being blocked by Big Monster at system %d, threat %d"%(this_system_id, foAI.foAIstate.systemStatus[this_system_id]['monsterThreat']) print "Full set of orders were:" for this_orders in self.orders: print "\t\t %s" % this_orders self.clear_fleet_orders() self.clear_targets(([-1] + self.get_mission_types()[:1])[-1]) return # moving to another system stops issuing all orders in system where fleet is # move order is also the last order in system if order_type == AIFleetOrderType.ORDER_MOVE: fleet = fo.getUniverse().getFleet(self.target_id) if fleet.systemID != fleet_order.get_target_target().target_id: break else: # went through entire order list if order_completed: orders = self.orders last_order = orders[-1] if orders else None universe = fo.getUniverse() if last_order and last_order.get_fleet_order_type() == AIFleetOrderType.ORDER_COLONISE: planet = universe.getPlanet(last_order.get_target_target().target_id) sys_partial_vis_turn = dict_from_map(universe.getVisibilityTurnsMap(planet.systemID, fo.empireID())).get(fo.visibility.partial, -9999) planet_partial_vis_turn = dict_from_map(universe.getVisibilityTurnsMap(planet.id, fo.empireID())).get(fo.visibility.partial, -9999) if planet_partial_vis_turn == sys_partial_vis_turn and not planet.currentMeterValue(fo.meterType.population): print "Potential Error: Fleet %d has tentatively completed its colonize mission but will wait to confirm population." % self.target_id print " Order details are %s" % last_order print " Order is valid: %s ; is Executed : %s; is execution completed: %s " % (last_order.is_valid(), last_order.isExecuted(), last_order.isExecutionCompleted()) if not last_order.is_valid(): source_target = last_order.get_source_target() target_target = last_order.get_target_target() print " source target validity: %s; target target validity: %s " % (source_target.valid, target_target.valid) if EnumsAI.AITargetType.TARGET_SHIP == source_target.target_type: ship_id = source_target.target_id ship = universe.getShip(ship_id) if not ship: print "Ship id %d not a valid ship id" % ship_id print " source target Ship (%d), species %s, can%s colonize" % (ship_id, ship.speciesName, ["not", ""][ship.canColonize]) return # colonize order must not have completed yet clearAll = True last_sys_target = -1 if last_order and last_order.get_fleet_order_type() == AIFleetOrderType.ORDER_MILITARY: last_sys_target = last_order.get_target_target().target_id # if (AIFleetMissionType.FLEET_MISSION_SECURE in self.get_mission_types()) or # not doing this until decide a way to release from a SECURE mission secure_targets = set(AIstate.colonyTargetedSystemIDs + AIstate.outpostTargetedSystemIDs + AIstate.invasionTargetedSystemIDs + AIstate.blockadeTargetedSystemIDs) if last_sys_target in secure_targets: # consider a secure mission if last_sys_target in AIstate.colonyTargetedSystemIDs: secure_type = "Colony" elif last_sys_target in AIstate.outpostTargetedSystemIDs: secure_type = "Outpost" elif last_sys_target in AIstate.invasionTargetedSystemIDs: secure_type = "Invasion" elif last_sys_target in AIstate.blockadeTargetedSystemIDs: secure_type = "Blockade" else: secure_type = "Unidentified" print "Fleet %d has completed initial stage of its mission to secure system %d (targeted for %s), may release a portion of ships" % (self.target_id, last_sys_target, secure_type) clearAll = False fleet_id = self.target_id if clearAll: if orders: print "Fleet %d has completed its mission; clearing all orders and targets." % self.target_id print "Full set of orders were:" for this_orders in orders: print "\t\t %s" % this_orders self.clear_fleet_orders() self.clear_targets(([-1] + self.get_mission_types()[:1])[-1]) if foAI.foAIstate.get_fleet_role(fleet_id) in (AIFleetMissionType.FLEET_MISSION_MILITARY, AIFleetMissionType.FLEET_MISSION_ATTACK, AIFleetMissionType.FLEET_MISSION_DEFEND, AIFleetMissionType.FLEET_MISSION_HIT_AND_RUN, AIFleetMissionType.FLEET_MISSION_SECURE): allocations = MilitaryAI.get_military_fleets(milFleetIDs=[fleet_id], tryReset=False, thisround="Fleet %d Reassignment" % fleet_id) if allocations: MilitaryAI.assign_military_fleets_to_systems(useFleetIDList=[fleet_id], allocations=allocations) else: # no orders print "No Current Orders" else: #TODO: evaluate releasing a smaller portion or none of the ships system_status = foAI.foAIstate.systemStatus.setdefault(last_sys_target, {}) new_fleets = [] threat_present = (system_status.get('totalThreat', 0) != 0) or (system_status.get('neighborThreat', 0) != 0) target_system = universe.getSystem(last_sys_target) if not threat_present and target_system: for pid in target_system.planetIDs: planet = universe.getPlanet(pid) if planet and planet.owner != fo.empireID() and planet.currentMeterValue(fo.meterType.maxDefense) > 0: threat_present = True break if not threat_present: print "No current threat in target system; releasing a portion of ships." new_fleets = FleetUtilsAI.split_fleet(self.target_id) # at least first stage of current task is done; release extra ships for potential other deployments else: print "Threat remains in target system; NOT releasing any ships." new_military_fleets = [] for fleet_id in new_fleets: if foAI.foAIstate.get_fleet_role(fleet_id) in (AIFleetMissionType.FLEET_MISSION_MILITARY, AIFleetMissionType.FLEET_MISSION_ATTACK, AIFleetMissionType.FLEET_MISSION_DEFEND, AIFleetMissionType.FLEET_MISSION_HIT_AND_RUN, AIFleetMissionType.FLEET_MISSION_SECURE): new_military_fleets.append(fleet_id) allocations = [] if new_military_fleets: allocations = MilitaryAI.get_military_fleets(milFleetIDs=new_military_fleets, tryReset=False, thisround="Fleet Reassignment %s" % new_military_fleets) if allocations: MilitaryAI.assign_military_fleets_to_systems(useFleetIDList=new_military_fleets, allocations=allocations)
def int_int_map_to_string(int_int_map): return str(dict_from_map(int_int_map))
def wrapper(*args): return dict_from_map(method(*args))
def evaluate_invasion_planet(planetID, missionType, fleetSupplyablePlanetIDs, empire, secureAIFleetMissions, verbose=True): """return the invasion value (score, troops) of a planet""" detail = [] buildingValues = {"BLD_IMPERIAL_PALACE": 1000, "BLD_CULTURE_ARCHIVES": 1000, "BLD_SHIPYARD_BASE": 100, "BLD_SHIPYARD_ORG_ORB_INC": 200, "BLD_SHIPYARD_ORG_XENO_FAC": 200, "BLD_SHIPYARD_ORG_CELL_GRO_CHAMB": 200, "BLD_SHIPYARD_CON_NANOROBO": 300, "BLD_SHIPYARD_CON_GEOINT": 400, "BLD_SHIPYARD_CON_ADV_ENGINE": 1000, "BLD_SHIPYARD_AST": 300, "BLD_SHIPYARD_AST_REF": 1000, "BLD_SHIPYARD_ENRG_SOLAR": 1500, "BLD_INDUSTRY_CENTER": 500, "BLD_GAS_GIANT_GEN": 200, "BLD_SOL_ORB_GEN": 800, "BLD_BLACK_HOLE_POW_GEN": 2000, "BLD_ENCLAVE_VOID": 500, "BLD_NEUTRONIUM_EXTRACTOR": 2000, "BLD_NEUTRONIUM_SYNTH": 2000, "BLD_NEUTRONIUM_FORGE": 1000, "BLD_CONC_CAMP": 100, "BLD_BIOTERROR_PROJECTOR": 1000, "BLD_SHIPYARD_ENRG_COMP": 3000, } #TODO: add more factors, as used for colonization universe = fo.getUniverse() empireID = empire.empireID maxJumps=8 planet = universe.getPlanet(planetID) if planet is None: #TODO: exclude planets with stealth higher than empireDetection print "invasion AI couldn't access any info for planet id %d"%planetID return [0, 0] sysPartialVisTurn = dict_from_map(universe.getVisibilityTurnsMap(planet.systemID, empireID)).get(fo.visibility.partial, -9999) planetPartialVisTurn = dict_from_map(universe.getVisibilityTurnsMap(planetID, empireID)).get(fo.visibility.partial, -9999) if planetPartialVisTurn < sysPartialVisTurn: print "invasion AI couldn't get current info on planet id %d (was stealthed at last sighting)"%planetID return [0, 0] #last time we had partial vis of the system, the planet was stealthed to us #TODO: track detection strength, order new scouting when it goes up specName=planet.speciesName species=fo.getSpecies(specName) if not species: #this call iterates over this Empire's available species with which it could colonize after an invasion planetEval = ColonisationAI.assign_colonisation_values([planetID], EnumsAI.AIFleetMissionType.FLEET_MISSION_INVASION, [planetID], None, empire, detail) popVal = max( 0.75*planetEval.get(planetID, [0])[0], ColonisationAI.evaluate_planet(planetID, EnumsAI.AIFleetMissionType.FLEET_MISSION_OUTPOST, [planetID], None, empire, detail) ) else: popVal = ColonisationAI.evaluate_planet(planetID, EnumsAI.AIFleetMissionType.FLEET_MISSION_INVASION, [planetID], specName, empire, detail) bldTally=0 for bldType in [universe.getObject(bldg).buildingTypeName for bldg in planet.buildingIDs]: bval = buildingValues.get(bldType, 50) bldTally += bval detail.append("%s: %d"%(bldType, bval)) pSysID = planet.systemID capitolID = PlanetUtilsAI.get_capital() leastJumpsPath = [] clear_path = True if capitolID: homeworld = universe.getPlanet(capitolID) if homeworld: homeSystemID = homeworld.systemID evalSystemID = planet.systemID if (homeSystemID != -1) and (evalSystemID != -1): leastJumpsPath = list(universe.leastJumpsPath(homeSystemID, evalSystemID, empireID)) maxJumps = len(leastJumpsPath) system_status = foAI.foAIstate.systemStatus.get(pSysID, {}) sysFThrt = system_status.get('fleetThreat', 1000 ) sysMThrt =system_status.get('monsterThreat', 0 ) sysPThrt = system_status.get('planetThreat', 0 ) sysTotThrt = sysFThrt + sysMThrt + sysPThrt max_path_threat = sysFThrt mil_ship_rating = ProductionAI.curBestMilShipRating() for path_sys_id in leastJumpsPath: path_leg_status = foAI.foAIstate.systemStatus.get(path_sys_id, {}) path_leg_threat = path_leg_status.get('fleetThreat', 1000 ) + path_leg_status.get('monsterThreat', 0 ) if path_leg_threat > 0.5 * mil_ship_rating: clear_path = False if path_leg_threat > max_path_threat: max_path_threat = path_leg_threat troops = planet.currentMeterValue(fo.meterType.troops) maxTroops = planet.currentMeterValue(fo.meterType.maxTroops) this_system = universe.getSystem(pSysID) secure_targets = [pSysID] + list(this_system.planetIDs) system_secured = False for mission in secureAIFleetMissions: if system_secured: break secure_fleet_id = mission.target_id s_fleet = universe.getFleet(secure_fleet_id) if (not s_fleet) or (s_fleet.systemID != pSysID): continue for ai_target in mission.get_targets(EnumsAI.AIFleetMissionType.FLEET_MISSION_SECURE): target_obj = ai_target.target_obj if (target_obj is not None) and target_obj.id in secure_targets: system_secured = True break pmaxShield = planet.currentMeterValue(fo.meterType.maxShield) if verbose: print "invasion eval of %s %d --- maxShields %.1f -- sysFleetThreat %.1f -- sysMonsterThreat %.1f"%(planet.name, planetID, pmaxShield, sysFThrt, sysMThrt) supplyVal=0 enemyVal=0 if planet.owner!=-1 : #value in taking this away from an enemy enemyVal= 20* (planet.currentMeterValue(fo.meterType.targetIndustry) + 2*planet.currentMeterValue(fo.meterType.targetResearch)) if pSysID in ColonisationAI.annexableSystemIDs: #TODO: extend to rings supplyVal = 100 elif pSysID in ColonisationAI.annexableRing1: supplyVal = 200 elif pSysID in ColonisationAI.annexableRing2: supplyVal = 300 elif pSysID in ColonisationAI.annexableRing3: supplyVal = 400 if max_path_threat > 0.5 * mil_ship_rating: if max_path_threat < 3 * mil_ship_rating: supplyVal *= 0.5 else: supplyVal *= 0.2 threatFactor = min(1, 0.2*MilitaryAI.totMilRating/(sysTotThrt+0.001))**2 #devalue invasions that would require too much military force buildTime=4 if system_secured: plannedTroops = troops else: plannedTroops = min(troops+maxJumps+buildTime, maxTroops) if not tech_is_complete("SHP_ORG_HULL"): troopCost = math.ceil(plannedTroops/6.0) * (40*(1+foAI.foAIstate.shipCount * AIDependencies.SHIP_UPKEEP)) else: troopCost = math.ceil(plannedTroops/6.0) * (20*(1+foAI.foAIstate.shipCount * AIDependencies.SHIP_UPKEEP)) planet_score = retaliation_risk_factor(planet.owner) * threatFactor * max(0, popVal+supplyVal+bldTally+enemyVal-0.8*troopCost) if clear_path: planet_score *= 1.5 invscore = [ planet_score, plannedTroops ] print invscore, "projected Troop Cost:", troopCost, ", threatFactor: ", threatFactor, ", planet detail ", detail, "popval, supplyval, bldval, enemyval", popVal, supplyVal, bldTally, enemyVal return invscore
def follow_vis_system_connections(start_system_id, home_system_id): universe = fo.getUniverse() empire_id = foAI.foAIstate.empireID exploration_list = [start_system_id] while exploration_list: cur_system_id = exploration_list.pop() if cur_system_id in graphFlags: continue graphFlags[cur_system_id] = 1 system = universe.getSystem(cur_system_id) if not system: sys_name = foAI.foAIstate.systemStatus.get(cur_system_id, {}).get( 'name', "name unknown") else: sys_name = system.name or foAI.foAIstate.systemStatus.get( cur_system_id, {}).get('name', "name unknown") if cur_system_id in foAI.foAIstate.visBorderSystemIDs: pre_vis = "a border system" elif cur_system_id in foAI.foAIstate.visInteriorSystemIDs: pre_vis = "an interior system" else: pre_vis = "an unknown system" if fo.currentTurn() < 50: visibility_turn_list = sorted(universe.getVisibilityTurnsMap( cur_system_id, empire_id).items(), key=lambda x: x[0].numerator) visibility_info = [ '%s: %s' % (vis.name, turn) for vis, turn in visibility_turn_list ] print "*** system ID %d ( %s ) ; previously %s, new visibility turns info: %s " % ( cur_system_id, sys_name, pre_vis, visibility_info) status_str = "*** system ID %d ( %s ) ; " % (cur_system_id, sys_name) has_been_visible = universe.getVisibilityTurnsMap( cur_system_id, empire_id).get(fo.visibility.partial, 0) > 0 is_connected = universe.systemsConnected(cur_system_id, home_system_id, -1) # self.empire_id) status_str += " -- is %s partially visible " % (["not", "" ][has_been_visible]) status_str += " -- is %s visibly connected to homesystem " % ( ["not", ""][is_connected]) if has_been_visible: sys_status = foAI.foAIstate.systemStatus.setdefault( cur_system_id, {}) foAI.foAIstate.visInteriorSystemIDs[cur_system_id] = 1 if cur_system_id in foAI.foAIstate.visBorderSystemIDs: del foAI.foAIstate.visBorderSystemIDs[cur_system_id] #neighbors= dict( [(el.key(), el.data()) for el in universe.getSystemNeighborsMap(cur_system_id, empire_id)] ) # neighbors = set( dict_from_map( universe.getSystemNeighborsMap(cur_system_id, empire_id)).keys()) sys_status.setdefault('neighbors', set()).update(neighbors) sys_planets = sys_status.setdefault('planets', {}) if fo.currentTurn() < 50: print " previously knew of system %d planets %s" % ( cur_system_id, sys_planets.keys()) if system: for planet_id in system.planetIDs: sys_planets.setdefault(planet_id, {}).setdefault(TARGET_POP, 0) sys_planets[planet_id].setdefault(TROOPS, 0) planet = universe.getPlanet(planet_id) if planet: new_pop = planet.currentMeterValue( fo.meterType.targetPopulation) if new_pop != sys_planets[planet_id][TARGET_POP]: if fo.currentTurn() < 50: print " * updating targetPop of planet %d ( %s ) to %.2f from %.2f" % ( planet_id, planet.name, new_pop, sys_planets[planet_id][TARGET_POP]) troops = planet.currentMeterValue(fo.meterType.troops) if troops != sys_planets[planet_id].get(TROOPS, 0): if fo.currentTurn() < 50: print " * updating troops of planet %d ( %s ) to %.2f from %.2f" % ( planet_id, planet.name, troops, sys_planets[planet_id][TROOPS]) sys_planets[planet_id][TARGET_POP] = new_pop sys_planets[planet_id][TROOPS] = troops if fo.currentTurn() < 50: print " now know of system %d planets %s" % ( cur_system_id, sys_planets.keys()) #neighbors = list( universe.getImmediateNeighbors(cur_system_id, empire_id) ) #imNeighbors #if set(neighbors) != set(neighbors2): # print "Error with neighbors: imn giving %s ; giN giving %s"%(neighbors2, neighbors) if neighbors: status_str += " -- has neighbors %s " % neighbors for sys_id in neighbors: if sys_id not in foAI.foAIstate.exploredSystemIDs: foAI.foAIstate.unexploredSystemIDs[sys_id] = 1 if (sys_id not in graphFlags) and ( sys_id not in foAI.foAIstate.visInteriorSystemIDs): foAI.foAIstate.visBorderSystemIDs[sys_id] = 1 exploration_list.append(sys_id) if fo.currentTurn() < 50: print status_str print "----------------------------------------------------------"
def follow_vis_system_connections(start_system_id, home_system_id): universe = fo.getUniverse() empire_id = foAI.foAIstate.empireID exploration_list = [start_system_id] while exploration_list: cur_system_id = exploration_list.pop() if cur_system_id in graphFlags: continue graphFlags[cur_system_id] = 1 system = universe.getSystem(cur_system_id) if cur_system_id in foAI.foAIstate.visBorderSystemIDs: pre_vis = "a border system" elif cur_system_id in foAI.foAIstate.visInteriorSystemIDs: pre_vis = "an interior system" else: pre_vis = "an unknown system" system_header = "*** system %s;" % system if fo.currentTurn() < 50: visibility_turn_list = sorted(universe.getVisibilityTurnsMap( cur_system_id, empire_id).items(), key=lambda x: x[0].numerator) visibility_info = ', '.join('%s: %s' % (vis.name, turn) for vis, turn in visibility_turn_list) print "%s previously %s, new visibility turns info: %s " % ( system_header, pre_vis, visibility_info) status_info = [] else: status_info = [system_header] has_been_visible = universe.getVisibilityTurnsMap( cur_system_id, empire_id).get(fo.visibility.partial, 0) > 0 is_connected = universe.systemsConnected(cur_system_id, home_system_id, -1) # self.empire_id) status_info.append(" -- is%s partially visible" % ([" not", ""][has_been_visible])) status_info.append(" -- is%s visibly connected to homesystem" % ([" not", ""][is_connected])) if has_been_visible: sys_status = foAI.foAIstate.systemStatus.setdefault( cur_system_id, {}) foAI.foAIstate.visInteriorSystemIDs[cur_system_id] = 1 if cur_system_id in foAI.foAIstate.visBorderSystemIDs: del foAI.foAIstate.visBorderSystemIDs[cur_system_id] neighbors = set( dict_from_map( universe.getSystemNeighborsMap(cur_system_id, empire_id)).keys()) sys_status.setdefault('neighbors', set()).update(neighbors) sys_planets = sys_status.setdefault('planets', {}) if fo.currentTurn() < 50: print " previously knew planets: %s" % sys_planets.keys() if system: for planet_id in system.planetIDs: sys_planets.setdefault(planet_id, {}).setdefault(TARGET_POP, 0) sys_planets[planet_id].setdefault(TROOPS, 0) planet = universe.getPlanet(planet_id) if planet: new_pop = planet.currentMeterValue( fo.meterType.targetPopulation) if new_pop != sys_planets[planet_id][TARGET_POP]: if fo.currentTurn() < 50: print " * updating targetPop of planet %s to %.2f from %.2f" % ( planet, new_pop, sys_planets[planet_id][TARGET_POP]) troops = planet.currentMeterValue(fo.meterType.troops) if troops != sys_planets[planet_id].get(TROOPS, 0): if fo.currentTurn() < 50: print " * updating troops of planet %s to %.2f from %.2f" % ( planet, troops, sys_planets[planet_id][TROOPS]) sys_planets[planet_id][TARGET_POP] = new_pop sys_planets[planet_id][TROOPS] = troops if fo.currentTurn() < 50: print " known planets %s" % sys_planets.keys() if neighbors: status_info.append(" -- has neighbors %s" % sorted(neighbors)) for sys_id in neighbors: if sys_id not in foAI.foAIstate.exploredSystemIDs: foAI.foAIstate.unexploredSystemIDs[sys_id] = 1 if (sys_id not in graphFlags) and ( sys_id not in foAI.foAIstate.visInteriorSystemIDs): foAI.foAIstate.visBorderSystemIDs[sys_id] = 1 exploration_list.append(sys_id) if fo.currentTurn() < 50: print '\n'.join(status_info) print "----------------------------------------------------------"
def get_invasion_fleets(): """get invasion fleets""" invasion_timer.start("gathering initial info") all_invasion_fleet_ids = FleetUtilsAI.get_empire_fleet_ids_by_role(EnumsAI.AIFleetMissionType.FLEET_MISSION_INVASION) AIstate.invasionFleetIDs = FleetUtilsAI.extract_fleet_ids_without_mission_types(all_invasion_fleet_ids) # get supplyable planets universe = fo.getUniverse() empire = fo.getEmpire() empire_id = empire.empireID capital_id = PlanetUtilsAI.get_capital() homeworld=None if capital_id: homeworld = universe.getPlanet(capital_id) if homeworld: home_system_id = homeworld.systemID else: home_system_id = -1 fleet_supplyable_system_ids = empire.fleetSupplyableSystemIDs fleet_supplyable_planet_ids = PlanetUtilsAI.get_planets_in__systems_ids(fleet_supplyable_system_ids) prime_invadable_system_ids = set(ColonisationAI.annexableSystemIDs) prime_invadable_planet_ids = PlanetUtilsAI.get_planets_in__systems_ids(prime_invadable_system_ids) visible_system_ids = foAI.foAIstate.visInteriorSystemIDs.keys() + foAI.foAIstate. visBorderSystemIDs.keys() if home_system_id != -1: accessible_system_ids = [sys_id for sys_id in visible_system_ids if (sys_id != -1 ) and universe.systemsConnected(sys_id, home_system_id, empire_id) ] else: print "Invasion Warning: this empire has no identifiable homeworld, will therefor treat all visible planets as accessible." accessible_system_ids = visible_system_ids #TODO: check if any troop ships still owned, use their system as home system acessible_planet_ids = PlanetUtilsAI.get_planets_in__systems_ids(accessible_system_ids) print "Accessible Systems: ", ", ".join(PlanetUtilsAI.sys_name_ids(accessible_system_ids)) print #all_owned_planet_ids = PlanetUtilsAI.get_all_owned_planet_ids(exploredPlanetIDs) all_owned_planet_ids = PlanetUtilsAI.get_all_owned_planet_ids(acessible_planet_ids)#need these for unpopulated outposts # print "All Owned and Populated PlanetIDs: " + str(all_owned_planet_ids) all_populated_planets = PlanetUtilsAI.get_populated_planet_ids(acessible_planet_ids)#need this for natives print "All Visible and accessible Populated PlanetIDs (including this empire's): ", ", ".join(PlanetUtilsAI.planet_name_ids(all_populated_planets )) print print "Prime Invadable Target Systems: ", ", ".join(PlanetUtilsAI.sys_name_ids(prime_invadable_system_ids)) print empire_owned_planet_ids = PlanetUtilsAI.get_owned_planets_by_empire(universe.planetIDs) # print "Empire Owned PlanetIDs: " + str(empire_owned_planet_ids) invadable_planet_ids = set(prime_invadable_planet_ids).intersection(set(all_owned_planet_ids).union(all_populated_planets ) - set(empire_owned_planet_ids)) print "Prime Invadable PlanetIDs: ", ", ".join(PlanetUtilsAI.planet_name_ids(invadable_planet_ids)) print print "Current Invasion Targeted SystemIDs: ", ", ".join(PlanetUtilsAI.sys_name_ids(AIstate.invasionTargetedSystemIDs)) invasion_targeted_planet_ids = get_invasion_targeted_planet_ids(universe.planetIDs, EnumsAI.AIFleetMissionType.FLEET_MISSION_INVASION, empire_id) invasion_targeted_planet_ids.extend( get_invasion_targeted_planet_ids(universe.planetIDs, EnumsAI.AIFleetMissionType.FLEET_MISSION_ORBITAL_INVASION, empire_id)) all_invasion_targeted_system_ids = set(PlanetUtilsAI.get_systems(invasion_targeted_planet_ids)) print "Current Invasion Targeted PlanetIDs: ", ", ".join(PlanetUtilsAI.planet_name_ids(invasion_targeted_planet_ids)) invasion_fleet_ids = FleetUtilsAI.get_empire_fleet_ids_by_role(EnumsAI.AIFleetMissionType.FLEET_MISSION_INVASION) if not invasion_fleet_ids: print "Available Invasion Fleets: 0" else: print "Invasion FleetIDs: " + str(FleetUtilsAI.get_empire_fleet_ids_by_role(EnumsAI.AIFleetMissionType.FLEET_MISSION_INVASION)) num_invasion_fleets = len(FleetUtilsAI.extract_fleet_ids_without_mission_types(invasion_fleet_ids)) print "Invasion Fleets Without Missions: " + str(num_invasion_fleets) invasion_timer.start("planning troop base production") # only do base invasions if aggression is typical or above reserved_troop_base_targets = [] if foAI.foAIstate.aggression > fo.aggression.typical: available_pp = {} for el in empire.planetsWithAvailablePP: #keys are sets of ints; data is doubles avail_pp = el.data() for pid in el.key(): available_pp[pid] = avail_pp if len (invadable_planet_ids) > 0: #print "Evaluating Troop Bases (SpaceInvaders) for %s"%(invadable_planet_ids) pass for pid in invadable_planet_ids: #TODO: reorganize planet = universe.getPlanet(pid) if not planet: continue sys_id = planet.systemID sys_partial_vis_turn = dict_from_map(universe.getVisibilityTurnsMap(planet.systemID, empire_id)).get(fo.visibility.partial, -9999) planet_partial_vis_turn = dict_from_map(universe.getVisibilityTurnsMap(pid, empire_id)).get(fo.visibility.partial, -9999) if planet_partial_vis_turn < sys_partial_vis_turn: #print "rejecting %s due to stealth"%planet.name continue for pid2 in ColonisationAI.empireSpeciesSystems.get(sys_id, {}).get('pids', []): if available_pp.get(pid2, 0) < 2: #TODO: improve troop base PP sufficiency determination #print "rejecting %s due to insufficient avail PP"%planet.name break planet2 = universe.getPlanet(pid2) if not planet2: continue if (pid not in foAI.foAIstate.qualifyingTroopBaseTargets) and (planet2.speciesName in ColonisationAI.empireShipBuilders): #print "Adding %s to Troop Bases (SpaceInvaders) potential target list, from %s"%(planet.name, planet2.name) foAI.foAIstate.qualifyingTroopBaseTargets.setdefault(pid, [pid2, -1]) break for pid in list(foAI.foAIstate.qualifyingTroopBaseTargets): planet = universe.getPlanet(pid) #TODO: also check that still have a colony in this system that can make troops if planet and planet.owner == empire_id: del foAI.foAIstate.qualifyingTroopBaseTargets[pid] secureAIFleetMissions = foAI.foAIstate.get_fleet_missions_with_any_mission_types([EnumsAI.AIFleetMissionType.FLEET_MISSION_SECURE]) #print "considering possible troop bases at %s" % (foAI.foAIstate.qualifyingTroopBaseTargets.keys()) for pid in (set(foAI.foAIstate.qualifyingTroopBaseTargets.keys()) - set(invasion_targeted_planet_ids)): #TODO: consider overriding standard invasion mission planet = universe.getPlanet(pid) if foAI.foAIstate.qualifyingTroopBaseTargets[pid][1] != -1: reserved_troop_base_targets.append(pid) if planet: all_invasion_targeted_system_ids.add( planet.systemID ) continue #already building for here sys_id = planet.systemID this_sys_status = foAI.foAIstate.systemStatus.get( sys_id, {} ) if ((planet.currentMeterValue(fo.meterType.shield) > 0) and (this_sys_status.get('myFleetRating', 0) < (0.8 * this_sys_status.get('totalThreat', 0)))): continue loc = foAI.foAIstate.qualifyingTroopBaseTargets[pid][0] this_score, p_troops = evaluate_invasion_planet(pid, EnumsAI.AIFleetMissionType.FLEET_MISSION_INVASION, fleet_supplyable_planet_ids, empire, secureAIFleetMissions, False) bestShip, colDesign, buildChoices = ProductionAI.getBestShipInfo(EnumsAI.AIPriorityType.PRIORITY_PRODUCTION_ORBITAL_INVASION, loc) if not bestShip: #print "Error: no troop base can be built at ", PlanetUtilsAI.planet_name_ids([loc]) continue #print "selecting ", PlanetUtilsAI.planet_name_ids([loc]), " to build Orbital troop bases" n_bases = math.ceil((p_troops+1) / 2)#TODO: reconsider this +1 safety factor retval = fo.issueEnqueueShipProductionOrder(bestShip, loc) print "Enqueueing %d Troop Bases at %s for %s"%( n_bases, PlanetUtilsAI.planet_name_ids([loc]), PlanetUtilsAI.planet_name_ids([pid])) if retval !=0: all_invasion_targeted_system_ids.add( planet.systemID ) reserved_troop_base_targets.append(pid) foAI.foAIstate.qualifyingTroopBaseTargets[pid][1] = loc fo.issueChangeProductionQuantityOrder(empire.productionQueue.size -1, 1, int(n_bases)) res=fo.issueRequeueProductionOrder(empire.productionQueue.size -1, 0) invasion_timer.start("evaluating target planets") #TODO: check if any invasion_targeted_planet_ids need more troops assigned evaluatedPlanetIDs = list(set(invadable_planet_ids) - set(invasion_targeted_planet_ids) - set(reserved_troop_base_targets) ) print "Evaluating potential invasions, PlanetIDs: " + str(evaluatedPlanetIDs) evaluatedPlanets = assign_invasion_values(evaluatedPlanetIDs, EnumsAI.AIFleetMissionType.FLEET_MISSION_INVASION, fleet_supplyable_planet_ids, empire) sortedPlanets = [(pid, pscore, ptroops) for (pid, (pscore, ptroops)) in evaluatedPlanets.items() ] sortedPlanets.sort(lambda x, y: cmp(x[1], y[1]), reverse=True) sortedPlanets = [(pid, pscore%10000, ptroops) for (pid, pscore, ptroops) in sortedPlanets ] print if sortedPlanets: print "Invadable planets\nIDs, ID | Score | Name | Race | Troops" for pid, pscore, ptroops in sortedPlanets: planet = universe.getPlanet(pid) if planet: print "%6d | %6d | %16s | %16s | %d"%(pid, pscore, planet.name, planet.speciesName, ptroops) else: print "%6d | %6d | Error: invalid planet ID"%(pid, pscore) else: print "No Invadable planets identified" sortedPlanets = [(pid, pscore, ptroops) for (pid, pscore, ptroops) in sortedPlanets if pscore > 0] # export opponent planets for other AI modules AIstate.opponentPlanetIDs = [pid for pid, pscore, trp in sortedPlanets] AIstate.invasionTargets = sortedPlanets # export invasion targeted systems for other AI modules AIstate.invasionTargetedSystemIDs = list(all_invasion_targeted_system_ids) invasion_timer.stop(section_name="evaluating %d target planets" % (len(evaluatedPlanetIDs))) invasion_timer.end()
def follow_vis_system_connections(start_system_id, home_system_id): universe = fo.getUniverse() empire_id = foAI.foAIstate.empireID exploration_list = [start_system_id] while exploration_list: cur_system_id = exploration_list.pop() if cur_system_id in graphFlags: continue graphFlags[cur_system_id] = 1 system = universe.getSystem(cur_system_id) if cur_system_id in foAI.foAIstate.visBorderSystemIDs: pre_vis = "a border system" elif cur_system_id in foAI.foAIstate.visInteriorSystemIDs: pre_vis = "an interior system" else: pre_vis = "an unknown system" system_header = "*** system %s;" % system if fo.currentTurn() < 50: visibility_turn_list = sorted(universe.getVisibilityTurnsMap(cur_system_id, empire_id).items(), key=lambda x: x[0].numerator) visibility_info = ', '.join('%s: %s' % (vis.name, turn) for vis, turn in visibility_turn_list) print "%s previously %s. Visibility per turn: %s " % (system_header, pre_vis, visibility_info) status_info = [] else: status_info = [system_header] has_been_visible = universe.getVisibilityTurnsMap(cur_system_id, empire_id).get(fo.visibility.partial, 0) > 0 is_connected = universe.systemsConnected(cur_system_id, home_system_id, -1) # self.empire_id) status_info.append(" -- is%s partially visible" % ([" not", ""][has_been_visible])) status_info.append(" -- is%s visibly connected to homesystem" % ([" not", ""][is_connected])) if has_been_visible: sys_status = foAI.foAIstate.systemStatus.setdefault(cur_system_id, {}) foAI.foAIstate.visInteriorSystemIDs[cur_system_id] = 1 if cur_system_id in foAI.foAIstate.visBorderSystemIDs: del foAI.foAIstate.visBorderSystemIDs[cur_system_id] neighbors = set(dict_from_map(universe.getSystemNeighborsMap(cur_system_id, empire_id)).keys()) sys_status.setdefault('neighbors', set()).update(neighbors) sys_planets = sys_status.setdefault('planets', {}) if fo.currentTurn() < 50: print " previously known planets: %s" % sys_planets.keys() if system: for planet_id in system.planetIDs: sys_planets.setdefault(planet_id, {}).setdefault(TARGET_POP, 0) sys_planets[planet_id].setdefault(TROOPS, 0) planet = universe.getPlanet(planet_id) if planet: new_pop = planet.currentMeterValue(fo.meterType.targetPopulation) if new_pop != sys_planets[planet_id][TARGET_POP]: if fo.currentTurn() < 50: print " * updating targetPop of planet %s to %.2f from %.2f" % (planet, new_pop, sys_planets[planet_id][TARGET_POP]) troops = planet.currentMeterValue(fo.meterType.troops) if troops != sys_planets[planet_id].get(TROOPS, 0): if fo.currentTurn() < 50: print " * updating troops of planet %s to %.2f from %.2f" % (planet, troops, sys_planets[planet_id][ TROOPS]) sys_planets[planet_id][TARGET_POP] = new_pop sys_planets[planet_id][TROOPS] = troops if fo.currentTurn() < 50: print " known planets %s" % sys_planets.keys() if neighbors: status_info.append(" -- has neighbors %s" % sorted(neighbors)) for sys_id in neighbors: if sys_id not in foAI.foAIstate.exploredSystemIDs: foAI.foAIstate.unexploredSystemIDs[sys_id] = 1 if (sys_id not in graphFlags) and (sys_id not in foAI.foAIstate.visInteriorSystemIDs): foAI.foAIstate.visBorderSystemIDs[sys_id] = 1 exploration_list.append(sys_id) if fo.currentTurn() < 50: print '\n'.join(status_info) print "----------------------------------------------------------"
def is_valid(self, verbose = True): """Check if FleetOrder could be somehow in future issued = is valid.""" if self.executed and self.execution_completed: if verbose: print "\t\t order not valid because already executed and completed" return False if self.fleet.valid and self.target.valid: universe = fo.getUniverse() target_type = self.target.target_type fleet_id = self.fleet.target_id # outpost if AIFleetOrderType.ORDER_OUTPOST == self.order_type: # colonise planet if TargetType.TARGET_PLANET == target_type: planet = universe.getPlanet(self.target.target_id) sys_partial_vis_turn = dict_from_map(universe.getVisibilityTurnsMap(planet.systemID, fo.empireID())).get(fo.visibility.partial, -9999) planet_partial_vis_turn = dict_from_map(universe.getVisibilityTurnsMap(planet.id, fo.empireID())).get(fo.visibility.partial, -9999) if not (planet_partial_vis_turn == sys_partial_vis_turn and planet.unowned): self.executed = True self.execution_completed = True return False else: fleet = universe.getFleet(fleet_id) return fleet.hasOutpostShips else: return False # colonise elif AIFleetOrderType.ORDER_COLONISE == self.order_type: # colonise planet if TargetType.TARGET_PLANET == target_type: planet = universe.getPlanet(self.target.target_id) sys_partial_vis_turn = dict_from_map(universe.getVisibilityTurnsMap(planet.systemID, fo.empireID())).get(fo.visibility.partial, -9999) planet_partial_vis_turn = dict_from_map(universe.getVisibilityTurnsMap(planet.id, fo.empireID())).get(fo.visibility.partial, -9999) if not (planet_partial_vis_turn == sys_partial_vis_turn and planet.unowned or (planet.ownedBy(fo.empireID()) and not planet.currentMeterValue(fo.meterType.population))): self.executed = True self.execution_completed = True return False else: fleet = universe.getFleet(fleet_id) return fleet.hasColonyShips else: return False # invade elif AIFleetOrderType.ORDER_INVADE == self.order_type: # invade planet if TargetType.TARGET_PLANET == target_type: planet = universe.getPlanet(self.target.target_id) planet_population = planet.currentMeterValue(fo.meterType.population) if planet.unowned and not planet_population: print "\t\t invasion order not valid due to target planet status-- owned: %s and population %.1f" % (not planet.unowned, planet_population) self.executed = True self.execution_completed = True return False else: fleet = universe.getFleet(fleet_id) return fleet.hasTroopShips else: return False # military elif AIFleetOrderType.ORDER_MILITARY == self.order_type: fleet = universe.getFleet(fleet_id) return fleet.hasArmedShips and TargetType.TARGET_SYSTEM == target_type # move to system elif AIFleetOrderType.ORDER_MOVE == self.order_type: return TargetType.TARGET_SYSTEM == target_type # resupply elif AIFleetOrderType.ORDER_RESUPPLY == self.order_type: # move to system if TargetType.TARGET_SYSTEM == target_type: empire = fo.getEmpire() return self.target.target_id in empire.fleetSupplyableSystemIDs else: return False # repair elif AIFleetOrderType.ORDER_REPAIR == self.order_type: # move to system if TargetType.TARGET_SYSTEM == target_type: empire = fo.getEmpire() return self.target.target_id in empire.fleetSupplyableSystemIDs # TODO: check for drydock still there/owned else: return False # split fleet elif AIFleetOrderType.ORDER_SPLIT_FLEET == self.order_type: return TargetType.TARGET_SHIP == target_type and self.ship_in_fleet() elif AIFleetOrderType.ORDER_ATTACK == self.order_type: return target_type in (TargetType.TARGET_SYSTEM, TargetType.TARGET_PLANET) elif AIFleetOrderType.ORDER_DEFEND == self.order_type: return target_type in (TargetType.TARGET_SYSTEM, TargetType.TARGET_PLANET) else: if verbose: print "\t\t order not valid: fleet validity: %s and target validity %s" % (self.fleet.valid, self.target.valid) return False
def __update_system_status(self): print 10 * "=", "Updating System Threats", 10 * "=" universe = fo.getUniverse() empire = fo.getEmpire() empire_id = fo.empireID() destroyed_object_ids = universe.destroyedObjectIDs(empire_id) supply_unobstructed_systems = set(empire.supplyUnobstructedSystems) min_hidden_attack = 4 min_hidden_health = 8 # TODO: Variables that are recalculated each turn from scratch should not be stored in AIstate # clear previous game state for sys_id in self.systemStatus: self.systemStatus[sys_id]['enemy_ship_count'] = 0 self.systemStatus[sys_id]['myFleetRating'] = 0 self.systemStatus[sys_id]['myFleetRatingVsPlanets'] = 0 # for use in debugging verbose = False # assess enemy fleets that may have been momentarily visible enemies_by_system = {} my_fleets_by_system = {} fleet_spot_position = {} current_turn = fo.currentTurn() for fleet_id in universe.fleetIDs: fleet = universe.getFleet(fleet_id) if not fleet or fleet.empty: self.delete_fleet_info(fleet_id) # this is safe even if fleet wasn't mine continue # TODO: check if currently in system and blockaded before accepting destination as location this_system_id = fleet.nextSystemID if fleet.nextSystemID != INVALID_ID else fleet.systemID dead_fleet = fleet_id in destroyed_object_ids if dead_fleet: self.delete_fleet_info(fleet_id) if fleet.ownedBy(empire_id): if not dead_fleet: my_fleets_by_system.setdefault(this_system_id, []).append(fleet_id) fleet_spot_position.setdefault(fleet.systemID, []).append(fleet_id) continue # TODO: consider checking death of individual ships. If ships had been moved from this fleet # into another fleet, we might have witnessed their death in that other fleet but if this fleet # had not been seen since before that transfer then the ships might also still be listed here. if dead_fleet: continue # we are only interested in immediately recent data if get_partial_visibility_turn(fleet_id) < (current_turn - 1): continue sys_status = self.systemStatus.setdefault(this_system_id, {}) sys_status['enemy_ship_count'] = sys_status.get('enemy_ship_count', 0) + len(fleet.shipIDs) enemies_by_system.setdefault(this_system_id, []).append(fleet_id) if not fleet.unowned: self.misc.setdefault('enemies_sighted', {}).setdefault(current_turn, []).append(fleet_id) # assess fleet and planet threats & my local fleets for sys_id in universe.systemIDs: sys_status = self.systemStatus.setdefault(sys_id, {}) system = universe.getSystem(sys_id) if verbose: print "AIState threat evaluation for %s" % system # update fleets sys_status['myfleets'] = my_fleets_by_system.get(sys_id, []) sys_status['myFleetsAccessible'] = fleet_spot_position.get(sys_id, []) local_enemy_fleet_ids = enemies_by_system.get(sys_id, []) sys_status['localEnemyFleetIDs'] = local_enemy_fleet_ids if system: sys_status['name'] = system.name # update threats monster_ratings = [] # immobile enemy_ratings = [] # owned & mobile mob_ratings = [] # mobile & unowned mobile_fleets = [] # mobile and either owned or unowned for fid in local_enemy_fleet_ids: fleet = universe.getFleet(fid) # ensured to exist fleet_rating = CombatRatingsAI.get_fleet_rating( fid, enemy_stats=CombatRatingsAI.get_empire_standard_fighter()) if fleet.speed == 0: monster_ratings.append(fleet_rating) if verbose: print "\t immobile enemy fleet %s has rating %.1f" % (fleet, fleet_rating) continue if verbose: print "\t mobile enemy fleet %s has rating %.1f" % (fleet, fleet_rating) mobile_fleets.append(fid) if fleet.unowned: mob_ratings.append(fleet_rating) else: enemy_ratings.append(fleet_rating) enemy_rating = CombatRatingsAI.combine_ratings_list(enemy_ratings) monster_rating = CombatRatingsAI.combine_ratings_list(monster_ratings) mob_rating = CombatRatingsAI.combine_ratings_list(mob_ratings) lost_fleets = fleetsLostBySystem.get(sys_id, []) lost_fleet_rating = CombatRatingsAI.combine_ratings_list(lost_fleets) # under current visibility rules should not be possible to have any losses or other info here, # but just in case... partial_vis_turn = get_partial_visibility_turn(sys_id) if not system or partial_vis_turn < 0: if verbose: print "Never had partial vis for %s - basing threat assessment on old info and lost ships" % system sys_status.setdefault('local_fleet_threats', set()) sys_status['planetThreat'] = 0 sys_status['fleetThreat'] = max( CombatRatingsAI.combine_ratings(enemy_rating, mob_rating), 0.98 * sys_status.get('fleetThreat', 0), 1.1*lost_fleet_rating - monster_rating) sys_status['monsterThreat'] = max( monster_rating, 0.98 * sys_status.get('monsterThreat', 0), 1.1*lost_fleet_rating - enemy_rating - mob_rating) sys_status['enemy_threat'] = max( enemy_rating, 0.98 * sys_status.get('enemy_threat', 0), 1.1*lost_fleet_rating - monster_rating - mob_rating) sys_status['mydefenses'] = {'overall': 0, 'attack': 0, 'health': 0} sys_status['totalThreat'] = sys_status['fleetThreat'] sys_status['regional_fleet_threats'] = sys_status['local_fleet_threats'].copy() continue # have either stale or current info pattack = phealth = 0 mypattack = myphealth = 0 for pid in system.planetIDs: planet = universe.getPlanet(pid) if not planet: continue prating = self.assess_planet_threat(pid, sighting_age=current_turn - partial_vis_turn) if planet.ownedBy(empire_id): # TODO: check for diplomatic status mypattack += prating['attack'] myphealth += prating['health'] else: pattack += prating['attack'] phealth += prating['health'] if any("_NEST_" in special for special in planet.specials): sys_status['nest_threat'] = 100 sys_status['planetThreat'] = pattack * phealth sys_status['mydefenses'] = {'overall': mypattack * myphealth, 'attack': mypattack, 'health': myphealth} # previous threat assessment could account for losses, ignore the losses now if max(sys_status.get('totalThreat', 0), pattack * phealth) >= 0.6 * lost_fleet_rating: lost_fleet_rating = 0 # TODO use sitrep combat info rather than estimating stealthed enemies by fleets lost to them # TODO also only consider past stealthed fleet threat to still be present if the system is still obstructed # TODO: track visibility across turns in order to distinguish the blip of visibility in (losing) combat, # which FO currently treats as being for the previous turn, # partially superseding the previous visibility for that turn if not partial_vis_turn == current_turn: sys_status.setdefault('local_fleet_threats', set()) sys_status['currently_visible'] = False # print ("Stale visibility for system %d ( %s ) -- last seen %d, " # "current Turn %d -- basing threat assessment on old info and lost ships") % ( # sys_id, sys_status.get('name', "name unknown"), partial_vis_turn, currentTurn) sys_status['fleetThreat'] = max( CombatRatingsAI.combine_ratings(enemy_rating, mob_rating), 0.98 * sys_status.get('fleetThreat', 0), 2.0 * lost_fleet_rating - max(sys_status.get('monsterThreat', 0), monster_rating)) sys_status['enemy_threat'] = max( enemy_rating, 0.98 * sys_status.get('enemy_threat', 0), 1.1*lost_fleet_rating - max(sys_status.get('monsterThreat', 0), monster_rating)) sys_status['monsterThreat'] = max(monster_rating, 0.98 * sys_status.get('monsterThreat', 0)) # sys_status['totalThreat'] = ((pattack + enemy_attack + monster_attack) ** 0.8)\ # * ((phealth + enemy_health + monster_health)** 0.6) # reevaluate this sys_status['totalThreat'] = max( CombatRatingsAI.combine_ratings_list([enemy_rating, mob_rating, monster_rating, pattack * phealth]), 2 * lost_fleet_rating, 0.98 * sys_status.get('totalThreat', 0)) else: # system considered visible sys_status['currently_visible'] = True sys_status['local_fleet_threats'] = set(mobile_fleets) # includes mobile monsters sys_status['fleetThreat'] = max( CombatRatingsAI.combine_ratings(enemy_rating, mob_rating), 2*lost_fleet_rating - monster_rating) if verbose: print "enemy threat calc parts: enemy rating %.1f, lost fleet rating %.1f, monster_rating %.1f" % ( enemy_rating, lost_fleet_rating, monster_rating) # does NOT include mobile monsters sys_status['enemy_threat'] = max(enemy_rating, 2*lost_fleet_rating - monster_rating) sys_status['monsterThreat'] = monster_rating sys_status['totalThreat'] = CombatRatingsAI.combine_ratings_list( [enemy_rating, mob_rating, monster_rating, pattack * phealth]) sys_status['regional_fleet_threats'] = sys_status['local_fleet_threats'].copy() sys_status['fleetThreat'] = max(sys_status['fleetThreat'], sys_status.get('nest_threat', 0)) sys_status['totalThreat'] = max(sys_status['totalThreat'], sys_status.get('nest_threat', 0)) # has been seen with Partial Vis, but is currently supply-blocked if partial_vis_turn > 0 and sys_id not in supply_unobstructed_systems: sys_status['fleetThreat'] = max(sys_status['fleetThreat'], min_hidden_attack * min_hidden_health) sys_status['totalThreat'] = max( sys_status['totalThreat'], ((pattack + min_hidden_attack) ** 0.8) * ((phealth + min_hidden_health) ** 0.6)) if verbose and sys_status['fleetThreat'] > 0: print "%s intermediate status: %s" % (system, sys_status) enemy_supply, enemy_near_supply = self.assess_enemy_supply() # TODO: assess change in enemy supply over time # assess secondary threats (threats of surrounding systems) and update my fleet rating for sys_id in universe.systemIDs: sys_status = self.systemStatus[sys_id] 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()) 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'] = 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))) for sys_id in universe.systemIDs: sys_status = self.systemStatus[sys_id] neighbors = sys_status.get('neighbors', set()) this_system = universe.getSystem(sys_id) if verbose: print "Regional Assessment for %s with local fleet threat %.1f" % ( this_system, sys_status.get('fleetThreat', 0)) jumps2 = set() jumps3 = set() jumps4 = set() for seta, setb in [(neighbors, jumps2), (jumps2, jumps3), (jumps3, jumps4)]: for sys2id in seta: setb.update(self.systemStatus.get(sys2id, {}).get('neighbors', set())) jump2ring = jumps2 - neighbors - {sys_id} jump3ring = jumps3 - jumps2 - neighbors - {sys_id} jump4ring = jumps4 - jumps3 - jumps2 - neighbors - {sys_id} sys_status['2jump_ring'] = jump2ring sys_status['3jump_ring'] = jump3ring sys_status['4jump_ring'] = jump4ring threat, max_threat, myrating, j1_threats = self.area_ratings(neighbors) sys_status['neighborThreat'] = threat sys_status['max_neighbor_threat'] = max_threat sys_status['my_neighbor_rating'] = myrating threat, max_threat, myrating, j2_threats = self.area_ratings(jump2ring) sys_status['jump2_threat'] = threat sys_status['my_jump2_rating'] = myrating threat, max_threat, myrating, j3_threats = self.area_ratings(jump3ring) sys_status['jump3_threat'] = threat sys_status['my_jump3_rating'] = myrating # for local system includes both enemies and mobs threat_keys = ['fleetThreat', 'neighborThreat', 'jump2_threat'] sys_status['regional_threat'] = CombatRatingsAI.combine_ratings_list( [sys_status.get(x, 0) for x in threat_keys]) # TODO: investigate cases where regional_threat has been nonzero but no regional_threat_fleets # (probably due to attenuating history of past threats) sys_status.setdefault('regional_fleet_threats', set()).update(j1_threats, j2_threats)
def wrapper(*args): return dict_from_map(method(*args))
def update_system_status(self): print 10 * "=", "Updating System Threats", 10 * "=" universe = fo.getUniverse() empire = fo.getEmpire() empire_id = fo.empireID() destroyed_object_ids = universe.destroyedObjectIDs(empire_id) supply_unobstructed_systems = set(empire.supplyUnobstructedSystems) min_hidden_attack = 4 min_hidden_health = 8 # for use in debugging verbose = False # assess enemy fleets that may have been momentarily visible # start with dummy entries cur_e_fighters = { CombatRatingsAI.default_ship_stats().get_stats(hashable=True): [0] } old_e_fighters = { CombatRatingsAI.default_ship_stats().get_stats(hashable=True): [0] } enemies_by_system = {} my_fleets_by_system = {} fleet_spot_position = {} current_turn = fo.currentTurn() for fleet_id in universe.fleetIDs: fleet = universe.getFleet(fleet_id) if not fleet or fleet.empty: continue # TODO: check if currently in system and blockaded before accepting destination as location this_system_id = fleet.nextSystemID if fleet.nextSystemID != INVALID_ID else fleet.systemID dead_fleet = fleet_id in destroyed_object_ids if fleet.ownedBy(empire_id): if not dead_fleet: my_fleets_by_system.setdefault(this_system_id, []).append(fleet_id) fleet_spot_position.setdefault(fleet.systemID, []).append(fleet_id) continue # this is a fleet not owned by us if not fleet.unowned and (fleet.hasArmedShips or fleet.hasFighterShips): ship_stats = CombatRatingsAI.FleetCombatStats( fleet_id).get_ship_stats(hashable=True) # track old/dead enemy fighters for rating assessments in case not enough current info e_f_dict = old_e_fighters if dead_fleet else cur_e_fighters for stats in ship_stats: # log only ships that are armed if stats[0]: e_f_dict.setdefault(stats, [0])[0] += 1 # TODO: consider checking death of individual ships. If ships had been moved from this fleet # into another fleet, we might have witnessed their death in that other fleet but if this fleet # had not been seen since before that transfer then the ships might also still be listed here. if dead_fleet: continue # we are only interested in immediately recent data if get_partial_visibility_turn(fleet_id) < (current_turn - 1): continue sys_status = self.systemStatus.setdefault(this_system_id, {}) sys_status['enemy_ship_count'] = sys_status.get( 'enemy_ship_count', 0) + len(fleet.shipIDs) enemies_by_system.setdefault(this_system_id, []).append(fleet_id) if not fleet.unowned: self.misc.setdefault('enemies_sighted', {}).setdefault(current_turn, []).append(fleet_id) # TODO: If no current information available, rate against own fighters e_f_dict = cur_e_fighters if len( cur_e_fighters) > 1 else old_e_fighters self.__empire_standard_enemy = sorted([ (v, k) for k, v in e_f_dict.items() ])[-1][1] self.empire_standard_enemy_rating = self.get_standard_enemy( ).get_rating() # assess fleet and planet threats & my local fleets for sys_id in universe.systemIDs: sys_status = self.systemStatus.setdefault(sys_id, {}) system = universe.getSystem(sys_id) if verbose: print "AIState threat evaluation for %s" % system # update fleets sys_status['myfleets'] = my_fleets_by_system.get(sys_id, []) sys_status['myFleetsAccessible'] = fleet_spot_position.get( sys_id, []) local_enemy_fleet_ids = enemies_by_system.get(sys_id, []) sys_status['localEnemyFleetIDs'] = local_enemy_fleet_ids if system: sys_status['name'] = system.name # TODO: double check are these checks/deletes necessary? for fid in system.fleetIDs: fleet = universe.getFleet(fid) if not fleet or fleet.empty or fid in destroyed_object_ids: self.delete_fleet_info( fid) # this is safe even if fleet wasn't mine # update threats monster_ratings = [] # immobile enemy_ratings = [] # owned & mobile mob_ratings = [] # mobile & unowned mobile_fleets = [] # mobile and either owned or unowned for fid in local_enemy_fleet_ids: fleet = universe.getFleet(fid) # ensured to exist fleet_rating = CombatRatingsAI.get_fleet_rating( fid, enemy_stats=CombatRatingsAI.get_empire_standard_fighter()) if fleet.speed == 0: monster_ratings.append(fleet_rating) if verbose: print "\t immobile enemy fleet %s has rating %.1f" % ( fleet, fleet_rating) continue if verbose: print "\t mobile enemy fleet %s has rating %.1f" % ( fleet, fleet_rating) mobile_fleets.append(fid) if fleet.unowned: mob_ratings.append(fleet_rating) else: enemy_ratings.append(fleet_rating) enemy_rating = CombatRatingsAI.combine_ratings_list(enemy_ratings) monster_rating = CombatRatingsAI.combine_ratings_list( monster_ratings) mob_rating = CombatRatingsAI.combine_ratings_list(mob_ratings) lost_fleets = fleetsLostBySystem.get(sys_id, []) lost_fleet_rating = CombatRatingsAI.combine_ratings_list( lost_fleets) # under current visibility rules should not be possible to have any losses or other info here, # but just in case... partial_vis_turn = get_partial_visibility_turn(sys_id) if not system or partial_vis_turn < 0: if verbose: print "Never had partial vis for %s - basing threat assessment on old info and lost ships" % system sys_status.setdefault('local_fleet_threats', set()) sys_status['planetThreat'] = 0 sys_status['fleetThreat'] = max( CombatRatingsAI.combine_ratings(enemy_rating, mob_rating), 0.98 * sys_status.get('fleetThreat', 0), 1.1 * lost_fleet_rating - monster_rating) sys_status['monsterThreat'] = max( monster_rating, 0.98 * sys_status.get('monsterThreat', 0), 1.1 * lost_fleet_rating - enemy_rating - mob_rating) sys_status['enemy_threat'] = max( enemy_rating, 0.98 * sys_status.get('enemy_threat', 0), 1.1 * lost_fleet_rating - monster_rating - mob_rating) sys_status['mydefenses'] = { 'overall': 0, 'attack': 0, 'health': 0 } sys_status['totalThreat'] = sys_status['fleetThreat'] sys_status['regional_fleet_threats'] = sys_status[ 'local_fleet_threats'].copy() continue # have either stale or current info pattack = phealth = 0 mypattack = myphealth = 0 for pid in system.planetIDs: planet = universe.getPlanet(pid) if not planet: continue prating = self.assess_planet_threat(pid, sighting_age=current_turn - partial_vis_turn) if planet.ownedBy( empire_id): # TODO: check for diplomatic status mypattack += prating['attack'] myphealth += prating['health'] else: pattack += prating['attack'] phealth += prating['health'] if any("_NEST_" in special for special in planet.specials): sys_status['nest_threat'] = 100 sys_status['planetThreat'] = pattack * phealth sys_status['mydefenses'] = { 'overall': mypattack * myphealth, 'attack': mypattack, 'health': myphealth } # previous threat assessment could account for losses, ignore the losses now if max(sys_status.get('totalThreat', 0), pattack * phealth) >= 0.6 * lost_fleet_rating: lost_fleet_rating = 0 # TODO use sitrep combat info rather than estimating stealthed enemies by fleets lost to them # TODO also only consider past stealthed fleet threat to still be present if the system is still obstructed # TODO: track visibility across turns in order to distinguish the blip of visibility in (losing) combat, # which FO currently treats as being for the previous turn, # partially superseding the previous visibility for that turn if not partial_vis_turn == current_turn: sys_status.setdefault('local_fleet_threats', set()) sys_status['currently_visible'] = False # print ("Stale visibility for system %d ( %s ) -- last seen %d, " # "current Turn %d -- basing threat assessment on old info and lost ships") % ( # sys_id, sys_status.get('name', "name unknown"), partial_vis_turn, currentTurn) sys_status['fleetThreat'] = max( CombatRatingsAI.combine_ratings(enemy_rating, mob_rating), 0.98 * sys_status.get('fleetThreat', 0), 2.0 * lost_fleet_rating - max(sys_status.get('monsterThreat', 0), monster_rating)) sys_status['enemy_threat'] = max( enemy_rating, 0.98 * sys_status.get('enemy_threat', 0), 1.1 * lost_fleet_rating - max(sys_status.get('monsterThreat', 0), monster_rating)) sys_status['monsterThreat'] = max( monster_rating, 0.98 * sys_status.get('monsterThreat', 0)) # sys_status['totalThreat'] = ((pattack + enemy_attack + monster_attack) ** 0.8)\ # * ((phealth + enemy_health + monster_health)** 0.6) # reevaluate this sys_status['totalThreat'] = max( CombatRatingsAI.combine_ratings_list([ enemy_rating, mob_rating, monster_rating, pattack * phealth ]), 2 * lost_fleet_rating, 0.98 * sys_status.get('totalThreat', 0)) else: # system considered visible sys_status['currently_visible'] = True sys_status['local_fleet_threats'] = set(mobile_fleets) # includes mobile monsters sys_status['fleetThreat'] = max( CombatRatingsAI.combine_ratings(enemy_rating, mob_rating), 2 * lost_fleet_rating - monster_rating) if verbose: print "enemy threat calc parts: enemy rating %.1f, lost fleet rating %.1f, monster_rating %.1f" % ( enemy_rating, lost_fleet_rating, monster_rating) # does NOT include mobile monsters sys_status['enemy_threat'] = max( enemy_rating, 2 * lost_fleet_rating - monster_rating) sys_status['monsterThreat'] = monster_rating sys_status[ 'totalThreat'] = CombatRatingsAI.combine_ratings_list([ enemy_rating, mob_rating, monster_rating, pattack * phealth ]) sys_status['regional_fleet_threats'] = sys_status[ 'local_fleet_threats'].copy() sys_status['fleetThreat'] = max(sys_status['fleetThreat'], sys_status.get('nest_threat', 0)) sys_status['totalThreat'] = max(sys_status['totalThreat'], sys_status.get('nest_threat', 0)) # has been seen with Partial Vis, but is currently supply-blocked if partial_vis_turn > 0 and sys_id not in supply_unobstructed_systems: sys_status['fleetThreat'] = max( sys_status['fleetThreat'], min_hidden_attack * min_hidden_health) sys_status['totalThreat'] = max( sys_status['totalThreat'], ((pattack + min_hidden_attack)**0.8) * ((phealth + min_hidden_health)**0.6)) if verbose and sys_status['fleetThreat'] > 0: print "%s intermediate status: %s" % (system, sys_status) enemy_supply, enemy_near_supply = self.assess_enemy_supply( ) # TODO: assess change in enemy supply over time # assess secondary threats (threats of surrounding systems) and update my fleet rating for sys_id in universe.systemIDs: sys_status = self.systemStatus[sys_id] 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()) 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'] = 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))) for sys_id in universe.systemIDs: sys_status = self.systemStatus[sys_id] neighbors = sys_status.get('neighbors', set()) this_system = universe.getSystem(sys_id) if verbose: print "Regional Assessment for %s with local fleet threat %.1f" % ( this_system, sys_status.get('fleetThreat', 0)) jumps2 = set() jumps3 = set() jumps4 = set() for seta, setb in [(neighbors, jumps2), (jumps2, jumps3), (jumps3, jumps4)]: for sys2id in seta: setb.update( self.systemStatus.get(sys2id, {}).get('neighbors', set())) jump2ring = jumps2 - neighbors - {sys_id} jump3ring = jumps3 - jumps2 - neighbors - {sys_id} jump4ring = jumps4 - jumps3 - jumps2 - neighbors - {sys_id} sys_status['2jump_ring'] = jump2ring sys_status['3jump_ring'] = jump3ring sys_status['4jump_ring'] = jump4ring threat, max_threat, myrating, j1_threats = self.area_ratings( neighbors) sys_status['neighborThreat'] = threat sys_status['max_neighbor_threat'] = max_threat sys_status['my_neighbor_rating'] = myrating threat, max_threat, myrating, j2_threats = self.area_ratings( jump2ring) sys_status['jump2_threat'] = threat sys_status['my_jump2_rating'] = myrating threat, max_threat, myrating, j3_threats = self.area_ratings( jump3ring) sys_status['jump3_threat'] = threat sys_status['my_jump3_rating'] = myrating # for local system includes both enemies and mobs threat_keys = ['fleetThreat', 'neighborThreat', 'jump2_threat'] sys_status[ 'regional_threat'] = CombatRatingsAI.combine_ratings_list( [sys_status.get(x, 0) for x in threat_keys]) # TODO: investigate cases where regional_threat has been nonzero but no regional_threat_fleets # (probably due to attenuating history of past threats) sys_status.setdefault('regional_fleet_threats', set()).update(j1_threats, j2_threats)
def follow_vis_system_connections(start_system_id, home_system_id): universe = fo.getUniverse() empire_id = foAI.foAIstate.empireID exploration_list = [start_system_id] while exploration_list: cur_system_id = exploration_list.pop() if cur_system_id in graphFlags: continue graphFlags[cur_system_id] = 1 system = universe.getSystem(cur_system_id) if not system: sys_name = foAI.foAIstate.systemStatus.get(cur_system_id, {}).get('name', "name unknown") else: sys_name = system.name or foAI.foAIstate.systemStatus.get(cur_system_id, {}).get('name', "name unknown") if cur_system_id in foAI.foAIstate.visBorderSystemIDs: pre_vis = "a border system" elif cur_system_id in foAI.foAIstate.visInteriorSystemIDs: pre_vis = "an interior system" else: pre_vis = "an unknown system" if fo.currentTurn() < 50: visibility_turn_list = sorted(universe.getVisibilityTurnsMap(cur_system_id, empire_id).items(), key=lambda x: x[0].numerator) visibility_info = ['%s: %s' % (vis.name, turn) for vis, turn in visibility_turn_list] print "*** system ID %d ( %s ) ; previously %s, new visibility turns info: %s " % (cur_system_id, sys_name, pre_vis, visibility_info) status_str = "*** system ID %d ( %s ) ; " % (cur_system_id, sys_name) has_been_visible = universe.getVisibilityTurnsMap(cur_system_id, empire_id).get(fo.visibility.partial, 0) > 0 is_connected = universe.systemsConnected(cur_system_id, home_system_id, -1) # self.empire_id) status_str += " -- is %s partially visible " % (["not", ""][has_been_visible]) status_str += " -- is %s visibly connected to homesystem " % (["not", ""][is_connected]) if has_been_visible: sys_status = foAI.foAIstate.systemStatus.setdefault(cur_system_id, {}) foAI.foAIstate.visInteriorSystemIDs[cur_system_id] = 1 if cur_system_id in foAI.foAIstate.visBorderSystemIDs: del foAI.foAIstate.visBorderSystemIDs[cur_system_id] #neighbors= dict( [(el.key(), el.data()) for el in universe.getSystemNeighborsMap(cur_system_id, empire_id)] ) # neighbors = set(dict_from_map(universe.getSystemNeighborsMap(cur_system_id, empire_id)).keys()) sys_status.setdefault('neighbors', set()).update(neighbors) sys_planets = sys_status.setdefault('planets', {}) if fo.currentTurn() < 50: print " previously knew of system %d planets %s" % (cur_system_id, sys_planets.keys()) if system: for planet_id in system.planetIDs: sys_planets.setdefault(planet_id, {}).setdefault(TARGET_POP, 0) sys_planets[planet_id].setdefault(TROOPS, 0) planet = universe.getPlanet(planet_id) if planet: new_pop = planet.currentMeterValue(fo.meterType.targetPopulation) if new_pop != sys_planets[planet_id][TARGET_POP]: if fo.currentTurn() < 50: print " * updating targetPop of planet %d ( %s ) to %.2f from %.2f" % (planet_id, planet.name, new_pop, sys_planets[planet_id][TARGET_POP]) troops = planet.currentMeterValue(fo.meterType.troops) if troops != sys_planets[planet_id].get(TROOPS, 0): if fo.currentTurn() < 50: print " * updating troops of planet %d ( %s ) to %.2f from %.2f" % (planet_id, planet.name, troops, sys_planets[planet_id][ TROOPS]) sys_planets[planet_id][TARGET_POP] = new_pop sys_planets[planet_id][TROOPS] = troops if fo.currentTurn() < 50: print " now know of system %d planets %s" % (cur_system_id, sys_planets.keys()) #neighbors = list( universe.getImmediateNeighbors(cur_system_id, empire_id) ) #imNeighbors #if set(neighbors) != set(neighbors2): # print "Error with neighbors: imn giving %s ; giN giving %s"%(neighbors2, neighbors) if neighbors: status_str += " -- has neighbors %s " % neighbors for sys_id in neighbors: if sys_id not in foAI.foAIstate.exploredSystemIDs: foAI.foAIstate.unexploredSystemIDs[sys_id] = 1 if (sys_id not in graphFlags) and (sys_id not in foAI.foAIstate.visInteriorSystemIDs): foAI.foAIstate.visBorderSystemIDs[sys_id] = 1 exploration_list.append(sys_id) if fo.currentTurn() < 50: print status_str print "----------------------------------------------------------"
def is_valid(self): """check if FleetOrder could be somehow in future issued = is valid""" if self.is_executed() and self.is_execution_completed(): return False if self.get_source_target().valid and self.get_target_target().valid: sourceAITargetTypeValid = False targetAITargetTypeValid = False universe = fo.getUniverse() # outpost if AIFleetOrderType.ORDER_OUTPOST == self.get_fleet_order_type(): # with ship if AITargetType.TARGET_SHIP == self.get_source_target().target_type: ship = universe.getShip(self.get_source_target().target_id) if ship.canColonize: sourceAITargetTypeValid = True # with fleet elif AITargetType.TARGET_FLEET == self.get_source_target().target_type: fleet = universe.getFleet(self.get_source_target().target_id) if fleet.hasOutpostShips: sourceAITargetTypeValid = True # colonise planet if AITargetType.TARGET_PLANET == self.get_target_target().target_type: planet = universe.getPlanet(self.get_target_target().target_id) system = universe.getSystem(planet.systemID) sysPartialVisTurn = dict_from_map(universe.getVisibilityTurnsMap(planet.systemID, fo.empireID())).get(fo.visibility.partial, -9999) planetPartialVisTurn = dict_from_map(universe.getVisibilityTurnsMap(planet.id, fo.empireID())).get(fo.visibility.partial, -9999) if (planetPartialVisTurn == sysPartialVisTurn) and planet.unowned: targetAITargetTypeValid = True else:#try to get order cancelled out self.__set_executed() self.__set_execution_completed() # colonise elif AIFleetOrderType.ORDER_COLONISE == self.get_fleet_order_type(): # with ship if AITargetType.TARGET_SHIP == self.get_source_target().target_type: ship = universe.getShip(self.get_source_target().target_id) if ship.canColonize: sourceAITargetTypeValid = True # with fleet elif AITargetType.TARGET_FLEET == self.get_source_target().target_type: fleet = universe.getFleet(self.get_source_target().target_id) if fleet.hasColonyShips: sourceAITargetTypeValid = True # colonise planet if AITargetType.TARGET_PLANET == self.get_target_target().target_type: planet = universe.getPlanet(self.get_target_target().target_id) system = universe.getSystem(planet.systemID) sysPartialVisTurn = dict_from_map(universe.getVisibilityTurnsMap(planet.systemID, fo.empireID())).get(fo.visibility.partial, -9999) planetPartialVisTurn = dict_from_map(universe.getVisibilityTurnsMap(planet.id, fo.empireID())).get(fo.visibility.partial, -9999) if (planetPartialVisTurn == sysPartialVisTurn) and ( planet.unowned or (planet.ownedBy(fo.empireID()) and planet.currentMeterValue(fo.meterType.population)==0 )): targetAITargetTypeValid = True else:#try to get order cancelled out self.__set_executed() self.__set_execution_completed() # invade elif AIFleetOrderType.ORDER_INVADE == self.get_fleet_order_type(): # with ship if AITargetType.TARGET_SHIP == self.get_source_target().target_type: ship = universe.getShip(self.get_source_target().target_id) if ship.canInvade: sourceAITargetTypeValid = True # with fleet elif AITargetType.TARGET_FLEET == self.get_source_target().target_type: fleet = universe.getFleet(self.get_source_target().target_id) if fleet.hasTroopShips: sourceAITargetTypeValid = True # invade planet if AITargetType.TARGET_PLANET == self.get_target_target().target_type: planet = universe.getPlanet(self.get_target_target().target_id) planetPopulation = planet.currentMeterValue(fo.meterType.population) if not planet.unowned or planetPopulation > 0: targetAITargetTypeValid = True else:#try to get order cancelled out self.__set_executed() self.__set_execution_completed() # military elif AIFleetOrderType.ORDER_MILITARY == self.get_fleet_order_type(): # with ship if AITargetType.TARGET_SHIP == self.get_source_target().target_type: ship = universe.getShip(self.get_source_target().target_id) if ship.isArmed: sourceAITargetTypeValid = True # with fleet elif AITargetType.TARGET_FLEET == self.get_source_target().target_type: fleet = universe.getFleet(self.get_source_target().target_id) if fleet.hasArmedShips: sourceAITargetTypeValid = True # military system if AITargetType.TARGET_SYSTEM == self.get_target_target().target_type: system = universe.getSystem(self.get_target_target().target_id) targetAITargetTypeValid = True # move elif AIFleetOrderType.ORDER_MOVE == self.get_fleet_order_type(): # with fleet if AITargetType.TARGET_FLEET == self.get_source_target().target_type: sourceAITargetTypeValid = True # move to system if AITargetType.TARGET_SYSTEM == self.get_target_target().target_type: targetAITargetTypeValid = True # resupply elif AIFleetOrderType.ORDER_RESUPPLY == self.get_fleet_order_type(): # with fleet if AITargetType.TARGET_FLEET == self.get_source_target().target_type: sourceAITargetTypeValid = True # move to system if AITargetType.TARGET_SYSTEM == self.get_target_target().target_type: empire = fo.getEmpire() fleetSupplyableSystemIDs = empire.fleetSupplyableSystemIDs if self.get_target_target().target_id in fleetSupplyableSystemIDs: targetAITargetTypeValid = True # repair elif AIFleetOrderType.ORDER_REPAIR == self.get_fleet_order_type(): # with fleet if AITargetType.TARGET_FLEET == self.get_source_target().target_type: sourceAITargetTypeValid = True # move to system if AITargetType.TARGET_SYSTEM == self.get_target_target().target_type: empire = fo.getEmpire() fleetSupplyableSystemIDs = empire.fleetSupplyableSystemIDs if self.get_target_target().target_id in fleetSupplyableSystemIDs: #TODO: check for drydock still there/owned targetAITargetTypeValid = True # split fleet elif AIFleetOrderType.ORDER_SPLIT_FLEET == self.get_fleet_order_type(): # with fleet if AITargetType.TARGET_FLEET == self.get_source_target().target_type: sourceAITargetTypeValid = True # split ship if AITargetType.TARGET_SHIP == self.get_target_target().target_type: targetAITargetTypeValid = True if sourceAITargetTypeValid == True and targetAITargetTypeValid == True: if self.__check_validity_ship_in_fleet(self.get_target_target(), self.get_source_target()): return True elif AIFleetOrderType.ORDER_ATTACK == self.get_fleet_order_type(): # with fleet if AITargetType.TARGET_FLEET == self.get_source_target().target_type: sourceAITargetTypeValid = True # move to system if AITargetType.TARGET_SYSTEM == self.get_target_target().target_type or AITargetType.TARGET_PLANET == self.get_target_target().target_type: targetAITargetTypeValid = True elif AIFleetOrderType.ORDER_DEFEND == self.get_fleet_order_type(): # with fleet if AITargetType.TARGET_FLEET == self.get_source_target().target_type: sourceAITargetTypeValid = True # move to system if AITargetType.TARGET_SYSTEM == self.get_target_target().target_type or AITargetType.TARGET_PLANET == self.get_target_target().target_type: targetAITargetTypeValid = True if sourceAITargetTypeValid == True and targetAITargetTypeValid == True: return True return False