File: _supply_state.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 (117 lines) | stat: -rw-r--r-- 4,537 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
import freeOrionAIInterface as fo
from collections.abc import Mapping
from logging import error, warning

from AIDependencies import INVALID_ID
from common.fo_typing import SystemId
from freeorion_tools.caching import cache_for_current_turn


@cache_for_current_turn
def _get_system_supply_map() -> dict[SystemId, int]:
    return fo.getEmpire().supplyProjections()  # map from system_id to supply


def get_system_supply(sys_id: SystemId) -> int:
    """
    Get the supply level of a system.

    Negative values indicate jumps away from supply. Return -99 if system is not connected.
    """
    far_away_from_supply = -99

    retval = _get_system_supply_map().get(sys_id, None)
    if retval is None:
        # This is only expected to happen if a system has no path to any supplied system.
        # As the current code should not allow such queries, this is logged as warning.
        # If future code breaks this assumption, feel free to adjust logging.
        warning("Queried supply value of a system not mapped in empire.supplyProjections(): %d" % sys_id)
        return far_away_from_supply
    return retval


@cache_for_current_turn
def _get_systems_map_by_jumps_to_supply() -> Mapping[int, tuple[SystemId]]:
    systems_by_jumps_to_supply = {}

    for sys_id, supply_val in _get_system_supply_map().items():
        systems_by_jumps_to_supply.setdefault(min(0, supply_val), []).append(sys_id)
    return {key: tuple(value) for key, value in systems_by_jumps_to_supply.items()}


def get_systems_by_supply_tier(supply_tier: int) -> tuple[SystemId]:
    """
    Get systems with supply tier.

    The current implementation does not distinguish between positive supply levels and caps at 0.
    Negative values indicate jumps away from supply.
    """
    if supply_tier > 0:
        warning(
            "The current implementation does not distinguish between positive supply levels. "
            "Interpreting the query as supply_tier=0 (indicating system in supply)."
        )
        supply_tier = 0
    return _get_systems_map_by_jumps_to_supply().get(supply_tier, tuple())


@cache_for_current_turn
def _get_enemy_supply_distance_map() -> Mapping[int, int]:
    enemies = [fo.getEmpire(_id) for _id in fo.allEmpireIDs() if _id != fo.empireID()]
    distance_to_enemy_supply = {}

    for enemy in enemies:
        if enemy is None:
            error("Got None for enemy empire!")
            continue

        for sys_id, supply_val in enemy.supplyProjections().items():
            distance_to_enemy_supply[sys_id] = min(distance_to_enemy_supply.get(sys_id, 999), -supply_val)

    return distance_to_enemy_supply


def get_distance_to_enemy_supply(sys_id: int) -> int:
    return _get_enemy_supply_distance_map().get(sys_id, 999)


@cache_for_current_turn
def _system_to_supply_group() -> Mapping[SystemId, int]:
    """
    Create a mapping of SystemIds to supply_group_id.
    Used buy supply_connected, get_supply_group_id and get_supply_group.
    """
    result = {}
    for num, group in enumerate(fo.getEmpire().resourceSupplyGroups, start=1):
        result.update((sys_id, num) for sys_id in group)
    return result


def supply_connected(system_id1: SystemId, system_id2: SystemId) -> bool:
    """Return whether the two systems are connected by this empire's supply chains."""
    mapping = _system_to_supply_group()
    # Different defaults to get False when neither system is part of any group
    return mapping.get(system_id1, -1) == mapping.get(system_id2, -2)


def get_supply_group_id(system_id: SystemId) -> int:
    """
    Return a number identifying a supply group, INVALID_ID if none.
    These number have no meaning beyond the current turn, groups may be completely different next turn.
    The number can be used to check if a set of system belongs to the same or multiple groups or as input to
    get_supply_group
    """
    return _system_to_supply_group().get(system_id, INVALID_ID)


def get_supply_group(group_id: int) -> fo.IntSet:
    """
    Returns a Set of systems in the given group. Note that group_ids are only valid for the current turn,
    so the argument must have been returned by get_supply_group_id in the same turn.
    """
    # There is no way to directly get the nth element, but it should iterate in the same order every time,
    # see _system_to_supply_group.
    for num, group in enumerate(fo.getEmpire().resourceSupplyGroups, start=1):
        if num == group_id:
            return group
    raise (ValueError(f"invalid supply group_id: {group_id}"))