示例#1
0
 def _calculate_threat(self):
     nearby_forces = CombatRatingsAI.combine_ratings(
         self._potential_support(), self.assigned_rating)
     return (
         self.threat_bias +
         + self.safety_factor * CombatRatingsAI.combine_ratings(self._local_threat(),
                                                                self._neighbor_threat()) +
         + max(0., self._potential_threat() + self._jump2_threat() - nearby_forces))
示例#2
0
 def area_ratings(self, system_ids):
     """Returns (fleet_threat, max_threat, myFleetRating, threat_fleets) compiled over a group of systems."""
     myrating = threat = max_threat = 0
     threat_fleets = set()
     for sys_id in system_ids:
         sys_status = self.systemStatus.get(sys_id, {})
         # TODO: have distinct treatment for both enemy_threat and fleetThreat, respectively
         fthreat = sys_status.get('enemy_threat', 0)
         max_threat = max(max_threat, fthreat)
         threat = CombatRatingsAI.combine_ratings(threat, fthreat)
         myrating = CombatRatingsAI.combine_ratings(myrating, sys_status.get('myFleetRating', 0))
         # myrating = FleetUtilsAI.combine_ratings(myrating, sys_status.get('all_local_defenses', 0))
         threat_fleets.update(sys_status.get('local_fleet_threats', []))
     return threat, max_threat, myrating, threat_fleets
示例#3
0
def merge_fleet_a_into_b(fleet_a_id, fleet_b_id, leave_rating=0, need_rating=0, context=""):
    universe = fo.getUniverse()
    fleet_a = universe.getFleet(fleet_a_id)
    fleet_b = universe.getFleet(fleet_b_id)
    if not fleet_a or not fleet_b:
        return 0
    system_id = fleet_a.systemID
    if fleet_b.systemID != system_id:
        return 0
    remaining_rating = CombatRatingsAI.get_fleet_rating(fleet_a_id)
    transferred_rating = 0
    for ship_id in fleet_a.shipIDs:
        this_ship = universe.getShip(ship_id)
        if not this_ship:
            continue
        this_rating = CombatRatingsAI.ShipCombatStats(ship_id).get_rating()
        remaining_rating = CombatRatingsAI.rating_needed(remaining_rating, this_rating)
        if remaining_rating < leave_rating:  # merging this would leave old fleet under minimum rating, try other ships.
            continue
        transferred = fo.issueFleetTransferOrder(ship_id, fleet_b_id)
        if transferred:
            transferred_rating = CombatRatingsAI.combine_ratings(transferred_rating, this_rating)
        else:
            print "  *** transfer of ship %4d, formerly of fleet %4d, into fleet %4d failed; %s" % (
                ship_id, fleet_a_id, fleet_b_id, (" context is %s" % context) if context else "")
        if need_rating != 0 and need_rating <= transferred_rating:
            break
    fleet_a = universe.getFleet(fleet_a_id)
    if not fleet_a or fleet_a.empty or fleet_a_id in universe.destroyedObjectIDs(fo.empireID()):
        foAI.foAIstate.delete_fleet_info(fleet_a_id)
    foAI.foAIstate.update_fleet_rating(fleet_b_id)
示例#4
0
    def _portion_of_fleet_needed_here(self):
        """Calculate the portion of the fleet needed in target system considering enemy forces."""
        # TODO check rating against planets
        if assertion_fails(self.type in COMBAT_MISSION_TYPES, msg=str(self)):
            return 0
        if assertion_fails(self.target and self.target.id != INVALID_ID, msg=str(self)):
            return 0
        system_id = self.target.id
        aistate = get_aistate()
        local_defenses = MilitaryAI.get_my_defense_rating_in_system(system_id)
        potential_threat = CombatRatingsAI.combine_ratings(
            MilitaryAI.get_system_local_threat(system_id),
            MilitaryAI.get_system_neighbor_threat(system_id)
        )
        universe = fo.getUniverse()
        system = universe.getSystem(system_id)

        # tally planetary defenses
        total_defense = total_shields = 0
        for planet_id in system.planetIDs:
            planet = universe.getPlanet(planet_id)
            total_defense += planet.currentMeterValue(fo.meterType.defense)
            total_shields += planet.currentMeterValue(fo.meterType.shield)
        planetary_ratings = total_defense * (total_shields + total_defense)
        potential_threat += planetary_ratings  # TODO: rewrite to return min rating vs planets as well

        # consider safety factor just once here rather than everywhere below
        safety_factor = aistate.character.military_safety_factor()
        potential_threat *= safety_factor

        fleet_rating = CombatRatingsAI.get_fleet_rating(self.fleet.id)
        return CombatRatingsAI.rating_needed(potential_threat, local_defenses) / float(fleet_rating)
示例#5
0
    def _calculate_threat(self):

        systems_status = foAI.foAIstate.systemStatus.get(self.sys_id, {})
        threat = self.safety_factor * CombatRatingsAI.combine_ratings(systems_status.get('fleetThreat', 0),
                                                                      systems_status.get('monsterThreat', 0) +
                                                                      + systems_status.get('planetThreat', 0))

        return self.threat_bias + threat
示例#6
0
def merge_fleet_a_into_b(fleet_a_id, fleet_b_id, leave_rating=0, need_rating=0, context=""):
    universe = fo.getUniverse()
    fleet_a = universe.getFleet(fleet_a_id)
    fleet_b = universe.getFleet(fleet_b_id)
    if not fleet_a or not fleet_b:
        return 0
    remaining_rating = CombatRatingsAI.get_fleet_rating(fleet_a_id)
    transferred_rating = 0
    b_has_monster = False
    for ship_id in fleet_b.shipIDs:
        this_ship = universe.getShip(ship_id)
        if not this_ship:
            continue
        if this_ship.isMonster:
            b_has_monster = True
            break
    for ship_id in fleet_a.shipIDs:
        this_ship = universe.getShip(ship_id)
        if not this_ship or this_ship.isMonster != b_has_monster:  # TODO Is there any reason for the monster check?
            continue
        this_rating = CombatRatingsAI.ShipCombatStats(ship_id).get_rating()
        remaining_rating = CombatRatingsAI.rating_needed(remaining_rating, this_rating)
        if remaining_rating < leave_rating:  # merging this would leave old fleet under minimum rating, try other ships.
            continue
        transferred = fo.issueFleetTransferOrder(ship_id, fleet_b_id)
        if transferred:
            transferred_rating = CombatRatingsAI.combine_ratings(transferred_rating, this_rating)
        else:
            print "  *** transfer of ship %4d, formerly of fleet %4d, into fleet %4d failed; %s" % (
                ship_id, fleet_a_id, fleet_b_id, [" context is %s" % context, ""][context == ""])
        if need_rating != 0 and need_rating <= transferred_rating:
            break
    fleet_a = universe.getFleet(fleet_a_id)
    if not fleet_a or fleet_a.empty or fleet_a_id in universe.destroyedObjectIDs(fo.empireID()):
        foAI.foAIstate.delete_fleet_info(fleet_a_id)
    foAI.foAIstate.update_fleet_rating(fleet_b_id)
