예제 #1
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)
예제 #2
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)