예제 #1
0
def get_safe_path_leg_to_dest(fleet_id, start_id, dest_id):
    start_targ = universe_object.System(start_id)
    dest_targ = universe_object.System(dest_id)
    # TODO actually get a safe path
    this_path = can_travel_to_system(fleet_id, start_targ, dest_targ, ensure_return=False)
    path_ids = [targ.id for targ in this_path if targ.id != start_id] + [start_id]
    start_info = PlanetUtilsAI.sys_name_ids([start_id])
    dest_info = PlanetUtilsAI.sys_name_ids([dest_id])
    path_info = [PlanetUtilsAI.sys_name_ids([sys_id]) for sys_id in path_ids]
    print "Fleet %d requested safe path leg from %s to %s, found path %s" % (fleet_id, ppstring(start_info), ppstring(dest_info), ppstring(path_info))
    return path_ids[0]
예제 #2
0
def get_safe_path_leg_to_dest(fleet_id, start_id, dest_id):
    start_targ = universe_object.System(start_id)
    dest_targ = universe_object.System(dest_id)
    # TODO actually get a safe path
    this_path = can_travel_to_system(fleet_id,
                                     start_targ,
                                     dest_targ,
                                     ensure_return=False)
    path_ids = [targ.id
                for targ in this_path if targ.id != start_id] + [start_id]
    universe = fo.getUniverse()
    print "Fleet %d requested safe path leg from %s to %s, found path %s" % (
        fleet_id, universe.getSystem(start_id), universe.getSystem(dest_id),
        PlanetUtilsAI.sys_name_ids(path_ids))
    return path_ids[0]
예제 #3
0
def can_travel_to_system(fleet_id, from_system_target, to_system_target, ensure_return=False):
    """
    Return list systems to be visited.

    :param fleet_id:
    :param fleet_id: int
    :param from_system_target:
    :type from_system_target: universe_object.System
    :param to_system_target:
    :type to_system_target:  universe_object.System
    :param ensure_return:
    :type ensure_return: bool
    :return:
    :rtype: list
    """
    empire = fo.getEmpire()
    empire_id = empire.empireID
    fleet_supplyable_system_ids = set(empire.fleetSupplyableSystemIDs)
    # get current fuel and max fuel
    universe = fo.getUniverse()
    fleet = universe.getFleet(fleet_id)
    fuel = int(fleet.fuel)
    if fuel < 1.0 or from_system_target.id == to_system_target.id:
        return []
    if foAI.foAIstate.aggression <= fo.aggression.typical or True:  # TODO: sort out if shortestPath leaves off some intermediate destinations
        path_func = universe.leastJumpsPath
    else:
        path_func = universe.shortestPath
    start_sys_id = from_system_target.id
    target_sys_id = to_system_target.id
    if start_sys_id != -1 and target_sys_id != -1:
        short_path = list(path_func(start_sys_id, target_sys_id, empire_id))
    else:
        short_path = []
    legs = zip(short_path[:-1], short_path[1:])
    # suppliedStops = [ sid for sid in short_path if sid in fleet_supplyable_system_ids ]
    # unsupplied_stops = [sid for sid in short_path if sid not in suppliedStops ]
    unsupplied_stops = [sys_b for sys_a, sys_b in legs if ((sys_a not in fleet_supplyable_system_ids) and (sys_b not in fleet_supplyable_system_ids))]
    # print "getting path from %s to %s "%(ppstring(PlanetUtilsAI.sys_name_ids([ start_sys_id ])), ppstring(PlanetUtilsAI.sys_name_ids([ target_sys_id ])) ),
    # print " ::: found initial path %s having suppliedStops %s and unsupplied_stops %s ; tot fuel available is %.1f"%( ppstring(PlanetUtilsAI.sys_name_ids( short_path[:])), suppliedStops, unsupplied_stops, fuel)
    if False:
        if target_sys_id in fleet_supplyable_system_ids:
            print "target has FleetSupply"
        elif target_sys_id in ColonisationAI.annexable_ring1:
            print "target in Ring 1"
        elif target_sys_id in ColonisationAI.annexable_ring2:
            print "target in Ring 2, has enough aggression is ", foAI.foAIstate.aggression >= fo.aggression.typical
        elif target_sys_id in ColonisationAI.annexable_ring3:
            print "target in Ring 2, has enough aggression is ", foAI.foAIstate.aggression >= fo.aggression.aggressive
    if (not unsupplied_stops or not ensure_return or
                target_sys_id in fleet_supplyable_system_ids and len(unsupplied_stops) <= fuel or
                target_sys_id in ColonisationAI.annexable_ring1 and len(unsupplied_stops) < fuel or
                foAI.foAIstate.aggression >= fo.aggression.typical and target_sys_id in ColonisationAI.annexable_ring2 and len(unsupplied_stops) < fuel - 1 or
                foAI.foAIstate.aggression >= fo.aggression.aggressive and target_sys_id in ColonisationAI.annexable_ring3 and len(unsupplied_stops) < fuel - 2):
        return [universe_object.System(sid) for sid in short_path]
    else:
        # print " getting path from 'can_travel_to_system_and_return_to_resupply' ",
        return can_travel_to_system_and_return_to_resupply(fleet_id, from_system_target, to_system_target)