示例#7
0
    def __update_system_status(self):
        print 10 * "=", "Updating System Threats", 10 * "="
        universe = fo.getUniverse()
        empire = fo.getEmpire()
        empire_id = fo.empireID()
        destroyed_object_ids = universe.destroyedObjectIDs(empire_id)
        supply_unobstructed_systems = set(empire.supplyUnobstructedSystems)
        min_hidden_attack = 4
        min_hidden_health = 8
        observed_empires = self.misc.setdefault("observed_empires", set())

        # TODO: Variables that are recalculated each turn from scratch should not be stored in AIstate
        # clear previous game state
        for sys_id in self.systemStatus:
            self.systemStatus[sys_id]['enemy_ship_count'] = 0
            self.systemStatus[sys_id]['myFleetRating'] = 0
            self.systemStatus[sys_id]['myFleetRatingVsPlanets'] = 0

        # for use in debugging
        verbose = False

        # assess enemy fleets that may have been momentarily visible
        enemies_by_system = {}
        my_fleets_by_system = {}
        fleet_spot_position = {}
        current_turn = fo.currentTurn()
        for fleet_id in universe.fleetIDs:
            fleet = universe.getFleet(fleet_id)
            if not fleet or fleet.empty:
                self.delete_fleet_info(fleet_id)  # this is safe even if fleet wasn't mine
                continue
            # TODO: check if currently in system and blockaded before accepting destination as location
            this_system_id = fleet.nextSystemID if fleet.nextSystemID != INVALID_ID else fleet.systemID
            dead_fleet = fleet_id in destroyed_object_ids
            if dead_fleet:
                self.delete_fleet_info(fleet_id)

            if fleet.ownedBy(empire_id):
                if not dead_fleet:
                    my_fleets_by_system.setdefault(this_system_id, []).append(fleet_id)
                    fleet_spot_position.setdefault(fleet.systemID, []).append(fleet_id)
                continue

            # TODO: consider checking death of individual ships.  If ships had been moved from this fleet
            # into another fleet, we might have witnessed their death in that other fleet but if this fleet
            # had not been seen since before that transfer then the ships might also still be listed here.
            if dead_fleet:
                continue

            # we are only interested in immediately recent data
            if get_partial_visibility_turn(fleet_id) < (current_turn - 1):
                continue

            sys_status = self.systemStatus.setdefault(this_system_id, {})
            sys_status['enemy_ship_count'] = sys_status.get('enemy_ship_count', 0) + len(fleet.shipIDs)
            enemies_by_system.setdefault(this_system_id, []).append(fleet_id)

            if not fleet.unowned:
                self.misc.setdefault('enemies_sighted', {}).setdefault(current_turn, []).append(fleet_id)
                observed_empires.add(fleet.owner)

        # assess fleet and planet threats & my local fleets
        for sys_id in universe.systemIDs:
            sys_status = self.systemStatus.setdefault(sys_id, {})
            system = universe.getSystem(sys_id)
            if verbose:
                print "AIState threat evaluation for %s" % system
            # update fleets
            sys_status['myfleets'] = my_fleets_by_system.get(sys_id, [])
            sys_status['myFleetsAccessible'] = fleet_spot_position.get(sys_id, [])
            local_enemy_fleet_ids = enemies_by_system.get(sys_id, [])
            sys_status['localEnemyFleetIDs'] = local_enemy_fleet_ids
            if system:
                sys_status['name'] = system.name

            # update threats
            monster_ratings = []  # immobile
            enemy_ratings = []  # owned & mobile
            mob_ratings = []  # mobile & unowned
            mobile_fleets = []  # mobile and either owned or unowned
            for fid in local_enemy_fleet_ids:
                fleet = universe.getFleet(fid)  # ensured to exist
                fleet_rating = CombatRatingsAI.get_fleet_rating(
                    fid, enemy_stats=CombatRatingsAI.get_empire_standard_fighter())
                if fleet.speed == 0:
                    monster_ratings.append(fleet_rating)
                    if verbose:
                        print "\t immobile enemy fleet %s has rating %.1f" % (fleet, fleet_rating)
                    continue

                if verbose:
                    print "\t mobile enemy fleet %s has rating %.1f" % (fleet, fleet_rating)
                mobile_fleets.append(fid)
                if fleet.unowned:
                    mob_ratings.append(fleet_rating)
                else:
                    enemy_ratings.append(fleet_rating)

            enemy_rating = CombatRatingsAI.combine_ratings_list(enemy_ratings)
            monster_rating = CombatRatingsAI.combine_ratings_list(monster_ratings)
            mob_rating = CombatRatingsAI.combine_ratings_list(mob_ratings)
            lost_fleets = fleetsLostBySystem.get(sys_id, [])
            lost_fleet_rating = CombatRatingsAI.combine_ratings_list(lost_fleets)

            # under current visibility rules should not be possible to have any losses or other info here,
            # but just in case...
            partial_vis_turn = get_partial_visibility_turn(sys_id)
            if not system or partial_vis_turn < 0:
                if verbose:
                    print "Never had partial vis for %s - basing threat assessment on old info and lost ships" % system
                sys_status.setdefault('local_fleet_threats', set())
                sys_status['planetThreat'] = 0
                sys_status['fleetThreat'] = max(
                    CombatRatingsAI.combine_ratings(enemy_rating, mob_rating),
                    0.98 * sys_status.get('fleetThreat', 0),
                    1.1*lost_fleet_rating - monster_rating)
                sys_status['monsterThreat'] = max(
                    monster_rating,
                    0.98 * sys_status.get('monsterThreat', 0),
                    1.1*lost_fleet_rating - enemy_rating - mob_rating)
                sys_status['enemy_threat'] = max(
                    enemy_rating,
                    0.98 * sys_status.get('enemy_threat', 0),
                    1.1*lost_fleet_rating - monster_rating - mob_rating)
                sys_status['mydefenses'] = {'overall': 0, 'attack': 0, 'health': 0}
                sys_status['totalThreat'] = sys_status['fleetThreat']
                sys_status['regional_fleet_threats'] = sys_status['local_fleet_threats'].copy()
                continue

            # have either stale or current info
            pattack = phealth = 0
            mypattack = myphealth = 0
            for pid in system.planetIDs:
                planet = universe.getPlanet(pid)
                if not planet:
                    continue
                prating = self.assess_planet_threat(pid, sighting_age=current_turn - partial_vis_turn)
                if planet.ownedBy(empire_id):  # TODO: check for diplomatic status
                    mypattack += prating['attack']
                    myphealth += prating['health']
                else:
                    pattack += prating['attack']
                    phealth += prating['health']
                    if any("_NEST_" in special for special in planet.specials):
                        sys_status['nest_threat'] = 100
            sys_status['planetThreat'] = pattack * phealth
            sys_status['mydefenses'] = {'overall': mypattack * myphealth, 'attack': mypattack, 'health': myphealth}

            # previous threat assessment could account for losses, ignore the losses now
            if max(sys_status.get('totalThreat', 0), pattack * phealth) >= 0.6 * lost_fleet_rating:
                lost_fleet_rating = 0

            # TODO use sitrep combat info rather than estimating stealthed enemies by fleets lost to them
            # TODO also only consider past stealthed fleet threat to still be present if the system is still obstructed
            # TODO: track visibility across turns in order to distinguish the blip of visibility in (losing) combat,
            #       which FO currently treats as being for the previous turn,
            #       partially superseding the previous visibility for that turn

            if not partial_vis_turn == current_turn:
                sys_status.setdefault('local_fleet_threats', set())
                sys_status['currently_visible'] = False
                # print ("Stale visibility for system %d ( %s ) -- last seen %d, "
                #        "current Turn %d -- basing threat assessment on old info and lost ships") % (
                #     sys_id, sys_status.get('name', "name unknown"), partial_vis_turn, currentTurn)
                sys_status['fleetThreat'] = max(
                    CombatRatingsAI.combine_ratings(enemy_rating, mob_rating),
                    0.98 * sys_status.get('fleetThreat', 0),
                    2.0 * lost_fleet_rating - max(sys_status.get('monsterThreat', 0), monster_rating))
                sys_status['enemy_threat'] = max(
                    enemy_rating,
                    0.98 * sys_status.get('enemy_threat', 0),
                    1.1*lost_fleet_rating - max(sys_status.get('monsterThreat', 0), monster_rating))
                sys_status['monsterThreat'] = max(monster_rating, 0.98 * sys_status.get('monsterThreat', 0))
                # sys_status['totalThreat'] = ((pattack + enemy_attack + monster_attack) ** 0.8)\
                #                             * ((phealth + enemy_health + monster_health)** 0.6)  # reevaluate this
                sys_status['totalThreat'] = max(
                    CombatRatingsAI.combine_ratings_list([enemy_rating, mob_rating, monster_rating, pattack * phealth]),
                    2 * lost_fleet_rating,
                    0.98 * sys_status.get('totalThreat', 0))
            else:  # system considered visible
                sys_status['currently_visible'] = True
                sys_status['local_fleet_threats'] = set(mobile_fleets)
                # includes mobile monsters
                sys_status['fleetThreat'] = max(
                    CombatRatingsAI.combine_ratings(enemy_rating, mob_rating), 2*lost_fleet_rating - monster_rating)
                if verbose:
                    print "enemy threat calc parts: enemy rating %.1f, lost fleet rating %.1f, monster_rating %.1f" % (
                        enemy_rating, lost_fleet_rating, monster_rating)
                # does NOT include mobile monsters
                sys_status['enemy_threat'] = max(enemy_rating, 2*lost_fleet_rating - monster_rating)
                sys_status['monsterThreat'] = monster_rating
                sys_status['totalThreat'] = CombatRatingsAI.combine_ratings_list(
                    [enemy_rating, mob_rating, monster_rating, pattack * phealth])
            sys_status['regional_fleet_threats'] = sys_status['local_fleet_threats'].copy()
            sys_status['fleetThreat'] = max(sys_status['fleetThreat'], sys_status.get('nest_threat', 0))
            sys_status['totalThreat'] = max(sys_status['totalThreat'], sys_status.get('nest_threat', 0))

            # has been seen with Partial Vis, but is currently supply-blocked
            if partial_vis_turn > 0 and sys_id not in supply_unobstructed_systems:
                sys_status['fleetThreat'] = max(sys_status['fleetThreat'], min_hidden_attack * min_hidden_health)
                sys_status['totalThreat'] = max(
                    sys_status['totalThreat'],
                    ((pattack + min_hidden_attack) ** 0.8) * ((phealth + min_hidden_health) ** 0.6))
            if verbose and sys_status['fleetThreat'] > 0:
                print "%s intermediate status: %s" % (system, sys_status)

        enemy_supply, enemy_near_supply = self.assess_enemy_supply()  # TODO: assess change in enemy supply over time
        # assess secondary threats (threats of surrounding systems) and update my fleet rating
        for sys_id in universe.systemIDs:
            sys_status = self.systemStatus[sys_id]
            sys_status['enemies_supplied'] = enemy_supply.get(sys_id, [])
            observed_empires.update(enemy_supply.get(sys_id, []))
            sys_status['enemies_nearly_supplied'] = enemy_near_supply.get(sys_id, [])
            my_ratings_list = []
            my_ratings_against_planets_list = []
            for fid in sys_status['myfleets']:
                this_rating = self.get_rating(fid, True, self.get_standard_enemy())
                my_ratings_list.append(this_rating)
                my_ratings_against_planets_list.append(self.get_rating(fid, against_planets=True))
            if sys_id != INVALID_ID:
                sys_status['myFleetRating'] = CombatRatingsAI.combine_ratings_list(my_ratings_list)
                sys_status['myFleetRatingVsPlanets'] = CombatRatingsAI.combine_ratings_list(
                    my_ratings_against_planets_list)
                sys_status['all_local_defenses'] = CombatRatingsAI.combine_ratings(
                    sys_status['myFleetRating'], sys_status['mydefenses']['overall'])
            sys_status['neighbors'] = set(universe.getImmediateNeighbors(sys_id, self.empireID))

        for sys_id in universe.systemIDs:
            sys_status = self.systemStatus[sys_id]
            neighbors = sys_status.get('neighbors', set())
            this_system = universe.getSystem(sys_id)
            if verbose:
                print "Regional Assessment for %s with local fleet threat %.1f" % (
                    this_system, sys_status.get('fleetThreat', 0))
            jumps2 = set()
            jumps3 = set()
            jumps4 = set()
            for seta, setb in [(neighbors, jumps2), (jumps2, jumps3), (jumps3, jumps4)]:
                for sys2id in seta:
                    setb.update(self.systemStatus.get(sys2id, {}).get('neighbors', set()))
            jump2ring = jumps2 - neighbors - {sys_id}
            jump3ring = jumps3 - jumps2 - neighbors - {sys_id}
            jump4ring = jumps4 - jumps3 - jumps2 - neighbors - {sys_id}
            sys_status['2jump_ring'] = jump2ring
            sys_status['3jump_ring'] = jump3ring
            sys_status['4jump_ring'] = jump4ring
            threat, max_threat, myrating, j1_threats = self.area_ratings(neighbors)
            sys_status['neighborThreat'] = threat
            sys_status['max_neighbor_threat'] = max_threat
            sys_status['my_neighbor_rating'] = myrating
            threat, max_threat, myrating, j2_threats = self.area_ratings(jump2ring)
            sys_status['jump2_threat'] = threat
            sys_status['my_jump2_rating'] = myrating
            threat, max_threat, myrating, j3_threats = self.area_ratings(jump3ring)
            sys_status['jump3_threat'] = threat
            sys_status['my_jump3_rating'] = myrating
            # for local system includes both enemies and mobs
            threat_keys = ['fleetThreat', 'neighborThreat', 'jump2_threat']
            sys_status['regional_threat'] = CombatRatingsAI.combine_ratings_list(
                [sys_status.get(x, 0) for x in threat_keys])
            # TODO: investigate cases where regional_threat has been nonzero but no regional_threat_fleets
            # (probably due to attenuating history of past threats)
            sys_status.setdefault('regional_fleet_threats', set()).update(j1_threats, j2_threats)
