def distribute_specials(specials_freq, universe_objects): """ Adds start-of-game specials to universe objects. """ # get basic chance for occurrence of specials from the universe tables base_chance = universe_tables.SPECIALS_FREQUENCY[specials_freq] if base_chance <= 0: return # get a list with all specials that have a spawn rate and limit both > 0 and a location condition defined # (no location condition means a special shouldn't get added at game start) specials = [ sp for sp in fo.get_all_specials() if fo.special_spawn_rate(sp) > 0.0 and fo.special_spawn_limit(sp) > 0 and fo.special_has_location(sp) ] if not specials: return # dump a list of all specials meeting that conditions and their properties to the log print("Specials available for distribution at game start:") for special in specials: print("... {:30}: spawn rate {:2.3f} / spawn limit {}".format( special, fo.special_spawn_rate(special), fo.special_spawn_limit(special))) objects_needing_specials = [ obj for obj in universe_objects if random.random() < base_chance ] track_num_placed = {obj: 0 for obj in universe_objects} print( "Base chance for specials is {}. Placing specials on {} of {} ({:1.4f})objects" .format( base_chance, len(objects_needing_specials), len(universe_objects), float(len(objects_needing_specials)) / len(universe_objects), )) obj_tuple_needing_specials = set( zip( objects_needing_specials, fo.objs_get_systems(objects_needing_specials), calculate_number_of_specials_to_place(objects_needing_specials), )) # Equal to the largest distance in WithinStarlaneJumps conditions # GALAXY_DECOUPLING_DISTANCE is used as follows. For any two or more objects # at least GALAXY_DECOUPLING_DISTANCE appart you only need to check # fo.special_locations once and then you can place as many specials as possible, # subject to number restrictions. # # Organize the objects into sets where all objects are spaced GALAXY_DECOUPLING_DISTANCE # appart. Place a special on each one. Repeat until you run out of specials or objects. GALAXY_DECOUPLING_DISTANCE = 6 while obj_tuple_needing_specials: systems_needing_specials = defaultdict(set) for (obj, system, specials_count) in obj_tuple_needing_specials: systems_needing_specials[system].add((obj, system, specials_count)) print(" Placing in {} locations remaining.".format( len(systems_needing_specials))) # Find a list of candidates all spaced GALAXY_DECOUPLING_DISTANCE apart candidates = [] while systems_needing_specials: random_sys = random.choice(list(systems_needing_specials.values())) member = random.choice(list(random_sys)) obj, system, specials_count = member candidates.append(obj) obj_tuple_needing_specials.remove(member) if specials_count > 1: obj_tuple_needing_specials.add( (obj, system, specials_count - 1)) # remove all neighbors from the local pool for neighbor in fo.systems_within_jumps_unordered( GALAXY_DECOUPLING_DISTANCE, [system]): if neighbor in systems_needing_specials: systems_needing_specials.pop(neighbor) print("Caching specials_locations() at {} of {} remaining locations.". format(str(len(candidates)), str(len(obj_tuple_needing_specials) + len(candidates)))) # Get the locations at which each special can be placed locations_cache = {} for special in specials: # The fo.special_locations in the following line consumes most of the time in this # function. Decreasing GALAXY_DECOUPLING_DISTANCE will speed up the whole # function by reducing the number of times this needs to be called. locations_cache[special] = set( fo.special_locations(special, candidates)) # Attempt to apply a special to each candidate # by finding a special that can be applied to it and hasn't been added too many times for obj in candidates: # check if the spawn limit for this special has already been reached (that is, if this special # has already been added the maximal allowed number of times) specials = [ s for s in specials if universe_statistics.specials_summary[s] < fo.special_spawn_limit(s) ] if not specials: break # Find which specials can be placed at this one location local_specials = [ sp for sp in specials if obj in locations_cache[sp] ] if not local_specials: universe_statistics.specials_repeat_dist[0] += 1 continue # All prerequisites and the test have been met, now add this special to this universe object. track_num_placed[obj] += place_special(local_specials, obj) for num_placed in track_num_placed.values(): universe_statistics.specials_repeat_dist[num_placed] += 1
def distribute_specials(specials_freq, universe_objects): """ Adds start-of-game specials to universe objects. """ # get basic chance for occurrence of specials from the universe tables base_chance = universe_tables.SPECIALS_FREQUENCY[specials_freq] if base_chance <= 0: return # get a list with all specials that have a spawn rate and limit both > 0 and a location condition defined # (no location condition means a special shouldn't get added at game start) specials = [sp for sp in fo.get_all_specials() if fo.special_spawn_rate(sp) > 0.0 and fo.special_spawn_limit(sp) > 0 and fo.special_has_location(sp)] if not specials: return # dump a list of all specials meeting that conditions and their properties to the log print "Specials available for distribution at game start:" for special in specials: print("... {:30}: spawn rate {:2.3f} / spawn limit {}". format(special, fo.special_spawn_rate(special), fo.special_spawn_limit(special))) objects_needing_specials = [obj for obj in universe_objects if random.random() < base_chance] track_num_placed = {obj: 0 for obj in universe_objects} print("Base chance for specials is {}. Placing specials on {} of {} ({:1.4f})objects" .format(base_chance, len(objects_needing_specials), len(universe_objects), float(len(objects_needing_specials)) / len(universe_objects))) obj_tuple_needing_specials = set(zip(objects_needing_specials, fo.objs_get_systems(objects_needing_specials), calculate_number_of_specials_to_place(objects_needing_specials))) # Equal to the largest distance in WithinStarlaneJumps conditions # GALAXY_DECOUPLING_DISTANCE is used as follows. For any two or more objects # at least GALAXY_DECOUPLING_DISTANCE appart you only need to check # fo.special_locations once and then you can place as many specials as possible, # subject to number restrictions. # # Organize the objects into sets where all objects are spaced GALAXY_DECOUPLING_DISTANCE # appart. Place a special on each one. Repeat until you run out of specials or objects. GALAXY_DECOUPLING_DISTANCE = 6 while obj_tuple_needing_specials: systems_needing_specials = defaultdict(set) for (obj, system, specials_count) in obj_tuple_needing_specials: systems_needing_specials[system].add((obj, system, specials_count)) print " Placing in {} locations remaining.".format(len(systems_needing_specials)) # Find a list of candidates all spaced GALAXY_DECOUPLING_DISTANCE apart candidates = [] while systems_needing_specials: random_sys = random.choice(systems_needing_specials.values()) member = random.choice(list(random_sys)) obj, system, specials_count = member candidates.append(obj) obj_tuple_needing_specials.remove(member) if specials_count > 1: obj_tuple_needing_specials.add((obj, system, specials_count - 1)) # remove all neighbors from the local pool for neighbor in fo.systems_within_jumps_unordered(GALAXY_DECOUPLING_DISTANCE, [system]): if neighbor in systems_needing_specials: systems_needing_specials.pop(neighbor) print("Caching specials_locations() at {} of {} remaining locations.". format(str(len(candidates)), str(len(obj_tuple_needing_specials) + len(candidates)))) # Get the locations at which each special can be placed locations_cache = {} for special in specials: # The fo.special_locations in the following line consumes most of the time in this # function. Decreasing GALAXY_DECOUPLING_DISTANCE will speed up the whole # function by reducing the number of times this needs to be called. locations_cache[special] = set(fo.special_locations(special, candidates)) # Attempt to apply a special to each candidate # by finding a special that can be applied to it and hasn't been added too many times for obj in candidates: # check if the spawn limit for this special has already been reached (that is, if this special # has already been added the maximal allowed number of times) specials = [s for s in specials if universe_statistics.specials_summary[s] < fo.special_spawn_limit(s)] if not specials: break # Find which specials can be placed at this one location local_specials = [sp for sp in specials if obj in locations_cache[sp]] if not local_specials: universe_statistics.specials_repeat_dist[0] += 1 continue # All prerequisites and the test have been met, now add this special to this universe object. track_num_placed[obj] += place_special(local_specials, obj) for num_placed in track_num_placed.values(): universe_statistics.specials_repeat_dist[num_placed] += 1
def distribute_specials(specials_freq, universe_objects): """ Adds start-of-game specials to universe objects. """ # get basic chance for occurrence of specials from the universe tables # the values there are integers, so we have to divide them by 10,000 to get the actual basic probability value basic_chance = float(fo.specials_frequency(specials_freq)) / 10000.0 if basic_chance <= 0: return # get a list with all specials that have a spawn rate and limit both > 0 and a location condition defined # (no location condition means a special shouldn't get added at game start) specials = [ sp for sp in fo.get_all_specials() if fo.special_spawn_rate(sp) > 0.0 and fo.special_spawn_limit(sp) > 0 and fo.special_has_location(sp) ] if not specials: return # dump a list of all specials meeting that conditions and their properties to the log print "Specials available for distribution at game start:" for special in specials: print "...", special, ": spawn rate", fo.special_spawn_rate(special),\ "/ spawn limit", fo.special_spawn_limit(special) # attempt to apply a special to each universe object in the list that has been passed to this function # by finding a special that can be applied to it and hasn't been added too many times, and then attempt # to add that special by testing its spawn rate repeat_rate = {1: 0.08, 2: 0.05, 3: 0.01, 4: 0.00} for univ_obj in universe_objects: # for this universe object, find a suitable special # start by shuffling our specials list, so each time the specials are considered in a new random order random.shuffle(specials) num_here = 0 # then, consider each special until one has been found or we run out of specials # (the latter case means that no special is added to this universe object) for special in specials: # check if the spawn limit for this special has already been reached (that is, if this special # has already been added the maximal allowed number of times) if statistics.specials_summary[special] >= fo.special_spawn_limit( special): # if yes, consider next special continue # check if this universe object matches the location condition for this special # (meaning, if this special can be added to this universe object at all) if not fo.special_location(special, univ_obj): # if not, consider next special continue # we have found a special that meets all prerequisites # now do the test if we want to add the selected special to this universe object by making a roll against # the basic probability multiplied by the spawn rate of the special if random.random() > basic_chance * fo.special_spawn_rate(special): # no, test failed, break out of the specials loop and continue with the next universe object statistics.specials_repeat_dist[num_here] += 1 break num_here += 1 # all prerequisites and the test have been met, now add this special to this universe object fo.add_special(univ_obj, special) # increase the statistic counter for this special, so we can keep track of how often it has already # been added (needed for the spawn limit test above, and to dump some statistics to the log later) statistics.specials_summary[special] += 1 print "Special", special, "added to", fo.get_name(univ_obj) # stop attempting to add specials here? give a small chance to try more than one special if random.random() > repeat_rate.get(num_here, 0.0): # sorry, no, break out of the specials loop and continue with the next universe object statistics.specials_repeat_dist[num_here] += 1 break else: statistics.specials_repeat_dist[num_here] += 1
def distribute_specials(specials_freq, universe_objects): """ Adds start-of-game specials to universe objects. """ # get basic chance for occurrence of specials from the universe tables # the values there are integers, so we have to divide them by 10,000 to get the actual basic probability value basic_chance = float(fo.specials_frequency(specials_freq)) / 10000.0 if basic_chance <= 0: return # get a list with all specials that have a spawn rate and limit both > 0 and a location condition defined # (no location condition means a special shouldn't get added at game start) specials = [sp for sp in fo.get_all_specials() if fo.special_spawn_rate(sp) > 0.0 and fo.special_spawn_limit(sp) > 0 and fo.special_has_location(sp)] if not specials: return # dump a list of all specials meeting that conditions and their properties to the log print "Specials available for distribution at game start:" for special in specials: print "...", special, ": spawn rate", fo.special_spawn_rate(special),\ "/ spawn limit", fo.special_spawn_limit(special) # attempt to apply a special to each universe object in the list that has been passed to this function # by finding a special that can be applied to it and hasn't been added too many times, and then attempt # to add that special by testing its spawn rate repeat_rate = {1 : 0.08, 2 : 0.05, 3 : 0.01, 4 : 0.00} for univ_obj in universe_objects: # for this universe object, find a suitable special # start by shuffling our specials list, so each time the specials are considered in a new random order random.shuffle(specials) num_here = 0 # then, consider each special until one has been found or we run out of specials # (the latter case means that no special is added to this universe object) for special in specials: # check if the spawn limit for this special has already been reached (that is, if this special # has already been added the maximal allowed number of times) if statistics.specials_summary[special] >= fo.special_spawn_limit(special): # if yes, consider next special continue # check if this universe object matches the location condition for this special # (meaning, if this special can be added to this universe object at all) if not fo.special_location(special, univ_obj): # if not, consider next special continue # we have found a special that meets all prerequisites # now do the test if we want to add the selected special to this universe object by making a roll against # the basic probability multiplied by the spawn rate of the special if random.random() > basic_chance * fo.special_spawn_rate(special): # no, test failed, break out of the specials loop and continue with the next universe object statistics.specials_repeat_dist[num_here] += 1 break num_here += 1 # all prerequisites and the test have been met, now add this special to this universe object fo.add_special(univ_obj, special) # increase the statistic counter for this special, so we can keep track of how often it has already # been added (needed for the spawn limit test above, and to dump some statistics to the log later) statistics.specials_summary[special] += 1 print "Special", special, "added to", fo.get_name(univ_obj) # stop attempting to add specials here? give a small chance to try more than one special if random.random() > repeat_rate.get(num_here, 0.0): # sorry, no, break out of the specials loop and continue with the next universe object statistics.specials_repeat_dist[num_here] += 1 break else: statistics.specials_repeat_dist[num_here] += 1