Ejemplo n.º 1
0
 def __border_exploration_update(self):
     universe = fo.getUniverse()
     exploration_center = PlanetUtilsAI.get_capital_sys_id()
     # a bad state probably from an old savegame, or else empire has lost (or almost has)
     if exploration_center == INVALID_ID:
         exploration_center = self.__origin_home_system_id
     ExplorationAI.graph_flags.clear()
     if fo.currentTurn() < 50:
         print "-------------------------------------------------"
         print "Border Exploration Update (relative to %s)" % universe.getSystem(exploration_center)
         print "-------------------------------------------------"
     if self.visBorderSystemIDs == {INVALID_ID}:
         self.visBorderSystemIDs.clear()
         self.visBorderSystemIDs.add(exploration_center)
     for sys_id in list(self.visBorderSystemIDs):  # This set is modified during iteration.
         if fo.currentTurn() < 50:
             print "Considering border system %s" % universe.getSystem(sys_id)
         ExplorationAI.follow_vis_system_connections(sys_id, exploration_center)
     newly_explored = ExplorationAI.update_explored_systems()
     nametags = []
     for sys_id in newly_explored:
         newsys = universe.getSystem(sys_id)
         # an explored system *should* always be able to be gotten
         nametags.append("ID:%4d -- %-20s" % (sys_id, (newsys and newsys.name) or "name unknown"))
     if newly_explored:
         print "-------------------------------------------------"
         print "Newly explored systems:\n%s" % "\n".join(nametags)
         print "-------------------------------------------------"
Ejemplo n.º 2
0
def system_could_have_unknown_stationary_guard(system_id: SystemId) -> bool:
    """Return True if the system may have spawned stationary guards.

    A stationary guard is defined as immobile monster fleets spawned at game start.
    If this function indicates that there is no such guard, there still may be other threats.
    """
    # We do not play around invisible guards, so if system was visible at some point,
    # there should not be a stationary guard there
    system_was_visible = get_partial_visibility_turn(system_id) > 0
    if system_was_visible:
        return False

    # Universe setup settings may forbid guards
    if fo.getGalaxySetupData(
    ).monsterFrequency == fo.galaxySetupOptionMonsterFreq.none:
        return False

    # Stationary guards require some distance to the home system to be spawned
    home_system = PlanetUtilsAI.get_capital_sys_id()
    jump_distance_to_home_system = fo.getUniverse().jumpDistance(
        system_id, home_system)
    if jump_distance_to_home_system < MINIMUM_GUARD_DISTANCE_TO_HOME_SYSTEM:
        return False

    # No indicator that there isn't a stationary guard
    return True
Ejemplo n.º 3
0
 def __border_exploration_update(self):
     universe = fo.getUniverse()
     exploration_center = PlanetUtilsAI.get_capital_sys_id()
     # a bad state probably from an old savegame, or else empire has lost (or almost has)
     if exploration_center == INVALID_ID:
         exploration_center = self.__origin_home_system_id
     ExplorationAI.graph_flags.clear()
     if fo.currentTurn() < 50:
         debug("-------------------------------------------------")
         debug("Border Exploration Update (relative to %s)" % universe.getSystem(exploration_center))
         debug("-------------------------------------------------")
     if self.visBorderSystemIDs == {INVALID_ID}:
         self.visBorderSystemIDs.clear()
         self.visBorderSystemIDs.add(exploration_center)
     for sys_id in list(self.visBorderSystemIDs):  # This set is modified during iteration.
         if fo.currentTurn() < 50:
             debug("Considering border system %s" % universe.getSystem(sys_id))
         ExplorationAI.follow_vis_system_connections(sys_id, exploration_center)
     newly_explored = ExplorationAI.update_explored_systems()
     nametags = []
     for sys_id in newly_explored:
         newsys = universe.getSystem(sys_id)
         # an explored system *should* always be able to be gotten
         nametags.append("ID:%4d -- %-20s" % (sys_id, (newsys and newsys.name) or "name unknown"))
     if newly_explored:
         debug("-------------------------------------------------")
         debug("Newly explored systems:\n%s" % "\n".join(nametags))
         debug("-------------------------------------------------")