示例#8
0
    def update_system_status(self):
        print 10 * "=", "Updating System Threats", 10 * "="
        universe = fo.getUniverse()
        empire = fo.getEmpire()
        empire_id = fo.empireID()
        destroyed_object_ids = universe.destroyedObjectIDs(empire_id)
        supply_unobstructed_systems = set(empire.supplyUnobstructedSystems)
        min_hidden_attack = 4
        min_hidden_health = 8
        system_id_list = universe.systemIDs  # will normally look at this, the list of all known systems

        # for use in debugging
        verbose = False

        # assess enemy fleets that may have been momentarily visible
        cur_e_fighters = {CombatRatingsAI.default_ship_stats().get_stats(hashable=True): [0]}  # start with a dummy entry
        old_e_fighters = {CombatRatingsAI.default_ship_stats().get_stats(hashable=True): [0]}  # start with a dummy entry
        enemy_fleet_ids = []
        enemies_by_system = {}
        my_fleets_by_system = {}
        fleet_spot_position = {}
        saw_enemies_at_system = {}
        my_milship_rating = MilitaryAI.cur_best_mil_ship_rating()
        current_turn = fo.currentTurn()
        for fleet_id in universe.fleetIDs:
            fleet = universe.getFleet(fleet_id)
            if fleet is None:
                continue
            if not fleet.empty:
                # TODO: check if currently in system and blockaded before accepting destination as location
                this_system_id = (fleet.nextSystemID != INVALID_ID and fleet.nextSystemID) or fleet.systemID
                if fleet.ownedBy(empire_id):
                    if fleet_id not in destroyed_object_ids:
                        my_fleets_by_system.setdefault(this_system_id, []).append(fleet_id)
                        fleet_spot_position.setdefault(fleet.systemID, []).append(fleet_id)
                else:
                    dead_fleet = fleet_id in destroyed_object_ids
                    if not fleet.ownedBy(-1) and (fleet.hasArmedShips or fleet.hasFighterShips):
                        ship_stats = CombatRatingsAI.FleetCombatStats(fleet_id).get_ship_stats(hashable=True)
                        e_f_dict = [cur_e_fighters, old_e_fighters][dead_fleet]  # track old/dead enemy fighters for rating assessments in case not enough current info
                        for stats in ship_stats:
                            attacks = stats[0]
                            if attacks:
                                e_f_dict.setdefault(stats, [0])[0] += 1
                    partial_vis_turn = universe.getVisibilityTurnsMap(fleet_id, empire_id).get(fo.visibility.partial, -9999)
                    if not dead_fleet:
                        # TODO: consider checking death of individual ships.  If ships had been moved from this fleet
                        # into another fleet, we might have witnessed their death in that other fleet but if this fleet
                        # had not been seen since before that transfer then the ships might also still be listed here.
                        sys_status = self.systemStatus.setdefault(this_system_id, {})
                        sys_status['enemy_ship_count'] = sys_status.get('enemy_ship_count', 0) + len(fleet.shipIDs)
                        if partial_vis_turn >= current_turn - 1:  # only interested in immediately recent data
                            saw_enemies_at_system[fleet.systemID] = True
                            enemy_fleet_ids.append(fleet_id)
                            enemies_by_system.setdefault(this_system_id, []).append(fleet_id)
                            if not fleet.ownedBy(-1):
                                self.misc.setdefault('enemies_sighted', {}).setdefault(current_turn, []).append(fleet_id)
                                rating = CombatRatingsAI.get_fleet_rating(fleet_id, enemy_stats=CombatRatingsAI.get_empire_standard_fighter())
                                if rating > 0.25 * my_milship_rating:
                                    self.misc.setdefault('dangerous_enemies_sighted', {}).setdefault(current_turn, []).append(fleet_id)
        e_f_dict = [cur_e_fighters, old_e_fighters][len(cur_e_fighters) == 1]
        std_fighter = sorted([(v, k) for k, v in e_f_dict.items()])[-1][1]
        self.__empire_standard_enemy = std_fighter
        self.empire_standard_enemy_rating = self.get_standard_enemy().get_rating()
        # TODO: If no current information available, rate against own fighters

        # assess fleet and planet threats & my local fleets
        for sys_id in system_id_list:
            sys_status = self.systemStatus.setdefault(sys_id, {})
            system = universe.getSystem(sys_id)
            if verbose:
                print "AIState threat evaluation for %s" % system
            # update fleets
            sys_status['myfleets'] = my_fleets_by_system.get(sys_id, [])
            sys_status['myFleetsAccessible'] = fleet_spot_position.get(sys_id, [])
            local_enemy_fleet_ids = enemies_by_system.get(sys_id, [])
            sys_status['localEnemyFleetIDs'] = local_enemy_fleet_ids
            if system:
                sys_status['name'] = system.name
                for fid in system.fleetIDs:
                    if fid in destroyed_object_ids:  # TODO: double check are these checks/deletes necessary?
                        self.delete_fleet_info(fid)  # this is safe even if fleet wasn't mine
                        continue
                    fleet = universe.getFleet(fid)
                    if not fleet or fleet.empty:
                        self.delete_fleet_info(fid)  # this is safe even if fleet wasn't mine
                        continue

            # update threats
            sys_vis_dict = universe.getVisibilityTurnsMap(sys_id, fo.empireID())
            partial_vis_turn = sys_vis_dict.get(fo.visibility.partial, -9999)
            mob_ratings = []  # for mobile unowned monster fleets
            lost_fleet_rating = 0
            enemy_ratings = []
            monster_ratings = []
            mobile_fleets = []
            for fid in local_enemy_fleet_ids:
                fleet = universe.getFleet(fid)
                if not fleet:
                    continue
                fleet_rating = CombatRatingsAI.get_fleet_rating(fid, enemy_stats=CombatRatingsAI.get_empire_standard_fighter())
                if fleet.speed == 0:
                    monster_ratings.append(fleet_rating)
                    if verbose:
                        print "\t immobile enemy fleet %s has rating %.1f" % (fleet, fleet_rating)
                else:
                    if verbose:
                        print "\t mobile enemy fleet %s has rating %.1f" % (fleet, fleet_rating)
                    mobile_fleets.append(fid)
                    if fleet.unowned:
                        mob_ratings.append(fleet_rating)
                    else:
                        enemy_ratings.append(fleet_rating)
            enemy_rating = CombatRatingsAI.combine_ratings_list(enemy_ratings)
            monster_rating = CombatRatingsAI.combine_ratings_list(monster_ratings)
            mob_rating = CombatRatingsAI.combine_ratings_list(mob_ratings)
            if fleetsLostBySystem.get(sys_id, []):
                lost_fleet_rating = CombatRatingsAI.combine_ratings_list(fleetsLostBySystem[sys_id])
            if not system or partial_vis_turn == -9999:  # under current visibility rules should not be possible to have any losses or other info here, but just in case...
                if verbose:
                    print "Have never had partial vis for system %d ( %s ) -- basing threat assessment on old info and lost ships" % (sys_id, sys_status.get('name', "name unknown"))
                sys_status.setdefault('local_fleet_threats', set())
                sys_status['planetThreat'] = 0
                sys_status['fleetThreat'] = int(max(CombatRatingsAI.combine_ratings(enemy_rating, mob_rating), 0.98 * sys_status.get('fleetThreat', 0), 1.1 * lost_fleet_rating - monster_rating))
                sys_status['monsterThreat'] = int(max(monster_rating, 0.98 * sys_status.get('monsterThreat', 0), 1.1 * lost_fleet_rating - enemy_rating - mob_rating))
                sys_status['enemy_threat'] = int(max(enemy_rating, 0.98 * sys_status.get('enemy_threat', 0), 1.1 * lost_fleet_rating - monster_rating - mob_rating))
                sys_status['mydefenses'] = {'overall': 0, 'attack': 0, 'health': 0}
                sys_status['totalThreat'] = sys_status['fleetThreat']
                sys_status['regional_fleet_threats'] = sys_status['local_fleet_threats'].copy()
                continue

            # have either stale or current info
            pattack = 0
            phealth = 0
            mypattack, myphealth = 0, 0
            for pid in system.planetIDs:
                prating = self.assess_planet_threat(pid, sighting_age=current_turn - partial_vis_turn)
                planet = universe.getPlanet(pid)
                if not planet:
                    continue
                if planet.owner == self.empireID:  # TODO: check for diplomatic status
                    mypattack += prating['attack']
                    myphealth += prating['health']
                else:
                    if [special for special in planet.specials if "_NEST_" in special]:
                        sys_status['nest_threat'] = 100
                    pattack += prating['attack']
                    phealth += prating['health']
            sys_status['planetThreat'] = pattack * phealth
            sys_status['mydefenses'] = {'overall': mypattack * myphealth, 'attack': mypattack, 'health': myphealth}

            if max(sys_status.get('totalThreat', 0), pattack * phealth) >= 0.6 * lost_fleet_rating:  # previous threat assessment could account for losses, ignore the losses now
                lost_fleet_rating = 0

            # TODO use sitrep combat info rather than estimating stealthed enemies by fleets lost to them
            # TODO also only consider past stealthed fleet threat to still be present if the system is still obstructed
            # TODO: track visibility across turns in order to distinguish the blip of visibility in (losing) combat,
            # which FO currently treats as being for the previous turn, partially superseding the previous visibility for that turn
            if not partial_vis_turn == current_turn:  # (universe.getVisibility(sys_id, self.empire_id) >= fo.visibility.partial):
                sys_status.setdefault('local_fleet_threats', set())
                sys_status['currently_visible'] = False
                # print "Stale visibility for system %d ( %s ) -- last seen %d, current Turn %d -- basing threat assessment on old info and lost ships"%(sys_id, sys_status.get('name', "name unknown"), partial_vis_turn, currentTurn)
                sys_status['fleetThreat'] = int(max(CombatRatingsAI.combine_ratings(enemy_rating, mob_rating), 0.98 * sys_status.get('fleetThreat', 0), 2.0 * lost_fleet_rating - max(sys_status.get('monsterThreat', 0), monster_rating)))
                sys_status['enemy_threat'] = int(max(enemy_rating, 0.98 * sys_status.get('enemy_threat', 0), 1.1 * lost_fleet_rating - max(sys_status.get('monsterThreat', 0), monster_rating)))
                sys_status['monsterThreat'] = int(max(monster_rating, 0.98 * sys_status.get('monsterThreat', 0)))
                # sys_status['totalThreat'] = ((pattack + enemy_attack + monster_attack) ** 0.8) * ((phealth + enemy_health + monster_health)** 0.6)  # reevaluate this
                sys_status['totalThreat'] = max(CombatRatingsAI.combine_ratings_list([enemy_rating, mob_rating, monster_rating, pattack * phealth]), 2 * lost_fleet_rating, 0.98 * sys_status.get('totalThreat', 0))
            else:  # system considered visible #TODO: reevaluate as visibility rules change
                sys_status['currently_visible'] = True
                sys_status['local_fleet_threats'] = set(mobile_fleets)
                sys_status['fleetThreat'] = int(max(CombatRatingsAI.combine_ratings(enemy_rating, mob_rating), 2 * lost_fleet_rating - monster_rating))  # includes mobile monsters
                if verbose:
                    print "enemy threat calc parts: enemy rating %.1f, lost fleet rating %.1f, monster_rating %.1f" % (enemy_rating, lost_fleet_rating, monster_rating)
                sys_status['enemy_threat'] = int(max(enemy_rating, 2 * lost_fleet_rating - monster_rating))  # does NOT include mobile monsters
                sys_status['monsterThreat'] = monster_rating
                sys_status['totalThreat'] = CombatRatingsAI.combine_ratings_list([enemy_rating, mob_rating, monster_rating, pattack * phealth])
            sys_status['regional_fleet_threats'] = sys_status['local_fleet_threats'].copy()
            sys_status['fleetThreat'] = max(sys_status['fleetThreat'], sys_status.get('nest_threat', 0))
            sys_status['totalThreat'] = max(sys_status['totalThreat'], sys_status.get('nest_threat', 0))

            if partial_vis_turn > 0 and sys_id not in supply_unobstructed_systems:  # has been seen with Partial Vis, but is currently supply-blocked
                sys_status['fleetThreat'] = max(sys_status['fleetThreat'], min_hidden_attack * min_hidden_health)
                sys_status['totalThreat'] = max(sys_status['totalThreat'], ((pattack + min_hidden_attack) ** 0.8) * ((phealth + min_hidden_health) ** 0.6))
            if verbose and sys_status['fleetThreat'] > 0:
                print "%s intermediate status: %s" % (system, sys_status)

        enemy_supply, enemy_near_supply = self.assess_enemy_supply()  # TODO: assess change in enemy supply over time
        # assess secondary threats (threats of surrounding systems) and update my fleet rating
        for sys_id in system_id_list:
            sys_status = self.systemStatus[sys_id]
            sys_status['enemies_supplied'] = enemy_supply.get(sys_id, [])
            sys_status['enemies_nearly_supplied'] = enemy_near_supply.get(sys_id, [])
            my_ratings_list = []
            my_ratings_against_planets_list = []
            for fid in sys_status['myfleets']:
                this_rating = self.get_rating(fid, True, self.get_standard_enemy())
                my_ratings_list.append(this_rating)
                my_ratings_against_planets_list.append(self.get_rating(fid, against_planets=True))
            if sys_id != INVALID_ID:
                sys_status['myFleetRating'] = CombatRatingsAI.combine_ratings_list(my_ratings_list)
                sys_status['myFleetRatingVsPlanets'] = CombatRatingsAI.combine_ratings_list(my_ratings_against_planets_list)
                sys_status['all_local_defenses'] = CombatRatingsAI.combine_ratings(sys_status['myFleetRating'], sys_status['mydefenses']['overall'])
            sys_status['neighbors'] = set(dict_from_map(universe.getSystemNeighborsMap(sys_id, self.empireID)))
            
        for sys_id in system_id_list:
            sys_status = self.systemStatus[sys_id]
            neighbors = sys_status.get('neighbors', set())
            this_system = fo.getUniverse().getSystem(sys_id)
            if verbose:
                print "Regional Assessment for %s with local fleet threat %.1f" % (this_system, sys_status.get('fleetThreat', 0))
            jumps2 = set()
            jumps3 = set()
            jumps4 = set()
            for seta, setb in [(neighbors, jumps2), (jumps2, jumps3), (jumps3, jumps4)]:
                for sys2id in seta:
                    setb.update(self.systemStatus.get(sys2id, {}).get('neighbors', set()))
            jump2ring = jumps2 - neighbors - {sys_id}
            jump3ring = jumps3 - jumps2 - neighbors - {sys_id}
            jump4ring = jumps4 - jumps3  - jumps2 - neighbors - {sys_id}
            sys_status['2jump_ring'] = jump2ring
            sys_status['3jump_ring'] = jump3ring
            sys_status['4jump_ring'] = jump4ring
            threat, max_threat, myrating, j1_threats = self.area_ratings(neighbors, ref_sys_name="neighbors %s" % this_system) if verbose else self.area_ratings(neighbors)
            sys_status['neighborThreat'] = threat
            sys_status['max_neighbor_threat'] = max_threat
            sys_status['my_neighbor_rating'] = myrating
            threat, max_threat, myrating, j2_threats = self.area_ratings(jump2ring, ref_sys_name="jump2 %s" % this_system) if verbose else self.area_ratings(jump2ring)
            sys_status['jump2_threat'] = threat
            sys_status['my_jump2_rating'] = myrating
            threat, max_threat, myrating, j3_threats = self.area_ratings(jump3ring)
            sys_status['jump3_threat'] = threat
            sys_status['my_jump3_rating'] = myrating
            threat_keys = ['fleetThreat', 'neighborThreat', 'jump2_threat']  # for local system includes both enemies and mobs
            sys_status['regional_threat'] = CombatRatingsAI.combine_ratings_list(map(lambda x: sys_status.get(x, 0), threat_keys))
            # TODO: investigate cases where regional_threat has been nonzero but no regional_threat_fleets
            # (probably due to attenuating history of past threats)
            sys_status.setdefault('regional_fleet_threats', set()).update(j1_threats, j2_threats)
