Esempio n. 1
0
    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),
            )
        foci_table.print_table(info)
        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():
            warning(
                "Population Warning! -- %s has unsustainable current pop %d -- target %d",
                name, cp, mp)
Esempio n. 2
0
    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 = {}
        for priority_type in get_priority_resource_types():
            resource_priorities[priority_type] = foAI.foAIstate.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)
            ])
        foci_table.print_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)
Esempio n. 3
0
 def __init__(self, status_only: bool = False):
     self._empire = fo.getEmpire()
     self._universe = fo.getUniverse()
     self._aistate = get_aistate()
     # resourceAvailable includes this turns production, but that is wrong for influence
     self._ip = self._empire.resourceAvailable(
         fo.resourceType.influence) - self._get_infl_prod()
     self._adopted = set(self._empire.adoptedPolicies)
     # When we continue a game in which we just adopted a policy, game state shows the policy as adopted,
     # but IP still unspent. Correct it here, then calculate anew whether we want to adopt it.
     if not status_only:
         for entry in self._empire.turnsPoliciesAdopted:
             if entry.data() == fo.currentTurn():
                 debug(f"reverting saved adopt {entry.key()}")
                 fo.issueDeadoptPolicyOrder(entry.key())
                 self._adopted.remove(entry.key())
     self._originally_adopted = copy(self._adopted)
     empire_owned_planet_ids = PlanetUtilsAI.get_owned_planets_by_empire()
     self._populated_planet_ids = PlanetUtilsAI.get_populated_planet_ids(
         empire_owned_planet_ids)
     self._num_populated = len(self._populated_planet_ids)
     self._num_outposts = len(empire_owned_planet_ids) - self._num_populated
     self._max_turn_bureaucracy = self._calculate_max_turn_bureaucracy()
     self._centralization_cost = fo.getPolicy(centralization).adoptionCost()
     self._bureaucracy_cost = fo.getPolicy(bureaucracy).adoptionCost()
     self._wanted_ip = self._wanted_for_bureaucracy()
     self._adoptable = self._get_adoptable()
     self._available = set(self._empire.availablePolicies)
     self._rating_functions = {
         propaganda: lambda: 20 + self._rate_opinion(propaganda),
         algo_research: self._rate_algo_research,
         diversity: self._rate_diversity,
         artisans: self._rate_artisans,
         population: self._rate_population,
         # military policies are mostly chosen by opinion, plus some rule-of-thumb values
         allied_repair: lambda: self._rate_opinion(
             charge),  # no effect, unless we have allies...
         charge:
         lambda: 5 + self._rate_opinion(charge),  # A small bonus in battle
         scanning: lambda: 20 + self._rate_opinion(
             scanning),  # May help us detect ships and planets
         simplicity: lambda: 20 + self._rate_opinion(
             simplicity),  # Makes simple ships cheaper
         engineering: self._rate_engineering_corps,
         exploration: lambda: 10 + self._rate_opinion(
             exploration),  # may give a little research, speeds up scouts
         flanking: lambda: 5 + self._rate_opinion(
             flanking),  # A small bonus in battle
         recruitment: lambda: 15 + self._rate_opinion(
             recruitment),  # cheaper troop ships
     }
Esempio n. 4
0
def print_resources_priority():
    """calculate top resource priority"""
    universe = fo.getUniverse()
    empire = fo.getEmpire()
    empirePlanetIDs = PlanetUtilsAI.get_owned_planets_by_empire(
        universe.planetIDs)
    print "Resource Management:"
    print
    print "Resource Priorities:"
    resourcePriorities = {}
    for priorityType in get_priority_resource_types():
        resourcePriorities[priorityType] = foAI.foAIstate.get_priority(
            priorityType)

    sortedPriorities = resourcePriorities.items()
    sortedPriorities.sort(lambda x, y: cmp(x[1], y[1]), reverse=True)
    topPriority = -1
    for evaluationPair in sortedPriorities:
        if topPriority < 0:
            topPriority = evaluationPair[0]
        print "    ResourcePriority |Score: %s | %s " % (
            AIPriorityTypeNames.name(evaluationPair[0]), evaluationPair[1])

    # what is the focus of available resource centers?
    print
    warnings = {}
    print "Planet Resources Foci:"
    for planetID in empirePlanetIDs:
        planet = universe.getPlanet(planetID)
        planetPopulation = planet.currentMeterValue(fo.meterType.population)
        maxPop = planet.currentMeterValue(fo.meterType.targetPopulation)
        if maxPop < 1 and planetPopulation > 0:
            warnings[planet.name] = (planetPopulation, maxPop)
        statusStr = "  ID: " + str(planetID) + " Name: % 18s -- % 6s % 8s " % (
            str(planet.name), str(planet.size), str(planet.type))
        statusStr += " Focus: % 8s" % ("_".join(
            str(planet.focus).split("_")[1:])[:8]) + " Species: " + str(
                planet.speciesName) + " Pop: %2d/%2d" % (planetPopulation,
                                                         maxPop)
        print statusStr
    print "\n\nEmpire Totals:\nPopulation: %5d \nProduction: %5d\nResearch: %5d\n" % (
        empire.population(), empire.productionPoints,
        empire.resourceProduction(fo.resourceType.research))
    if warnings != {}:
        for pname in warnings:
            mp, cp = warnings[pname]
            print "Population Warning! -- %s has unsustainable current pop %d -- target %d" % (
                pname, cp, mp)
        print
    warnings.clear()
Esempio n. 5
0
    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)
Esempio n. 6
0
    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 Management:"
        print
        print "Resource Priorities:"
        resource_priorities = {}
        for priority_type in get_priority_resource_types():
            resource_priorities[priority_type] = foAI.foAIstate.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 "    ResourcePriority |Score: %s | %s " % (
                evaluation_priority, evaluation_score)

        # what is the focus of available resource centers?
        print
        warnings = {}
        # TODO combine this with previous table to reduce report duplication?
        print "Planet Resources Foci:"
        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)
            print "  ID: %d Name: % 18s -- % 6s % 8s  Focus: % 8s Species: %s Pop: %2d/%2d" % (
                pid, planet.name, planet.size, planet.type, "_".join(
                    str(planet.focus).split("_")[1:])[:8], planet.speciesName,
                population, max_population)
        print "\n\nEmpire Totals:\nPopulation: %5d \nProduction: %5d\nResearch: %5d\n" % (
            empire.population(), empire.productionPoints,
            empire.resourceProduction(fo.resourceType.research))
        if warnings != {}:
            for name in warnings:
                mp, cp = warnings[name]
                print "Population Warning! -- %s has unsustainable current pop %d -- target %d" % (
                    name, cp, mp)
            print
        warnings.clear()
Esempio n. 7
0
    def __init__(self):
        universe = fo.getUniverse()

        resource_timer.start("getPlanets")
        planet_ids = list(PlanetUtilsAI.get_owned_planets_by_empire(universe.planetIDs))

        resource_timer.start("Targets")
        self.all_planet_info = {pid: PlanetFocusInfo(universe.getPlanet(pid)) for pid in planet_ids}

        self.raw_planet_info = dict(self.all_planet_info)
        self.baked_planet_info = {}

        for pid, pinfo in self.raw_planet_info.items():
            if not pinfo.planet.availableFoci:
                self.baked_planet_info[pid] = self.raw_planet_info.pop(pid)
Esempio n. 8
0
    def __init__(self):
        universe = fo.getUniverse()

        resource_timer.start("getPlanets")
        planet_ids = list(PlanetUtilsAI.get_owned_planets_by_empire(universe.planetIDs))

        resource_timer.start("Targets")
        self.all_planet_info = {pid: PlanetFocusInfo(universe.getPlanet(pid)) for pid in planet_ids}

        self.raw_planet_info = dict(self.all_planet_info)
        self.baked_planet_info = {}

        for pid, info in self.raw_planet_info.items():
            if not info.planet.availableFoci:
                self.baked_planet_info[pid] = self.raw_planet_info.pop(pid)
Esempio n. 9
0
def _calculate_industry_priority():  # currently only used to print status
    """calculates the demand for industry"""
    universe = fo.getUniverse()
    empire = fo.getEmpire()
    # get current industry production & Target
    industry_production = empire.resourceProduction(fo.resourceType.industry)
    owned_planet_ids = PlanetUtilsAI.get_owned_planets_by_empire(universe.planetIDs)
    planets = map(universe.getPlanet, owned_planet_ids)
    target_pp = sum(map(lambda x: x.currentMeterValue(fo.meterType.targetIndustry), planets))

    # currently, previously set to 50 in calculatePriorities(), this is just for reporting
    industry_priority = foAI.foAIstate.get_priority(PriorityType.RESOURCE_PRODUCTION)

    print
    print "Industry Production (current/target) : ( %.1f / %.1f ) at turn %s" % (industry_production, target_pp, fo.currentTurn())
    print "Priority for Industry: %s" % industry_priority
    return industry_priority
Esempio n. 10
0
def _calculate_industry_priority():  # currently only used to print status
    """calculates the demand for industry"""
    universe = fo.getUniverse()
    empire = fo.getEmpire()
    # get current industry production & Target
    industry_production = empire.resourceProduction(fo.resourceType.industry)
    owned_planet_ids = PlanetUtilsAI.get_owned_planets_by_empire(universe.planetIDs)
    planets = map(universe.getPlanet, owned_planet_ids)
    target_pp = sum(map(lambda x: x.currentMeterValue(fo.meterType.targetIndustry), planets))

    # currently, previously set to 50 in calculatePriorities(), this is just for reporting
    industry_priority = foAI.foAIstate.get_priority(PriorityType.RESOURCE_PRODUCTION)

    print
    print "Industry Production (current/target) : ( %.1f / %.1f ) at turn %s" % (industry_production, target_pp, fo.currentTurn())
    print "Priority for Industry: %s" % industry_priority
    return industry_priority
Esempio n. 11
0
def _calculate_industry_priority():  # currently only used to print status
    """calculates the demand for industry"""
    universe = fo.getUniverse()
    empire = fo.getEmpire()
    # get current industry production & Target
    industry_production = empire.resourceProduction(fo.resourceType.industry)
    owned_planet_ids = PlanetUtilsAI.get_owned_planets_by_empire()
    planets = (universe.getPlanet(x) for x in owned_planet_ids)
    target_pp = sum(
        x.currentMeterValue(fo.meterType.targetIndustry) for x in planets)

    # currently, previously set to 50 in calculatePriorities(), this is just for reporting
    industry_priority = get_aistate().get_priority(
        PriorityType.RESOURCE_PRODUCTION)

    debug("")
    debug("Industry Production (current/target) : ( %.1f / %.1f ) at turn %s" %
          (industry_production, target_pp, fo.currentTurn()))
    debug("Priority for Industry: %s" % industry_priority)
    return industry_priority
Esempio n. 12
0
def print_resources_priority():
    """calculate top resource priority"""
    universe = fo.getUniverse()
    empire = fo.getEmpire()
    empirePlanetIDs = PlanetUtilsAI.get_owned_planets_by_empire(universe.planetIDs)
    print "Resource Management:"
    print
    print "Resource Priorities:"
    resourcePriorities = {}
    for priorityType in get_priority_resource_types():
        resourcePriorities[priorityType] = foAI.foAIstate.get_priority(priorityType)

    sortedPriorities = resourcePriorities.items()
    sortedPriorities.sort(lambda x, y: cmp(x[1], y[1]), reverse=True)
    topPriority = -1
    for evaluationPair in sortedPriorities:
        if topPriority < 0:
            topPriority = evaluationPair[0]
        print "    ResourcePriority |Score: %s | %s " % (AIPriorityTypeNames.name(evaluationPair[0]), evaluationPair[1])

    # what is the focus of available resource centers?
    print
    warnings = {}
    print "Planet Resources Foci:"
    for planetID in empirePlanetIDs:
        planet = universe.getPlanet(planetID)
        planetPopulation = planet.currentMeterValue(fo.meterType.population)
        maxPop = planet.currentMeterValue(fo.meterType.targetPopulation)
        if maxPop < 1 and planetPopulation > 0:
            warnings[planet.name] = (planetPopulation, maxPop)
        statusStr = "  ID: " + str(planetID) + " Name: % 18s -- % 6s % 8s " % (str(planet.name), str(planet.size), str(planet.type))
        statusStr += " Focus: % 8s" % ("_".join(str(planet.focus).split("_")[1:])[:8]) + " Species: " + str(planet.speciesName) + " Pop: %2d/%2d" % (planetPopulation, maxPop)
        print statusStr
    print "\n\nEmpire Totals:\nPopulation: %5d \nProduction: %5d\nResearch: %5d\n" % (empire.population(), empire.productionPoints, empire.resourceProduction(fo.resourceType.research))
    if warnings != {}:
        for pname in warnings:
            mp, cp = warnings[pname]
            print "Population Warning! -- %s has unsustainable current pop %d -- target %d" % (pname, cp, mp)
        print
    warnings.clear()
Esempio n. 13
0
    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 Management:"
        print
        print "Resource Priorities:"
        resource_priorities = {}
        for priority_type in get_priority_resource_types():
            resource_priorities[priority_type] = foAI.foAIstate.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 "    ResourcePriority |Score: %s | %s " % (evaluation_priority, evaluation_score)

        # what is the focus of available resource centers?
        print
        warnings = {}
        # TODO combine this with previous table to reduce report duplication?
        print "Planet Resources Foci:"
        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)
            print "  ID: %d Name: % 18s -- % 6s % 8s  Focus: % 8s Species: %s Pop: %2d/%2d" % (pid, planet.name, planet.size, planet.type, "_".join(str(planet.focus).split("_")[1:])[:8], planet.speciesName, population, max_population)
        print "\n\nEmpire Totals:\nPopulation: %5d \nProduction: %5d\nResearch: %5d\n" % (empire.population(), empire.productionPoints, empire.resourceProduction(fo.resourceType.research))
        if warnings != {}:
            for name in warnings:
                mp, cp = warnings[name]
                print "Population Warning! -- %s has unsustainable current pop %d -- target %d" % (name, cp, mp)
            print
        warnings.clear()
Esempio n. 14
0
    def __init__(self):
        universe = fo.getUniverse()

        resource_timer.start("getPlanets")
        planet_ids = list(PlanetUtilsAI.get_owned_planets_by_empire())

        resource_timer.start("Targets")
        self.all_planet_info = {
            pid: PlanetFocusInfo(universe.getPlanet(pid))
            for pid in planet_ids
        }

        self.raw_planet_info = dict(self.all_planet_info)
        self.baked_planet_info = {}
        for pid, pinfo in list(self.raw_planet_info.items()):
            if not pinfo.planet.availableFoci:
                self.baked_planet_info[pid] = self.raw_planet_info.pop(pid)
        aistate = get_aistate()
        self.priority = (
            aistate.get_priority(PriorityType.RESOURCE_PRODUCTION),
            aistate.get_priority(PriorityType.RESOURCE_RESEARCH),
            aistate.get_priority(PriorityType.RESOURCE_INFLUENCE),
        )
Esempio n. 15
0
def get_military_fleets(mil_fleets_ids=None, try_reset=True, thisround="Main"):
    """Get armed military fleets."""
    global _military_allocations

    universe = fo.getUniverse()
    empire_id = fo.empireID()
    home_system_id = PlanetUtilsAI.get_capital_sys_id()

    all_military_fleet_ids = (mil_fleets_ids if mil_fleets_ids is not None else
                              FleetUtilsAI.get_empire_fleet_ids_by_role(
                                  MissionType.MILITARY))

    # Todo: This block had been originally added to address situations where fleet missions were not properly
    #  terminating, leaving fleets stuck in stale deployments. Assess if this block is still needed at all; delete
    #  if not, otherwise restructure the following code so that in event a reset is occurring greater priority is given
    #  to providing military support to locations where a necessary Secure mission might have just been released (i.e.,
    #  at invasion and colony/outpost targets where the troopships and colony ships are on their way), or else allow
    #  only a partial reset which does not reset Secure missions.
    enable_periodic_mission_reset = False
    if enable_periodic_mission_reset and try_reset and (
            fo.currentTurn() + empire_id) % 30 == 0 and thisround == "Main":
        debug(
            "Resetting all Military missions as part of an automatic periodic reset to clear stale missions."
        )
        try_again(all_military_fleet_ids,
                  try_reset=False,
                  thisround=thisround + " Reset")
        return

    mil_fleets_ids = list(
        FleetUtilsAI.extract_fleet_ids_without_mission_types(
            all_military_fleet_ids))
    mil_needing_repair_ids, mil_fleets_ids = avail_mil_needing_repair(
        mil_fleets_ids, split_ships=True)
    avail_mil_rating = combine_ratings(
        get_fleet_rating(x) for x in mil_fleets_ids)

    if not mil_fleets_ids:
        if "Main" in thisround:
            _military_allocations = []
        return []

    # for each system, get total rating of fleets assigned to it
    already_assigned_rating = {}
    already_assigned_rating_vs_planets = {}
    aistate = get_aistate()
    systems_status = aistate.systemStatus
    enemy_sup_factor = {}  # enemy supply
    for sys_id in universe.systemIDs:
        already_assigned_rating[sys_id] = 0
        already_assigned_rating_vs_planets[sys_id] = 0
        enemy_sup_factor[sys_id] = min(
            2,
            len(
                systems_status.get(sys_id, {}).get("enemies_nearly_supplied",
                                                   [])))
    for fleet_id in [
            fid for fid in all_military_fleet_ids if fid not in mil_fleets_ids
    ]:
        ai_fleet_mission = aistate.get_fleet_mission(fleet_id)
        if not ai_fleet_mission.target:  # shouldn't really be possible
            continue
        last_sys = (
            ai_fleet_mission.target.get_system().id
        )  # will count this fleet as assigned to last system in target list  # TODO last_sys or target sys?
        this_rating = get_fleet_rating(fleet_id)
        this_rating_vs_planets = get_fleet_rating_against_planets(fleet_id)
        already_assigned_rating[last_sys] = combine_ratings(
            already_assigned_rating.get(last_sys, 0), this_rating)
        already_assigned_rating_vs_planets[last_sys] = combine_ratings(
            already_assigned_rating_vs_planets.get(last_sys, 0),
            this_rating_vs_planets)
    for sys_id in universe.systemIDs:
        my_defense_rating = systems_status.get(sys_id,
                                               {}).get("mydefenses",
                                                       {}).get("overall", 0)
        already_assigned_rating[sys_id] = combine_ratings(
            my_defense_rating, already_assigned_rating[sys_id])
        if _verbose_mil_reporting and already_assigned_rating[sys_id]:
            debug(
                "\t System %s already assigned rating %.1f" %
                (universe.getSystem(sys_id), already_assigned_rating[sys_id]))

    # get systems to defend
    capital_id = PlanetUtilsAI.get_capital()
    if capital_id is not None:
        capital_planet = universe.getPlanet(capital_id)
    else:
        capital_planet = None
    # TODO: if no owned planets try to capture one!
    if capital_planet:
        capital_sys_id = capital_planet.systemID
    else:  # should be rare, but so as to not break code below, pick a randomish mil-centroid system
        capital_sys_id = None  # unless we can find one to use
        system_dict = {}
        for fleet_id in all_military_fleet_ids:
            status = aistate.fleetStatus.get(fleet_id, None)
            if status is not None:
                system_id = status["sysID"]
                if not list(universe.getSystem(system_id).planetIDs):
                    continue
                system_dict[system_id] = system_dict.get(
                    system_id, 0) + status.get("rating", 0)
        ranked_systems = sorted([(val, sys_id)
                                 for sys_id, val in system_dict.items()])
        if ranked_systems:
            capital_sys_id = ranked_systems[-1][-1]
        else:
            try:
                capital_sys_id = next(iter(
                    aistate.fleetStatus.items()))[1]["sysID"]
            except:  # noqa: E722
                pass

    num_targets = max(10, PriorityAI.allotted_outpost_targets)
    top_target_planets = ([
        pid for pid, pscore, trp in
        AIstate.invasionTargets[:PriorityAI.allotted_invasion_targets()]
        if pscore > InvasionAI.MIN_INVASION_SCORE
    ] + [
        pid
        for pid, (pscore, spec) in list(aistate.colonisableOutpostIDs.items())
        [:num_targets] if pscore > InvasionAI.MIN_INVASION_SCORE
    ] + [
        pid
        for pid, (pscore, spec) in list(aistate.colonisablePlanetIDs.items())
        [:num_targets] if pscore > InvasionAI.MIN_INVASION_SCORE
    ])
    top_target_planets.extend(aistate.qualifyingTroopBaseTargets.keys())

    base_col_target_systems = PlanetUtilsAI.get_systems(top_target_planets)
    top_target_systems = []
    for sys_id in AIstate.invasionTargetedSystemIDs + base_col_target_systems:
        if sys_id not in top_target_systems:
            if aistate.systemStatus[sys_id][
                    "totalThreat"] > get_tot_mil_rating():
                continue
            top_target_systems.append(
                sys_id)  # doing this rather than set, to preserve order

    try:
        # capital defense
        allocation_helper = AllocationHelper(
            already_assigned_rating, already_assigned_rating_vs_planets,
            avail_mil_rating, try_reset)
        if capital_sys_id is not None:
            CapitalDefenseAllocator(capital_sys_id,
                                    allocation_helper).allocate()

        # defend other planets
        empire_planet_ids = PlanetUtilsAI.get_owned_planets_by_empire()
        empire_occupied_system_ids = list(
            set(PlanetUtilsAI.get_systems(empire_planet_ids)) -
            {capital_sys_id})
        for sys_id in empire_occupied_system_ids:
            PlanetDefenseAllocator(sys_id, allocation_helper).allocate()

        # attack / protect high priority targets
        for sys_id in top_target_systems:
            TopTargetAllocator(sys_id, allocation_helper).allocate()

        # enemy planets
        other_targeted_system_ids = [
            sys_id for sys_id in set(
                PlanetUtilsAI.get_systems(AIstate.opponentPlanetIDs))
            if sys_id not in top_target_systems
        ]
        for sys_id in other_targeted_system_ids:
            TargetAllocator(sys_id, allocation_helper).allocate()

        # colony / outpost targets
        other_targeted_system_ids = [
            sys_id for sys_id in list(
                set(AIstate.colonyTargetedSystemIDs +
                    AIstate.outpostTargetedSystemIDs))
            if sys_id not in top_target_systems
        ]
        for sys_id in other_targeted_system_ids:
            OutpostTargetAllocator(sys_id, allocation_helper).allocate()

        # TODO blockade enemy systems

        # interior systems
        targetable_ids = set(get_systems_by_supply_tier(0))
        current_mil_systems = [
            sid for sid, _, _, _, _ in allocation_helper.allocations
        ]
        interior_targets1 = targetable_ids.difference(current_mil_systems)
        interior_targets = [
            sid for sid in interior_targets1
            if (allocation_helper.threat_bias +
                systems_status.get(sid, {}).get("totalThreat", 0) > 0.8 *
                allocation_helper.already_assigned_rating[sid])
        ]
        for sys_id in interior_targets:
            InteriorTargetsAllocator(sys_id, allocation_helper).allocate()

        # TODO Exploration targets

        # border protections
        visible_system_ids = aistate.visInteriorSystemIDs | aistate.visBorderSystemIDs
        accessible_system_ids = ([
            sys_id for sys_id in visible_system_ids
            if systems_connected(sys_id, home_system_id)
        ] if home_system_id != INVALID_ID else [])
        current_mil_systems = [
            sid
            for sid, alloc, rvp, take_any, _ in allocation_helper.allocations
            if alloc > 0
        ]
        border_targets1 = [
            sid for sid in accessible_system_ids
            if sid not in current_mil_systems
        ]
        border_targets = [
            sid for sid in border_targets1
            if (allocation_helper.threat_bias +
                systems_status.get(sid, {}).get("fleetThreat", 0) +
                systems_status.get(sid, {}).get("planetThreat", 0) > 0.8 *
                allocation_helper.already_assigned_rating[sid])
        ]
        for sys_id in border_targets:
            BorderSecurityAllocator(sys_id, allocation_helper).allocate()
    except ReleaseMilitaryException:
        try_again(all_military_fleet_ids)
        return

    new_allocations = []
    remaining_mil_rating = avail_mil_rating
    # for top categories assign max_alloc right away as available
    for cat in ["capitol", "occupied", "topTargets"]:
        for sid, alloc, rvp, take_any, max_alloc in allocation_helper.allocation_by_groups.get(
                cat, []):
            if remaining_mil_rating <= 0:
                break
            this_alloc = min(remaining_mil_rating, max_alloc)
            new_allocations.append((sid, this_alloc, alloc, rvp, take_any))
            remaining_mil_rating = rating_difference(remaining_mil_rating,
                                                     this_alloc)

    base_allocs = set()
    # for lower priority categories, first assign base_alloc around to all, then top up as available
    for cat in ["otherTargets", "accessibleTargets", "exploreTargets"]:
        for sid, alloc, rvp, take_any, max_alloc in allocation_helper.allocation_by_groups.get(
                cat, []):
            if remaining_mil_rating <= 0:
                break
            alloc = min(remaining_mil_rating, alloc)
            base_allocs.add(sid)
            remaining_mil_rating = rating_difference(remaining_mil_rating,
                                                     alloc)
    for cat in ["otherTargets", "accessibleTargets", "exploreTargets"]:
        for sid, alloc, rvp, take_any, max_alloc in allocation_helper.allocation_by_groups.get(
                cat, []):
            if sid not in base_allocs:
                break
            if remaining_mil_rating <= 0:
                new_allocations.append((sid, alloc, alloc, rvp, take_any))
            else:
                local_max_avail = combine_ratings(remaining_mil_rating, alloc)
                new_rating = min(local_max_avail, max_alloc)
                new_allocations.append((sid, new_rating, alloc, rvp, take_any))
                remaining_mil_rating = rating_difference(
                    local_max_avail, new_rating)

    if "Main" in thisround:
        _military_allocations = new_allocations
    if _verbose_mil_reporting or "Main" in thisround:
        debug(
            "------------------------------\nFinal %s Round Military Allocations: %s \n-----------------------"
            % (thisround,
               dict([(sid, alloc)
                     for sid, alloc, _, _, _ in new_allocations])))
        debug("(Apparently) remaining military rating: %.1f" %
              remaining_mil_rating)

    return new_allocations