Ejemplo n.º 4
0
def assign_scouts_to_explore_systems():
    # TODO: use Graph Theory to explore closest systems
    universe = fo.getUniverse()
    capital_sys_id = PlanetUtilsAI.get_capital_sys_id()
    # order fleets to explore
    #explorable_system_ids = foAI.foAIstate.get_explorable_systems(AIExplorableSystemType.EXPLORABLE_SYSTEM_UNEXPLORED)
    explorable_system_ids = list(borderUnexploredSystemIDs)
    if not explorable_system_ids or (capital_sys_id == -1):
        return
    exp_systems_by_dist = sorted(map(lambda x: (universe.linearDistance(capital_sys_id, x), x), explorable_system_ids))
    print "Exploration system considering following system-distance pairs:\n %s" % ("[ " + ", ".join(["%3d : %5.1f" % (sys, dist) for dist, sys in exp_systems_by_dist]) + " ]")
    explore_list = [sys_id for dist, sys_id in exp_systems_by_dist]

    already_covered, available_scouts = get_current_exploration_info()

    print "explorable sys IDs: %s" % explore_list
    print "already targeted: %s" % already_covered
    if 'needsEmergencyExploration' not in dir(foAI.foAIstate):
        foAI.foAIstate.needsEmergencyExploration = []
    needs_coverage = foAI.foAIstate.needsEmergencyExploration + [sys_id for sys_id in explore_list if sys_id not in already_covered]  # emergency coverage cane be due to invasion detection trouble, etc.
    print "needs coverage: %s" % needs_coverage

    print "available scouts & AIstate locs: %s" % (map(lambda x: (x, foAI.foAIstate.fleetStatus.get(x, {}).get('sysID', -1)), available_scouts))
    print "available scouts & universe locs: %s" % (map(lambda x: (x, universe.getFleet(x).systemID), available_scouts))
    if not needs_coverage or not available_scouts:
        return

    available_scouts = set(available_scouts)
    sent_list = []
    while (len(available_scouts) > 0) and (len(needs_coverage) > 0):
        this_sys_id = needs_coverage.pop(0)
        if (foAI.foAIstate.systemStatus.setdefault(this_sys_id, {}).setdefault('monsterThreat', 0) > 2000 * foAI.foAIstate.aggression) or (fo.currentTurn() < 20 and foAI.foAIstate.systemStatus[this_sys_id]['monsterThreat'] > 200):
            print "Skipping exploration of system %d due to Big Monster, threat %d" % (this_sys_id, foAI.foAIstate.systemStatus[this_sys_id]['monsterThreat'])
            continue
        found_fleets = []
        this_fleet_list = FleetUtilsAI.get_fleets_for_mission(nships=1, target_stats={}, min_stats={}, cur_stats={}, species="", systems_to_check=[this_sys_id], systems_checked=[],
                                                     fleet_pool_set=available_scouts, fleet_list=found_fleets, verbose=False)
        if not this_fleet_list:
            print "seem to have run out of scouts while trying to cover sys_id %d" % this_sys_id
            break  # must have ran out of scouts
        fleet_id = this_fleet_list[0]
        fleet_mission = foAI.foAIstate.get_fleet_mission(fleet_id)
        target = AITarget.AITarget(TargetType.TARGET_SYSTEM, this_sys_id)
        if len(MoveUtilsAI.can_travel_to_system_and_return_to_resupply(fleet_id, fleet_mission.get_location_target(), target)) > 0:
            fleet_mission.add_target(AIFleetMissionType.FLEET_MISSION_EXPLORATION, target)
            sent_list.append(this_sys_id)
        else:  # system too far out, skip it, but can add scout back to available pool
            print "sys_id %d too far out for fleet ( ID %d ) to reach" % (this_sys_id, fleet_id)
            available_scouts.update(this_fleet_list)
    print "sent scouting fleets to sysIDs : %s" % sent_list
    return
    # pylint: disable=pointless-string-statement
    """
Ejemplo n.º 5
0
def assign_scouts_to_explore_systems():
    # TODO: use Graph Theory to explore closest systems
    universe = fo.getUniverse()
    capital_sys_id = PlanetUtilsAI.get_capital_sys_id()
    # order fleets to explore
    #explorable_system_ids = foAI.foAIstate.get_explorable_systems(AIExplorableSystemType.EXPLORABLE_SYSTEM_UNEXPLORED)
    explorable_system_ids = list(borderUnexploredSystemIDs)
    if not explorable_system_ids or (capital_sys_id == -1):
        return
    exp_systems_by_dist = sorted(map(lambda x: (universe.linearDistance(capital_sys_id, x), x), explorable_system_ids))
    print "Exploration system considering following system-distance pairs:\n %s"%("[ "+ ", ".join(["%3d : %5.1f"%(sys, dist) for dist, sys in exp_systems_by_dist]) +" ]")
    explore_list = [sys_id for dist, sys_id in exp_systems_by_dist ]

    already_covered, available_scouts = get_current_exploration_info()

    print "explorable sys IDs: %s"%explore_list
    print "already targeted: %s"%already_covered
    if 'needsEmergencyExploration' not in dir(foAI.foAIstate):
        foAI.foAIstate.needsEmergencyExploration = []
    needs_coverage = foAI.foAIstate.needsEmergencyExploration + [sys_id for sys_id in explore_list if sys_id not in already_covered ] # emergency coverage cane be due to invasion detection trouble, etc.
    print "needs coverage: %s"%needs_coverage

    print "available scouts & AIstate locs: %s" % (map(lambda x: (x, foAI.foAIstate.fleetStatus.get(x, {}).get('sysID', -1)), available_scouts) )
    print "available scouts & universe locs: %s" % (map(lambda x: (x, universe.getFleet(x).systemID), available_scouts))
    if not needs_coverage or not available_scouts:
        return

    available_scouts = set(available_scouts)
    sent_list = []
    while (len(available_scouts) > 0 ) and ( len(needs_coverage) > 0):
        this_sys_id = needs_coverage.pop(0)
        if (foAI.foAIstate.systemStatus.setdefault(this_sys_id, {}).setdefault('monsterThreat', 0) > 2000* foAI.foAIstate.aggression ) or (fo.currentTurn() <20 and foAI.foAIstate.systemStatus[this_sys_id]['monsterThreat'] > 200):
            print "Skipping exploration of system %d due to Big Monster, threat %d"%(this_sys_id, foAI.foAIstate.systemStatus[this_sys_id]['monsterThreat'])
            continue
        found_fleets=[]
        this_fleet_list = FleetUtilsAI.get_fleets_for_mission(nships=1, target_stats={}, min_stats={}, cur_stats={}, species="", systems_to_check=[this_sys_id], systems_checked=[],
                                                     fleet_pool_set= available_scouts, fleet_list=found_fleets, verbose=False)
        if not this_fleet_list:
            print "seem to have run out of scouts while trying to cover sys_id %d"%this_sys_id
            break #must have ran out of scouts
        fleet_id = this_fleet_list[0]
        fleet_mission = foAI.foAIstate.get_fleet_mission( fleet_id )
        target = AITarget.AITarget(AITargetType.TARGET_SYSTEM, this_sys_id )
        if len(MoveUtilsAI.can_travel_to_system_and_return_to_resupply(fleet_id, fleet_mission.get_location_target(), target)) > 0:
            fleet_mission.add_target(AIFleetMissionType.FLEET_MISSION_EXPLORATION, target)
            sent_list.append(this_sys_id)
        else: #system too far out, skip it, but can add scout back to available pool
            print "sys_id %d too far out for fleet ( ID %d ) to readch"%(this_sys_id, fleet_id)
            available_scouts.update(this_fleet_list)
    print "sent scouting fleets to sysIDs : %s"%sent_list
    return
    # pylint: disable=pointless-string-statement
    """
