valanga 0.1.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
valanga/__init__.py ADDED
@@ -0,0 +1,42 @@
1
+ """Common types and utilities shared by multiple libraries."""
2
+
3
+ from .evaluations import EvalItem, FloatyStateEvaluation, ForcedOutcome, StateEvaluation
4
+ from .game import (
5
+ BLACK,
6
+ WHITE,
7
+ BranchKey,
8
+ BranchKeyGeneratorP,
9
+ Color,
10
+ ColorIndex,
11
+ HasTurn,
12
+ State,
13
+ StateModifications,
14
+ StateTag,
15
+ TurnState,
16
+ )
17
+ from .over_event import OverEvent
18
+ from .progress_messsage import PlayerProgressMessage
19
+ from .representation_factory import RepresentationFactory
20
+ from .represention_for_evaluation import ContentRepresentation
21
+
22
+ __all__ = [
23
+ "ForcedOutcome",
24
+ "FloatyStateEvaluation",
25
+ "StateEvaluation",
26
+ "EvalItem",
27
+ "OverEvent",
28
+ "Color",
29
+ "WHITE",
30
+ "BLACK",
31
+ "ColorIndex",
32
+ "HasTurn",
33
+ "TurnState",
34
+ "ContentRepresentation",
35
+ "RepresentationFactory",
36
+ "State",
37
+ "StateModifications",
38
+ "BranchKeyGeneratorP",
39
+ "BranchKey",
40
+ "PlayerProgressMessage",
41
+ "StateTag",
42
+ ]
valanga/evaluations.py ADDED
@@ -0,0 +1,58 @@
1
+ """
2
+ Evaluation-related classes and types.
3
+ """
4
+
5
+ from collections.abc import Hashable
6
+ from dataclasses import dataclass
7
+ from typing import Annotated, Protocol
8
+
9
+ from .game import BranchKey, State
10
+ from .over_event import OverEvent
11
+ from .represention_for_evaluation import ContentRepresentation
12
+
13
+ type ActionKey = Annotated[Hashable, "A label or identifier for an action"]
14
+
15
+
16
+ class EvalItem[StateT: State](Protocol):
17
+ """
18
+ The protocol for an evaluation item.
19
+ An evaluation item is something that has a state and optionally a representation of that state.
20
+ """
21
+
22
+ @property
23
+ def state(self) -> StateT:
24
+ """The state associated with this evaluation item."""
25
+ ...
26
+
27
+ @property
28
+ def state_representation(self) -> ContentRepresentation | None:
29
+ """The representation of the state associated with this evaluation item, if available."""
30
+ ...
31
+
32
+
33
+ @dataclass
34
+ class ForcedOutcome:
35
+ """
36
+ The class
37
+ """
38
+
39
+ # The forced outcome with optimal play by both sides.
40
+ outcome: OverEvent
41
+
42
+ # the line
43
+ line: list[BranchKey]
44
+
45
+
46
+ @dataclass
47
+ class FloatyStateEvaluation:
48
+ """
49
+ The class to defines what is an evaluation of a board.
50
+ By convention is it always evaluated from the view point of the white side.
51
+ """
52
+
53
+ # The evaluation value for the white side when the outcome is not certain. Typically, a float.
54
+ # todo can we remove the None option?
55
+ value_white: float | None
56
+
57
+
58
+ StateEvaluation = FloatyStateEvaluation | ForcedOutcome
@@ -0,0 +1,13 @@
1
+ """Small shared types used by evaluators and representations.
2
+
3
+ This module exists to avoid runtime circular imports between `evaluations` and
4
+ `represention_for_evaluation`.
5
+ """
6
+
7
+ from typing import Annotated
8
+
9
+ EvaluatorInput = Annotated[
10
+ object, "The input type for the evaluator, typically a tensor or array"
11
+ ]
12
+
13
+ __all__ = ["EvaluatorInput"]
valanga/game.py ADDED
@@ -0,0 +1,158 @@
1
+ """
2
+ Common types and utilities representing game objects shared by multiple libraries.
3
+ """
4
+
5
+ from collections.abc import Hashable
6
+ from enum import Enum
7
+ from typing import Annotated, Iterator, Protocol, Self, Sequence, TypeVar
8
+
9
+ type Seed = Annotated[int, "seed"]
10
+
11
+ type StateTag = Annotated[Hashable, "A label or identifier for a state in a game"]
12
+
13
+ type StateModifications = Annotated[
14
+ object, "Modifications to the state between to time steps of the game"
15
+ ] # used for time and memory optimisation
16
+
17
+
18
+ type BranchKey = Annotated[Hashable, "A label or identifier for a branch in a tree"]
19
+
20
+
21
+ T_co = TypeVar("T_co", bound=BranchKey, covariant=True, default=BranchKey)
22
+
23
+
24
+ class BranchKeyGeneratorP(Protocol[T_co]):
25
+ """Protocol for a branch key generator that yields branch keys."""
26
+
27
+ # whether to sort the branch keys by their respective uci for easy comparison of various implementations
28
+ sort_branch_keys: bool = False
29
+
30
+ @property
31
+ def all_generated_keys(self) -> Sequence[T_co] | None:
32
+ """Returns all generated branch keys if available, otherwise None."""
33
+ ...
34
+
35
+ def __iter__(self) -> Iterator[T_co]:
36
+ """Returns an iterator over the branch keys."""
37
+ ...
38
+
39
+ def __next__(self) -> T_co:
40
+ """Returns the next branch key."""
41
+ ...
42
+
43
+ def more_than_one(self) -> bool:
44
+ """Checks if there is more than one branch available.
45
+
46
+ Returns:
47
+ bool: True if there is more than one branch, False otherwise.
48
+ """
49
+ ...
50
+
51
+ def get_all(self) -> Sequence[T_co]:
52
+ """Returns a list of all branch keys."""
53
+ ...
54
+
55
+ def copy_with_reset(self) -> Self:
56
+ """Creates a copy of the legal move generator with an optional reset of generated moves.
57
+
58
+ Returns:
59
+ Self: A new instance of the legal move generator with the specified generated moves.
60
+ """
61
+ ...
62
+
63
+
64
+ class State(Protocol):
65
+ """Protocol for a content object that has a tag."""
66
+
67
+ @property
68
+ def tag(self) -> StateTag:
69
+ """Returns the tag of the content.
70
+
71
+ Returns:
72
+ StateTag: The tag of the content.
73
+ """
74
+ ...
75
+
76
+ @property
77
+ def branch_keys(self) -> BranchKeyGeneratorP[BranchKey]:
78
+ """Returns the branch keys associated with the content.
79
+
80
+ Returns:
81
+ BranchKeyGeneratorP: The branch keys associated with the content.
82
+ """
83
+ ...
84
+
85
+ def branch_name_from_key(self, key: BranchKey) -> str:
86
+ """Returns the branch name corresponding to the given branch key.
87
+
88
+ Args:
89
+ key (BranchKey): The branch key.
90
+
91
+ Returns:
92
+ str: The branch name corresponding to the given branch key.
93
+ """
94
+ ...
95
+
96
+ def is_game_over(self) -> bool:
97
+ """Checks if the game represented by the content is over.
98
+
99
+ Returns:
100
+ bool: True if the game is over, False otherwise.
101
+ """
102
+ ...
103
+
104
+ def copy(self, stack: bool, deep_copy_legal_moves: bool = True) -> Self:
105
+ """
106
+ Create a copy of the current board.
107
+
108
+ Args:
109
+ stack (bool): Whether to copy the previous action stack as well. Important in some games.
110
+ deep_copy_legal_moves (bool): Whether to deep copy the legal moves generator.
111
+
112
+ Returns:
113
+ BoardChi: A new instance of the BoardChi class with the copied board.
114
+ """
115
+ ...
116
+
117
+ def step(self, branch_key: BranchKey) -> StateModifications | None:
118
+ """Advances the state by applying the action corresponding to the given branch key.
119
+
120
+ Args:
121
+ branch_key (BranchKey): The branch key representing the action to be applied.
122
+
123
+ Returns:
124
+ StateModifications | None: The modifications to the state after applying the action, or None if the action is invalid.
125
+ """
126
+ ...
127
+
128
+
129
+ type ColorIndex = Annotated[int, "1 for white, 0 for black"]
130
+
131
+ WHITE: ColorIndex = 1
132
+ BLACK: ColorIndex = 0
133
+
134
+
135
+ class Color(int, Enum):
136
+ """Represents the color of a player in a game."""
137
+
138
+ WHITE = WHITE
139
+ BLACK = BLACK
140
+
141
+
142
+ class HasTurn(Protocol):
143
+ """Protocol for a content object that has a tag."""
144
+
145
+ @property
146
+ def turn(self) -> Color:
147
+ """Returns the tag of the content.
148
+
149
+ Returns:
150
+ ContentTag: The tag of the content.
151
+ """
152
+ ...
153
+
154
+
155
+ class TurnState(State, HasTurn, Protocol):
156
+ """A State that also supports turn()."""
157
+
158
+ ...
valanga/over_event.py ADDED
@@ -0,0 +1,254 @@
1
+ """
2
+ Module for handling game over events.
3
+ """
4
+
5
+ from dataclasses import dataclass
6
+ from enum import Enum
7
+
8
+ from .game import Color
9
+
10
+
11
+ class HowOver(Enum):
12
+ """Represents the possible outcomes of a game.
13
+
14
+ Attributes:
15
+ WIN (int): Indicates a win.
16
+ DRAW (int): Indicates a draw.
17
+ DO_NOT_KNOW_OVER (int): Indicates that the outcome is unknown.
18
+ """
19
+
20
+ WIN = 1
21
+ DRAW = 2
22
+ DO_NOT_KNOW_OVER = 3
23
+
24
+
25
+ class Winner(Enum):
26
+ """Represents the winner of a chess game.
27
+
28
+ Args:
29
+ Enum: The base class for enumeration types.
30
+
31
+ Attributes:
32
+ WHITE (Winner): Represents the winner as white.
33
+ BLACK (Winner): Represents the winner as black.
34
+ NO_KNOWN_WINNER (Winner): Represents the absence of a known winner.
35
+
36
+ Methods:
37
+ is_none() -> bool: Checks if the winner is None.
38
+ is_white() -> bool: Checks if the winner is white.
39
+ is_black() -> bool: Checks if the winner is black.
40
+ """
41
+
42
+ WHITE = Color.WHITE
43
+ BLACK = Color.BLACK
44
+ NO_KNOWN_WINNER = None
45
+
46
+ def is_none(self) -> bool:
47
+ """Check if the winner is NO_KNOWN_WINNER.
48
+
49
+ Returns:
50
+ bool: True if the winner is NO_KNOWN_WINNER, False otherwise.
51
+ """
52
+ return self is Winner.NO_KNOWN_WINNER
53
+
54
+ def is_white(self) -> bool:
55
+ """Check if the winner is white.
56
+
57
+ Returns:
58
+ bool: True if the winner is white, False otherwise.
59
+ """
60
+ is_white_bool: bool
61
+ if not self.is_none():
62
+ is_white_bool = self is Winner.WHITE
63
+ else:
64
+ is_white_bool = False
65
+ return is_white_bool
66
+
67
+ def is_black(self) -> bool:
68
+ """Check if the winner is black.
69
+
70
+ Returns:
71
+ bool: True if the winner is black, False otherwise.
72
+ """
73
+ is_black_bool: bool
74
+
75
+ if not self.is_none():
76
+ is_black_bool = self is Winner.BLACK
77
+ else:
78
+ is_black_bool = False
79
+ return is_black_bool
80
+
81
+
82
+ class OverTags(str, Enum):
83
+ """Represents the possible tags for game over events.
84
+
85
+ Attributes:
86
+ TAG_WIN_WHITE (str): Tag indicating a win for the white player.
87
+ TAG_WIN_BLACK (str): Tag indicating a win for the black player.
88
+ TAG_DRAW (str): Tag indicating a draw.
89
+ TAG_DO_NOT_KNOW (str): Tag indicating an unknown outcome.
90
+ """
91
+
92
+ TAG_WIN_WHITE = "Win-Wh"
93
+ TAG_WIN_BLACK = "Win-Bl"
94
+ TAG_DRAW = "Draw"
95
+ TAG_DO_NOT_KNOW = "?"
96
+
97
+
98
+ @dataclass(slots=True)
99
+ class OverEvent:
100
+ """Represents an event that indicates the end of a game.
101
+
102
+ Attributes:
103
+ how_over (HowOver): The way the game ended.
104
+ who_is_winner (Winner): The winner of the game.
105
+
106
+ Raises:
107
+ AssertionError: If the `how_over` attribute is not a valid value from the `HowOver` enum.
108
+ AssertionError: If the `who_is_winner` attribute is not a valid value from the `Winner` enum.
109
+ Exception: If the winner is not properly defined.
110
+
111
+ Methods:
112
+ __post_init__: Performs additional initialization after the object is created.
113
+ becomes_over: Sets the `how_over` and `who_is_winner` attributes.
114
+ get_over_tag: Returns a tag string used in databases.
115
+ __bool__: Raises an exception.
116
+ is_over: Checks if the game is over.
117
+ is_win: Checks if the game ended with a win.
118
+ is_draw: Checks if the game ended with a draw.
119
+ is_winner: Checks if the specified player is the winner.
120
+ print_info: Prints information about the `OverEvent` object.
121
+ test: Performs tests on the `OverEvent` object.
122
+ """
123
+
124
+ how_over: HowOver = HowOver.DO_NOT_KNOW_OVER
125
+ who_is_winner: Winner = Winner.NO_KNOWN_WINNER
126
+ termination: Enum | None = None # Optional termination reason
127
+
128
+ def __post_init__(self) -> None:
129
+ assert self.how_over in HowOver
130
+ assert self.who_is_winner in Winner
131
+
132
+ if self.how_over == HowOver.WIN:
133
+ assert (
134
+ self.who_is_winner is Winner.WHITE or self.who_is_winner is Winner.BLACK
135
+ )
136
+ elif self.how_over == HowOver.DRAW:
137
+ assert self.who_is_winner is Winner.NO_KNOWN_WINNER
138
+
139
+ def becomes_over(
140
+ self,
141
+ how_over: HowOver,
142
+ termination: Enum | None,
143
+ who_is_winner: Winner = Winner.NO_KNOWN_WINNER,
144
+ ) -> None:
145
+ """Sets the `how_over` and `who_is_winner` attributes.
146
+
147
+ Args:
148
+ how_over (HowOver): The way the game ended.
149
+ who_is_winner (Winner, optional): The winner of the game. Defaults to `Winner.NO_KNOWN_WINNER`.
150
+ """
151
+ self.how_over = how_over
152
+ self.who_is_winner = who_is_winner
153
+ self.termination = termination
154
+
155
+ def get_over_tag(self) -> OverTags:
156
+ """Returns a tag string used in databases.
157
+
158
+ Returns:
159
+ OverTags: The tag string representing the game outcome.
160
+
161
+ Raises:
162
+ Exception: If the winner is not properly defined.
163
+ """
164
+ over_tag: OverTags
165
+ if self.how_over == HowOver.WIN:
166
+ if self.who_is_winner.is_white():
167
+ over_tag = OverTags.TAG_WIN_WHITE
168
+ elif self.who_is_winner.is_black():
169
+ over_tag = OverTags.TAG_WIN_BLACK
170
+ else:
171
+ raise ValueError("error: winner is not properly defined.")
172
+ elif self.how_over == HowOver.DRAW:
173
+ over_tag = OverTags.TAG_DRAW
174
+ elif self.how_over == HowOver.DO_NOT_KNOW_OVER:
175
+ over_tag = OverTags.TAG_DO_NOT_KNOW
176
+ else:
177
+ raise ValueError("error: over is not properly defined.")
178
+ return over_tag
179
+
180
+ def __bool__(self) -> None:
181
+ """Raises an exception.
182
+
183
+ Raises:
184
+ Exception: Always raises an exception.
185
+ """
186
+ raise ValueError("Nooooooooooo in over ebvent.py")
187
+
188
+ def is_over(self) -> bool:
189
+ """Checks if the game is over.
190
+
191
+ Returns:
192
+ bool: True if the game is over, False otherwise.
193
+ """
194
+ return self.how_over in {HowOver.WIN, HowOver.DRAW}
195
+
196
+ def is_win(self) -> bool:
197
+ """Checks if the game ended with a win.
198
+
199
+ Returns:
200
+ bool: True if the game ended with a win, False otherwise.
201
+ """
202
+ return self.how_over == HowOver.WIN
203
+
204
+ def is_draw(self) -> bool:
205
+ """Checks if the game ended with a draw.
206
+
207
+ Returns:
208
+ bool: True if the game ended with a draw, False otherwise.
209
+ """
210
+ return self.how_over == HowOver.DRAW
211
+
212
+ def is_winner(self, player: Color) -> bool:
213
+ """Checks if the specified player is the winner.
214
+
215
+ Args:
216
+ player (chess.Color): The player to check.
217
+
218
+ Returns:
219
+ bool: True if the specified player is the winner, False otherwise.
220
+
221
+ Raises:
222
+ AssertionError: If the `player` argument is not a valid value from the `chess.Color` enum.
223
+ """
224
+ assert player in {Color.WHITE, Color.BLACK}
225
+
226
+ is_winner: bool
227
+ if self.how_over == HowOver.WIN:
228
+ is_winner = bool(
229
+ self.who_is_winner == Winner.WHITE
230
+ and player == Color.WHITE
231
+ or self.who_is_winner == Winner.BLACK
232
+ and player == Color.BLACK
233
+ )
234
+ else:
235
+ is_winner = False
236
+ return is_winner
237
+
238
+ def print_info(self) -> None:
239
+ """Prints information about the `OverEvent` object."""
240
+ print(
241
+ "over_event:",
242
+ "how_over:",
243
+ self.how_over,
244
+ "who_is_winner:",
245
+ self.who_is_winner,
246
+ )
247
+
248
+ def test(self) -> None:
249
+ """Performs tests on the `OverEvent` object."""
250
+ if self.how_over == HowOver.WIN:
251
+ assert self.who_is_winner is not None
252
+ assert self.who_is_winner.is_white() or self.who_is_winner.is_black()
253
+ if self.how_over == HowOver.DRAW:
254
+ assert self.who_is_winner is None
valanga/policy.py ADDED
@@ -0,0 +1,42 @@
1
+ """
2
+ Policy-related classes and protocols for branch selection in game trees.
3
+ """
4
+
5
+ from dataclasses import dataclass
6
+ from typing import Mapping, Protocol
7
+
8
+ from valanga.evaluations import StateEvaluation
9
+ from valanga.game import BranchKey, Seed, State
10
+
11
+
12
+ @dataclass(frozen=True, slots=True)
13
+ class BranchPolicy:
14
+ """
15
+ Represents a probability distribution over branches.
16
+ """
17
+
18
+ probs: Mapping[BranchKey, float] # should sum to ~1.0
19
+
20
+
21
+ @dataclass(frozen=True, slots=True)
22
+ class Recommendation:
23
+ """A recommendation for a specific branch in a tree node."""
24
+
25
+ recommended_key: BranchKey
26
+ evaluation: StateEvaluation | None = None
27
+ policy: BranchPolicy | None = None
28
+ branch_evals: Mapping[BranchKey, StateEvaluation] | None = None
29
+
30
+
31
+ class BranchSelector(Protocol):
32
+ """Protocol for a branch selector."""
33
+
34
+ def recommend(self, state: State, seed: Seed) -> Recommendation:
35
+ """Given a state and a seed, recommends a branch to take.
36
+ Args:
37
+ state (State): The current state of the game.
38
+ seed (Seed): A seed for any randomness involved in the selection.
39
+ Returns:
40
+ Recommendation: The recommended branch to take.
41
+ """
42
+ ...
@@ -0,0 +1,20 @@
1
+ """
2
+ Module for the ProgressMessage class.
3
+ """
4
+
5
+ from dataclasses import dataclass
6
+
7
+ from .game import Color
8
+
9
+
10
+ @dataclass
11
+ class PlayerProgressMessage:
12
+ """
13
+ Represents a message containing evaluation information.
14
+
15
+ Attributes:
16
+ evaluation_stock (Any): The evaluation for the stock.
17
+ """
18
+
19
+ progress_percent: int | None
20
+ player_color: Color
valanga/py.typed ADDED
File without changes
@@ -0,0 +1,74 @@
1
+ """
2
+ Factory class for creating state representations.
3
+ """
4
+
5
+ from dataclasses import dataclass
6
+ from typing import Protocol
7
+
8
+ from .game import State, StateModifications
9
+ from .represention_for_evaluation import ContentRepresentation
10
+
11
+
12
+ class CreateFromState[T: ContentRepresentation](Protocol):
13
+ """
14
+ Protocol for creating a state representation from a state.
15
+ """
16
+
17
+ def __call__(self, state: State) -> T: ...
18
+
19
+
20
+ class CreateFromStateAndModifications[T: ContentRepresentation](Protocol):
21
+ """
22
+ Protocol for creating a state representation from a state and modifications.
23
+ """
24
+
25
+ def __call__(
26
+ self,
27
+ state: State,
28
+ state_modifications: StateModifications,
29
+ previous_state_representation: T,
30
+ ) -> T: ...
31
+
32
+
33
+ @dataclass
34
+ class RepresentationFactory[T: ContentRepresentation = ContentRepresentation]:
35
+ """
36
+ Factory class for creating state representations.
37
+ """
38
+
39
+ create_from_state: CreateFromState[T]
40
+ create_from_state_and_modifications: CreateFromStateAndModifications[T]
41
+
42
+ def create_from_transition(
43
+ self,
44
+ state: State,
45
+ previous_state_representation: T | None,
46
+ modifications: StateModifications | None,
47
+ ) -> T:
48
+ """
49
+ Create a Generic T_StateRepresentation object from a transition.
50
+
51
+ Args:
52
+ state (State): The current state of the game.
53
+ previous_state_representation (Representation364 | None): The representation of the previous state. None if this is the root node.
54
+ modifications (StateModifications | None): The modifications from the parent state to the current state. None if this is the root node.
55
+
56
+ Returns:
57
+ T_StateRepresentation: The created (Generic) Representation object
58
+
59
+ This version is supposed to be faster as it only modifies the previous state
60
+ representation with the last modification
61
+ """
62
+ if previous_state_representation is None: # this is the root_node
63
+ representation = self.create_from_state(state=state)
64
+ else:
65
+ if modifications is None:
66
+ representation = self.create_from_state(state=state)
67
+ else:
68
+ representation = self.create_from_state_and_modifications(
69
+ state=state,
70
+ state_modifications=modifications,
71
+ previous_state_representation=previous_state_representation,
72
+ )
73
+
74
+ return representation
@@ -0,0 +1,26 @@
1
+ """
2
+ Contains the definition of the ContentRepresentation protocol for content representations used in evaluations.
3
+ """
4
+
5
+ from typing import Protocol
6
+
7
+ from .evaluator_types import EvaluatorInput
8
+ from .game import State
9
+
10
+
11
+ class ContentRepresentation(Protocol):
12
+ """
13
+ Protocol defining the interface for a content representation.
14
+ It is a function returning the proper input for evaluation by the content evaluator.
15
+ """
16
+
17
+ def get_evaluator_input(self, state: State) -> EvaluatorInput:
18
+ """
19
+ Returns the evaluator input tensor for the content. Content representations have generally a compressed view and complemetary view of state info so to avoid redundancy and have all the necessary info we also give the state as input.
20
+
21
+ Args:
22
+ state: The current state of the game.
23
+
24
+ Returns:
25
+ The evaluator input tensor.
26
+ """