Ejemplo n.º 1
0
def log_planets():
    universe = fo.get_universe()

    planets_table = Table([
        Text('id'),
        Text('name'),
        Text('system'),
        Text('type'),
        Sequence('specials'),
        Text('species'),
        Sequence('buildings')
    ],
                          table_name='Planets summary')
    # group planets by system
    for sid in fo.get_systems():
        for pid in fo.sys_get_planets(sid):
            planet = universe.getPlanet(pid)

            planet_type = fo.planet_get_type(pid).name
            planet_size = fo.planet_get_size(pid).name
            if planet_type != planet_size:
                planet_type = '%s %s' % (planet_type, planet_size)

            buildings = [
                universe.getBuilding(x).name for x in planet.buildingIDs
            ]
            planets_table.add_row([
                pid, planet.name, planet.systemID, planet_type,
                list(planet.specials), planet.speciesName, buildings
            ])

    # Printing too much info at once will lead to truncation of text
    for line in planets_table.get_table().split('\n'):
        print line
Ejemplo n.º 2
0
def handle_interfaces_mismatch():
    """
    Handle cases when AI and universe generation interfaces mismatch.
    """

    try:
        import freeorion as fo

        universe = fo.get_universe()
        empire_of_first_ai = fo.get_empire(2)
        galaxy_data = fo.get_galaxy_setup_data()
        return fo, universe, empire_of_first_ai, galaxy_data
    except ImportError:
        pass

    try:
        import freeOrionAIInterface as fo

        universe = fo.getUniverse()
        empire_of_first_ai = fo.getEmpire(2)
        galaxy_data = fo.getGalaxySetupData()
        return fo, universe, empire_of_first_ai, galaxy_data

    except ImportError:
        pass
Ejemplo n.º 3
0
def log_planets():
    universe = fo.get_universe()

    planets_table = Table(
        [Text('id'), Text('name'), Text('system'), Text('type'),
         Sequence('specials'), Text('species'), Sequence('buildings')],
        table_name='Planets summary')
    # group planets by system
    for sid in fo.get_systems():
        for pid in fo.sys_get_planets(sid):
            planet = universe.getPlanet(pid)

            planet_type = fo.planet_get_type(pid).name
            planet_size = fo.planet_get_size(pid).name
            if planet_type != planet_size:
                planet_type = '%s %s' % (planet_type, planet_size)

            buildings = [universe.getBuilding(x).name for x in planet.buildingIDs]
            planets_table.add_row([
                pid, planet.name, planet.systemID, planet_type, list(planet.specials), planet.speciesName, buildings
            ])

    # Printing too much info at once will lead to truncation of text
    for line in planets_table.get_table().split('\n'):
        print line
Ejemplo n.º 4
0
def inspect_universe_generation_interface(*args, **kwargs):
    import freeorion as fo
    tech = fo.getTech('LRN_NASCENT_AI')
    universe = fo.get_universe()
    empire = fo.get_empire(1)
    rules = fo.getGameRules()
    ship_hull = fo.getShipHull('SH_XENTRONIUM')
    species = fo.getSpecies('SP_ABADDONI')
    generate_stub(
        fo,
        instances=[
            fo.getFieldType('FLD_ION_STORM'),
            fo.getBuildingType('BLD_SHIPYARD_BASE'),
            ship_hull,
            ship_hull.slots,
            fo.getShipPart('SR_WEAPON_1_1'),
            fo.getSpecial('MODERATE_TECH_NATIVES_SPECIAL'),
            species,
            fo.diplomaticMessage(1, 2,
                                 fo.diplomaticMessageType.acceptPeaceProposal),
            rules,
            tech,
            tech.unlockedItems,
            universe,
            universe.effectAccounting,
            universe.buildingIDs,
            fo.get_galaxy_setup_data(),
            empire,
            empire.colour,
            empire.productionQueue,
            empire.researchQueue,
        ],
        classes_to_ignore=(
            'IntBoolMap',
            'IntDblMap',
            'IntFltMap',
            'IntIntMap',
            'IntPairVec',
            'IntSetSet',
            'MeterTypeAccountingInfoVecMap',
            'MeterTypeMeterMap',
            'MeterTypeStringPair',
            'MonsterFleetPlan',
            'PairIntInt_IntMap',
            'RuleValueStringStringPair',
            'ShipPartMeterMap',
            'VisibilityIntMap',
            'AccountingInfoVec',
            'IntSet',
            'StringSet',
            'StringVec',
        ),
        path=".",
        dump=False,
    )