예제 #4
0
def get_nearest_supplied_system(start_system_id):
    """ Return systemAITarget of nearest supplied system from starting system startSystemID."""
    empire = fo.getEmpire()
    fleet_supplyable_system_ids = empire.fleetSupplyableSystemIDs
    universe = fo.getUniverse()

    if start_system_id in fleet_supplyable_system_ids:
        return universe_object.System(start_system_id)
    else:
        min_jumps = 9999  # infinity
        supply_system_id = -1
        for system_id in fleet_supplyable_system_ids:
            if start_system_id != -1 and system_id != -1:
                least_jumps_len = universe.jumpDistance(start_system_id, system_id)
                if least_jumps_len < min_jumps:
                    min_jumps = least_jumps_len
                    supply_system_id = system_id
        return universe_object.System(supply_system_id)
예제 #5
0
def can_travel_to_system(fleet_id, start, target, ensure_return=False):
    """
    Return list systems to be visited.

    :param fleet_id:
    :type fleet_id: int
    :param start:
    :type start: universe_object.System
    :param target:
    :type target:  universe_object.System
    :param ensure_return:
    :type ensure_return: bool
    :return:
    :rtype: list
    """
    if start == target:
        return [universe_object.System(start.id)]

    debug("Requesting path for fleet %s from %s to %s" %
          (fo.getUniverse().getFleet(fleet_id), start, target))
    target_distance_from_supply = -min(state.get_system_supply(target.id), 0)

    # low-aggression AIs may not travel far from supply
    if not foAI.foAIstate.character.may_travel_beyond_supply(
            target_distance_from_supply):
        debug("May not move %d out of supply" % target_distance_from_supply)
        return []

    min_fuel_at_target = target_distance_from_supply if ensure_return else 0
    path_info = pathfinding.find_path_with_resupply(
        start.id,
        target.id,
        fleet_id,
        minimum_fuel_at_target=min_fuel_at_target)
    if path_info is None:
        debug("Found no valid path.")
        return []

    debug("Found valid path: %s" % str(path_info))
    return [universe_object.System(sys_id) for sys_id in path_info.path]
