def get_resupply_fleet_order( fleet_target: TargetFleet) -> "fleet_orders.OrderResupply": """ Return fleet_orders.OrderResupply to nearest supplied system. """ # find nearest supplied system supplied_system_target = get_nearest_supplied_system( get_fleet_position(fleet_target.id)) # create resupply AIFleetOrder return fleet_orders.OrderResupply(fleet_target, supplied_system_target)
def generate_fleet_orders(self): """generates AIFleetOrders from fleets targets to accomplish""" universe = fo.getUniverse() fleet_id = self.fleet.id fleet = universe.getFleet(fleet_id) if (not fleet) or fleet.empty or (fleet_id in universe.destroyedObjectIDs(fo.empireID())): # fleet was probably merged into another or was destroyed get_aistate().delete_fleet_info(fleet_id) return # TODO: priority self.clear_fleet_orders() system_id = fleet.systemID # if fleet doesn't have any mission, # then repair if needed or resupply if is current location not in supplyable system empire = fo.getEmpire() fleet_supplyable_system_ids = empire.fleetSupplyableSystemIDs # if (not self.hasAnyAIMissionTypes()): if not self.target and (system_id not in set(AIstate.colonyTargetedSystemIDs + AIstate.outpostTargetedSystemIDs + AIstate.invasionTargetedSystemIDs)): if self._need_repair(): repair_fleet_order = MoveUtilsAI.get_repair_fleet_order(self.fleet) if repair_fleet_order and repair_fleet_order.is_valid(): self.orders.append(repair_fleet_order) cur_fighter_capacity, max_fighter_capacity = FleetUtilsAI.get_fighter_capacity_of_fleet(fleet_id) if (fleet.fuel < fleet.maxFuel or cur_fighter_capacity < max_fighter_capacity and get_fleet_position(self.fleet.id) not in fleet_supplyable_system_ids): resupply_fleet_order = MoveUtilsAI.get_resupply_fleet_order(self.fleet) if resupply_fleet_order.is_valid(): self.orders.append(resupply_fleet_order) return # no targets if self.target: # for some targets fleet has to visit systems and therefore fleet visit them system_to_visit = (self.target.get_system() if not self.type == MissionType.PROTECT_REGION else TargetSystem(self._get_target_for_protection_mission())) if not system_to_visit: return orders_to_visit_systems = MoveUtilsAI.create_move_orders_to_system(self.fleet, system_to_visit) # TODO: if fleet doesn't have enough fuel to get to final target, consider resetting Mission for fleet_order in orders_to_visit_systems: self.orders.append(fleet_order) # also generate appropriate final orders fleet_order = self._get_fleet_order_from_target(self.type, self.target if not self.type == MissionType.PROTECT_REGION else system_to_visit) self.orders.append(fleet_order)
def get_repair_fleet_order( fleet: TargetFleet) -> Optional["fleet_orders.OrderRepair"]: """ Return fleet_orders.OrderRepair for fleet to proceed to system with drydock. """ # TODO Cover new mechanics where happiness increases repair rate - don't always use nearest system! # find nearest drydock system drydock_sys_id = get_best_drydock_system_id(get_fleet_position(fleet.id), fleet.id) if drydock_sys_id is None: return None debug("Ordering fleet %s to %s for repair" % (fleet, fo.getUniverse().getSystem(drydock_sys_id))) return fleet_orders.OrderRepair(fleet, TargetSystem(drydock_sys_id))
def get_location_target(self) -> TargetSystem: # TODO add parameter turn return TargetSystem(get_fleet_position(self.fleet.id))
def assign_scouts_to_explore_systems(): # TODO: use Graph Theory to explore closest systems universe = fo.getUniverse() capital_sys_id = PlanetUtilsAI.get_capital_sys_id() # order fleets to explore if not border_unexplored_system_ids or (capital_sys_id == INVALID_ID): return exp_systems_by_dist = sorted( (universe.linearDistance(capital_sys_id, x), x) for x in border_unexplored_system_ids) debug( "Exploration system considering following system-distance pairs:\n %s" % ("\n ".join("%3d: %5.1f" % (sys_id, dist) for (dist, sys_id) in exp_systems_by_dist))) explore_list = [sys_id for dist, sys_id in exp_systems_by_dist] already_covered, available_scouts = get_current_exploration_info() debug("Explorable system IDs: %s" % explore_list) debug("Already targeted: %s" % already_covered) aistate = get_aistate() needs_vis = aistate.misc.setdefault("needs_vis", []) check_list = aistate.needsEmergencyExploration + needs_vis + explore_list if INVALID_ID in check_list: # shouldn't normally happen, unless due to bug elsewhere for sys_list, name in [ (aistate.needsEmergencyExploration, "aistate.needsEmergencyExploration"), (needs_vis, "needs_vis"), (explore_list, "explore_list"), ]: if INVALID_ID in sys_list: error("INVALID_ID found in " + name, exc_info=True) # emergency coverage can be due to invasion detection trouble, etc. debug("Check list: %s" % check_list) needs_coverage = [ sys_id for sys_id in check_list if sys_id not in already_covered and sys_id != INVALID_ID ] debug("Needs coverage: %s" % needs_coverage) debug("Available scouts & AIstate locs: %s" % [(x, aistate.fleetStatus.get(x, {}).get("sysID", INVALID_ID)) for x in available_scouts]) debug("Available scouts & universe locs: %s" % [(x, universe.getFleet(x).systemID) for x in available_scouts]) if not needs_coverage or not available_scouts: return # clean up targets which can not or don't need to be scouted for sys_id in list(needs_coverage): if sys_id not in explore_list: # doesn't necessarily need direct visit if universe.getVisibility(sys_id, fo.empireID()) >= fo.visibility.partial: # already got visibility; remove from visit lists and skip if sys_id in needs_vis: del needs_vis[needs_vis.index(sys_id)] if sys_id in aistate.needsEmergencyExploration: del aistate.needsEmergencyExploration[ aistate.needsEmergencyExploration.index(sys_id)] debug( "system id %d already currently visible; skipping exploration" % sys_id) needs_coverage.remove(sys_id) continue # skip systems threatened by monsters sys_status = aistate.systemStatus.setdefault(sys_id, {}) if not aistate.character.may_explore_system( sys_status.setdefault("monsterThreat", 0)) or ( fo.currentTurn() < 20 and aistate.systemStatus[sys_id]["monsterThreat"] > 0): debug( "Skipping exploration of system %d due to Big Monster, threat %d" % (sys_id, aistate.systemStatus[sys_id]["monsterThreat"])) needs_coverage.remove(sys_id) continue # find the jump distance for all possible scout-system pairings options = [] available_scouts = set(available_scouts) for fleet_id in available_scouts: start = TargetSystem(get_fleet_position(fleet_id)) for sys_id in needs_coverage: target = TargetSystem(sys_id) path = MoveUtilsAI.can_travel_to_system(fleet_id, start, target, ensure_return=True) if not path: continue num_jumps = len( path) - 1 # -1 as path contains the original system options.append((num_jumps, fleet_id, sys_id)) # Apply a simple, greedy heuristic to match scouts to nearby systems: # Always choose the shortest possible path from the remaining scout-system pairing. # This is clearly not optimal in the general case but it works well enough for now. # TODO: Consider using a more sophisticated assignment algorithm options.sort() while options: debug("Remaining options: %s" % options) _, fleet_id, sys_id = options[0] fleet_mission = aistate.get_fleet_mission(fleet_id) target = TargetSystem(sys_id) info("Sending fleet %d to explore %s" % (fleet_id, target)) fleet_mission.set_target(MissionType.EXPLORATION, target) options = [ option for option in options if option[1] != fleet_id and option[2] != sys_id ] available_scouts.remove(fleet_id) needs_coverage.remove(sys_id) debug("Exploration assignment finished.") debug("Unassigned scouts: %s" % available_scouts) debug("Unassigned exploration targets: %s" % needs_coverage)