Ejemplo n.º 5
0
def log_systems():
    universe = fo.get_universe()

    systems_table = Table(
        [Text('id'), Text('name'), Sequence('planets'), Sequence('connections'), Text('star')],
        table_name='System summary')
    for sid in fo.get_systems():
        system = universe.getSystem(sid)
        systems_table.add_row([
            sid, system.name, fo.sys_get_planets(sid), fo.sys_get_starlanes(sid), system.starType.name
        ])

    # Printing too much info at once will lead to truncation of text
    for line in systems_table.get_table().split('\n'):
        print(line)
Ejemplo n.º 6
0
def log_systems():
    universe = fo.get_universe()

    systems_table = Table(
        [Text('id'), Text('name'), Sequence('planets'), Sequence('connections'), Text('star')],
        table_name='System summary')
    for sid in fo.get_systems():
        system = universe.getSystem(sid)
        systems_table.add_row([
            sid, system.name, fo.sys_get_planets(sid), fo.sys_get_starlanes(sid), system.starType.name
        ])

    # Printing too much info at once will lead to truncation of text
    for line in systems_table.get_table().split('\n'):
        print line
def inspect_universe_generation_interface(*args, **kwargs):
    import freeorion as fo
    universe = fo.get_universe()

    # this field should be visible to AI
    empire_of_first_ai = fo.get_empire(2)  # first AI
    fo.create_field_in_system(
        "FLD_NEBULA_1", 100,
        universe.getPlanet(empire_of_first_ai.capitalID).systemID)
    instances = list(get_item_with_location(get_common_instances()))

    generate_stub(
        fo,
        instances=instances,
        classes_to_ignore=classes_to_exclude_from_universe,
        path=".",
        dump=False,
    )
Ejemplo n.º 8
0
def log_systems():
    universe = fo.get_universe()

    systems_table = Table(
        Text("id"),
        Text("name"),
        Sequence("planets"),
        Sequence("connections"),
        Text("star"),
        table_name="System summary",
    )
    for sid in fo.get_systems():
        system = universe.getSystem(sid)
        systems_table.add_row(
            sid,
            system.name,
            fo.sys_get_planets(sid),
            fo.sys_get_starlanes(sid),
            system.starType.name,
        )
    systems_table.print_table(print)
def inspect_universe_generation_interface(*args, **kwargs):
    import freeorion as fo
    tech = fo.getTech('LRN_ARTIF_MINDS')
    universe = fo.get_universe()
    empire = fo.get_empire(1)
    rules = fo.getGameRules()
    hull_type = fo.getHullType('SH_XENTRONIUM')
    species = fo.getSpecies('SP_ABADDONI')
    inspect(
        fo,
        instances=[
            fo.getFieldType('FLD_ION_STORM'),
            fo.getBuildingType('BLD_SHIPYARD_BASE'),
            hull_type,
            hull_type.slots,
            fo.getPartType('SR_WEAPON_1_1'),
            fo.getSpecial('MODERATE_TECH_NATIVES_SPECIAL'),
            species,
            fo.diplomaticMessage(1, 2, fo.diplomaticMessageType.acceptPeaceProposal),
            rules,
            tech,
            tech.unlockedItems,
            rules.getRulesAsStrings,
            universe,
            universe.effectAccounting,
            universe.buildingIDs,
            fo.get_galaxy_setup_data(),
            empire,
            empire.colour,
            empire.productionQueue,
            empire.researchQueue,
        ],
        classes_to_ignore=(
            'IntBoolMap', 'IntDblMap', 'IntFltMap', 'IntIntMap', 'IntPairVec', 'IntSetSet',
            'MeterTypeAccountingInfoVecMap', 'MeterTypeMeterMap', 'MeterTypeStringPair', 'MonsterFleetPlan',
            'PairIntInt_IntMap', 'RuleValueStringStringPair', 'ShipPartMeterMap', 'VisibilityIntMap',
            'AccountingInfoVec', 'IntSet', 'StringSet', 'StringVec',
        ),
        path=".",
    )