示例#9
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
示例#10
0
    def issue_fleet_orders(self):
        """issues AIFleetOrders which can be issued in system and moves to next one if is possible"""
        # TODO: priority
        order_completed = True

        debug(
            "\nChecking orders for fleet %s (on turn %d), with mission type %s and target %s",
            self.fleet.get_object(), fo.currentTurn(), self.type
            or 'No mission', self.target or 'No Target')
        if MissionType.INVASION == self.type:
            self._check_retarget_invasion()
        just_issued_move_order = False
        last_move_target_id = INVALID_ID
        # Note: the following abort check somewhat assumes only one major mission type
        for fleet_order in self.orders:
            if (isinstance(fleet_order,
                           (OrderColonize, OrderOutpost, OrderInvade))
                    and self._check_abort_mission(fleet_order)):
                return
        aistate = get_aistate()
        for fleet_order in self.orders:
            if just_issued_move_order and self.fleet.get_object(
            ).systemID != last_move_target_id:
                # having just issued a move order, we will normally stop issuing orders this turn, except that if there
                # are consecutive move orders we will consider moving through the first destination rather than stopping
                # Without the below noinspection directive, PyCharm is concerned about the 2nd part of the test
                # noinspection PyTypeChecker
                if (not isinstance(fleet_order, OrderMove)
                        or self.need_to_pause_movement(last_move_target_id,
                                                       fleet_order)):
                    break
            debug("Checking order: %s" % fleet_order)
            self.check_mergers(context=str(fleet_order))
            if fleet_order.can_issue_order(verbose=False):
                # only move if all other orders completed
                if isinstance(fleet_order, OrderMove) and order_completed:
                    debug("Issuing fleet order %s" % fleet_order)
                    fleet_order.issue_order()
                    just_issued_move_order = True
                    last_move_target_id = fleet_order.target.id
                elif not isinstance(fleet_order, OrderMove):
                    debug("Issuing fleet order %s" % fleet_order)
                    fleet_order.issue_order()
                else:
                    debug(
                        "NOT issuing (even though can_issue) fleet order %s" %
                        fleet_order)
                status_words = tuple(
                    ["not", ""][_s]
                    for _s in [fleet_order.order_issued, fleet_order.executed])
                debug("Order %s issued and %s fully executed." % status_words)
                if not fleet_order.executed:
                    order_completed = False
            else:  # check that we're not held up by a Big Monster
                if fleet_order.order_issued:
                    # A previously issued order that wasn't instantly executed must have had cirumstances change so that
                    # the order can't currently be reissued (or perhaps simply a savegame has been reloaded on the same
                    # turn the order was issued).
                    if not fleet_order.executed:
                        order_completed = False
                    # Go on to the next order.
                    continue
                debug("CAN'T issue fleet order %s because:" % fleet_order)
                fleet_order.can_issue_order(verbose=True)
                if isinstance(fleet_order, OrderMove):
                    this_system_id = fleet_order.target.id
                    this_status = aistate.systemStatus.setdefault(
                        this_system_id, {})
                    threat_threshold = fo.currentTurn(
                    ) * MilitaryAI.cur_best_mil_ship_rating() / 4.0
                    if this_status.get('monsterThreat', 0) > threat_threshold:
                        # if this move order is not this mil fleet's final destination, and blocked by Big Monster,
                        # release and hope for more effective reassignment
                        if (self.type not in (MissionType.MILITARY,
                                              MissionType.SECURE)
                                or fleet_order != self.orders[-1]):
                            debug(
                                "Aborting mission due to being blocked by Big Monster at system %d, threat %d"
                                % (this_system_id,
                                   aistate.systemStatus[this_system_id]
                                   ['monsterThreat']))
                            debug("Full set of orders were:")
                            for this_order in self.orders:
                                debug(" - %s" % this_order)
                            self.clear_fleet_orders()
                            self.clear_target()
                            return
                break  # do not order the next order until this one is finished.
        else:  # went through entire order list
            if order_completed:
                debug("Final order is completed")
                orders = self.orders
                last_order = orders[-1] if orders else None
                universe = fo.getUniverse()

                if last_order and isinstance(last_order, OrderColonize):
                    planet = universe.getPlanet(last_order.target.id)
                    sys_partial_vis_turn = get_partial_visibility_turn(
                        planet.systemID)
                    planet_partial_vis_turn = get_partial_visibility_turn(
                        planet.id)
                    if (planet_partial_vis_turn == sys_partial_vis_turn
                            and not planet.initialMeterValue(
                                fo.meterType.population)):
                        warning(
                            "Fleet %s has tentatively completed its "
                            "colonize mission but will wait to confirm population.",
                            self.fleet)
                        debug("    Order details are %s" % last_order)
                        debug(
                            "    Order is valid: %s; issued: %s; executed: %s"
                            % (last_order.is_valid(), last_order.order_issued,
                               last_order.executed))
                        if not last_order.is_valid():
                            source_target = last_order.fleet
                            target_target = last_order.target
                            debug(
                                "        source target validity: %s; target target validity: %s "
                                % (bool(source_target), bool(target_target)))
                        return  # colonize order must not have completed yet
                clear_all = True
                last_sys_target = INVALID_ID
                if last_order and isinstance(last_order, OrderMilitary):
                    last_sys_target = last_order.target.id
                    # not doing this until decide a way to release from a SECURE mission
                    # if (MissionType.SECURE == self.type) or
                    secure_targets = set(AIstate.colonyTargetedSystemIDs +
                                         AIstate.outpostTargetedSystemIDs +
                                         AIstate.invasionTargetedSystemIDs)
                    if last_sys_target in secure_targets:  # consider a secure mission
                        if last_sys_target in AIstate.colonyTargetedSystemIDs:
                            secure_type = "Colony"
                        elif last_sys_target in AIstate.outpostTargetedSystemIDs:
                            secure_type = "Outpost"
                        elif last_sys_target in AIstate.invasionTargetedSystemIDs:
                            secure_type = "Invasion"
                        else:
                            secure_type = "Unidentified"
                        debug(
                            "Fleet %d has completed initial stage of its mission "
                            "to secure system %d (targeted for %s), "
                            "may release a portion of ships" %
                            (self.fleet.id, last_sys_target, secure_type))
                        clear_all = False

                # for PROTECT_REGION missions, only release fleet if no more threat
                if self.type == MissionType.PROTECT_REGION:
                    # use military logic code below to determine if can release
                    # any or even all of the ships.
                    clear_all = False
                    last_sys_target = self.target.id
                    debug(
                        "Check if PROTECT_REGION mission with target %d is finished.",
                        last_sys_target)

                fleet_id = self.fleet.id
                if clear_all:
                    if orders:
                        debug(
                            "Fleet %d has completed its mission; clearing all orders and targets."
                            % self.fleet.id)
                        debug("Full set of orders were:")
                        for this_order in orders:
                            debug("\t\t %s" % this_order)
                        self.clear_fleet_orders()
                        self.clear_target()
                        if aistate.get_fleet_role(fleet_id) in (
                                MissionType.MILITARY, MissionType.SECURE):
                            allocations = MilitaryAI.get_military_fleets(
                                mil_fleets_ids=[fleet_id],
                                try_reset=False,
                                thisround="Fleet %d Reassignment" % fleet_id)
                            if allocations:
                                MilitaryAI.assign_military_fleets_to_systems(
                                    use_fleet_id_list=[fleet_id],
                                    allocations=allocations)
                    else:  # no orders
                        debug("No Current Orders")
                else:
                    potential_threat = CombatRatingsAI.combine_ratings(
                        MilitaryAI.get_system_local_threat(last_sys_target),
                        MilitaryAI.get_system_neighbor_threat(last_sys_target))
                    threat_present = potential_threat > 0
                    debug("Fleet threat present? %s", threat_present)
                    target_system = universe.getSystem(last_sys_target)
                    if not threat_present and target_system:
                        for pid in target_system.planetIDs:
                            planet = universe.getPlanet(pid)
                            if (planet and planet.owner != fo.empireID()
                                    and planet.currentMeterValue(
                                        fo.meterType.maxDefense) > 0):
                                debug("Found local planetary threat: %s",
                                      planet)
                                threat_present = True
                                break
                    if not threat_present:
                        debug(
                            "No current threat in target system; releasing a portion of ships."
                        )
                        # at least first stage of current task is done;
                        # release extra ships for potential other deployments
                        new_fleets = FleetUtilsAI.split_fleet(self.fleet.id)
                        if self.type == MissionType.PROTECT_REGION:
                            self.clear_fleet_orders()
                            self.clear_target()
                            new_fleets.append(self.fleet.id)
                    else:
                        debug(
                            "Threat remains in target system; Considering to release some ships."
                        )
                        new_fleets = []
                        fleet_portion_to_remain = self._portion_of_fleet_needed_here(
                        )
                        if fleet_portion_to_remain >= 1:
                            debug(
                                "Can not release fleet yet due to large threat."
                            )
                        elif fleet_portion_to_remain > 0:
                            debug(
                                "Not all ships are needed here - considering releasing a few"
                            )
                            # TODO: Rate against specific enemy threat cause
                            fleet_remaining_rating = CombatRatingsAI.get_fleet_rating(
                                fleet_id)
                            fleet_min_rating = fleet_portion_to_remain * fleet_remaining_rating
                            debug("Starting rating: %.1f, Target rating: %.1f",
                                  fleet_remaining_rating, fleet_min_rating)
                            allowance = CombatRatingsAI.rating_needed(
                                fleet_remaining_rating, fleet_min_rating)
                            debug(
                                "May release ships with total rating of %.1f",
                                allowance)
                            ship_ids = list(self.fleet.get_object().shipIDs)
                            for ship_id in ship_ids:
                                ship_rating = CombatRatingsAI.get_ship_rating(
                                    ship_id)
                                debug(
                                    "Considering to release ship %d with rating %.1f",
                                    ship_id, ship_rating)
                                if ship_rating > allowance:
                                    debug(
                                        "Remaining rating insufficient. Not released."
                                    )
                                    continue
                                debug("Splitting from fleet.")
                                new_fleet_id = FleetUtilsAI.split_ship_from_fleet(
                                    fleet_id, ship_id)
                                if assertion_fails(
                                        new_fleet_id
                                        and new_fleet_id != INVALID_ID):
                                    break
                                new_fleets.append(new_fleet_id)
                                fleet_remaining_rating = CombatRatingsAI.rating_difference(
                                    fleet_remaining_rating, ship_rating)
                                allowance = CombatRatingsAI.rating_difference(
                                    fleet_remaining_rating, fleet_min_rating)
                                debug(
                                    "Remaining fleet rating: %.1f - Allowance: %.1f",
                                    fleet_remaining_rating, allowance)
                            if new_fleets:
                                aistate.get_fleet_role(fleet_id,
                                                       force_new=True)
                                aistate.update_fleet_rating(fleet_id)
                                aistate.ensure_have_fleet_missions(new_fleets)
                        else:
                            debug(
                                "Planetary defenses are deemed sufficient. Release fleet."
                            )
                            new_fleets = FleetUtilsAI.split_fleet(
                                self.fleet.id)

                    new_military_fleets = []
                    for fleet_id in new_fleets:
                        if aistate.get_fleet_role(
                                fleet_id) in COMBAT_MISSION_TYPES:
                            new_military_fleets.append(fleet_id)
                    allocations = []
                    if new_military_fleets:
                        allocations = MilitaryAI.get_military_fleets(
                            mil_fleets_ids=new_military_fleets,
                            try_reset=False,
                            thisround="Fleet Reassignment %s" %
                            new_military_fleets)
                    if allocations:
                        MilitaryAI.assign_military_fleets_to_systems(
                            use_fleet_id_list=new_military_fleets,
                            allocations=allocations)
