Exemple #1
0
    def __update_empire_standard_enemy(self):
        """Update the empire's standard enemy.

        The standard enemy is the enemy that is most often seen.
        """
        # TODO: If no current information available, rate against own fighters
        universe = fo.getUniverse()
        empire_id = fo.empireID()

        # assess enemy fleets that may have been momentarily visible (start with dummy entries)
        dummy_stats = CombatRatingsAI.default_ship_stats().get_stats(hashable=True)
        cur_e_fighters = Counter()  # actual visible enemies
        old_e_fighters = Counter({dummy_stats: 0})  # destroyed enemies TODO: consider seen but out of sight enemies

        for fleet_id in universe.fleetIDs:
            fleet = universe.getFleet(fleet_id)
            if (not fleet or fleet.empty or fleet.ownedBy(empire_id) or fleet.unowned or
                    not (fleet.hasArmedShips or fleet.hasFighterShips)):
                continue

            # track old/dead enemy fighters for rating assessments in case not enough current info
            ship_stats = CombatRatingsAI.FleetCombatStats(fleet_id).get_ship_stats(hashable=True)
            dead_fleet = fleet_id in universe.destroyedObjectIDs(empire_id)
            e_f_dict = old_e_fighters if dead_fleet else cur_e_fighters
            for stats in ship_stats:
                # log only ships that are armed
                if stats[0]:
                    e_f_dict[stats] += 1

        e_f_dict = cur_e_fighters or old_e_fighters
        self.__empire_standard_enemy = sorted([(v, k) for k, v in e_f_dict.items()])[-1][1]
        self.empire_standard_enemy_rating = self.get_standard_enemy().get_rating()
Exemple #2
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

        # for use in debugging
        verbose = False

        # assess enemy fleets that may have been momentarily visible
        # start with dummy entries
        cur_e_fighters = {
            CombatRatingsAI.default_ship_stats().get_stats(hashable=True): [0]
        }
        old_e_fighters = {
            CombatRatingsAI.default_ship_stats().get_stats(hashable=True): [0]
        }
        enemies_by_system = {}
        my_fleets_by_system = {}
        fleet_spot_position = {}
        current_turn = fo.currentTurn()
        for fleet_id in universe.fleetIDs:
            fleet = universe.getFleet(fleet_id)
            if not fleet or fleet.empty:
                continue
            # TODO: check if currently in system and blockaded before accepting destination as location
            this_system_id = fleet.nextSystemID if fleet.nextSystemID != INVALID_ID else fleet.systemID
            dead_fleet = fleet_id in destroyed_object_ids

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

            # this is a fleet not owned by us
            if not fleet.unowned and (fleet.hasArmedShips
                                      or fleet.hasFighterShips):
                ship_stats = CombatRatingsAI.FleetCombatStats(
                    fleet_id).get_ship_stats(hashable=True)
                # track old/dead enemy fighters for rating assessments in case not enough current info
                e_f_dict = old_e_fighters if dead_fleet else cur_e_fighters
                for stats in ship_stats:
                    # log only ships that are armed
                    if stats[0]:
                        e_f_dict.setdefault(stats, [0])[0] += 1

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

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

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

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

        # TODO: If no current information available, rate against own fighters
        e_f_dict = cur_e_fighters if len(
            cur_e_fighters) > 1 else old_e_fighters
        self.__empire_standard_enemy = sorted([
            (v, k) for k, v in e_f_dict.items()
        ])[-1][1]
        self.empire_standard_enemy_rating = self.get_standard_enemy(
        ).get_rating()

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

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

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

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

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

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

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

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

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

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

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

        for sys_id in universe.systemIDs:
            sys_status = self.systemStatus[sys_id]
            neighbors = sys_status.get('neighbors', set())
            this_system = universe.getSystem(sys_id)
            if verbose:
                print "Regional Assessment for %s with local fleet threat %.1f" % (
                    this_system, sys_status.get('fleetThreat', 0))
            jumps2 = set()
            jumps3 = set()
            jumps4 = set()
            for seta, setb in [(neighbors, jumps2), (jumps2, jumps3),
                               (jumps3, jumps4)]:
                for sys2id in seta:
                    setb.update(
                        self.systemStatus.get(sys2id,
                                              {}).get('neighbors', set()))
            jump2ring = jumps2 - neighbors - {sys_id}
            jump3ring = jumps3 - jumps2 - neighbors - {sys_id}
            jump4ring = jumps4 - jumps3 - jumps2 - neighbors - {sys_id}
            sys_status['2jump_ring'] = jump2ring
            sys_status['3jump_ring'] = jump3ring
            sys_status['4jump_ring'] = jump4ring
            threat, max_threat, myrating, j1_threats = self.area_ratings(
                neighbors)
            sys_status['neighborThreat'] = threat
            sys_status['max_neighbor_threat'] = max_threat
            sys_status['my_neighbor_rating'] = myrating
            threat, max_threat, myrating, j2_threats = self.area_ratings(
                jump2ring)
            sys_status['jump2_threat'] = threat
            sys_status['my_jump2_rating'] = myrating
            threat, max_threat, myrating, j3_threats = self.area_ratings(
                jump3ring)
            sys_status['jump3_threat'] = threat
            sys_status['my_jump3_rating'] = myrating
            # for local system includes both enemies and mobs
            threat_keys = ['fleetThreat', 'neighborThreat', 'jump2_threat']
            sys_status[
                'regional_threat'] = CombatRatingsAI.combine_ratings_list(
                    [sys_status.get(x, 0) for x in threat_keys])
            # TODO: investigate cases where regional_threat has been nonzero but no regional_threat_fleets
            # (probably due to attenuating history of past threats)
            sys_status.setdefault('regional_fleet_threats',
                                  set()).update(j1_threats, j2_threats)