Ejemplo n.º 10
0
def log_planets():
    universe = fo.get_universe()
    planets_table = Table(
        Text("id"),
        Text("name"),
        Text("system"),
        Text("type"),
        Sequence("specials"),
        Text("species"),
        Sequence("buildings"),
        table_name="Planets summary",
    )
    # group planets by system
    for sid in fo.get_systems():
        for pid in fo.sys_get_planets(sid):
            planet = universe.getPlanet(pid)

            planet_type = fo.planet_get_type(pid).name
            planet_size = fo.planet_get_size(pid).name
            if planet_type != planet_size:
                planet_type = "%s %s" % (planet_type, planet_size)

            buildings = [
                universe.getBuilding(x).name for x in planet.buildingIDs
            ]
            planets_table.add_row(
                pid,
                planet.name,
                planet.systemID,
                planet_type,
                list(planet.specials),
                planet.speciesName,
                buildings,
            )

    planets_table.print_table(print)
Ejemplo n.º 11
0
def inspect_universe_generation():
    """
    Inspect function for universe generation interface.

    We have no way to run it without modifying of game code.

    To generate interface
    put call before return section of universe_generation.universe_generator.create_universe function
    """
    import freeorion as fo
    techs = fo.techs()
    universe = fo.get_universe()
    planet_ids = universe.planetIDs

    system_ids = universe.systemIDs
    system = universe.getSystem(system_ids[0])

    building_ids = universe.buildingIDs
    building = universe.getBuilding(building_ids[0])
    empire_ids = fo.get_all_empires()
    empire = fo.get_empire(empire_ids[0])
    fields_ids = universe.fieldIDs
    field = universe.getField(fields_ids[0])

    fleets_ids = universe.fleetIDs
    fleet = universe.getFleet(fleets_ids[0])
    tech = fo.getTech(techs[0])
    unlocked_items = tech.unlockedItems

    ship = universe.getShip(list(fleet.shipIDs)[0])
    # meter = ship.getMeter(fo.meterType.maxFuel)

    design = ship.design

    universe_object = universe.getObject(universe.systemIDs[0])

    # fo.getHullType ?

    species = None
    special = None
    planets = [universe.getPlanet(pid) for pid in planet_ids]
    for planet in planets:
        species_name = planet.speciesName
        if species_name:
            species = fo.getSpecies(species_name)
        specials = list(planet.specials)
        if specials:
            special = fo.getSpecial(specials[0])

    inspect(
        fo,
        instances=[
            fo.get_galaxy_setup_data(),
            universe,
            planet_ids,
            universe.getPlanet(planet_ids[0]),
            system,
            techs,
            tech,
            tech.unlockedTechs,
            building,
            fo.getBuildingType(building.buildingTypeName),
            empire,
            field,
            fo.getFieldType(field.fieldTypeName),
            fleet,
            ship,
            # meter,
            design,
            fleet.shipIDs,  # int set wtf?
            universe_object,
            system.starlanesWormholes,
            empire.systemSupplyRanges,
            empire.supplyProjections(),
            empire.obstructedStarlanes(),
            empire.planetsWithWastedPP,
            unlocked_items,
            species,
            special
        ],
        classes_to_ignore=(
            'FleetPlan', 'GGColor', 'MonsterFleetPlan', 'PlayerSetupData', 'ShipPartMeterMap', 'ShipSlotVec',
            'VisibilityIntMap', 'diplomaticMessage', 'diplomaticStatusUpdate', 'meter',
        ),
        path=''
    )
    # fo.sys_get_star_type(system),
    # fo.planet_get_size(pid),
    # fo.planet_get_type(planet),
    # fo.species_get_planet_environment(species, planet_type),
    # fo.species_preferred_focus(species),

    # fo.getBuildingType(string)
    # fo.getFieldType(string)
    # fo.getHullType(string)
    # fo.getPartType(string)
    # fo.getShipDesign(number)
    # fo.getSpecial(string)
    # fo.getSpecies(string)
    # fo.getTech(string)
    # fo.getTechCategories(obj)
    # fo.get_empire(number)
    # fo.planet_get_species(number)
    # fo.techsInCategory(string)
    exit(1)