示例#11
0
 def _calculate_threat(self):
     potential_threat = max(self._potential_threat() - self._potential_support(), 0)
     actual_threat = self.safety_factor * (
         2*self.threat_bias +
         + CombatRatingsAI.combine_ratings(self._local_threat(), self._neighbor_threat()))
     return potential_threat + actual_threat
示例#12
0
def assess_protection_focus(pinfo):
    """Return True if planet should use Protection Focus."""
    this_planet = pinfo.planet
    aistate = get_aistate()
    sys_status = aistate.systemStatus.get(this_planet.systemID, {})
    threat_from_supply = (
        0.25 * aistate.empire_standard_enemy_rating *
        min(2, len(sys_status.get('enemies_nearly_supplied', []))))
    debug("%s has regional+supply threat of %.1f", this_planet,
          threat_from_supply)
    regional_threat = sys_status.get('regional_threat', 0) + threat_from_supply
    if not regional_threat:  # no need for protection
        if pinfo.current_focus == PROTECTION:
            debug(
                "Advising dropping Protection Focus at %s due to no regional threat",
                this_planet)
        return False
    cur_prod_val = weighted_sum_output(pinfo.current_output)
    target_prod_val = max(
        map(weighted_sum_output,
            [pinfo.possible_output[INDUSTRY], pinfo.possible_output[RESEARCH]
             ]))
    prot_prod_val = weighted_sum_output(pinfo.possible_output[PROTECTION])
    local_production_diff = 0.8 * cur_prod_val + 0.2 * target_prod_val - prot_prod_val
    fleet_threat = sys_status.get('fleetThreat', 0)
    # TODO: relax the below rejection once the overall determination of PFocus is better tuned
    if not fleet_threat and local_production_diff > 8:
        if pinfo.current_focus == PROTECTION:
            debug(
                "Advising dropping Protection Focus at %s due to excessive productivity loss",
                this_planet)
        return False
    local_p_defenses = sys_status.get('mydefenses', {}).get('overall', 0)
    # TODO have adjusted_p_defenses take other in-system planets into account
    adjusted_p_defenses = local_p_defenses * (
        1.0 if pinfo.current_focus != PROTECTION else 0.5)
    local_fleet_rating = sys_status.get('myFleetRating', 0)
    combined_local_defenses = sys_status.get('all_local_defenses', 0)
    my_neighbor_rating = sys_status.get('my_neighbor_rating', 0)
    neighbor_threat = sys_status.get('neighborThreat', 0)
    safety_factor = 1.2 if pinfo.current_focus == PROTECTION else 0.5
    cur_shield = this_planet.initialMeterValue(fo.meterType.shield)
    max_shield = this_planet.initialMeterValue(fo.meterType.maxShield)
    cur_troops = this_planet.initialMeterValue(fo.meterType.troops)
    max_troops = this_planet.initialMeterValue(fo.meterType.maxTroops)
    cur_defense = this_planet.initialMeterValue(fo.meterType.defense)
    max_defense = this_planet.initialMeterValue(fo.meterType.maxDefense)
    def_meter_pairs = [(cur_troops, max_troops), (cur_shield, max_shield),
                       (cur_defense, max_defense)]
    use_protection = True
    reason = ""
    if (fleet_threat and  # i.e., an enemy is sitting on us
        (
            pinfo.current_focus != PROTECTION
            or  # too late to start protection TODO: but maybe regen worth it
            # protection focus only useful here if it maintains an elevated level
            all([
                AIDependencies.PROT_FOCUS_MULTIPLIER * a <= b
                for a, b in def_meter_pairs
            ]))):
        use_protection = False
        reason = "A"
    elif ((pinfo.current_focus != PROTECTION and cur_shield < max_shield - 2
           and not tech_is_complete(AIDependencies.PLANET_BARRIER_I_TECH))
          and (cur_defense < max_defense - 2
               and not tech_is_complete(AIDependencies.DEFENSE_REGEN_1_TECH))
          and (cur_troops < max_troops - 2)):
        use_protection = False
        reason = "B1"
    elif (
        (pinfo.current_focus == PROTECTION
         and cur_shield * AIDependencies.PROT_FOCUS_MULTIPLIER < max_shield - 2
         and not tech_is_complete(AIDependencies.PLANET_BARRIER_I_TECH)) and
        (cur_defense * AIDependencies.PROT_FOCUS_MULTIPLIER < max_defense - 2
         and not tech_is_complete(AIDependencies.DEFENSE_REGEN_1_TECH)) and
        (cur_troops * AIDependencies.PROT_FOCUS_MULTIPLIER < max_troops - 2)):
        use_protection = False
        reason = "B2"
    elif max(max_shield, max_troops, max_defense) < 3:
        # joke defenses, don't bother with protection focus
        use_protection = False
        reason = "C"
    elif regional_threat and local_production_diff <= 2.0:
        use_protection = True
        reason = "D"
    elif safety_factor * regional_threat <= local_fleet_rating:
        use_protection = False
        reason = "E"
    elif (safety_factor * regional_threat <= combined_local_defenses and
          (pinfo.current_focus != PROTECTION or
           (0.5 * safety_factor * regional_threat <= local_fleet_rating
            and fleet_threat == 0 and neighbor_threat < combined_local_defenses
            and local_production_diff > 5))):
        use_protection = False
        reason = "F"
    elif (regional_threat <= CombatRatingsAI.combine_ratings(
            local_fleet_rating, adjusted_p_defenses)
          and safety_factor * regional_threat <=
          CombatRatingsAI.combine_ratings_list(
              [my_neighbor_rating, local_fleet_rating, adjusted_p_defenses])
          and local_production_diff > 5):
        use_protection = False
        reason = "G"
    if use_protection or pinfo.current_focus == PROTECTION:
        debug(
            "Advising %sProtection Focus (reason %s) for planet %s, with local_prod_diff of %.1f, comb. local"
            " defenses %.1f, local fleet rating %.1f and regional threat %.1f, threat sources: %s",
            ["dropping ", ""][use_protection], reason, this_planet,
            local_production_diff, combined_local_defenses, local_fleet_rating,
            regional_threat, sys_status['regional_fleet_threats'])
    return use_protection
示例#13
0
 def _calculate_threat(self):
     potential_threat = max(self._potential_threat() - self._potential_support(), 0)
     actual_threat = self.safety_factor * (
         2*self.threat_bias +
         + CombatRatingsAI.combine_ratings(self._local_threat(), self._neighbor_threat()))
     return potential_threat + actual_threat