Ejemplo n.º 6
0
    def refresh(self):
        """Turn start AIstate cleanup/refresh."""
        universe = fo.getUniverse()
        # checks exploration border & clears roles/missions of missing fleets & updates fleet locs & threats
        fleetsLostBySystem.clear()
        invasionTargets[:] = []
        exploration_center = PlanetUtilsAI.get_capital_sys_id()
        # a bad state probably from an old savegame, or else empire has lost (or almost has)
        if exploration_center == INVALID_ID:
            exploration_center = self.__origin_home_system_id

        for system_id, info in sorted(self.systemStatus.items()):
            self.systemStatus[system_id][
                'enemy_ship_count'] = 0  # clear now in prep for update_system_status()
        ExplorationAI.graph_flags.clear()
        if fo.currentTurn() < 50:
            print "-------------------------------------------------"
            print "Border Exploration Update (relative to %s)" % (
                PlanetUtilsAI.sys_name_ids([exploration_center, INVALID_ID
                                            ])[0])
            print "-------------------------------------------------"
        if self.visBorderSystemIDs == {INVALID_ID}:
            self.visBorderSystemIDs.clear()
            self.visBorderSystemIDs.add(exploration_center)
        for sys_id in list(self.visBorderSystemIDs
                           ):  # This set is modified during iteration.
            if fo.currentTurn() < 50:
                print "Considering border system %s" % (
                    PlanetUtilsAI.sys_name_ids([sys_id, INVALID_ID])[0])
            ExplorationAI.follow_vis_system_connections(
                sys_id, exploration_center)
        newly_explored = ExplorationAI.update_explored_systems()
        nametags = []
        for sys_id in newly_explored:
            newsys = universe.getSystem(sys_id)
            # an explored system *should* always be able to be gotten
            nametags.append("ID:%4d -- %-20s" %
                            (sys_id,
                             (newsys and newsys.name) or "name unknown"))
        if newly_explored:
            print "-------------------------------------------------"
            print "Newly explored systems:\n%s" % "\n".join(nametags)
            print "-------------------------------------------------"
        # cleanup fleet roles
        # self.update_fleet_locs()
        self.__clean_fleet_roles()
        self.__clean_fleet_missions(FleetUtilsAI.get_empire_fleet_ids())
        print "Fleets lost by system: %s" % fleetsLostBySystem
        self.update_system_status()
Ejemplo n.º 7
0
    def refresh(self):
        """Turn start AIstate cleanup/refresh."""
        universe = fo.getUniverse()
        # checks exploration border & clears roles/missions of missing fleets & updates fleet locs & threats
        fleetsLostBySystem.clear()
        invasionTargets[:] = []
        exploration_center = PlanetUtilsAI.get_capital_sys_id()
        if exploration_center == -1:  # a bad state probably from an old savegame, or else empire has lost (or almost has)
            exploration_center = self.__origin_home_system_id

        # check if planets in cache is still present. Remove destroyed.
        for system_id, info in sorted(self.systemStatus.items()):
            planet_dict = info.get('planets', {})
            cache_planet_set = set(planet_dict)
            system_planet_set = set(universe.getSystem(system_id).planetIDs)
            diff = cache_planet_set - system_planet_set
            if diff:
                print "Removing destroyed planets from systemStatus for system %s: planets to be removed: %s" % (system_id, sorted(diff))
                for key in diff:
                    del planet_dict[key]

        ExplorationAI.graphFlags.clear()
        if fo.currentTurn() < 50:
            print "-------------------------------------------------"
            print "Border Exploration Update (relative to %s)" % (PlanetUtilsAI.sys_name_ids([exploration_center, -1])[0])
            print "-------------------------------------------------"
        if self.visBorderSystemIDs.keys() == [-1]:
            self.visBorderSystemIDs.clear()
            self.visBorderSystemIDs[exploration_center] = 1
        for sys_id in self.visBorderSystemIDs.keys():  # This dict modified during iteration.
            if fo.currentTurn() < 50:
                print "Considering border system %s" % (PlanetUtilsAI.sys_name_ids([sys_id, -1])[0])
            ExplorationAI.follow_vis_system_connections(sys_id, exploration_center)
        newly_explored = ExplorationAI.update_explored_systems()
        nametags = []
        for sys_id in newly_explored:
            newsys = universe.getSystem(sys_id)
            nametags.append("ID:%4d -- %-20s" % (sys_id, (newsys and newsys.name) or "name unknown"))  # an explored system *should* always be able to be gotten
        if newly_explored:
            print "-------------------------------------------------"
            print "Newly explored systems:\n%s" % "\n".join(nametags)
            print "-------------------------------------------------"
        # cleanup fleet roles
        # self.update_fleet_locs()
        self.__clean_fleet_roles()
        self.__clean_fleet_missions(FleetUtilsAI.get_empire_fleet_ids())
        print "Fleets lost by system: %s" % fleetsLostBySystem
        self.update_system_status()