Ejemplo n.º 12
0
def inspect_universe_generation():
    """
    Inspect function for universe generation interface.

    We have no way to run it without modifying of game code.

    To generate interface
    put call before return section of universe_generation.universe_generator.create_universe function
    """
    import freeorion as fo
    techs = fo.techs()
    universe = fo.get_universe()
    planet_ids = universe.planetIDs

    system_ids = universe.systemIDs
    system = universe.getSystem(system_ids[0])

    building_ids = universe.buildingIDs
    building = universe.getBuilding(building_ids[0])
    empire_ids = fo.get_all_empires()
    empire = fo.get_empire(empire_ids[0])
    fields_ids = universe.fieldIDs
    field = universe.getField(fields_ids[0])

    fleets_ids = universe.fleetIDs
    fleet = universe.getFleet(fleets_ids[0])
    tech = fo.getTech(techs[0])
    unlocked_items = tech.unlockedItems

    ship = universe.getShip(list(fleet.shipIDs)[0])
    # meter = ship.getMeter(fo.meterType.maxFuel)

    design = ship.design

    universe_object = universe.getObject(universe.systemIDs[0])

    # fo.getHullType ?

    species = None
    special = None
    planets = [universe.getPlanet(pid) for pid in planet_ids]
    for planet in planets:
        species_name = planet.speciesName
        if species_name:
            species = fo.getSpecies(species_name)
        specials = list(planet.specials)
        if specials:
            special = fo.getSpecial(specials[0])

    inspect(
        fo,
        instances=[
            fo.get_galaxy_setup_data(),
            universe,
            planet_ids,
            universe.getPlanet(planet_ids[0]),
            system,
            techs,
            tech,
            tech.unlockedTechs,
            building,
            fo.getBuildingType(building.buildingTypeName),
            empire,
            field,
            fo.getFieldType(field.fieldTypeName),
            fleet,
            ship,
            # meter,
            design,
            fleet.shipIDs,  # int set wtf?
            universe_object,
            system.starlanesWormholes,
            empire.systemSupplyRanges,
            empire.supplyProjections(),
            empire.obstructedStarlanes(),
            empire.planetsWithWastedPP,
            unlocked_items,
            species,
            special
        ],
        classes_to_ignore=(
            'FleetPlan',
            'GGColor',
            'MonsterFleetPlan',
            'PlayerSetupData',
            'ShipPartMeterMap',
            'ShipSlotVec',
            'VisibilityIntMap',
            'diplomaticMessage',
            'diplomaticStatusUpdate',
            'meter',
        ),
        path='')
    # fo.sys_get_star_type(system),
    # fo.planet_get_size(pid),
    # fo.planet_get_type(planet),
    # fo.species_get_planet_environment(species, planet_type),
    # fo.species_preferred_focus(species),

    # fo.getBuildingType(string)
    # fo.getFieldType(string)
    # fo.getHullType(string)
    # fo.getShipPart(string)
    # fo.getShipDesign(number)
    # fo.getSpecial(string)
    # fo.getSpecies(string)
    # fo.getTech(string)
    # fo.getTechCategories(obj)
    # fo.get_empire(number)
    # fo.planet_get_species(number)
    # fo.techsInCategory(string)
    exit(1)
