def find_systems_with_min_jumps_between(num_systems, systems_pool, min_jumps): """ Find requested number of systems out of a pool that are at least a specified number of jumps apart. """ # make several tries to get the requested number of systems attempts = min(100, len(systems_pool)) while attempts > 0: attempts -= 1 # shuffle our pool of systems so each try the candidates are tried in different order # (otherwise each try would yield the same result, which would make trying several times kind of pointless...) random.shuffle(systems_pool) # try to find systems that meet our condition until we either have the requested number # or we have tried all systems in our pool accepted = [] for candidate in systems_pool: # check if our candidate is at least min_jumps away from all other systems we already found if all( fo.jump_distance(candidate, system) >= min_jumps for system in accepted): # if yes, add the candidate to the list of accepted systems accepted.append(candidate) # if we have the requested number of systems, we can stop and return the systems we found if len(accepted) >= num_systems: return accepted # all tries failed, return an empty list to indicate failure return []
def is_too_close_to_empire_home_systems(system, home_systems): """ Checks if a system is too close to the player home systems. Player home systems should be at least 2 jumps away. """ for home_system in home_systems: if fo.jump_distance(system, home_system) < 2: return True return False
def systems_min_jumps_away_from(systems_pool, min_jumps, chosen_systems): """ Generator that returns systems randomly picked from a pool that are at least a certain jump distance away from the group systems listed in chosen_systems. """ # on generator initialization shuffle our pool of systems to randomize the order in which the systems are returned random.shuffle(systems_pool) # return systems that meet our condition until we have exhausted our pool for candidate in systems_pool: # check if our candidate is at least the specified min jump distance away from the systems in chosen_systems # if yes, return candidate, otherwise continue with next candidate if all(fo.jump_distance(candidate, system) >= min_jumps for system in chosen_systems): yield candidate
def place_teams_layout(layout: Dict[int, List[int]], cores: Dict[int, int], placement_teams: List[int]) -> Dict[int, int]: """ Place teams on home systems layout. Returns map from home system to team. """ # set team cores left_home_systems = set(layout.keys()) result = {} for team, hs in cores.items(): result[hs] = team left_home_systems.remove(hs) # for each team search for home system for team in placement_teams: # search such home system # 1. with maximum same team neighbors choose_hs = set() neighbors_count = None for hs in left_home_systems: cnt = len([n for n in layout[hs] if result.get(n, -1) == team]) if neighbors_count is None or cnt > neighbors_count: choose_hs = {hs} neighbors_count = cnt elif cnt == neighbors_count: choose_hs.add(hs) # 2. with maximum jump distance from other teams if len(choose_hs) > 1: choose_hs2 = set() max_dist_to_enemy = None for hs in choose_hs: dist_to_enemy = None for hs2, team2 in result.items(): if team2 != team: dist = fo.jump_distance(hs, hs2) if dist_to_enemy is None or dist < dist_to_enemy: dist_to_enemy = dist if max_dist_to_enemy is None or dist_to_enemy > max_dist_to_enemy: max_dist_to_enemy = dist_to_enemy choose_hs2 = {hs} elif dist_to_enemy == max_dist_to_enemy: choose_hs2.add(hs) choose_hs = choose_hs2 if choose_hs: hs = choose_hs.pop() result[hs] = team left_home_systems.remove(hs) return result.items()
def home_system_layout(home_systems, systems): """ Home systems layout generation to place teams. Returns map from home system to neighbor home systems. """ # for each system found nearest home systems # maybe multiple if home worlds placed on the same jump distnace system_hs = {} for system in systems: nearest_hs = set() nearest_dist = None for hs in home_systems: dist = fo.jump_distance(system, hs) if nearest_dist is None or nearest_dist > dist: nearest_dist = dist nearest_hs = {hs} elif nearest_dist == dist: nearest_hs.add(hs) system_hs[system] = nearest_hs # homeworld is connected to the other # if both are nearest for some system # if each of them is nearest for systems on the starline ends home_system_connections = {} for system, connected_home_systems in system_hs.items(): if len(connected_home_systems) >= 2: for hs1, hs2 in unique_product(connected_home_systems, connected_home_systems): home_system_connections.setdefault(hs1, set()).add(hs2) home_system_connections.setdefault(hs2, set()).add(hs1) for system, connected_home_systems in system_hs.items(): adj_systems = fo.systems_within_jumps_unordered(1, [system]) for system2 in adj_systems: connected_home_systems2 = system_hs.get(system2, set()) for hs1, hs2 in unique_product(connected_home_systems, connected_home_systems2): home_system_connections.setdefault(hs1, set()).add(hs2) home_system_connections.setdefault(hs2, set()).add(hs1) return home_system_connections
def home_system_team_core(home_systems: List[int], teams: List[Tuple[int, int]]) -> Dict[int, int]: """ Choose core for teams which is a list of pairs team id and count of empires in the team. Returns map from team to core home system. """ if not teams: return {} debug("Teams: %s", teams) # sort all home systems by distance home_systems_distances = {} for hs1, hs2 in unique_product(home_systems, home_systems): dist = fo.jump_distance(hs1, hs2) home_systems_distances[(hs1, hs2)] = dist home_systems_sorted = sorted(home_systems_distances.items(), key=itemgetter(1), reverse=True) debug("Home systems sorted: %s", home_systems_sorted) result = {} if not home_systems_sorted: pass elif len(teams) == 1: first_team = teams[0][0] first_of_most_distant_systems = home_systems_sorted[0][0][0] result[first_team] = first_of_most_distant_systems else: first_team = teams[0][0] first_of_most_distant_systems = home_systems_sorted[0][0][0] second_team = teams[1][0] second_of_most_distant_systems = home_systems_sorted[0][0][1] result[first_team] = first_of_most_distant_systems result[second_team] = second_of_most_distant_systems if len(teams) > 2: warning("Teamed placement poorly implemented for %d teams", len(teams)) return result
def find_systems_with_min_jumps_between(num_systems, systems_pool, min_jumps): """ Find requested number of systems out of a pool that are at least a specified number of jumps apart. """ # make several tries to get the requested number of systems attempts = min(100, len(systems_pool)) while attempts > 0: attempts -= 1 # shuffle our pool of systems so each try the candidates are tried in different order # (otherwise each try would yield the same result, which would make trying several times kind of pointless...) random.shuffle(systems_pool) # try to find systems that meet our condition until we either have the requested number # or we have tried all systems in our pool accepted = [] for candidate in systems_pool: # check if our candidate is at least min_jumps away from all other systems we already found if all(fo.jump_distance(candidate, system) >= min_jumps for system in accepted): # if yes, add the candidate to the list of accepted systems accepted.append(candidate) # if we have the requested number of systems, we can stop and return the systems we found if len(accepted) >= num_systems: return accepted # all tries failed, return an empty list to indicate failure return []