Ejemplo n.º 8
0
    def refresh(self):
        """Turn start AIstate cleanup/refresh."""
        universe = fo.getUniverse()
        # checks exploration border & clears roles/missions of missing fleets & updates fleet locs & threats
        fleetsLostBySystem.clear()
        invasionTargets[:] = []
        exploration_center = PlanetUtilsAI.get_capital_sys_id()
        if exploration_center == INVALID_ID:  # a bad state probably from an old savegame, or else empire has lost (or almost has)
            exploration_center = self.__origin_home_system_id

        for system_id, info in sorted(self.systemStatus.items()):
            self.systemStatus[system_id]['enemy_ship_count'] = 0  # clear now in prep for update_system_status()
        ExplorationAI.graph_flags.clear()
        if fo.currentTurn() < 50:
            print "-------------------------------------------------"
            print "Border Exploration Update (relative to %s)" % (PlanetUtilsAI.sys_name_ids([exploration_center, INVALID_ID])[0])
            print "-------------------------------------------------"
        if self.visBorderSystemIDs == {INVALID_ID}:
            self.visBorderSystemIDs.clear()
            self.visBorderSystemIDs.add(exploration_center)
        for sys_id in list(self.visBorderSystemIDs):  # This set is modified during iteration.
            if fo.currentTurn() < 50:
                print "Considering border system %s" % (PlanetUtilsAI.sys_name_ids([sys_id, INVALID_ID])[0])
            ExplorationAI.follow_vis_system_connections(sys_id, exploration_center)
        newly_explored = ExplorationAI.update_explored_systems()
        nametags = []
        for sys_id in newly_explored:
            newsys = universe.getSystem(sys_id)
            nametags.append("ID:%4d -- %-20s" % (sys_id, (newsys and newsys.name) or "name unknown"))  # an explored system *should* always be able to be gotten
        if newly_explored:
            print "-------------------------------------------------"
            print "Newly explored systems:\n%s" % "\n".join(nametags)
            print "-------------------------------------------------"
        # cleanup fleet roles
        # self.update_fleet_locs()
        self.__clean_fleet_roles()
        self.__clean_fleet_missions(FleetUtilsAI.get_empire_fleet_ids())
        print "Fleets lost by system: %s" % fleetsLostBySystem
        self.update_system_status()
