コード例 #1
0
def create_universe():

    print "Python Universe Generator"

    # fetch universe and player setup data
    global gsd, psd_list
    gsd = fo.get_galaxy_setup_data()
    psd_list = fo.get_player_setup_data()
    total_players = len(psd_list)

    # initialize RNG
    random.seed(gsd.seed)

    # store list of possible star system names in global container
    global star_names
    star_names = get_name_list("STAR_NAMES")
    # randomly shuffle the list so we don't get the names always
    # in the same order when we pop names from the list later
    random.shuffle(star_names)

    # make sure there are enough systems for the given number of players
    print "Universe creation requested with %d systems for %d players" % (gsd.size, total_players)
    new_size = adjust_universe_size(gsd.size, total_players)
    if new_size > gsd.size:
        gsd.size = new_size
        print "Too few systems for the requested number of players, number of systems adjusted accordingly"
    print "Creating universe with %d systems for %d players" % (gsd.size, total_players)

    # get typical width for universe based on number of systems
    width = fo.calc_typical_universe_width(gsd.size)
    fo.set_universe_width(width)
    print "Set universe width to", width

    # Calling universe generator helper functions to calculate positions
    # for the requested galaxy shape and number of systems
    system_positions = fo.SystemPositionVec()
    if gsd.shape == fo.galaxyShape.random:
        gsd.shape = random.choice(galaxy_shapes)
    if gsd.shape == fo.galaxyShape.spiral2:
        fo.spiral_galaxy_calc_positions(system_positions, 2, gsd.size, width, width)
    elif gsd.shape == fo.galaxyShape.spiral3:
        fo.spiral_galaxy_calc_positions(system_positions, 3, gsd.size, width, width)
    elif gsd.shape == fo.galaxyShape.spiral4:
        fo.spiral_galaxy_calc_positions(system_positions, 4, gsd.size, width, width)
    elif gsd.shape == fo.galaxyShape.elliptical:
        fo.elliptical_galaxy_calc_positions(system_positions, gsd.size, width, width)
    elif gsd.shape == fo.galaxyShape.cluster:
        # Typically a galaxy with 100 systems should have ~5 clusters
        avg_clusters = gsd.size / 20
        if avg_clusters < 2:
            avg_clusters = 2
        # Add a bit of random variation (+/- 20%)
        clusters = random.randint((avg_clusters * 8) / 10, (avg_clusters * 12) / 10)
        if clusters >= 2:
            fo.cluster_galaxy_calc_positions(system_positions, clusters, gsd.size, width, width)
    elif gsd.shape == fo.galaxyShape.ring:
        fo.ring_galaxy_calc_positions(system_positions, gsd.size, width, width)
    elif gsd.shape == fo.galaxyShape.test:
        test_galaxy_calc_positions(system_positions, gsd.size, width)
    # Check if any positions have been calculated...
    if len(system_positions) <= 0:
        # ...if not, fall back on irregular shape
        gsd.shape = fo.galaxyShape.irregular
        fo.irregular_galaxy_positions(system_positions, gsd.size, width, width)
    gsd.size = len(system_positions)
    print gsd.shape, "galaxy created, final number of systems:", gsd.size

    # choose star types and planet sizes, before choosting names, so naming can have special handling of Deep Space
    star_type_assignments = {}
    planet_size_assignments = {}
    planet_count_dist = {}
    for position in system_positions:
        star_type = pick_star_type() # needed to determine planet size
        star_type_assignments[(position.x, position.y)] = star_type
        these_planets = {}
        planet_count = 0
        for orbit in range(0, system_orbits):
            # check for each orbit if a planet shall be created by determining planet size
            planet_size = calc_planet_size(star_type, orbit)
            these_planets[orbit] = planet_size
            if planet_size in planet_sizes:
                planet_count += 1
        planet_size_assignments[(position.x, position.y)] = these_planets
        planet_count_dist.setdefault(planet_count, [0])[0] += 1
    print "\n Planet Count Distribution: planets_in_system | num_systems"
    for planet_count, sys_count in planet_count_dist.items():
        print "\t\t\t%2d  | %5d" % (planet_count, sys_count[0])
    print

    # will name name a portion of stars on a group basis, where the stars of each group share the same base star name,
    # suffixed by different (default greek) letters or characters (options at top of file)
    star_name_map = {}
    group_names = get_name_list("STAR_GROUP_NAMES")
    potential_group_names = []
    individual_names = []
    stargroup_words[:] = get_name_list("STAR_GROUP_WORDS")
    stargroup_chars[:] = get_name_list("STAR_GROUP_CHARS")
    stargroup_modifiers[:] = [stargroup_words, stargroup_chars][ star_groups_use_chars ]
    for starname in star_names:
        if len(starname) > 6: #if starname is long, don't allow it for groups
            individual_names.append(starname)
            continue
        # any names that already have a greek letter in them can only be used for individual stars, not groups
        for namepart in starname.split():
            if namepart in greek_letters:
                individual_names.append(starname)
                break
        else:
            potential_group_names.append(starname)

    if potential_group_names == []:
        potential_group_names.append("XYZZY")

    # ensure at least a portion of galaxy gets individual starnames
    num_systems = len(system_positions)
    target_indiv_ratio = [target_indiv_ratio_small, target_indiv_ratio_large][ num_systems >= naming_large_galaxy_size ]
    #TODO improve the following calc to be more likely to hit target_indiv_ratio if more or less than 50% potential_group_names used for groups
    num_individual_stars = int(max(min( num_systems * target_indiv_ratio,
                               len(individual_names)+int(0.5*len(potential_group_names))),
                               num_systems - 0.8*len(stargroup_modifiers)*(len(group_names)+int(0.5*len(potential_group_names)))))
    star_group_size = 1 + int( (num_systems-num_individual_stars)/(max(1, len(group_names)+int(0.5*len(potential_group_names)))))
    # make group size a bit bigger than min necessary, at least a trio
    star_group_size = max(3, star_group_size)
    num_star_groups = 1 + int(num_systems/star_group_size) #initial value
    #print "num_individual_stars:", num_individual_stars, "star group size: ", star_group_size, "num_star_groups", num_star_groups
    #print "num indiv names:", len(individual_names), "num group_names:", len(group_names), "num potential_group_names:", len(potential_group_names)

    # first cluster all systems, then remove some to be individually named (otherwise groups can have too many individually
    # named systems in their middle).  First remove any that are too small (only 1 or 2 systems).  The clusters with the most systems
    # are generally the most closely spaced, and though they might make good logical candidates for groups, their names are then prone
    # to overlapping on the galaxy map, so after removing small groups, remove the groups with the most systems.
    position_list = list(system_positions)
    random.shuffle(position_list) #just to be sure it is randomized
    init_cluster_assgts = cluster_stars(position_list, num_star_groups)
    star_groups = {}
    for index_pos, index_group in enumerate(init_cluster_assgts):
        this_pos = position_list[index_pos]
        star_groups.setdefault(index_group, []).append( (this_pos.x, this_pos.y) )
    indiv_systems = []

    # remove groups with only one non-deep-system
    for groupindex, group_list in star_groups.items():
        max_can_transfer = len(potential_group_names)-len(star_groups)+len(individual_names)-len(indiv_systems)
        if max_can_transfer <= 0:
            break
        elif max_can_transfer <= len(group_list):
            continue
        not_deep,  deep_space = check_deep_space(group_list, star_type_assignments, planet_size_assignments)
        if len(not_deep) > 1:
            continue
        for systemxy in star_groups[groupindex]:
            indiv_systems.append(systemxy)
        del star_groups[groupindex]

    # remove tiny groups
    group_sizes = [(len(group), index) for index, group in star_groups.items()]
    group_sizes.sort()
    while (len(indiv_systems) < num_individual_stars) and len(group_sizes)>0:
        groupsize, groupindex = group_sizes.pop()
        max_can_transfer = len(potential_group_names)-len(star_groups)+len(individual_names)-len(indiv_systems)
        if (max_can_transfer <= 0) or (groupsize > 2):
            break
        if max_can_transfer <= groupsize:
            continue
        for systemxy in star_groups[groupindex]:
            indiv_systems.append(systemxy)
        del star_groups[groupindex]

    # remove largest (likely most compact) groups
    while (len(indiv_systems) < num_individual_stars) and len(group_sizes)>0:
        groupsize, groupindex = group_sizes.pop(-1)
        max_can_transfer = len(potential_group_names)-len(star_groups)+len(individual_names)-len(indiv_systems)
        if max_can_transfer <= 0:
            break
        if max_can_transfer <= groupsize:
            continue
        for systemxy in star_groups[groupindex]:
            indiv_systems.append(systemxy)
        del star_groups[groupindex]

    num_star_groups = len(star_groups)
    num_individual_stars = len(indiv_systems)
    random.shuffle(potential_group_names)
    random.shuffle(individual_names)
    random.shuffle(group_names)
    num_for_indiv = min(max(len(potential_group_names)/2, num_individual_stars+1-len(individual_names)), len(potential_group_names))
    individual_names.extend( potential_group_names[:num_for_indiv])
    group_names.extend( potential_group_names[num_for_indiv:])

    #print "sampling for %d indiv names from list of %d total indiv names"%(num_individual_stars, len(individual_names))
    indiv_name_sample = random.sample(individual_names, num_individual_stars)
    #indiv_name_assignments = zip([(pos.x, pos.y) for pos in position_list[:num_individual_stars]], indiv_name_sample)
    indiv_name_assignments = zip(indiv_systems, indiv_name_sample)
    star_name_map.update( indiv_name_assignments )
    #print "sampling for %d group names from list of %d total group names"%(num_star_groups, len(group_names))
    group_name_sample = random.sample(group_names, num_star_groups)
    for index_group, group_list in enumerate(star_groups.values()):
        star_name_map.update( name_group(group_list, group_name_sample[index_group], star_type_assignments, planet_size_assignments) )

    # Generate and populate systems
    systems = []
    planet_type_summary = {}
    for position in system_positions:
        systemxy = (position.x, position.y)
        star_type = star_type_assignments.get(systemxy, fo.starType.noStar)
        star_name = star_name_map.get(systemxy, "") or get_star_name()
        system = fo.create_system(star_type, star_name, position.x, position.y)
        systems.append(system)
        these_planets = planet_size_assignments[systemxy]
        for orbit in range(0, fo.sys_get_num_orbits(system)):
            # check for each orbit if a planet shall be created by determining planet size
            planet_size = these_planets.get(orbit, fo.planetSize.noWorld)
            if planet_size in planet_sizes:
                # ok, we want a planet, determine planet type and generate the planet
                planet_type = calc_planet_type(star_type, orbit, planet_size)
                planet_type_summary.setdefault(planet_type, [0])[0]+=1
                generate_planet(planet_size, planet_type, system, orbit)
    print len(systems), "systems generated and populated"
    planet_type_names = dict(zip(planet_types_all, ["Swamp", "Radiated", "Toxic", "Inferno", "Barren", "Tundra", "Desert", "Terran", "Ocean", "Asteroids", "Gas Giant"]))
    planet_total = sum([type_tally[0] for type_tally in planet_type_summary.values()])
    print "\nPlanet Type Summary for a total of %d placed planets" % (planet_total)
    for planet_type in planet_type_summary:
        print "\t %12s: %.1f%%" % (planet_type_names[planet_type], (100.0*planet_type_summary.get(planet_type, [0])[0])/planet_total)
    print
    # generate Starlanes
    fo.generate_starlanes(gsd.starlaneFrequency)
    print "Starlanes generated"

    print "Generate list of home systems..."
    home_systems = generate_home_system_list(total_players, systems)
    print "...systems choosen:", home_systems

    # store list of possible empire names in global container
    global empire_names
    print "Load list of empire names..."
    empire_names = get_name_list("EMPIRE_NAMES")
    print "...", len(empire_names), "names loaded"
    # randomly shuffle the list so we don't get the names always
    # in the same order when we pop names from the list later
    random.shuffle(empire_names)

    # set up empires for each player
    for psd_entry in psd_list:
        empire = psd_entry.key()
        psd = psd_entry.data()
        home_system = home_systems.pop()
        setup_empire(empire, psd.empire_name, home_system, psd.starting_species, psd.player_name)

    # iterate over all systems and name their planets
    # this needs to be done after empire home systems have been set, as
    # during that process asteroid belts might be changed into planets,
    # and we need to know the final type of a planet to name it
    print "Set planet names"
    for system in systems:
        name_planets(system)

    print "Python Universe Generator completed"