Esempio n. 16
0
def _calculate_research_priority():
    """Calculates the AI empire's demand for research."""
    universe = fo.getUniverse()
    empire = fo.getEmpire()
    current_turn = fo.currentTurn()
    enemies_sighted = foAI.foAIstate.misc.get('enemies_sighted', {})
    recent_enemies = [x for x in enemies_sighted if x > current_turn - 8]

    industry_priority = foAI.foAIstate.get_priority(PriorityType.RESOURCE_PRODUCTION)

    got_algo = tech_is_complete(AIDependencies.LRN_ALGO_ELEGANCE)
    got_quant = tech_is_complete(AIDependencies.LRN_QUANT_NET)
    research_queue_list = ResearchAI.get_research_queue_techs()
    orb_gen_tech = AIDependencies.PRO_ORBITAL_GEN
    got_orb_gen = tech_is_complete(orb_gen_tech)
    mgrav_prod_tech = AIDependencies.PRO_MICROGRAV_MAN
    got_mgrav_prod = tech_is_complete(mgrav_prod_tech)
    # got_solar_gen = tech_is_complete(AIDependencies.PRO_SOL_ORB_GEN)

    milestone_techs = ["PRO_SENTIENT_AUTOMATION", "LRN_DISTRIB_THOUGHT", "LRN_QUANT_NET", "SHP_WEAPON_2_4", "SHP_WEAPON_3_2", "SHP_WEAPON_4_2"]
    milestones_done = [mstone for mstone in milestone_techs if tech_is_complete(mstone)]
    print "Research Milestones accomplished at turn %d: %s" % (current_turn, milestones_done)

    total_pp = empire.productionPoints
    total_rp = empire.resourceProduction(fo.resourceType.research)
    industry_surge = (foAI.foAIstate.character.may_surge_industry(total_pp, total_rp) and
                      (((orb_gen_tech in research_queue_list[:2] or got_orb_gen) and state.have_gas_giant) or
                       ((mgrav_prod_tech in research_queue_list[:2] or got_mgrav_prod) and state.have_asteroids)) and
                      (state.get_number_of_colonies() < 12))
    # get current industry production & Target
    owned_planet_ids = PlanetUtilsAI.get_owned_planets_by_empire(universe.planetIDs)
    planets = map(universe.getPlanet, owned_planet_ids)
    target_rp = sum(map(lambda x: x.currentMeterValue(fo.meterType.targetResearch), planets))
    galaxy_is_sparse = ColonisationAI.galaxy_is_sparse()
    enemies_sighted = foAI.foAIstate.misc.get('enemies_sighted', {})

    style_index = foAI.foAIstate.character.preferred_research_cutoff([0, 1])
    if foAI.foAIstate.character.may_maximize_research():
        style_index += 1

    cutoff_sets = [[25, 45, 70, 110], [30, 45, 70, 150], [25, 40, 80, 160]]
    cutoffs = cutoff_sets[style_index]
    settings = [[1.3, .7, .5, .4, .35], [1.4, 0.8, 0.6, 0.5, 0.35], [1.4, 0.8, 0.6, 0.5, 0.4]][style_index]

    if (current_turn < cutoffs[0]) or (not got_algo) or ((style_index == 0) and not got_orb_gen and (current_turn < cutoffs[1])):
        research_priority = settings[0] * industry_priority  # high research at beginning of game to get easy gro tech and to get research booster Algotrithmic Elegance
    elif (not got_orb_gen) or (current_turn < cutoffs[1]):
        research_priority = settings[1] * industry_priority  # med-high research
    elif current_turn < cutoffs[2]:
        research_priority = settings[2] * industry_priority  # med-high industry
    elif current_turn < cutoffs[3]:
        research_priority = settings[3] * industry_priority  # med-high industry
    else:
        research_queue = list(empire.researchQueue)
        research_priority = settings[4] * industry_priority  # high industry , low research
        if len(research_queue) == 0:
            research_priority = 0  # done with research
        elif len(research_queue) < 5 and research_queue[-1].allocation > 0:
            research_priority = len(research_queue) * 0.01 * industry_priority  # barely not done with research
        elif len(research_queue) < 10 and research_queue[-1].allocation > 0:
            research_priority = (4 + 2 * len(research_queue)) * 0.01 * industry_priority  # almost done with research
        elif len(research_queue) < 20 and research_queue[int(len(research_queue) / 2)].allocation > 0:
            research_priority *= 0.7  # closing in on end of research
    if industry_surge:
        if galaxy_is_sparse and not any(enemies_sighted):
            research_priority *= 0.5
        else:
            research_priority *= 0.8

    if ((tech_is_complete("SHP_WEAPON_2_4") or
         tech_is_complete("SHP_WEAPON_4_1")) and
            tech_is_complete(AIDependencies.PROD_AUTO_NAME)):
        # industry_factor = [ [0.25, 0.2], [0.3, 0.25], [0.3, 0.25] ][style_index ]
        # researchPriority = min(researchPriority, industry_factor[got_solar_gen]*industryPriority)
        research_priority *= 0.9
    if got_quant:
        research_priority = min(research_priority + 0.1 * industry_priority, research_priority * 1.3)
    research_priority = int(research_priority)
    print "Research Production (current/target) : ( %.1f / %.1f )" % (total_rp, target_rp)
    print "Priority for Research: %d (new target ~ %d RP)" % (research_priority, total_pp * research_priority / industry_priority)

    if len(enemies_sighted) < (2 + current_turn/20.0):  # TODO: adjust for colonisation priority
        research_priority *= 1.2
    if (current_turn > 20) and (len(recent_enemies) > 3):
        research_priority *= 0.8
    return research_priority
Esempio n. 17
0
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 = foAI.foAIstate.get_piloting_grades(
                loc_planet.speciesName)
            troops_per_ship = foAI.foAIstate.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()
Esempio n. 18
0
def get_military_fleets(mil_fleets_ids=None, try_reset=True, thisround="Main"):
    """Get armed military fleets."""
    global military_allocations, totMilRating, num_milships

    universe = fo.getUniverse()
    empire = fo.getEmpire()
    empire_id = empire.empireID
    capital_id = PlanetUtilsAI.get_capital()
    if capital_id is None:
        homeworld = None
    else:
        homeworld = universe.getPlanet(capital_id)
    if homeworld:
        home_system_id = homeworld.systemID
    else:
        home_system_id = -1

    if mil_fleets_ids is not None:
        all_military_fleet_ids = mil_fleets_ids
    else:
        all_military_fleet_ids = FleetUtilsAI.get_empire_fleet_ids_by_role(MissionType.MILITARY)
    if try_reset and (fo.currentTurn() + empire_id) % 30 == 0 and thisround == "Main":
        try_again(all_military_fleet_ids, try_reset=False, thisround=thisround + " Reset")

    num_milships = 0
    for fid in all_military_fleet_ids:
        num_milships += foAI.foAIstate.fleetStatus.get(fid, {}).get('nships', 0)

    enemy_rating = foAI.foAIstate.empire_standard_enemy_rating

    mil_fleets_ids = list(FleetUtilsAI.extract_fleet_ids_without_mission_types(all_military_fleet_ids))
    mil_needing_repair_ids, mil_fleets_ids = avail_mil_needing_repair(mil_fleets_ids, split_ships=True)
    avail_mil_rating = sum(map(lambda x: foAI.foAIstate.get_rating(x).get('overall', 0), mil_fleets_ids))
    if "Main" in thisround:
        print "=================================================="
        print "%s Round Available Military Rating: %d" % (thisround, avail_mil_rating)
        print "---------------------------------"
    remaining_mil_rating = avail_mil_rating
    allocations = []
    allocation_groups = {}

    if not mil_fleets_ids:
        if "Main" in thisround:
            military_allocations = []
        return []

    # for each system, get total rating of fleets assigned to it
    already_assigned_rating = {}
    assigned_attack = {}
    assigned_hp = {}
    systems_status = foAI.foAIstate.systemStatus
    enemy_sup_factor = {}  # enemy supply
    for sys_id in universe.systemIDs:
        assigned_attack[sys_id] = 0
        assigned_hp[sys_id] = 0
        enemy_sup_factor[sys_id] = min(2, len(systems_status.get(sys_id, {}).get('enemies_nearly_supplied', [])))
    for fleet_id in [fid for fid in all_military_fleet_ids if fid not in mil_fleets_ids]:
        ai_fleet_mission = foAI.foAIstate.get_fleet_mission(fleet_id)
        if not ai_fleet_mission.target:  # shouldn't really be possible
            continue
        last_sys = ai_fleet_mission.target.get_system().id  # will count this fleet as assigned to last system in target list
        assigned_attack[last_sys] += foAI.foAIstate.get_rating(fleet_id).get('attack', 0)
        assigned_hp[last_sys] += foAI.foAIstate.get_rating(fleet_id).get('health', 0)
    for sys_id in universe.systemIDs:
        mydefenses = systems_status.get(sys_id, {}).get('mydefenses', {})
        mypattack = mydefenses.get('attack', 0)
        myphealth = mydefenses.get('health', 0)
        already_assigned_rating[sys_id] = (assigned_attack[sys_id] + mypattack) * (assigned_hp[sys_id] + myphealth)
        if verbose_mil_reporting and already_assigned_rating[sys_id]:
            print "\t System %s already assigned rating %.1f" % (
                universe.getSystem(sys_id), already_assigned_rating[sys_id])

    # get systems to defend
    capital_id = PlanetUtilsAI.get_capital()
    if capital_id is not None:
        capital_planet = universe.getPlanet(capital_id)
    else:
        capital_planet = None
    # TODO: if no owned planets try to capture one!
    if capital_planet:
        capital_sys_id = capital_planet.systemID
    else:  # should be rare, but so as to not break code below, pick a randomish mil-centroid system
        capital_sys_id = None  # unless we can find one to use
        system_dict = {}
        for fleet_id in all_military_fleet_ids:
            status = foAI.foAIstate.fleetStatus.get(fleet_id, None)
            if status is not None:
                sys_id = status['sysID']
                if not list(universe.getSystem(sys_id).planetIDs):
                    continue
                system_dict[sys_id] = system_dict.get(sys_id, 0) + status.get('rating', {}).get('overall', 0)
        ranked_systems = sorted([(val, sys_id) for sys_id, val in system_dict.items()])
        if ranked_systems:
            capital_sys_id = ranked_systems[-1][-1]
        else:
            try:
                capital_sys_id = foAI.foAIstate.fleetStatus.items()[0][1]['sysID']
            except:
                pass

    if False:
        if fo.currentTurn() < 20:
            threat_bias = 0
        elif fo.currentTurn() < 40:
            threat_bias = 10
        elif fo.currentTurn() < 60:
            threat_bias = 80
        elif fo.currentTurn() < 80:
            threat_bias = 200
        else:
            threat_bias = 400
    else:
        threat_bias = 0

    safety_factor = get_safety_factor()

    num_targets = max(10, PriorityAI.allotted_outpost_targets)
    top_target_planets = ([pid for pid, pscore, trp in AIstate.invasionTargets[:PriorityAI.allottedInvasionTargets] if pscore > 20] +
                          [pid for pid, (pscore, spec) in foAI.foAIstate.colonisableOutpostIDs.items()[:num_targets] if pscore > 20] +
                          [pid for pid, (pscore, spec) in foAI.foAIstate.colonisablePlanetIDs.items()[:num_targets] if pscore > 20])
    top_target_planets.extend(foAI.foAIstate.qualifyingTroopBaseTargets.keys())
    base_col_target_systems = PlanetUtilsAI.get_systems(top_target_planets)
    top_target_systems = []
    for sys_id in AIstate.invasionTargetedSystemIDs + base_col_target_systems:
        if sys_id not in top_target_systems:
            if foAI.foAIstate.systemStatus[sys_id]['totalThreat'] > totMilRating:
                continue
            top_target_systems.append(sys_id)  # doing this rather than set, to preserve order

    if verbose_mil_reporting:
        print "----------------------------"
    # allocation format: ( sysID, newAllocation, takeAny, maxMultiplier )
    # ================================
    # --------Capital Threat ----------
    if capital_sys_id is not None:
        capital_sys_status = systems_status[capital_sys_id]
        capital_threat = safety_factor*(2 * threat_bias + combine_ratings_list([capital_sys_status[thrt_key] for thrt_key in ['totalThreat', 'neighborThreat']]))
        capital_threat += max(0, enemy_sup_factor[sys_id]*enemy_rating - capital_sys_status.get('my_neighbor_rating', 0))
        local_support = combine_ratings(already_assigned_rating[capital_sys_id], capital_sys_status['my_neighbor_rating'])
        base_needed_rating = rating_needed(capital_sys_status['regional_threat'], local_support)
        needed_rating = max(base_needed_rating, rating_needed(1.4 * capital_threat, already_assigned_rating[capital_sys_id]))
        max_alloc = max(rating_needed(1.5 * capital_sys_status['regional_threat'], already_assigned_rating[capital_sys_id]),
                        rating_needed(2 * capital_threat, already_assigned_rating[capital_sys_id]))
        new_alloc = 0
        if try_reset:
            if needed_rating > 0.5*avail_mil_rating:
                try_again(all_military_fleet_ids)
                return
        if needed_rating > 0:
            new_alloc = min(remaining_mil_rating, needed_rating)
            allocations.append((capital_sys_id, new_alloc, True, max_alloc))
            allocation_groups.setdefault('capitol', []).append((capital_sys_id, new_alloc, True, max_alloc))
            if verbose_mil_reporting:
                report_format = ("\tAt Capital system %s, local threat %.1f, regional threat %.1f, local support %.1f, "
                                 "base_needed_rating %.1f, needed_rating %.1f, new allocation %.1f")
                print report_format % (universe.getSystem(capital_sys_id), capital_threat,
                                       capital_sys_status['regional_threat'], local_support,
                                       base_needed_rating, needed_rating, new_alloc)
            remaining_mil_rating -= new_alloc
        if "Main" in thisround or new_alloc > 0:
            if verbose_mil_reporting:
                print "Empire Capital System: (%d) %s -- threat : %d, military allocation: existing: %d ; new: %d" % (capital_sys_id, universe.getSystem(capital_sys_id).name, capital_threat, already_assigned_rating[capital_sys_id], new_alloc)
                print "-----------------"

    # ================================
    # --------Empire Occupied Systems ----------
    empire_planet_ids = PlanetUtilsAI.get_owned_planets_by_empire(universe.planetIDs)
    empire_occupied_system_ids = list(set(PlanetUtilsAI.get_systems(empire_planet_ids)) - {capital_sys_id})
    if "Main" in thisround:
        if verbose_mil_reporting:
            print "Empire-Occupied Systems: %s" % (["| %d %s |" % (eo_sys_id, universe.getSystem(eo_sys_id).name) for eo_sys_id in empire_occupied_system_ids])
            print "-----------------"
    new_alloc = 0
    if empire_occupied_system_ids:
        oc_sys_tot_threat_v1 = [(o_s_id, threat_bias + safety_factor*combine_ratings_list(
                                [systems_status.get(o_s_id, {}).get(thrt_key, 0) for thrt_key in ['totalThreat', 'neighborThreat']]))
                                                                                            for o_s_id in empire_occupied_system_ids]
        tot_oc_sys_threat = sum([thrt for _, thrt in oc_sys_tot_threat_v1])
        tot_cur_alloc = sum([already_assigned_rating[sid] for sid, _ in oc_sys_tot_threat_v1])
        # intentionally after tallies, but perhaps should be before
        oc_sys_tot_threat = []
        threat_details = {}
        for sys_id, sys_threat in oc_sys_tot_threat_v1:
            j2_threat = systems_status.get(sys_id, {}).get('jump2_threat', 0)
            local_defenses = combine_ratings_list([systems_status.get(sys_id, {}).get('my_neighbor_rating', 0),
                                                   already_assigned_rating[sys_id]])
            threat_details[sys_id] = (sys_threat, enemy_sup_factor[sys_id] * 0.5 * enemy_rating,
                                      j2_threat, local_defenses)
            oc_sys_tot_threat.append((sys_id, sys_threat + max(0,
                                                               enemy_sup_factor[sys_id] * 0.5 * enemy_rating +
                                                               j2_threat - local_defenses
                                                               )))

        oc_sys_alloc = 0
        for sid, thrt in oc_sys_tot_threat:
            cur_alloc = already_assigned_rating[sid]
            needed_rating = rating_needed(1.3 * thrt, cur_alloc)
            max_alloc = rating_needed(2*thrt, cur_alloc)
            if (needed_rating > 0.8 * remaining_mil_rating) and try_reset:
                try_again(all_military_fleet_ids)
                return
            this_alloc = 0
            if needed_rating > 0 and remaining_mil_rating > 0:
                this_alloc = max(0, min(needed_rating, 0.5 * avail_mil_rating, remaining_mil_rating))
                new_alloc += this_alloc
                allocations.append((sid, this_alloc, True, max_alloc))
                allocation_groups.setdefault('occupied', []).append((sid, this_alloc, True, max_alloc))
                remaining_mil_rating -= this_alloc
                oc_sys_alloc += this_alloc
            if "Main" in thisround or this_alloc > 0:
                if verbose_mil_reporting:
                    print "Provincial Occupied system %4d ( %10s ) has local threat %8d ; existing military allocation %d and new allocation %8d" % (sid, universe.getSystem(sid).name, thrt, cur_alloc, this_alloc)
                    print "\t base threat was %.1f, supply_threat %.1f, jump2_threat %.1f, and local defenses %.1f" % (
                        threat_details[sid])
        if "Main" in thisround or new_alloc > 0:
            if verbose_mil_reporting:
                print "Provincial Empire-Occupied Sytems under total threat: %d -- total mil allocation: existing %d ; new: %d" % (tot_oc_sys_threat, tot_cur_alloc, oc_sys_alloc)
                print "-----------------"

    # ================================
    # --------Top Targeted Systems ----------
    # TODO: do native invasions highest priority
    other_targeted_system_ids = top_target_systems
    if "Main" in thisround:
        if verbose_mil_reporting:
            print "Top Colony and Invasion Targeted Systems : %s" % (["| %d %s |" % (sys_id, universe.getSystem(sys_id).name) for sys_id in other_targeted_system_ids])
            print "-----------------"
    new_alloc = 0
    if other_targeted_system_ids:
        ot_sys_alloc = 0
        ot_sys_threat = [(o_s_id, threat_bias + safety_factor * combine_ratings_list([
                                systems_status.get(o_s_id, {}).get('totalThreat', 0),
                                0.75 * systems_status.get(o_s_id, {}).get('neighborThreat', 0),
                                0.5 * systems_status.get(o_s_id, {}).get('jump2_threat', 0)]))
                         for o_s_id in other_targeted_system_ids]
        totot_sys_threat = sum([thrt for sid, thrt in ot_sys_threat])
        tot_cur_alloc = sum([already_assigned_rating[sid] for sid, thrt in ot_sys_threat])
        # intentionally after tallies, but perhaps should be before
        ot_sys_threat = [(sys_id, sys_threat + enemy_sup_factor[sys_id] * 0.5 * enemy_rating) for sys_id, sys_threat in ot_sys_threat]
        for sid, thrt in ot_sys_threat:
            cur_alloc = already_assigned_rating[sid]
            needed_rating = rating_needed(1.4*thrt, cur_alloc)
            this_alloc = 0
            # only record more allocation for this invasion if we already started or have enough rating available
            take_any = already_assigned_rating[sid] > 0
            if needed_rating > 0 and (remaining_mil_rating > needed_rating or take_any):
                this_alloc = max(0, min(needed_rating, remaining_mil_rating))
                max_alloc = rating_needed(3 * thrt, cur_alloc)
                new_alloc += this_alloc
                allocations.append((sid, this_alloc, take_any, max_alloc))
                allocation_groups.setdefault('topTargets', []).append((sid, this_alloc, take_any, max_alloc))
                remaining_mil_rating -= this_alloc
                ot_sys_alloc += this_alloc
            if "Main" in thisround or this_alloc > 0:
                if verbose_mil_reporting:
                    print "Targeted system %4d ( %10s ) has local threat %8d ; existing military allocation %d and new allocation %8d" % (sid, universe.getSystem(sid).name, thrt, cur_alloc, this_alloc)
        if "Main" in thisround or new_alloc > 0:
            if verbose_mil_reporting:
                print "-----------------"
                print "Top Colony and Invasion Targeted Systems under total threat: %d -- total mil allocation-- existing: %d ; new: %d" % (totot_sys_threat, tot_cur_alloc, ot_sys_alloc)
                print "-----------------"

    # ================================
    # --------Targeted Systems ----------
    # TODO: do native invasions highest priority
    other_targeted_system_ids = [sys_id for sys_id in set(PlanetUtilsAI.get_systems(AIstate.opponentPlanetIDs)) if sys_id not in top_target_systems]
    if "Main" in thisround:
        if verbose_mil_reporting:
            print "Other Invasion Targeted Systems : %s" % (["| %d %s |" % (sys_id, universe.getSystem(sys_id).name) for sys_id in other_targeted_system_ids])
            print "-----------------"
    # for these, calc local threat only, no neighbor threat, but use a multiplier for fleet safety
    new_alloc = 0
    if other_targeted_system_ids:
        ot_sys_alloc = 0
        ot_sys_threat = [(o_s_id, threat_bias + safety_factor*combine_ratings_list([
                                systems_status.get(o_s_id, {}).get('totalThreat', 0),
                                0.75*systems_status.get(o_s_id, {}).get('neighborThreat', 0),
                                0.5*systems_status.get(o_s_id, {}).get('jump2_threat', 0)]))
                         for o_s_id in other_targeted_system_ids]
        totot_sys_threat = sum([thrt for sid, thrt in ot_sys_threat])
        tot_cur_alloc = sum([already_assigned_rating[sid] for sid, thrt in ot_sys_threat])
        # intentionally after tallies, but perhaps should be before
        ot_sys_threat = [(sys_id, sys_threat + enemy_sup_factor[sys_id] * 0.5 * enemy_rating) for sys_id, sys_threat in ot_sys_threat]
        for sid, thrt in ot_sys_threat:
            cur_alloc = already_assigned_rating[sid]
            needed_rating = rating_needed(1.4 * thrt, cur_alloc)
            this_alloc = 0
            # only record more allocation for this invasion if we already started or have enough rating available
            take_any = already_assigned_rating[sid] > 0
            if needed_rating > 0 and (remaining_mil_rating > needed_rating or take_any):
                this_alloc = max(0, min(needed_rating, remaining_mil_rating))
                new_alloc += this_alloc
                max_alloc = rating_needed(2 * thrt, cur_alloc)
                allocations.append((sid, this_alloc, take_any, max_alloc))
                allocation_groups.setdefault('otherTargets', []).append((sid, this_alloc, take_any, max_alloc))
                remaining_mil_rating -= this_alloc
                ot_sys_alloc += this_alloc
            if "Main" in thisround or this_alloc > 0:
                if verbose_mil_reporting:
                    print "Targeted system %4d ( %10s ) has local threat %8d ; existing military allocation %d and new allocation %8d" % (sid, universe.getSystem(sid).name, thrt, cur_alloc, this_alloc)
        if "Main" in thisround or new_alloc > 0:
            if verbose_mil_reporting:
                print "-----------------"
                print "Invasion Targeted Systems under total threat: %d -- total mil allocation-- existing: %d ; new: %d" % (totot_sys_threat, tot_cur_alloc, ot_sys_alloc)
                print "-----------------"

    other_targeted_system_ids = [sys_id for sys_id in list(set(AIstate.colonyTargetedSystemIDs + AIstate.outpostTargetedSystemIDs)) if sys_id not in top_target_systems]
    if "Main" in thisround:
        if verbose_mil_reporting:
            print "Other Targeted Systems : %s" % (["| %d %s |" % (sys_id, universe.getSystem(sys_id).name) for sys_id in other_targeted_system_ids])
            print "-----------------"
    if other_targeted_system_ids:
        ot_sys_alloc = 0
        ot_sys_threat = [(o_s_id, threat_bias + safety_factor*combine_ratings_list([
                                systems_status.get(o_s_id, {}).get('totalThreat', 0),
                                0.75*systems_status.get(o_s_id, {}).get('neighborThreat', 0),
                                0.5*systems_status.get(o_s_id, {}).get('jump2_threat', 0)]))
                         for o_s_id in other_targeted_system_ids]
        totot_sys_threat = sum([thrt for sid, thrt in ot_sys_threat])
        # intentionally after tallies, but perhaps should be before
        ot_sys_threat = [(sys_id, sys_threat + enemy_sup_factor[sys_id] * 0.5 * enemy_rating) for sys_id, sys_threat in ot_sys_threat]
        tot_cur_alloc = sum([already_assigned_rating[sid] for sid, thrt in ot_sys_threat])
        new_alloc = 0
        for sid, thrt in ot_sys_threat:
            cur_alloc = already_assigned_rating[sid]
            needed_rating = rating_needed(1.4 * thrt, cur_alloc)
            this_alloc = 0
            # only record more allocation for this invasion if we already started or have enough rating available
            take_any = already_assigned_rating[sid] > 0
            if needed_rating > 0 and (remaining_mil_rating > needed_rating or take_any):
                this_alloc = max(0, min(needed_rating, remaining_mil_rating))
                new_alloc += this_alloc
                max_alloc = rating_needed(3*thrt, cur_alloc)
                allocations.append((sid, this_alloc, take_any, max_alloc))
                allocation_groups.setdefault('otherTargets', []).append((sid, this_alloc, take_any, max_alloc))
                remaining_mil_rating -= this_alloc
                ot_sys_alloc += this_alloc
            if "Main" in thisround or this_alloc > 0:
                if verbose_mil_reporting:
                    print "Targeted system %4d ( %10s ) has local threat %8d ; existing military allocation %d and new allocation %8d" % (sid, universe.getSystem(sid).name, thrt, cur_alloc, this_alloc)
        if "Main" in thisround or new_alloc > 0:
            if verbose_mil_reporting:
                print "-----------------"
                print "Other Targeted Systems under total threat: %d -- total mil allocation-- existing: %d ; new: %d" % (totot_sys_threat, tot_cur_alloc, ot_sys_alloc)
                print "-----------------"

    other_targeted_system_ids = []
    # targetable_ids = ColonisationAI.annexableSystemIDs.union(empire.fleetSupplyableSystemIDs)
    targetable_ids = set(ColonisationAI.systems_by_supply_tier.get(0, []) + ColonisationAI.systems_by_supply_tier.get(1, []))
    for sys_id in AIstate.opponentSystemIDs:
        if sys_id in targetable_ids:
            other_targeted_system_ids.append(sys_id)
        else:
            for nID in universe.getImmediateNeighbors(sys_id, empire_id):
                if nID in targetable_ids:
                    other_targeted_system_ids.append(sys_id)
                    break

    if "Main" in thisround:
        if verbose_mil_reporting:
            print "Blockade Targeted Systems : %s" % (["| %d %s |" % (sys_id, universe.getSystem(sys_id).name) for sys_id in other_targeted_system_ids])
            print "-----------------"
    if other_targeted_system_ids:
        ot_sys_alloc = 0
        ot_sys_threat = [(o_s_id, threat_bias + safety_factor*combine_ratings_list([
                                systems_status.get(o_s_id, {}).get('totalThreat', 0),
                                0.75*systems_status.get(o_s_id, {}).get('neighborThreat', 0),
                                0.5*systems_status.get(o_s_id, {}).get('jump2_threat', 0)]))
                         for o_s_id in other_targeted_system_ids]
        totot_sys_threat = sum([thrt for sid, thrt in ot_sys_threat])
        tot_cur_alloc = sum([already_assigned_rating[sid] for sid, thrt in ot_sys_threat])
        # intentionally after tallies, but perhaps should be before
        # this supply threat calc intentionally uses a lower multiplier 0.25
        ot_sys_threat = [(sys_id, sys_threat + enemy_sup_factor[sys_id] * 0.25 * enemy_rating) for sys_id, sys_threat in ot_sys_threat]
        new_alloc = 0
        for sid, thrt in ot_sys_threat:
            cur_alloc = already_assigned_rating[sid]
            needed_rating = rating_needed(1.4*thrt, cur_alloc)
            this_alloc = 0
            # only record more allocation for this invasion if we already started or have enough rating available
            take_any = already_assigned_rating[sid] > 0
            if needed_rating > 0 and (remaining_mil_rating > needed_rating or take_any):
                this_alloc = max(0, min(needed_rating, remaining_mil_rating))
                new_alloc += this_alloc
                max_alloc = 1.5 * this_alloc
                allocations.append((sid, this_alloc, take_any, max_alloc))
                allocation_groups.setdefault('otherTargets', []).append((sid, this_alloc, take_any, max_alloc))
                remaining_mil_rating -= this_alloc
                ot_sys_alloc += this_alloc
            if "Main" in thisround or this_alloc > 0:
                if verbose_mil_reporting:
                    print "Blockade Targeted system %4d ( %10s ) has local threat %8d ; existing military allocation %d and new allocation %8d" % (sid, universe.getSystem(sid).name, thrt, cur_alloc, this_alloc)
        if "Main" in thisround or new_alloc > 0:
            if verbose_mil_reporting:
                print "-----------------"
                print "Blockade Targeted Systems under total threat: %d -- total mil allocation-- existing: %d ; new: %d" % (totot_sys_threat, tot_cur_alloc, ot_sys_alloc)
                print "-----------------"

    current_mil_systems = [sid for sid, alloc, take_any, mm in allocations]
    interior_targets1 = targetable_ids.difference(current_mil_systems)
    interior_targets = [sid for sid in interior_targets1 if (
        threat_bias + systems_status.get(sid, {}).get('totalThreat', 0) > 0.8 * already_assigned_rating[sid])]
    if "Main" in thisround:
        if verbose_mil_reporting:
            print
            print "Other Empire-Proximal Systems : %s" % (["| %d %s |" % (sys_id, universe.getSystem(sys_id).name) for sys_id in interior_targets1])
            print "-----------------"
    # for these, calc local threat only, no neighbor threat, but use a multiplier for fleet safety
    new_alloc = 0
    if interior_targets:
        ot_sys_alloc = 0
        ot_sys_threat = [(o_s_id, threat_bias + safety_factor*(systems_status.get(o_s_id, {}).get('totalThreat', 0))) for o_s_id in interior_targets]
        totot_sys_threat = sum([thrt for sid, thrt in ot_sys_threat])
        tot_cur_alloc = sum([already_assigned_rating[sid] for sid, thrt in ot_sys_threat])
        # do not add enemy supply threat here
        for sid, thrt in ot_sys_threat:
            cur_alloc = already_assigned_rating[sid]
            needed_rating = rating_needed(1.5 * thrt, cur_alloc)
            this_alloc = 0
            # only record more allocation for this invasion if we already started or have enough rating available
            take_any = already_assigned_rating[sid] > 0
            if needed_rating > 0 and (remaining_mil_rating > needed_rating or take_any):
                this_alloc = max(0, min(needed_rating, remaining_mil_rating))
                new_alloc += this_alloc
                max_alloc = 3 * this_alloc
                allocations.append((sid, this_alloc, take_any, max_alloc))
                allocation_groups.setdefault('otherTargets', []).append((sid, this_alloc, take_any, max_alloc))
                remaining_mil_rating -= this_alloc
                ot_sys_alloc += this_alloc
            if "Main" in thisround or this_alloc > 0:
                if verbose_mil_reporting:
                    print "Other interior system %4d ( %10s ) has local threat %8d ; existing military allocation %d and new allocation %8d" % (sid, universe.getSystem(sid).name, thrt, cur_alloc, this_alloc)
        if "Main" in thisround or new_alloc > 0:
            if verbose_mil_reporting:
                print "-----------------"
                print "Other Interior Systems under total threat: %d -- total mil allocation-- existing: %d ; new: %d" % (totot_sys_threat, tot_cur_alloc, ot_sys_alloc)
                print "-----------------"
    elif "Main" in thisround:
        if verbose_mil_reporting:
            print "-----------------"
            print "No Other Interior Systems with fleet threat "
            print "-----------------"

    monster_dens = []

    # explo_target_ids, _ = ExplorationAI.get_current_exploration_info(verbose=False)
    explo_target_ids = []
    if "Main" in thisround:
        if verbose_mil_reporting:
            print
            print "Exploration-targeted Systems: %s" % (["| %d %s |" % (sys_id, universe.getSystem(sys_id).name) for sys_id in explo_target_ids])
            print "-----------------"
    # for these, calc fleet threat only, no neighbor threat, but use a multiplier for fleet safety
    new_alloc = 0
    if explo_target_ids:
        ot_sys_alloc = 0
        ot_sys_threat = [(o_s_id, safety_factor*combine_ratings(systems_status.get(o_s_id, {}).get('fleetThreat', 0), systems_status.get(o_s_id, {}).get('monsterThreat', 0) + systems_status.get(o_s_id, {}).get('planetThreat', 0))) for o_s_id in explo_target_ids]
        totot_sys_threat = sum([thrt for sid, thrt in ot_sys_threat])
        tot_cur_alloc = sum([0.8*already_assigned_rating[sid] for sid, thrt in ot_sys_threat])
        # intentionally after tallies, but perhaps should be before
        # this supply threat calc intentionally uses a lower multiplier 0.25
        ot_sys_threat = [(sys_id, sys_threat + enemy_sup_factor[sys_id] * 0.25 * enemy_rating) for sys_id, sys_threat in ot_sys_threat]
        for sid, thrt in ot_sys_threat:
            cur_alloc = already_assigned_rating[sid]
            needed_rating = rating_needed(1.4 * thrt, cur_alloc)
            this_alloc = 0
            # only record more allocation for this invasion if we already started or have enough rating available
            take_any = False
            if needed_rating > 0 and (remaining_mil_rating > needed_rating or take_any):
                this_alloc = max(0, min(needed_rating, remaining_mil_rating))
                new_alloc += this_alloc
                max_alloc = 2 * this_alloc
                allocations.append((sid, this_alloc, take_any, max_alloc))
                allocation_groups.setdefault('exploreTargets', []).append((sid, this_alloc, take_any, max_alloc))
                remaining_mil_rating -= this_alloc
                ot_sys_alloc += this_alloc
            if "Main" in thisround or this_alloc > 0:
                if verbose_mil_reporting:
                    print "Exploration-targeted %4d ( %10s ) has local threat %8d ; existing military allocation %d and new allocation %8d" % (sid, universe.getSystem(sid).name, thrt, cur_alloc, this_alloc)
        if "Main" in thisround or new_alloc > 0:
            if verbose_mil_reporting:
                print "-----------------"
                print "Exploration-targeted s under total threat: %d -- total mil allocation-- existing: %d ; new: %d" % (totot_sys_threat, tot_cur_alloc, ot_sys_alloc)
                print "-----------------"

    visible_system_ids = foAI.foAIstate.visInteriorSystemIDs.keys() + foAI.foAIstate. visBorderSystemIDs.keys()
    accessible_system_ids = [sys_id for sys_id in visible_system_ids if universe.systemsConnected(sys_id, home_system_id, empire_id)]
    current_mil_systems = [sid for sid, alloc, take_any, multiplier in allocations if alloc > 0]
    border_targets1 = [sid for sid in accessible_system_ids if sid not in current_mil_systems]
    border_targets = [sid for sid in border_targets1 if (threat_bias + systems_status.get(sid, {}).get('fleetThreat', 0) + systems_status.get(sid, {}).get('planetThreat', 0) > 0.8*already_assigned_rating[sid])]
    if "Main" in thisround:
        if verbose_mil_reporting:
            print
            print "Empire-Accessible Systems not yet allocated military: %s" % (["| %d %s |" % (sys_id, universe.getSystem(sys_id) and universe.getSystem(sys_id).name) for sys_id in border_targets1])
            print "-----------------"
    # for these, calc fleet threat only, no neighbor threat, but use a multiplier for fleet safety
    new_alloc = 0
    if border_targets:
        ot_sys_alloc = 0
        ot_sys_threat = [(o_s_id, threat_bias + safety_factor*combine_ratings(systems_status.get(o_s_id, {}).get('fleetThreat', 0), systems_status.get(o_s_id, {}).get('monsterThreat', 0) + systems_status.get(o_s_id, {}).get('planetThreat', 0))) for o_s_id in border_targets]
        totot_sys_threat = sum([thrt for sid, thrt in ot_sys_threat])
        tot_cur_alloc = sum([0.8*already_assigned_rating[sid] for sid, thrt in ot_sys_threat])
        # do not add enemy supply threat here
        for sid, thrt in ot_sys_threat:
            cur_alloc = already_assigned_rating[sid]
            needed_rating = rating_needed(1.2 * thrt, cur_alloc)
            this_alloc = 0
            # only record more allocation for this invasion if we already started or have enough rating available
            take_any = False
            if needed_rating > 0 and (remaining_mil_rating > needed_rating or take_any):
                this_alloc = max(0, min(needed_rating, remaining_mil_rating))
                new_alloc += this_alloc
                max_alloc = safety_factor*2*max(systems_status.get(sid, {}).get('totalThreat', 0), systems_status.get(sid, {}).get('neighborThreat', 0))
                allocations.append((sid, this_alloc, take_any, max_alloc))
                allocation_groups.setdefault('accessibleTargets', []).append((sid, this_alloc, take_any, max_alloc))
                remaining_mil_rating -= this_alloc
                ot_sys_alloc += this_alloc
            if "Main" in thisround or this_alloc > 0:
                if verbose_mil_reporting:
                    print "Other Empire-Accessible system %4d ( %10s ) has local biased threat %8d ; existing military allocation %d and new allocation %8d" % (sid, universe.getSystem(sid).name, thrt, cur_alloc, this_alloc)
        if "Main" in thisround or new_alloc > 0:
            if verbose_mil_reporting:
                print "-----------------"
                print "Other Empire-Accessible Systems under total biased threat: %d -- total mil allocation-- existing: %d ; new: %d" % (totot_sys_threat, tot_cur_alloc, ot_sys_alloc)
                print "-----------------"
    elif "Main" in thisround:
        if verbose_mil_reporting:
            print "-----------------"
            print "No Other Empire-Accessible Systems with biased local threat "
            print "-----------------"

    # monster den treatment probably unnecessary now
    if "Main" in thisround:
        if verbose_mil_reporting:
            print
            print "Big-Monster Dens: %s" % (["| %d %s |" % (sys_id, universe.getSystem(sys_id).name) for sys_id in monster_dens])
            print "-----------------"
    # for these, calc fleet threat only, no neighbor threat, but use a multiplier for fleet safety
    new_alloc = 0
    if monster_dens:
        ot_sys_alloc = 0
        ot_sys_threat = [(o_s_id, safety_factor * combine_ratings_list([systems_status.get(o_s_id, {}).get('fleetThreat', 0), systems_status.get(o_s_id, {}).get('monsterThreat', 0), systems_status.get(o_s_id, {}).get('planetThreat', 0)])) for o_s_id in monster_dens]
        for sid, thrt in ot_sys_threat:
            cur_alloc = 0.8 * already_assigned_rating[sid]
            this_alloc = 0
            if (thrt > cur_alloc) and remaining_mil_rating > 2 * thrt:
                this_alloc = int(0.99999 + (thrt - cur_alloc) * 1.5)
                new_alloc += this_alloc
                allocations.append((sid, this_alloc, False, 5))
                remaining_mil_rating -= this_alloc
                ot_sys_alloc += this_alloc
            if "Main" in thisround or this_alloc > 0:
                if verbose_mil_reporting:
                    print "Monster Den %4d ( %10s ) has local threat %8d ; existing military allocation %d and new allocation %8d" % (sid, universe.getSystem(sid).name, thrt, cur_alloc, this_alloc)
        if "Main" in thisround or new_alloc > 0:
            if verbose_mil_reporting:
                print "-----------------"

    new_allocations = []
    remaining_mil_rating = avail_mil_rating
    # for top categories assign max_alloc right away as available
    for cat in ['capitol', 'occupied', 'topTargets']:
        for sid, alloc, take_any, max_alloc in allocation_groups.get(cat, []):
            if remaining_mil_rating <= 0:
                break
            this_alloc = min(remaining_mil_rating, max_alloc)
            new_allocations.append((sid, this_alloc, alloc, take_any))
            remaining_mil_rating -= this_alloc

    base_allocs = set()
    # for lower priority categories, first assign base_alloc around to all, then top up as available
    for cat in ['otherTargets', 'accessibleTargets', 'exploreTargets']:
        for sid, alloc, take_any, max_alloc in allocation_groups.get(cat, []):
            if remaining_mil_rating <= 0:
                break
            base_allocs.add(sid)
            remaining_mil_rating -= alloc
    for cat in ['otherTargets', 'accessibleTargets', 'exploreTargets']:
        for sid, alloc, take_any, max_alloc in allocation_groups.get(cat, []):
            if sid not in base_allocs:
                break
            if remaining_mil_rating <= 0:
                new_allocations.append((sid, alloc, alloc, take_any))
            else:
                new_rating = min(remaining_mil_rating + alloc, max_alloc)
                new_allocations.append((sid, new_rating, alloc, take_any))
                remaining_mil_rating -= (new_rating - alloc)

    if "Main" in thisround:
        military_allocations = new_allocations
    minMilAllocations.clear()
    minMilAllocations.update([(sid, alloc) for sid, alloc, take_any, mm in allocations])
    if verbose_mil_reporting or "Main" in thisround:
        print "------------------------------\nFinal %s Round Military Allocations: %s \n-----------------------" % (thisround, dict([(sid, alloc) for sid, alloc, minalloc, take_any in new_allocations]))
        print "(Apparently) remaining military rating: %.1f" % remaining_mil_rating

    # export military systems for other AI modules
    if "Main" in thisround:
        AIstate.militarySystemIDs = list(set([sid for sid, alloc, minalloc, take_any in new_allocations]).union([sid for sid in already_assigned_rating if already_assigned_rating[sid] > 0]))
    else:
        AIstate.militarySystemIDs = list(set([sid for sid, alloc, minalloc, take_any in new_allocations]).union(AIstate.militarySystemIDs))
    return new_allocations