Ejemplo n.º 9
0
def assign_scouts_to_explore_systems():
    # TODO: use Graph Theory to explore closest systems
    universe = fo.getUniverse()
    capital_sys_id = PlanetUtilsAI.get_capital_sys_id()
    # order fleets to explore
    if not border_unexplored_system_ids or (capital_sys_id == INVALID_ID):
        return
    exp_systems_by_dist = sorted(
        (universe.linearDistance(capital_sys_id, x), x)
        for x in border_unexplored_system_ids)
    debug(
        "Exploration system considering following system-distance pairs:\n  %s"
        % ("\n  ".join("%3d: %5.1f" % (sys_id, dist)
                       for (dist, sys_id) in exp_systems_by_dist)))
    explore_list = [sys_id for dist, sys_id in exp_systems_by_dist]

    already_covered, available_scouts = get_current_exploration_info()

    debug("Explorable system IDs: %s" % explore_list)
    debug("Already targeted: %s" % already_covered)
    aistate = get_aistate()
    needs_vis = aistate.misc.setdefault('needs_vis', [])
    check_list = aistate.needsEmergencyExploration + needs_vis + explore_list
    if INVALID_ID in check_list:  # shouldn't normally happen, unless due to bug elsewhere
        for sys_list, name in [(aistate.needsEmergencyExploration,
                                "aistate.needsEmergencyExploration"),
                               (needs_vis, "needs_vis"),
                               (explore_list, "explore_list")]:
            if INVALID_ID in sys_list:
                error("INVALID_ID found in " + name, exc_info=True)
    # emergency coverage can be due to invasion detection trouble, etc.
    debug("Check list: %s" % check_list)
    needs_coverage = [
        sys_id for sys_id in check_list
        if sys_id not in already_covered and sys_id != INVALID_ID
    ]
    debug("Needs coverage: %s" % needs_coverage)

    debug("Available scouts & AIstate locs: %s" %
          [(x, aistate.fleetStatus.get(x, {}).get('sysID', INVALID_ID))
           for x in available_scouts])
    debug("Available scouts & universe locs: %s" %
          [(x, universe.getFleet(x).systemID) for x in available_scouts])
    if not needs_coverage or not available_scouts:
        return

    # clean up targets which can not or don't need to be scouted
    for sys_id in list(needs_coverage):
        if sys_id not in explore_list:  # doesn't necessarily need direct visit
            if universe.getVisibility(sys_id,
                                      fo.empireID()) >= fo.visibility.partial:
                # already got visibility; remove from visit lists and skip
                if sys_id in needs_vis:
                    del needs_vis[needs_vis.index(sys_id)]
                if sys_id in aistate.needsEmergencyExploration:
                    del aistate.needsEmergencyExploration[
                        aistate.needsEmergencyExploration.index(sys_id)]
                debug(
                    "system id %d already currently visible; skipping exploration"
                    % sys_id)
                needs_coverage.remove(sys_id)
                continue

        # skip systems threatened by monsters
        sys_status = aistate.systemStatus.setdefault(sys_id, {})
        if (not aistate.character.may_explore_system(
                sys_status.setdefault('monsterThreat', 0))
                or (fo.currentTurn() < 20
                    and aistate.systemStatus[sys_id]['monsterThreat'] > 0)):
            debug(
                "Skipping exploration of system %d due to Big Monster, threat %d"
                % (sys_id, aistate.systemStatus[sys_id]['monsterThreat']))
            needs_coverage.remove(sys_id)
            continue

    # find the jump distance for all possible scout-system pairings
    options = []
    available_scouts = set(available_scouts)
    for fleet_id in available_scouts:
        fleet_mission = aistate.get_fleet_mission(fleet_id)
        start = fleet_mission.get_location_target()
        for sys_id in needs_coverage:
            target = TargetSystem(sys_id)
            path = MoveUtilsAI.can_travel_to_system(fleet_id,
                                                    start,
                                                    target,
                                                    ensure_return=True)
            if not path:
                continue
            num_jumps = len(
                path) - 1  # -1 as path contains the original system
            options.append((num_jumps, fleet_id, sys_id))

    # Apply a simple, greedy heuristic to match scouts to nearby systems:
    # Always choose the shortest possible path from the remaining scout-system pairing.
    # This is clearly not optimal in the general case but it works well enough for now.
    # TODO: Consider using a more sophisticated assignment algorithm
    options.sort()
    while options:
        debug("Remaining options: %s" % options)
        _, fleet_id, sys_id = options[0]
        fleet_mission = aistate.get_fleet_mission(fleet_id)
        target = TargetSystem(sys_id)
        info("Sending fleet %d to explore %s" % (fleet_id, target))
        fleet_mission.set_target(MissionType.EXPLORATION, target)
        options = [
            option for option in options
            if option[1] != fleet_id and option[2] != sys_id
        ]
        available_scouts.remove(fleet_id)
        needs_coverage.remove(sys_id)

    debug("Exploration assignment finished.")
    debug("Unassigned scouts: %s" % available_scouts)
    debug("Unassigned exploration targets: %s" % needs_coverage)