Ejemplo n.º 13
0
def generate_monsters(monster_freq, systems):
    """
    Adds space monsters to systems.
    """
    # first, calculate the basic chance for monster generation in a system
    # based on the monster frequency that has been passed
    # get the corresponding value for the specified monster frequency from the universe tables
    basic_chance = universe_tables.MONSTER_FREQUENCY[monster_freq]
    # a value of 0 means no monsters, in this case return immediately
    if basic_chance <= 0:
        return
    print "Default monster spawn chance:", basic_chance
    expectation_tally = 0.0
    actual_tally = 0

    # get all monster fleets that have a spawn rate and limit both > 0 and at least one monster ship design in it
    # (a monster fleet with no monsters in it is pointless) and store them in a list
    fleet_plans = fo.load_monster_fleet_plan_list()

    # create a map where we store a spawn counter for each monster fleet
    # this counter will be set to the spawn limit initially and decreased every time the monster fleet is spawned
    # this map (dict) needs to be separate from the list holding the fleet plans because the order in which items
    # are stored in a dict is undefined (can be different each time), which would result in different distribution
    # even when using the same seed for the RNG
    spawn_limits = {fp: fp.spawn_limit() for fp in fleet_plans
                    if fp.spawn_rate() > 0.0 and fp.spawn_limit() > 0 and fp.ship_designs()}

    # map nests to monsters for ease of reporting
    nest_name_map = {"KRAKEN_NEST_SPECIAL": "SM_KRAKEN_1",
                     "SNOWFLAKE_NEST_SPECIAL": "SM_SNOWFLAKE_1",
                     "JUGGERNAUT_NEST_SPECIAL": "SM_JUGGERNAUT_1"}
    tracked_plan_tries = {name: 0 for name in nest_name_map.values()}
    tracked_plan_counts = {name: 0 for name in nest_name_map.values()}
    tracked_plan_valid_locations = {fp: 0 for fp in fleet_plans if fp.name() in tracked_plan_counts}

    if not fleet_plans:
        return

    universe = fo.get_universe()

    # Fleet plans that include ships capable of altering starlanes.
    # @content_tag{CAN_ALTER_STARLANES} universe_generator special handling
    # for fleets containing a hull design with this tag.
    fleet_can_alter_starlanes = {fp for fp in fleet_plans
                                 if any([universe.getGenericShipDesign(design).hull_type.hasTag("CAN_ALTER_STARLANES")
                                         for design in fp.ship_designs()])}

    # dump a list of all monster fleets meeting these conditions and their properties to the log
    print "Monster fleets available for generation at game start:"
    fp_location_cache = {}
    for fleet_plan in fleet_plans:
        print "...", fleet_plan.name(), ": spawn rate", fleet_plan.spawn_rate(),
        print "/ spawn limit", fleet_plan.spawn_limit(),
        print "/ effective chance", basic_chance * fleet_plan.spawn_rate(),
        fp_location_cache[fleet_plan] = set(fleet_plan.locations(systems))
        print ("/ can be spawned at", len(fp_location_cache[fleet_plan]),
               "of", len(systems), "systems")
        if fleet_plan.name() in nest_name_map.values():
            universe_statistics.tracked_monsters_chance[fleet_plan.name()] = basic_chance * fleet_plan.spawn_rate()

    # initialize a manager for monsters that can alter the map
    # required to prevent their placement from disjoining the map
    starlane_altering_monsters = StarlaneAlteringMonsters(systems)

    # collect info for tracked monster nest valid locations
    planets = [p for s in systems for p in fo.sys_get_planets(s)]
    tracked_nest_valid_locations = {nest: len(fo.special_locations(nest, planets))
                                    for nest in nest_name_map}

    # for each system in the list that has been passed to this function, find a monster fleet that can be spawned at
    # the system and which hasn't already been added too many times, then attempt to add that monster fleet by
    # testing the spawn rate chance
    random.shuffle(systems)
    for system in systems:
        # collect info for tracked monster valid locations
        for fp in tracked_plan_valid_locations:
            if system in fp_location_cache[fp]:
                tracked_plan_valid_locations[fp] += 1

        # filter out all monster fleets whose location condition allows this system and whose counter hasn't reached 0.
        suitable_fleet_plans = [fp for fp in fleet_plans
                                if system in fp_location_cache[fp]
                                and spawn_limits.get(fp, 0)
                                and (fp not in fleet_can_alter_starlanes
                                     or starlane_altering_monsters.can_place_at(system, fp))]
        # if there are no suitable monster fleets for this system, continue with the next
        if not suitable_fleet_plans:
            continue

        # randomly select one monster fleet out of the suitable ones and then test if we want to add it to this system
        # by making a roll against the basic chance multiplied by the spawn rate of this monster fleet
        expectation_tally += basic_chance * sum([fp.spawn_rate()
                                                 for fp in suitable_fleet_plans]) / len(suitable_fleet_plans)
        fleet_plan = random.choice(suitable_fleet_plans)
        if fleet_plan.name() in tracked_plan_tries:
            tracked_plan_tries[fleet_plan.name()] += 1
        if random.random() > basic_chance * fleet_plan.spawn_rate():
            print("\t\t At system %4d rejected monster fleet %s from %d suitable fleets"
                  % (system, fleet_plan.name(), len(suitable_fleet_plans)))
            # no, test failed, continue with the next system
            continue
        actual_tally += 1
        if fleet_plan.name() in tracked_plan_counts:
            tracked_plan_counts[fleet_plan.name()] += 1

        # all prerequisites and the test have been met, now spawn this monster fleet in this system
        # create monster fleet
        try:
            if fleet_plan in fleet_can_alter_starlanes:
                starlane_altering_monsters.place(system, fleet_plan)
            else:
                populate_monster_fleet(fleet_plan, system)
            # decrement counter for this monster fleet
            spawn_limits[fleet_plan] -= 1

        except MapGenerationError as err:
            report_error(str(err))
            continue

    print "Actual # monster fleets placed: %d; Total Placement Expectation: %.1f" % (actual_tally, expectation_tally)
    # finally, compile some statistics to be dumped to the log later
    universe_statistics.monsters_summary = [
        (fp.name(), fp.spawn_limit() - counter) for fp, counter in spawn_limits.iteritems()
    ]
    universe_statistics.tracked_monsters_tries.update(tracked_plan_tries)
    universe_statistics.tracked_monsters_summary.update(tracked_plan_counts)
    universe_statistics.tracked_monsters_location_summary.update(
        (fp.name(), count) for fp, count in tracked_plan_valid_locations.iteritems())
    universe_statistics.tracked_nest_location_summary.update(
        (nest_name_map[nest], count) for nest, count in tracked_nest_valid_locations.items())