Esempio n. 19
0
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()
Esempio n. 20
0
def _calculate_research_priority():
    """Calculates the AI empire's demand for research."""
    universe = fo.getUniverse()
    empire = fo.getEmpire()
    empire_id = empire.empireID
    current_turn = fo.currentTurn()
    enemies_sighted = foAI.foAIstate.misc.get('enemies_sighted', {})
    recent_enemies = [x for x in enemies_sighted if x > current_turn - 8]

    industry_priority = foAI.foAIstate.get_priority(PriorityType.RESOURCE_PRODUCTION)

    got_algo = tech_is_complete(AIDependencies.LRN_ALGO_ELEGANCE)
    got_quant = tech_is_complete(AIDependencies.LRN_QUANT_NET)
    research_queue_list = ResearchAI.get_research_queue_techs()
    orb_gen_tech = AIDependencies.PRO_ORBITAL_GEN
    got_orb_gen = tech_is_complete(orb_gen_tech)
    mgrav_prod_tech = AIDependencies.PRO_MICROGRAV_MAN
    got_mgrav_prod = tech_is_complete(mgrav_prod_tech)
    # got_solar_gen = tech_is_complete(AIDependencies.PRO_SOL_ORB_GEN)
    
    milestone_techs = ["PRO_SENTIENT_AUTOMATION", "LRN_DISTRIB_THOUGHT", "LRN_QUANT_NET", "SHP_WEAPON_2_4", "SHP_WEAPON_3_2", "SHP_WEAPON_4_2"]
    milestones_done = [mstone for mstone in milestone_techs if tech_is_complete(mstone)]
    print "Research Milestones accomplished at turn %d: %s" % (current_turn, milestones_done)

    total_pp = empire.productionPoints
    total_rp = empire.resourceProduction(fo.resourceType.research)
    industry_surge = (foAI.foAIstate.character.may_surge_industry(total_pp, total_rp) and
                      (((orb_gen_tech in research_queue_list[:2] or got_orb_gen) and state.have_gas_giant) or
                       ((mgrav_prod_tech in research_queue_list[:2] or got_mgrav_prod) and state.have_asteroids)) and
                      (not (len(AIstate.popCtrIDs) >= 12)))
    # get current industry production & Target
    owned_planet_ids = PlanetUtilsAI.get_owned_planets_by_empire(universe.planetIDs)
    planets = map(universe.getPlanet, owned_planet_ids)
    target_rp = sum(map(lambda x: x.currentMeterValue(fo.meterType.targetResearch), planets))
    galaxy_is_sparse = ColonisationAI.galaxy_is_sparse()
    enemies_sighted = foAI.foAIstate.misc.get('enemies_sighted', {})

    style_index = foAI.foAIstate.character.preferred_research_cutoff([0, 1])
    if foAI.foAIstate.character.may_maximize_research():
        style_index += 1

    cutoff_sets = [[25, 45, 70, 110], [30, 45, 70, 150], [25, 40, 80, 160]]
    cutoffs = cutoff_sets[style_index]
    settings = [[1.3, .7, .5, .4, .35], [1.4, 0.8, 0.6, 0.5, 0.35], [1.4, 0.8, 0.6, 0.5, 0.4]][style_index]

    if (current_turn < cutoffs[0]) or (not got_algo) or ((style_index == 0) and not got_orb_gen and (current_turn < cutoffs[1])):
        research_priority = settings[0] * industry_priority  # high research at beginning of game to get easy gro tech and to get research booster Algotrithmic Elegance
    elif (not got_orb_gen) or (current_turn < cutoffs[1]):
        research_priority = settings[1] * industry_priority  # med-high research
    elif current_turn < cutoffs[2]:
        research_priority = settings[2] * industry_priority  # med-high industry
    elif current_turn < cutoffs[3]:
        research_priority = settings[3] * industry_priority  # med-high industry
    else:
        research_queue = list(empire.researchQueue)
        research_priority = settings[4] * industry_priority  # high industry , low research
        if len(research_queue) == 0:
            research_priority = 0  # done with research
        elif len(research_queue) < 5 and research_queue[-1].allocation > 0:
            research_priority = len(research_queue) * 0.01 * industry_priority  # barely not done with research
        elif len(research_queue) < 10 and research_queue[-1].allocation > 0:
            research_priority = (4 + 2 * len(research_queue)) * 0.01 * industry_priority  # almost done with research
        elif len(research_queue) < 20 and research_queue[int(len(research_queue) / 2)].allocation > 0:
            research_priority *= 0.7  # closing in on end of research
    if industry_surge:
        if galaxy_is_sparse and not any(enemies_sighted):
            research_priority *= 0.5
        else:
            research_priority *= 0.8
                
    if ((tech_is_complete("SHP_WEAPON_2_4") or
         tech_is_complete("SHP_WEAPON_4_1")) and
            tech_is_complete(AIDependencies.PROD_AUTO_NAME)):
        # industry_factor = [ [0.25, 0.2], [0.3, 0.25], [0.3, 0.25] ][style_index ]
        # researchPriority = min(researchPriority, industry_factor[got_solar_gen]*industryPriority)
        research_priority *= 0.9
    if got_quant:
        research_priority = min(research_priority + 0.1 * industry_priority, research_priority * 1.3)
    research_priority = int(research_priority)
    print "Research Production (current/target) : ( %.1f / %.1f )" % (total_rp, target_rp)
    print "Priority for Research: %d (new target ~ %d RP)" % (research_priority, total_pp * research_priority / industry_priority)

    if len(enemies_sighted) < (2 + current_turn/20.0):  # TODO: adjust for colonisation priority
        research_priority *= 1.2
    if (current_turn > 20) and (len(recent_enemies) > 3):
        research_priority *= 0.8
    return research_priority
