File: teams.py

package info (click to toggle)
freeorion 0.5.1-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 194,940 kB
  • sloc: cpp: 186,508; python: 40,969; ansic: 1,164; xml: 719; makefile: 32; sh: 7
file content (120 lines) | stat: -rw-r--r-- 4,717 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
from logging import debug, warning

from common.configure_logging import redirect_logging_to_freeorion_logger

# Logging is redirected before other imports so that import errors appear in log files.
redirect_logging_to_freeorion_logger()

import freeorion as fo
from operator import itemgetter

from empires import home_system_layout
from util import unique_product


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 place_teams_layout(  # noqa: C901
    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 place_teams(home_systems: list[int], systems: list[int], teams: dict[int, int]) -> dict[int, int]:
    """
    Place teams on home systems layout.
    Returns map from home system to team.
    """
    # choose team's core home system
    home_system_teams = home_system_team_core(home_systems, sorted(teams.items(), key=itemgetter(1), reverse=True))
    debug("Home systems team core: %s", home_system_teams)
    # choose order of placing teams exclude already placed cores
    placement_list = []
    for team in home_system_teams.keys():
        placement_list += [(i, team) for i in range(teams[team] - (1 if team in home_system_teams.keys() else 0))]
    placement_list.sort(reverse=True)
    placement_teams = [i[1] for i in placement_list]
    debug("Placement teams: %s", placement_teams)
    # calculate placement based on teams
    layout = home_system_layout(home_systems, systems)
    debug("Home systems layout: %s", layout)
    return place_teams_layout(layout, home_system_teams, placement_teams)