예제 #6
0
def send_invasion_fleets(fleet_ids, evaluated_planets, mission_type):
    """sends a list of invasion fleets to a list of planet_value_pairs"""
    if not fleet_ids:
        return

    universe = fo.getUniverse()
    invasion_fleet_pool = set(fleet_ids)

    for planet_id, pscore, ptroops in evaluated_planets:
        if pscore < MIN_INVASION_SCORE:
            continue
        planet = universe.getPlanet(planet_id)
        if not planet:
            continue
        sys_id = planet.systemID
        found_fleets = []
        found_stats = {}
        min_stats = {'rating': 0, 'troopCapacity': ptroops}
        target_stats = {
            'rating': 10,
            'troopCapacity': ptroops + _TROOPS_SAFETY_MARGIN,
            'target_system': universe_object.System(sys_id),
            'move_path_func': can_travel_to_system
        }
        these_fleets = FleetUtilsAI.get_fleets_for_mission(
            target_stats,
            min_stats,
            found_stats,
            starting_system=sys_id,
            fleet_pool_set=invasion_fleet_pool,
            fleet_list=found_fleets)
        if not these_fleets:
            if not FleetUtilsAI.stats_meet_reqs(found_stats, min_stats):
                print "Insufficient invasion troop allocation for system %d ( %s ) -- requested %s , found %s" % (
                    sys_id, universe.getSystem(sys_id).name, min_stats,
                    found_stats)
                invasion_fleet_pool.update(found_fleets)
                continue
            else:
                these_fleets = found_fleets
        target = universe_object.Planet(planet_id)
        print "assigning invasion fleets %s to target %s" % (these_fleets,
                                                             target)
        for fleetID in these_fleets:
            fleet_mission = foAI.foAIstate.get_fleet_mission(fleetID)
            fleet_mission.clear_fleet_orders()
            fleet_mission.clear_target()
            fleet_mission.set_target(mission_type, target)
예제 #7
0
def get_repair_fleet_order(fleet_target, current_sys_id):
    """
    Return fleet_orders.OrderRepair for fleet to proceed system with drydock.

    :param fleet_target: fleet that need to be repaired
    :type fleet_target: universe_object.Fleet
    # TODO check if we can remove this id, because fleet already have it.
    :param current_sys_id: current system id
    :type current_sys_id: int
    :return: order to repair
    :rtype fleet_orders.OrderRepair
    """
    # TODO Cover new mechanics where happiness increases repair rate - don't always use nearest system!
    # find nearest supplied system
    drydock_sys_id = get_best_drydock_system_id(current_sys_id, fleet_target.id)
    if drydock_sys_id is None:
        return None
    print "ordering fleet %d to %s for repair" % (fleet_target.id, ppstring(PlanetUtilsAI.sys_name_ids([drydock_sys_id])))
    # create resupply AIFleetOrder
    return fleet_orders.OrderRepair(fleet_target, universe_object.System(drydock_sys_id))
예제 #8
0
def get_repair_fleet_order(fleet, current_system_id):
    """Return fleet_orders.OrderRepair for fleet to proceed to system with drydock.

    :param fleet: fleet that need to be repaired
    :type fleet: universe_object.Fleet
    # TODO check if we can remove this id, because fleet already have it.
    :param current_system_id: current location of the fleet, next system if currently on starlane.
    :type current_system_id: int
    :return: order to repair
    :rtype fleet_orders.OrderRepair
    """
    # 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(current_system_id, fleet.id)
    if drydock_sys_id is None:
        return None

    print "Ordering fleet %s to %s for repair" % (
        fleet, fo.getUniverse().getSystem(drydock_sys_id))
    return fleet_orders.OrderRepair(fleet,
                                    universe_object.System(drydock_sys_id))