Esempio n. 21
0
def 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 suppliable 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_suppliable_system_ids = empire.fleetSupplyableSystemIDs
    fleet_suppliable_planet_ids = PlanetUtilsAI.get_planets_in__systems_ids(fleet_suppliable_system_ids)

    prime_invadable_system_ids = set(ColonisationAI.annexable_system_ids)
    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(acessible_planet_ids)  # need these for unpopulated outposts
    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)

    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)
    invasion_targeted_planet_ids.extend(get_invasion_targeted_planet_ids(universe.planetIDs, EnumsAI.AIFleetMissionType.FLEET_MISSION_ORBITAL_INVASION))
    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: %s" % 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:    %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 ColonisationAI.empire_species_systems.get(sys_id,  {}).get('pids', []):
                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([EnumsAI.AIFleetMissionType.FLEET_MISSION_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.getBestShipInfo(EnumsAI.AIPriorityType.PRIORITY_PRODUCTION_ORBITAL_INVASION, loc)[1]
            if best_base_trooper_here == None:  # shouldn't be possible at this point, but just to be safe
                continue
            # TODO: have TroopShipDesigner give the expected number of troops including species effects
            troops_per_ship = best_base_trooper_here.troopCapacity
            if not troops_per_ship:
                continue
            this_score,  p_troops = evaluate_invasion_planet(pid, empire, secure_ai_fleet_missions, False)
            best_ship, col_design, build_choices = ProductionAI.getBestShipInfo(EnumsAI.AIPriorityType.PRIORITY_PRODUCTION_ORBITAL_INVASION, loc)
            if not best_ship:
                continue
            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(best_ship, 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))
    print "Evaluating potential invasions, PlanetIDs: %s" % evaluated_planet_ids

    evaluated_planets = assign_invasion_values(evaluated_planet_ids, EnumsAI.AIFleetMissionType.FLEET_MISSION_INVASION, fleet_suppliable_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]

    print
    if sorted_planets:
        print "Invadable planets:\n%-6s | %-6s | %-16s | %-16s | Troops" % ('ID', 'Score', 'Name', 'Race')
        for pid, pscore, ptroops in sorted_planets:
            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"

    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()
Esempio n. 22
0
def 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 suppliable 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_suppliable_system_ids = empire.fleetSupplyableSystemIDs
    fleet_suppliable_planet_ids = PlanetUtilsAI.get_planets_in__systems_ids(fleet_suppliable_system_ids)

    prime_invadable_system_ids = set(ColonisationAI.annexable_system_ids)
    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(acessible_planet_ids)  # need these for unpopulated outposts
    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)

    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)
    invasion_targeted_planet_ids.extend(get_invasion_targeted_planet_ids(universe.planetIDs, EnumsAI.AIFleetMissionType.FLEET_MISSION_ORBITAL_INVASION))
    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: %s" % 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:    %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 ColonisationAI.empire_species_systems.get(sys_id,  {}).get('pids', []):
                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([EnumsAI.AIFleetMissionType.FLEET_MISSION_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]
            this_score,  p_troops = evaluate_invasion_planet(pid, empire, secure_ai_fleet_missions, False)
            best_ship, col_design, build_choices = ProductionAI.getBestShipInfo(EnumsAI.AIPriorityType.PRIORITY_PRODUCTION_ORBITAL_INVASION, loc)
            if not best_ship:
                continue
            n_bases = math.ceil((p_troops+1) / 2)  # TODO: reconsider this +1 safety factor
            retval = fo.issueEnqueueShipProductionOrder(best_ship, 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))
    print "Evaluating potential invasions, PlanetIDs: %s" % evaluated_planet_ids

    evaluated_planets = assign_invasion_values(evaluated_planet_ids, EnumsAI.AIFleetMissionType.FLEET_MISSION_INVASION, fleet_suppliable_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]

    print
    if sorted_planets:
        print "Invadable planets:\n%-6s | %-6s | %-16s | %-16s | Troops" % ('ID', 'Score', 'Name', 'Race')
        for pid, pscore, ptroops in sorted_planets:
            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"

    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()
Esempio n. 23
0
def set_planet_resource_foci():
    """set resource focus of planets """
    newFoci = {}

    print "\n============================"
    print "Collecting info to assess Planet Focus Changes\n"
    universe = fo.getUniverse()
    empire = fo.getEmpire()
    currentTurn = fo.currentTurn()
    # set the random seed (based on galaxy seed, empire ID and current turn)
    # for game-reload consistency 
    freq = min(3, (max(5, currentTurn - 80)) / 4.0) ** (1.0 / 3)
    if not (limitAssessments and (abs(currentTurn - lastFociCheck[0]) < 1.5 * freq) and (random.random() < 1.0 / freq)):
        lastFociCheck[0] = currentTurn
        resource_timer.start("getPlanets")
        empirePlanetIDs = list(PlanetUtilsAI.get_owned_planets_by_empire(universe.planetIDs))
        resource_timer.start("Filter")
        resource_timer.start("Priority")
        # TODO: take into acct splintering of resource groups
        # fleetSupplyableSystemIDs = empire.fleetSupplyableSystemIDs
        # fleetSupplyablePlanetIDs = PlanetUtilsAI.get_planets_in__systems_ids(fleetSupplyableSystemIDs)
        ppPrio = foAI.foAIstate.get_priority(AIPriorityType.PRIORITY_RESOURCE_PRODUCTION)
        rpPrio = foAI.foAIstate.get_priority(AIPriorityType.PRIORITY_RESOURCE_RESEARCH)
        priorityRatio = float(rpPrio) / (ppPrio + 0.0001)
        resource_timer.start("Shuffle")
        # not supporting Growth for general planets until also adding code to make sure would actually benefit
        # shuffle(generalPlanetIDs)
        resource_timer.start("Targets")
        planets = map(universe.getPlanet, empirePlanetIDs)
        planetMap.clear()
        planetMap.update(zip(empirePlanetIDs, planets))
        if useGrowth:
            for metab, metabIncPop in ColonisationAI.empire_metabolisms.items():
                for special in [aspec for aspec in AIDependencies.metabolismBoostMap.get(metab, []) if aspec in ColonisationAI.available_growth_specials]:
                    rankedPlanets = []
                    for pid in ColonisationAI.available_growth_specials[special]:
                        planet = planetMap[pid]
                        cur_focus = planet.focus
                        pop = planet.currentMeterValue(fo.meterType.population)
                        if (pop > metabIncPop - 2 * planet.size) or (GFocus not in planet.availableFoci):  # not enough benefit to lose local production, or can't put growth focus here
                            continue
                        for special2 in ["COMPUTRONIUM_SPECIAL"]:
                            if special2 in planet.specials:
                                break
                        else:  # didn't have any specials that would override interest in growth special
                            print "Considering Growth Focus for %s (%d) with special %s; planet has pop %.1f and %s metabolism incremental pop is %.1f" % (
                                planet.name, pid, special, pop, metab, metabIncPop)
                            if cur_focus == GFocus:
                                pop -= 4  # discourage changing current focus to minimize focus-changing penalties
                            rankedPlanets.append((pop, pid, cur_focus))
                    if not rankedPlanets:
                        continue
                    rankedPlanets.sort()
                    print "Considering Growth Focus choice for special %s; possible planet pop, id pairs are %s" % (metab, rankedPlanets)
                    for spSize, spPID, cur_focus in rankedPlanets:  # index 0 should be able to set focus, but just in case...
                        result = 1
                        if cur_focus != GFocus:
                            result = fo.issueChangeFocusOrder(spPID, GFocus)
                        if result == 1:
                            newFoci[spPID] = GFocus
                            if spPID in empirePlanetIDs:
                                del empirePlanetIDs[empirePlanetIDs.index(spPID)]
                            print "%s focus of planet %s (%d) at Growth Focus" % (["set", "left"][cur_focus == GFocus], planetMap[spPID].name, spPID)
                            break
                        else:
                            print "failed setting focus of planet %s (%d) at Growth Focus; focus left at %s" % (planetMap[spPID].name, spPID, planetMap[spPID].focus)
        already_have_comp_moon = False
        for pid in empirePlanetIDs:
            planet = planetMap[pid]
            if "COMPUTRONIUM_SPECIAL" in planet.specials and RFocus in planet.availableFoci and not already_have_comp_moon:
                curFocus = planet.focus
                newFoci[pid] = RFocus
                result = 0
                if curFocus != RFocus:
                    result = fo.issueChangeFocusOrder(pid, RFocus)
                    if result == 1:
                        universe.updateMeterEstimates(empirePlanetIDs)
                if curFocus == RFocus or result == 1:
                    already_have_comp_moon = True
                    print "%s focus of planet %s (%d) (with Computronium Moon) at Research Focus" % (["set", "left"][curFocus == RFocus], planetMap[pid].name, pid)
                    if pid in empirePlanetIDs:
                        del empirePlanetIDs[empirePlanetIDs.index(pid)]
                    continue
            if ((([bld.buildingTypeName for bld in map(universe.getObject, planet.buildingIDs) if bld.buildingTypeName in
                    ["BLD_CONC_CAMP", "BLD_CONC_CAMP_REMNANT"]] != []) or
                             ([ccspec for ccspec in planet.specials if ccspec in ["CONC_CAMP_MASTER_SPECIAL", "CONC_CAMP_SLAVE_SPECIAL"]] != []))
                    and IFocus in planet.availableFoci):
                curFocus = planet.focus
                newFoci[pid] = IFocus
                result = 0
                if curFocus != IFocus:
                    result = fo.issueChangeFocusOrder(pid, IFocus)
                    if result == 1:
                        print ("Tried setting %s for Concentration Camp planet %s (%d) with species %s and current focus %s, got result %d and focus %s" %
                               (newFoci[pid], planet.name, pid, planet.speciesName, curFocus, result, planetMap[pid].focus))
                        universe.updateMeterEstimates(empirePlanetIDs)
                    if (result != 1) or planetMap[pid].focus != IFocus:
                        newplanet = universe.getPlanet(pid)
                        print ("Error: Failed setting %s for Concentration Camp planet %s (%d) with species %s and current focus %s, but new planet copy shows %s" %
                               (newFoci[pid], planetMap[pid].name, pid, planetMap[pid].speciesName, planetMap[pid].focus, newplanet.focus))
                if curFocus == IFocus or result == 1:
                    print "%s focus of planet %s (%d) (with Concentration Camps/Remnants) at Industry Focus" % (["set", "left"][curFocus == IFocus], planetMap[pid].name, pid)
                    if pid in empirePlanetIDs:
                        del empirePlanetIDs[empirePlanetIDs.index(pid)]
                    continue
        # pp, rp = get_resource_target_totals(empirePlanetIDs, planetMap)
        pp, rp = get_resource_target_totals(planetMap.keys())

        for pid in empirePlanetIDs:
            planet = planetMap[pid]
            if PFocus in planet.availableFoci and assess_protection_focus(pid):
                curFocus = planet.focus
                newFoci[pid] = PFocus
                result = 0
                if curFocus != PFocus:
                    result = fo.issueChangeFocusOrder(pid, PFocus)
                    if result == 1:
                        print ("Tried setting %s for planet %s (%d) with species %s and current focus %s, got result %d and focus %s" %
                               (newFoci[pid], planet.name, pid, planet.speciesName, curFocus, result, planet.focus))
                        universe.updateMeterEstimates(empirePlanetIDs)
                    if (result != 1) or planet.focus != PFocus:
                        newplanet = universe.getPlanet(pid)
                        print ("Error: Failed setting %s for planet %s (%d) with species %s and current focus %s, but new planet copy shows %s" %
                               (newFoci[pid], planet.name, pid, planet.speciesName, planet.focus, newplanet.focus))
                if curFocus == PFocus or result == 1:
                    print "%s focus of planet %s (%d) at Protection(Defense) Focus" % (["set", "left"][curFocus == PFocus], planet.name, pid)
                    if pid in empirePlanetIDs:
                        del empirePlanetIDs[empirePlanetIDs.index(pid)]
                    continue

        print "\n-----------------------------------------"
        print "Making Planet Focus Change Determinations\n"

        ratios = []
        # for each planet, calculate RP:PP value ratio at which industry/Mining focus and research focus would have the same total value, & sort by that
        # include a bias to slightly discourage changing foci
        curTargetPP = 0.001
        curTargetRP = 0.001
        resource_timer.start("Loop")  # loop
        has_force = tech_is_complete("CON_FRC_ENRG_STRC")
        preset_ids = set(planetMap.keys()) - set(empirePlanetIDs)
        ctPP0, ctRP0 = 0, 0
        for pid in preset_ids:
            nPP, nRP = newTargets.get(pid, {}).get(planetMap[pid].focus, [0, 0])
            curTargetPP += nPP
            curTargetRP += nRP
            iPP, iRP = newTargets.get(pid, {}).get(IFocus, [0, 0])
            ctPP0 += iPP
            ctRP0 += iRP

        id_set = set(empirePlanetIDs)
        for adj_round in [1, 2, 3, 4]:
            maxi_ratio = ctRP0 / max(0.01, ctPP0)  # should only change between rounds 1 and 2
            for pid in list(id_set):
                if adj_round == 1:  # tally max Industry
                    iPP, iRP = newTargets.get(pid, {}).get(IFocus, [0, 0])
                    ctPP0 += iPP
                    ctRP0 += iRP
                    continue
                II, IR = newTargets[pid][IFocus]
                RI, RR = newTargets[pid][RFocus]
                CI, CR = currentOutput[pid][IFocus], currentOutput[pid][RFocus]
                research_penalty = (currentFocus[pid] != RFocus)
                # calculate factor F at which II + F * IR == RI + F * RR =====> F = ( II-RI ) / (RR-IR)
                thisFactor = (II - RI) / max(0.01, RR - IR)  # don't let denominator be zero for planets where focus doesn't change RP
                planet = planetMap[pid]
                if adj_round == 2:  # take research at planets with very cheap research
                    if (maxi_ratio < priorityRatio) and (curTargetRP < priorityRatio * ctPP0) and (thisFactor <= 1.0):
                        curTargetPP += RI
                        curTargetRP += RR
                        newFoci[pid] = RFocus
                        id_set.discard(pid)
                    continue
                if adj_round == 3:  # take research at planets where can do reasonable balance
                    if has_force or (foAI.foAIstate.aggression < fo.aggression.aggressive) or (curTargetRP >= priorityRatio * ctPP0):
                        continue
                    pop = planet.currentMeterValue(fo.meterType.population)
                    t_pop = planet.currentMeterValue(fo.meterType.targetPopulation)
                    # if AI is aggressive+, and this planet in range where temporary Research focus can get an additional RP at cost of 1 PP, and still need some RP, then do it
                    if pop < t_pop - 5:
                        continue
                    if (CI > II + 8) or (((RR > II) or ((RR - CR) >= 1 + 2 * research_penalty)) and ((RR - IR) >= 3) and ((CR - IR) >= 0.7 * ((II - CI) * (1 + 0.1 * research_penalty)))):
                        curTargetPP += CI - 1 - research_penalty
                        curTargetRP += CR + 1
                        newFoci[pid] = RFocus
                        id_set.discard(pid)
                    continue
                # adj_round == 4 assume default IFocus
                curTargetPP += II  # icurTargets initially calculated by Industry focus, which will be our default focus
                curTargetRP += IR
                newFoci[pid] = IFocus
                ratios.append((thisFactor, pid))

        ratios.sort()
        printedHeader = False
        fociMap = {IFocus: "Industry", RFocus: "Research", MFocus: "Mining", GFocus: "Growth", PFocus: "Defense"}
        gotAlgo = tech_is_complete("LRN_ALGO_ELEGANCE")
        for ratio, pid in ratios:
            do_research = False  # (newFoci[pid]==RFocus)
            if (priorityRatio < (curTargetRP / (curTargetPP + 0.0001))) and not do_research:  # we have enough RP
                if ratio < 1.1 and foAI.foAIstate.aggression > fo.aggression.cautious:  # but wait, RP is still super cheap relative to PP, maybe will take more RP
                    if priorityRatio < 1.5 * (curTargetRP / (curTargetPP + 0.0001)):  # yeah, really a glut of RP, stop taking RP
                        break
                else:  # RP not super cheap & we have enough, stop taking it
                    break
            II, IR = newTargets[pid][IFocus]
            RI, RR = newTargets[pid][RFocus]
            # if currentFocus[pid] == MFocus:
            # II = max( II, newTargets[pid][MFocus][0] )
            if do_research or (gotAlgo and (
                   (ratio > 2.0 and curTargetPP < 15) or
                   (ratio > 2.5 and curTargetPP < 25 and II > 5) or
                   (ratio > 3.0 and curTargetPP < 40 and II > 5) or
                   (ratio > 4.0 and curTargetPP < 100 and II > 10) or
                   ((curTargetRP + RR - IR) / max(0.001, curTargetPP - II + RI) > 2 * priorityRatio))):  # we already have algo elegance and more RP would be too expensive, or overkill
                if not printedHeader:
                    printedHeader = True
                    print "Rejecting further Research Focus choices as too expensive:"
                    print "%34s|%20s|%15s |%15s|%15s |%15s |%15s" % ("                      Planet ", " current RP/PP ", " current target RP/PP ", "current Focus ", "  rejectedFocus ", " rejected target RP/PP ", "rejected RP-PP EQF")
                    oldFocus = currentFocus[pid]
                    cPP, cRP = currentOutput[pid][IFocus], currentOutput[pid][RFocus]
                    otPP, otRP = newTargets[pid].get(oldFocus, (0, 0))
                    ntPP, ntRP = newTargets[pid].get(RFocus, (0, 0))
                    print "pID (%3d) %22s | c: %5.1f / %5.1f | cT: %5.1f / %5.1f |  cF: %8s | nF: %8s | cT: %5.1f / %5.1f | %.2f" % (pid, planetMap[pid].name, cRP, cPP, otRP, otPP, fociMap.get(oldFocus, 'unknown'), fociMap[RFocus], ntRP, ntPP, ratio)
                    continue  # RP is getting too expensive, but might be willing to still allocate from a planet with less PP to lose
            # if planetMap[pid].currentMeterValue(fo.meterType.targetPopulation) >0: #only set to research if pop won't die out
            newFoci[pid] = RFocus
            curTargetRP += (RR - IR)
            curTargetPP -= (II - RI)
        print "============================"
        print "Planet Focus Assignments to achieve target RP/PP ratio of %.2f from current ratio of %.2f ( %.1f / %.1f )" % (priorityRatio, rp / (pp + 0.0001), rp, pp)
        print "Max Industry assignments would result in target RP/PP ratio of %.2f ( %.1f / %.1f )" % (ctRP0 / (ctPP0 + 0.0001), ctRP0, ctPP0)
        print "-------------------------------------"
        print "%34s|%20s|%15s |%15s|%15s |%15s " % ("                      Planet ", " current RP/PP ", " current target RP/PP ", "current Focus ", "  newFocus ", " new target RP/PP ")
        totalChanged = 0
        for id_set in empirePlanetIDs, preset_ids:
            for pid in id_set:
                canFocus = planetMap[pid].currentMeterValue(fo.meterType.targetPopulation) > 0
                oldFocus = currentFocus[pid]
                newFocus = newFoci.get(pid, IFocus)
                cPP, cRP = currentOutput[pid][IFocus], currentOutput[pid][RFocus]
                otPP, otRP = newTargets[pid].get(oldFocus, (0, 0))
                ntPP, ntRP = otPP, otRP
                if newFocus != oldFocus and newFocus in planetMap[pid].availableFoci:  # planetMap[pid].focus
                    totalChanged += 1
                    if newFocus != planetMap[pid].focus:
                        result = fo.issueChangeFocusOrder(pid, newFocus)
                        if result == 1:
                            ntPP, ntRP = newTargets[pid].get(newFocus, (0, 0))
                        else:
                            print "Trouble changing focus of planet %s (%d) to %s" % (planetMap[pid].name, pid, newFocus)
                print "pID (%3d) %22s | c: %5.1f / %5.1f | cT: %5.1f / %5.1f |  cF: %8s | nF: %8s | cT: %5.1f / %5.1f " % (pid, planetMap[pid].name, cRP, cPP, otRP, otPP, fociMap.get(oldFocus, 'unknown'), fociMap[newFocus], ntRP, ntPP)
            print "-------------------------------------"
        print "-------------------------------------"
        print "Final Ratio Target (turn %4d) RP/PP : %.2f ( %.1f / %.1f ) after %d Focus changes" % (fo.currentTurn(), curTargetRP / (curTargetPP + 0.0001), curTargetRP, curTargetPP, totalChanged)
        resource_timer.end()
    aPP, aRP = empire.productionPoints, empire.resourceProduction(fo.resourceType.research)
    # Next string used in charts. Don't modify it!
    print "Current Output (turn %4d) RP/PP : %.2f ( %.1f / %.1f )" % (fo.currentTurn(), aRP / (aPP + 0.0001), aRP, aPP)
    print "------------------------"
    print "ResourcesAI Time Requirements:"
Esempio n. 24
0
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()
Esempio n. 25
0
def get_military_fleets(mil_fleets_ids=None, try_reset=True, thisround="Main"):
    """Get armed military fleets."""
    global _military_allocations

    universe = fo.getUniverse()
    empire_id = fo.empireID()
    home_system_id = PlanetUtilsAI.get_capital_sys_id()

    all_military_fleet_ids = (mil_fleets_ids if mil_fleets_ids is not None
                              else FleetUtilsAI.get_empire_fleet_ids_by_role(MissionType.MILITARY))

    if try_reset and (fo.currentTurn() + empire_id) % 30 == 0 and thisround == "Main":
        try_again(all_military_fleet_ids, try_reset=False, thisround=thisround + " Reset")
        return

    mil_fleets_ids = list(FleetUtilsAI.extract_fleet_ids_without_mission_types(all_military_fleet_ids))
    mil_needing_repair_ids, mil_fleets_ids = avail_mil_needing_repair(mil_fleets_ids, split_ships=True)
    avail_mil_rating = sum(map(CombatRatingsAI.get_fleet_rating, mil_fleets_ids))

    if not mil_fleets_ids:
        if "Main" in thisround:
            _military_allocations = []
        return []

    # for each system, get total rating of fleets assigned to it
    already_assigned_rating = {}
    systems_status = foAI.foAIstate.systemStatus
    enemy_sup_factor = {}  # enemy supply
    for sys_id in universe.systemIDs:
        already_assigned_rating[sys_id] = 0
        enemy_sup_factor[sys_id] = min(2, len(systems_status.get(sys_id, {}).get('enemies_nearly_supplied', [])))
    for fleet_id in [fid for fid in all_military_fleet_ids if fid not in mil_fleets_ids]:
        ai_fleet_mission = foAI.foAIstate.get_fleet_mission(fleet_id)
        if not ai_fleet_mission.target:  # shouldn't really be possible
            continue
        last_sys = ai_fleet_mission.target.get_system().id  # will count this fleet as assigned to last system in target list  # TODO last_sys or target sys?
        this_rating = CombatRatingsAI.get_fleet_rating(fleet_id)
        already_assigned_rating[last_sys] = CombatRatingsAI.combine_ratings(already_assigned_rating.get(last_sys, 0), this_rating)
    for sys_id in universe.systemIDs:
        my_defense_rating = systems_status.get(sys_id, {}).get('mydefenses', {}).get('overall', 0)
        already_assigned_rating[sys_id] = CombatRatingsAI.combine_ratings(my_defense_rating, already_assigned_rating[sys_id])
        if _verbose_mil_reporting and already_assigned_rating[sys_id]:
            print "\t System %s already assigned rating %.1f" % (
                universe.getSystem(sys_id), already_assigned_rating[sys_id])

    # get systems to defend
    capital_id = PlanetUtilsAI.get_capital()
    if capital_id is not None:
        capital_planet = universe.getPlanet(capital_id)
    else:
        capital_planet = None
    # TODO: if no owned planets try to capture one!
    if capital_planet:
        capital_sys_id = capital_planet.systemID
    else:  # should be rare, but so as to not break code below, pick a randomish mil-centroid system
        capital_sys_id = None  # unless we can find one to use
        system_dict = {}
        for fleet_id in all_military_fleet_ids:
            status = foAI.foAIstate.fleetStatus.get(fleet_id, None)
            if status is not None:
                sys_id = status['sysID']
                if not list(universe.getSystem(sys_id).planetIDs):
                    continue
                system_dict[sys_id] = system_dict.get(sys_id, 0) + status.get('rating', 0)
        ranked_systems = sorted([(val, sys_id) for sys_id, val in system_dict.items()])
        if ranked_systems:
            capital_sys_id = ranked_systems[-1][-1]
        else:
            try:
                capital_sys_id = foAI.foAIstate.fleetStatus.items()[0][1]['sysID']
            except:
                pass

    num_targets = max(10, PriorityAI.allotted_outpost_targets)
    top_target_planets = ([pid for pid, pscore, trp in AIstate.invasionTargets[:PriorityAI.allottedInvasionTargets] if pscore > 20] +
                          [pid for pid, (pscore, spec) in foAI.foAIstate.colonisableOutpostIDs.items()[:num_targets] if pscore > 20] +
                          [pid for pid, (pscore, spec) in foAI.foAIstate.colonisablePlanetIDs.items()[:num_targets] if pscore > 20])
    top_target_planets.extend(foAI.foAIstate.qualifyingTroopBaseTargets.keys())
    base_col_target_systems = PlanetUtilsAI.get_systems(top_target_planets)
    top_target_systems = []
    for sys_id in AIstate.invasionTargetedSystemIDs + base_col_target_systems:
        if sys_id not in top_target_systems:
            if foAI.foAIstate.systemStatus[sys_id]['totalThreat'] > get_tot_mil_rating():
                continue
            top_target_systems.append(sys_id)  # doing this rather than set, to preserve order

    try:
        # capital defense
        allocation_helper = AllocationHelper(already_assigned_rating, avail_mil_rating, try_reset)
        if capital_sys_id is not None:
            CapitalDefenseAllocator(capital_sys_id, allocation_helper).allocate()

        # defend other planets
        empire_planet_ids = PlanetUtilsAI.get_owned_planets_by_empire(universe.planetIDs)
        empire_occupied_system_ids = list(set(PlanetUtilsAI.get_systems(empire_planet_ids)) - {capital_sys_id})
        for sys_id in empire_occupied_system_ids:
            PlanetDefenseAllocator(sys_id, allocation_helper).allocate()

        # attack / protect high priority targets
        for sys_id in top_target_systems:
            TopTargetAllocator(sys_id, allocation_helper).allocate()

        # enemy planets
        other_targeted_system_ids = [sys_id for sys_id in set(PlanetUtilsAI.get_systems(AIstate.opponentPlanetIDs)) if
                                     sys_id not in top_target_systems]
        for sys_id in other_targeted_system_ids:
            TargetAllocator(sys_id, allocation_helper).allocate()

        # colony / outpost targets
        other_targeted_system_ids = [sys_id for sys_id in
                                     list(set(AIstate.colonyTargetedSystemIDs + AIstate.outpostTargetedSystemIDs)) if
                                     sys_id not in top_target_systems]
        for sys_id in other_targeted_system_ids:
            OutpostTargetAllocator(sys_id, allocation_helper).allocate()

        # TODO blockade enemy systems

        # interior systems
        targetable_ids = set(ColonisationAI.systems_by_supply_tier.get(0, []) +
                             ColonisationAI.systems_by_supply_tier.get(1, []))
        current_mil_systems = [sid for sid, _, _, _ in allocation_helper.allocations]
        interior_targets1 = targetable_ids.difference(current_mil_systems)
        interior_targets = [sid for sid in interior_targets1 if (
            allocation_helper.threat_bias + systems_status.get(sid, {}).get('totalThreat', 0) > 0.8 * allocation_helper.already_assigned_rating[sid])]
        for sys_id in interior_targets:
            InteriorTargetsAllocator(sys_id, allocation_helper).allocate()

        # TODO Exploration targets

        # border protections
        visible_system_ids = foAI.foAIstate.visInteriorSystemIDs.keys() + foAI.foAIstate.visBorderSystemIDs.keys()
        accessible_system_ids = [sys_id for sys_id in visible_system_ids if
                                 universe.systemsConnected(sys_id, home_system_id, empire_id)]
        current_mil_systems = [sid for sid, alloc, take_any, _ in allocation_helper.allocations if alloc > 0]
        border_targets1 = [sid for sid in accessible_system_ids if sid not in current_mil_systems]
        border_targets = [sid for sid in border_targets1 if (
            allocation_helper.threat_bias + systems_status.get(sid, {}).get('fleetThreat', 0) + systems_status.get(sid, {}).get(
                    'planetThreat', 0) > 0.8 * allocation_helper.already_assigned_rating[sid])]
        for sys_id in border_targets:
            BorderSecurityAllocator(sys_id, allocation_helper).allocate()
    except ReleaseMilitaryException:
        try_again(all_military_fleet_ids)
        return

    allocation_groups = allocation_helper.allocation_by_groups
    allocations = allocation_helper.allocations

    new_allocations = []
    remaining_mil_rating = avail_mil_rating
    # for top categories assign max_alloc right away as available
    for cat in ['capitol', 'occupied', 'topTargets']:
        for sid, alloc, take_any, max_alloc in allocation_groups.get(cat, []):
            if remaining_mil_rating <= 0:
                break
            this_alloc = min(remaining_mil_rating, max_alloc)
            new_allocations.append((sid, this_alloc, alloc, take_any))
            remaining_mil_rating -= this_alloc

    base_allocs = set()
    # for lower priority categories, first assign base_alloc around to all, then top up as available
    for cat in ['otherTargets', 'accessibleTargets', 'exploreTargets']:
        for sid, alloc, take_any, max_alloc in allocation_groups.get(cat, []):
            if remaining_mil_rating <= 0:
                break
            base_allocs.add(sid)
            remaining_mil_rating -= alloc
    for cat in ['otherTargets', 'accessibleTargets', 'exploreTargets']:
        for sid, alloc, take_any, max_alloc in allocation_groups.get(cat, []):
            if sid not in base_allocs:
                break
            if remaining_mil_rating <= 0:
                new_allocations.append((sid, alloc, alloc, take_any))
            else:
                new_rating = min(remaining_mil_rating + alloc, max_alloc)
                new_allocations.append((sid, new_rating, alloc, take_any))
                remaining_mil_rating -= (new_rating - alloc)

    if "Main" in thisround:
        _military_allocations = new_allocations
    _min_mil_allocations.clear()
    _min_mil_allocations.update([(sid, alloc) for sid, alloc, take_any, _ in allocations])
    if _verbose_mil_reporting or "Main" in thisround:
        print "------------------------------\nFinal %s Round Military Allocations: %s \n-----------------------" % (thisround, dict([(sid, alloc) for sid, alloc, minalloc, take_any in new_allocations]))
        print "(Apparently) remaining military rating: %.1f" % remaining_mil_rating

    # export military systems for other AI modules
    if "Main" in thisround:
        AIstate.militarySystemIDs = list(set([sid for sid, alloc, minalloc, take_any in new_allocations]).union(
                [sid for sid in allocation_helper.already_assigned_rating
                 if allocation_helper.already_assigned_rating[sid] > 0]))
    else:
        AIstate.militarySystemIDs = list(set([sid for sid, alloc, minalloc, take_any in new_allocations]).union(AIstate.militarySystemIDs))
    return new_allocations
