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 +42 -0
- valanga/evaluations.py +58 -0
- valanga/evaluator_types.py +13 -0
- valanga/game.py +158 -0
- valanga/over_event.py +254 -0
- valanga/policy.py +42 -0
- valanga/progress_messsage.py +20 -0
- valanga/py.typed +0 -0
- valanga/representation_factory.py +74 -0
- valanga/represention_for_evaluation.py +26 -0
- valanga-0.1.3.dist-info/METADATA +31 -0
- valanga-0.1.3.dist-info/RECORD +15 -0
- valanga-0.1.3.dist-info/WHEEL +5 -0
- valanga-0.1.3.dist-info/licenses/LICENSE +674 -0
- valanga-0.1.3.dist-info/top_level.txt +1 -0
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
|
+
"""
|