示例#14
0
    def issue_fleet_orders(self):
        """issues AIFleetOrders which can be issued in system and moves to next one if is possible"""
        # TODO: priority
        order_completed = True

        debug("\nChecking orders for fleet %s (on turn %d), with mission type %s and target %s",
              self.fleet.get_object(), fo.currentTurn(), self.type or 'No mission', self.target or 'No Target')
        if MissionType.INVASION == self.type:
            self._check_retarget_invasion()
        just_issued_move_order = False
        last_move_target_id = INVALID_ID
        # Note: the following abort check somewhat assumes only one major mission type
        for fleet_order in self.orders:
            if (isinstance(fleet_order, (OrderColonize, OrderOutpost, OrderInvade)) and
                    self._check_abort_mission(fleet_order)):
                return
        aistate = get_aistate()
        for fleet_order in self.orders:
            if just_issued_move_order and self.fleet.get_object().systemID != last_move_target_id:
                # having just issued a move order, we will normally stop issuing orders this turn, except that if there
                # are consecutive move orders we will consider moving through the first destination rather than stopping
                # Without the below noinspection directive, PyCharm is concerned about the 2nd part of the test
                # noinspection PyTypeChecker
                if (not isinstance(fleet_order, OrderMove) or
                        self.need_to_pause_movement(last_move_target_id, fleet_order)):
                    break
            debug("Checking order: %s" % fleet_order)
            self.check_mergers(context=str(fleet_order))
            if fleet_order.can_issue_order(verbose=False):
                # only move if all other orders completed
                if isinstance(fleet_order, OrderMove) and order_completed:
                    debug("Issuing fleet order %s" % fleet_order)
                    fleet_order.issue_order()
                    just_issued_move_order = True
                    last_move_target_id = fleet_order.target.id
                elif not isinstance(fleet_order, OrderMove):
                    debug("Issuing fleet order %s" % fleet_order)
                    fleet_order.issue_order()
                else:
                    debug("NOT issuing (even though can_issue) fleet order %s" % fleet_order)
                status_words = tuple(["not", ""][_s] for _s in [fleet_order.order_issued, fleet_order.executed])
                debug("Order %s issued and %s fully executed." % status_words)
                if not fleet_order.executed:
                    order_completed = False
            else:  # check that we're not held up by a Big Monster
                if fleet_order.order_issued:
                    # A previously issued order that wasn't instantly executed must have had cirumstances change so that
                    # the order can't currently be reissued (or perhaps simply a savegame has been reloaded on the same
                    # turn the order was issued).
                    if not fleet_order.executed:
                        order_completed = False
                    # Go on to the next order.
                    continue
                debug("CAN'T issue fleet order %s because:" % fleet_order)
                fleet_order.can_issue_order(verbose=True)
                if isinstance(fleet_order, OrderMove):
                    this_system_id = fleet_order.target.id
                    this_status = aistate.systemStatus.setdefault(this_system_id, {})
                    threat_threshold = fo.currentTurn() * MilitaryAI.cur_best_mil_ship_rating() / 4.0
                    if this_status.get('monsterThreat', 0) > threat_threshold:
                        # if this move order is not this mil fleet's final destination, and blocked by Big Monster,
                        # release and hope for more effective reassignment
                        if (self.type not in (MissionType.MILITARY, MissionType.SECURE) or
                                fleet_order != self.orders[-1]):
                            debug("Aborting mission due to being blocked by Big Monster at system %d, threat %d" % (
                                this_system_id, aistate.systemStatus[this_system_id]['monsterThreat']))
                            debug("Full set of orders were:")
                            for this_order in self.orders:
                                debug(" - %s" % this_order)
                            self.clear_fleet_orders()
                            self.clear_target()
                            return
                break  # do not order the next order until this one is finished.
        else:  # went through entire order list
            if order_completed:
                debug("Final order is completed")
                orders = self.orders
                last_order = orders[-1] if orders else None
                universe = fo.getUniverse()

                if last_order and isinstance(last_order, OrderColonize):
                    planet = universe.getPlanet(last_order.target.id)
                    sys_partial_vis_turn = get_partial_visibility_turn(planet.systemID)
                    planet_partial_vis_turn = get_partial_visibility_turn(planet.id)
                    if (planet_partial_vis_turn == sys_partial_vis_turn and
                            not planet.initialMeterValue(fo.meterType.population)):
                        warn("Fleet %d has tentatively completed its "
                             "colonize mission but will wait to confirm population." % self.fleet.id)
                        debug("    Order details are %s" % last_order)
                        debug("    Order is valid: %s; issued: %s; executed: %s" % (
                            last_order.is_valid(), last_order.order_issued, last_order.executed))
                        if not last_order.is_valid():
                            source_target = last_order.fleet
                            target_target = last_order.target
                            debug("        source target validity: %s; target target validity: %s " % (
                                bool(source_target), bool(target_target)))
                        return  # colonize order must not have completed yet
                clear_all = True
                last_sys_target = INVALID_ID
                if last_order and isinstance(last_order, OrderMilitary):
                    last_sys_target = last_order.target.id
                    # not doing this until decide a way to release from a SECURE mission
                    # if (MissionType.SECURE == self.type) or
                    secure_targets = set(AIstate.colonyTargetedSystemIDs +
                                         AIstate.outpostTargetedSystemIDs +
                                         AIstate.invasionTargetedSystemIDs)
                    if last_sys_target in secure_targets:  # consider a secure mission
                        if last_sys_target in AIstate.colonyTargetedSystemIDs:
                            secure_type = "Colony"
                        elif last_sys_target in AIstate.outpostTargetedSystemIDs:
                            secure_type = "Outpost"
                        elif last_sys_target in AIstate.invasionTargetedSystemIDs:
                            secure_type = "Invasion"
                        else:
                            secure_type = "Unidentified"
                        debug("Fleet %d has completed initial stage of its mission "
                              "to secure system %d (targeted for %s), "
                              "may release a portion of ships" % (self.fleet.id, last_sys_target, secure_type))
                        clear_all = False

                # for PROTECT_REGION missions, only release fleet if no more threat
                if self.type == MissionType.PROTECT_REGION:
                    # use military logic code below to determine if can release
                    # any or even all of the ships.
                    clear_all = False
                    last_sys_target = self.target.id
                    debug("Check if PROTECT_REGION mission with target %d is finished.", last_sys_target)

                fleet_id = self.fleet.id
                if clear_all:
                    if orders:
                        debug("Fleet %d has completed its mission; clearing all orders and targets." % self.fleet.id)
                        debug("Full set of orders were:")
                        for this_order in orders:
                            debug("\t\t %s" % this_order)
                        self.clear_fleet_orders()
                        self.clear_target()
                        if aistate.get_fleet_role(fleet_id) in (MissionType.MILITARY, MissionType.SECURE):
                            allocations = MilitaryAI.get_military_fleets(mil_fleets_ids=[fleet_id],
                                                                         try_reset=False,
                                                                         thisround="Fleet %d Reassignment" % fleet_id)
                            if allocations:
                                MilitaryAI.assign_military_fleets_to_systems(use_fleet_id_list=[fleet_id],
                                                                             allocations=allocations)
                    else:  # no orders
                        debug("No Current Orders")
                else:
                    potential_threat = CombatRatingsAI.combine_ratings(
                        MilitaryAI.get_system_local_threat(last_sys_target),
                        MilitaryAI.get_system_neighbor_threat(last_sys_target)
                    )
                    threat_present = potential_threat > 0
                    debug("Fleet threat present? %s", threat_present)
                    target_system = universe.getSystem(last_sys_target)
                    if not threat_present and target_system:
                        for pid in target_system.planetIDs:
                            planet = universe.getPlanet(pid)
                            if (planet and
                                    planet.owner != fo.empireID() and
                                    planet.currentMeterValue(fo.meterType.maxDefense) > 0):
                                debug("Found local planetary threat: %s", planet)
                                threat_present = True
                                break
                    if not threat_present:
                        debug("No current threat in target system; releasing a portion of ships.")
                        # at least first stage of current task is done;
                        # release extra ships for potential other deployments
                        new_fleets = FleetUtilsAI.split_fleet(self.fleet.id)
                        if self.type == MissionType.PROTECT_REGION:
                            self.clear_fleet_orders()
                            self.clear_target()
                            new_fleets.append(self.fleet.id)
                    else:
                        debug("Threat remains in target system; Considering to release some ships.")
                        new_fleets = []
                        fleet_portion_to_remain = self._portion_of_fleet_needed_here()
                        if fleet_portion_to_remain > 1:
                            debug("Can not release fleet yet due to large threat.")
                        elif fleet_portion_to_remain > 0:
                            debug("Not all ships are needed here - considering releasing a few")
                            fleet_remaining_rating = CombatRatingsAI.get_fleet_rating(fleet_id)
                            fleet_min_rating = fleet_portion_to_remain * fleet_remaining_rating
                            debug("Starting rating: %.1f, Target rating: %.1f",
                                  fleet_remaining_rating, fleet_min_rating)
                            allowance = CombatRatingsAI.rating_needed(fleet_remaining_rating, fleet_min_rating)
                            debug("May release ships with total rating of %.1f", allowance)
                            ship_ids = list(self.fleet.get_object().shipIDs)
                            for ship_id in ship_ids:
                                ship_rating = CombatRatingsAI.get_ship_rating(ship_id)
                                debug("Considering to release ship %d with rating %.1f", ship_id, ship_rating)
                                if ship_rating > allowance:
                                    debug("Remaining rating insufficient. Not released.")
                                    continue
                                debug("Splitting from fleet.")
                                new_fleet_id = FleetUtilsAI.split_ship_from_fleet(fleet_id, ship_id)
                                if assertion_fails(new_fleet_id and new_fleet_id != INVALID_ID):
                                    break
                                new_fleets.append(new_fleet_id)
                                fleet_remaining_rating = CombatRatingsAI.rating_difference(
                                    fleet_remaining_rating, ship_rating)
                                allowance = CombatRatingsAI.rating_difference(
                                    fleet_remaining_rating, fleet_min_rating)
                                debug("Remaining fleet rating: %.1f - Allowance: %.1f",
                                      fleet_remaining_rating, allowance)
                            if new_fleets:
                                aistate.get_fleet_role(fleet_id, force_new=True)
                                aistate.update_fleet_rating(fleet_id)
                                aistate.ensure_have_fleet_missions(new_fleets)
                        else:
                            debug("Planetary defenses are deemed sufficient. Release fleet.")
                            new_fleets = FleetUtilsAI.split_fleet(self.fleet.id)

                    new_military_fleets = []
                    for fleet_id in new_fleets:
                        if aistate.get_fleet_role(fleet_id) in COMBAT_MISSION_TYPES:
                            new_military_fleets.append(fleet_id)
                    allocations = []
                    if new_military_fleets:
                        allocations = MilitaryAI.get_military_fleets(
                            mil_fleets_ids=new_military_fleets,
                            try_reset=False,
                            thisround="Fleet Reassignment %s" % new_military_fleets
                        )
                    if allocations:
                        MilitaryAI.assign_military_fleets_to_systems(use_fleet_id_list=new_military_fleets,
                                                                     allocations=allocations)