Esempio n. 26
0
def get_military_fleets(milFleetIDs=None, tryReset=True, thisround="Main"):
    """get armed military fleets"""
    global MilitaryAllocations, totMilRating, num_milships

    universe = fo.getUniverse()
    empire = fo.getEmpire()
    empireID = empire.empireID
    capitalID = PlanetUtilsAI.get_capital()
    if capitalID is None:
        homeworld=None
    else:
        homeworld = universe.getPlanet(capitalID)
    if homeworld:
        homeSystemID = homeworld.systemID
    else:
        homeSystemID=-1

    if milFleetIDs is not None:
        allMilitaryFleetIDs = milFleetIDs
    else:
        allMilitaryFleetIDs = FleetUtilsAI.get_empire_fleet_ids_by_role(AIFleetMissionType.FLEET_MISSION_MILITARY )
    if tryReset and ((fo.currentTurn()+empireID) % 30 ==0) and thisround=="Main": 
        try_again(allMilitaryFleetIDs, try_reset=False, thisround = thisround+" Reset")

    num_milships = 0
    for fid in allMilitaryFleetIDs:
        num_milships += foAI.foAIstate.fleetStatus.get(fid, {}).get('nships', 0)

    thisTotMilRating = sum( map(lambda x: foAI.foAIstate.get_rating(x).get('overall', 0), allMilitaryFleetIDs )  )
    if "Main" in thisround:
        totMilRating = thisTotMilRating
        print "=================================================="
        print "%s Round Total Military Rating: %d"%(thisround, totMilRating)
        print "---------------------------------"
        foAI.foAIstate.militaryRating=totMilRating

    milFleetIDs = list( FleetUtilsAI.extract_fleet_ids_without_mission_types(allMilitaryFleetIDs))
    mil_needing_repair_ids, milFleetIDs = avail_mil_needing_repair(milFleetIDs, split_ships=True)
    availMilRating = sum( map(lambda x: foAI.foAIstate.get_rating(x).get('overall', 0), milFleetIDs )  )
    if "Main" in thisround:
        print "=================================================="
        print "%s Round Available Military Rating: %d"%(thisround, availMilRating)
        print "---------------------------------"
    remainingMilRating = availMilRating
    allocations = []
    allocationGroups={}

    if not milFleetIDs:
        if "Main" in thisround:
            MilitaryAllocations = []
        return []

    #for each system, get total rating of fleets assigned to it
    alreadyAssignedRating={}
    assignedAttack={}
    assignedHP={}
    for sysID in universe.systemIDs:
        assignedAttack[sysID]=0
        assignedHP[sysID]=0
    for fleetID in [fid for fid in allMilitaryFleetIDs if fid not in milFleetIDs]:
        aiFleetMission = foAI.foAIstate.get_fleet_mission(fleetID)
        sysTargets= []
        for aiFleetMissionType in aiFleetMission.get_mission_types():
            aiTargets = aiFleetMission.get_targets(aiFleetMissionType)
            for aiTarget in aiTargets:
                sysTargets.extend(aiTarget.get_required_system_ai_targets())
        if not sysTargets: #shouldn't really be possible
            continue
        lastSys = sysTargets[-1].target_id # will count this fleet as assigned to last system in target list
        assignedAttack[lastSys] += foAI.foAIstate.get_rating(fleetID).get('attack', 0)
        assignedHP[lastSys] += foAI.foAIstate.get_rating(fleetID).get('health', 0)
    for sysID in universe.systemIDs:
        mydefenses = foAI.foAIstate.systemStatus.get(sysID, {}).get( 'mydefenses', {} )
        mypattack = mydefenses.get('attack', 0)
        myphealth = mydefenses.get('health', 0)
        alreadyAssignedRating[sysID] = ( assignedAttack[sysID] + mypattack ) * ( assignedHP[sysID] + myphealth )

    # get systems to defend
    capitalID = PlanetUtilsAI.get_capital()
    if capitalID is not None:
        capitalPlanet = universe.getPlanet(capitalID)
    else:
        capitalPlanet=None
    #TODO: if no owned planets try to capture one!
    if capitalPlanet:
        capitalSysID = capitalPlanet.systemID
    else: # should be rare, but so as to not break code below, pick a randomish mil-centroid system
        capitalSysID=None #unless we can find one to use
        systemDict = {}
        for fleetID in allMilitaryFleetIDs:
            status = foAI.foAIstate.fleetStatus.get(fleetID, None)
            if status is not None:
                sysID = status['sysID']
                if len( list( universe.getSystem(sysID).planetIDs ) ) ==0:
                    continue
                systemDict[sysID] = systemDict.get( sysID, 0) + status.get('rating', {}).get('overall', 0)
        rankedSystems = sorted( [(val, sysID) for sysID, val in systemDict.items() ]   )
        if rankedSystems:
            capitalSysID = rankedSystems[-1][-1]
        else:
            try:
                capitalSysID = foAI.foAIstate.fleetStatus.items()[0][1]['sysID']
            except:
                pass

    if False:
        if fo.currentTurn() < 20:
            threatBias = 0
        elif fo.currentTurn() < 40:
            threatBias = 10
        elif fo.currentTurn() < 60:
            threatBias = 80
        elif fo.currentTurn() < 80:
            threatBias = 200
        else:
            threatBias = 400
    else:
        threatBias = 0

    safetyFactor = [ 4.0, 3.0, 2.0, 1.5, 1.2, 1.0 ][foAI.foAIstate.aggression]

    topTargetPlanets = [pid for pid, pscore, trp in AIstate.invasionTargets[:PriorityAI.allottedInvasionTargets] if pscore > 20] + [pid for pid, pscore in foAI.foAIstate.colonisablePlanetIDs[:10] if pscore > 20]
    topTargetPlanets.extend( foAI.foAIstate.qualifyingTroopBaseTargets.keys() )
    topTargetSystems = []
    for sysID in AIstate.invasionTargetedSystemIDs + PlanetUtilsAI.get_systems( topTargetPlanets ):
        if sysID not in topTargetSystems:
            topTargetSystems.append(sysID) #doing this rather than set, to preserve order

    # allocation format: ( sysID, newAllocation, takeAny, maxMultiplier )
    #================================
    #--------Capital Threat ----------
    capitalThreat = safetyFactor*(2* threatBias +sum( [ foAI.foAIstate.systemStatus[capitalSysID][thrtKey] for thrtKey in ['totalThreat', 'neighborThreat']] ))
    neededRating = rating_needed(1.4*capitalThreat, alreadyAssignedRating[capitalSysID])
    newAlloc=0
    if tryReset:
        if neededRating > 0.5*availMilRating:
            try_again(allMilitaryFleetIDs)
            return
    if neededRating > 0:
        newAlloc = min(remainingMilRating, neededRating )
        allocations.append( ( capitalSysID, newAlloc, True, 1.6*capitalThreat) )
        allocationGroups.setdefault('capitol', []).append( ( capitalSysID, newAlloc, True, 2*capitalThreat) )
        remainingMilRating -= newAlloc
    if "Main" in thisround or newAlloc >0:
        if verboseMilReporting:
            print "Empire Capital System: (%d) %s -- threat : %d, military allocation: existing: %d ; new: %d"%(capitalSysID, universe.getSystem(capitalSysID).name , capitalThreat, alreadyAssignedRating[capitalSysID], newAlloc)
            print "-----------------"

    #================================
    #--------Empire Occupied Systems ----------
    empirePlanetIDs = PlanetUtilsAI.get_owned_planets_by_empire(universe.planetIDs)
    empireOccupiedSystemIDs = list(set(PlanetUtilsAI.get_systems(empirePlanetIDs)) - {capitalSysID})
    if "Main" in thisround:
        if verboseMilReporting:
            print "Empire-Occupied Systems: %s"%( [ "| %d %s |"%(eoSysID, universe.getSystem(eoSysID).name) for eoSysID in empireOccupiedSystemIDs ]  )
            print "-----------------"
    newAlloc=0
    if len( empireOccupiedSystemIDs ) > 0:
        ocSysTotThreat = [ ( oSID, threatBias +safetyFactor*sum( [ foAI.foAIstate.systemStatus.get(oSID, {}).get(thrtKey, 0) for thrtKey in ['totalThreat', 'neighborThreat']] ))
                                                                                                                                                for oSID in empireOccupiedSystemIDs ]
        totocSysThreat = sum( [thrt for sid, thrt in ocSysTotThreat] )
        totCurAlloc = sum( [alreadyAssignedRating[sid] for sid, thrt in ocSysTotThreat] )
        allocationFactor = min( 1.5, remainingMilRating /max(0.01, ( totocSysThreat -totCurAlloc) ))
        ocSysAlloc = 0
        for sid, thrt in ocSysTotThreat:
            curAlloc=alreadyAssignedRating[sid]
            neededRating = rating_needed( 1.3*thrt, curAlloc)
            if (neededRating > 0.8* remainingMilRating) and tryReset:
                try_again(allMilitaryFleetIDs)
                return
            thisAlloc=0
            if ( neededRating>0 ) and remainingMilRating > 0:
                thisAlloc = max(0, min( neededRating, 0.5*availMilRating, remainingMilRating))
                newAlloc+=thisAlloc
                allocations.append( (sid, thisAlloc, True, 1.6*thrt) )
                allocationGroups.setdefault('occupied', []).append( (sid, thisAlloc, True, 2*thrt) )
                remainingMilRating -= thisAlloc
                ocSysAlloc += thisAlloc
            if "Main" in thisround or thisAlloc >0:
                if verboseMilReporting:
                    print "Provincial Occupied system %4d ( %10s ) has local threat %8d ; existing military allocation %d and new allocation %8d"%(sid, universe.getSystem(sid).name, thrt, curAlloc, thisAlloc)
        if "Main" in thisround or newAlloc >0:
            if verboseMilReporting:
                print "Provincial Empire-Occupied Sytems under total threat: %d -- total mil allocation: existing %d ; new: %d"%(totocSysThreat, totCurAlloc, ocSysAlloc )
                print "-----------------"

    #================================
    #--------Top Targeted Systems ----------
    #TODO: do native invasions highest priority
    otherTargetedSystemIDs = topTargetSystems
    if "Main" in thisround:
        if verboseMilReporting:
            print "Top Colony and Invasion Targeted Systems : %s"%( [ "| %d %s |"%(sysID, universe.getSystem(sysID).name) for sysID in otherTargetedSystemIDs ]  )
            print "-----------------"
    # for these, calc local threat only, no neighbor threat, but use a multiplier for fleet safety
    newAlloc=0
    if len( otherTargetedSystemIDs ) > 0:
        otSysAlloc = 0
        otSysThreat = [ ( oSID, threatBias +safetyFactor*(foAI.foAIstate.systemStatus.get(oSID, {}).get('totalThreat', 0)+0.5*foAI.foAIstate.systemStatus.get(oSID, {}).get('neightborThreat', 0) )  ) for oSID in otherTargetedSystemIDs ]
        tototSysThreat = sum( [thrt for sid, thrt in otSysThreat] )
        totCurAlloc = sum( [alreadyAssignedRating[sid] for sid, thrt in otSysThreat] )
        for sid, thrt in otSysThreat:
            curAlloc=alreadyAssignedRating[sid]
            neededRating = rating_needed( 1.4*thrt, curAlloc)
            thisAlloc=0
            #only record more allocation for this invasion if we already started or have enough rating available
            takeAny= alreadyAssignedRating[sid] > 0
            if ( neededRating>0 ) and (remainingMilRating > neededRating or takeAny):
                thisAlloc = max(0, min( neededRating, remainingMilRating))
                maxAlloc = safetyFactor*3*max( foAI.foAIstate.systemStatus.get(sid, {}).get('totalThreat', 0), foAI.foAIstate.systemStatus.get(sid, {}).get('neightborThreat', 0))
                newAlloc+=thisAlloc
                allocations.append( (sid, thisAlloc, takeAny , maxAlloc) )
                allocationGroups.setdefault('topTargets', []).append( (sid, thisAlloc, takeAny , maxAlloc) )
                remainingMilRating -= thisAlloc
                otSysAlloc += thisAlloc
            if "Main" in thisround or thisAlloc >0:
                if verboseMilReporting:
                    print "Targeted system %4d ( %10s ) has local threat %8d ; existing military allocation %d and new allocation %8d"%(sid, universe.getSystem(sid).name, thrt, curAlloc, thisAlloc)
        if "Main" in thisround or newAlloc >0:
            if verboseMilReporting:
                print "-----------------"
                print "Top Colony and Invasion Targeted Systems under total threat: %d -- total mil allocation-- existing: %d ; new: %d"%(tototSysThreat, totCurAlloc, otSysAlloc )
                print "-----------------"

    #================================
    #--------Targeted Systems ----------
    #TODO: do native invasions highest priority
    otherTargetedSystemIDs = [sysID for sysID in set( PlanetUtilsAI.get_systems(AIstate.opponentPlanetIDs)) if sysID not in topTargetSystems]
    if "Main" in thisround:
        if verboseMilReporting:
            print "Other Invasion Targeted Systems : %s"%( [ "| %d %s |"%(sysID, universe.getSystem(sysID).name) for sysID in otherTargetedSystemIDs ]  )
            print "-----------------"
    # for these, calc local threat only, no neighbor threat, but use a multiplier for fleet safety
    newAlloc=0
    if len( otherTargetedSystemIDs ) > 0:
        otSysAlloc = 0
        otSysThreat = [ ( oSID, threatBias +safetyFactor*(foAI.foAIstate.systemStatus.get(oSID, {}).get('totalThreat', 0)+0.5*foAI.foAIstate.systemStatus.get(oSID, {}).get('neighborThreat', 0) ) ) for oSID in otherTargetedSystemIDs ]
        tototSysThreat = sum( [thrt for sid, thrt in otSysThreat] )
        totCurAlloc = sum( [alreadyAssignedRating[sid] for sid, thrt in otSysThreat] )
        for sid, thrt in otSysThreat:
            curAlloc=alreadyAssignedRating[sid]
            neededRating = rating_needed( 1.4*thrt, curAlloc)
            thisAlloc=0
            #only record more allocation for this invasion if we already started or have enough rating available
            takeAny= alreadyAssignedRating[sid] > 0
            if ( neededRating>0 ) and (remainingMilRating > neededRating or takeAny):
                thisAlloc = max(0, min( neededRating, remainingMilRating))
                newAlloc+=thisAlloc
                maxAlloc = safetyFactor*3*max( foAI.foAIstate.systemStatus.get(sid, {}).get('totalThreat', 0), foAI.foAIstate.systemStatus.get(sid, {}).get('neightborThreat', 0))
                allocations.append( (sid, thisAlloc, takeAny , maxAlloc) )
                allocationGroups.setdefault('otherTargets', []).append( (sid, thisAlloc, takeAny , maxAlloc) )
                remainingMilRating -= thisAlloc
                otSysAlloc += thisAlloc
            if "Main" in thisround or thisAlloc >0:
                if verboseMilReporting:
                    print "Targeted system %4d ( %10s ) has local threat %8d ; existing military allocation %d and new allocation %8d"%(sid, universe.getSystem(sid).name, thrt, curAlloc, thisAlloc)
        if "Main" in thisround or newAlloc >0:
            if verboseMilReporting:
                print "-----------------"
                print "Invasion Targeted Systems under total threat: %d -- total mil allocation-- existing: %d ; new: %d"%(tototSysThreat, totCurAlloc, otSysAlloc )
                print "-----------------"

    otherTargetedSystemIDs = [sysID for sysID in list(set(AIstate.colonyTargetedSystemIDs + AIstate.outpostTargetedSystemIDs)) if sysID not in topTargetSystems]
    if "Main" in thisround:
        if verboseMilReporting:
            print "Other Targeted Systems : %s"%( [ "| %d %s |"%(sysID, universe.getSystem(sysID).name) for sysID in otherTargetedSystemIDs ]  )
            print "-----------------"
    if len( otherTargetedSystemIDs ) > 0:
        otSysAlloc = 0
        otSysThreat = [ ( oSID, safetyFactor*(threatBias +foAI.foAIstate.systemStatus.get(oSID, {}).get('totalThreat', 0)+0.5*foAI.foAIstate.systemStatus.get(oSID, {}).get('neighborThreat', 0) ) ) for oSID in otherTargetedSystemIDs ]
        tototSysThreat = sum( [thrt for sid, thrt in otSysThreat] )
        totCurAlloc = sum( [alreadyAssignedRating[sid] for sid, thrt in otSysThreat] )
        newAlloc=0
        for sid, thrt in otSysThreat:
            curAlloc=alreadyAssignedRating[sid]
            neededRating = rating_needed( 1.2*thrt, curAlloc)
            thisAlloc=0
            #only record more allocation for this invasion if we already started or have enough rating available
            takeAny= alreadyAssignedRating[sid] > 0
            if ( neededRating>0 ) and (remainingMilRating > neededRating or takeAny):
                thisAlloc = max(0, min( neededRating, remainingMilRating))
                newAlloc+=thisAlloc
                maxAlloc = safetyFactor*3*max( foAI.foAIstate.systemStatus.get(sid, {}).get('totalThreat', 0), foAI.foAIstate.systemStatus.get(sid, {}).get('neightborThreat', 0))
                allocations.append( (sid, thisAlloc, takeAny , maxAlloc) )
                allocationGroups.setdefault('otherTargets', []).append( (sid, thisAlloc, takeAny , maxAlloc) )
                remainingMilRating -= thisAlloc
                otSysAlloc += thisAlloc
            if "Main" in thisround or thisAlloc >0:
                if verboseMilReporting:
                    print "Targeted system %4d ( %10s ) has local threat %8d ; existing military allocation %d and new allocation %8d"%(sid, universe.getSystem(sid).name, thrt, curAlloc, thisAlloc)
        if "Main" in thisround or newAlloc >0:
            if verboseMilReporting:
                print "-----------------"
                print "Other Targeted Systems under total threat: %d -- total mil allocation-- existing: %d ; new: %d"%(tototSysThreat, totCurAlloc, otSysAlloc )
                print "-----------------"

    otherTargetedSystemIDs = []
    #targetableIDs = ColonisationAI.annexableSystemIDs.union( empire.fleetSupplyableSystemIDs )
    targetableIDs = set(ColonisationAI.systems_by_supply_tier.get(0, []) + ColonisationAI.systems_by_supply_tier.get(1, []))
    for sysID in AIstate.opponentSystemIDs:
        if sysID in targetableIDs:
            otherTargetedSystemIDs.append(sysID)
        else:
            for nID in universe.getImmediateNeighbors(sysID, empireID):
                if nID in targetableIDs:
                    otherTargetedSystemIDs.append(sysID)
                    break

    if "Main" in thisround:
        if verboseMilReporting:
            print "Blockade Targeted Systems : %s"%( [ "| %d %s |"%(sysID, universe.getSystem(sysID).name) for sysID in otherTargetedSystemIDs ]  )
            print "-----------------"
    if len( otherTargetedSystemIDs ) > 0:
        otSysAlloc = 0
        otSysThreat = [ ( oSID, threatBias +safetyFactor*(foAI.foAIstate.systemStatus.get(oSID, {}).get('totalThreat', 0)+ 0.5*foAI.foAIstate.systemStatus.get(oSID, {}).get('neighborThreat', 0) ) ) for oSID in otherTargetedSystemIDs ]
        tototSysThreat = sum( [thrt for sid, thrt in otSysThreat] )
        totCurAlloc = sum( [alreadyAssignedRating[sid] for sid, thrt in otSysThreat] )
        newAlloc=0
        for sid, thrt in otSysThreat:
            curAlloc=alreadyAssignedRating[sid]
            neededRating = rating_needed( 1.2*thrt, curAlloc)
            thisAlloc=0
            #only record more allocation for this invasion if we already started or have enough rating available
            takeAny= alreadyAssignedRating[sid] > 0
            if ( neededRating>0 ) and (remainingMilRating > neededRating or takeAny):
                thisAlloc = max(0, min( neededRating, remainingMilRating))
                newAlloc+=thisAlloc
                maxAlloc = safetyFactor*2*max( foAI.foAIstate.systemStatus.get(sid, {}).get('totalThreat', 0), foAI.foAIstate.systemStatus.get(sid, {}).get('neightborThreat', 0))
                allocations.append( (sid, thisAlloc, takeAny , maxAlloc) )
                allocationGroups.setdefault('otherTargets', []).append( (sid, thisAlloc, takeAny , maxAlloc) )
                remainingMilRating -= thisAlloc
                otSysAlloc += thisAlloc
            if "Main" in thisround or thisAlloc >0:
                if verboseMilReporting:
                    print "Blockade Targeted system %4d ( %10s ) has local threat %8d ; existing military allocation %d and new allocation %8d"%(sid, universe.getSystem(sid).name, thrt, curAlloc, thisAlloc)
        if "Main" in thisround or newAlloc >0:
            if verboseMilReporting:
                print "-----------------"
                print "Blockade Targeted Systems under total threat: %d -- total mil allocation-- existing: %d ; new: %d"%(tototSysThreat, totCurAlloc, otSysAlloc )
                print "-----------------"

    currentMilSystems = [sid for sid, alloc, takeAny, mm in allocations ]
    interiorIDs = list( foAI.foAIstate.expInteriorSystemIDs)
    interiorTargets1 = (targetableIDs.union(interiorIDs)).difference( currentMilSystems )
    interiorTargets = [sid for sid in interiorTargets1 if (
		 threatBias + foAI.foAIstate.systemStatus.get(sid, {}).get('totalThreat', 0) >0.8*alreadyAssignedRating[sid] ) ]
    if "Main" in thisround:
        if verboseMilReporting:
            print
            print "Other Empire-Proximal Systems : %s"%( [ "| %d %s |"%(sysID, universe.getSystem(sysID).name) for sysID in interiorTargets1 ]  )
            print "-----------------"
    # for these, calc fleet threat only, no neighbor threat, but use a multiplier for fleet safety
    newAlloc=0
    if len(interiorTargets) >0:
        otSysAlloc = 0
        otSysThreat = [ ( oSID, threatBias +safetyFactor*(foAI.foAIstate.systemStatus.get(oSID, {}).get('totalThreat', 0)) )   for oSID in interiorTargets ]
        tototSysThreat = sum( [thrt for sid, thrt in otSysThreat] )
        totCurAlloc = sum( [alreadyAssignedRating[sid] for sid, thrt in otSysThreat] )
        for sid, thrt in otSysThreat:
            curAlloc=alreadyAssignedRating[sid]
            neededRating = rating_needed( 1.2*thrt, curAlloc)
            thisAlloc=0
            #only record more allocation for this invasion if we already started or have enough rating available
            takeAny= alreadyAssignedRating[sid] > 0
            if ( neededRating>0 ) and (remainingMilRating > neededRating or takeAny):
                thisAlloc = max(0, min( neededRating, remainingMilRating))
                newAlloc+=thisAlloc
                maxAlloc = safetyFactor*2*max( foAI.foAIstate.systemStatus.get(sid, {}).get('totalThreat', 0), foAI.foAIstate.systemStatus.get(sid, {}).get('neightborThreat', 0))
                allocations.append( (sid, thisAlloc, takeAny , maxAlloc) )
                allocationGroups.setdefault('otherTargets', []).append( (sid, thisAlloc, takeAny , maxAlloc) )
                remainingMilRating -= thisAlloc
                otSysAlloc += thisAlloc
            if "Main" in thisround or thisAlloc >0:
                if verboseMilReporting:
                    print "Other interior system %4d ( %10s ) has local threat %8d ; existing military allocation %d and new allocation %8d"%(sid, universe.getSystem(sid).name, thrt, curAlloc, thisAlloc)
        if "Main" in thisround or newAlloc >0:
            if verboseMilReporting:
                print "-----------------"
                print "Other Interior Systems under total threat: %d -- total mil allocation-- existing: %d ; new: %d"%(tototSysThreat, totCurAlloc, otSysAlloc )
                print "-----------------"
    elif "Main" in thisround:
        if verboseMilReporting:
            print "-----------------"
            print "No Other Interior Systems with fleet threat "
            print "-----------------"

    monsterDens=[]

    #exploTargetIDs, _ = ExplorationAI.get_current_exploration_info(verbose=False)
    exploTargetIDs=[]
    if "Main" in thisround:
        if verboseMilReporting:
            print
            print "Exploration-targeted Systems: %s"%( [ "| %d %s |"%(sysID, universe.getSystem(sysID).name) for sysID in exploTargetIDs ]  )
            print "-----------------"
    # for these, calc fleet threat only, no neighbor threat, but use a multiplier for fleet safety
    newAlloc=0
    if len(exploTargetIDs) > 0:
        otSysAlloc = 0
        otSysThreat = [ ( oSID, safetyFactor*(foAI.foAIstate.systemStatus.get(oSID, {}).get('fleetThreat', 0) + foAI.foAIstate.systemStatus.get(oSID, {}).get('monsterThreat', 0)+ foAI.foAIstate.systemStatus.get(oSID, {}).get('planetThreat', 0) )) for oSID in exploTargetIDs ]
        tototSysThreat = sum( [thrt for sid, thrt in otSysThreat] )
        totCurAlloc = sum( [0.8*alreadyAssignedRating[sid] for sid, thrt in otSysThreat] )
        if availMilRating <1125:
            maxMilRating = availMilRating
        else:
            maxMilRating = 0.5*availMilRating
        for sid, thrt in otSysThreat:
            curAlloc=alreadyAssignedRating[sid]
            neededRating = rating_needed( 1.2*thrt, curAlloc)
            thisAlloc=0
            #only record more allocation for this invasion if we already started or have enough rating available
            takeAny= False
            if ( neededRating>0 ) and (remainingMilRating > neededRating or takeAny):
                thisAlloc = max(0, min( neededRating, remainingMilRating))
                newAlloc+=thisAlloc
                maxAlloc = safetyFactor*2*max( foAI.foAIstate.systemStatus.get(sid, {}).get('totalThreat', 0), foAI.foAIstate.systemStatus.get(sid, {}).get('neightborThreat', 0))
                allocations.append( (sid, thisAlloc, takeAny , maxAlloc) )
                allocationGroups.setdefault('exploreTargets', []).append( (sid, thisAlloc, takeAny , maxAlloc) )
                remainingMilRating -= thisAlloc
                otSysAlloc += thisAlloc
            if "Main" in thisround or thisAlloc >0:
                if verboseMilReporting:
                    print "Exploration-targeted %4d ( %10s ) has local threat %8d ; existing military allocation %d and new allocation %8d"%(sid, universe.getSystem(sid).name, thrt, curAlloc, thisAlloc)
        if "Main" in thisround or newAlloc >0:
            if verboseMilReporting:
                print "-----------------"
                print "Exploration-targeted s under total threat: %d -- total mil allocation-- existing: %d ; new: %d"%(tototSysThreat, totCurAlloc, otSysAlloc )
                print "-----------------"

    visibleSystemIDs = foAI.foAIstate.visInteriorSystemIDs.keys() + foAI.foAIstate. visBorderSystemIDs.keys()
    accessibleSystemIDs = [sysID for sysID in visibleSystemIDs if universe.systemsConnected(sysID, homeSystemID, empireID) ]
    currentMilSystems = [sid for sid, alloc, takeAny, multiplier in allocations if alloc > 0 ]
    borderTargets1 = [sid for sid in accessibleSystemIDs if ( sid not in currentMilSystems) ]
    borderTargets = [sid for sid in borderTargets1 if (threatBias +foAI.foAIstate.systemStatus.get(sid, {}).get('fleetThreat', 0) + foAI.foAIstate.systemStatus.get(sid, {}).get('planetThreat', 0) > 0.8*alreadyAssignedRating[sid]) ]
    if "Main" in thisround:
        if verboseMilReporting:
            print
            print "Empire-Accessible Systems not yet allocated military: %s"%( [ "| %d %s |"%(sysID, universe.getSystem(sysID) and universe.getSystem(sysID).name) for sysID in borderTargets1 ]  )
            print "-----------------"
    # for these, calc fleet threat only, no neighbor threat, but use a multiplier for fleet safety
    newAlloc=0
    if len(borderTargets) > 0:
        otSysAlloc = 0
        otSysThreat = [ ( oSID, threatBias +safetyFactor*(foAI.foAIstate.systemStatus.get(oSID, {}).get('fleetThreat', 0) + foAI.foAIstate.systemStatus.get(oSID, {}).get('monsterThreat', 0)+ foAI.foAIstate.systemStatus.get(oSID, {}).get('planetThreat', 0)) )   for oSID in borderTargets ]
        tototSysThreat = sum( [thrt for sid, thrt in otSysThreat] )
        totCurAlloc = sum( [0.8*alreadyAssignedRating[sid] for sid, thrt in otSysThreat] )
        for sid, thrt in otSysThreat:
            curAlloc=alreadyAssignedRating[sid]
            neededRating = rating_needed( 1.2*thrt, curAlloc)
            thisAlloc=0
            #only record more allocation for this invasion if we already started or have enough rating available
            takeAny= False
            if ( neededRating>0 ) and (remainingMilRating > neededRating or takeAny):
                thisAlloc = max(0, min( neededRating, remainingMilRating))
                newAlloc+=thisAlloc
                maxAlloc = safetyFactor*2*max( foAI.foAIstate.systemStatus.get(sid, {}).get('totalThreat', 0), foAI.foAIstate.systemStatus.get(sid, {}).get('neightborThreat', 0))
                allocations.append( (sid, thisAlloc, takeAny , maxAlloc) )
                allocationGroups.setdefault('accessibleTargets', []).append( (sid, thisAlloc, takeAny , maxAlloc) )
                remainingMilRating -= thisAlloc
                otSysAlloc += thisAlloc
            if "Main" in thisround or thisAlloc >0:
                if verboseMilReporting:
                    print "Other Empire-Accessible system %4d ( %10s ) has local biased threat %8d ; existing military allocation %d and new allocation %8d"%(sid, universe.getSystem(sid).name, thrt, curAlloc, thisAlloc)
        if "Main" in thisround or newAlloc >0:
            if verboseMilReporting:
                print "-----------------"
                print "Other Empire-Accessible Systems under total biased threat: %d -- total mil allocation-- existing: %d ; new: %d"%(tototSysThreat, totCurAlloc, otSysAlloc )
                print "-----------------"
    elif "Main" in thisround:
        if verboseMilReporting:
            print "-----------------"
            print "No Other Empire-Accessible Systems with biased local threat "
            print "-----------------"

    #monster den treatment probably unnecessary now
    if "Main" in thisround:
        if verboseMilReporting:
            print
            print "Big-Monster Dens: %s"%( [ "| %d %s |"%(sysID, universe.getSystem(sysID).name) for sysID in monsterDens ]  )
            print "-----------------"
    # for these, calc fleet threat only, no neighbor threat, but use a multiplier for fleet safety
    newAlloc=0
    if len(monsterDens) > 0:
        otSysAlloc = 0
        otSysThreat = [ ( oSID, safetyFactor*(foAI.foAIstate.systemStatus.get(oSID, {}).get('fleetThreat', 0)+foAI.foAIstate.systemStatus.get(oSID, {}).get('monsterThreat', 0) + foAI.foAIstate.systemStatus.get(oSID, {}).get('planetThreat', 0) ) ) for oSID in monsterDens ]
        tototSysThreat = sum( [thrt for sid, thrt in otSysThreat] )
        totCurAlloc = sum( [0.8*alreadyAssignedRating[sid] for sid, thrt in otSysThreat] )
        for sid, thrt in otSysThreat:
            curAlloc=0.8*alreadyAssignedRating[sid]
            thisAlloc=0
            if (thrt > curAlloc) and remainingMilRating > 2* thrt:
                thisAlloc = int(0.99999 + (thrt-curAlloc)*1.5)
                newAlloc+=thisAlloc
                allocations.append( (sid, thisAlloc, False, 5) )
                remainingMilRating -= thisAlloc
                otSysAlloc += thisAlloc
            if "Main" in thisround or thisAlloc >0:
                if verboseMilReporting:
                    print "Monster Den %4d ( %10s ) has local threat %8d ; existing military allocation %d and new allocation %8d"%(sid, universe.getSystem(sid).name, thrt, curAlloc, thisAlloc)
        if "Main" in thisround or newAlloc >0:
            if verboseMilReporting:
                print "-----------------"


    if remainingMilRating <=6:
        newAllocations = [ (sid, alc, alc, ta) for (sid, alc, ta, mm) in allocations ]
    else:
        #oldAllocations = dict( [ (entry[0], entry ) for entry in allocations ] )
        try:
            totAlloc = sum( [alloc for sid, alloc, takeAny, maxAlloc in allocations ] )
        except:
            print "error unpacking sid, alloc, takeAny, maxAlloc from ", allocations
        factor =(2.0* remainingMilRating ) / ( totAlloc + 0.1)
        #print "Remaining military strength allocation %d will be allocated as %.1f %% surplus allocation to top current priorities"%(remainingMilRating, 100*factor)
        print "%s Round Remaining military strength allocation %d will be allocated as surplus allocation to top current priorities"%(thisround, remainingMilRating)
        newAllocations = []
        for cat in ['capitol', 'topTargets', 'otherTargets', 'accessibleTargets', 'occupied', 'exploreTargets']:
            for sid, alloc, takeAny, maxAlloc in allocationGroups.get(cat, []):
                if remainingMilRating <= 0 :
                    newAllocations.append( ( sid, alloc, alloc, takeAny ) )
                else:
                    newRating = min(remainingMilRating+alloc, max(alloc, rating_needed( maxAlloc, alreadyAssignedRating[sid]) ) )
                    newAllocations.append( ( sid, newRating, alloc, takeAny ) )
                    remainingMilRating -= ( newRating - alloc )

    if "Main" in thisround:
        MilitaryAllocations = newAllocations
    minMilAllocations.clear()
    minMilAllocations.update( [ (sid, alloc) for sid, alloc, takeAny, mm in allocations ]   )
    if verboseMilReporting or "Main" in thisround:
        print "------------------------------\nFinal %s Round Military Allocations: %s \n-----------------------"%(thisround, dict( [ (sid, alloc) for sid, alloc, minalloc, takeAny in newAllocations ]   ) )

    # export military systems for other AI modules
    if "Main" in thisround:
        AIstate.militarySystemIDs = list( set([sid for sid, alloc, minalloc, takeAny in newAllocations]).union( [sid for sid in alreadyAssignedRating if alreadyAssignedRating[sid]>0 ] ))
    else:
        AIstate.militarySystemIDs = list( set([sid for sid, alloc, minalloc, takeAny in newAllocations]).union( AIstate.militarySystemIDs) )
    return newAllocations