예제 #9
0
def assign_military_fleets_to_systems(use_fleet_id_list=None, allocations=None, round=1):
    # assign military fleets to military theater systems
    global _military_allocations
    universe = fo.getUniverse()
    if allocations is None:
        allocations = []

    doing_main = (use_fleet_id_list is None)
    if doing_main:
        foAI.foAIstate.misc['ReassignedFleetMissions'] = []
        base_defense_ids = FleetUtilsAI.get_empire_fleet_ids_by_role(MissionType.ORBITAL_DEFENSE)
        unassigned_base_defense_ids = FleetUtilsAI.extract_fleet_ids_without_mission_types(base_defense_ids)
        for fleet_id in unassigned_base_defense_ids:
            fleet = universe.getFleet(fleet_id)
            if not fleet:
                continue
            sys_id = fleet.systemID
            target = universe_object.System(sys_id)
            fleet_mission = foAI.foAIstate.get_fleet_mission(fleet_id)
            fleet_mission.clear_fleet_orders()
            fleet_mission.clear_target()
            mission_type = MissionType.ORBITAL_DEFENSE
            fleet_mission.set_target(mission_type, target)

        all_military_fleet_ids = FleetUtilsAI.get_empire_fleet_ids_by_role(MissionType.MILITARY)
        if not all_military_fleet_ids:
            _military_allocations = []
            return
        avail_mil_fleet_ids = list(FleetUtilsAI.extract_fleet_ids_without_mission_types(all_military_fleet_ids))
        mil_needing_repair_ids, avail_mil_fleet_ids = avail_mil_needing_repair(avail_mil_fleet_ids)
        these_allocations = _military_allocations
        print "=================================================="
        print "Assigning military fleets"
        print "---------------------------------"
    else:
        avail_mil_fleet_ids = list(use_fleet_id_list)
        mil_needing_repair_ids, avail_mil_fleet_ids = avail_mil_needing_repair(avail_mil_fleet_ids)
        these_allocations = allocations

    # send_for_repair(mil_needing_repair_ids) #currently, let get taken care of by AIFleetMission.generate_fleet_orders()

    # get systems to defend

    avail_mil_fleet_ids = set(avail_mil_fleet_ids)
    for sys_id, alloc, minalloc, rvp, takeAny in these_allocations:
        if not doing_main and not avail_mil_fleet_ids:
            break
        found_fleets = []
        found_stats = {}
        these_fleets = FleetUtilsAI.get_fleets_for_mission({'rating': alloc, 'ratingVsPlanets': rvp},
                                                           {'rating': minalloc, 'ratingVsPlanets': rvp}, found_stats,
                                                           starting_system=sys_id, fleet_pool_set=avail_mil_fleet_ids,
                                                           fleet_list=found_fleets)
        if not these_fleets:
            if not found_fleets or not (FleetUtilsAI.stats_meet_reqs(found_stats, {'rating': minalloc}) or takeAny):
                if doing_main:
                    if _verbose_mil_reporting:
                        print "NO available/suitable military allocation for system %d ( %s ) -- requested allocation %8d, found available rating %8d in fleets %s" % (sys_id, universe.getSystem(sys_id).name, minalloc, found_stats.get('rating', 0), found_fleets)
                avail_mil_fleet_ids.update(found_fleets)
                continue
            else:
                these_fleets = found_fleets
        elif doing_main and _verbose_mil_reporting:
            print "FULL+ military allocation for system %d ( %s ) -- requested allocation %8d, got %8d with fleets %s" % (sys_id, universe.getSystem(sys_id).name, alloc, found_stats.get('rating', 0), these_fleets)
        target = universe_object.System(sys_id)
        for fleet_id in these_fleets:
            fo.issueAggressionOrder(fleet_id, True)
            fleet_mission = foAI.foAIstate.get_fleet_mission(fleet_id)
            fleet_mission.clear_fleet_orders()
            fleet_mission.clear_target()
            if sys_id in set(AIstate.colonyTargetedSystemIDs + AIstate.outpostTargetedSystemIDs + AIstate.invasionTargetedSystemIDs):
                mission_type = MissionType.SECURE
            else:
                mission_type = MissionType.MILITARY
            fleet_mission.set_target(mission_type, target)
            fleet_mission.generate_fleet_orders()
            if not doing_main:
                foAI.foAIstate.misc.setdefault('ReassignedFleetMissions', []).append(fleet_mission)

    if doing_main:
        print "---------------------------------"
    last_round = 3
    last_round_name = "LastRound"
    if round <= last_round:
        # check if any fleets remain unassigned
        all_military_fleet_ids = FleetUtilsAI.get_empire_fleet_ids_by_role(MissionType.MILITARY)
        avail_mil_fleet_ids = list(FleetUtilsAI.extract_fleet_ids_without_mission_types(all_military_fleet_ids))
        allocations = []
        round += 1
        thisround = "Extras Remaining Round %d" % round if round < last_round else last_round_name
        if avail_mil_fleet_ids:
            print "Still have available military fleets: %s" % avail_mil_fleet_ids
            allocations = get_military_fleets(mil_fleets_ids=avail_mil_fleet_ids, try_reset=False, thisround=thisround)
        if allocations:
            assign_military_fleets_to_systems(use_fleet_id_list=avail_mil_fleet_ids, allocations=allocations, round=round)