示例#15
0
def assess_protection_focus(pid, info):
    """Return True if planet should use Protection Focus."""
    this_planet = info.planet
    sys_status = foAI.foAIstate.systemStatus.get(this_planet.systemID, {})
    threat_from_supply = (0.25 * foAI.foAIstate.empire_standard_enemy_rating *
                          min(2, len(sys_status.get('enemies_nearly_supplied', []))))
    print "Planet %s has regional+supply threat of %.1f" % ('P_%d<%s>' % (pid, this_planet.name), threat_from_supply)
    regional_threat = sys_status.get('regional_threat', 0) + threat_from_supply
    if not regional_threat:  # no need for protection
        if info.current_focus == PRODUCTION:
            print "Advising dropping Protection Focus at %s due to no regional threat" % this_planet
        return False
    cur_prod_val = weighted_sum_output(info.current_output)
    target_prod_val = max(map(weighted_sum_output, [info.possible_output[INDUSTRY], info.possible_output[RESEARCH]]))
    prot_prod_val = weighted_sum_output(info.possible_output[PRODUCTION])
    local_production_diff = 0.8 * cur_prod_val + 0.2 * target_prod_val - prot_prod_val
    fleet_threat = sys_status.get('fleetThreat', 0)
    # TODO: relax the below rejection once the overall determination of PFocus is better tuned
    if not fleet_threat and local_production_diff > 8:
        if info.current_focus == PRODUCTION:
            print "Advising dropping Protection Focus at %s due to excessive productivity loss" % this_planet
        return False
    local_p_defenses = sys_status.get('mydefenses', {}).get('overall', 0)
    # TODO have adjusted_p_defenses take other in-system planets into account
    adjusted_p_defenses = local_p_defenses * (1.0 if info.current_focus != PRODUCTION else 0.5)
    local_fleet_rating = sys_status.get('myFleetRating', 0)
    combined_local_defenses = sys_status.get('all_local_defenses', 0)
    my_neighbor_rating = sys_status.get('my_neighbor_rating', 0)
    neighbor_threat = sys_status.get('neighborThreat', 0)
    safety_factor = 1.2 if info.current_focus == PRODUCTION else 0.5
    cur_shield = this_planet.currentMeterValue(fo.meterType.shield)
    max_shield = this_planet.currentMeterValue(fo.meterType.maxShield)
    cur_troops = this_planet.currentMeterValue(fo.meterType.troops)
    max_troops = this_planet.currentMeterValue(fo.meterType.maxTroops)
    cur_defense = this_planet.currentMeterValue(fo.meterType.defense)
    max_defense = this_planet.currentMeterValue(fo.meterType.maxDefense)
    def_meter_pairs = [(cur_troops, max_troops), (cur_shield, max_shield), (cur_defense, max_defense)]
    use_protection = True
    reason = ""
    if (fleet_threat and  # i.e., an enemy is sitting on us
            (info.current_focus != PRODUCTION or  # too late to start protection TODO: but maybe regen worth it
             # protection focus only useful here if it maintains an elevated level
             all([AIDependencies.PROT_FOCUS_MULTIPLIER * a <= b for a, b in def_meter_pairs]))):
        use_protection = False
        reason = "A"
    elif ((info.current_focus != PRODUCTION and cur_shield < max_shield - 2 and
           not tech_is_complete(AIDependencies.PLANET_BARRIER_I_TECH)) and
          (cur_defense < max_defense - 2 and not tech_is_complete(AIDependencies.DEFENSE_REGEN_1_TECH)) and
          (cur_troops < max_troops - 2)):
        use_protection = False
        reason = "B1"
    elif ((info.current_focus == PRODUCTION and cur_shield * AIDependencies.PROT_FOCUS_MULTIPLIER < max_shield - 2 and
           not tech_is_complete(AIDependencies.PLANET_BARRIER_I_TECH)) and
          (cur_defense * AIDependencies.PROT_FOCUS_MULTIPLIER < max_defense - 2 and
           not tech_is_complete(AIDependencies.DEFENSE_REGEN_1_TECH)) and
          (cur_troops * AIDependencies.PROT_FOCUS_MULTIPLIER < max_troops - 2)):
        use_protection = False
        reason = "B2"
    elif max(max_shield, max_troops, max_defense) < 3:  # joke defenses, don't bother with protection focus
        use_protection = False
        reason = "C"
    elif regional_threat and local_production_diff <= 2.0:
        reason = "D"
        pass  # i.e., use_protection = True
    elif safety_factor * regional_threat <= local_fleet_rating:
        use_protection = False
        reason = "E"
    elif (safety_factor * regional_threat <= combined_local_defenses and
          (info.current_focus != PRODUCTION or
           (0.5 * safety_factor * regional_threat <= local_fleet_rating and
            fleet_threat == 0 and neighbor_threat < combined_local_defenses and
            local_production_diff > 5))):
        use_protection = False
        reason = "F"
    elif (regional_threat <= CombatRatingsAI.combine_ratings(local_fleet_rating, adjusted_p_defenses) and
          safety_factor * regional_threat <=
          CombatRatingsAI.combine_ratings_list([my_neighbor_rating, local_fleet_rating, adjusted_p_defenses]) and
          local_production_diff > 5):
        use_protection = False
        reason = "G"
    if use_protection or info.current_focus == PRODUCTION:
        print ("Advising %sProtection Focus (reason %s) for planet %s, with local_prod_diff of %.1f, comb. local"
               " defenses %.1f, local fleet rating %.1f and regional threat %.1f, threat sources: %s") % (
                   ["dropping ", ""][use_protection], reason, this_planet, local_production_diff, combined_local_defenses,
                   local_fleet_rating, regional_threat, sys_status['regional_fleet_threats'])
    return use_protection
示例#16
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
示例#17
0
def get_fleets_for_mission(target_stats, min_stats, cur_stats, starting_system,
                           fleet_pool_set, fleet_list, species=""):
    """Get fleets for a mission.

    Implements breadth-first search through systems starting at the **starting_sytem**.
    In each system, local fleets are checked if they are in the allowed **fleet_pool_set** and suitable for the mission.
    If so, they are added to the **fleet_list** and **cur_stats** is updated with the currently selected fleet summary.
    The search continues until the requirements defined in **target_stats** are met or there are no more systems/fleets.
    In that case, if the **min_stats** are covered, the **fleet_list** is returned anyway.
    Otherwise, an empty list is returned by the function, in which case the caller can make an evaluation of
    an emergency use of the found fleets in fleet_list; if not to be used they should be added back to the main pool.

    :param target_stats: stats the fleet should ideally meet
    :type target_stats: dict
    :param min_stats: minimum stats the final fleet must meet to be accepted
    :type min_stats: dict
    :param cur_stats: (**mutated**) stat summary of selected fleets
    :type cur_stats: dict
    :param starting_system: system_id where breadth-first-search is centered
    :type starting_system: int
    :param fleet_pool_set: (**mutated**) fleets allowed to be selected. Split fleed_ids are added, used ones removed.
    :type: fleet_pool_set: set[int]
    :param fleet_list: (**mutated**) fleets that are selected for the mission. Gets filled during the call.
    :type fleet_list: list[int]
    :param species: species for colonization mission
    :type species: str
    :return: List of selected fleet_ids or empty list if couldn't meet minimum requirements.
    :rtype: list[int]
    """
    universe = fo.getUniverse()
    colonization_roles = (ShipRoleType.CIVILIAN_COLONISATION, ShipRoleType.BASE_COLONISATION)
    systems_enqueued = [starting_system]
    systems_visited = []
    # loop over systems in a breadth-first-search trying to find nearby suitable ships in fleet_pool_set
    while systems_enqueued and fleet_pool_set:
        this_system_id = systems_enqueued.pop(0)
        systems_visited.append(this_system_id)
        accessible_fleets = foAI.foAIstate.systemStatus.get(this_system_id, {}).get('myFleetsAccessible', [])
        fleets_here = [fid for fid in accessible_fleets if fid in fleet_pool_set]
        # loop over all fleets in the system, split them if possible and select suitable ships
        while fleets_here:
            fleet_id = fleets_here.pop(0)
            fleet = universe.getFleet(fleet_id)
            if not fleet:  # TODO should be checked before passed to the function
                fleet_pool_set.remove(fleet_id)
                continue
            # try splitting fleet
            if len(list(fleet.shipIDs)) > 1:
                new_fleets = split_fleet(fleet_id)
                fleet_pool_set.update(new_fleets)
                fleets_here.extend(new_fleets)
            # check species for colonization missions
            if species:
                for ship_id in fleet.shipIDs:
                    ship = universe.getShip(ship_id)
                    if ship and foAI.foAIstate.get_ship_role(ship.design.id) in colonization_roles and species == ship.speciesName:
                        break
                else:  # no suitable species found
                    continue
            # check troop capacity for invasion missions
            troop_capacity = 0
            if 'troopCapacity' in target_stats:
                troop_capacity = count_troops_in_fleet(fleet_id)
                if troop_capacity <= 0:
                    continue

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

            # all checks passed, add ship to selected fleets and update the stats
            fleet_list.append(fleet_id)
            fleet_pool_set.remove(fleet_id)
            this_rating = foAI.foAIstate.get_rating(fleet_id)
            cur_stats['rating'] = CombatRatingsAI.combine_ratings(cur_stats.get('rating', 0), this_rating)
            if 'ratingVsPlanets' in target_stats:
                cur_stats['ratingVsPlanets'] = CombatRatingsAI.combine_ratings(cur_stats.get('ratingVsPlanets', 0),
                                                                               this_rating_vs_planets)
            if 'troopCapacity' in target_stats:
                cur_stats['troopCapacity'] = cur_stats.get('troopCapacity', 0) + troop_capacity
            # if we already meet the requirements, we can stop looking for more ships
            if (sum(len(universe.getFleet(fid).shipIDs) for fid in fleet_list) >= 1) \
                    and stats_meet_reqs(cur_stats, target_stats):
                return fleet_list

        # finished system without meeting requirements. Add neighboring systems to search queue.
        for neighbor_id in [el.key() for el in
                            universe.getSystemNeighborsMap(this_system_id, fo.empireID())]:
            if all((
                    neighbor_id not in systems_visited,
                    neighbor_id not in systems_enqueued,
                    neighbor_id in foAI.foAIstate.exploredSystemIDs
            )):
                systems_enqueued.append(neighbor_id)
    # we ran out of systems or fleets to check but did not meet requirements yet.
    if stats_meet_reqs(cur_stats, min_stats) and any(universe.getFleet(fid).shipIDs for fid in fleet_list):
        return fleet_list
    else:
        return []
示例#18
0
    def can_issue_order(self, verbose=False):
        if not super(OrderMove, self).can_issue_order(verbose=verbose):
            return False
        # TODO: figure out better way to have invasions (& possibly colonizations)
        #       require visibility on target without needing visibility of all intermediate systems
        # if False and main_mission_type not in [MissionType.ATTACK,  # TODO: consider this later
        #                                        MissionType.MILITARY,
        #                                        MissionType.SECURE,
        #                                        MissionType.HIT_AND_RUN,
        #                                        MissionType.EXPLORATION]:
        #     if not universe.getVisibility(target_id, get_aistate().empireID) >= fo.visibility.partial:
        #         #if not target_id in interior systems
        #         get_aistate().needsEmergencyExploration.append(fleet.systemID)
        #         return False
        system_id = self.fleet.get_system().id
        if system_id == self.target.get_system().id:
            return True  # TODO: already there, but could consider retreating

        aistate = get_aistate()
        main_fleet_mission = aistate.get_fleet_mission(self.fleet.id)

        # TODO: Rate against specific enemies here
        fleet_rating = CombatRatingsAI.get_fleet_rating(self.fleet.id)
        fleet_rating_vs_planets = CombatRatingsAI.get_fleet_rating_against_planets(
            self.fleet.id)
        target_sys_status = aistate.systemStatus.get(self.target.id, {})
        f_threat = target_sys_status.get('fleetThreat', 0)
        m_threat = target_sys_status.get('monsterThreat', 0)
        p_threat = target_sys_status.get('planetThreat', 0)
        threat = f_threat + m_threat + p_threat
        safety_factor = aistate.character.military_safety_factor()
        universe = fo.getUniverse()
        if main_fleet_mission.type == MissionType.INVASION and not trooper_move_reqs_met(
                main_fleet_mission, self, verbose):
            return False
        if fleet_rating >= safety_factor * threat and fleet_rating_vs_planets >= p_threat:
            return True
        elif not p_threat and self.target.id in fo.getEmpire(
        ).supplyUnobstructedSystems:
            return True
        else:
            sys1 = universe.getSystem(system_id)
            sys1_name = sys1 and sys1.name or "unknown"
            target_system = self.target.get_system()
            target_system_name = (target_system and
                                  target_system.get_object().name) or "unknown"
            # TODO: adjust calc for any departing fleets
            my_other_fleet_rating = aistate.systemStatus.get(
                self.target.id, {}).get('myFleetRating', 0)
            my_other_fleet_rating_vs_planets = aistate.systemStatus.get(
                self.target.id, {}).get('myFleetRatingVsPlanets', 0)
            is_military = aistate.get_fleet_role(
                self.fleet.id) == MissionType.MILITARY

            total_rating = CombatRatingsAI.combine_ratings(
                my_other_fleet_rating, fleet_rating)
            total_rating_vs_planets = CombatRatingsAI.combine_ratings(
                my_other_fleet_rating_vs_planets, fleet_rating_vs_planets)
            if (my_other_fleet_rating > 3 * safety_factor * threat or
                (is_military and total_rating_vs_planets > 2.5 * p_threat
                 and total_rating > safety_factor * threat)):
                debug((
                    "\tAdvancing fleet %d (rating %d) at system %d (%s) into system %d (%s) with threat %d"
                    " because of sufficient empire fleet strength already at destination"
                    % (self.fleet.id, fleet_rating, system_id, sys1_name,
                       self.target.id, target_system_name, threat)))
                return True
            elif (threat == p_threat and not self.fleet.get_object().aggressive
                  and not my_other_fleet_rating
                  and not target_sys_status.get('localEnemyFleetIDs', [-1])):
                if verbose:
                    debug(
                        "\tAdvancing fleet %d (rating %d) at system %d (%s) "
                        "into system %d (%s) with planet threat %d because non aggressive"
                        " and no other fleets present to trigger combat" %
                        (self.fleet.id, fleet_rating, system_id, sys1_name,
                         self.target.id, target_system_name, threat))
                return True
            else:
                if verbose:
                    _info = (self.fleet.id, fleet_rating, system_id, sys1_name,
                             self.target.id, target_system_name, threat)
                    debug(
                        "\tHolding fleet %d (rating %d) at system %d (%s) "
                        "before travelling to system %d (%s) with threat %d" %
                        _info)
                needs_vis = aistate.misc.setdefault('needs_vis', [])
                if self.target.id not in needs_vis:
                    needs_vis.append(self.target.id)
                return False