Esempio n. 27
0
def get_military_fleets(milFleetIDs=None, tryReset=True, thisround="Main"):
    """get armed military fleets"""
    global military_allocations, totMilRating, num_milships

    universe = fo.getUniverse()
    empire = fo.getEmpire()
    empire_id = empire.empireID
    capital_id = PlanetUtilsAI.get_capital()
    if capital_id is None:
        homeworld = None
    else:
        homeworld = universe.getPlanet(capital_id)
    if homeworld:
        home_system_id = homeworld.systemID
    else:
        home_system_id = -1

    if milFleetIDs is not None:
        all_military_fleet_ids = milFleetIDs
    else:
        all_military_fleet_ids = FleetUtilsAI.get_empire_fleet_ids_by_role(
            AIFleetMissionType.FLEET_MISSION_MILITARY)
    if tryReset and (fo.currentTurn() +
                     empire_id) % 30 == 0 and thisround == "Main":
        try_again(all_military_fleet_ids,
                  try_reset=False,
                  thisround=thisround + " Reset")

    num_milships = 0
    for fid in all_military_fleet_ids:
        num_milships += foAI.foAIstate.fleetStatus.get(fid,
                                                       {}).get('nships', 0)

    this_tot_mil_rating = sum(
        map(lambda x: foAI.foAIstate.get_rating(x).get('overall', 0),
            all_military_fleet_ids))
    if "Main" in thisround:
        totMilRating = this_tot_mil_rating
        print "=================================================="
        print "%s Round Total Military Rating: %d" % (thisround, totMilRating)
        print "---------------------------------"
        foAI.foAIstate.militaryRating = totMilRating

    milFleetIDs = list(
        FleetUtilsAI.extract_fleet_ids_without_mission_types(
            all_military_fleet_ids))
    mil_needing_repair_ids, milFleetIDs = avail_mil_needing_repair(
        milFleetIDs, split_ships=True)
    avail_mil_rating = sum(
        map(lambda x: foAI.foAIstate.get_rating(x).get('overall', 0),
            milFleetIDs))
    if "Main" in thisround:
        print "=================================================="
        print "%s Round Available Military Rating: %d" % (thisround,
                                                          avail_mil_rating)
        print "---------------------------------"
    remaining_mil_rating = avail_mil_rating
    allocations = []
    allocation_groups = {}

    if not milFleetIDs:
        if "Main" in thisround:
            military_allocations = []
        return []

    # for each system, get total rating of fleets assigned to it
    already_assigned_rating = {}
    assigned_attack = {}
    assigned_hp = {}
    for sys_id in universe.systemIDs:
        assigned_attack[sys_id] = 0
        assigned_hp[sys_id] = 0
    for fleet_id in [
            fid for fid in all_military_fleet_ids if fid not in milFleetIDs
    ]:
        ai_fleet_mission = foAI.foAIstate.get_fleet_mission(fleet_id)
        sys_targets = []
        for ai_fleet_mission_type in ai_fleet_mission.get_mission_types():
            ai_targets = ai_fleet_mission.get_targets(ai_fleet_mission_type)
            for aiTarget in ai_targets:
                sys_targets.extend(aiTarget.get_required_system_ai_targets())
        if not sys_targets:  # shouldn't really be possible
            continue
        last_sys = sys_targets[
            -1].target_id  # will count this fleet as assigned to last system in target list
        assigned_attack[last_sys] += foAI.foAIstate.get_rating(fleet_id).get(
            'attack', 0)
        assigned_hp[last_sys] += foAI.foAIstate.get_rating(fleet_id).get(
            'health', 0)
    for sys_id in universe.systemIDs:
        mydefenses = foAI.foAIstate.systemStatus.get(sys_id,
                                                     {}).get('mydefenses', {})
        mypattack = mydefenses.get('attack', 0)
        myphealth = mydefenses.get('health', 0)
        already_assigned_rating[sys_id] = (assigned_attack[sys_id] +
                                           mypattack) * (assigned_hp[sys_id] +
                                                         myphealth)

    # get systems to defend
    capital_id = PlanetUtilsAI.get_capital()
    if capital_id is not None:
        capital_planet = universe.getPlanet(capital_id)
    else:
        capital_planet = None
    # TODO: if no owned planets try to capture one!
    if capital_planet:
        capital_sys_id = capital_planet.systemID
    else:  # should be rare, but so as to not break code below, pick a randomish mil-centroid system
        capital_sys_id = None  # unless we can find one to use
        system_dict = {}
        for fleet_id in all_military_fleet_ids:
            status = foAI.foAIstate.fleetStatus.get(fleet_id, None)
            if status is not None:
                sys_id = status['sysID']
                if not list(universe.getSystem(sys_id).planetIDs):
                    continue
                system_dict[sys_id] = system_dict.get(sys_id, 0) + status.get(
                    'rating', {}).get('overall', 0)
        ranked_systems = sorted([(val, sys_id)
                                 for sys_id, val in system_dict.items()])
        if ranked_systems:
            capital_sys_id = ranked_systems[-1][-1]
        else:
            try:
                capital_sys_id = foAI.foAIstate.fleetStatus.items(
                )[0][1]['sysID']
            except:
                pass

    if False:
        if fo.currentTurn() < 20:
            threat_bias = 0
        elif fo.currentTurn() < 40:
            threat_bias = 10
        elif fo.currentTurn() < 60:
            threat_bias = 80
        elif fo.currentTurn() < 80:
            threat_bias = 200
        else:
            threat_bias = 400
    else:
        threat_bias = 0

    safety_factor = get_safety_factor()

    top_target_planets = [
        pid for pid, pscore, trp in AIstate.
        invasionTargets[:PriorityAI.allottedInvasionTargets] if pscore > 20
    ] + [
        pid for pid, (pscore,
                      spec) in foAI.foAIstate.colonisablePlanetIDs.items()[:10]
        if pscore > 20
    ]
    top_target_planets.extend(foAI.foAIstate.qualifyingTroopBaseTargets.keys())
    top_target_systems = []
    for sys_id in AIstate.invasionTargetedSystemIDs + PlanetUtilsAI.get_systems(
            top_target_planets):
        if sys_id not in top_target_systems:
            top_target_systems.append(
                sys_id)  # doing this rather than set, to preserve order

    # allocation format: ( sysID, newAllocation, takeAny, maxMultiplier )
    # ================================
    # --------Capital Threat ----------
    capital_threat = safety_factor * (2 * threat_bias + sum([
        foAI.foAIstate.systemStatus[capital_sys_id][thrt_key]
        for thrt_key in ['totalThreat', 'neighborThreat']
    ]))
    needed_rating = rating_needed(1.4 * capital_threat,
                                  already_assigned_rating[capital_sys_id])
    new_alloc = 0
    if tryReset:
        if needed_rating > 0.5 * avail_mil_rating:
            try_again(all_military_fleet_ids)
            return
    if needed_rating > 0:
        new_alloc = min(remaining_mil_rating, needed_rating)
        allocations.append(
            (capital_sys_id, new_alloc, True, 1.6 * capital_threat))
        allocation_groups.setdefault('capitol', []).append(
            (capital_sys_id, new_alloc, True, 2 * capital_threat))
        remaining_mil_rating -= new_alloc
    if "Main" in thisround or new_alloc > 0:
        if verbose_mil_reporting:
            print "Empire Capital System: (%d) %s -- threat : %d, military allocation: existing: %d ; new: %d" % (
                capital_sys_id, universe.getSystem(capital_sys_id).name,
                capital_threat, already_assigned_rating[capital_sys_id],
                new_alloc)
            print "-----------------"

    # ================================
    # --------Empire Occupied Systems ----------
    empire_planet_ids = PlanetUtilsAI.get_owned_planets_by_empire(
        universe.planetIDs)
    empire_occupied_system_ids = list(
        set(PlanetUtilsAI.get_systems(empire_planet_ids)) - {capital_sys_id})
    if "Main" in thisround:
        if verbose_mil_reporting:
            print "Empire-Occupied Systems: %s" % ([
                "| %d %s |" % (eo_sys_id, universe.getSystem(eo_sys_id).name)
                for eo_sys_id in empire_occupied_system_ids
            ])
            print "-----------------"
    new_alloc = 0
    if empire_occupied_system_ids:
        oc_sys_tot_threat = [(o_s_id, threat_bias + safety_factor * sum([
            foAI.foAIstate.systemStatus.get(o_s_id, {}).get(thrt_key, 0)
            for thrt_key in ['totalThreat', 'neighborThreat']
        ])) for o_s_id in empire_occupied_system_ids]
        totoc_sys_threat = sum([thrt for sid, thrt in oc_sys_tot_threat])
        tot_cur_alloc = sum(
            [already_assigned_rating[sid] for sid, thrt in oc_sys_tot_threat])
        oc_sys_alloc = 0
        for sid, thrt in oc_sys_tot_threat:
            cur_alloc = already_assigned_rating[sid]
            needed_rating = rating_needed(1.3 * thrt, cur_alloc)
            if (needed_rating > 0.8 * remaining_mil_rating) and tryReset:
                try_again(all_military_fleet_ids)
                return
            this_alloc = 0
            if needed_rating > 0 and remaining_mil_rating > 0:
                this_alloc = max(
                    0,
                    min(needed_rating, 0.5 * avail_mil_rating,
                        remaining_mil_rating))
                new_alloc += this_alloc
                allocations.append((sid, this_alloc, True, 1.6 * thrt))
                allocation_groups.setdefault('occupied', []).append(
                    (sid, this_alloc, True, 2 * thrt))
                remaining_mil_rating -= this_alloc
                oc_sys_alloc += this_alloc
            if "Main" in thisround or this_alloc > 0:
                if verbose_mil_reporting:
                    print "Provincial Occupied system %4d ( %10s ) has local threat %8d ; existing military allocation %d and new allocation %8d" % (
                        sid, universe.getSystem(sid).name, thrt, cur_alloc,
                        this_alloc)
        if "Main" in thisround or new_alloc > 0:
            if verbose_mil_reporting:
                print "Provincial Empire-Occupied Sytems under total threat: %d -- total mil allocation: existing %d ; new: %d" % (
                    totoc_sys_threat, tot_cur_alloc, oc_sys_alloc)
                print "-----------------"

    # ================================
    # --------Top Targeted Systems ----------
    # TODO: do native invasions highest priority
    other_targeted_system_ids = top_target_systems
    if "Main" in thisround:
        if verbose_mil_reporting:
            print "Top Colony and Invasion Targeted Systems : %s" % ([
                "| %d %s |" % (sys_id, universe.getSystem(sys_id).name)
                for sys_id in other_targeted_system_ids
            ])
            print "-----------------"
    # for these, calc local threat only, no neighbor threat, but use a multiplier for fleet safety
    new_alloc = 0
    if other_targeted_system_ids:
        ot_sys_alloc = 0
        ot_sys_threat = [
            (o_s_id, threat_bias + safety_factor *
             (foAI.foAIstate.systemStatus.get(o_s_id, {}).get(
                 'totalThreat', 0) + 0.5 * foAI.foAIstate.systemStatus.get(
                     o_s_id, {}).get('neightborThreat', 0)))
            for o_s_id in other_targeted_system_ids
        ]
        totot_sys_threat = sum([thrt for sid, thrt in ot_sys_threat])
        tot_cur_alloc = sum(
            [already_assigned_rating[sid] for sid, thrt in ot_sys_threat])
        for sid, thrt in ot_sys_threat:
            cur_alloc = already_assigned_rating[sid]
            needed_rating = rating_needed(1.4 * thrt, cur_alloc)
            this_alloc = 0
            # only record more allocation for this invasion if we already started or have enough rating available
            take_any = already_assigned_rating[sid] > 0
            if needed_rating > 0 and (remaining_mil_rating > needed_rating
                                      or take_any):
                this_alloc = max(0, min(needed_rating, remaining_mil_rating))
                max_alloc = safety_factor * 3 * max(
                    foAI.foAIstate.systemStatus.get(sid, {}).get(
                        'totalThreat', 0),
                    foAI.foAIstate.systemStatus.get(sid, {}).get(
                        'neightborThreat', 0))
                new_alloc += this_alloc
                allocations.append((sid, this_alloc, take_any, max_alloc))
                allocation_groups.setdefault('topTargets', []).append(
                    (sid, this_alloc, take_any, max_alloc))
                remaining_mil_rating -= this_alloc
                ot_sys_alloc += this_alloc
            if "Main" in thisround or this_alloc > 0:
                if verbose_mil_reporting:
                    print "Targeted system %4d ( %10s ) has local threat %8d ; existing military allocation %d and new allocation %8d" % (
                        sid, universe.getSystem(sid).name, thrt, cur_alloc,
                        this_alloc)
        if "Main" in thisround or new_alloc > 0:
            if verbose_mil_reporting:
                print "-----------------"
                print "Top Colony and Invasion Targeted Systems under total threat: %d -- total mil allocation-- existing: %d ; new: %d" % (
                    totot_sys_threat, tot_cur_alloc, ot_sys_alloc)
                print "-----------------"

    # ================================
    # --------Targeted Systems ----------
    # TODO: do native invasions highest priority
    other_targeted_system_ids = [
        sys_id
        for sys_id in set(PlanetUtilsAI.get_systems(AIstate.opponentPlanetIDs))
        if sys_id not in top_target_systems
    ]
    if "Main" in thisround:
        if verbose_mil_reporting:
            print "Other Invasion Targeted Systems : %s" % ([
                "| %d %s |" % (sys_id, universe.getSystem(sys_id).name)
                for sys_id in other_targeted_system_ids
            ])
            print "-----------------"
    # for these, calc local threat only, no neighbor threat, but use a multiplier for fleet safety
    new_alloc = 0
    if other_targeted_system_ids:
        ot_sys_alloc = 0
        ot_sys_threat = [
            (o_s_id, threat_bias + safety_factor *
             (foAI.foAIstate.systemStatus.get(o_s_id, {}).get(
                 'totalThreat', 0) + 0.5 * foAI.foAIstate.systemStatus.get(
                     o_s_id, {}).get('neighborThreat', 0)))
            for o_s_id in other_targeted_system_ids
        ]
        totot_sys_threat = sum([thrt for sid, thrt in ot_sys_threat])
        tot_cur_alloc = sum(
            [already_assigned_rating[sid] for sid, thrt in ot_sys_threat])
        for sid, thrt in ot_sys_threat:
            cur_alloc = already_assigned_rating[sid]
            needed_rating = rating_needed(1.4 * thrt, cur_alloc)
            this_alloc = 0
            # only record more allocation for this invasion if we already started or have enough rating available
            take_any = already_assigned_rating[sid] > 0
            if needed_rating > 0 and (remaining_mil_rating > needed_rating
                                      or take_any):
                this_alloc = max(0, min(needed_rating, remaining_mil_rating))
                new_alloc += this_alloc
                max_alloc = safety_factor * 3 * max(
                    foAI.foAIstate.systemStatus.get(sid, {}).get(
                        'totalThreat', 0),
                    foAI.foAIstate.systemStatus.get(sid, {}).get(
                        'neightborThreat', 0))
                allocations.append((sid, this_alloc, take_any, max_alloc))
                allocation_groups.setdefault('otherTargets', []).append(
                    (sid, this_alloc, take_any, max_alloc))
                remaining_mil_rating -= this_alloc
                ot_sys_alloc += this_alloc
            if "Main" in thisround or this_alloc > 0:
                if verbose_mil_reporting:
                    print "Targeted system %4d ( %10s ) has local threat %8d ; existing military allocation %d and new allocation %8d" % (
                        sid, universe.getSystem(sid).name, thrt, cur_alloc,
                        this_alloc)
        if "Main" in thisround or new_alloc > 0:
            if verbose_mil_reporting:
                print "-----------------"
                print "Invasion Targeted Systems under total threat: %d -- total mil allocation-- existing: %d ; new: %d" % (
                    totot_sys_threat, tot_cur_alloc, ot_sys_alloc)
                print "-----------------"

    other_targeted_system_ids = [
        sys_id for sys_id in list(
            set(AIstate.colonyTargetedSystemIDs +
                AIstate.outpostTargetedSystemIDs))
        if sys_id not in top_target_systems
    ]
    if "Main" in thisround:
        if verbose_mil_reporting:
            print "Other Targeted Systems : %s" % ([
                "| %d %s |" % (sys_id, universe.getSystem(sys_id).name)
                for sys_id in other_targeted_system_ids
            ])
            print "-----------------"
    if other_targeted_system_ids:
        ot_sys_alloc = 0
        ot_sys_threat = [
            (o_s_id, safety_factor *
             (threat_bias + foAI.foAIstate.systemStatus.get(o_s_id, {}).get(
                 'totalThreat', 0) + 0.5 * foAI.foAIstate.systemStatus.get(
                     o_s_id, {}).get('neighborThreat', 0)))
            for o_s_id in other_targeted_system_ids
        ]
        totot_sys_threat = sum([thrt for sid, thrt in ot_sys_threat])
        tot_cur_alloc = sum(
            [already_assigned_rating[sid] for sid, thrt in ot_sys_threat])
        new_alloc = 0
        for sid, thrt in ot_sys_threat:
            cur_alloc = already_assigned_rating[sid]
            needed_rating = rating_needed(1.2 * thrt, cur_alloc)
            this_alloc = 0
            # only record more allocation for this invasion if we already started or have enough rating available
            take_any = already_assigned_rating[sid] > 0
            if needed_rating > 0 and (remaining_mil_rating > needed_rating
                                      or take_any):
                this_alloc = max(0, min(needed_rating, remaining_mil_rating))
                new_alloc += this_alloc
                max_alloc = safety_factor * 3 * max(
                    foAI.foAIstate.systemStatus.get(sid, {}).get(
                        'totalThreat', 0),
                    foAI.foAIstate.systemStatus.get(sid, {}).get(
                        'neightborThreat', 0))
                allocations.append((sid, this_alloc, take_any, max_alloc))
                allocation_groups.setdefault('otherTargets', []).append(
                    (sid, this_alloc, take_any, max_alloc))
                remaining_mil_rating -= this_alloc
                ot_sys_alloc += this_alloc
            if "Main" in thisround or this_alloc > 0:
                if verbose_mil_reporting:
                    print "Targeted system %4d ( %10s ) has local threat %8d ; existing military allocation %d and new allocation %8d" % (
                        sid, universe.getSystem(sid).name, thrt, cur_alloc,
                        this_alloc)
        if "Main" in thisround or new_alloc > 0:
            if verbose_mil_reporting:
                print "-----------------"
                print "Other Targeted Systems under total threat: %d -- total mil allocation-- existing: %d ; new: %d" % (
                    totot_sys_threat, tot_cur_alloc, ot_sys_alloc)
                print "-----------------"

    other_targeted_system_ids = []
    # targetable_ids = ColonisationAI.annexableSystemIDs.union(empire.fleetSupplyableSystemIDs)
    targetable_ids = set(
        ColonisationAI.systems_by_supply_tier.get(0, []) +
        ColonisationAI.systems_by_supply_tier.get(1, []))
    for sys_id in AIstate.opponentSystemIDs:
        if sys_id in targetable_ids:
            other_targeted_system_ids.append(sys_id)
        else:
            for nID in universe.getImmediateNeighbors(sys_id, empire_id):
                if nID in targetable_ids:
                    other_targeted_system_ids.append(sys_id)
                    break

    if "Main" in thisround:
        if verbose_mil_reporting:
            print "Blockade Targeted Systems : %s" % ([
                "| %d %s |" % (sys_id, universe.getSystem(sys_id).name)
                for sys_id in other_targeted_system_ids
            ])
            print "-----------------"
    if other_targeted_system_ids:
        ot_sys_alloc = 0
        ot_sys_threat = [
            (o_s_id, threat_bias + safety_factor *
             (foAI.foAIstate.systemStatus.get(o_s_id, {}).get(
                 'totalThreat', 0) + 0.5 * foAI.foAIstate.systemStatus.get(
                     o_s_id, {}).get('neighborThreat', 0)))
            for o_s_id in other_targeted_system_ids
        ]
        totot_sys_threat = sum([thrt for sid, thrt in ot_sys_threat])
        tot_cur_alloc = sum(
            [already_assigned_rating[sid] for sid, thrt in ot_sys_threat])
        new_alloc = 0
        for sid, thrt in ot_sys_threat:
            cur_alloc = already_assigned_rating[sid]
            needed_rating = rating_needed(1.2 * thrt, cur_alloc)
            this_alloc = 0
            # only record more allocation for this invasion if we already started or have enough rating available
            take_any = already_assigned_rating[sid] > 0
            if needed_rating > 0 and (remaining_mil_rating > needed_rating
                                      or take_any):
                this_alloc = max(0, min(needed_rating, remaining_mil_rating))
                new_alloc += this_alloc
                max_alloc = safety_factor * 2 * max(
                    foAI.foAIstate.systemStatus.get(sid, {}).get(
                        'totalThreat', 0),
                    foAI.foAIstate.systemStatus.get(sid, {}).get(
                        'neightborThreat', 0))
                allocations.append((sid, this_alloc, take_any, max_alloc))
                allocation_groups.setdefault('otherTargets', []).append(
                    (sid, this_alloc, take_any, max_alloc))
                remaining_mil_rating -= this_alloc
                ot_sys_alloc += this_alloc
            if "Main" in thisround or this_alloc > 0:
                if verbose_mil_reporting:
                    print "Blockade Targeted system %4d ( %10s ) has local threat %8d ; existing military allocation %d and new allocation %8d" % (
                        sid, universe.getSystem(sid).name, thrt, cur_alloc,
                        this_alloc)
        if "Main" in thisround or new_alloc > 0:
            if verbose_mil_reporting:
                print "-----------------"
                print "Blockade Targeted Systems under total threat: %d -- total mil allocation-- existing: %d ; new: %d" % (
                    totot_sys_threat, tot_cur_alloc, ot_sys_alloc)
                print "-----------------"

    current_mil_systems = [sid for sid, alloc, take_any, mm in allocations]
    interior_ids = list(foAI.foAIstate.expInteriorSystemIDs)
    interior_targets1 = (
        targetable_ids.union(interior_ids)).difference(current_mil_systems)
    interior_targets = [
        sid for sid in interior_targets1
        if (threat_bias +
            foAI.foAIstate.systemStatus.get(sid, {}).get('totalThreat', 0) >
            0.8 * already_assigned_rating[sid])
    ]
    if "Main" in thisround:
        if verbose_mil_reporting:
            print
            print "Other Empire-Proximal Systems : %s" % ([
                "| %d %s |" % (sys_id, universe.getSystem(sys_id).name)
                for sys_id in interior_targets1
            ])
            print "-----------------"
    # for these, calc fleet threat only, no neighbor threat, but use a multiplier for fleet safety
    new_alloc = 0
    if interior_targets:
        ot_sys_alloc = 0
        ot_sys_threat = [
            (o_s_id,
             threat_bias + safety_factor * (foAI.foAIstate.systemStatus.get(
                 o_s_id, {}).get('totalThreat', 0)))
            for o_s_id in interior_targets
        ]
        totot_sys_threat = sum([thrt for sid, thrt in ot_sys_threat])
        tot_cur_alloc = sum(
            [already_assigned_rating[sid] for sid, thrt in ot_sys_threat])
        for sid, thrt in ot_sys_threat:
            cur_alloc = already_assigned_rating[sid]
            needed_rating = rating_needed(1.2 * thrt, cur_alloc)
            this_alloc = 0
            # only record more allocation for this invasion if we already started or have enough rating available
            take_any = already_assigned_rating[sid] > 0
            if needed_rating > 0 and (remaining_mil_rating > needed_rating
                                      or take_any):
                this_alloc = max(0, min(needed_rating, remaining_mil_rating))
                new_alloc += this_alloc
                max_alloc = safety_factor * 2 * max(
                    foAI.foAIstate.systemStatus.get(sid, {}).get(
                        'totalThreat', 0),
                    foAI.foAIstate.systemStatus.get(sid, {}).get(
                        'neightborThreat', 0))
                allocations.append((sid, this_alloc, take_any, max_alloc))
                allocation_groups.setdefault('otherTargets', []).append(
                    (sid, this_alloc, take_any, max_alloc))
                remaining_mil_rating -= this_alloc
                ot_sys_alloc += this_alloc
            if "Main" in thisround or this_alloc > 0:
                if verbose_mil_reporting:
                    print "Other interior system %4d ( %10s ) has local threat %8d ; existing military allocation %d and new allocation %8d" % (
                        sid, universe.getSystem(sid).name, thrt, cur_alloc,
                        this_alloc)
        if "Main" in thisround or new_alloc > 0:
            if verbose_mil_reporting:
                print "-----------------"
                print "Other Interior Systems under total threat: %d -- total mil allocation-- existing: %d ; new: %d" % (
                    totot_sys_threat, tot_cur_alloc, ot_sys_alloc)
                print "-----------------"
    elif "Main" in thisround:
        if verbose_mil_reporting:
            print "-----------------"
            print "No Other Interior Systems with fleet threat "
            print "-----------------"

    monster_dens = []

    # explo_target_ids, _ = ExplorationAI.get_current_exploration_info(verbose=False)
    explo_target_ids = []
    if "Main" in thisround:
        if verbose_mil_reporting:
            print
            print "Exploration-targeted Systems: %s" % ([
                "| %d %s |" % (sys_id, universe.getSystem(sys_id).name)
                for sys_id in explo_target_ids
            ])
            print "-----------------"
    # for these, calc fleet threat only, no neighbor threat, but use a multiplier for fleet safety
    new_alloc = 0
    if explo_target_ids:
        ot_sys_alloc = 0
        ot_sys_threat = [(
            o_s_id, safety_factor *
            (foAI.foAIstate.systemStatus.get(o_s_id, {}).get('fleetThreat', 0)
             + foAI.foAIstate.systemStatus.get(o_s_id, {}).get(
                 'monsterThreat', 0) + foAI.foAIstate.systemStatus.get(
                     o_s_id, {}).get('planetThreat', 0)))
                         for o_s_id in explo_target_ids]
        totot_sys_threat = sum([thrt for sid, thrt in ot_sys_threat])
        tot_cur_alloc = sum([
            0.8 * already_assigned_rating[sid] for sid, thrt in ot_sys_threat
        ])
        for sid, thrt in ot_sys_threat:
            cur_alloc = already_assigned_rating[sid]
            needed_rating = rating_needed(1.2 * thrt, cur_alloc)
            this_alloc = 0
            # only record more allocation for this invasion if we already started or have enough rating available
            take_any = False
            if needed_rating > 0 and (remaining_mil_rating > needed_rating
                                      or take_any):
                this_alloc = max(0, min(needed_rating, remaining_mil_rating))
                new_alloc += this_alloc
                max_alloc = safety_factor * 2 * max(
                    foAI.foAIstate.systemStatus.get(sid, {}).get(
                        'totalThreat', 0),
                    foAI.foAIstate.systemStatus.get(sid, {}).get(
                        'neightborThreat', 0))
                allocations.append((sid, this_alloc, take_any, max_alloc))
                allocation_groups.setdefault('exploreTargets', []).append(
                    (sid, this_alloc, take_any, max_alloc))
                remaining_mil_rating -= this_alloc
                ot_sys_alloc += this_alloc
            if "Main" in thisround or this_alloc > 0:
                if verbose_mil_reporting:
                    print "Exploration-targeted %4d ( %10s ) has local threat %8d ; existing military allocation %d and new allocation %8d" % (
                        sid, universe.getSystem(sid).name, thrt, cur_alloc,
                        this_alloc)
        if "Main" in thisround or new_alloc > 0:
            if verbose_mil_reporting:
                print "-----------------"
                print "Exploration-targeted s under total threat: %d -- total mil allocation-- existing: %d ; new: %d" % (
                    totot_sys_threat, tot_cur_alloc, ot_sys_alloc)
                print "-----------------"

    visible_system_ids = foAI.foAIstate.visInteriorSystemIDs.keys(
    ) + foAI.foAIstate.visBorderSystemIDs.keys()
    accessible_system_ids = [
        sys_id for sys_id in visible_system_ids
        if universe.systemsConnected(sys_id, home_system_id, empire_id)
    ]
    current_mil_systems = [
        sid for sid, alloc, take_any, multiplier in allocations if alloc > 0
    ]
    border_targets1 = [
        sid for sid in accessible_system_ids if sid not in current_mil_systems
    ]
    border_targets = [
        sid for sid in border_targets1
        if (threat_bias +
            foAI.foAIstate.systemStatus.get(sid, {}).get('fleetThreat', 0) +
            foAI.foAIstate.systemStatus.get(sid, {}).get('planetThreat', 0) >
            0.8 * already_assigned_rating[sid])
    ]
    if "Main" in thisround:
        if verbose_mil_reporting:
            print
            print "Empire-Accessible Systems not yet allocated military: %s" % (
                [
                    "| %d %s |" % (sys_id, universe.getSystem(sys_id)
                                   and universe.getSystem(sys_id).name)
                    for sys_id in border_targets1
                ])
            print "-----------------"
    # for these, calc fleet threat only, no neighbor threat, but use a multiplier for fleet safety
    new_alloc = 0
    if border_targets:
        ot_sys_alloc = 0
        ot_sys_threat = [(
            o_s_id, threat_bias + safety_factor *
            (foAI.foAIstate.systemStatus.get(o_s_id, {}).get('fleetThreat', 0)
             + foAI.foAIstate.systemStatus.get(o_s_id, {}).get(
                 'monsterThreat', 0) + foAI.foAIstate.systemStatus.get(
                     o_s_id, {}).get('planetThreat', 0)))
                         for o_s_id in border_targets]
        totot_sys_threat = sum([thrt for sid, thrt in ot_sys_threat])
        tot_cur_alloc = sum([
            0.8 * already_assigned_rating[sid] for sid, thrt in ot_sys_threat
        ])
        for sid, thrt in ot_sys_threat:
            cur_alloc = already_assigned_rating[sid]
            needed_rating = rating_needed(1.2 * thrt, cur_alloc)
            this_alloc = 0
            # only record more allocation for this invasion if we already started or have enough rating available
            take_any = False
            if needed_rating > 0 and (remaining_mil_rating > needed_rating
                                      or take_any):
                this_alloc = max(0, min(needed_rating, remaining_mil_rating))
                new_alloc += this_alloc
                max_alloc = safety_factor * 2 * max(
                    foAI.foAIstate.systemStatus.get(sid, {}).get(
                        'totalThreat', 0),
                    foAI.foAIstate.systemStatus.get(sid, {}).get(
                        'neightborThreat', 0))
                allocations.append((sid, this_alloc, take_any, max_alloc))
                allocation_groups.setdefault('accessibleTargets', []).append(
                    (sid, this_alloc, take_any, max_alloc))
                remaining_mil_rating -= this_alloc
                ot_sys_alloc += this_alloc
            if "Main" in thisround or this_alloc > 0:
                if verbose_mil_reporting:
                    print "Other Empire-Accessible system %4d ( %10s ) has local biased threat %8d ; existing military allocation %d and new allocation %8d" % (
                        sid, universe.getSystem(sid).name, thrt, cur_alloc,
                        this_alloc)
        if "Main" in thisround or new_alloc > 0:
            if verbose_mil_reporting:
                print "-----------------"
                print "Other Empire-Accessible Systems under total biased threat: %d -- total mil allocation-- existing: %d ; new: %d" % (
                    totot_sys_threat, tot_cur_alloc, ot_sys_alloc)
                print "-----------------"
    elif "Main" in thisround:
        if verbose_mil_reporting:
            print "-----------------"
            print "No Other Empire-Accessible Systems with biased local threat "
            print "-----------------"

    # monster den treatment probably unnecessary now
    if "Main" in thisround:
        if verbose_mil_reporting:
            print
            print "Big-Monster Dens: %s" % ([
                "| %d %s |" % (sys_id, universe.getSystem(sys_id).name)
                for sys_id in monster_dens
            ])
            print "-----------------"
    # for these, calc fleet threat only, no neighbor threat, but use a multiplier for fleet safety
    new_alloc = 0
    if monster_dens:
        ot_sys_alloc = 0
        ot_sys_threat = [(
            o_s_id, safety_factor *
            (foAI.foAIstate.systemStatus.get(o_s_id, {}).get('fleetThreat', 0)
             + foAI.foAIstate.systemStatus.get(o_s_id, {}).get(
                 'monsterThreat', 0) + foAI.foAIstate.systemStatus.get(
                     o_s_id, {}).get('planetThreat', 0)))
                         for o_s_id in monster_dens]
        for sid, thrt in ot_sys_threat:
            cur_alloc = 0.8 * already_assigned_rating[sid]
            this_alloc = 0
            if (thrt > cur_alloc) and remaining_mil_rating > 2 * thrt:
                this_alloc = int(0.99999 + (thrt - cur_alloc) * 1.5)
                new_alloc += this_alloc
                allocations.append((sid, this_alloc, False, 5))
                remaining_mil_rating -= this_alloc
                ot_sys_alloc += this_alloc
            if "Main" in thisround or this_alloc > 0:
                if verbose_mil_reporting:
                    print "Monster Den %4d ( %10s ) has local threat %8d ; existing military allocation %d and new allocation %8d" % (
                        sid, universe.getSystem(sid).name, thrt, cur_alloc,
                        this_alloc)
        if "Main" in thisround or new_alloc > 0:
            if verbose_mil_reporting:
                print "-----------------"

    if remaining_mil_rating <= 6:
        new_allocations = [(sid, alc, alc, ta)
                           for (sid, alc, ta, mm) in allocations]
    else:
        print "%s Round Remaining military strength allocation %d will be allocated as surplus allocation to top current priorities" % (
            thisround, remaining_mil_rating)
        new_allocations = []
        for cat in [
                'capitol', 'topTargets', 'otherTargets', 'accessibleTargets',
                'occupied', 'exploreTargets'
        ]:
            for sid, alloc, take_any, max_alloc in allocation_groups.get(
                    cat, []):
                if remaining_mil_rating <= 0:
                    new_allocations.append((sid, alloc, alloc, take_any))
                else:
                    new_rating = min(
                        remaining_mil_rating + alloc,
                        max(
                            alloc,
                            rating_needed(max_alloc,
                                          already_assigned_rating[sid])))
                    new_allocations.append((sid, new_rating, alloc, take_any))
                    remaining_mil_rating -= (new_rating - alloc)

    if "Main" in thisround:
        military_allocations = new_allocations
    minMilAllocations.clear()
    minMilAllocations.update([(sid, alloc)
                              for sid, alloc, take_any, mm in allocations])
    if verbose_mil_reporting or "Main" in thisround:
        print "------------------------------\nFinal %s Round Military Allocations: %s \n-----------------------" % (
            thisround,
            dict([(sid, alloc)
                  for sid, alloc, minalloc, take_any in new_allocations]))

    # export military systems for other AI modules
    if "Main" in thisround:
        AIstate.militarySystemIDs = list(
            set([sid for sid, alloc, minalloc, take_any in new_allocations
                 ]).union([
                     sid for sid in already_assigned_rating
                     if already_assigned_rating[sid] > 0
                 ]))
    else:
        AIstate.militarySystemIDs = list(
            set([sid for sid, alloc, minalloc, take_any in new_allocations
                 ]).union(AIstate.militarySystemIDs))
    return new_allocations