Ejemplo n.º 10
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()
Ejemplo n.º 11
0
def assign_scouts_to_explore_systems():
    # TODO: use Graph Theory to explore closest systems
    universe = fo.getUniverse()
    capital_sys_id = PlanetUtilsAI.get_capital_sys_id()
    # order fleets to explore
    if not border_unexplored_system_ids or (capital_sys_id == INVALID_ID):
        return
    exp_systems_by_dist = sorted((universe.linearDistance(capital_sys_id, x), x) for x in border_unexplored_system_ids)
    print "Exploration system considering following system-distance pairs:\n  %s" % ("\n  ".join("%3d: %5.1f" % info for info in exp_systems_by_dist))
    explore_list = [sys_id for dist, sys_id in exp_systems_by_dist]

    already_covered, available_scouts = get_current_exploration_info()

    print "Explorable system IDs: %s" % explore_list
    print "Already targeted: %s" % already_covered
    needs_vis = foAI.foAIstate.misc.setdefault('needs_vis', [])
    check_list = foAI.foAIstate.needsEmergencyExploration + needs_vis + explore_list
    if INVALID_ID in check_list:  # shouldn't normally happen, unless due to bug elsewhere
        for sys_list, name in [(foAI.foAIstate.needsEmergencyExploration, "foAI.foAIstate.needsEmergencyExploration"), (needs_vis, "needs_vis"), (explore_list, "explore_list")]:
            if INVALID_ID in sys_list:
                print_error("INVALID_ID found in " + name)
    needs_coverage = [sys_id for sys_id in check_list if sys_id not in already_covered and sys_id != INVALID_ID]  # emergency coverage can be due to invasion detection trouble, etc.
    print "Needs coverage: %s" % needs_coverage

    print "Available scouts & AIstate locs: %s" % [(x, foAI.foAIstate.fleetStatus.get(x, {}).get('sysID', INVALID_ID)) for x in available_scouts]
    print "Available scouts & universe locs: %s" % [(x, universe.getFleet(x).systemID) for x in available_scouts]
    if not needs_coverage or not available_scouts:
        return

    available_scouts = set(available_scouts)
    sent_list = []
    while available_scouts and needs_coverage:
        this_sys_id = needs_coverage.pop(0)
        sys_status = foAI.foAIstate.systemStatus.setdefault(this_sys_id, {})
        if this_sys_id not in explore_list:  # doesn't necessarily need direct visit
            if universe.getVisibility(this_sys_id, fo.empireID()) >= fo.visibility.partial:
                # already got visibility; remove from visit lists and skip
                if this_sys_id in needs_vis:
                    del needs_vis[needs_vis.index(this_sys_id)]
                if this_sys_id in foAI.foAIstate.needsEmergencyExploration:
                    del foAI.foAIstate.needsEmergencyExploration[
                        foAI.foAIstate.needsEmergencyExploration.index(this_sys_id)]
                print "system id %d already currently visible; skipping exploration" % this_sys_id
                continue
        # TODO: if blocked byu monster, try to find nearby system from which to see this system
        if not foAI.foAIstate.character.may_explore_system(sys_status.setdefault('monsterThreat', 0)) or (fo.currentTurn() < 20 and foAI.foAIstate.systemStatus[this_sys_id]['monsterThreat'] > 200):
            print "Skipping exploration of system %d due to Big Monster, threat %d" % (this_sys_id, foAI.foAIstate.systemStatus[this_sys_id]['monsterThreat'])
            continue
        this_fleet_list = FleetUtilsAI.get_fleets_for_mission(target_stats={}, min_stats={}, cur_stats={},
                                                              starting_system=this_sys_id, fleet_pool_set=available_scouts,
                                                              fleet_list=[])
        if not this_fleet_list:
            print "Seem to have run out of scouts while trying to cover sys_id %d" % this_sys_id
            break  # must have ran out of scouts
        fleet_id = this_fleet_list[0]
        fleet_mission = foAI.foAIstate.get_fleet_mission(fleet_id)
        target = universe_object.System(this_sys_id)
        if len(MoveUtilsAI.can_travel_to_system_and_return_to_resupply(fleet_id, fleet_mission.get_location_target(), target)) > 0:
            fleet_mission.set_target(MissionType.EXPLORATION, target)
            sent_list.append(this_sys_id)
        else:  # system too far out, skip it, but can add scout back to available pool
            print "sys_id %d too far out for fleet ( ID %d ) to reach" % (this_sys_id, fleet_id)
            available_scouts.update(this_fleet_list)
    print "Sent scouting fleets to sysIDs : %s" % sent_list
    return
    # pylint: disable=pointless-string-statement
    """
Ejemplo n.º 12
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_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")

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

    if "Main" in thisround:
        totMilRating = sum(CombatRatingsAI.get_fleet_rating(fid) for fid in all_military_fleet_ids)

    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(CombatRatingsAI.get_fleet_rating, 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 = {}
    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(sys_id, 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

    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 "-----------------"

    # TODO The entire code block below is effectively not used as AIstate.opponentSystemIDs is never filled...
    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
    _min_mil_allocations.clear()
    _min_mil_allocations.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
Ejemplo n.º 13
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()
Ejemplo n.º 14
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
Ejemplo n.º 15
0
def assign_scouts_to_explore_systems():
    # TODO: use Graph Theory to explore closest systems
    universe = fo.getUniverse()
    capital_sys_id = PlanetUtilsAI.get_capital_sys_id()
    # order fleets to explore
    # explorable_system_ids = foAI.foAIstate.get_explorable_systems(AIExplorableSystemType.EXPLORABLE_SYSTEM_UNEXPLORED)
    explorable_system_ids = list(borderUnexploredSystemIDs)
    if not explorable_system_ids or (capital_sys_id == -1):
        return
    exp_systems_by_dist = sorted(map(lambda x: (universe.linearDistance(capital_sys_id, x), x), explorable_system_ids))
    print "Exploration system considering following system-distance pairs:\n %s" % (
        "[ " + ", ".join(["%3d : %5.1f" % (sys, dist) for dist, sys in exp_systems_by_dist]) + " ]"
    )
    explore_list = [sys_id for dist, sys_id in exp_systems_by_dist]

    already_covered, available_scouts = get_current_exploration_info()

    print "explorable sys IDs: %s" % explore_list
    print "already targeted: %s" % already_covered
    if "needsEmergencyExploration" not in dir(foAI.foAIstate):
        foAI.foAIstate.needsEmergencyExploration = []
    needs_vis = foAI.foAIstate.misc.setdefault("needs_vis", [])
    check_list = foAI.foAIstate.needsEmergencyExploration + needs_vis + explore_list
    needs_coverage = [
        sys_id for sys_id in check_list if sys_id not in already_covered
    ]  # emergency coverage cane be due to invasion detection trouble, etc.
    print "needs coverage: %s" % needs_coverage

    print "available scouts & AIstate locs: %s" % (
        map(lambda x: (x, foAI.foAIstate.fleetStatus.get(x, {}).get("sysID", -1)), available_scouts)
    )
    print "available scouts & universe locs: %s" % (map(lambda x: (x, universe.getFleet(x).systemID), available_scouts))
    if not needs_coverage or not available_scouts:
        return

    available_scouts = set(available_scouts)
    sent_list = []
    while (len(available_scouts) > 0) and (len(needs_coverage) > 0):
        this_sys_id = needs_coverage.pop(0)
        sys_status = foAI.foAIstate.systemStatus.setdefault(this_sys_id, {})
        if this_sys_id not in explore_list:  # doesn't necessarily need direct visit
            if universe.getVisibility(this_sys_id, fo.empireID()) >= fo.visibility.partial:
                # already got visibility; remove from visit lists and skip
                if this_sys_id in needs_vis:
                    del needs_vis[needs_vis.index(this_sys_id)]
                if this_sys_id in foAI.foAIstate.needsEmergencyExploration:
                    del foAI.foAIstate.needsEmergencyExploration[
                        foAI.foAIstate.needsEmergencyExploration.index(this_sys_id)
                    ]
                print "sys id %d already currently visible; skipping exploration" % this_sys_id
                continue
        # TODO: if blocked byu monster, try to find nearby sys from which to see this sys
        if (sys_status.setdefault("monsterThreat", 0) > 2000 * foAI.foAIstate.aggression) or (
            fo.currentTurn() < 20 and foAI.foAIstate.systemStatus[this_sys_id]["monsterThreat"] > 200
        ):
            print "Skipping exploration of system %d due to Big Monster, threat %d" % (
                this_sys_id,
                foAI.foAIstate.systemStatus[this_sys_id]["monsterThreat"],
            )
            continue
        found_fleets = []
        this_fleet_list = FleetUtilsAI.get_fleets_for_mission(
            nships=1,
            target_stats={},
            min_stats={},
            cur_stats={},
            species="",
            systems_to_check=[this_sys_id],
            systems_checked=[],
            fleet_pool_set=available_scouts,
            fleet_list=found_fleets,
            verbose=False,
        )
        if not this_fleet_list:
            print "seem to have run out of scouts while trying to cover sys_id %d" % this_sys_id
            break  # must have ran out of scouts
        fleet_id = this_fleet_list[0]
        fleet_mission = foAI.foAIstate.get_fleet_mission(fleet_id)
        target = universe_object.System(this_sys_id)
        if (
            len(
                MoveUtilsAI.can_travel_to_system_and_return_to_resupply(
                    fleet_id, fleet_mission.get_location_target(), target
                )
            )
            > 0
        ):
            fleet_mission.add_target(AIFleetMissionType.FLEET_MISSION_EXPLORATION, target)
            sent_list.append(this_sys_id)
        else:  # system too far out, skip it, but can add scout back to available pool
            print "sys_id %d too far out for fleet ( ID %d ) to reach" % (this_sys_id, fleet_id)
            available_scouts.update(this_fleet_list)
    print "sent scouting fleets to sysIDs : %s" % sent_list
    return
    # pylint: disable=pointless-string-statement
    """