Ejemplo n.º 14
0
def generate_monsters(monster_freq, systems):
    """
    Adds space monsters to systems.
    """
    # first, calculate the basic chance for monster generation in a system
    # based on the monster frequency that has been passed
    # get the corresponding value for the specified monster frequency from the universe tables
    basic_chance = universe_tables.MONSTER_FREQUENCY[monster_freq]
    # a value of 0 means no monsters, in this case return immediately
    if basic_chance <= 0:
        return
    print "Default monster spawn chance:", basic_chance
    expectation_tally = 0.0
    actual_tally = 0

    # get all monster fleets that have a spawn rate and limit both > 0 and at least one monster ship design in it
    # (a monster fleet with no monsters in it is pointless) and store them in a list
    fleet_plans = fo.load_monster_fleet_plan_list()

    # create a map where we store a spawn counter for each monster fleet
    # this counter will be set to the spawn limit initially and decreased every time the monster fleet is spawned
    # this map (dict) needs to be separate from the list holding the fleet plans because the order in which items
    # are stored in a dict is undefined (can be different each time), which would result in different distribution
    # even when using the same seed for the RNG
    spawn_limits = {
        fp: fp.spawn_limit()
        for fp in fleet_plans if fp.spawn_rate() > 0.0 and fp.spawn_limit() > 0
        and fp.ship_designs()
    }

    # map nests to monsters for ease of reporting
    nest_name_map = {
        "KRAKEN_NEST_SPECIAL": "SM_KRAKEN_1",
        "SNOWFLAKE_NEST_SPECIAL": "SM_SNOWFLAKE_1",
        "JUGGERNAUT_NEST_SPECIAL": "SM_JUGGERNAUT_1"
    }
    tracked_plan_tries = {name: 0 for name in nest_name_map.values()}
    tracked_plan_counts = {name: 0 for name in nest_name_map.values()}
    tracked_plan_valid_locations = {
        fp: 0
        for fp in fleet_plans if fp.name() in tracked_plan_counts
    }
    tracked_nest_valid_locations = {nest: 0 for nest in nest_name_map}

    if not fleet_plans:
        return

    universe = fo.get_universe()

    # Fleet plans that include ships capable of altering starlanes.
    ## @content_tag{CAN_ALTER_STARLANES} universe_generator special handling for fleets containing a hull design with this tag.
    fleet_can_alter_starlanes = {
        fp
        for fp in fleet_plans if any([
            universe.getGenericShipDesign(design).hull_type.hasTag(
                "CAN_ALTER_STARLANES") for design in fp.ship_designs()
        ])
    }

    # dump a list of all monster fleets meeting these conditions and their properties to the log
    print "Monster fleets available for generation at game start:"
    for fleet_plan in fleet_plans:
        print "...", fleet_plan.name(), ": spawn rate", fleet_plan.spawn_rate(
        ),
        print "/ spawn limit", fleet_plan.spawn_limit(),
        print "/ effective chance", basic_chance * fleet_plan.spawn_rate(),

        if len(systems) < 100:
            # Note: The WithinStarlaneJumps condition in fp.location()
            # is the most time costly function in universe generation
            print "/ can be spawned at", len(
                [s for s in systems if fleet_plan.location(s)]), "systems"
        else:
            print  # to terminate the print line
        if fleet_plan.name() in nest_name_map.values():
            statistics.tracked_monsters_chance[
                fleet_plan.name()] = basic_chance * fleet_plan.spawn_rate()

    # initialize a manager for monsters that can alter the map
    # required to prevent their placement from disjoining the map
    starlane_altering_monsters = StarlaneAlteringMonsters(systems)

    # for each system in the list that has been passed to this function, find a monster fleet that can be spawned at
    # the system and which hasn't already been added too many times, then attempt to add that monster fleet by
    # testing the spawn rate chance
    for system in systems:
        # collect info for tracked monster nest valid locations
        for planet in fo.sys_get_planets(system):
            for nest in tracked_nest_valid_locations:
                # print "\t tracked monster check planet: %d size: %s for nest: %20s  | result: %s"
                # % (planet, fo.planet_get_size(planet), nest, fo.special_location(nest, planet))
                if fo.special_location(nest, planet):
                    tracked_nest_valid_locations[nest] += 1

        # collect info for tracked monster valid locations
        for fp in tracked_plan_valid_locations:
            if fp.location(system):
                tracked_plan_valid_locations[fp] += 1

        # filter out all monster fleets whose location condition allows this system and whose counter hasn't reached 0.
        # Note: The WithinStarlaneJumps condition in fp.location() is
        # the most time costly function in universe generation.
        suitable_fleet_plans = [
            fp for fp in fleet_plans
            if spawn_limits[fp] and fp.location(system) and (
                fp not in fleet_can_alter_starlanes
                or starlane_altering_monsters.can_place_at(system, fp))
        ]
        # if there are no suitable monster fleets for this system, continue with the next
        if not suitable_fleet_plans:
            continue

        # randomly select one monster fleet out of the suitable ones and then test if we want to add it to this system
        # by making a roll against the basic chance multiplied by the spawn rate of this monster fleet
        expectation_tally += basic_chance * sum(
            [fp.spawn_rate()
             for fp in suitable_fleet_plans]) / len(suitable_fleet_plans)
        fleet_plan = random.choice(suitable_fleet_plans)
        if fleet_plan.name() in tracked_plan_tries:
            tracked_plan_tries[fleet_plan.name()] += 1
        if random.random() > basic_chance * fleet_plan.spawn_rate():
            print(
                "\t\t At system %4d rejected monster fleet %s from %d suitable fleets"
                % (system, fleet_plan.name(), len(suitable_fleet_plans)))
            # no, test failed, continue with the next system
            continue
        actual_tally += 1
        if fleet_plan.name() in tracked_plan_counts:
            tracked_plan_counts[fleet_plan.name()] += 1

        # all prerequisites and the test have been met, now spawn this monster fleet in this system
        # create monster fleet
        try:
            if fleet_plan in fleet_can_alter_starlanes:
                starlane_altering_monsters.place(system, fleet_plan)
            else:
                populate_monster_fleet(fleet_plan, system)
            # decrement counter for this monster fleet
            spawn_limits[fleet_plan] -= 1

        except MapGenerationError as e:
            report_error(str(e))
            continue

    print "Actual # monster fleets placed: %d; Total Placement Expectation: %.1f" % (
        actual_tally, expectation_tally)
    # finally, compile some statistics to be dumped to the log later
    statistics.monsters_summary = [(fp.name(), fp.spawn_limit() - counter)
                                   for fp, counter in spawn_limits.iteritems()]
    statistics.tracked_monsters_tries.update(tracked_plan_tries)
    statistics.tracked_monsters_summary.update(tracked_plan_counts)
    statistics.tracked_monsters_location_summary.update([
        (fp.name(), count)
        for fp, count in tracked_plan_valid_locations.iteritems()
    ])
    statistics.tracked_nest_location_summary.update([
        (nest_name_map[nest], count)
        for nest, count in tracked_nest_valid_locations.items()
    ])