def stitching_positions(p1, p2): """ Returns a list of positions between p1 and p2 between MIN_SYSTEM_SEPARATION and MAX_STARLANE_LENGTH apart """ if 2 * universe_tables.MIN_SYSTEM_SEPARATION >= universe_tables.MAX_STARLANE_LENGTH: util.report_error("MAX_STARLANE_LENGTH must be twice MIN_SYSTEM_SEPARATION to " "allow extra positions to be added to enforce MAX_STARLANE_LENGTH") return [] max_dist = universe_tables.MAX_STARLANE_LENGTH min_dist = universe_tables.MIN_SYSTEM_SEPARATION p1_p2_dist = util.distance(p1, p2) if p1_p2_dist < max_dist: return [] # Pick a random point in an 2:1 ratio ellipse and then rotate and slide it in between p1 and p2 p1_p2_theta = acos((p2[0] - p1[0]) / p1_p2_dist) p1_p2_scale = (p1_p2_dist - 2*min_dist) / 2 p1_p2_translation = ((p1[0] + p2[0]) / 2, (p1[1] + p2[1]) / 2) radius_0space = uniform(0.0, 1.0) angle_0space = uniform(0.0, 2.0 * pi) rotated_point = (radius_0space * p1_p2_scale * cos(angle_0space + p1_p2_theta), radius_0space * p1_p2_scale * sin(angle_0space + p1_p2_theta)) p3 = (rotated_point[0] + p1_p2_translation[0], rotated_point[1] + p1_p2_translation[1]) return [p3] + stitching_positions(p1, p3) + stitching_positions(p2, p3)
def calc_planet_size(star_type, orbit, planet_density, galaxy_shape): """ Calculate planet size for a potential new planet based on planet density setup option, star type and orbit number. """ # try to pick a planet size by making a series of "rolls" (1-100) # for each planet size, and take the highest modified roll planet_size = fo.planetSize.unknown try: max_roll = 0 for candidate in planet_sizes_all: roll = random.randint(1, 100) \ + tables.DENSITY_MOD_TO_PLANET_SIZE_DIST[planet_density][candidate] \ + tables.STAR_TYPE_MOD_TO_PLANET_SIZE_DIST[star_type][candidate] \ + tables.ORBIT_MOD_TO_PLANET_SIZE_DIST[orbit][candidate] \ + tables.GALAXY_SHAPE_MOD_TO_PLANET_SIZE_DIST[galaxy_shape][candidate] if max_roll < roll: max_roll = roll planet_size = candidate except: # in case of an error play it safe and set planet size to invalid planet_size = fo.planetSize.unknown util.report_error("Python calc_planet_size: Pick planet size failed" + str(sys.exc_info()[1])) # if we got an invalid planet size (for whatever reason), # just select one randomly from the global tuple based # only on the planet density setup option if planet_size == fo.planetSize.unknown: if random.randint(1, 10) <= planet_density: planet_size = random.choice(planet_sizes) else: planet_size = fo.planetSize.noWorld return planet_size
def __distribute(self, dl_info): assert isinstance(dl_info, download_info) util.report_info("Verifying <%s> ..." % dl_info.res_path) # Verify distribute source if not os.path.isfile(dl_info.store_path): util.report_error("File <%s> is not existed. Please check your network state and re-run build script." % dl_info.res_path) if fhash.hash_file(dl_info.store_path) != dl_info.tag: util.report_error("File <%s> verificaition is failed. Please check your network state and re-run build script." % dl_info.res_path) util.report_info("Distributing <%s> ..." % dl_info.res_path) # Clean target if file is existed. if dl_info.res_type in [RAW_FILE, COMPRESSED_FILE]: if os.path.isfile(dl_info.dist_path): os.remove(dl_info.dist_path) elif dl_info.res_type in [COMPRESSED_FOLDER]: if os.path.isdir(dl_info.dist_path): shutil.rmtree(dl_info.dist_path) dist_parent = os.path.dirname(dl_info.dist_path) if dl_info.res_type in [COMPRESSED_FILE, COMPRESSED_FOLDER]: self.__decompress(dl_info.store_path, dist_parent) else: shutil.copy(dl_info.store_path, dist_parent)
def can_place_at(self, system, plan): """ Check if the starlane altering monster fleet ''plan'' can be placed at ''system'' without disjoining the galaxy map. Compute the disjoint set of the galaxy without the starlanes from the proposed system. Return False if there will be more connected regions than the number of placed starlane altering monsters plus one, otherwise True. """ local_lanes = {(min(system, s), max(system, s)) for s in fo.sys_get_starlanes(system)} dsets = DisjointSets() for s in self.systems: dsets.add(s) for lane in self.starlanes: if lane in self.removed_lanes or lane in local_lanes: continue dsets.link(lane[0], lane[1]) num_contiguous_regions = len(dsets.complete_sets()) expected_num_contiguous_regions = (len(self.placed) + 2) if num_contiguous_regions > expected_num_contiguous_regions: return False if num_contiguous_regions < expected_num_contiguous_regions: report_error("Number of contiguous regions %d is below the expected number " "of contiguous regions %d when placing %d monster %s that can " "break starlanes." % (num_contiguous_regions, expected_num_contiguous_regions, len(self.placed) + 1, plan.name())) return False return True
def pick_star_type(galaxy_age): """ Picks and returns a star type based on universe tables distribution modifiers. """ # try to pick a star type by making a series of "rolls" (1-100) # for each star type, and take the highest modified roll star_type = fo.starType.unknown try: max_roll = 0 for candidate in star_types: roll = random.randint(1, 100) \ + fo.universe_age_mod_to_star_type_dist(galaxy_age, candidate) \ + fo.base_star_type_dist(candidate) if max_roll < roll: max_roll = roll star_type = candidate except: # in case of an error play save and set star type to invalid star_type = fo.starType.unknown util.report_error("Python pick_star_type: Pick star type failed\n" + sys.exc_info()[1]) # if we got an invalid star type (for whatever reason), # just select one randomly from the global tuple if star_type == fo.starType.unknown: star_type = random.choice(star_types) return star_type
def calc_planet_size(star_type, orbit, planet_density, galaxy_shape): """ Calculate planet size for a potential new planet based on planet density setup option, star type and orbit number. """ # try to pick a planet size by making a series of "rolls" (1-100) # for each planet size, and take the highest modified roll planet_size = fo.planetSize.unknown try: max_roll = 0 for size in planet_sizes_all: roll = (random.randint(1, 100) + base_chance_of_planet( planet_density, galaxy_shape, star_type, orbit, size)) if max_roll < roll: max_roll = roll planet_size = size except: # in case of an error play it safe and set planet size to invalid planet_size = fo.planetSize.unknown util.report_error("Python calc_planet_size: Pick planet size failed" + str(sys.exc_info()[1])) # if we got an invalid planet size (for whatever reason), # just select one randomly from the global tuple based # only on the planet density setup option if planet_size == fo.planetSize.unknown: if random.randint(1, 10) <= planet_density: planet_size = random.choice(planet_sizes) else: planet_size = fo.planetSize.noWorld return planet_size
def generate_fields(systems): """ Generates stationary fields in some randomly chosen empty no star systems. """ # filter out all empty no star systems candidates = [ s for s in systems if (fo.sys_get_star_type(s) == fo.starType.noStar) and ( not fo.sys_get_planets(s)) ] # make sure we have at least one empty no star system, otherwise return without creating any fields if not candidates: print("...no empty no star systems found, no fields created") return # pick 10-15% of all empty no star systems to create stationary fields in them, but at least one accepted = sample(candidates, max(int(len(candidates) * uniform(0.1, 0.15)), 1)) for system in accepted: # randomly pick a field type field_type = choice(["FLD_NEBULA_1", "FLD_NEBULA_2", "FLD_NEBULA_3"]) # and create the field if fo.create_field_in_system(field_type, uniform(40, 120), system) == fo.invalid_object(): # create field failed, report an error report_error( "Python generate_fields: create field %s in system %d failed" % (field_type, system)) print("...fields created in %d systems out of %d empty no star systems" % (len(accepted), len(candidates)))
def generate_systems(pos_list, gsd): """ Generates and populates star systems at all positions in specified list. """ sys_list = [] for position in pos_list: star_type = pick_star_type(gsd.age) system = fo.create_system(star_type, "", position.x, position.y) if system == fo.invalid_object(): # create system failed, report an error and try to continue with next position util.report_error( "Python generate_systems: create system at position (%f, %f) failed" % (position.x, position.y)) continue sys_list.append(system) for orbit in range(fo.sys_get_num_orbits(system)): # check for each orbit if a planet shall be created by determining planet size planet_size = planets.calc_planet_size(star_type, orbit, gsd.planetDensity, gsd.shape) if planet_size in planets.planet_sizes: # ok, we want a planet, determine planet type and generate the planet planet_type = planets.calc_planet_type(star_type, orbit, planet_size) if fo.create_planet(planet_size, planet_type, system, orbit, "") == fo.invalid_object(): # create planet failed, report an error and try to continue with next orbit util.report_error( "Python generate_systems: create planet in system %d failed" % system) return sys_list
def calc_planet_size(star_type, orbit, planet_density, galaxy_shape): """ Calculate planet size for a potential new planet based on planet density setup option, star type and orbit number. """ # try to pick a planet size by making a series of "rolls" (1-100) # for each planet size, and take the highest modified roll planet_size = fo.planetSize.unknown try: max_roll = 0 for size in planet_sizes_all: roll = (random.randint(1, 100) + base_chance_of_planet(planet_density, galaxy_shape, star_type, orbit, size)) if max_roll < roll: max_roll = roll planet_size = size except: # in case of an error play it safe and set planet size to invalid planet_size = fo.planetSize.unknown util.report_error("Python calc_planet_size: Pick planet size failed" + str(sys.exc_info()[1])) # if we got an invalid planet size (for whatever reason), # just select one randomly from the global tuple based # only on the planet density setup option if planet_size == fo.planetSize.unknown: if random.randint(1, 10) <= planet_density: planet_size = random.choice(planet_sizes) else: planet_size = fo.planetSize.noWorld return planet_size
def report_error(self, resp, status=None): """Report an error to StackDriver Error reporting.""" # don't report specific known failures if ('Deadline exceeded while waiting for HTTP response' in resp or 'urlfetch.Fetch() took too long' in resp or # WordPress Jetpack bugs # https://github.com/snarfed/bridgy/issues/161 '"resp": "invalid_input"' in resp or # https://github.com/snarfed/bridgy/issues/750 '"error": "jetpack_verification_failed"' in resp or # https://console.cloud.google.com/errors/CMjIg52NkMLQYA 'The Jetpack site encountered an error and could not process the API request' in resp or # Blogger known bug # https://github.com/snarfed/bridgy/issues/175 'bX-2i87au' in resp or # Tumblr: transient Disqus error looking up thread # https://github.com/snarfed/bridgy/issues/177 "Invalid argument, 'thread': Unable to find thread" in resp or # expected for partially set up tumblr accounts "we haven't found your Disqus account" in resp or # Twitter 5MB image file size limit '"message":"Image file size must be' in resp or # Twitter media file number limits 'Tweet with media must have exactly 1 gif or video' in resp or # Facebook image type/size req'ts 'Missing or invalid image file' in resp or "Your photos couldn't be uploaded. Photos should be less than 4 MB" in resp or # Twitter duplicate publish attempts 'Status is a duplicate.' in resp or 'You have already favorited this status.' in resp or 'You have already retweeted this' in resp or # Facebook duplicate publish attempts 'This status update is identical to the last one you posted.' in resp or # WordPress duplicate comment # "error": "Error: 409 HTTP Error 409: Conflict; {\n \"error\": \"comment_duplicate\",\n \"message\": \"Duplicate comment detected; it looks as though you’ve already said that!\"\n}\n" 'comment_duplicate' in resp): return subject = '%s %s' % (self.__class__.__name__, '%s %s' % (self.entity.type, self.entity.status) if self.entity else 'failed') user = self.source.bridgy_url() if self.source else None util.report_error(subject, user=user, http_context=error_reporting.HTTPContext( method=request.method, url=request.url, response_status_code=status, remote_ip=request.remote_addr))
def generate_systems(pos_list, gsd): """ Generates and populates star systems at all positions in specified list. """ sys_list = [] for position in pos_list: star_type = pick_star_type(gsd.age) system = fo.create_system(star_type, "", position[0], position[1]) if system == fo.invalid_object(): # create system failed, report an error and try to continue with next position util.report_error( "Python generate_systems: create system at position (%f, %f) failed" % (position[0], position[1])) continue sys_list.append(system) orbits = list(range(fo.sys_get_num_orbits(system))) if not planets.can_have_planets(star_type, orbits, gsd.planet_density, gsd.shape): continue # Try to generate planets in each orbit. # If after each orbit is tried once there are no planets then # keep trying until a single planet is placed. # Except for black hole systems, which can be empty. at_least_one_planet = False random.shuffle(orbits) for orbit in orbits: if planets.generate_a_planet(system, star_type, orbit, gsd.planet_density, gsd.shape): at_least_one_planet = True if at_least_one_planet or can_have_no_planets(star_type): continue recursion_limit = 1000 for _, orbit in product(range(recursion_limit), orbits): if planets.generate_a_planet(system, star_type, orbit, gsd.planet_density, gsd.shape): break else: # Intentionally non-modal. Should be a warning. print( ("Python generate_systems: place planets in system %d at position (%.2f, %.2f) failed" % (system, position[0], position[1])), file=sys.stderr, ) return sys_list
def generate_a_planet(system, star_type, orbit, planet_density, galaxy_shape): """ Place a planet in an orbit of a system. Return True on success """ planet_size = calc_planet_size(star_type, orbit, planet_density, galaxy_shape) if planet_size not in planet_sizes: return False # ok, we want a planet, determine planet type and generate the planet planet_type = calc_planet_type(star_type, orbit, planet_size) if planet_type == fo.planetType.unknown: return False if fo.create_planet(planet_size, planet_type, system, orbit, "") == fo.invalid_object(): # create planet failed, report an error util.report_error("Python generate_systems: create planet in system %d failed" % system) return False return True
def copy_package_fields(package, pkg): fields = [ "activity_period-from", "activity_period-to", "activity_count", "country", "filetype", "verified" ] for field in fields: with report_error(None, None): field_name = "package_" + field.replace("-", "_") setattr(package, field_name, pkg["extras"][field])
def actually_save_file(package_name, orig_url, pkg, runtime_id): # `pkg` is a CKAN dataset success = False directory = app.config['DATA_STORAGE_DIR'] print "Attempting to fetch package", package_name, "from", orig_url url = fixURL(orig_url) path = os.path.join(directory, package_name + '.xml') success = manage_download(path, url) with report_error(" Wrote metadata to DB", " Couldn't write metadata to DB"): metadata_to_db(pkg, package_name, success, runtime_id) with report_error(" Package tested", " Couldn't test package %s" % package_name): dqruntests.start_testing(package_name)
def generate_systems(pos_list, gsd): """ Generates and populates star systems at all positions in specified list. """ sys_list = [] for position in pos_list: star_type = pick_star_type(gsd.age) system = fo.create_system(star_type, "", position[0], position[1]) if system == fo.invalid_object(): # create system failed, report an error and try to continue with next position util.report_error("Python generate_systems: create system at position (%f, %f) failed" % (position[0], position[1])) continue sys_list.append(system) orbits = range(fo.sys_get_num_orbits(system)) if not planets.can_have_planets(star_type, orbits, gsd.planet_density, gsd.shape): continue # Try to generate planets in each orbit. # If after each orbit is tried once there are no planets then # keep trying until a single planet is placed. # Except for black hole systems, which can be empty. at_least_one_planet = False random.shuffle(orbits) for orbit in orbits: if planets.generate_a_planet(system, star_type, orbit, gsd.planet_density, gsd.shape): at_least_one_planet = True if at_least_one_planet or can_have_no_planets(star_type): continue recursion_limit = 1000 for _, orbit in product(range(recursion_limit), orbits): if planets.generate_a_planet(system, star_type, orbit, gsd.planet_density, gsd.shape): break else: # Intentionally non-modal. Should be a warning. print >> sys.stderr, ("Python generate_systems: place planets in system %d at position (%.2f, %.2f) failed" % (system, position[0], position[1])) return sys_list
def copy_newer( src, dst ): if not( os.path.isfile(src) ): util.report_error('Error: src parameter of copy_newer is a file path.') target = None if os.path.isdir(dst): target = os.path.join( dst, os.path.basename(src) ) else: target = dst if os.path.exists(target): if os.path.getmtime(target) < os.path.getmtime(src): os.remove(target) if not os.path.exists(target): shutil.copy2( src, target ) return True else: return False
def setup_package_group(group): with util.report_error(None, "Error saving package_group"): # there is a group, so use that group name, or create one if group is not None: pg = models.PackageGroup.query.filter_by(name=group).first() if pg is None: pg = create_package_group(group, handle_country=False) print "Created new group" return pg
def generate_fields(systems): """ Generates stationary fields in some randomly chosen empty no star systems. """ # filter out all empty no star systems candidates = [s for s in systems if (fo.sys_get_star_type(s) == fo.starType.noStar) and (not fo.sys_get_planets(s))] # make sure we have at least one empty no star system, otherwise return without creating any fields if not candidates: print "...no empty no star systems found, no fields created" return # pick 10-20% of all empty no star systems to create stationary fields in them, but at least one accepted = sample(candidates, max(int(len(candidates) * uniform(0.1, 0.2)), 1)) for system in accepted: # randomly pick a field type field_type = choice(["FLD_NEBULA_1", "FLD_NEBULA_2"]) # and create the field if fo.create_field_in_system(field_type, uniform(40, 120), system) == fo.invalid_object(): # create field failed, report an error report_error("Python generate_fields: create field %s in system %d failed" % (field_type, system)) print "...fields created in %d systems out of %d empty no star systems" % (len(accepted), len(candidates))
def generate_a_planet(system, star_type, orbit, planet_density, galaxy_shape): """ Place a planet in an orbit of a system. Return True on success """ planet_size = calc_planet_size(star_type, orbit, planet_density, galaxy_shape) if planet_size not in planet_sizes: return False # ok, we want a planet, determine planet type and generate the planet planet_type = calc_planet_type(star_type, orbit, planet_size) if planet_type == fo.planetType.unknown: return False if fo.create_planet(planet_size, planet_type, system, orbit, "") == fo.invalid_object(): # create planet failed, report an error util.report_error( "Python generate_systems: create planet in system %d failed" % system) return False return True
def setup_package_group(package, pkg): with util.report_error(None, "Error saving package_group"): # there is a group, so use that group ID, or create one if 'groups' not in pkg: print "Warning: package %s has no groups key" % pkg['name'] return group = pkg['organization']["name"] pg = models.PackageGroup.query.filter_by(name=group).first() if pg is None: pg = create_package_group(group, handle_country=False) package.package_group = pg.id
def boost_version( boost_root ): """ Get boost version. If it didn't include boost or unknown versioned boost, it will return None. """ version_hpp = os.path.join( boost_root, 'boost', 'version.hpp' ) try: f = open(version_hpp) except: util.report_error('Cannot find boost/version.hpp. Please specify correct boost directory.') if f is None: return None version_lines = f.readlines() f.close() for line in version_lines: matched = re.match('\s*#\s*define\s+BOOST_VERSION\s+(?P<version>\d+)\s*', line ) if matched and not matched.groupdict() is None: if 'version' in matched.groupdict(): return version_object( int(matched.group('version')) ) return None
def windows_kit_dirs(): if systems.current() != systems.win32: report_error("Windows Kits only works on windows system.") kits = None close_key = None try: import _winreg winkit_key = _winreg.OpenKey( _winreg.HKEY_LOCAL_MACHINE, r"SOFTWARE\Microsoft\Windows Kits\Installed Roots") if winkit_key is None: return None def CloseKey(): _winreg.CloseKey(winkit_key) close_key = CloseKey kit_key_names = ["KitsRoot", "KitsRoot81"] kits = [] for kit_key_name in kit_key_names: try: kit_value = _winreg.QueryValueEx(winkit_key, kit_key_name)[0] kits.append(str(kit_value)) except Exception: continue if close_key: close_key() return kits except ImportError as e: if close_key: close_key() util.report_error( "_winreg library is not existed in Python on Win32 platform.") except WindowsError as e: if close_key: close_key() util.report_error( 'Windows error occurs: "%s" when reading Windows Kits reg.' % e.strerror)
def stitch_clusters(self, p1, p2, stitches): """ Stitch the clusters containing ''p1'' and ''p2'' together with the positions in ''stitches'' This assumes that the stitching is correctly formed. ''p1'' and ''p2'' are the closest positions between the clusters and the positions in ''stitches'' are only between ''p1'' and ''p2'' After stitch_clusters there will be one fewer clusters in the clusterer, unless there were fewer than two clusters to begin with. If ''p1'' or ''p2'' are not in clusters then stitch_clusters just stitches two random clusters together. This preserves the invariant that stitch_clusters always reduces the number of clusters. """ len_dset1 = None len_dset2 = None for len_dset in self.clusters: if p1 in len_dset[1]: len_dset1 = len_dset if p2 in len_dset[1]: len_dset2 = len_dset if not len_dset1 or not len_dset2: util.report_error( "p1 and p2 must be points in disjoint sets of positions") if len(self.clusters) < 2: return len_dset1, len_dset2 = list(self.clusters)[0:2] self.clusters.remove(len_dset1) self.clusters.remove(len_dset2) # Decorate the new cluster with its length to speed sorting new_set = len_dset1[1].union(len_dset2[1]).union(frozenset(stitches)) self.clusters.add((len(new_set), new_set))
def compile_home_system_list(num_home_systems, systems): """ Compiles a list with a requested number of home systems. """ # if the list of systems to choose home systems from is empty, report an error and return empty list if not systems: util.report_error("Python generate_home_system_list: no systems to choose from") return [] # calculate an initial minimal number of jumps that the home systems should be apart, # based on the total number of systems to choose from and the requested number of home systems min_jumps = max(int(float(len(systems)) / float(num_home_systems * 2)), 5) # try to find the home systems, decrease the min jumps until enough systems can be found, or the min jump distance # gets reduced to 0 (meaning we don't have enough systems to choose from at all) while min_jumps > 0: print "Trying to find", num_home_systems, "home systems that are at least", min_jumps, "jumps apart" # try to find home systems... home_systems = find_systems_with_min_jumps_between(num_home_systems, systems, min_jumps) # ...check if we got enough... if len(home_systems) >= num_home_systems: # ...yes, we got what we need, so let's break out of the loop break print "Home system min jump conflict: %d systems and %d empires, tried %d min jump and failed"\ % (len(systems), num_home_systems, min_jumps) # ...no, decrease the min jump distance and try again min_jumps -= 1 # check if the loop above delivered a list with enough home systems, or if it exited because the min jump distance # has been decreased to 0 without finding enough systems # in that case, our galaxy obviously is too crowded, report an error and return an empty list if len(home_systems) < num_home_systems: util.report_error("Python generate_home_system_list: requested %d homeworlds in a galaxy with %d systems" % (num_home_systems, len(systems))) return [] # make sure all our home systems have a "real" star (that is, a star that is not a neutron star, black hole, # or even no star at all) and at least one planet in it for home_system in home_systems: # if this home system has no "real" star, change star type to a randomly selected "real" star if fo.sys_get_star_type(home_system) not in starsystems.star_types_real: star_type = random.choice(starsystems.star_types_real) print "Home system", home_system, "has star type", fo.sys_get_star_type(home_system),\ ", changing that to", star_type fo.sys_set_star_type(home_system, star_type) # if this home 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 not fo.sys_get_planets(home_system): print "Home system", home_system, "has no planets, adding one" planet = fo.create_planet(random.choice(planets.planet_sizes_real), random.choice(planets.planet_types_real), home_system, random.randint(0, fo.sys_get_num_orbits(home_system) - 1), "") # if we couldn't create the planet, report an error and return an empty list if planet == fo.invalid_object(): util.report_error("Python generate_home_system_list: couldn't create planet in home system") return [] return home_systems
def stitch_clusters(self, p1, p2, stitches): """ Stitch the clusters containing ''p1'' and ''p2'' together with the positions in ''stitches'' This assumes that the stitching is correctly formed. ''p1'' and ''p2'' are the closest positions between the clusters and the positions in ''stitches'' are only between ''p1'' and ''p2'' After stitch_clusters there will be one fewer clusters in the clusterer, unless there were fewer than two clusters to begin with. If ''p1'' or ''p2'' are not in clusters then stitch_clusters just stitches two random clusters together. This preserves the invariant that stitch_clusters always reduces the number of clusters. """ len_dset1 = None len_dset2 = None for len_dset in self.clusters: if p1 in len_dset[1]: len_dset1 = len_dset if p2 in len_dset[1]: len_dset2 = len_dset if not len_dset1 or not len_dset2: util.report_error("p1 and p2 must be points in disjoint sets of positions") if len(self.clusters) < 2: return len_dset1, len_dset2 = list(self.clusters)[0:2] self.clusters.remove(len_dset1) self.clusters.remove(len_dset2) # Decorate the new cluster with its length to speed sorting new_set = len_dset1[1].union(len_dset2[1]).union(frozenset(stitches)) self.clusters.add((len(new_set), new_set))
def boost_version(boost_root): """ Get boost version. If it didn't include boost or unknown versioned boost, it will return None. """ version_hpp = os.path.join(boost_root, 'boost', 'version.hpp') try: f = open(version_hpp) except: util.report_error( 'Cannot find boost/version.hpp. Please specify correct boost directory.' ) if f is None: return None version_lines = f.readlines() f.close() for line in version_lines: matched = re.match( '\s*#\s*define\s+BOOST_VERSION\s+(?P<version>\d+)\s*', line) if matched and not matched.groupdict() is None: if 'version' in matched.groupdict(): return version_object(int(matched.group('version'))) return None
def copy_package_attributes(package, pkg): mapping = [ ("package_ckan_id", "id"), ("package_name", "name"), ("package_title", "title"), ("package_license_id", "license_id"), ("package_license", "license"), ("package_metadata_created", "metadata_created"), ("package_metadata_modified", "metadata_modified"), ("package_revision_id", "revision_id") ] for attr, key in mapping: with report_error(None, None): setattr(package, attr, pkg[key])
def generate_systems(pos_list, gsd): """ Generates and populates star systems at all positions in specified list. """ sys_list = [] for position in pos_list: star_type = pick_star_type(gsd.age) system = fo.create_system(star_type, "", position.x, position.y) if system == fo.invalid_object(): # create system failed, report an error and try to continue with next position util.report_error("Python generate_systems: create system at position (%f, %f) failed" % (position.x, position.y)) continue sys_list.append(system) for orbit in range(0, fo.sys_get_num_orbits(system) - 1): # check for each orbit if a planet shall be created by determining planet size planet_size = planets.calc_planet_size(star_type, orbit, gsd.planetDensity, gsd.shape) if planet_size in planets.planet_sizes: # ok, we want a planet, determine planet type and generate the planet planet_type = planets.calc_planet_type(star_type, orbit, planet_size) if fo.create_planet(planet_size, planet_type, system, orbit, "") == fo.invalid_object(): # create planet failed, report an error and try to continue with next orbit util.report_error("Python generate_systems: create planet in system %d failed" % system) return sys_list
def actually_save_manual_file(package_name): success = False directory = app.config['DATA_STORAGE_DIR'] package = models.Package.query.filter_by( package_name=package_name).first() url = fixURL(package.source_url) path = os.path.join(directory, package_name + '.xml') success = manage_download(path, url) with db.session.begin(): package.hash = 'retrieved' db.session.add(package) with report_error(" Package tested", " Couldn't test package %s" % package_name): dqruntests.start_testing(package_name)
def save_file(package_id, package_name, runtime_id, man_auto): if man_auto == 'auto': print "Trying to get auto package" registry = ckanclient.CkanClient(base_location=CKANurl) try: pkg = registry.package_entity_get(package_name) resources = pkg.get('resources', []) except Exception, e: print "Couldn't get URL from CKAN for package", package_name, e return print package_id, package_name, runtime_id if resources == []: return if len(resources) > 1: print "WARNING: multiple resources found; attempting to use first" url = resources[0]['url'] print url with report_error("Saving %s" % url, None): actually_save_file(package_name, url, pkg, runtime_id)
def create_universe(psd_map): """ Main universe generation function invoked from C++ code. """ print("Python Universe Generator") # fetch universe and player setup data gsd = PyGalaxySetupData(fo.get_galaxy_setup_data()) gsd.dump() total_players = len(psd_map) # initialize RNG h = int_hash(six.ensure_binary(gsd.seed, 'utf-8')) print("Using hashed seed", h) seed_rng(h) seed_pool = [random.random() for _ in range(100)] print("Seed pool:", seed_pool) # 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)) min_size = total_players * 3 if min_size > gsd.size: gsd.size = min_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)) # calculate star system positions seed_rng(seed_pool.pop()) system_positions = calc_star_system_positions(gsd) size = len(system_positions) print(gsd.shape, "Star system positions calculated, final number of systems:", size) # generate and populate systems seed_rng(seed_pool.pop()) systems = generate_systems(system_positions, gsd) print(len(systems), "systems generated and populated") # generate Starlanes seed_rng(seed_pool.pop()) fo.generate_starlanes(MAX_JUMPS_BETWEEN_SYSTEMS[gsd.starlane_frequency], MAX_STARLANE_LENGTH) print("Starlanes generated") print("Compile list of home systems...") seed_rng(seed_pool.pop()) home_systems = compile_home_system_list(total_players, systems, gsd) if not home_systems: err_msg = "Python create_universe: couldn't get any home systems, ABORTING!" report_error(err_msg) raise Exception(err_msg) print("Home systems:", home_systems) # set up empires for each player seed_rng(seed_pool.pop()) for empire, psd, home_system in zip(psd_map.keys(), psd_map.values(), home_systems): if not setup_empire(empire, psd.empire_name, home_system, psd.starting_species, psd.player_name): report_error( "Python create_universe: couldn't set up empire for player %s" % psd.player_name) # assign names to all star systems and their planets # this needs to be done after all systems have been generated and empire home systems have been set, as # only after all that is finished star types as well as planet sizes and types are fixed, and the naming # process depends on that print("Assign star system names") seed_rng(seed_pool.pop()) name_star_systems(systems) print("Set planet names") for system in systems: name_planets(system) print("Generating stationary fields in systems") seed_rng(seed_pool.pop()) generate_fields(systems) print("Generating Natives") seed_rng(seed_pool.pop()) generate_natives(gsd.native_frequency, systems, home_systems) print("Generating Space Monsters") seed_rng(seed_pool.pop()) generate_monsters(gsd.monster_frequency, systems) print("Distributing Starting Specials") seed_rng(seed_pool.pop()) distribute_specials(gsd.specials_frequency, fo.get_all_objects()) # finally, write some statistics to the log file print("############################################################") print("## Universe generation statistics ##") print("############################################################") universe_statistics.log_planet_count_dist(systems) print("############################################################") universe_statistics.log_planet_type_summary(systems) print("############################################################") universe_statistics.log_species_summary(gsd.native_frequency) print("############################################################") universe_statistics.log_monsters_summary(gsd.monster_frequency) print("############################################################") universe_statistics.log_specials_summary() print("############################################################") universe_statistics.log_systems() universe_statistics.log_planets() if error_list: print("Python Universe Generator completed with errors") return False else: print("Python Universe Generator completed successfully") return True
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() ])
def create_universe(psd_map): """ Main universe generation function invoked from C++ code. """ print "Python Universe Generator" # fetch universe and player setup data gsd = fo.get_galaxy_setup_data() total_players = len(psd_map) # initialize RNG seed_rng(gsd.seed) seed_pool = [random.random() for _ in range(100)] # 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) size = max(gsd.size, (total_players * 3)) if size > gsd.size: # gsd.size = 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" % (size, total_players) # calculate star system positions seed_rng(seed_pool.pop()) system_positions = calc_star_system_positions(gsd.shape, size) size = len(system_positions) print gsd.shape, "Star system positions calculated, final number of systems:", size # generate and populate systems seed_rng(seed_pool.pop()) systems = generate_systems(system_positions, gsd) print len(systems), "systems generated and populated" # generate Starlanes seed_rng(seed_pool.pop()) fo.generate_starlanes(gsd.starlaneFrequency) print "Starlanes generated" print "Compile list of home systems..." seed_rng(seed_pool.pop()) home_systems = compile_home_system_list(total_players, systems) if not home_systems: err_msg = "Python create_universe: couldn't get any home systems, ABORTING!" report_error(err_msg) raise Exception(err_msg) print "Home systems:", home_systems # set up empires for each player seed_rng(seed_pool.pop()) for empire, psd, home_system in zip(psd_map.keys(), psd_map.values(), home_systems): if not setup_empire(empire, psd.empire_name, home_system, psd.starting_species, psd.player_name): report_error("Python create_universe: couldn't set up empire for player %s" % psd.player_name) # assign names to all star systems and their planets # this needs to be done after all systems have been generated and empire home systems have been set, as # only after all that is finished star types as well as planet sizes and types are fixed, and the naming # process depends on that print "Assign star system names" seed_rng(seed_pool.pop()) name_star_systems(systems) print "Set planet names" for system in systems: name_planets(system) print "Generating Natives" seed_rng(seed_pool.pop()) generate_natives(gsd.nativeFrequency, systems, home_systems) print "Generating Space Monsters" seed_rng(seed_pool.pop()) generate_monsters(gsd.monsterFrequency, systems) print "Distributing Starting Specials" seed_rng(seed_pool.pop()) distribute_specials(gsd.specialsFrequency, fo.get_all_objects()) # finally, write some statistics to the log file print "############################################################" print "## Universe generation statistics ##" print "############################################################" statistics.log_planet_count_dist(systems) print "############################################################" statistics.log_planet_type_summary(systems) print "############################################################" statistics.log_species_summary() print "############################################################" statistics.log_monsters_summary() print "############################################################" statistics.log_specials_summary() print "############################################################" if error_list: print "Python Universe Generator completed with errors" return False else: print "Python Universe Generator completed successfully" return True
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 inverse_monster_chance = fo.monster_frequency(monster_freq) # as the value in the universe table is higher for a lower frequency, we have to invert it # exception: a value of 0 means no monsters, in this case return immediately if inverse_monster_chance <= 0: return basic_chance = 1.0 / float(inverse_monster_chance) 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 with a spawn counter in a dict # this counter will be set to the spawn limit initially and decreased every time the monster fleet is spawned fleet_plans = {fp: fp.spawn_limit() for fp in fo.load_monster_fleet_plan_list("space_monster_spawn_fleets.txt") 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 = dict(zip(["KRAKEN_NEST_SPECIAL", "SNOWFLAKE_NEST_SPECIAL", "JUGGERNAUT_NEST_SPECIAL"], ["SM_KRAKEN_1", "SM_SNOWFLAKE_1", "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, limit in fleet_plans.iteritems() if fp.name() in tracked_plan_counts} tracked_nest_valid_locations = {nest: 0 for nest in nest_name_map} if not fleet_plans: return # 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(),\ "/ spawn limit", fleet_plan.spawn_limit(),\ "/ effective chance", basic_chance * fleet_plan.spawn_rate(),\ "/ can be spawned at", len([s for s in systems if fleet_plan.location(s)]), "systems" if fleet_plan.name() in nest_name_map.values(): statistics.tracked_monsters_chance[fleet_plan.name()] = basic_chance * fleet_plan.spawn_rate() # 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 suitable_fleet_plans = [fp for fp, counter in fleet_plans.iteritems() if counter and fp.location(system)] # 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 print "Spawn", fleet_plan.name(), "at", fo.get_name(system) # decrement counter for this monster fleet fleet_plans[fleet_plan] -= 1 # create monster fleet monster_fleet = fo.create_monster_fleet(system) # if fleet creation fails, report an error and try to continue with next system if monster_fleet == fo.invalid_object(): util.report_error("Python generate_monsters: unable to create new monster fleet %s" % fleet_plan.name()) continue # add monsters to fleet for design in fleet_plan.ship_designs(): # create monster, if creation fails, report an error and try to continue with the next design if fo.create_monster(design, monster_fleet) == fo.invalid_object(): util.report_error("Python generate_monsters: unable to create monster %s" % design) 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 fleet_plans.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()])
def compile_home_system_list(num_home_systems, systems): """ Compiles a list with a requested number of home systems. """ print "Compile home system list:", num_home_systems, "systems requested" # if the list of systems to choose home systems from is empty, report an error and return empty list if not systems: report_error("Python generate_home_system_list: no systems to choose from") return [] # calculate an initial minimal number of jumps that the home systems should be apart, # based on the total number of systems to choose from and the requested number of home systems # Don't let min_jumps be larger than 10, because a larger number is really not at all needed and with large # galaxies an excessive amount of time can be used in failed attempts min_jumps = min(10, max(int(float(len(systems)) / float(num_home_systems * 2)), 5)) # home systems must have a certain minimum of systems in their near vicinity # we will try to select our home systems from systems that match this criteria, if that fails, we will select our # home systems from all systems and add the missing number planets to the systems in their vicinity afterwards # the minimum planet limit and the jump range that defines the "near vicinity" are controlled by the # HS_* option constants in options.py (see there) # lets start by filtering out all systems from the pool we got passed into this function that match the criteria filtered_pool = [s for s in systems if has_min_planets_in_vicinity(s)] print "Filtering out systems that meet the minimum planets in the near vicinity condition yielded",\ len(filtered_pool), "systems" print "Using this as the preferred pool for home system selection" # now try to pick the requested number of home systems by calling find_home_systems # this function takes two pools, a "complete" pool and one with preferred systems # it will try to pick the home systems from the preferred pool first, so pass our filtered pool as preferred pool home_systems = find_home_systems(num_home_systems, systems, filtered_pool, min_jumps) # check if the selection process delivered a list with enough home systems # if not, our galaxy obviously is too crowded, report an error and return an empty list if len(home_systems) < num_home_systems: report_error("Python generate_home_system_list: requested %d homeworlds in a galaxy with %d systems" % (num_home_systems, len(systems))) return [] # check if we got more home systems than we requested if len(home_systems) > num_home_systems: # yes: calculate the number of planets in the near vicinity of each system # and store that value with each system in a map hs_planets_in_vicinity_map = {s: count_planets_in_systems(get_systems_within_jumps(s, HS_VICINITY_RANGE)) for s in home_systems} # sort the home systems by the number of planets in their near vicinity using the map # now only pick the number of home systems we need, taking those with the highest number of planets home_systems = sorted(home_systems, key=hs_planets_in_vicinity_map.get, reverse=True)[:num_home_systems] # make sure all our home systems have a "real" star (that is, a star that is not a neutron star, black hole, # or even no star at all) and at least one planet in it for home_system in home_systems: # if this home system has no "real" star, change star type to a randomly selected "real" star if fo.sys_get_star_type(home_system) not in star_types_real: star_type = random.choice(star_types_real) print "Home system", home_system, "has star type", fo.sys_get_star_type(home_system),\ ", changing that to", star_type fo.sys_set_star_type(home_system, star_type) # if this home 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 not fo.sys_get_planets(home_system): print "Home system", home_system, "has no planets, adding one" planet = fo.create_planet(random.choice(planet_sizes_real), random.choice(planet_types_real), home_system, random.randint(0, fo.sys_get_num_orbits(home_system) - 1), "") # if we couldn't create the planet, report an error and return an empty list if planet == fo.invalid_object(): report_error("Python generate_home_system_list: couldn't create planet in home system") return [] # finally, check again if all home systems meet the criteria of having the required minimum number of planets # within their near vicinity, if not, add the missing number of planets print "Checking if home systems have the required minimum of planets within the near vicinity..." for home_system in home_systems: # calculate the number of missing planets, and add them if this number is > 0 systems_in_vicinity = get_systems_within_jumps(home_system, HS_VICINITY_RANGE) num_systems_in_vicinity = len(systems_in_vicinity) num_planets_in_vicinity = count_planets_in_systems(systems_in_vicinity) num_planets_to_add = min_planets_in_vicinity_limit(num_systems_in_vicinity) - num_planets_in_vicinity print "Home system", home_system, "has", num_systems_in_vicinity, "systems and", num_planets_in_vicinity,\ "planets in the near vicinity, required minimum:", min_planets_in_vicinity_limit(num_systems_in_vicinity) if num_planets_to_add > 0: systems_in_vicinity.remove(home_system) # don't add planets to the home system, so remove it from the list add_planets_to_vicinity(systems_in_vicinity, num_planets_to_add) # as we've sorted the home system list before, lets shuffle it to ensure random order and return random.shuffle(home_systems) return home_systems
def setup_empire(empire, empire_name, home_system, starting_species, player_name): """ Sets up various aspects of an empire, like empire name, homeworld, etc. """ # set empire name, if no one is given, pick one randomly if not empire_name: print "No empire name set for player", player_name, ", picking one randomly" empire_name = next(empire_name_generator) fo.empire_set_name(empire, empire_name) print "Empire name for player", player_name, "is", empire_name # check starting species, if no one is given, pick one randomly if starting_species == "RANDOM" or not starting_species: print "Picking random starting species for player", player_name starting_species = next(starting_species_pool) print "Starting species for player", player_name, "is", starting_species statistics.empire_species[starting_species] += 1 # pick a planet from the specified home system as homeworld planet_list = fo.sys_get_planets(home_system) # if the system is empty, report an error and return false, indicating failure if not planet_list: report_error("Python setup_empire: got home system with no planets") return False homeworld = random.choice(planet_list) # set selected planet as empire homeworld with selected starting species fo.empire_set_homeworld(empire, homeworld, starting_species) # set homeworld focus # check if the preferred focus for the starting species is among # the foci available on the homeworld planet available_foci = fo.planet_available_foci(homeworld) preferred_focus = fo.species_preferred_focus(starting_species) if preferred_focus in available_foci: # if yes, set the homeworld focus to the preferred focus print "Player", player_name, ": setting preferred focus", preferred_focus, "on homeworld" fo.planet_set_focus(homeworld, preferred_focus) elif len(available_foci) > 0: # if no, and there is at least one available focus, # just take the first of the list if preferred_focus == "": print "Player", player_name, ": starting species", starting_species, "has no preferred focus, using",\ available_foci[0], "instead" else: print "Player", player_name, ": preferred focus", preferred_focus, "for starting species",\ starting_species, "not available on homeworld, using", available_foci[0], "instead" fo.planet_set_focus(homeworld, available_foci[0]) else: # if no focus is available on the homeworld, don't set any focus print "Player", player_name, ": no available foci on homeworld for starting species", starting_species # give homeworld starting buildings # use the list provided in scripting/starting_unlocks/buildings.inf print "Player", player_name, ": add starting buildings to homeworld" for building in load_string_list(os.path.join(fo.get_resource_dir(), "scripting/starting_unlocks/buildings.inf")): fo.create_building(building, homeworld, empire) # unlock starting techs, buildings, hulls, ship parts, etc. # use default content file print "Player", player_name, ": add unlocked items" for item in fo.load_item_spec_list(): fo.empire_unlock_item(empire, item.type, item.name) # add premade ship designs to empire print "Player", player_name, ": add premade ship designs" for ship_design in fo.design_get_premade_list(): fo.empire_add_ship_design(empire, ship_design) # add starting fleets to empire # use default content file print "Player", player_name, ": add starting fleets" fleet_plans = fo.load_fleet_plan_list() for fleet_plan in fleet_plans: # first, create the fleet fleet = fo.create_fleet(fleet_plan.name(), home_system, empire) # if the fleet couldn't be created, report an error and try to continue with the next fleet plan if fleet == fo.invalid_object(): report_error("Python setup empire: couldn't create fleet %s" % fleet_plan.name()) continue # second, iterate over the list of ship design names in the fleet plan for ship_design in fleet_plan.ship_designs(): # create a ship in the fleet # if the ship couldn't be created, report an error and try to continue with the next ship design if fo.create_ship("", ship_design, starting_species, fleet) == fo.invalid_object(): report_error("Python setup empire: couldn't create ship %s for fleet %s" % (ship_design, fleet_plan.name())) return True
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())
def setup_empire(empire, empire_name, home_system, starting_species, player_name): """ Sets up various aspects of an empire, like empire name, homeworld, etc. """ # set empire name, if no one is given, pick one randomly if not empire_name: print "No empire name set for player", player_name, ", picking one randomly" empire_name = next(empire_name_generator) fo.empire_set_name(empire, empire_name) print "Empire name for player", player_name, "is", empire_name # check starting species, if no one is given, pick one randomly if starting_species == "RANDOM" or not starting_species: print "Picking random starting species for player", player_name starting_species = next(starting_species_pool) print "Starting species for player", player_name, "is", starting_species universe_statistics.empire_species[starting_species] += 1 # pick a planet from the specified home system as homeworld planet_list = fo.sys_get_planets(home_system) # if the system is empty, report an error and return false, indicating failure if not planet_list: report_error("Python setup_empire: got home system with no planets") return False homeworld = random.choice(planet_list) # set selected planet as empire homeworld with selected starting species fo.empire_set_homeworld(empire, homeworld, starting_species) # set homeworld focus # check if the preferred focus for the starting species is among # the foci available on the homeworld planet available_foci = fo.planet_available_foci(homeworld) preferred_focus = fo.species_preferred_focus(starting_species) if preferred_focus in available_foci: # if yes, set the homeworld focus to the preferred focus print "Player", player_name, ": setting preferred focus", preferred_focus, "on homeworld" fo.planet_set_focus(homeworld, preferred_focus) elif len(available_foci) > 0: # if no, and there is at least one available focus, # just take the first of the list if preferred_focus == "": print "Player", player_name, ": starting species", starting_species, "has no preferred focus, using",\ available_foci[0], "instead" else: print "Player", player_name, ": preferred focus", preferred_focus, "for starting species",\ starting_species, "not available on homeworld, using", available_foci[0], "instead" fo.planet_set_focus(homeworld, available_foci[0]) else: # if no focus is available on the homeworld, don't set any focus print "Player", player_name, ": no available foci on homeworld for starting species", starting_species # give homeworld starting buildings print "Player", player_name, ": add starting buildings to homeworld" for item in fo.load_starting_buildings(): fo.create_building(item.name, homeworld, empire) # unlock starting techs, buildings, hulls, ship parts, etc. # use default content file print "Player", player_name, ": add unlocked items" for item in fo.load_item_spec_list(): fo.empire_unlock_item(empire, item.type, item.name) # add premade ship designs to empire print "Player", player_name, ": add premade ship designs" for ship_design in fo.design_get_premade_list(): fo.empire_add_ship_design(empire, ship_design) # add starting fleets to empire # use default content file print "Player", player_name, ": add starting fleets" fleet_plans = fo.load_fleet_plan_list() for fleet_plan in fleet_plans: # first, create the fleet fleet = fo.create_fleet(fleet_plan.name(), home_system, empire) # if the fleet couldn't be created, report an error and try to continue with the next fleet plan if fleet == fo.invalid_object(): report_error("Python setup empire: couldn't create fleet %s" % fleet_plan.name()) continue # second, iterate over the list of ship design names in the fleet plan for ship_design in fleet_plan.ship_designs(): # create a ship in the fleet # if the ship couldn't be created, report an error and try to continue with the next ship design if fo.create_ship("", ship_design, starting_species, fleet) == fo.invalid_object(): report_error( "Python setup empire: couldn't create ship %s for fleet %s" % (ship_design, fleet_plan.name())) return True
def add_planets_to_vicinity(vicinity, num_planets, gsd): """ Adds the specified number of planets to the specified systems. """ print "Adding", num_planets, "planets to the following systems:", vicinity # first, compile a list containing all the free orbits in the specified systems # begin with adding the free orbits of all systems that have a real star (that is, no neutron star, black hole, # and not no star), if that isn't enough, also one, by one, add the free orbits of neutron star, black hole and # no star systems (in that order) until we have enough free orbits # for that, we use this list of tuples # the first tuple contains all real star types, the following tuples the neutron, black hole and no star types, # so we can iterate over this list and only add the free orbits of systems that match the respective star types # each step # this way we can prioritize the systems we want to add planets to by star type acceptable_star_types_list = [ star_types_real, (fo.starType.noStar, ), (fo.starType.neutron, ), (fo.starType.blackHole, ) ] # store the free orbits as a list of tuples of (system, orbit) free_orbits_map = [] # now, iterate over the list of acceptable star types for acceptable_star_types in acceptable_star_types_list: # check all systems in the list of systems we got passed into this function for system in vicinity: # if this system has a star type we want to accept in this step, add its free orbits to our list if fo.sys_get_star_type(system) in acceptable_star_types: free_orbits_map.extend([ (system, orbit) for orbit in fo.sys_free_orbits(system) ]) # check if we got enough free orbits after completing this step # we want 4 times as much free orbits as planets we want to add, that means each system shouldn't get more # than 2-3 additional planets on average if len(free_orbits_map) > (num_planets * 4): break # if we got less free orbits than planets that should be added, something is wrong # in that case abort and log an error if len(free_orbits_map) < num_planets: report_error( "Python add_planets_to_vicinity: less free orbits than planets to add - cancelled" ) print "...free orbits available:", free_orbits_map # as we will pop the free orbits from this list afterwards, shuffle it to randomize the order of the orbits random.shuffle(free_orbits_map) # add the requested number of planets while num_planets > 0: # get the next free orbit from the list we just compiled system, orbit = free_orbits_map.pop() # check the star type of the system containing the orbit we got star_type = fo.sys_get_star_type(system) if star_type in [fo.starType.noStar, fo.starType.blackHole]: # if it is a black hole or has no star, change the star type # pick a star type, continue until we get a real star # don't accept neutron, black hole or no star print "...system picked to add a planet has star type", star_type while star_type not in star_types_real: star_type = pick_star_type(gsd.age) print "...change that to", star_type fo.sys_set_star_type(system, star_type) # pick a planet size, continue until we get a size that matches the HS_ACCEPTABLE_PLANET_SIZES option planet_size = fo.planetSize.unknown while planet_size not in HS_ACCEPTABLE_PLANET_SIZES: planet_size = calc_planet_size(star_type, orbit, fo.galaxySetupOption.high, gsd.shape) # pick an according planet type planet_type = calc_planet_type(star_type, orbit, planet_size) # finally, create the planet in the system and orbit we got print "...adding", planet_size, planet_type, "planet to system", system if fo.create_planet(planet_size, planet_type, system, orbit, "") == fo.invalid_object(): report_error( "Python add_planets_to_vicinity: create planet in system %d failed" % system) # continue with next planet num_planets -= 1
def __decompress(self, source_path, dist_parent): try: o = subprocess.check_output([self.decompressor.store_path, "x", source_path, '-o%s' % dist_parent]) except: util.report_error("Found error while decompressing <%s>" % source_path)
def compile_home_system_list(num_home_systems, systems): """ Compiles a list with a requested number of home systems. """ print "Compile home system list:", num_home_systems, "systems requested" # if the list of systems to choose home systems from is empty, report an error and return empty list if not systems: report_error( "Python generate_home_system_list: no systems to choose from") return [] # calculate an initial minimal number of jumps that the home systems should be apart, # based on the total number of systems to choose from and the requested number of home systems # Don't let min_jumps be larger than 10, because a larger number is really not at all needed and with large # galaxies an excessive amount of time can be used in failed attempts min_jumps = min( 10, max(int(float(len(systems)) / float(num_home_systems * 2)), 5)) # home systems must have a certain minimum of systems in their near vicinity # we will try to select our home systems from systems that match this criteria, if that fails, we will select our # home systems from all systems and add the missing number planets to the systems in their vicinity afterwards # the minimum planet limit and the jump range that defines the "near vicinity" are controlled by the # HS_* option constants in options.py (see there) # lets start by filtering out all systems from the pool we got passed into this function that match the criteria filtered_pool = [s for s in systems if has_min_planets_in_vicinity(s)] print "Filtering out systems that meet the minimum planets in the near vicinity condition yielded",\ len(filtered_pool), "systems" print "Using this as the preferred pool for home system selection" # now try to pick the requested number of home systems by calling find_home_systems # this function takes two pools, a "complete" pool and one with preferred systems # it will try to pick the home systems from the preferred pool first, so pass our filtered pool as preferred pool home_systems = find_home_systems(num_home_systems, systems, filtered_pool, min_jumps) # check if the selection process delivered a list with enough home systems # if not, our galaxy obviously is too crowded, report an error and return an empty list if len(home_systems) < num_home_systems: report_error( "Python generate_home_system_list: requested %d homeworlds in a galaxy with %d systems" % (num_home_systems, len(systems))) return [] # check if we got more home systems than we requested if len(home_systems) > num_home_systems: # yes: calculate the number of planets in the near vicinity of each system # and store that value with each system in a map hs_planets_in_vicinity_map = { s: count_planets_in_systems( get_systems_within_jumps(s, HS_VICINITY_RANGE)) for s in home_systems } # sort the home systems by the number of planets in their near vicinity using the map # now only pick the number of home systems we need, taking those with the highest number of planets home_systems = sorted(home_systems, key=hs_planets_in_vicinity_map.get, reverse=True)[:num_home_systems] # make sure all our home systems have a "real" star (that is, a star that is not a neutron star, black hole, # or even no star at all) and at least one planet in it for home_system in home_systems: # if this home system has no "real" star, change star type to a randomly selected "real" star if fo.sys_get_star_type(home_system) not in star_types_real: star_type = random.choice(star_types_real) print "Home system", home_system, "has star type", fo.sys_get_star_type(home_system),\ ", changing that to", star_type fo.sys_set_star_type(home_system, star_type) # if this home 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 not fo.sys_get_planets(home_system): print "Home system", home_system, "has no planets, adding one" planet = fo.create_planet( random.choice(planet_sizes_real), random.choice(planet_types_real), home_system, random.randint(0, fo.sys_get_num_orbits(home_system) - 1), "") # if we couldn't create the planet, report an error and return an empty list if planet == fo.invalid_object(): report_error( "Python generate_home_system_list: couldn't create planet in home system" ) return [] # finally, check again if all home systems meet the criteria of having the required minimum number of planets # within their near vicinity, if not, add the missing number of planets print "Checking if home systems have the required minimum of planets within the near vicinity..." for home_system in home_systems: # calculate the number of missing planets, and add them if this number is > 0 systems_in_vicinity = get_systems_within_jumps(home_system, HS_VICINITY_RANGE) num_systems_in_vicinity = len(systems_in_vicinity) num_planets_in_vicinity = count_planets_in_systems(systems_in_vicinity) num_planets_to_add = min_planets_in_vicinity_limit( num_systems_in_vicinity) - num_planets_in_vicinity print "Home system", home_system, "has", num_systems_in_vicinity, "systems and", num_planets_in_vicinity,\ "planets in the near vicinity, required minimum:", min_planets_in_vicinity_limit(num_systems_in_vicinity) if num_planets_to_add > 0: systems_in_vicinity.remove( home_system ) # don't add planets to the home system, so remove it from the list add_planets_to_vicinity(systems_in_vicinity, num_planets_to_add) # as we've sorted the home system list before, lets shuffle it to ensure random order and return random.shuffle(home_systems) return home_systems
pkg = registry.package_entity_get(package_name) resources = pkg.get('resources', []) except Exception, e: print "Couldn't get URL from CKAN for package", package_name, e return print package_id, package_name, runtime_id if resources == []: return if len(resources) > 1: print "WARNING: multiple resources found; attempting to use first" url = resources[0]['url'] print url with report_error("Saving %s" % url, None): actually_save_file(package_name, url, pkg, runtime_id) def dequeue_download(body): args = json.loads(body) try: save_file(args['package_id'], args['package_name'], args['runtime_id']) except Exception: print sys.exc_info() print "Exception!!", e print def callback_fn(ch, method, properties, body):
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 inverse_monster_chance = fo.monster_frequency(monster_freq) # as the value in the universe table is higher for a lower frequency, we have to invert it # exception: a value of 0 means no monsters, in this case return immediately if inverse_monster_chance <= 0: return basic_chance = 1.0 / float(inverse_monster_chance) 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 with a spawn counter in a dict # this counter will be set to the spawn limit initially and decreased every time the monster fleet is spawned fleet_plans = { fp: fp.spawn_limit() for fp in fo.load_monster_fleet_plan_list( "space_monster_spawn_fleets.txt") 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 = dict( zip([ "KRAKEN_NEST_SPECIAL", "SNOWFLAKE_NEST_SPECIAL", "JUGGERNAUT_NEST_SPECIAL" ], ["SM_KRAKEN_1", "SM_SNOWFLAKE_1", "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, limit in fleet_plans.iteritems() if fp.name() in tracked_plan_counts } tracked_nest_valid_locations = {nest: 0 for nest in nest_name_map} if not fleet_plans: return # 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) < 1000: 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() # 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 suitable_fleet_plans = [ fp for fp, counter in fleet_plans.iteritems() if counter and fp.location(system) ] # 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 print "Spawn", fleet_plan.name(), "at", fo.get_name(system) # decrement counter for this monster fleet fleet_plans[fleet_plan] -= 1 # create monster fleet monster_fleet = fo.create_monster_fleet(system) # if fleet creation fails, report an error and try to continue with next system if monster_fleet == fo.invalid_object(): util.report_error( "Python generate_monsters: unable to create new monster fleet %s" % fleet_plan.name()) continue # add monsters to fleet for design in fleet_plan.ship_designs(): # create monster, if creation fails, report an error and try to continue with the next design if fo.create_monster(design, monster_fleet) == fo.invalid_object(): util.report_error( "Python generate_monsters: unable to create monster %s" % design) 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 fleet_plans.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() ])
def add_planets_to_vicinity(vicinity, num_planets, gsd): """ Adds the specified number of planets to the specified systems. """ print "Adding", num_planets, "planets to the following systems:", vicinity # first, compile a list containing all the free orbits in the specified systems # begin with adding the free orbits of all systems that have a real star (that is, no neutron star, black hole, # and not no star), if that isn't enough, also one, by one, add the free orbits of neutron star, black hole and # no star systems (in that order) until we have enough free orbits # for that, we use this list of tuples # the first tuple contains all real star types, the following tuples the neutron, black hole and no star types, # so we can iterate over this list and only add the free orbits of systems that match the respective star types # each step # this way we can prioritize the systems we want to add planets to by star type acceptable_star_types_list = [ star_types_real, (fo.starType.noStar,), (fo.starType.neutron,), (fo.starType.blackHole,) ] # store the free orbits as a list of tuples of (system, orbit) free_orbits_map = [] # now, iterate over the list of acceptable star types for acceptable_star_types in acceptable_star_types_list: # check all systems in the list of systems we got passed into this function for system in vicinity: # if this system has a star type we want to accept in this step, add its free orbits to our list if fo.sys_get_star_type(system) in acceptable_star_types: free_orbits_map.extend([(system, orbit) for orbit in fo.sys_free_orbits(system)]) # check if we got enough free orbits after completing this step # we want 4 times as much free orbits as planets we want to add, that means each system shouldn't get more # than 2-3 additional planets on average if len(free_orbits_map) > (num_planets * 4): break # if we got less free orbits than planets that should be added, something is wrong # in that case abort and log an error if len(free_orbits_map) < num_planets: report_error("Python add_planets_to_vicinity: less free orbits than planets to add - cancelled") print "...free orbits available:", free_orbits_map # as we will pop the free orbits from this list afterwards, shuffle it to randomize the order of the orbits random.shuffle(free_orbits_map) # add the requested number of planets while num_planets > 0: # get the next free orbit from the list we just compiled system, orbit = free_orbits_map.pop() # check the star type of the system containing the orbit we got star_type = fo.sys_get_star_type(system) if star_type in [fo.starType.noStar, fo.starType.blackHole]: # if it is a black hole or has no star, change the star type # pick a star type, continue until we get a real star # don't accept neutron, black hole or no star print "...system picked to add a planet has star type", star_type while star_type not in star_types_real: star_type = pick_star_type(gsd.age) print "...change that to", star_type fo.sys_set_star_type(system, star_type) # pick a planet size, continue until we get a size that matches the HS_ACCEPTABLE_PLANET_SIZES option planet_size = fo.planetSize.unknown while planet_size not in HS_ACCEPTABLE_PLANET_SIZES: planet_size = calc_planet_size(star_type, orbit, fo.galaxySetupOption.high, gsd.shape) # pick an according planet type planet_type = calc_planet_type(star_type, orbit, planet_size) # finally, create the planet in the system and orbit we got print "...adding", planet_size, planet_type, "planet to system", system if fo.create_planet(planet_size, planet_type, system, orbit, "") == fo.invalid_object(): report_error("Python add_planets_to_vicinity: create planet in system %d failed" % system) # continue with next planet num_planets -= 1
def compile_home_system_list(num_home_systems, systems, gsd): """ Compiles a list with a requested number of home systems. """ print "Compile home system list:", num_home_systems, "systems requested" # if the list of systems to choose home systems from is empty, report an error and return empty list if not systems: report_error( "Python generate_home_system_list: no systems to choose from") return [] # calculate an initial minimal number of jumps that the home systems should be apart, # based on the total number of systems to choose from and the requested number of home systems # don't let min_jumps be either: # a.) larger than a defined limit, because an unreasonably large number is really not at all needed, # and with large galaxies an excessive amount of time can be used in failed attempts # b.) lower than the minimum jump distance limit that should be considered high priority (see options.py), # otherwise no attempt at all would be made to enforce the other requirements for home systems (see below) min_jumps = min( HS_MAX_JUMP_DISTANCE_LIMIT, max(int(float(len(systems)) / float(num_home_systems * 2)), HS_MIN_DISTANCE_PRIORITY_LIMIT)) # home systems must have a certain minimum of systems and planets in their near vicinity # we will try to select our home systems from systems that match this criteria, if that fails, we will select our # home systems from all systems and add the missing number planets to the systems in their vicinity afterwards # the minimum system and planet limit and the jump range that defines the "near vicinity" are controlled by the # HS_* option constants in options.py (see there) # we start by building two additional pools of systems: one that contains all systems that match the criteria # completely (meets the min systems and planets limit), and one that contains all systems that match the criteria # at least partially (meets the min systems limit) pool_matching_sys_and_planet_limit = [] pool_matching_sys_limit = [] for system in systems: systems_in_vicinity = fo.systems_within_jumps_unordered( HS_VICINITY_RANGE, [system]) if len(systems_in_vicinity) >= HS_MIN_SYSTEMS_IN_VICINITY: pool_matching_sys_limit.append(system) if count_planets_in_systems( systems_in_vicinity) >= min_planets_in_vicinity_limit( len(systems_in_vicinity)): pool_matching_sys_and_planet_limit.append(system) print( len(pool_matching_sys_and_planet_limit), "systems meet the min systems and planets in the near vicinity limit") print len(pool_matching_sys_limit ), "systems meet the min systems in the near vicinity limit" # now try to pick the requested number of home systems # we will do this by calling find_home_systems, which takes a list of tuples defining the pools from which to pick # the home systems; it will use the pools in the order in which they appear in the list, so put better pools first # we will make two attempts: the first one with the filtered pools we just created, and tell find_home_systems # to start with the min_jumps jumps distance we calculated above, but not to go lower than # HS_MIN_DISTANCE_PRIORITY_LIMIT print "First attempt: trying to pick home systems from the filtered pools of preferred systems" pool_list = [ # the better pool is of course the one where all systems meet BOTH the min systems and planets limit (pool_matching_sys_and_planet_limit, "pool of systems that meet both the min systems and planets limit"), # next the less preferred pool where all systems at least meets the min systems limit # specify 0 as number of requested home systems to pick as much systems as possible (pool_matching_sys_limit, "pool of systems that meet at least the min systems limit"), ] home_systems = find_home_systems(num_home_systems, pool_list, min_jumps, HS_MIN_DISTANCE_PRIORITY_LIMIT) # check if the first attempt delivered a list with enough home systems # if not, we make our second attempt, this time disregarding the filtered pools and using all systems, starting # again with the min_jumps jump distance limit and specifying 0 as number of required home systems to pick as much # systems as possible if len(home_systems) < num_home_systems: print "Second attempt: trying to pick home systems from all systems" home_systems = find_home_systems(num_home_systems, [(systems, "complete pool")], min_jumps, 1) # check if the selection process delivered a list with enough home systems # if not, our galaxy obviously is too crowded, report an error and return an empty list if len(home_systems) < num_home_systems: report_error( "Python generate_home_system_list: requested %d homeworlds in a galaxy with %d systems" % (num_home_systems, len(systems))) return [] # check if we got more home systems than we requested if len(home_systems) > num_home_systems: # yes: calculate the number of planets in the near vicinity of each system # and store that value with each system in a map hs_planets_in_vicinity_map = { s: calculate_home_system_merit(s) for s in home_systems } # sort the home systems by the number of planets in their near vicinity using the map # now only pick the number of home systems we need, taking those with the highest number of planets home_systems = sorted(home_systems, key=hs_planets_in_vicinity_map.get, reverse=True)[:num_home_systems] # make sure all our home systems have a "real" star (that is, a star that is not a neutron star, black hole, # or even no star at all) and at least one planet in it for home_system in home_systems: # if this home system has no "real" star, change star type to a randomly selected "real" star if fo.sys_get_star_type(home_system) not in star_types_real: star_type = random.choice(star_types_real) print "Home system", home_system, "has star type", fo.sys_get_star_type(home_system),\ ", changing that to", star_type fo.sys_set_star_type(home_system, star_type) # if this home 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 not fo.sys_get_planets(home_system): print "Home system", home_system, "has no planets, adding one" planet = fo.create_planet( random.choice(planet_sizes_real), random.choice(planet_types_real), home_system, random.randint(0, fo.sys_get_num_orbits(home_system) - 1), "") # if we couldn't create the planet, report an error and return an empty list if planet == fo.invalid_object(): report_error( "Python generate_home_system_list: couldn't create planet in home system" ) return [] # finally, check again if all home systems meet the criteria of having the required minimum number of planets # within their near vicinity, if not, add the missing number of planets print "Checking if home systems have the required minimum of planets within the near vicinity..." for home_system in home_systems: # calculate the number of missing planets, and add them if this number is > 0 systems_in_vicinity = fo.systems_within_jumps_unordered( HS_VICINITY_RANGE, [home_system]) num_systems_in_vicinity = len(systems_in_vicinity) num_planets_in_vicinity = count_planets_in_systems(systems_in_vicinity) num_planets_to_add = min_planets_in_vicinity_limit( num_systems_in_vicinity) - num_planets_in_vicinity print "Home system", home_system, "has", num_systems_in_vicinity, "systems and", num_planets_in_vicinity,\ "planets in the near vicinity, required minimum:", min_planets_in_vicinity_limit(num_systems_in_vicinity) if num_planets_to_add > 0: systems_in_vicinity.remove( home_system ) # don't add planets to the home system, so remove it from the list # sort the systems_in_vicinity before adding, since the C++ engine doesn't guarrantee the same # platform independence as python. add_planets_to_vicinity(sorted(systems_in_vicinity), num_planets_to_add, gsd) # as we've sorted the home system list before, lets shuffle it to ensure random order and return random.shuffle(home_systems) return home_systems
def compile_home_system_list(num_home_systems, systems, gsd): """ Compiles a list with a requested number of home systems. """ print "Compile home system list:", num_home_systems, "systems requested" # if the list of systems to choose home systems from is empty, report an error and return empty list if not systems: report_error("Python generate_home_system_list: no systems to choose from") return [] # calculate an initial minimal number of jumps that the home systems should be apart, # based on the total number of systems to choose from and the requested number of home systems # don't let min_jumps be either: # a.) larger than a defined limit, because an unreasonably large number is really not at all needed, # and with large galaxies an excessive amount of time can be used in failed attempts # b.) lower than the minimum jump distance limit that should be considered high priority (see options.py), # otherwise no attempt at all would be made to enforce the other requirements for home systems (see below) min_jumps = min(HS_MAX_JUMP_DISTANCE_LIMIT, max(int(float(len(systems)) / float(num_home_systems * 2)), HS_MIN_DISTANCE_PRIORITY_LIMIT)) # home systems must have a certain minimum of systems and planets in their near vicinity # we will try to select our home systems from systems that match this criteria, if that fails, we will select our # home systems from all systems and add the missing number planets to the systems in their vicinity afterwards # the minimum system and planet limit and the jump range that defines the "near vicinity" are controlled by the # HS_* option constants in options.py (see there) # we start by building two additional pools of systems: one that contains all systems that match the criteria # completely (meets the min systems and planets limit), and one that contains all systems that match the criteria # at least partially (meets the min systems limit) pool_matching_sys_and_planet_limit = [] pool_matching_sys_limit = [] for system in systems: systems_in_vicinity = get_systems_within_jumps(system, HS_VICINITY_RANGE) if len(systems_in_vicinity) >= HS_MIN_SYSTEMS_IN_VICINITY: pool_matching_sys_limit.append(system) if count_planets_in_systems(systems_in_vicinity) >= min_planets_in_vicinity_limit(len(systems_in_vicinity)): pool_matching_sys_and_planet_limit.append(system) print len(pool_matching_sys_and_planet_limit), "systems meet the min systems and planets in the near vicinity limit" print len(pool_matching_sys_limit), "systems meet the min systems in the near vicinity limit" # now try to pick the requested number of home systems # we will do this by calling find_home_systems, which takes a list of tuples defining the pools from which to pick # the home systems; it will use the pools in the order in which they appear in the list, so put better pools first # we will make two attempts: the first one with the filtered pools we just created, and tell find_home_systems # to start with the min_jumps jumps distance we calculated above, but not to go lower than # HS_MIN_DISTANCE_PRIORITY_LIMIT print "First attempt: trying to pick home systems from the filtered pools of preferred systems" pool_list = [ # the better pool is of course the one where all systems meet BOTH the min systems and planets limit (pool_matching_sys_and_planet_limit, "pool of systems that meet both the min systems and planets limit"), # next the less preferred pool where all systems at least meets the min systems limit # specify 0 as number of requested home systems to pick as much systems as possible (pool_matching_sys_limit, "pool of systems that meet at least the min systems limit"), ] home_systems = find_home_systems(num_home_systems, pool_list, min_jumps, HS_MIN_DISTANCE_PRIORITY_LIMIT) # check if the first attempt delivered a list with enough home systems # if not, we make our second attempt, this time disregarding the filtered pools and using all systems, starting # again with the min_jumps jump distance limit and specifying 0 as number of required home systems to pick as much # systems as possible if len(home_systems) < num_home_systems: print "Second attempt: trying to pick home systems from all systems" home_systems = find_home_systems(num_home_systems, [(systems, "complete pool")], min_jumps, 1) # check if the selection process delivered a list with enough home systems # if not, our galaxy obviously is too crowded, report an error and return an empty list if len(home_systems) < num_home_systems: report_error("Python generate_home_system_list: requested %d homeworlds in a galaxy with %d systems" % (num_home_systems, len(systems))) return [] # check if we got more home systems than we requested if len(home_systems) > num_home_systems: # yes: calculate the number of planets in the near vicinity of each system # and store that value with each system in a map hs_planets_in_vicinity_map = {s: count_planets_in_systems(get_systems_within_jumps(s, HS_VICINITY_RANGE)) for s in home_systems} # sort the home systems by the number of planets in their near vicinity using the map # now only pick the number of home systems we need, taking those with the highest number of planets home_systems = sorted(home_systems, key=hs_planets_in_vicinity_map.get, reverse=True)[:num_home_systems] # make sure all our home systems have a "real" star (that is, a star that is not a neutron star, black hole, # or even no star at all) and at least one planet in it for home_system in home_systems: # if this home system has no "real" star, change star type to a randomly selected "real" star if fo.sys_get_star_type(home_system) not in star_types_real: star_type = random.choice(star_types_real) print "Home system", home_system, "has star type", fo.sys_get_star_type(home_system),\ ", changing that to", star_type fo.sys_set_star_type(home_system, star_type) # if this home 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 not fo.sys_get_planets(home_system): print "Home system", home_system, "has no planets, adding one" planet = fo.create_planet(random.choice(planet_sizes_real), random.choice(planet_types_real), home_system, random.randint(0, fo.sys_get_num_orbits(home_system) - 1), "") # if we couldn't create the planet, report an error and return an empty list if planet == fo.invalid_object(): report_error("Python generate_home_system_list: couldn't create planet in home system") return [] # finally, check again if all home systems meet the criteria of having the required minimum number of planets # within their near vicinity, if not, add the missing number of planets print "Checking if home systems have the required minimum of planets within the near vicinity..." for home_system in home_systems: # calculate the number of missing planets, and add them if this number is > 0 systems_in_vicinity = get_systems_within_jumps(home_system, HS_VICINITY_RANGE) num_systems_in_vicinity = len(systems_in_vicinity) num_planets_in_vicinity = count_planets_in_systems(systems_in_vicinity) num_planets_to_add = min_planets_in_vicinity_limit(num_systems_in_vicinity) - num_planets_in_vicinity print "Home system", home_system, "has", num_systems_in_vicinity, "systems and", num_planets_in_vicinity,\ "planets in the near vicinity, required minimum:", min_planets_in_vicinity_limit(num_systems_in_vicinity) if num_planets_to_add > 0: systems_in_vicinity.remove(home_system) # don't add planets to the home system, so remove it from the list add_planets_to_vicinity(systems_in_vicinity, num_planets_to_add, gsd) # as we've sorted the home system list before, lets shuffle it to ensure random order and return random.shuffle(home_systems) return home_systems
def create_universe(psd_map): """ Main universe generation function invoked from C++ code. """ print "Python Universe Generator" # fetch universe and player setup data gsd = fo.get_galaxy_setup_data() total_players = len(psd_map) # initialize RNG seed_rng(gsd.seed) seed_pool = [random.random() for _ in range(100)] # 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) size = max(gsd.size, (total_players * 3)) if size > gsd.size: # gsd.size = 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" % (size, total_players) # calculate star system positions seed_rng(seed_pool.pop()) system_positions = calc_star_system_positions(gsd.shape, size) size = len(system_positions) print gsd.shape, "Star system positions calculated, final number of systems:", size # generate and populate systems seed_rng(seed_pool.pop()) systems = generate_systems(system_positions, gsd) print len(systems), "systems generated and populated" # generate Starlanes seed_rng(seed_pool.pop()) fo.generate_starlanes(gsd.starlaneFrequency) print "Starlanes generated" print "Compile list of home systems..." seed_rng(seed_pool.pop()) home_systems = compile_home_system_list(total_players, systems) if not home_systems: err_msg = "Python create_universe: couldn't get any home systems, ABORTING!" report_error(err_msg) raise Exception(err_msg) print "Home systems:", home_systems # set up empires for each player seed_rng(seed_pool.pop()) for empire, psd, home_system in zip(psd_map.keys(), psd_map.values(), home_systems): if not setup_empire(empire, psd.empire_name, home_system, psd.starting_species, psd.player_name): report_error( "Python create_universe: couldn't set up empire for player %s" % psd.player_name) # assign names to all star systems and their planets # this needs to be done after all systems have been generated and empire home systems have been set, as # only after all that is finished star types as well as planet sizes and types are fixed, and the naming # process depends on that print "Assign star system names" seed_rng(seed_pool.pop()) name_star_systems(systems) print "Set planet names" for system in systems: name_planets(system) print "Generating Natives" seed_rng(seed_pool.pop()) generate_natives(gsd.nativeFrequency, systems, home_systems) print "Generating Space Monsters" seed_rng(seed_pool.pop()) generate_monsters(gsd.monsterFrequency, systems) print "Distributing Starting Specials" seed_rng(seed_pool.pop()) distribute_specials(gsd.specialsFrequency, fo.get_all_objects()) # finally, write some statistics to the log file print "############################################################" print "## Universe generation statistics ##" print "############################################################" statistics.log_planet_count_dist(systems) print "############################################################" statistics.log_planet_type_summary(systems) print "############################################################" statistics.log_species_summary() print "############################################################" statistics.log_monsters_summary() print "############################################################" statistics.log_specials_summary() print "############################################################" if error_list: print "Python Universe Generator completed with errors" return False else: print "Python Universe Generator completed successfully" return True
def compile_home_system_list(num_home_systems, systems): """ Compiles a list with a requested number of home systems. """ # if the list of systems to choose home systems from is empty, report an error and return empty list if not systems: util.report_error( "Python generate_home_system_list: no systems to choose from") return [] # calculate an initial minimal number of jumps that the home systems should be apart, # based on the total number of systems to choose from and the requested number of home systems min_jumps = max(int(float(len(systems)) / float(num_home_systems * 2)), 5) # try to find the home systems, decrease the min jumps until enough systems can be found, or the min jump distance # gets reduced to 0 (meaning we don't have enough systems to choose from at all) while min_jumps > 0: print "Trying to find", num_home_systems, "home systems that are at least", min_jumps, "jumps apart" # try to find home systems... home_systems = find_systems_with_min_jumps_between( num_home_systems, systems, min_jumps) # ...check if we got enough... if len(home_systems) >= num_home_systems: # ...yes, we got what we need, so let's break out of the loop break print "Home system min jump conflict: %d systems and %d empires, tried %d min jump and failed"\ % (len(systems), num_home_systems, min_jumps) # ...no, decrease the min jump distance and try again min_jumps -= 1 # check if the loop above delivered a list with enough home systems, or if it exited because the min jump distance # has been decreased to 0 without finding enough systems # in that case, our galaxy obviously is too crowded, report an error and return an empty list if len(home_systems) < num_home_systems: util.report_error( "Python generate_home_system_list: requested %d homeworlds in a galaxy with %d systems" % (num_home_systems, len(systems))) return [] # make sure all our home systems have a "real" star (that is, a star that is not a neutron star, black hole, # or even no star at all) and at least one planet in it for home_system in home_systems: # if this home system has no "real" star, change star type to a randomly selected "real" star if fo.sys_get_star_type( home_system) not in starsystems.star_types_real: star_type = random.choice(starsystems.star_types_real) print "Home system", home_system, "has star type", fo.sys_get_star_type(home_system),\ ", changing that to", star_type fo.sys_set_star_type(home_system, star_type) # if this home 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 not fo.sys_get_planets(home_system): print "Home system", home_system, "has no planets, adding one" planet = fo.create_planet( random.choice(planets.planet_sizes_real), random.choice(planets.planet_types_real), home_system, random.randint(0, fo.sys_get_num_orbits(home_system) - 1), "") # if we couldn't create the planet, report an error and return an empty list if planet == fo.invalid_object(): util.report_error( "Python generate_home_system_list: couldn't create planet in home system" ) return [] return home_systems