Ejemplo n.º 16
0
    def refresh(self):
        """Turn start AIstate cleanup/refresh."""
        universe = fo.getUniverse()
        # checks exploration border & clears roles/missions of missing fleets & updates fleet locs & threats
        fleetsLostBySystem.clear()
        invasionTargets[:] = []
        exploration_center = PlanetUtilsAI.get_capital_sys_id()
        if exploration_center == INVALID_ID:  # a bad state probably from an old savegame, or else empire has lost (or almost has)
            exploration_center = self.__origin_home_system_id

        # check if planets in cache is still present. Remove destroyed.
        for system_id, info in sorted(self.systemStatus.items()):
            self.systemStatus[system_id][
                'enemy_ship_count'] = 0  # clear now in prep for update_system_status()
            planet_dict = info.get('planets', {})
            cache_planet_set = set(planet_dict)
            system_planet_set = set(
                *(sys.planetIDs for sys in [universe.getSystem(system_id)]
                  if sys))
            diff = cache_planet_set - system_planet_set
            if diff:
                print "Removing destroyed planets from systemStatus for system %s: planets to be removed: %s" % (
                    system_id, sorted(diff))
                for key in diff:
                    del planet_dict[key]

        ExplorationAI.graphFlags.clear()
        if fo.currentTurn() < 50:
            print "-------------------------------------------------"
            print "Border Exploration Update (relative to %s)" % (
                PlanetUtilsAI.sys_name_ids([exploration_center, INVALID_ID
                                            ])[0])
            print "-------------------------------------------------"
        if self.visBorderSystemIDs.keys() == [INVALID_ID]:
            self.visBorderSystemIDs.clear()
            self.visBorderSystemIDs[exploration_center] = 1
        for sys_id in self.visBorderSystemIDs.keys(
        ):  # This dict modified during iteration.
            if fo.currentTurn() < 50:
                print "Considering border system %s" % (
                    PlanetUtilsAI.sys_name_ids([sys_id, INVALID_ID])[0])
            ExplorationAI.follow_vis_system_connections(
                sys_id, exploration_center)
        newly_explored = ExplorationAI.update_explored_systems()
        nametags = []
        for sys_id in newly_explored:
            newsys = universe.getSystem(sys_id)
            nametags.append(
                "ID:%4d -- %-20s" %
                (sys_id, (newsys and newsys.name) or "name unknown")
            )  # an explored system *should* always be able to be gotten
        if newly_explored:
            print "-------------------------------------------------"
            print "Newly explored systems:\n%s" % "\n".join(nametags)
            print "-------------------------------------------------"
        # cleanup fleet roles
        # self.update_fleet_locs()
        self.__clean_fleet_roles()
        self.__clean_fleet_missions(FleetUtilsAI.get_empire_fleet_ids())
        print "Fleets lost by system: %s" % fleetsLostBySystem
        self.update_system_status()