示例#19
0
 def _minimum_allocation(self, threat):
     nearby_forces = CombatRatingsAI.combine_ratings(
             self.assigned_rating, self._potential_support())
     return max(
             CombatRatingsAI.rating_needed(self._regional_threat(), nearby_forces),
             CombatRatingsAI.rating_needed(1.4*threat, self.assigned_rating))
示例#20
0
    def can_issue_order(self, verbose=False):
        if not super(OrderMove, self).can_issue_order(verbose=verbose):
            return False
        # TODO: figure out better way to have invasions (& possibly colonizations)
        #       require visibility on target without needing visibility of all intermediate systems
        # if False and main_mission_type not in [MissionType.ATTACK,  # TODO: consider this later
        #                                        MissionType.MILITARY,
        #                                        MissionType.SECURE,
        #                                        MissionType.HIT_AND_RUN,
        #                                        MissionType.EXPLORATION]:
        #     if not universe.getVisibility(target_id, foAI.foAIstate.empireID) >= fo.visibility.partial:
        #         #if not target_id in interior systems
        #         foAI.foAIstate.needsEmergencyExploration.append(fleet.systemID)
        #         return False
        system_id = self.fleet.get_system().id
        if system_id == self.target.get_system().id:
            return True  # TODO: already there, but could consider retreating

        main_fleet_mission = foAI.foAIstate.get_fleet_mission(self.fleet.id)

        fleet_rating = CombatRatingsAI.get_fleet_rating(self.fleet.id)
        fleet_rating_vs_planets = CombatRatingsAI.get_fleet_rating_against_planets(self.fleet.id)
        target_sys_status = foAI.foAIstate.systemStatus.get(self.target.id, {})
        f_threat = target_sys_status.get('fleetThreat', 0)
        m_threat = target_sys_status.get('monsterThreat', 0)
        p_threat = target_sys_status.get('planetThreat', 0)
        threat = f_threat + m_threat + p_threat
        safety_factor = foAI.foAIstate.character.military_safety_factor()
        universe = fo.getUniverse()
        if main_fleet_mission.type == MissionType.INVASION and not trooper_move_reqs_met(main_fleet_mission,
                                                                                         self, verbose):
            return False
        if fleet_rating >= safety_factor * threat and fleet_rating_vs_planets >= p_threat:
            return True
        elif not p_threat and self.target.id in fo.getEmpire().supplyUnobstructedSystems:
            return True
        else:
            sys1 = universe.getSystem(system_id)
            sys1_name = sys1 and sys1.name or "unknown"
            target_system = self.target.get_system()
            target_system_name = (target_system and target_system.get_object().name) or "unknown"
            # TODO: adjust calc for any departing fleets
            my_other_fleet_rating = foAI.foAIstate.systemStatus.get(self.target.id, {}).get('myFleetRating', 0)
            my_other_fleet_rating_vs_planets = foAI.foAIstate.systemStatus.get(self.target.id, {}).get(
                'myFleetRatingVsPlanets', 0)
            is_military = foAI.foAIstate.get_fleet_role(self.fleet.id) == MissionType.MILITARY

            total_rating = CombatRatingsAI.combine_ratings(my_other_fleet_rating, fleet_rating)
            total_rating_vs_planets = CombatRatingsAI.combine_ratings(my_other_fleet_rating_vs_planets,
                                                                      fleet_rating_vs_planets)
            if (my_other_fleet_rating > 3 * safety_factor * threat or
                    (is_military and total_rating_vs_planets > 2.5*p_threat and total_rating > safety_factor * threat)):
                debug(("\tAdvancing fleet %d (rating %d) at system %d (%s) into system %d (%s) with threat %d"
                       " because of sufficient empire fleet strength already at destination") %
                      (self.fleet.id, fleet_rating, system_id, sys1_name, self.target.id, target_system_name, threat))
                return True
            elif (threat == p_threat and
                  not self.fleet.get_object().aggressive and
                  not my_other_fleet_rating and
                  not target_sys_status.get('localEnemyFleetIDs', [-1])):
                if verbose:
                    print ("\tAdvancing fleet %d (rating %d) at system %d (%s) "
                           "into system %d (%s) with planet threat %d because non aggressive"
                           " and no other fleets present to trigger combat") % (
                        self.fleet.id, fleet_rating, system_id, sys1_name, self.target.id, target_system_name, threat)
                return True
            else:
                if verbose:
                    print ("\tHolding fleet %d (rating %d) at system %d (%s) "
                           "before travelling to system %d (%s) with threat %d") % (
                        self.fleet.id, fleet_rating, system_id, sys1_name, self.target.id, target_system_name, threat)
                needs_vis = foAI.foAIstate.misc.setdefault('needs_vis', [])
                if self.target.id not in needs_vis:
                    needs_vis.append(self.target.id)
                return False
示例#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))

    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 > 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, 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
示例#22
0
def get_fleets_for_mission(target_stats, min_stats, cur_stats, starting_system,
                           fleet_pool_set, fleet_list, species=""):
    """Get fleets for a mission.

    Implements breadth-first search through systems starting at the **starting_sytem**.
    In each system, local fleets are checked if they are in the allowed **fleet_pool_set** and suitable for the mission.
    If so, they are added to the **fleet_list** and **cur_stats** is updated with the currently selected fleet summary.
    The search continues until the requirements defined in **target_stats** are met or there are no more systems/fleets.
    In that case, if the **min_stats** are covered, the **fleet_list** is returned anyway.
    Otherwise, an empty list is returned by the function, in which case the caller can make an evaluation of
    an emergency use of the found fleets in fleet_list; if not to be used they should be added back to the main pool.

    :param target_stats: stats the fleet should ideally meet
    :type target_stats: dict
    :param min_stats: minimum stats the final fleet must meet to be accepted
    :type min_stats: dict
    :param cur_stats: (**mutated**) stat summary of selected fleets
    :type cur_stats: dict
    :param starting_system: system_id where breadth-first-search is centered
    :type starting_system: int
    :param fleet_pool_set: (**mutated**) fleets allowed to be selected. Split fleed_ids are added, used ones removed.
    :type: fleet_pool_set: set[int]
    :param fleet_list: (**mutated**) fleets that are selected for the mission. Gets filled during the call.
    :type fleet_list: list[int]
    :param species: species for colonization mission
    :type species: str
    :return: List of selected fleet_ids or empty list if couldn't meet minimum requirements.
    :rtype: list[int]
    """
    universe = fo.getUniverse()
    colonization_roles = (ShipRoleType.CIVILIAN_COLONISATION, ShipRoleType.BASE_COLONISATION)
    systems_enqueued = [starting_system]
    systems_visited = []
    # loop over systems in a breadth-first-search trying to find nearby suitable ships in fleet_pool_set
    while systems_enqueued and fleet_pool_set:
        this_system_id = systems_enqueued.pop(0)
        systems_visited.append(this_system_id)
        accessible_fleets = foAI.foAIstate.systemStatus.get(this_system_id, {}).get('myFleetsAccessible', [])
        fleets_here = [fid for fid in accessible_fleets if fid in fleet_pool_set]
        # loop over all fleets in the system, split them if possible and select suitable ships
        while fleets_here:
            fleet_id = fleets_here.pop(0)
            fleet = universe.getFleet(fleet_id)
            if not fleet:  # TODO should be checked before passed to the function
                fleet_pool_set.remove(fleet_id)
                continue
            # try splitting fleet
            if len(list(fleet.shipIDs)) > 1:
                new_fleets = split_fleet(fleet_id)
                fleet_pool_set.update(new_fleets)
                fleets_here.extend(new_fleets)
            # check species for colonization missions
            if species:
                for ship_id in fleet.shipIDs:
                    ship = universe.getShip(ship_id)
                    if (ship and foAI.foAIstate.get_ship_role(ship.design.id) in colonization_roles and
                            species == ship.speciesName):
                        break
                else:  # no suitable species found
                    continue
            # check troop capacity for invasion missions
            troop_capacity = 0
            if 'troopCapacity' in target_stats:
                troop_capacity = count_troops_in_fleet(fleet_id)
                if troop_capacity <= 0:
                    continue

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

            # all checks passed, add ship to selected fleets and update the stats
            try:
                fleet_pool_set.remove(fleet_id)
            except KeyError:
                error("After having split a fleet, the original fleet apparently no longer exists.", exc_info=True)
                continue
            fleet_list.append(fleet_id)

            this_rating = foAI.foAIstate.get_rating(fleet_id)
            cur_stats['rating'] = CombatRatingsAI.combine_ratings(cur_stats.get('rating', 0), this_rating)
            if 'ratingVsPlanets' in target_stats:
                cur_stats['ratingVsPlanets'] = CombatRatingsAI.combine_ratings(cur_stats.get('ratingVsPlanets', 0),
                                                                               this_rating_vs_planets)
            if 'troopCapacity' in target_stats:
                cur_stats['troopCapacity'] = cur_stats.get('troopCapacity', 0) + troop_capacity
            # if we already meet the requirements, we can stop looking for more ships
            if (sum(len(universe.getFleet(fid).shipIDs) for fid in fleet_list) >= 1) \
                    and stats_meet_reqs(cur_stats, target_stats):
                return fleet_list

        # finished system without meeting requirements. Add neighboring systems to search queue.
        for neighbor_id in universe.getImmediateNeighbors(this_system_id, fo.empireID()):
            if all((
                    neighbor_id not in systems_visited,
                    neighbor_id not in systems_enqueued,
                    neighbor_id in foAI.foAIstate.exploredSystemIDs
            )):
                systems_enqueued.append(neighbor_id)
    # we ran out of systems or fleets to check but did not meet requirements yet.
    if stats_meet_reqs(cur_stats, min_stats) and any(universe.getFleet(fid).shipIDs for fid in fleet_list):
        return fleet_list
    else:
        return []
示例#23
0
 def _minimum_allocation(self, threat):
     nearby_forces = CombatRatingsAI.combine_ratings(
             self.assigned_rating, self._potential_support())
     return max(
             CombatRatingsAI.rating_needed(self._regional_threat(), nearby_forces),
             CombatRatingsAI.rating_needed(1.4*threat, self.assigned_rating))