예제 #10
0
def assign_scouts_to_explore_systems():
    # TODO: use Graph Theory to explore closest systems
    universe = fo.getUniverse()
    capital_sys_id = PlanetUtilsAI.get_capital_sys_id()
    # order fleets to explore
    if not border_unexplored_system_ids or (capital_sys_id == INVALID_ID):
        return
    exp_systems_by_dist = sorted((universe.linearDistance(capital_sys_id, x), x) for x in border_unexplored_system_ids)
    print "Exploration system considering following system-distance pairs:\n  %s" % ("\n  ".join("%3d: %5.1f" % info for info in exp_systems_by_dist))
    explore_list = [sys_id for dist, sys_id in exp_systems_by_dist]

    already_covered, available_scouts = get_current_exploration_info()

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

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

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

    :param from_system_target:
    :type from_system_target: universe_object.System
    :param to_system_target:
    :type to_system_target: universe_object.System
    :param result_system_targets:
    :type result_system_targets: list
    :param fleet_supplyable_system_ids:
    :type fleet_supplyable_system_ids: list
    :param max_fuel:
    :type max_fuel: int
    :param fuel:
    :type fuel: int
    :param supply_system_target:
    :type supply_system_target: universe_object.System
    :return:
    :rtype list:
    """
    empire_id = fo.empireID()
    result = True
    # try to find if there is possible path to wanted system from system
    new_targets = result_system_targets[:]
    if from_system_target and to_system_target and supply_system_target:
        universe = fo.getUniverse()
        if from_system_target.id != -1 and to_system_target.id != -1:
            least_jumps_path = universe.leastJumpsPath(from_system_target.id, to_system_target.id, empire_id)
        else:
            least_jumps_path = []
            result = False
        from_system_id = from_system_target.id
        for system_id in least_jumps_path:
            if from_system_id != system_id:
                if from_system_id in fleet_supplyable_system_ids:
                    # from supplied system fleet can travel without fuel consumption and also in this system refuels
                    fuel = max_fuel
                else:
                    fuel -= 1

                # leastJumpPath can differ from shortestPath
                # TODO: use Graph Theory to optimize
                if True or (system_id != to_system_target.id and system_id in fleet_supplyable_system_ids):  # TODO: restructure
                    new_targets.append(universe_object.System(system_id))
                if fuel < 0:
                    result = False
            from_system_id = system_id
    else:
        result = False

    # if there is path to wanted system, then also if there is path back to supplyable system
    if result:
        # jump from A to B means least_jumps_path=[A,B], but min_jumps=1
        min_jumps = len(universe.leastJumpsPath(to_system_target.id, supply_system_target.id, empire_id)) - 1

        if min_jumps > fuel:
            # print "fleetID:" + str(fleetID) + " fuel:" + str(fuel) + " required: " + str(min_jumps)
            result = False
        # else:
        #     resultSystemAITargets.append(toSystemAITarget)

    if not result:
        return []
    return new_targets
예제 #12
0
def _info_string(path_info):
    sequence_string = ", ".join(
        [str(universe_object.System(sys_id)) for sys_id in path_info.path])
    return "dist %.1f, path %s" % (path_info.distance, sequence_string)
예제 #13
0
def assign_scouts_to_explore_systems():
    # TODO: use Graph Theory to explore closest systems
    universe = fo.getUniverse()
    capital_sys_id = PlanetUtilsAI.get_capital_sys_id()
    # order fleets to explore
    if not border_unexplored_system_ids or (capital_sys_id == INVALID_ID):
        return
    exp_systems_by_dist = sorted(
        (universe.linearDistance(capital_sys_id, x), x)
        for x in border_unexplored_system_ids)
    print "Exploration system considering following system-distance pairs:\n  %s" % (
        "\n  ".join("%3d: %5.1f" % (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()

    print "Explorable system IDs: %s" % explore_list
    print "Already targeted: %s" % already_covered
    needs_vis = foAI.foAIstate.misc.setdefault('needs_vis', [])
    check_list = foAI.foAIstate.needsEmergencyExploration + needs_vis + explore_list
    if INVALID_ID in check_list:  # shouldn't normally happen, unless due to bug elsewhere
        for sys_list, name in [(foAI.foAIstate.needsEmergencyExploration,
                                "foAI.foAIstate.needsEmergencyExploration"),
                               (needs_vis, "needs_vis"),
                               (explore_list, "explore_list")]:
            if INVALID_ID in sys_list:
                error("INVALID_ID found in " + name, exc_info=True)
    # emergency coverage can be due to invasion detection trouble, etc.
    print "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
    ]
    print "Needs coverage: %s" % needs_coverage

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

    # 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 foAI.foAIstate.needsEmergencyExploration:
                    del foAI.foAIstate.needsEmergencyExploration[
                        foAI.foAIstate.needsEmergencyExploration.index(sys_id)]
                print "system id %d already currently visible; skipping exploration" % sys_id
                needs_coverage.remove(sys_id)
                continue

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

    # find the jump distance for all possible scout-system pairings
    options = []
    available_scouts = set(available_scouts)
    for fleet_id in available_scouts:
        fleet_mission = foAI.foAIstate.get_fleet_mission(fleet_id)
        start = fleet_mission.get_location_target()
        for sys_id in needs_coverage:
            target = universe_object.System(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 = foAI.foAIstate.get_fleet_mission(fleet_id)
        target = universe_object.System(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)
예제 #14
0
def assign_scouts_to_explore_systems():
    # TODO: use Graph Theory to explore closest systems
    universe = fo.getUniverse()
    capital_sys_id = PlanetUtilsAI.get_capital_sys_id()
    # order fleets to explore
    #explorable_system_ids = foAI.foAIstate.get_explorable_systems(AIExplorableSystemType.EXPLORABLE_SYSTEM_UNEXPLORED)
    explorable_system_ids = list(borderUnexploredSystemIDs)
    if not explorable_system_ids or (capital_sys_id == -1):
        return
    exp_systems_by_dist = sorted(
        map(lambda x: (universe.linearDistance(capital_sys_id, x), x),
            explorable_system_ids))
    print "Exploration system considering following system-distance pairs:\n %s" % (
        "[ " + ", ".join(
            ["%3d : %5.1f" % (sys, dist)
             for dist, sys in exp_systems_by_dist]) + " ]")
    explore_list = [sys_id for dist, sys_id in exp_systems_by_dist]

    already_covered, available_scouts = get_current_exploration_info()

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

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

    available_scouts = set(available_scouts)
    sent_list = []
    while (len(available_scouts) > 0) and (len(needs_coverage) > 0):
        this_sys_id = needs_coverage.pop(0)
        sys_status = foAI.foAIstate.systemStatus.setdefault(this_sys_id, {})
        if this_sys_id not in explore_list:  # doesn't necessarily need direct visit
            if universe.getVisibility(this_sys_id,
                                      fo.empireID()) >= fo.visibility.partial:
                # already got visibility; remove from visit lists and skip
                if this_sys_id in needs_vis:
                    del needs_vis[needs_vis.index(this_sys_id)]
                if this_sys_id in foAI.foAIstate.needsEmergencyExploration:
                    del foAI.foAIstate.needsEmergencyExploration[
                        foAI.foAIstate.needsEmergencyExploration.index(
                            this_sys_id)]
                print "sys id %d already currently visible; skipping exploration" % this_sys_id
                continue
        # TODO: if blocked byu monster, try to find nearby sys from which to see this sys
        if (sys_status.setdefault('monsterThreat',
                                  0) > 2000 * foAI.foAIstate.aggression
            ) or (fo.currentTurn() < 20
                  and foAI.foAIstate.systemStatus[this_sys_id]['monsterThreat']
                  > 200):
            print "Skipping exploration of system %d due to Big Monster, threat %d" % (
                this_sys_id,
                foAI.foAIstate.systemStatus[this_sys_id]['monsterThreat'])
            continue
        found_fleets = []
        this_fleet_list = FleetUtilsAI.get_fleets_for_mission(
            nships=1,
            target_stats={},
            min_stats={},
            cur_stats={},
            species="",
            systems_to_check=[this_sys_id],
            systems_checked=[],
            fleet_pool_set=available_scouts,
            fleet_list=found_fleets,
            verbose=False)
        if not this_fleet_list:
            print "seem to have run out of scouts while trying to cover sys_id %d" % this_sys_id
            break  # must have ran out of scouts
        fleet_id = this_fleet_list[0]
        fleet_mission = foAI.foAIstate.get_fleet_mission(fleet_id)
        target = universe_object.System(this_sys_id)
        if len(
                MoveUtilsAI.can_travel_to_system_and_return_to_resupply(
                    fleet_id, fleet_mission.get_location_target(),
                    target)) > 0:
            fleet_mission.add_target(
                AIFleetMissionType.FLEET_MISSION_EXPLORATION, target)
            sent_list.append(this_sys_id)
        else:  # system too far out, skip it, but can add scout back to available pool
            print "sys_id %d too far out for fleet ( ID %d ) to reach" % (
                this_sys_id, fleet_id)
            available_scouts.update(this_fleet_list)
    print "sent scouting fleets to sysIDs : %s" % sent_list
    return
    # pylint: disable=pointless-string-statement
    """