コード例 #2
0
def generate_home_system_list(num_home_systems, systems):

    # id the list of systems to choose home systems from is empty, raise an exception
    if len(systems) == 0:
        err_msg = "Python generate_home_system_list: no systems to choose from"
        print err_msg
        raise Exception(err_msg)

    # initialize list of home systems
    home_systems = []

    # loop and get a new home systems until we have the requested number
    while len(home_systems) < num_home_systems:

        # try to choose a system until too many attempty failed or a system has been found
        attempts = 0
        found = False
        while (attempts < 100) and not found:
            attempts = attempts + 1
            # randomly choose one system from the list we got
            candidate = random.choice(systems)
            # for the first 50 attempts, only consider systems with "real" stars and at least one planet
            # if we haven't found a system after 50 attempts, consider all systems
            if (attempts < 50):
                if fo.sys_get_star_type(candidate) not in real_star_types:
                    continue
                if len(fo.sys_get_planets(candidate)) == 0:
                    continue
            # if our candidate is too close to the already choosen home systems, don't use it
            if is_too_close_to_other_home_systems(candidate, home_systems):
                continue
            # if our candidate passed the above tests, add it to our list
            home_systems.append(candidate)
            found = True

        # if no system could be found, just attempt to find one that's not
        # already a home system and disregard any other conditions
        if not found:
            print "Couldn't find homeworld #", len(home_systems) + 1, "after 100 attempts, just trying to find one now that's not already a home system and disregard any other condition"
            attempts = 0
            while (attempts < 50) and not found:
                attempts = attempts +1
                # again, choose one system from the list we got
                candidate = random.choice(systems)
                # but now just check if it has already been choosen as home system
                if candidate in home_systems:
                    # if yes, try again
                    continue
                # if our candidate passed the test, add it to our list
                home_systems.append(candidate)
                found = True

        # if we still haven't found a suitable system, our galaxy obviously is too crowded
        # in that case, throw a fit, em, exception ;)
        if not found:
            raise Exception("Python generate_home_system_list: requested %d homeworlds in a galaxy with %d systems, aborting" % (num_home_systems, len(systems)))

        # if choosen system has no "real" star, change star type to a randomly selected "real" star
        if fo.sys_get_star_type(candidate) not in real_star_types:
            star_type = random.choice(real_star_types)
            print "Home system #", len(home_systems), "has star type", fo.sys_get_star_type(candidate), ", changing that to", star_type
            fo.sys_set_star_type(candidate, star_type)

        # if choosen system has no planets, create one in a random orbit
        # we take random values for type and size, as these will be
        # set to suitable values later
        if len(fo.sys_get_planets(candidate)) == 0:
            print "Home system #", len(home_systems), "has no planets, adding one"
            if generate_planet(random.choice(real_planet_sizes), random.choice(planet_types), candidate, random.randint(0, fo.sys_get_num_orbits(candidate) - 1)) == fo.invalid_object():
                # generate planet failed, throw an exception
                raise Exception("Python generate_home_system_list: couldn't create planet in home system")

    return home_systems