Ejemplo n.º 17
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
Ejemplo n.º 18
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()
Ejemplo n.º 19
0
def assign_scouts_to_explore_systems():
    # TODO: use Graph Theory to explore closest systems
    universe = fo.getUniverse()
    capital_sys_id = PlanetUtilsAI.get_capital_sys_id()
    # order fleets to explore
    if not border_unexplored_system_ids or (capital_sys_id == INVALID_ID):
        return
    exp_systems_by_dist = sorted((universe.linearDistance(capital_sys_id, x), x) for x in border_unexplored_system_ids)
    debug("Exploration system considering following system-distance pairs:\n  %s" % (
        "\n  ".join("%3d: %5.1f" % (sys_id, dist) for (dist, sys_id) in exp_systems_by_dist)))
    explore_list = [sys_id for dist, sys_id in exp_systems_by_dist]

    already_covered, available_scouts = get_current_exploration_info()

    debug("Explorable system IDs: %s" % explore_list)
    debug("Already targeted: %s" % already_covered)
    aistate = get_aistate()
    needs_vis = aistate.misc.setdefault('needs_vis', [])
    check_list = aistate.needsEmergencyExploration + needs_vis + explore_list
    if INVALID_ID in check_list:  # shouldn't normally happen, unless due to bug elsewhere
        for sys_list, name in [(aistate.needsEmergencyExploration, "aistate.needsEmergencyExploration"),
                               (needs_vis, "needs_vis"), (explore_list, "explore_list")]:
            if INVALID_ID in sys_list:
                error("INVALID_ID found in " + name, exc_info=True)
    # emergency coverage can be due to invasion detection trouble, etc.
    debug("Check list: %s" % check_list)
    needs_coverage = [sys_id for sys_id in check_list if sys_id not in already_covered and sys_id != INVALID_ID]
    debug("Needs coverage: %s" % needs_coverage)

    debug("Available scouts & AIstate locs: %s" % [(x, aistate.fleetStatus.get(x, {}).get('sysID', INVALID_ID))
                                                   for x in available_scouts])
    debug("Available scouts & universe locs: %s" % [(x, universe.getFleet(x).systemID) for x in available_scouts])
    if not needs_coverage or not available_scouts:
        return

    # clean up targets which can not or don't need to be scouted
    for sys_id in list(needs_coverage):
        if sys_id not in explore_list:  # doesn't necessarily need direct visit
            if universe.getVisibility(sys_id, fo.empireID()) >= fo.visibility.partial:
                # already got visibility; remove from visit lists and skip
                if sys_id in needs_vis:
                    del needs_vis[needs_vis.index(sys_id)]
                if sys_id in aistate.needsEmergencyExploration:
                    del aistate.needsEmergencyExploration[
                        aistate.needsEmergencyExploration.index(sys_id)]
                debug("system id %d already currently visible; skipping exploration" % sys_id)
                needs_coverage.remove(sys_id)
                continue

        # skip systems threatened by monsters
        sys_status = aistate.systemStatus.setdefault(sys_id, {})
        if (not aistate.character.may_explore_system(sys_status.setdefault('monsterThreat', 0)) or (
                fo.currentTurn() < 20 and aistate.systemStatus[sys_id]['monsterThreat'] > 0)):
            debug("Skipping exploration of system %d due to Big Monster, threat %d" % (
                sys_id, aistate.systemStatus[sys_id]['monsterThreat']))
            needs_coverage.remove(sys_id)
            continue

    # find the jump distance for all possible scout-system pairings
    options = []
    available_scouts = set(available_scouts)
    for fleet_id in available_scouts:
        fleet_mission = aistate.get_fleet_mission(fleet_id)
        start = fleet_mission.get_location_target()
        for sys_id in needs_coverage:
            target = TargetSystem(sys_id)
            path = MoveUtilsAI.can_travel_to_system(fleet_id, start, target, ensure_return=True)
            if not path:
                continue
            num_jumps = len(path) - 1  # -1 as path contains the original system
            options.append((num_jumps, fleet_id, sys_id))

    # Apply a simple, greedy heuristic to match scouts to nearby systems:
    # Always choose the shortest possible path from the remaining scout-system pairing.
    # This is clearly not optimal in the general case but it works well enough for now.
    # TODO: Consider using a more sophisticated assignment algorithm
    options.sort()
    while options:
        debug("Remaining options: %s" % options)
        _, fleet_id, sys_id = options[0]
        fleet_mission = aistate.get_fleet_mission(fleet_id)
        target = TargetSystem(sys_id)
        info("Sending fleet %d to explore %s" % (fleet_id, target))
        fleet_mission.set_target(MissionType.EXPLORATION, target)
        options = [option for option in options if option[1] != fleet_id and option[2] != sys_id]
        available_scouts.remove(fleet_id)
        needs_coverage.remove(sys_id)

    debug("Exploration assignment finished.")
    debug("Unassigned scouts: %s" % available_scouts)
    debug("Unassigned exploration targets: %s" % needs_coverage)
Ejemplo n.º 20
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()
Ejemplo n.º 21
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