def log_planet_count_dist(sys_list): planet_count_dist = {} planet_size_dist = {size: 0 for size in planets.planet_sizes} for system in sys_list: planet_count = 0 for planet in fo.sys_get_planets(system): this_size = fo.planet_get_size(planet) if this_size in planets.planet_sizes: planet_count += 1 planet_size_dist[this_size] += 1 planet_count_dist.setdefault(planet_count, [0])[0] += 1 planet_tally = sum(planet_size_dist.values()) count_distribution_table = Table( [Text('planets in system'), Text('num of systems'), Float('% of systems', precession=1)], table_name='Planet Count Distribution' ) for planet_count, sys_count in planet_count_dist.items(): count_distribution_table.add_row((planet_count, sys_count[0], 100.0 * sys_count[0] / len(sys_list))) count_distribution_table.print_table() print size_distribution = Table( [Text('size'), Text('count'), Float('% of planets', precession=1)], table_name='Planet Size Distribution' ) for planet_size, planet_count in sorted(planet_size_dist.items()): size_distribution.add_row((planet_size, planet_count, 100.0 * planet_count / planet_tally)) size_distribution.print_table() print
def _report_classes_without_instances(classes_map, instance_names, classes_to_ignore: set): missed_instances = instance_names.symmetric_difference( classes_map).difference(classes_to_ignore) if not missed_instances: return warning("") warning( "In order to get more information about the classes in API we need to process an instances of thea classes." ) warning( "Classes mentioned bellow does not have instances so their specs are not full." ) warning("Please provide instances or add them to ignored,") warning( "check generate_stub usage in the freeorion/default/python/handlers folder." ) warning("") table = Table([Text("classes")], ) for inst in sorted(missed_instances, key=str.lower): table.add_row((str(inst), )) warning(table.get_table())
def log_planets(): universe = fo.get_universe() planets_table = Table( [Text('id'), Text('name'), Text('system'), Text('type'), Sequence('specials'), Text('species'), Sequence('buildings')], table_name='Planets summary') # group planets by system for sid in fo.get_systems(): for pid in fo.sys_get_planets(sid): planet = universe.getPlanet(pid) planet_type = fo.planet_get_type(pid).name planet_size = fo.planet_get_size(pid).name if planet_type != planet_size: planet_type = '%s %s' % (planet_type, planet_size) buildings = [universe.getBuilding(x).name for x in planet.buildingIDs] planets_table.add_row([ pid, planet.name, planet.systemID, planet_type, list(planet.specials), planet.speciesName, buildings ]) # Printing too much info at once will lead to truncation of text for line in planets_table.get_table().split('\n'): print line
def log_planets(): universe = fo.get_universe() planets_table = Table([ Text('id'), Text('name'), Text('system'), Text('type'), Sequence('specials'), Text('species'), Sequence('buildings') ], table_name='Planets summary') # group planets by system for sid in fo.get_systems(): for pid in fo.sys_get_planets(sid): planet = universe.getPlanet(pid) planet_type = fo.planet_get_type(pid).name planet_size = fo.planet_get_size(pid).name if planet_type != planet_size: planet_type = '%s %s' % (planet_type, planet_size) buildings = [ universe.getBuilding(x).name for x in planet.buildingIDs ] planets_table.add_row([ pid, planet.name, planet.systemID, planet_type, list(planet.specials), planet.speciesName, buildings ]) # Printing too much info at once will lead to truncation of text for line in planets_table.get_table().split('\n'): print line
def test_empty_table(): empty = Table( [Text('name', description='Name for first column'), Float('value', description='VValue'), Sequence('zzz'), Sequence('zzzzzzzzzzzzzzzzzz')], table_name='Wooho' ) assert empty.get_table() == EXPECTED_EMPTY_TABLE
def __report_system_threats(self): """Print a table with system threats to the logfile.""" current_turn = fo.currentTurn() if current_turn >= 100: return threat_table = Table([ Text('System'), Text('Vis.'), Float('Total'), Float('by Monsters'), Float('by Fleets'), Float('by Planets'), Float('1 jump away'), Float('2 jumps'), Float('3 jumps')], table_name="System Threat Turn %d" % current_turn ) universe = fo.getUniverse() for sys_id in universe.systemIDs: sys_status = self.systemStatus.get(sys_id, {}) system = universe.getSystem(sys_id) threat_table.add_row([ system, "Yes" if sys_status.get('currently_visible', False) else "No", sys_status.get('totalThreat', 0), sys_status.get('monsterThreat', 0), sys_status.get('fleetThreat', 0), sys_status.get('planetThreat', 0), sys_status.get('neighborThreat', 0.0), sys_status.get('jump2_threat', 0.0), sys_status.get('jump3_threat', 0.0), ]) info(threat_table)
def print_resources_priority(): """Calculate top resource priority.""" universe = fo.getUniverse() empire = fo.getEmpire() empire_planet_ids = PlanetUtilsAI.get_owned_planets_by_empire( universe.planetIDs) debug("Resource Priorities:") resource_priorities = {} aistate = get_aistate() for priority_type in get_priority_resource_types(): resource_priorities[priority_type] = aistate.get_priority( priority_type) sorted_priorities = sorted(resource_priorities.items(), key=itemgetter(1), reverse=True) top_priority = -1 for evaluation_priority, evaluation_score in sorted_priorities: if top_priority < 0: top_priority = evaluation_priority debug(" %s: %.2f", evaluation_priority, evaluation_score) # what is the focus of available resource centers? debug('') warnings = {} foci_table = Table([ Text('Planet'), Text('Size'), Text('Type'), Text('Focus'), Text('Species'), Text('Pop') ], table_name="Planetary Foci Overview Turn %d" % fo.currentTurn()) for pid in empire_planet_ids: planet = universe.getPlanet(pid) population = planet.currentMeterValue(fo.meterType.population) max_population = planet.currentMeterValue( fo.meterType.targetPopulation) if max_population < 1 and population > 0: warnings[planet.name] = (population, max_population) foci_table.add_row([ planet, planet.size, planet.type, "_".join(str(planet.focus).split("_")[1:])[:8], planet.speciesName, "%.1f/%.1f" % (population, max_population) ]) info(foci_table) debug( "Empire Totals:\nPopulation: %5d \nProduction: %5d\nResearch: %5d\n", empire.population(), empire.productionPoints, empire.resourceProduction(fo.resourceType.research)) for name, (cp, mp) in warnings.items(): warn( "Population Warning! -- %s has unsustainable current pop %d -- target %d", name, cp, mp)
def test_empty_table(): empty = Table( [ Text("name", description="Name for first column"), Float("value", description="VValue"), Sequence("zzz"), Sequence("zzzzzzzzzzzzzzzzzz"), ], table_name="Wooho", ) assert empty.get_table() == EXPECTED_EMPTY_TABLE
def log_planet_count_dist(sys_list): planet_count_dist = {} planet_size_dist = {size: 0 for size in planets.planet_sizes} for system in sys_list: planet_count = 0 for planet in fo.sys_get_planets(system): this_size = fo.planet_get_size(planet) if this_size in planets.planet_sizes: planet_count += 1 planet_size_dist[this_size] += 1 planet_count_dist.setdefault(planet_count, [0])[0] += 1 planet_tally = sum(planet_size_dist.values()) count_distribution_table = Table( [Text('planets in system'), Text('num of systems'), Float('% of systems', precession=1)], table_name='Planet Count Distribution' ) for planet_count, sys_count in planet_count_dist.items(): count_distribution_table.add_row((planet_count, sys_count[0], 100.0 * sys_count[0] / len(sys_list))) print(count_distribution_table) print() size_distribution = Table( [Text('size'), Text('count'), Float('% of planets', precession=1)], table_name='Planet Size Distribution' ) for planet_size, planet_count in sorted(planet_size_dist.items()): size_distribution.add_row((planet_size, planet_count, 100.0 * planet_count / planet_tally)) print(size_distribution) print()
def log_monsters_summary(monster_freq): monster_place_table = Table( [Text('monster'), Text('count')], table_name='Monster placement') for monster, counter in sorted(monsters_summary): monster_place_table.add_row([monster, counter]) print(monster_place_table) print() monster_chance = universe_tables.MONSTER_FREQUENCY[monster_freq] monster_table = Table( [ Text('monster'), Float('chance'), Text('attempts'), Text('number placed'), Text('number valid sys locs'), Text('number valid nest planet locs') ], table_name=( "Space Monsters Placement Summary\n" "Tracked Monster and Nest Summary (base monster freq: %4.1f%%)" % (100 * monster_chance))) for monster in sorted(tracked_monsters_summary): monster_table.add_row((monster, tracked_monsters_chance[monster], tracked_monsters_tries[monster], tracked_monsters_summary[monster], tracked_monsters_location_summary[monster], tracked_nest_location_summary[monster])) print(monster_table) print()
def print_resources_priority(): """Calculate top resource priority.""" universe = fo.getUniverse() empire = fo.getEmpire() empire_planet_ids = PlanetUtilsAI.get_owned_planets_by_empire(universe.planetIDs) print "Resource Priorities:" resource_priorities = {} aistate = get_aistate() for priority_type in get_priority_resource_types(): resource_priorities[priority_type] = aistate.get_priority(priority_type) sorted_priorities = resource_priorities.items() sorted_priorities.sort(lambda x, y: cmp(x[1], y[1]), reverse=True) top_priority = -1 for evaluation_priority, evaluation_score in sorted_priorities: if top_priority < 0: top_priority = evaluation_priority print " %s: %.2f" % (evaluation_priority, evaluation_score) # what is the focus of available resource centers? print warnings = {} foci_table = Table([ Text('Planet'), Text('Size'), Text('Type'), Text('Focus'), Text('Species'), Text('Pop') ], table_name="Planetary Foci Overview Turn %d" % fo.currentTurn()) for pid in empire_planet_ids: planet = universe.getPlanet(pid) population = planet.currentMeterValue(fo.meterType.population) max_population = planet.currentMeterValue(fo.meterType.targetPopulation) if max_population < 1 and population > 0: warnings[planet.name] = (population, max_population) foci_table.add_row([ planet, planet.size, planet.type, "_".join(str(planet.focus).split("_")[1:])[:8], planet.speciesName, "%.1f/%.1f" % (population, max_population) ]) info(foci_table) print "Empire Totals:\nPopulation: %5d \nProduction: %5d\nResearch: %5d\n" % ( empire.population(), empire.productionPoints, empire.resourceProduction(fo.resourceType.research)) for name, (cp, mp) in warnings.iteritems(): print "Population Warning! -- %s has unsustainable current pop %d -- target %d" % (name, cp, mp)
def log_systems(): universe = fo.get_universe() systems_table = Table( [Text('id'), Text('name'), Sequence('planets'), Sequence('connections'), Text('star')], table_name='System summary') for sid in fo.get_systems(): system = universe.getSystem(sid) systems_table.add_row([ sid, system.name, fo.sys_get_planets(sid), fo.sys_get_starlanes(sid), system.starType.name ]) # Printing too much info at once will lead to truncation of text for line in systems_table.get_table().split('\n'): print line
def log_systems(): universe = fo.get_universe() systems_table = Table( [Text('id'), Text('name'), Sequence('planets'), Sequence('connections'), Text('star')], table_name='System summary') for sid in fo.get_systems(): system = universe.getSystem(sid) systems_table.add_row([ sid, system.name, fo.sys_get_planets(sid), fo.sys_get_starlanes(sid), system.starType.name ]) # Printing too much info at once will lead to truncation of text for line in systems_table.get_table().split('\n'): print(line)
def log_planet_type_summary(sys_list): planet_type_summary_table = {k: 0 for k in planets.planet_types} for system in sys_list: for planet in fo.sys_get_planets(system): planet_type_summary_table[fo.planet_get_type(planet)] += 1 planet_total = sum(planet_type_summary_table.values()) type_summary_table = Table( [Text('planet type', align='<'), Float('% of planets', precession=1)], table_name='Planet Type Summary for a total of %d placed planets' % planet_total ) for planet_type, planet_count in sorted(planet_type_summary_table.items()): type_summary_table.add_row((planet_type.name, 100.0 * planet_count / planet_total)) print(type_summary_table) print()
def log_planet_type_summary(sys_list): planet_type_summary_table = {k: 0 for k in planets.planet_types} for system in sys_list: for planet in fo.sys_get_planets(system): planet_type_summary_table[fo.planet_get_type(planet)] += 1 planet_total = sum(planet_type_summary_table.values()) type_summary_table = Table( [Text('planet type', align='<'), Float('% of planets', precession=1)], table_name='Planet Type Summary for a total of %d placed planets' % planet_total ) for planet_type, planet_count in sorted(planet_type_summary_table.items()): type_summary_table.add_row((planet_type.name, 100.0 * planet_count / planet_total)) print type_summary_table print
def test_total_is_calculated(): table = Table( Number("A", precession=0, total=True), Number("B", precession=0, total=True), ) table.add_row(1, 10) table.add_row(2, 20) assert list(table) == [ "============", "| A | B |", "============", "| 1 | 10 |", "| 2 | 20 |", "============", "| 3 | 30 |", "============", ]
def test_empty_table(): empty = Table( Text("name", description="Name for first column"), Number("value", description="VValue"), Sequence("zzz"), Sequence("zzzzzzzzzzzzzzzzzz"), table_name="Wooho", ) io = StringIO() def writer(row): io.write(row) io.write("\n") empty.print_table(writer) assert io.getvalue() == EXPECTED_EMPTY_TABLE
def log_specials_summary(): special_placement_count_table = Table( [Text('special'), Text('times placed')], table_name="Special Placement Summary" ) for special in sorted(specials_summary): special_placement_count_table.add_row([special, specials_summary[special]]) special_placement_count_table.print_table() print special_placement = Table( [Text('count'), Text('tally'), Float('% of objects', precession=1)], table_name="Specials Count(Repeat) Distribution" ) objects_tally = sum(specials_repeat_dist.values()) for number, tally in specials_repeat_dist.items(): special_placement.add_row((number, tally, 100.0 * tally / (1E-10 + objects_tally))) special_placement.print_table() print
def split_new_fleets(self): """Split any new fleets (at new game creation, can have unplanned mix of ship roles).""" universe = fo.getUniverse() mission_table = Table( [ Text('Fleet'), Text('Mission'), Text('Ships'), Float('Rating'), Float('Troops'), Text('Target') ], table_name="Turn %d: Fleet Mission Review from Last Turn" % fo.currentTurn()) for fleet_id, mission in self.get_fleet_missions_map().items(): fleet = universe.getFleet(fleet_id) if not fleet: continue if not mission: mission_table.add_row([fleet]) else: mission_table.add_row([ fleet, mission.type or "None", len(fleet.shipIDs), CombatRatingsAI.get_fleet_rating(fleet_id), FleetUtilsAI.count_troops_in_fleet(fleet_id), mission.target or "-" ]) mission_table.print_table() # TODO: check length of fleets for losses or do in AIstat.__cleanRoles known_fleets = self.get_fleet_roles_map() self.newlySplitFleets.clear() fleets_to_split = [ fleet_id for fleet_id in FleetUtilsAI.get_empire_fleet_ids() if fleet_id not in known_fleets ] if fleets_to_split: print "Splitting new fleets" for fleet_id in fleets_to_split: fleet = universe.getFleet(fleet_id) if not fleet: print >> sys.stderr, "After splitting fleet: resulting fleet ID %d appears to not exist" % fleet_id continue fleet_len = len(list(fleet.shipIDs)) if fleet_len == 1: continue new_fleets = FleetUtilsAI.split_fleet( fleet_id) # try splitting fleet print "\t from splitting fleet ID %4d with %d ships, got %d new fleets:" % ( fleet_id, fleet_len, len(new_fleets))
def report_system_threats(self): if fo.currentTurn() >= 100: return universe = fo.getUniverse() sys_id_list = sorted(universe.systemIDs) # will normally look at this, the list of all known systems current_turn = fo.currentTurn() # assess fleet and planet threats threat_table = Table([ Text('System'), Text('Vis.'), Float('Total'), Float('by Monsters'), Float('by Fleets'), Float('by Planets'), Float('1 jump away'), Float('2 jumps'), Float('3 jumps')], table_name="System Threat Turn %d" % current_turn ) defense_table = Table([ Text('System Defenses'), Float('Total'), Float('by Planets'), Float('by Fleets'), Float('Fleets 1 jump away'), Float('2 jumps'), Float('3 jumps')], table_name="System Defenses Turn %d" % current_turn ) for sys_id in sys_id_list: sys_status = self.systemStatus.get(sys_id, {}) system = universe.getSystem(sys_id) threat_table.add_row([ system, "Yes" if sys_status.get('currently_visible', False) else "No", sys_status.get('totalThreat', 0), sys_status.get('monsterThreat', 0), sys_status.get('fleetThreat', 0), sys_status.get('planetThreat', 0), sys_status.get('neighborThreat', 0.0), sys_status.get('jump2_threat', 0.0), sys_status.get('jump3_threat', 0.0), ]) defense_table.add_row([ system, sys_status.get('all_local_defenses', 0.0), sys_status.get('mydefenses', {}).get('overall', 0.0), sys_status.get('myFleetRating', 0.0), sys_status.get('my_neighbor_rating', 0.0), sys_status.get('my_jump2_rating', 0.0), sys_status.get('my_jump3_rating', 0.0), ]) threat_table.print_table() defense_table.print_table()
def print_capital_info(homeworld): table = Table( Text("Id", description="Building id"), Text("Name"), Text("Type"), Sequence("Tags"), Sequence("Specials"), Text("Owner Id"), table_name="Buildings present at empire Capital in Turn %d" % fo.currentTurn(), ) universe = fo.getUniverse() for building_id in homeworld.buildingIDs: building = universe.getBuilding(building_id) table.add_row( building_id, building.name, "_".join(building.buildingTypeName.split("_")[-2:]), sorted(building.tags), sorted(building.specials), building.owner, ) table.print_table(info)
def print_production_queue(after_turn=False): """Print production queue content with relevant info in table format.""" universe = fo.getUniverse() s = "after" if after_turn else "before" title = "Production Queue Turn %d %s ProductionAI calls" % ( fo.currentTurn(), s) prod_queue_table = Table( Text("Object"), Text("Location"), Text("Quantity"), Text("Progress"), Text("Allocated PP"), Text("Turns left"), table_name=title, ) for element in fo.getEmpire().productionQueue: if element.buildType == EmpireProductionTypes.BT_SHIP: item = fo.getShipDesign(element.designID) elif element.buildType == EmpireProductionTypes.BT_BUILDING: item = fo.getBuildingType(element.name) else: continue cost = item.productionCost(fo.empireID(), element.locationID) prod_queue_table.add_row( element.name, universe.getPlanet(element.locationID), "%dx %d" % (element.remaining, element.blocksize), "%.1f / %.1f" % (element.progress * cost, cost), "%.1f" % element.allocation, "%d" % element.turnsLeft, ) prod_queue_table.print_table(info)
def __print_candidate_table(candidates, mission, show_detail=False): universe = fo.getUniverse() if mission == "Colonization": first_column = Text("Score/Species") def get_first_column_value(x): return round(x[0], 2), x[1] elif mission == "Outposts": first_column = Number("Score") get_first_column_value = itemgetter(0) else: warning("__print_candidate_table(%s, %s): Invalid mission type" % (candidates, mission)) return columns = [first_column, Text("Planet"), Text("System"), Sequence("Specials")] if show_detail: columns.append(Sequence("Detail")) candidate_table = Table(*columns, table_name="Potential Targets for %s in Turn %d" % (mission, fo.currentTurn())) for planet_id, score_tuple in candidates: if score_tuple[0] > 0.5: planet = universe.getPlanet(planet_id) entries = [ get_first_column_value(score_tuple), planet, universe.getSystem(planet.systemID), planet.specials, ] if show_detail: entries.append(score_tuple[-1]) candidate_table.add_row(*entries) candidate_table.print_table(info)
def _report_classes_without_instances(classes_map: Iterable[str], instance_names, classes_to_ignore: Set[str]): missed_instances = instance_names.symmetric_difference( classes_map).difference(classes_to_ignore) if not missed_instances: return warning("") warning( fill( "In order to get more information about the classes in API" " we need to process an instances of classes." " Classes mentioned bellow does not have instances so their specs are not full." " Please provide instances or add them to ignored," " check generate_stub usage in the" " freeorion/default/python/handlers folder.", width=60, )) table = Table(Text("classes without instances")) for inst in sorted(missed_instances, key=str.lower): table.add_row(inst) table.print_table(warning)
def log_monsters_summary(monster_freq): monster_place_table = Table([Text('monster'), Text('count')], table_name='Monster placement') for monster, counter in sorted(monsters_summary): monster_place_table.add_row([monster, counter]) monster_place_table.print_table() print monster_chance = universe_tables.MONSTER_FREQUENCY[monster_freq] monster_table = Table( [Text('monster'), Float('chance'), Text('attempts'), Text('number placed'), Text('number valid sys locs'), Text('number valid nest planet locs')], table_name=("Space Monsters Placement Summary\n" "Tracked Monster and Nest Summary (base monster freq: %4.1f%%)" % (100 * monster_chance)) ) for monster in sorted(tracked_monsters_summary): monster_table.add_row( (monster, tracked_monsters_chance[monster], tracked_monsters_tries[monster], tracked_monsters_summary[monster], tracked_monsters_location_summary[monster], tracked_nest_location_summary[monster]) ) monster_table.print_table() print
def __report_last_turn_fleet_missions(self): """Print a table reviewing last turn fleet missions to the log file.""" universe = fo.getUniverse() mission_table = Table( [Text('Fleet'), Text('Mission'), Text('Ships'), Float('Rating'), Float('Troops'), Text('Target')], table_name="Turn %d: Fleet Mission Review from Last Turn" % fo.currentTurn()) for fleet_id, mission in self.get_fleet_missions_map().items(): fleet = universe.getFleet(fleet_id) if not fleet: continue if not mission: mission_table.add_row([fleet]) else: mission_table.add_row([ fleet, mission.type or "None", len(fleet.shipIDs), CombatRatingsAI.get_fleet_rating(fleet_id), FleetUtilsAI.count_troops_in_fleet(fleet_id), mission.target or "-" ]) info(mission_table)
def __report_system_defenses(self): """Print a table with system defenses to the logfile.""" current_turn = fo.currentTurn() if current_turn >= 100: return defense_table = Table([ Text('System Defenses'), Float('Total'), Float('by Planets'), Float('by Fleets'), Float('Fleets 1 jump away'), Float('2 jumps'), Float('3 jumps')], table_name="System Defenses Turn %d" % current_turn ) universe = fo.getUniverse() for sys_id in universe.systemIDs: sys_status = self.systemStatus.get(sys_id, {}) system = universe.getSystem(sys_id) defense_table.add_row([ system, sys_status.get('all_local_defenses', 0.0), sys_status.get('mydefenses', {}).get('overall', 0.0), sys_status.get('myFleetRating', 0.0), sys_status.get('my_neighbor_rating', 0.0), sys_status.get('my_jump2_rating', 0.0), sys_status.get('my_jump3_rating', 0.0), ]) info(defense_table)
def log_specials_summary(): special_placement_count_table = Table( [Text('special'), Text('times placed')], table_name="Special Placement Summary" ) for special in sorted(specials_summary): special_placement_count_table.add_row([special, specials_summary[special]]) print(special_placement_count_table) print() special_placement = Table( [Text('count'), Text('tally'), Float('% of objects', precession=1)], table_name="Specials Count(Repeat) Distribution" ) objects_tally = sum(specials_repeat_dist.values()) for number, tally in specials_repeat_dist.items(): special_placement.add_row((number, tally, 100.0 * tally / (1E-10 + objects_tally))) print(special_placement) print()
def split_new_fleets(self): """Split any new fleets (at new game creation, can have unplanned mix of ship roles).""" universe = fo.getUniverse() mission_table = Table([Text('Fleet'), Text('Mission'), Text('Ships'), Float('Rating'), Float('Troops'), Text('Target')], table_name="Turn %d: Fleet Mission Review from Last Turn" % fo.currentTurn()) for fleet_id, mission in self.get_fleet_missions_map().items(): fleet = universe.getFleet(fleet_id) if not fleet: continue if not mission: mission_table.add_row([fleet]) else: mission_table.add_row([ fleet, mission.type or "None", len(fleet.shipIDs), CombatRatingsAI.get_fleet_rating(fleet_id), FleetUtilsAI.count_troops_in_fleet(fleet_id), mission.target or "-" ]) mission_table.print_table() # TODO: check length of fleets for losses or do in AIstat.__cleanRoles known_fleets = self.get_fleet_roles_map() self.newlySplitFleets.clear() fleets_to_split = [fleet_id for fleet_id in FleetUtilsAI.get_empire_fleet_ids() if fleet_id not in known_fleets] if fleets_to_split: print "Splitting new fleets" for fleet_id in fleets_to_split: fleet = universe.getFleet(fleet_id) if not fleet: print >> sys.stderr, "After splitting fleet: resulting fleet ID %d appears to not exist" % fleet_id continue fleet_len = len(list(fleet.shipIDs)) if fleet_len == 1: continue new_fleets = FleetUtilsAI.split_fleet(fleet_id) # try splitting fleet print "\t from splitting fleet ID %4d with %d ships, got %d new fleets:" % (fleet_id, fleet_len, len(new_fleets))
def _print_empire_species_roster(): """Print empire species roster in table format to log.""" grade_map = {"ULTIMATE": "+++", "GREAT": "++", "GOOD": "+", "AVERAGE": "o", "BAD": "-", "NO": "---"} grade_tags = [ Tags.INDUSTRY, Tags.RESEARCH, Tags.POPULATION, Tags.SUPPLY, Tags.WEAPONS, Tags.ATTACKTROOPS, ] grade_tags_names = { Tags.INDUSTRY: "Ind.", Tags.RESEARCH: "Res.", Tags.POPULATION: "Pop.", Tags.SUPPLY: "Supply", Tags.WEAPONS: "Pilot", Tags.ATTACKTROOPS: "Troop", } species_table = Table( Text("species"), Sequence("PIDs"), Bool("Colonize"), Text("Shipyards"), *[Text(grade_tags_names[v]) for v in grade_tags], Sequence("Tags"), table_name="Empire species roster Turn %d" % fo.currentTurn(), ) for species_name, planet_ids in get_empire_planets_by_species().items(): species_tags = fo.getSpecies(species_name).tags number_of_shipyards = len(get_ship_builder_locations(species_name)) species_table.add_row( species_name, planet_ids, can_build_colony_for_species(species_name), number_of_shipyards, *[grade_map.get(get_species_tag_grade(species_name, tag).upper(), "o") for tag in grade_tags], [tag for tag in species_tags if not any(s in tag for s in grade_tags) and "PEDIA" not in tag], ) species_table.print_table(info)
def log_planet_type_summary(sys_list): planet_type_summary_table = {k: 0 for k in planets.planet_types} for system in sys_list: for planet in fo.sys_get_planets(system): planet_type_summary_table[fo.planet_get_type(planet)] += 1 planet_total = sum(planet_type_summary_table.values()) type_summary_table = Table( Text("planet type", align="<"), Number("% of planets", precession=1), table_name="Planet Type Summary for a total of %d placed planets" % planet_total, ) for planet_type, planet_count in sorted(planet_type_summary_table.items()): type_summary_table.add_row( planet_type.name, 100.0 * planet_count / planet_total, ) type_summary_table.print_table(print) print()
def log_systems(): universe = fo.get_universe() systems_table = Table( Text("id"), Text("name"), Sequence("planets"), Sequence("connections"), Text("star"), table_name="System summary", ) for sid in fo.get_systems(): system = universe.getSystem(sid) systems_table.add_row( sid, system.name, fo.sys_get_planets(sid), fo.sys_get_starlanes(sid), system.starType.name, ) systems_table.print_table(print)
def log_planets(): universe = fo.get_universe() planets_table = Table( Text("id"), Text("name"), Text("system"), Text("type"), Sequence("specials"), Text("species"), Sequence("buildings"), table_name="Planets summary", ) # group planets by system for sid in fo.get_systems(): for pid in fo.sys_get_planets(sid): planet = universe.getPlanet(pid) planet_type = fo.planet_get_type(pid).name planet_size = fo.planet_get_size(pid).name if planet_type != planet_size: planet_type = "%s %s" % (planet_type, planet_size) buildings = [ universe.getBuilding(x).name for x in planet.buildingIDs ] planets_table.add_row( pid, planet.name, planet.systemID, planet_type, list(planet.specials), planet.speciesName, buildings, ) planets_table.print_table(print)
def log_species_summary(native_freq): num_empires = sum(empire_species.values()) num_species = len(fo.get_playable_species()) exp_count = num_empires // num_species species_summary_table = Table( [Text('species'), Text('count'), Float('%', precession=1)], table_name=('Empire Starting Species Summary\n' 'Approximately %d to %d empires expected per species\n' '%d Empires and %d playable species') % (max(0, exp_count - 1), exp_count + 1, num_empires, num_species)) for species, count in sorted(empire_species.items()): species_summary_table.add_row( (species, count, 100.0 * count / num_empires)) species_summary_table.print_table() print native_chance = universe_tables.NATIVE_FREQUENCY[native_freq] # as the value in the universe table is higher for a lower frequency, we have to invert it # a value of 0 means no natives, in this case return immediately if native_chance <= 0: return native_table = Table( [ Text('settled natives'), Text('on planets'), Float('total', precession=1), Float('actual', precession=1), Float('expected', precession=1), Sequence('planet types') ], table_name="Natives Placement Summary (native frequency: %.1f%%)" % (100 * native_chance)) native_potential_planet_total = sum( potential_native_planet_summary.values()) for species in sorted(species_summary): if species_summary[species] > 0: settleable_planets = 0 expectation_tally = 0.0 for p_type in natives.planet_types_for_natives[species]: settleable_planets += potential_native_planet_summary[p_type] expectation_tally += native_chance * 100.0 * potential_native_planet_summary[ p_type] / (1E-10 + len(natives.natives_for_planet_type[p_type])) expectation = expectation_tally / (1E-10 + settleable_planets) native_table.add_row([ species, species_summary[species], 100.0 * species_summary[species] / (1E-10 + native_potential_planet_total), 100.0 * species_summary[species] / (1E-10 + settleable_planets), expectation, [ str(p_t) for p_t in natives.planet_types_for_natives[species] ] ]) native_table.print_table() print native_settled_planet_total = sum(settled_native_planet_summary.values()) type_summary_table = Table( [ Text('planet type'), Float('potential (% of tot)', precession=1), Float('settled (% of potential)', precession=1) ], table_name= ("Planet Type Summary for Native Planets (native frequency: %.1f%%)\n" "Totals: native_potential_planet_total: %s; native_settled_planet_total %s" ) % (100 * native_chance, native_potential_planet_total, native_settled_planet_total)) for planet_type, planet_count in sorted( potential_native_planet_summary.items()): settled_planet_count = settled_native_planet_summary.get( planet_type, 0) potential_percent = 100.0 * planet_count / ( 1E-10 + native_potential_planet_total) settled_percent = 100.0 * settled_planet_count / (1E-10 + planet_count) type_summary_table.add_row( (planet_type.name, potential_percent, settled_percent)) type_summary_table.print_table() print
def test_simple_table(): t = Table( [ Text("name", description="Name for first column"), Float("value", description="VValue"), Sequence("zzz"), Sequence("zzzzzzzzzzzzzzzzzz"), ], table_name="Wooho", ) t.add_row(["hello", 144444, "abcffff", "a"]) t.add_row([u"Plato aa\u03b2 III", 21, "de", "a"]) t.add_row([u"Plato \u03b2 III", 21, "de", "a"]) t.add_row(["Plato B III", 21, "d", "a"]) t.add_row(["Plato Bddddd III", 21, "d", "a"]) t.add_row(["Plato III", 21, "d", "a"]) assert t.get_table() == EXPECTED_SIMPLE_TABLE
def report_system_threats(self): if fo.currentTurn() >= 100: return universe = fo.getUniverse() sys_id_list = sorted( universe.systemIDs ) # will normally look at this, the list of all known systems current_turn = fo.currentTurn() # assess fleet and planet threats threat_table = Table([ Text('System'), Text('Vis.'), Float('Total'), Float('by Monsters'), Float('by Fleets'), Float('by Planets'), Float('1 jump away'), Float('2 jumps'), Float('3 jumps') ], table_name="System Threat Turn %d" % current_turn) defense_table = Table([ Text('System Defenses'), Float('Total'), Float('by Planets'), Float('by Fleets'), Float('Fleets 1 jump away'), Float('2 jumps'), Float('3 jumps') ], table_name="System Defenses Turn %d" % current_turn) for sys_id in sys_id_list: sys_status = self.systemStatus.get(sys_id, {}) system = universe.getSystem(sys_id) threat_table.add_row([ system, "Yes" if sys_status.get('currently_visible', False) else "No", sys_status.get('totalThreat', 0), sys_status.get('monsterThreat', 0), sys_status.get('fleetThreat', 0), sys_status.get('planetThreat', 0), sys_status.get('neighborThreat', 0.0), sys_status.get('jump2_threat', 0.0), sys_status.get('jump3_threat', 0.0), ]) defense_table.add_row([ system, sys_status.get('all_local_defenses', 0.0), sys_status.get('mydefenses', {}).get('overall', 0.0), sys_status.get('myFleetRating', 0.0), sys_status.get('my_neighbor_rating', 0.0), sys_status.get('my_jump2_rating', 0.0), sys_status.get('my_jump3_rating', 0.0), ]) threat_table.print_table() defense_table.print_table()
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() fleet_list = sorted(list(self.__fleetRoleByID)) ship_count = 0 destroyed_object_ids = universe.destroyedObjectIDs(fo.empireID()) fleet_table = Table([ Text('Fleet'), Float('Rating'), Float('Troops'), Text('Location'), Text('Destination') ], table_name="Fleet Summary Turn %d" % fo.currentTurn()) for fleet_id in fleet_list: status = self.fleetStatus.setdefault(fleet_id, {}) rating = CombatRatingsAI.get_fleet_rating( fleet_id, self.get_standard_enemy()) troops = FleetUtilsAI.count_troops_in_fleet(fleet_id) old_sys_id = status.get('sysID', -2) fleet = universe.getFleet(fleet_id) if fleet: sys_id = fleet.systemID if old_sys_id in [-2, -1]: old_sys_id = sys_id status['nships'] = len(fleet.shipIDs) ship_count += status['nships'] else: # can still retrieve a fleet object even if fleet was just destroyed, so shouldn't get here # however,this has been observed happening, and is the reason a fleet check was added a few lines below. # Not at all sure how this came about, but was throwing off threat assessments sys_id = old_sys_id if fleet_id not in ok_fleets: # or fleet.empty: if (fleet and self.__fleetRoleByID.get(fleet_id, -1) != -1 and fleet_id not in destroyed_object_ids and any(ship_id not in destroyed_object_ids for ship_id in fleet.shipIDs)): if not just_resumed: fleetsLostBySystem.setdefault(old_sys_id, []).append( max(rating, MilitaryAI.MinThreat)) if fleet_id in self.__fleetRoleByID: del self.__fleetRoleByID[fleet_id] if fleet_id in self.__aiMissionsByFleetID: del self.__aiMissionsByFleetID[fleet_id] if fleet_id in self.fleetStatus: del self.fleetStatus[fleet_id] continue else: # fleet in ok fleets this_sys = universe.getSystem(sys_id) next_sys = universe.getSystem(fleet.nextSystemID) fleet_table.add_row([ fleet, rating, troops, this_sys or 'starlane', next_sys or '-', ]) status['rating'] = rating if next_sys: status['sysID'] = next_sys.id elif this_sys: status['sysID'] = this_sys.id else: main_mission = self.get_fleet_mission(fleet_id) main_mission_type = (main_mission.getAIMissionTypes() + [-1])[0] if main_mission_type != -1: targets = main_mission.getAITargets(main_mission_type) if targets: m_mt0 = targets[0] if isinstance(m_mt0.target_type, System): status[ 'sysID'] = m_mt0.target.id # hmm, but might still be a fair ways from here fleet_table.print_table() self.shipCount = ship_count # Next string used in charts. Don't modify it! print "Empire Ship Count: ", ship_count print "Empire standard fighter summary: ", CombatRatingsAI.get_empire_standard_fighter( ).get_stats() print "------------------------"
def get_invasion_fleets(): invasion_timer.start("gathering initial info") universe = fo.getUniverse() empire = fo.getEmpire() empire_id = fo.empireID() home_system_id = PlanetUtilsAI.get_capital_sys_id() aistate = get_aistate() visible_system_ids = list(aistate.visInteriorSystemIDs) + list(aistate.visBorderSystemIDs) if home_system_id != INVALID_ID: accessible_system_ids = [sys_id for sys_id in visible_system_ids if (sys_id != INVALID_ID) and universe.systemsConnected(sys_id, home_system_id, empire_id)] else: debug("Empire has no identifiable homeworld; will treat all visible planets as accessible.") # TODO: check if any troop ships owned, use their system as home system accessible_system_ids = visible_system_ids acessible_planet_ids = PlanetUtilsAI.get_planets_in__systems_ids(accessible_system_ids) all_owned_planet_ids = PlanetUtilsAI.get_all_owned_planet_ids(acessible_planet_ids) # includes unpopulated outposts all_populated_planets = PlanetUtilsAI.get_populated_planet_ids(acessible_planet_ids) # includes unowned natives empire_owned_planet_ids = PlanetUtilsAI.get_owned_planets_by_empire(universe.planetIDs) invadable_planet_ids = set(all_owned_planet_ids).union(all_populated_planets) - set(empire_owned_planet_ids) invasion_targeted_planet_ids = get_invasion_targeted_planet_ids(universe.planetIDs, MissionType.INVASION) invasion_targeted_planet_ids.extend( get_invasion_targeted_planet_ids(universe.planetIDs, MissionType.ORBITAL_INVASION)) all_invasion_targeted_system_ids = set(PlanetUtilsAI.get_systems(invasion_targeted_planet_ids)) invasion_fleet_ids = FleetUtilsAI.get_empire_fleet_ids_by_role(MissionType.INVASION) num_invasion_fleets = len(FleetUtilsAI.extract_fleet_ids_without_mission_types(invasion_fleet_ids)) debug("Current Invasion Targeted SystemIDs: %s" % PlanetUtilsAI.sys_name_ids(AIstate.invasionTargetedSystemIDs)) debug("Current Invasion Targeted PlanetIDs: %s" % PlanetUtilsAI.planet_string(invasion_targeted_planet_ids)) debug(invasion_fleet_ids and "Invasion Fleet IDs: %s" % invasion_fleet_ids or "Available Invasion Fleets: 0") debug("Invasion Fleets Without Missions: %s" % num_invasion_fleets) invasion_timer.start("planning troop base production") reserved_troop_base_targets = [] if aistate.character.may_invade_with_bases(): 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 # For planning base trooper invasion targets we have a two-pass system. (1) In the first pass we consider all # the invasion targets and figure out which ones appear to be suitable for using base troopers against (i.e., we # already have a populated planet in the same system that could build base troopers) and we have at least a # minimal amount of PP available, and (2) in the second pass we go through the reserved base trooper target list # and check to make sure that there does not appear to be too much military action still needed before the # target is ready to be invaded, we double check that not too many base troopers would be needed, and if things # look clear then we queue up the base troopers on the Production Queue and keep track of where we are building # them, and how many; we may also disqualify and remove previously qualified targets (in case, for example, # we lost our base trooper source planet since it was first added to list). # # For planning and tracking base troopers under construction, we use a dictionary store in # get_aistate().qualifyingTroopBaseTargets, keyed by the invasion target planet ID. We only store values # for invasion targets that appear likely to be suitable for base trooper use, and store a 2-item list. # The first item in this list is the ID of the planet where we expect to build the base troopers, and the second # entry initially is set to INVALID_ID (-1). The presence of this entry in qualifyingTroopBaseTargets # flags this target as being reserved as a base-trooper invasion target. # In the second pass, if/when we actually start construction, then we modify the record, replacing that second # value with the ID of the planet where the troopers are actually being built. (Right now that is always the # same as the source planet originally identified, but we could consider reevaluating that, or use that second # value to instead record how many base troopers have been queued, so that on later turns we can assess if the # process got delayed & perhaps more troopers need to be queued). secure_ai_fleet_missions = aistate.get_fleet_missions_with_any_mission_types([MissionType.SECURE, MissionType.MILITARY]) # Pass 1: identify qualifying base troop invasion targets for pid in invadable_planet_ids: # TODO: reorganize if pid in aistate.qualifyingTroopBaseTargets: continue planet = universe.getPlanet(pid) if not planet: continue sys_id = planet.systemID sys_partial_vis_turn = get_partial_visibility_turn(sys_id) planet_partial_vis_turn = get_partial_visibility_turn(pid) if planet_partial_vis_turn < sys_partial_vis_turn: continue best_base_planet = INVALID_ID best_trooper_count = 0 for pid2 in state.get_empire_planets_by_system(sys_id, include_outposts=False): if available_pp.get(pid2, 0) < 2: # TODO: improve troop base PP sufficiency determination break planet2 = universe.getPlanet(pid2) if not planet2 or planet2.speciesName not in ColonisationAI.empire_ship_builders: continue best_base_trooper_here = \ ProductionAI.get_best_ship_info(PriorityType.PRODUCTION_ORBITAL_INVASION, pid2)[1] if not best_base_trooper_here: continue troops_per_ship = best_base_trooper_here.troopCapacity if not troops_per_ship: continue species_troop_grade = CombatRatingsAI.get_species_troops_grade(planet2.speciesName) troops_per_ship = CombatRatingsAI.weight_attack_troops(troops_per_ship, species_troop_grade) if troops_per_ship > best_trooper_count: best_base_planet = pid2 best_trooper_count = troops_per_ship if best_base_planet != INVALID_ID: aistate.qualifyingTroopBaseTargets.setdefault(pid, [best_base_planet, INVALID_ID]) # Pass 2: for each target previously identified for base troopers, check that still qualifies and # check how many base troopers would be needed; if reasonable then queue up the troops and record this in # get_aistate().qualifyingTroopBaseTargets for pid in aistate.qualifyingTroopBaseTargets.keys(): planet = universe.getPlanet(pid) if planet and planet.owner == empire_id: del aistate.qualifyingTroopBaseTargets[pid] continue if pid in invasion_targeted_planet_ids: # TODO: consider overriding standard invasion mission continue if aistate.qualifyingTroopBaseTargets[pid][1] != -1: reserved_troop_base_targets.append(pid) if planet: all_invasion_targeted_system_ids.add(planet.systemID) # TODO: evaluate changes to situation, any more troops needed, etc. continue # already building for here _, planet_troops = evaluate_invasion_planet(pid, secure_ai_fleet_missions, True) sys_id = planet.systemID this_sys_status = aistate.systemStatus.get(sys_id, {}) troop_tally = 0 for _fid in this_sys_status.get('myfleets', []): troop_tally += FleetUtilsAI.count_troops_in_fleet(_fid) if troop_tally > planet_troops: # base troopers appear unneeded del aistate.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) 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 continue loc = aistate.qualifyingTroopBaseTargets[pid][0] best_base_trooper_here = ProductionAI.get_best_ship_info(PriorityType.PRODUCTION_ORBITAL_INVASION, loc)[1] loc_planet = universe.getPlanet(loc) if best_base_trooper_here is None: # shouldn't be possible at this point, but just to be safe warn("Could not find a suitable orbital invasion design at %s" % loc_planet) continue # TODO: have TroopShipDesigner give the expected number of troops including species effects directly troops_per_ship = best_base_trooper_here.troopCapacity species_troop_grade = CombatRatingsAI.get_species_troops_grade(loc_planet.speciesName) troops_per_ship = CombatRatingsAI.weight_attack_troops(troops_per_ship, species_troop_grade) if not troops_per_ship: warn("The best orbital invasion design at %s seems not to have any troop capacity." % loc_planet) continue _, col_design, build_choices = ProductionAI.get_best_ship_info(PriorityType.PRODUCTION_ORBITAL_INVASION, loc) if not col_design: continue if loc not in build_choices: warn('Best troop design %s can not be produced at planet with id: %s\d' % (col_design, build_choices)) continue n_bases = math.ceil((planet_troops + 1) / troops_per_ship) # TODO: reconsider this +1 safety factor # TODO: evaluate cost and time-to-build of best base trooper here versus cost and time-to-build-and-travel # for best regular trooper elsewhere # For now, we assume what building base troopers is best so long as either (1) we would need no more than # MAX_BASE_TROOPERS_POOR_INVADERS base troop ships, or (2) our base troopers have more than 1 trooper per # ship and we would need no more than MAX_BASE_TROOPERS_GOOD_INVADERS base troop ships if (n_bases > MAX_BASE_TROOPERS_POOR_INVADERS or (troops_per_ship > 1 and n_bases > MAX_BASE_TROOPERS_GOOD_INVADERS)): debug("ruling out base invasion troopers for %s due to high number (%d) required." % (planet, n_bases)) del aistate.qualifyingTroopBaseTargets[pid] continue debug("Invasion base planning, need %d troops at %d per ship, will build %d ships." % ( (planet_troops + 1), troops_per_ship, n_bases)) retval = fo.issueEnqueueShipProductionOrder(col_design.id, loc) debug("Enqueueing %d Troop Bases at %s for %s" % (n_bases, PlanetUtilsAI.planet_string(loc), PlanetUtilsAI.planet_string(pid))) if retval != 0: all_invasion_targeted_system_ids.add(planet.systemID) reserved_troop_base_targets.append(pid) aistate.qualifyingTroopBaseTargets[pid][1] = loc fo.issueChangeProductionQuantityOrder(empire.productionQueue.size - 1, 1, int(n_bases)) 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 evaluated_planet_ids = list( set(invadable_planet_ids) - set(invasion_targeted_planet_ids) - set(reserved_troop_base_targets)) evaluated_planets = assign_invasion_values(evaluated_planet_ids) sorted_planets = [(pid, pscore % 10000, ptroops) for pid, (pscore, ptroops) in evaluated_planets.items()] sorted_planets.sort(key=lambda x: x[1], reverse=True) sorted_planets = [(pid, pscore % 10000, ptroops) for pid, pscore, ptroops in sorted_planets] invasion_table = Table([Text('Planet'), Float('Score'), Text('Species'), Float('Troops')], table_name="Potential Targets for Invasion Turn %d" % fo.currentTurn()) for pid, pscore, ptroops in sorted_planets: planet = universe.getPlanet(pid) invasion_table.add_row([ planet, pscore, planet and planet.speciesName or "unknown", ptroops ]) info(invasion_table) sorted_planets = filter(lambda x: x[1] > 0, sorted_planets) # export opponent planets for other AI modules AIstate.opponentPlanetIDs = [pid for pid, __, __ in sorted_planets] AIstate.invasionTargets = sorted_planets # 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(evaluated_planet_ids))) invasion_timer.stop_print_and_clear()
def log_species_summary(native_freq): num_empires = sum(empire_species.values()) num_species = len(fo.get_playable_species()) exp_count = num_empires // num_species species_summary_table = Table( [Text('species'), Text('count'), Float('%', precession=1)], table_name=('Empire Starting Species Summary\n' 'Approximately %d to %d empires expected per species\n' '%d Empires and %d playable species') % (max(0, exp_count - 1), exp_count + 1, num_empires, num_species) ) for species, count in sorted(empire_species.items()): species_summary_table.add_row((species, count, 100.0 * count / num_empires)) print species_summary_table print native_chance = universe_tables.NATIVE_FREQUENCY[native_freq] # as the value in the universe table is higher for a lower frequency, we have to invert it # a value of 0 means no natives, in this case return immediately if native_chance <= 0: return native_table = Table( [ Text('settled natives'), Text('on planets'), Float('total', precession=1), Float('actual', precession=1), Float('expected', precession=1), Sequence('planet types') ], table_name="Natives Placement Summary (native frequency: %.1f%%)" % (100 * native_chance) ) native_potential_planet_total = sum(potential_native_planet_summary.values()) for species in sorted(species_summary): if species_summary[species] > 0: settleable_planets = 0 expectation_tally = 0.0 for p_type in natives.planet_types_for_natives[species]: settleable_planets += potential_native_planet_summary[p_type] expectation_tally += ( native_chance * 100.0 * potential_native_planet_summary[p_type] / (1E-10 + len(natives.natives_for_planet_type[p_type])) ) expectation = expectation_tally / (1E-10 + settleable_planets) native_table.add_row( [species, species_summary[species], 100.0 * species_summary[species] / (1E-10 + native_potential_planet_total), 100.0 * species_summary[species] / (1E-10 + settleable_planets), expectation, [str(p_t) for p_t in natives.planet_types_for_natives[species]]] ) print native_table print native_settled_planet_total = sum(settled_native_planet_summary.values()) type_summary_table = Table( [Text('planet type'), Float('potential (% of tot)', precession=1), Float('settled (% of potential)', precession=1)], table_name=("Planet Type Summary for Native Planets (native frequency: %.1f%%)\n" "Totals: native_potential_planet_total: %s; native_settled_planet_total %s" ) % (100 * native_chance, native_potential_planet_total, native_settled_planet_total) ) for planet_type, planet_count in sorted(potential_native_planet_summary.items()): settled_planet_count = settled_native_planet_summary.get(planet_type, 0) potential_percent = 100.0 * planet_count / (1E-10 + native_potential_planet_total) settled_percent = 100.0 * settled_planet_count / (1E-10 + planet_count) type_summary_table.add_row((planet_type.name, potential_percent, settled_percent)) print type_summary_table print
def __clean_fleet_roles(self, just_resumed=False): """Removes fleetRoles if a fleet has been lost, and update fleet Ratings.""" universe = fo.getUniverse() current_empire_fleets = FleetUtilsAI.get_empire_fleet_ids() self.shipCount = 0 destroyed_object_ids = universe.destroyedObjectIDs(fo.empireID()) fleet_table = Table([ Text('Fleet'), Float('Rating'), Float('Troops'), Text('Location'), Text('Destination')], table_name="Fleet Summary Turn %d" % fo.currentTurn() ) # need to loop over a copy as entries are deleted in loop for fleet_id in list(self.__fleetRoleByID): fleet_status = self.fleetStatus.setdefault(fleet_id, {}) rating = CombatRatingsAI.get_fleet_rating(fleet_id, self.get_standard_enemy()) old_sys_id = fleet_status.get('sysID', -2) # TODO: Introduce helper function instead fleet = universe.getFleet(fleet_id) if fleet: sys_id = fleet.systemID if old_sys_id in [-2, -1]: old_sys_id = sys_id fleet_status['nships'] = len(fleet.shipIDs) # TODO: Introduce helper function instead self.shipCount += fleet_status['nships'] else: # can still retrieve a fleet object even if fleet was just destroyed, so shouldn't get here # however,this has been observed happening, and is the reason a fleet check was added a few lines below. # Not at all sure how this came about, but was throwing off threat assessments sys_id = old_sys_id # check if fleet is destroyed and if so, delete stored information if fleet_id not in current_empire_fleets: # or fleet.empty: # TODO(Morlic): Is this condition really correct? Seems like should actually be in destroyed object ids if (fleet and self.__fleetRoleByID.get(fleet_id, -1) != -1 and fleet_id not in destroyed_object_ids and any(ship_id not in destroyed_object_ids for ship_id in fleet.shipIDs)): if not just_resumed: fleetsLostBySystem.setdefault(old_sys_id, []).append(max(rating, MilitaryAI.MinThreat)) self.delete_fleet_info(fleet_id) continue # if reached here, the fleet does still exist this_sys = universe.getSystem(sys_id) next_sys = universe.getSystem(fleet.nextSystemID) fleet_table.add_row([ fleet, rating, FleetUtilsAI.count_troops_in_fleet(fleet_id), this_sys or 'starlane', next_sys or '-', ]) fleet_status['rating'] = rating if next_sys: fleet_status['sysID'] = next_sys.id elif this_sys: fleet_status['sysID'] = this_sys.id else: error("Fleet %s has no valid system." % fleet) info(fleet_table) # Next string used in charts. Don't modify it! debug("Empire Ship Count: %s" % self.shipCount) debug("Empire standard fighter summary: %s", (CombatRatingsAI.get_empire_standard_fighter().get_stats(), )) debug("------------------------")
def __clean_fleet_roles(self, just_resumed=False): """Removes fleetRoles if a fleet has been lost, and update fleet Ratings.""" universe = fo.getUniverse() current_empire_fleets = FleetUtilsAI.get_empire_fleet_ids() self.shipCount = 0 destroyed_object_ids = universe.destroyedObjectIDs(fo.empireID()) fleet_table = Table([ Text('Fleet'), Float('Rating'), Float('Troops'), Text('Location'), Text('Destination')], table_name="Fleet Summary Turn %d" % fo.currentTurn() ) # need to loop over a copy as entries are deleted in loop for fleet_id in list(self.__fleetRoleByID): fleet_status = self.fleetStatus.setdefault(fleet_id, {}) rating = CombatRatingsAI.get_fleet_rating(fleet_id, self.get_standard_enemy()) old_sys_id = fleet_status.get('sysID', -2) # TODO: Introduce helper function instead fleet = universe.getFleet(fleet_id) if fleet: sys_id = fleet.systemID if old_sys_id in [-2, -1]: old_sys_id = sys_id fleet_status['nships'] = len(fleet.shipIDs) # TODO: Introduce helper function instead self.shipCount += fleet_status['nships'] else: # can still retrieve a fleet object even if fleet was just destroyed, so shouldn't get here # however,this has been observed happening, and is the reason a fleet check was added a few lines below. # Not at all sure how this came about, but was throwing off threat assessments sys_id = old_sys_id # check if fleet is destroyed and if so, delete stored information if fleet_id not in current_empire_fleets: # or fleet.empty: # TODO(Morlic): Is this condition really correct? Seems like should actually be in destroyed object ids if (fleet and self.__fleetRoleByID.get(fleet_id, -1) != -1 and fleet_id not in destroyed_object_ids and any(ship_id not in destroyed_object_ids for ship_id in fleet.shipIDs)): if not just_resumed: fleetsLostBySystem.setdefault(old_sys_id, []).append(max(rating, MilitaryAI.MinThreat)) self.delete_fleet_info(fleet_id) continue # if reached here, the fleet does still exist this_sys = universe.getSystem(sys_id) next_sys = universe.getSystem(fleet.nextSystemID) fleet_table.add_row([ fleet, rating, FleetUtilsAI.count_troops_in_fleet(fleet_id), this_sys or 'starlane', next_sys or '-', ]) fleet_status['rating'] = rating if next_sys: fleet_status['sysID'] = next_sys.id elif this_sys: fleet_status['sysID'] = this_sys.id else: error("Fleet %s has no valid system." % fleet) info(fleet_table) # Next string used in charts. Don't modify it! print "Empire Ship Count: ", self.shipCount print "Empire standard fighter summary: ", CombatRatingsAI.get_empire_standard_fighter().get_stats() print "------------------------"
def test_simple_table(): t = Table( [Text('name', description='Name for first column'), Float('value', description='VValue'), Sequence('zzz'), Sequence('zzzzzzzzzzzzzzzzzz')], table_name='Wooho') t.add_row(['hello', 144444, 'abcffff', 'a']) t.add_row([u'Plato aa\u03b2 III', 21, 'de', 'a']) t.add_row([u'Plato \u03b2 III', 21, 'de', 'a']) t.add_row(['Plato B III', 21, 'd', 'a']) t.add_row(['Plato Bddddd III', 21, 'd', 'a']) t.add_row(['Plato III', 21, 'd', 'a']) assert t.get_table() == EXPECTED_SIMPLE_TABLE
def make_table(): t = Table( Text("name", description="Name for first column"), Number("value", description="VValue"), Sequence("zzz"), Sequence("zzzzzzzzzzzzzzzzzz"), table_name="Wooho", ) t.add_row("hello", 144444, "abcffff", "a") t.add_row("Plato aa\u03b2 III", 21, "de", "a") t.add_row("Plato \u03b2 III", 21, "de", "a") t.add_row("Plato B III", 21, "d", "a") t.add_row("Plato Bddddd III", 21, "d", "a") t.add_row("Plato III", 21, "d", "a") return t
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() fleet_list = sorted(list(self.__fleetRoleByID)) ship_count = 0 destroyed_object_ids = universe.destroyedObjectIDs(fo.empireID()) fleet_table = Table([ Text('Fleet'), Float('Rating'), Float('Troops'), Text('Location'), Text('Destination')], table_name="Fleet Summary Turn %d" % fo.currentTurn() ) for fleet_id in fleet_list: status = self.fleetStatus.setdefault(fleet_id, {}) rating = CombatRatingsAI.get_fleet_rating(fleet_id, self.get_standard_enemy()) troops = FleetUtilsAI.count_troops_in_fleet(fleet_id) old_sys_id = status.get('sysID', -2) fleet = universe.getFleet(fleet_id) if fleet: sys_id = fleet.systemID if old_sys_id in [-2, -1]: old_sys_id = sys_id status['nships'] = len(fleet.shipIDs) ship_count += status['nships'] else: sys_id = old_sys_id # can still retrieve a fleet object even if fleet was just destroyed, so shouldn't get here # however,this has been observed happening, and is the reason a fleet check was added a few lines below. # Not at all sure how this came about, but was throwing off threat assessments if fleet_id not in ok_fleets: # or fleet.empty: if (fleet and self.__fleetRoleByID.get(fleet_id, -1) != -1 and fleet_id not in destroyed_object_ids and [ship_id for ship_id in fleet.shipIDs if ship_id not in destroyed_object_ids]): if not just_resumed: fleetsLostBySystem.setdefault(old_sys_id, []).append(max(rating, MilitaryAI.MinThreat)) if fleet_id in self.__fleetRoleByID: del self.__fleetRoleByID[fleet_id] if fleet_id in self.__aiMissionsByFleetID: del self.__aiMissionsByFleetID[fleet_id] if fleet_id in self.fleetStatus: del self.fleetStatus[fleet_id] continue else: # fleet in ok fleets this_sys = universe.getSystem(sys_id) next_sys = universe.getSystem(fleet.nextSystemID) fleet_table.add_row( [ fleet, rating, troops, this_sys or 'starlane', next_sys or '-', ]) status['rating'] = rating if next_sys: status['sysID'] = next_sys.id elif this_sys: status['sysID'] = this_sys.id else: main_mission = self.get_fleet_mission(fleet_id) main_mission_type = (main_mission.getAIMissionTypes() + [-1])[0] if main_mission_type != -1: targets = main_mission.getAITargets(main_mission_type) if targets: m_mt0 = targets[0] if isinstance(m_mt0.target_type, System): status['sysID'] = m_mt0.target.id # hmm, but might still be a fair ways from here fleet_table.print_table() self.shipCount = ship_count # Next string used in charts. Don't modify it! print "Empire Ship Count: ", ship_count print "Empire standard fighter summary: ", CombatRatingsAI.get_empire_standard_fighter().get_stats() print "------------------------"
def get_invasion_fleets(): invasion_timer.start("gathering initial info") universe = fo.getUniverse() empire = fo.getEmpire() empire_id = fo.empireID() home_system_id = PlanetUtilsAI.get_capital_sys_id() aistate = get_aistate() visible_system_ids = list(aistate.visInteriorSystemIDs) + list( aistate.visBorderSystemIDs) if home_system_id != INVALID_ID: accessible_system_ids = [ sys_id for sys_id in visible_system_ids if systems_connected(sys_id, home_system_id) ] else: debug( "Empire has no identifiable homeworld; will treat all visible planets as accessible." ) # TODO: check if any troop ships owned, use their system as home system accessible_system_ids = visible_system_ids acessible_planet_ids = PlanetUtilsAI.get_planets_in__systems_ids( accessible_system_ids) all_owned_planet_ids = PlanetUtilsAI.get_all_owned_planet_ids( acessible_planet_ids) # includes unpopulated outposts all_populated_planets = PlanetUtilsAI.get_populated_planet_ids( acessible_planet_ids) # includes unowned natives empire_owned_planet_ids = PlanetUtilsAI.get_owned_planets_by_empire( universe.planetIDs) invadable_planet_ids = set(all_owned_planet_ids).union( all_populated_planets) - set(empire_owned_planet_ids) invasion_targeted_planet_ids = get_invasion_targeted_planet_ids( universe.planetIDs, MissionType.INVASION) invasion_targeted_planet_ids.extend( get_invasion_targeted_planet_ids(universe.planetIDs, MissionType.ORBITAL_INVASION)) all_invasion_targeted_system_ids = set( PlanetUtilsAI.get_systems(invasion_targeted_planet_ids)) invasion_fleet_ids = FleetUtilsAI.get_empire_fleet_ids_by_role( MissionType.INVASION) num_invasion_fleets = len( FleetUtilsAI.extract_fleet_ids_without_mission_types( invasion_fleet_ids)) debug("Current Invasion Targeted SystemIDs: %s" % PlanetUtilsAI.sys_name_ids(AIstate.invasionTargetedSystemIDs)) debug("Current Invasion Targeted PlanetIDs: %s" % PlanetUtilsAI.planet_string(invasion_targeted_planet_ids)) debug(invasion_fleet_ids and "Invasion Fleet IDs: %s" % invasion_fleet_ids or "Available Invasion Fleets: 0") debug("Invasion Fleets Without Missions: %s" % num_invasion_fleets) invasion_timer.start("planning troop base production") reserved_troop_base_targets = [] if aistate.character.may_invade_with_bases(): 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 # For planning base trooper invasion targets we have a two-pass system. (1) In the first pass we consider all # the invasion targets and figure out which ones appear to be suitable for using base troopers against (i.e., we # already have a populated planet in the same system that could build base troopers) and we have at least a # minimal amount of PP available, and (2) in the second pass we go through the reserved base trooper target list # and check to make sure that there does not appear to be too much military action still needed before the # target is ready to be invaded, we double check that not too many base troopers would be needed, and if things # look clear then we queue up the base troopers on the Production Queue and keep track of where we are building # them, and how many; we may also disqualify and remove previously qualified targets (in case, for example, # we lost our base trooper source planet since it was first added to list). # # For planning and tracking base troopers under construction, we use a dictionary store in # get_aistate().qualifyingTroopBaseTargets, keyed by the invasion target planet ID. We only store values # for invasion targets that appear likely to be suitable for base trooper use, and store a 2-item list. # The first item in this list is the ID of the planet where we expect to build the base troopers, and the second # entry initially is set to INVALID_ID (-1). The presence of this entry in qualifyingTroopBaseTargets # flags this target as being reserved as a base-trooper invasion target. # In the second pass, if/when we actually start construction, then we modify the record, replacing that second # value with the ID of the planet where the troopers are actually being built. (Right now that is always the # same as the source planet originally identified, but we could consider reevaluating that, or use that second # value to instead record how many base troopers have been queued, so that on later turns we can assess if the # process got delayed & perhaps more troopers need to be queued). # Pass 1: identify qualifying base troop invasion targets for pid in invadable_planet_ids: # TODO: reorganize if pid in aistate.qualifyingTroopBaseTargets: continue planet = universe.getPlanet(pid) if not planet: continue sys_id = planet.systemID sys_partial_vis_turn = get_partial_visibility_turn(sys_id) planet_partial_vis_turn = get_partial_visibility_turn(pid) if planet_partial_vis_turn < sys_partial_vis_turn: continue best_base_planet = INVALID_ID best_trooper_count = 0 for pid2 in get_colonized_planets_in_system(sys_id): if available_pp.get( pid2, 0 ) < 2: # TODO: improve troop base PP sufficiency determination break planet2 = universe.getPlanet(pid2) if not planet2 or not can_build_ship_for_species( planet2.speciesName): continue best_base_trooper_here = get_best_ship_info( PriorityType.PRODUCTION_ORBITAL_INVASION, pid2)[1] if not best_base_trooper_here: continue troops_per_ship = best_base_trooper_here.troopCapacity if not troops_per_ship: continue species_troop_grade = get_species_tag_grade( planet2.speciesName, Tags.ATTACKTROOPS) troops_per_ship = CombatRatingsAI.weight_attack_troops( troops_per_ship, species_troop_grade) if troops_per_ship > best_trooper_count: best_base_planet = pid2 best_trooper_count = troops_per_ship if best_base_planet != INVALID_ID: aistate.qualifyingTroopBaseTargets.setdefault( pid, [best_base_planet, INVALID_ID]) # Pass 2: for each target previously identified for base troopers, check that still qualifies and # check how many base troopers would be needed; if reasonable then queue up the troops and record this in # get_aistate().qualifyingTroopBaseTargets for pid in list(aistate.qualifyingTroopBaseTargets.keys()): planet = universe.getPlanet(pid) if planet and planet.owner == empire_id: del aistate.qualifyingTroopBaseTargets[pid] continue if pid in invasion_targeted_planet_ids: # TODO: consider overriding standard invasion mission continue if aistate.qualifyingTroopBaseTargets[pid][1] != -1: reserved_troop_base_targets.append(pid) if planet: all_invasion_targeted_system_ids.add(planet.systemID) # TODO: evaluate changes to situation, any more troops needed, etc. continue # already building for here _, planet_troops = evaluate_invasion_planet(pid) sys_id = planet.systemID this_sys_status = aistate.systemStatus.get(sys_id, {}) troop_tally = 0 for _fid in this_sys_status.get("myfleets", []): troop_tally += FleetUtilsAI.count_troops_in_fleet(_fid) if troop_tally > planet_troops: # base troopers appear unneeded del aistate.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) 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 continue loc = aistate.qualifyingTroopBaseTargets[pid][0] best_base_trooper_here = get_best_ship_info( PriorityType.PRODUCTION_ORBITAL_INVASION, loc)[1] loc_planet = universe.getPlanet(loc) if best_base_trooper_here is None: # shouldn't be possible at this point, but just to be safe warning( "Could not find a suitable orbital invasion design at %s" % loc_planet) continue # TODO: have TroopShipDesigner give the expected number of troops including species effects directly troops_per_ship = best_base_trooper_here.troopCapacity species_troop_grade = get_species_tag_grade( loc_planet.speciesName, Tags.ATTACKTROOPS) troops_per_ship = CombatRatingsAI.weight_attack_troops( troops_per_ship, species_troop_grade) if not troops_per_ship: warning( "The best orbital invasion design at %s seems not to have any troop capacity." % loc_planet) continue _, col_design, build_choices = get_best_ship_info( PriorityType.PRODUCTION_ORBITAL_INVASION, loc) if not col_design: continue if loc not in build_choices: warning( "Best troop design %s can not be produced at planet with id: %s" % (col_design, build_choices)) continue n_bases = math.ceil( (planet_troops + 1) / troops_per_ship) # TODO: reconsider this +1 safety factor # TODO: evaluate cost and time-to-build of best base trooper here versus cost and time-to-build-and-travel # for best regular trooper elsewhere # For now, we assume what building base troopers is best so long as either (1) we would need no more than # MAX_BASE_TROOPERS_POOR_INVADERS base troop ships, or (2) our base troopers have more than 1 trooper per # ship and we would need no more than MAX_BASE_TROOPERS_GOOD_INVADERS base troop ships if n_bases > MAX_BASE_TROOPERS_POOR_INVADERS or ( troops_per_ship > 1 and n_bases > MAX_BASE_TROOPERS_GOOD_INVADERS): debug( "ruling out base invasion troopers for %s due to high number (%d) required." % (planet, n_bases)) del aistate.qualifyingTroopBaseTargets[pid] continue debug( "Invasion base planning, need %d troops at %d per ship, will build %d ships." % ((planet_troops + 1), troops_per_ship, n_bases)) retval = fo.issueEnqueueShipProductionOrder(col_design.id, loc) debug("Enqueueing %d Troop Bases at %s for %s" % (n_bases, PlanetUtilsAI.planet_string(loc), PlanetUtilsAI.planet_string(pid))) if retval != 0: all_invasion_targeted_system_ids.add(planet.systemID) reserved_troop_base_targets.append(pid) aistate.qualifyingTroopBaseTargets[pid][1] = loc fo.issueChangeProductionQuantityOrder( empire.productionQueue.size - 1, 1, int(n_bases)) 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 evaluated_planet_ids = list( set(invadable_planet_ids) - set(invasion_targeted_planet_ids) - set(reserved_troop_base_targets)) evaluated_planets = assign_invasion_values(evaluated_planet_ids) sorted_planets = [(pid, pscore % 10000, ptroops) for pid, (pscore, ptroops) in evaluated_planets.items()] sorted_planets.sort(key=lambda x: x[1], reverse=True) sorted_planets = [(pid, pscore % 10000, ptroops) for pid, pscore, ptroops in sorted_planets] invasion_table = Table( Text("Planet"), Number("Score"), Text("Species"), Number("Troops"), table_name="Potential Targets for Invasion Turn %d" % fo.currentTurn(), ) for pid, pscore, ptroops in sorted_planets: planet = universe.getPlanet(pid) invasion_table.add_row(planet, pscore, planet and planet.speciesName or "unknown", ptroops) invasion_table.print_table(info) sorted_planets = [x for x in sorted_planets if x[1] > 0] # export opponent planets for other AI modules AIstate.opponentPlanetIDs = [pid for pid, __, __ in sorted_planets] AIstate.invasionTargets = sorted_planets # 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(evaluated_planet_ids))) invasion_timer.stop_print_and_clear()
def get_invasion_fleets(): invasion_timer.start("gathering initial info") universe = fo.getUniverse() empire = fo.getEmpire() empire_id = empire.empireID all_invasion_fleet_ids = FleetUtilsAI.get_empire_fleet_ids_by_role(MissionType.INVASION) AIstate.invasionFleetIDs = FleetUtilsAI.extract_fleet_ids_without_mission_types(all_invasion_fleet_ids) home_system_id = PlanetUtilsAI.get_capital_sys_id() 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 "Warning: Empire has no identifiable homeworld; will treat all visible planets as accessible." accessible_system_ids = visible_system_ids # TODO: check if any troop ships owned, use their system as home system acessible_planet_ids = PlanetUtilsAI.get_planets_in__systems_ids(accessible_system_ids) all_owned_planet_ids = PlanetUtilsAI.get_all_owned_planet_ids(acessible_planet_ids) # includes unpopulated outposts all_populated_planets = PlanetUtilsAI.get_populated_planet_ids(acessible_planet_ids) # includes unowned natives empire_owned_planet_ids = PlanetUtilsAI.get_owned_planets_by_empire(universe.planetIDs) invadable_planet_ids = set(all_owned_planet_ids).union(all_populated_planets) - set(empire_owned_planet_ids) invasion_targeted_planet_ids = get_invasion_targeted_planet_ids(universe.planetIDs, MissionType.INVASION) invasion_targeted_planet_ids.extend(get_invasion_targeted_planet_ids(universe.planetIDs, MissionType.ORBITAL_INVASION)) all_invasion_targeted_system_ids = set(PlanetUtilsAI.get_systems(invasion_targeted_planet_ids)) invasion_fleet_ids = FleetUtilsAI.get_empire_fleet_ids_by_role(MissionType.INVASION) num_invasion_fleets = len(FleetUtilsAI.extract_fleet_ids_without_mission_types(invasion_fleet_ids)) print "Current Invasion Targeted SystemIDs: ", PlanetUtilsAI.sys_name_ids(AIstate.invasionTargetedSystemIDs) print "Current Invasion Targeted PlanetIDs: ", PlanetUtilsAI.planet_name_ids(invasion_targeted_planet_ids) print invasion_fleet_ids and "Invasion Fleet IDs: %s" % invasion_fleet_ids or "Available Invasion Fleets: 0" print "Invasion Fleets Without Missions: %s" % 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 for pid in invadable_planet_ids: # TODO: reorganize planet = universe.getPlanet(pid) if not planet: continue sys_id = planet.systemID sys_partial_vis_turn = universe.getVisibilityTurnsMap(planet.systemID, empire_id).get(fo.visibility.partial, -9999) planet_partial_vis_turn = universe.getVisibilityTurnsMap(pid, empire_id).get(fo.visibility.partial, -9999) if planet_partial_vis_turn < sys_partial_vis_turn: continue for pid2 in state.get_empire_inhabited_planets_by_system().get(sys_id, []): if available_pp.get(pid2, 0) < 2: # TODO: improve troop base PP sufficiency determination break planet2 = universe.getPlanet(pid2) if not planet2: continue if pid not in foAI.foAIstate.qualifyingTroopBaseTargets and planet2.speciesName in ColonisationAI.empire_ship_builders: 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] secure_ai_fleet_missions = foAI.foAIstate.get_fleet_missions_with_any_mission_types([MissionType.SECURE]) 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] best_base_trooper_here = ProductionAI.get_best_ship_info(PriorityType.PRODUCTION_ORBITAL_INVASION, loc)[1] loc_planet = universe.getPlanet(loc) if best_base_trooper_here is None: # shouldn't be possible at this point, but just to be safe print "Could not find a suitable orbital invasion design at %s" % loc_planet continue # TODO: have TroopShipDesigner give the expected number of troops including species effects directly troops_per_ship = best_base_trooper_here.troopCapacity species_troop_grade = CombatRatingsAI.get_species_troops_grade(loc_planet.speciesName) troops_per_ship = CombatRatingsAI.weight_attack_troops(troops_per_ship, species_troop_grade) if not troops_per_ship: print "The best orbital invasion design at %s seems not to have any troop capacity." % loc_planet continue this_score, p_troops = evaluate_invasion_planet(pid, empire, secure_ai_fleet_missions, False) _, col_design, build_choices = ProductionAI.get_best_ship_info(PriorityType.PRODUCTION_ORBITAL_INVASION, loc) if not col_design: continue if loc not in build_choices: sys.stderr.write( 'Best troop design %s can not be produces in at planet with id: %s\d' % (col_design, build_choices) ) n_bases = math.ceil((p_troops + 1) / troops_per_ship) # TODO: reconsider this +1 safety factor print "Invasion base planning, need %d troops at %d pership, will build %d ships." % ((p_troops + 1), troops_per_ship, n_bases) retval = fo.issueEnqueueShipProductionOrder(col_design.id, 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)) 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 evaluated_planet_ids = list(set(invadable_planet_ids) - set(invasion_targeted_planet_ids) - set(reserved_troop_base_targets)) evaluated_planets = assign_invasion_values(evaluated_planet_ids, empire) sorted_planets = [(pid, pscore % 10000, ptroops) for pid, (pscore, ptroops) in evaluated_planets.items()] sorted_planets.sort(key=lambda x: x[1], reverse=True) sorted_planets = [(pid, pscore % 10000, ptroops) for pid, pscore, ptroops in sorted_planets] invasion_table = Table([Text('Planet'), Float('Score'), Text('Species'), Float('Troops')], table_name="Potential Targets for Invasion Turn %d" % fo.currentTurn()) for pid, pscore, ptroops in sorted_planets: planet = universe.getPlanet(pid) invasion_table.add_row([ planet, pscore, planet and planet.speciesName or "unknown", ptroops ]) print invasion_table.print_table() sorted_planets = filter(lambda x: x[1] > 0, sorted_planets) # export opponent planets for other AI modules AIstate.opponentPlanetIDs = [pid for pid, _, _ in sorted_planets] AIstate.invasionTargets = sorted_planets # 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(evaluated_planet_ids))) invasion_timer.end()