Esempio n. 28
0
def get_military_fleets(mil_fleets_ids=None, try_reset=True, thisround="Main"):
    """Get armed military fleets."""
    global _military_allocations

    universe = fo.getUniverse()
    empire_id = fo.empireID()
    home_system_id = PlanetUtilsAI.get_capital_sys_id()

    all_military_fleet_ids = (mil_fleets_ids if mil_fleets_ids is not None
                              else FleetUtilsAI.get_empire_fleet_ids_by_role(MissionType.MILITARY))

    if try_reset and (fo.currentTurn() + empire_id) % 30 == 0 and thisround == "Main":
        try_again(all_military_fleet_ids, try_reset=False, thisround=thisround + " Reset")
        return

    mil_fleets_ids = list(FleetUtilsAI.extract_fleet_ids_without_mission_types(all_military_fleet_ids))
    mil_needing_repair_ids, mil_fleets_ids = avail_mil_needing_repair(mil_fleets_ids, split_ships=True)
    avail_mil_rating = sum(map(CombatRatingsAI.get_fleet_rating, mil_fleets_ids))

    if not mil_fleets_ids:
        if "Main" in thisround:
            _military_allocations = []
        return []

    # for each system, get total rating of fleets assigned to it
    already_assigned_rating = {}
    already_assigned_rating_vs_planets = {}
    systems_status = foAI.foAIstate.systemStatus
    enemy_sup_factor = {}  # enemy supply
    for sys_id in universe.systemIDs:
        already_assigned_rating[sys_id] = 0
        already_assigned_rating_vs_planets[sys_id] = 0
        enemy_sup_factor[sys_id] = min(2, len(systems_status.get(sys_id, {}).get('enemies_nearly_supplied', [])))
    for fleet_id in [fid for fid in all_military_fleet_ids if fid not in mil_fleets_ids]:
        ai_fleet_mission = foAI.foAIstate.get_fleet_mission(fleet_id)
        if not ai_fleet_mission.target:  # shouldn't really be possible
            continue
        last_sys = ai_fleet_mission.target.get_system().id  # will count this fleet as assigned to last system in target list  # TODO last_sys or target sys?
        this_rating = CombatRatingsAI.get_fleet_rating(fleet_id)
        this_rating_vs_planets = CombatRatingsAI.get_fleet_rating_against_planets(fleet_id)
        already_assigned_rating[last_sys] = CombatRatingsAI.combine_ratings(
                already_assigned_rating.get(last_sys, 0), this_rating)
        already_assigned_rating_vs_planets[last_sys] = CombatRatingsAI.combine_ratings(
                already_assigned_rating_vs_planets.get(last_sys, 0), this_rating_vs_planets)
    for sys_id in universe.systemIDs:
        my_defense_rating = systems_status.get(sys_id, {}).get('mydefenses', {}).get('overall', 0)
        already_assigned_rating[sys_id] = CombatRatingsAI.combine_ratings(my_defense_rating, already_assigned_rating[sys_id])
        if _verbose_mil_reporting and already_assigned_rating[sys_id]:
            print "\t System %s already assigned rating %.1f" % (
                universe.getSystem(sys_id), already_assigned_rating[sys_id])

    # get systems to defend
    capital_id = PlanetUtilsAI.get_capital()
    if capital_id is not None:
        capital_planet = universe.getPlanet(capital_id)
    else:
        capital_planet = None
    # TODO: if no owned planets try to capture one!
    if capital_planet:
        capital_sys_id = capital_planet.systemID
    else:  # should be rare, but so as to not break code below, pick a randomish mil-centroid system
        capital_sys_id = None  # unless we can find one to use
        system_dict = {}
        for fleet_id in all_military_fleet_ids:
            status = foAI.foAIstate.fleetStatus.get(fleet_id, None)
            if status is not None:
                system_id = status['sysID']
                if not list(universe.getSystem(system_id).planetIDs):
                    continue
                system_dict[system_id] = system_dict.get(system_id, 0) + status.get('rating', 0)
        ranked_systems = sorted([(val, sys_id) for sys_id, val in system_dict.items()])
        if ranked_systems:
            capital_sys_id = ranked_systems[-1][-1]
        else:
            try:
                capital_sys_id = foAI.foAIstate.fleetStatus.items()[0][1]['sysID']
            except:
                pass

    num_targets = max(10, PriorityAI.allotted_outpost_targets)
    top_target_planets = ([pid for pid, pscore, trp in AIstate.invasionTargets[:PriorityAI.allotted_invasion_targets()]
                           if pscore > MIN_INVASION_SCORE] +
                          [pid for pid, (pscore, spec) in foAI.foAIstate.colonisableOutpostIDs.items()[:num_targets]
                           if pscore > MIN_INVASION_SCORE] +
                          [pid for pid, (pscore, spec) in foAI.foAIstate.colonisablePlanetIDs.items()[:num_targets]
                           if pscore > MIN_INVASION_SCORE])
    top_target_planets.extend(foAI.foAIstate.qualifyingTroopBaseTargets.keys())
    base_col_target_systems = PlanetUtilsAI.get_systems(top_target_planets)
    top_target_systems = []
    for sys_id in AIstate.invasionTargetedSystemIDs + base_col_target_systems:
        if sys_id not in top_target_systems:
            if foAI.foAIstate.systemStatus[sys_id]['totalThreat'] > get_tot_mil_rating():
                continue
            top_target_systems.append(sys_id)  # doing this rather than set, to preserve order

    try:
        # capital defense
        allocation_helper = AllocationHelper(already_assigned_rating, already_assigned_rating_vs_planets, avail_mil_rating, try_reset)
        if capital_sys_id is not None:
            CapitalDefenseAllocator(capital_sys_id, allocation_helper).allocate()

        # defend other planets
        empire_planet_ids = PlanetUtilsAI.get_owned_planets_by_empire(universe.planetIDs)
        empire_occupied_system_ids = list(set(PlanetUtilsAI.get_systems(empire_planet_ids)) - {capital_sys_id})
        for sys_id in empire_occupied_system_ids:
            PlanetDefenseAllocator(sys_id, allocation_helper).allocate()

        # attack / protect high priority targets
        for sys_id in top_target_systems:
            TopTargetAllocator(sys_id, allocation_helper).allocate()

        # enemy planets
        other_targeted_system_ids = [sys_id for sys_id in set(PlanetUtilsAI.get_systems(AIstate.opponentPlanetIDs)) if
                                     sys_id not in top_target_systems]
        for sys_id in other_targeted_system_ids:
            TargetAllocator(sys_id, allocation_helper).allocate()

        # colony / outpost targets
        other_targeted_system_ids = [sys_id for sys_id in
                                     list(set(AIstate.colonyTargetedSystemIDs + AIstate.outpostTargetedSystemIDs)) if
                                     sys_id not in top_target_systems]
        for sys_id in other_targeted_system_ids:
            OutpostTargetAllocator(sys_id, allocation_helper).allocate()

        # TODO blockade enemy systems

        # interior systems
        targetable_ids = set(state.get_systems_by_supply_tier(0))
        current_mil_systems = [sid for sid, _, _, _, _ in allocation_helper.allocations]
        interior_targets1 = targetable_ids.difference(current_mil_systems)
        interior_targets = [sid for sid in interior_targets1 if (
            allocation_helper.threat_bias + systems_status.get(sid, {}).get('totalThreat', 0) > 0.8 * allocation_helper.already_assigned_rating[sid])]
        for sys_id in interior_targets:
            InteriorTargetsAllocator(sys_id, allocation_helper).allocate()

        # TODO Exploration targets

        # border protections
        visible_system_ids = foAI.foAIstate.visInteriorSystemIDs | foAI.foAIstate.visBorderSystemIDs
        accessible_system_ids = ([sys_id for sys_id in visible_system_ids if
                                 universe.systemsConnected(sys_id, home_system_id, empire_id)]
                                 if home_system_id != INVALID_ID else [])
        current_mil_systems = [sid for sid, alloc, rvp, take_any, _ in allocation_helper.allocations if alloc > 0]
        border_targets1 = [sid for sid in accessible_system_ids if sid not in current_mil_systems]
        border_targets = [sid for sid in border_targets1 if (
            allocation_helper.threat_bias + systems_status.get(sid, {}).get('fleetThreat', 0) + systems_status.get(sid, {}).get(
                    'planetThreat', 0) > 0.8 * allocation_helper.already_assigned_rating[sid])]
        for sys_id in border_targets:
            BorderSecurityAllocator(sys_id, allocation_helper).allocate()
    except ReleaseMilitaryException:
        try_again(all_military_fleet_ids)
        return

    new_allocations = []
    remaining_mil_rating = avail_mil_rating
    # for top categories assign max_alloc right away as available
    for cat in ['capitol', 'occupied', 'topTargets']:
        for sid, alloc, rvp, take_any, max_alloc in allocation_helper.allocation_by_groups.get(cat, []):
            if remaining_mil_rating <= 0:
                break
            this_alloc = min(remaining_mil_rating, max_alloc)
            new_allocations.append((sid, this_alloc, alloc, rvp, take_any))
            remaining_mil_rating -= this_alloc

    base_allocs = set()
    # for lower priority categories, first assign base_alloc around to all, then top up as available
    for cat in ['otherTargets', 'accessibleTargets', 'exploreTargets']:
        for sid, alloc, rvp, take_any, max_alloc in allocation_helper.allocation_by_groups.get(cat, []):
            if remaining_mil_rating <= 0:
                break
            base_allocs.add(sid)
            remaining_mil_rating -= alloc
    for cat in ['otherTargets', 'accessibleTargets', 'exploreTargets']:
        for sid, alloc, rvp, take_any, max_alloc in allocation_helper.allocation_by_groups.get(cat, []):
            if sid not in base_allocs:
                break
            if remaining_mil_rating <= 0:
                new_allocations.append((sid, alloc, alloc, rvp, take_any))
            else:
                new_rating = min(remaining_mil_rating + alloc, max_alloc)
                new_allocations.append((sid, new_rating, alloc, rvp, take_any))
                remaining_mil_rating -= (new_rating - alloc)

    if "Main" in thisround:
        _military_allocations = new_allocations
    if _verbose_mil_reporting or "Main" in thisround:
        print "------------------------------\nFinal %s Round Military Allocations: %s \n-----------------------" % (thisround, dict([(sid, alloc) for sid, alloc, _, _, _ in new_allocations]))
        print "(Apparently) remaining military rating: %.1f" % remaining_mil_rating

    return new_allocations