예제 #15
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)
        this_system_obj = universe_object.System(this_system_id)
        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
                # providing move_path_func in this fashion rather than directly importing here, in order
                # to avoid a circular import problem
                if 'target_system' in target_stats and 'move_path_func' in target_stats:
                    if not target_stats['move_path_func'](fleet_id, this_system_obj, target_stats['target_system']):
                        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 []
예제 #16
0
def get_best_drydock_system_id(start_system_id, fleet_id):
    """

    Get system_id of best drydock capable of repair, where best is nearest drydock
    that has a current and target happiness greater than the HAPPINESS_THRESHOLD
    with a path that is not blockaded or that the fleet can fight through to with
    acceptable losses.

    :param start_system_id: current location of fleet - used to find closest target
    :type start_system_id: int
    :param fleet_id: fleet that needs path to drydock
    :type: int
    :return: most suitable system id where the fleet should be repaired.
    :rtype: int
    """
    if start_system_id == INVALID_ID:
        warn("get_best_drydock_system_id passed bad system id.")
        return None

    if fleet_id == INVALID_ID:
        warn("get_best_drydock_system_id passed bad fleet id.")
        return None

    universe = fo.getUniverse()
    start_system = universe_object.System(start_system_id)
    drydock_system_ids = set()
    for sys_id, pids in state.get_empire_drydocks().iteritems():
        if sys_id == INVALID_ID:
            warn("get_best_drydock_system_id passed bad drydock sys_id.")
            continue
        for pid in pids:
            planet = universe.getPlanet(pid)
            if (planet and planet.currentMeterValue(
                    fo.meterType.happiness) >= DRYDOCK_HAPPINESS_THRESHOLD
                    and planet.currentMeterValue(fo.meterType.targetHappiness)
                    >= DRYDOCK_HAPPINESS_THRESHOLD):
                drydock_system_ids.add(sys_id)
                break

    sys_distances = sorted([(universe.jumpDistance(start_system_id,
                                                   sys_id), sys_id)
                            for sys_id in drydock_system_ids])

    fleet_rating = foAI.foAIstate.get_rating(fleet_id)
    for _, dock_sys_id in sys_distances:
        dock_system = universe_object.System(dock_sys_id)
        path = can_travel_to_system(fleet_id, start_system, dock_system)

        path_rating = sum([
            foAI.foAIstate.systemStatus[path_sys.id]['totalThreat']
            for path_sys in path
        ])

        SAFETY_MARGIN = 10
        if SAFETY_MARGIN * path_rating <= fleet_rating:
            print(
                "Drydock recommendation %s from %s for fleet %s with fleet rating %.1f and path rating %.1f."
                % (dock_system, start_system, universe.getFleet(fleet_id),
                   fleet_rating, path_rating))
            return dock_system.id

    print(
        "No safe drydock recommendation from %s for fleet %s with fleet rating %.1f."
        % (start_system, universe.getFleet(fleet_id), fleet_rating))
    return None