Esempio n. 29
0
def set_planet_resource_foci():
    """set resource focus of planets """
    newFoci = {}

    print "\n============================"
    print "Collecting info to assess Planet Focus Changes\n"
    universe = fo.getUniverse()
    empire = fo.getEmpire()
    currentTurn = fo.currentTurn()
    # set the random seed (based on galaxy seed, empire ID and current turn)
    # for game-reload consistency
    freq = min(3, (max(5, currentTurn - 80)) / 4.0)**(1.0 / 3)
    if not (limitAssessments and
            (abs(currentTurn - lastFociCheck[0]) < 1.5 * freq) and
            (random.random() < 1.0 / freq)):
        lastFociCheck[0] = currentTurn
        resource_timer.start("getPlanets")
        empirePlanetIDs = list(
            PlanetUtilsAI.get_owned_planets_by_empire(universe.planetIDs))
        resource_timer.start("Filter")
        resource_timer.start("Priority")
        # TODO: take into acct splintering of resource groups
        # fleetSupplyableSystemIDs = empire.fleetSupplyableSystemIDs
        # fleetSupplyablePlanetIDs = PlanetUtilsAI.get_planets_in__systems_ids(fleetSupplyableSystemIDs)
        ppPrio = foAI.foAIstate.get_priority(
            AIPriorityType.PRIORITY_RESOURCE_PRODUCTION)
        rpPrio = foAI.foAIstate.get_priority(
            AIPriorityType.PRIORITY_RESOURCE_RESEARCH)
        priorityRatio = float(rpPrio) / (ppPrio + 0.0001)
        resource_timer.start("Shuffle")
        # not supporting Growth for general planets until also adding code to make sure would actually benefit
        # shuffle(generalPlanetIDs)
        resource_timer.start("Targets")
        planets = map(universe.getPlanet, empirePlanetIDs)
        planetMap.clear()
        planetMap.update(zip(empirePlanetIDs, planets))
        if useGrowth:
            for metab, metabIncPop in ColonisationAI.empire_metabolisms.items(
            ):
                for special in [
                        aspec
                        for aspec in AIDependencies.metabolismBoostMap.get(
                            metab, [])
                        if aspec in ColonisationAI.available_growth_specials
                ]:
                    rankedPlanets = []
                    for pid in ColonisationAI.available_growth_specials[
                            special]:
                        planet = planetMap[pid]
                        cur_focus = planet.focus
                        pop = planet.currentMeterValue(fo.meterType.population)
                        if (pop > metabIncPop - 2 * planet.size) or (
                                GFocus not in planet.availableFoci
                        ):  # not enough benefit to lose local production, or can't put growth focus here
                            continue
                        for special2 in ["COMPUTRONIUM_SPECIAL"]:
                            if special2 in planet.specials:
                                break
                        else:  # didn't have any specials that would override interest in growth special
                            print "Considering Growth Focus for %s (%d) with special %s; planet has pop %.1f and %s metabolism incremental pop is %.1f" % (
                                planet.name, pid, special, pop, metab,
                                metabIncPop)
                            if cur_focus == GFocus:
                                pop -= 4  # discourage changing current focus to minimize focus-changing penalties
                            rankedPlanets.append((pop, pid, cur_focus))
                    if not rankedPlanets:
                        continue
                    rankedPlanets.sort()
                    print "Considering Growth Focus choice for special %s; possible planet pop, id pairs are %s" % (
                        metab, rankedPlanets)
                    for spSize, spPID, cur_focus in rankedPlanets:  # index 0 should be able to set focus, but just in case...
                        result = 1
                        if cur_focus != GFocus:
                            result = fo.issueChangeFocusOrder(spPID, GFocus)
                        if result == 1:
                            newFoci[spPID] = GFocus
                            if spPID in empirePlanetIDs:
                                del empirePlanetIDs[empirePlanetIDs.index(
                                    spPID)]
                            print "%s focus of planet %s (%d) at Growth Focus" % (
                                ["set", "left"][cur_focus == GFocus],
                                planetMap[spPID].name, spPID)
                            break
                        else:
                            print "failed setting focus of planet %s (%d) at Growth Focus; focus left at %s" % (
                                planetMap[spPID].name, spPID,
                                planetMap[spPID].focus)
        for pid in empirePlanetIDs:
            planet = planetMap[pid]
            if "COMPUTRONIUM_SPECIAL" in planet.specials:  # TODO: ensure only one (extremely rarely needed)
                curFocus = planet.focus
                if RFocus not in planet.availableFoci:
                    continue
                newFoci[pid] = RFocus
                result = 0
                if curFocus != RFocus:
                    result = fo.issueChangeFocusOrder(pid, RFocus)
                    if result == 1:
                        universe.updateMeterEstimates(empirePlanetIDs)
                if curFocus == RFocus or result == 1:
                    print "%s focus of planet %s (%d) (with Computronium Moon) at Research Focus" % (
                        ["set", "left"
                         ][curFocus == RFocus], planetMap[pid].name, pid)
                    if pid in empirePlanetIDs:
                        del empirePlanetIDs[empirePlanetIDs.index(pid)]
            elif (([
                    bld.buildingTypeName
                    for bld in map(universe.getObject, planet.buildingIDs)
                    if bld.buildingTypeName in
                ["BLD_CONC_CAMP", "BLD_CONC_CAMP_REMNANT"]
            ] != []) or ([
                    ccspec for ccspec in planet.specials if ccspec in
                ["CONC_CAMP_MASTER_SPECIAL", "CONC_CAMP_SLAVE_SPECIAL"]
            ] != [])):
                if IFocus not in planet.availableFoci:
                    continue
                curFocus = planet.focus
                newFoci[pid] = IFocus
                result = 0
                if curFocus != IFocus:
                    result = fo.issueChangeFocusOrder(pid, IFocus)
                    if result == 1:
                        print(
                            "Tried setting %s for Concentration Camp planet %s (%d) with species %s and current focus %s, got result %d and focus %s"
                            % (newFoci[pid], planet.name, pid,
                               planet.speciesName, curFocus, result,
                               planetMap[pid].focus))
                        universe.updateMeterEstimates(empirePlanetIDs)
                    if (result != 1) or planetMap[pid].focus != IFocus:
                        newplanet = universe.getPlanet(pid)
                        print(
                            "Error: Failed setting %s for Concentration Camp planet %s (%d) with species %s and current focus %s, but new planet copy shows %s"
                            % (newFoci[pid], planetMap[pid].name, pid,
                               planetMap[pid].speciesName,
                               planetMap[pid].focus, newplanet.focus))
                if curFocus == IFocus or result == 1:
                    print "%s focus of planet %s (%d) (with Concentration Camps/Remnants) at Industry Focus" % (
                        ["set", "left"
                         ][curFocus == IFocus], planetMap[pid].name, pid)
                    if pid in empirePlanetIDs:
                        del empirePlanetIDs[empirePlanetIDs.index(pid)]

        # pp, rp = get_resource_target_totals(empirePlanetIDs, planetMap)
        pp, rp = get_resource_target_totals(planetMap.keys())
        print "\n-----------------------------------------"
        print "Making Planet Focus Change Determinations\n"

        ratios = []
        # for each planet, calculate RP:PP value ratio at which industry/Mining focus and research focus would have the same total value, & sort by that
        # include a bias to slightly discourage changing foci
        curTargetPP = 0.001
        curTargetRP = 0.001
        resource_timer.start("Loop")  # loop
        has_force = tech_is_complete("CON_FRC_ENRG_STRC")
        preset_ids = set(planetMap.keys()) - set(empirePlanetIDs)
        ctPP0, ctRP0 = 0, 0
        for pid in preset_ids:
            nPP, nRP = newTargets.get(pid, {}).get(planetMap[pid].focus,
                                                   [0, 0])
            curTargetPP += nPP
            curTargetRP += nRP
            iPP, iRP = newTargets.get(pid, {}).get(IFocus, [0, 0])
            ctPP0 += iPP
            ctRP0 += iRP

        id_set = set(empirePlanetIDs)
        for adj_round in [1, 2, 3, 4]:
            maxi_ratio = ctRP0 / max(
                0.01, ctPP0)  # should only change between rounds 1 and 2
            for pid in list(id_set):
                if adj_round == 1:  # tally max Industry
                    iPP, iRP = newTargets.get(pid, {}).get(IFocus, [0, 0])
                    ctPP0 += iPP
                    ctRP0 += iRP
                    continue
                II, IR = newTargets[pid][IFocus]
                RI, RR = newTargets[pid][RFocus]
                CI, CR = currentOutput[pid][IFocus], currentOutput[pid][RFocus]
                research_penalty = (currentFocus[pid] != RFocus)
                # calculate factor F at which II + F * IR == RI + F * RR =====> F = ( II-RI ) / (RR-IR)
                thisFactor = (II - RI) / max(
                    0.01, RR - IR
                )  # don't let denominator be zero for planets where focus doesn't change RP
                planet = planetMap[pid]
                if adj_round == 2:  # take research at planets with very cheap research
                    if (maxi_ratio < priorityRatio) and (
                            curTargetRP <
                            priorityRatio * ctPP0) and (thisFactor <= 1.0):
                        curTargetPP += RI
                        curTargetRP += RR
                        newFoci[pid] = RFocus
                        id_set.discard(pid)
                    continue
                if adj_round == 3:  # take research at planets where can do reasonable balance
                    if has_force or (foAI.foAIstate.aggression <
                                     fo.aggression.aggressive) or (
                                         curTargetRP >= priorityRatio * ctPP0):
                        continue
                    pop = planet.currentMeterValue(fo.meterType.population)
                    t_pop = planet.currentMeterValue(
                        fo.meterType.targetPopulation)
                    # if AI is aggressive+, and this planet in range where temporary Research focus can get an additional RP at cost of 1 PP, and still need some RP, then do it
                    if pop < t_pop - 5:
                        continue
                    if (CI > II + 8) or (((RR > II) or (
                        (RR - CR) >= 1 + 2 * research_penalty)) and
                                         ((RR - IR) >= 3) and
                                         ((CR - IR) >= 0.7 *
                                          ((II - CI) *
                                           (1 + 0.1 * research_penalty)))):
                        curTargetPP += CI - 1 - research_penalty
                        curTargetRP += CR + 1
                        newFoci[pid] = RFocus
                        id_set.discard(pid)
                    continue
                # adj_round == 4 assume default IFocus
                curTargetPP += II  # icurTargets initially calculated by Industry focus, which will be our default focus
                curTargetRP += IR
                newFoci[pid] = IFocus
                ratios.append((thisFactor, pid))

        ratios.sort()
        printedHeader = False
        fociMap = {
            IFocus: "Industry",
            RFocus: "Research",
            MFocus: "Mining",
            GFocus: "Growth"
        }
        gotAlgo = tech_is_complete("LRN_ALGO_ELEGANCE")
        for ratio, pid in ratios:
            do_research = False  # (newFoci[pid]==RFocus)
            if (priorityRatio < (curTargetRP / (curTargetPP + 0.0001))
                ) and not do_research:  # we have enough RP
                if ratio < 1.1 and foAI.foAIstate.aggression > fo.aggression.cautious:  # but wait, RP is still super cheap relative to PP, maybe will take more RP
                    if priorityRatio < 1.5 * (
                            curTargetRP / (curTargetPP + 0.0001)
                    ):  # yeah, really a glut of RP, stop taking RP
                        break
                else:  # RP not super cheap & we have enough, stop taking it
                    break
            II, IR = newTargets[pid][IFocus]
            RI, RR = newTargets[pid][RFocus]
            # if currentFocus[pid] == MFocus:
            # II = max( II, newTargets[pid][MFocus][0] )
            if do_research or (
                    gotAlgo and
                ((ratio > 2.0 and curTargetPP < 15) or
                 (ratio > 2.5 and curTargetPP < 25 and II > 5) or
                 (ratio > 3.0 and curTargetPP < 40 and II > 5) or
                 (ratio > 4.0 and curTargetPP < 100 and II > 10) or
                 ((curTargetRP + RR - IR) / max(0.001, curTargetPP - II + RI) >
                  2 * priorityRatio))
            ):  # we already have algo elegance and more RP would be too expensive, or overkill
                if not printedHeader:
                    printedHeader = True
                    print "Rejecting further Research Focus choices as too expensive:"
                    print "%34s|%20s|%15s |%15s|%15s |%15s |%15s" % (
                        "                      Planet ", " current RP/PP ",
                        " current target RP/PP ", "current Focus ",
                        "  rejectedFocus ", " rejected target RP/PP ",
                        "rejected RP-PP EQF")
                    oldFocus = currentFocus[pid]
                    cPP, cRP = currentOutput[pid][IFocus], currentOutput[pid][
                        RFocus]
                    otPP, otRP = newTargets[pid].get(oldFocus, (0, 0))
                    ntPP, ntRP = newTargets[pid].get(RFocus, (0, 0))
                    print "pID (%3d) %22s | c: %5.1f / %5.1f | cT: %5.1f / %5.1f |  cF: %8s | nF: %8s | cT: %5.1f / %5.1f | %.2f" % (
                        pid, planetMap[pid].name, cRP, cPP, otRP, otPP,
                        fociMap.get(oldFocus, 'unknown'), fociMap[RFocus],
                        ntRP, ntPP, ratio)
                    continue  # RP is getting too expensive, but might be willing to still allocate from a planet with less PP to lose
            # if planetMap[pid].currentMeterValue(fo.meterType.targetPopulation) >0: #only set to research if pop won't die out
            newFoci[pid] = RFocus
            curTargetRP += (RR - IR)
            curTargetPP -= (II - RI)
        print "============================"
        print "Planet Focus Assignments to achieve target RP/PP ratio of %.2f from current ratio of %.2f ( %.1f / %.1f )" % (
            priorityRatio, rp / (pp + 0.0001), rp, pp)
        print "Max Industry assignments would result in target RP/PP ratio of %.2f ( %.1f / %.1f )" % (
            ctRP0 / (ctPP0 + 0.0001), ctRP0, ctPP0)
        print "-------------------------------------"
        print "%34s|%20s|%15s |%15s|%15s |%15s " % (
            "                      Planet ", " current RP/PP ",
            " current target RP/PP ", "current Focus ", "  newFocus ",
            " new target RP/PP ")
        totalChanged = 0
        for id_set in empirePlanetIDs, preset_ids:
            for pid in id_set:
                canFocus = planetMap[pid].currentMeterValue(
                    fo.meterType.targetPopulation) > 0
                oldFocus = currentFocus[pid]
                newFocus = newFoci.get(pid, IFocus)
                cPP, cRP = currentOutput[pid][IFocus], currentOutput[pid][
                    RFocus]
                otPP, otRP = newTargets[pid].get(oldFocus, (0, 0))
                ntPP, ntRP = otPP, otRP
                if newFocus != oldFocus and newFocus in planetMap[
                        pid].availableFoci:  # planetMap[pid].focus
                    totalChanged += 1
                    if newFocus != planetMap[pid].focus:
                        result = fo.issueChangeFocusOrder(pid, newFocus)
                        if result == 1:
                            ntPP, ntRP = newTargets[pid].get(newFocus, (0, 0))
                        else:
                            print "Trouble changing focus of planet %s (%d) to %s" % (
                                planetMap[pid].name, pid, newFocus)
                print "pID (%3d) %22s | c: %5.1f / %5.1f | cT: %5.1f / %5.1f |  cF: %8s | nF: %8s | cT: %5.1f / %5.1f " % (
                    pid, planetMap[pid].name, cRP, cPP, otRP, otPP,
                    fociMap.get(oldFocus,
                                'unknown'), fociMap[newFocus], ntRP, ntPP)
            print "-------------------------------------"
        print "-------------------------------------"
        print "Final Ratio Target (turn %4d) RP/PP : %.2f ( %.1f / %.1f ) after %d Focus changes" % (
            fo.currentTurn(), curTargetRP /
            (curTargetPP + 0.0001), curTargetRP, curTargetPP, totalChanged)
        resource_timer.end()
    aPP, aRP = empire.productionPoints, empire.resourceProduction(
        fo.resourceType.research)
    # Next string used in charts. Don't modify it!
    print "Current Output (turn %4d) RP/PP : %.2f ( %.1f / %.1f )" % (
        fo.currentTurn(), aRP / (aPP + 0.0001), aRP, aPP)
    print "------------------------"
    print "ResourcesAI Time Requirements:"
Esempio n. 30
0
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()
Esempio n. 31
0
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()