valanga 0.1.1__tar.gz → 0.1.2__tar.gz
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-0.1.1/src/valanga.egg-info → valanga-0.1.2}/PKG-INFO +1 -1
- valanga-0.1.2/README.md +43 -0
- {valanga-0.1.1 → valanga-0.1.2}/pyproject.toml +1 -1
- {valanga-0.1.1 → valanga-0.1.2}/src/valanga/game.py +1 -1
- {valanga-0.1.1 → valanga-0.1.2/src/valanga.egg-info}/PKG-INFO +1 -1
- {valanga-0.1.1 → valanga-0.1.2}/src/valanga.egg-info/SOURCES.txt +3 -1
- valanga-0.1.2/tests/test_over_event.py +106 -0
- valanga-0.1.2/tests/test_representation_factory.py +70 -0
- valanga-0.1.1/README.md +0 -2
- {valanga-0.1.1 → valanga-0.1.2}/LICENSE +0 -0
- {valanga-0.1.1 → valanga-0.1.2}/setup.cfg +0 -0
- {valanga-0.1.1 → valanga-0.1.2}/src/valanga/__init__.py +0 -0
- {valanga-0.1.1 → valanga-0.1.2}/src/valanga/evaluations.py +0 -0
- {valanga-0.1.1 → valanga-0.1.2}/src/valanga/evaluator_types.py +0 -0
- {valanga-0.1.1 → valanga-0.1.2}/src/valanga/over_event.py +0 -0
- {valanga-0.1.1 → valanga-0.1.2}/src/valanga/progress_messsage.py +0 -0
- {valanga-0.1.1 → valanga-0.1.2}/src/valanga/py.typed +0 -0
- {valanga-0.1.1 → valanga-0.1.2}/src/valanga/representation_factory.py +0 -0
- {valanga-0.1.1 → valanga-0.1.2}/src/valanga/represention_for_evaluation.py +0 -0
- {valanga-0.1.1 → valanga-0.1.2}/src/valanga.egg-info/dependency_links.txt +0 -0
- {valanga-0.1.1 → valanga-0.1.2}/src/valanga.egg-info/requires.txt +0 -0
- {valanga-0.1.1 → valanga-0.1.2}/src/valanga.egg-info/top_level.txt +0 -0
- {valanga-0.1.1 → valanga-0.1.2}/tests/test_placeholder.py +0 -0
valanga-0.1.2/README.md
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# valanga
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
[](https://github.com/victorgabillon/valanga/actions/workflows/ci.yaml)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
|
|
7
|
+
Shared Python types and lightweight utilities for describing turn-based games and their evaluations. The package exposes protocols for representing states, outcomes, and state representations that other libraries can build on.
|
|
8
|
+
|
|
9
|
+
## Key concepts
|
|
10
|
+
- **Game primitives**: `Color`, `BranchKey`, `State`, and related protocols describe whose turn it is, how to enumerate legal branches, and how to copy or advance a state. 【F:src/valanga/game.py†L14-L95】
|
|
11
|
+
- **Evaluations**: `FloatyStateEvaluation` captures heuristic scores while `ForcedOutcome` records a definitive result plus the line of play that forces it. Both are grouped under `BoardEvaluation`. 【F:src/valanga/evaluations.py†L13-L43】
|
|
12
|
+
- **Game termination**: `OverEvent` combines how a game ended with who won, backed by `HowOver` and `Winner` enums. Utility helpers like `is_over()` and `get_over_tag()` simplify downstream checks. 【F:src/valanga/over_event.py†L8-L115】
|
|
13
|
+
- **State representations**: `ContentRepresentation` defines how to turn a `State` into evaluator input, and `RepresentationFactory` builds or updates those representations from states and modifications. 【F:src/valanga/represention_for_evaluation.py†L9-L22】【F:src/valanga/representation_factory.py†L7-L55】
|
|
14
|
+
- **Progress reporting**: `PlayerProgressMessage` communicates per-player progress percentages during long-running work. 【F:src/valanga/progress_messsage.py†L9-L18】
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
```bash
|
|
18
|
+
pip install .
|
|
19
|
+
```
|
|
20
|
+
The project targets Python 3.13 and has no required runtime dependencies.
|
|
21
|
+
|
|
22
|
+
## Quick start
|
|
23
|
+
Below is a minimal example that marks a finished game and obtains an evaluation structure:
|
|
24
|
+
```python
|
|
25
|
+
from valanga import ForcedOutcome, OverEvent
|
|
26
|
+
from valanga.over_event import HowOver, Winner
|
|
27
|
+
|
|
28
|
+
# A checkmate where white wins.
|
|
29
|
+
over_event = OverEvent(how_over=HowOver.WIN, who_is_winner=Winner.WHITE)
|
|
30
|
+
|
|
31
|
+
# Record the forced outcome and the line of optimal moves that lead to it.
|
|
32
|
+
forced = ForcedOutcome(outcome=over_event, line=["e2e4", "e7e5"])
|
|
33
|
+
print(forced.outcome.get_over_tag()) # -> OverTags.TAG_WIN_WHITE
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
To wire in a custom state representation, supply callables to `RepresentationFactory` that know how to build representations from a `State` and its modifications.
|
|
37
|
+
|
|
38
|
+
## Development
|
|
39
|
+
Install the development dependencies and run the test suite:
|
|
40
|
+
```bash
|
|
41
|
+
pip install -e '.[dev]'
|
|
42
|
+
pytest
|
|
43
|
+
```
|
|
@@ -16,7 +16,7 @@ type StateModifications = Annotated[
|
|
|
16
16
|
type BranchKey = Annotated[Hashable, "A label or identifier for a branch in a tree"]
|
|
17
17
|
|
|
18
18
|
|
|
19
|
-
T_co = TypeVar("T_co", bound=BranchKey, covariant=True)
|
|
19
|
+
T_co = TypeVar("T_co", bound=BranchKey, covariant=True, default=BranchKey)
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
class BranchKeyGeneratorP(Protocol[T_co]):
|
|
@@ -15,4 +15,6 @@ src/valanga.egg-info/SOURCES.txt
|
|
|
15
15
|
src/valanga.egg-info/dependency_links.txt
|
|
16
16
|
src/valanga.egg-info/requires.txt
|
|
17
17
|
src/valanga.egg-info/top_level.txt
|
|
18
|
-
tests/
|
|
18
|
+
tests/test_over_event.py
|
|
19
|
+
tests/test_placeholder.py
|
|
20
|
+
tests/test_representation_factory.py
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from valanga.game import Color
|
|
4
|
+
from valanga.over_event import HowOver, OverEvent, OverTags, Winner
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@pytest.mark.parametrize(
|
|
8
|
+
"who, expected",
|
|
9
|
+
[
|
|
10
|
+
(Winner.WHITE, OverTags.TAG_WIN_WHITE),
|
|
11
|
+
(Winner.BLACK, OverTags.TAG_WIN_BLACK),
|
|
12
|
+
],
|
|
13
|
+
)
|
|
14
|
+
def test_get_over_tag_win_outcomes(who, expected):
|
|
15
|
+
event = OverEvent(how_over=HowOver.WIN, who_is_winner=who)
|
|
16
|
+
|
|
17
|
+
assert event.get_over_tag() == expected
|
|
18
|
+
assert event.is_over() is True
|
|
19
|
+
assert event.is_win() is True
|
|
20
|
+
assert event.is_draw() is False
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@pytest.mark.parametrize("winner", [Winner.WHITE, Winner.BLACK])
|
|
24
|
+
def test_is_winner_matches_color(winner):
|
|
25
|
+
event = OverEvent(how_over=HowOver.WIN, who_is_winner=winner)
|
|
26
|
+
|
|
27
|
+
assert event.is_winner(Color.WHITE) is (winner == Winner.WHITE)
|
|
28
|
+
assert event.is_winner(Color.BLACK) is (winner == Winner.BLACK)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@pytest.mark.parametrize(
|
|
32
|
+
"how_over, winner, expected",
|
|
33
|
+
[
|
|
34
|
+
(HowOver.DRAW, Winner.NO_KNOWN_WINNER, OverTags.TAG_DRAW),
|
|
35
|
+
(HowOver.DO_NOT_KNOW_OVER, Winner.NO_KNOWN_WINNER, OverTags.TAG_DO_NOT_KNOW),
|
|
36
|
+
],
|
|
37
|
+
)
|
|
38
|
+
def test_get_over_tag_draw_and_unknown(how_over, winner, expected):
|
|
39
|
+
event = OverEvent(how_over=how_over, who_is_winner=winner)
|
|
40
|
+
|
|
41
|
+
assert event.get_over_tag() == expected
|
|
42
|
+
assert event.is_over() is (how_over == HowOver.DRAW)
|
|
43
|
+
assert event.is_win() is False
|
|
44
|
+
assert event.is_draw() is (how_over == HowOver.DRAW)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@pytest.mark.parametrize(
|
|
48
|
+
"how_over, who_is_winner",
|
|
49
|
+
[
|
|
50
|
+
(HowOver.WIN, Winner.NO_KNOWN_WINNER),
|
|
51
|
+
(HowOver.DRAW, Winner.WHITE),
|
|
52
|
+
],
|
|
53
|
+
)
|
|
54
|
+
def test_post_init_validates_winner_configuration(how_over, who_is_winner):
|
|
55
|
+
with pytest.raises(AssertionError):
|
|
56
|
+
OverEvent(how_over=how_over, who_is_winner=who_is_winner)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@pytest.mark.parametrize("player", [Color.WHITE, Color.BLACK])
|
|
60
|
+
def test_is_winner_requires_win(player):
|
|
61
|
+
event = OverEvent(how_over=HowOver.DRAW, who_is_winner=Winner.NO_KNOWN_WINNER)
|
|
62
|
+
|
|
63
|
+
assert event.is_winner(player) is False
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@pytest.mark.parametrize(
|
|
67
|
+
"how_over, expected_exception",
|
|
68
|
+
[
|
|
69
|
+
(HowOver.WIN, ValueError),
|
|
70
|
+
(None, ValueError),
|
|
71
|
+
],
|
|
72
|
+
)
|
|
73
|
+
def test_get_over_tag_invalid_configurations(how_over, expected_exception):
|
|
74
|
+
event = OverEvent(how_over=HowOver.WIN, who_is_winner=Winner.WHITE)
|
|
75
|
+
event.how_over = how_over # mutate to bypass post-init validation
|
|
76
|
+
event.who_is_winner = Winner.NO_KNOWN_WINNER
|
|
77
|
+
|
|
78
|
+
with pytest.raises(expected_exception):
|
|
79
|
+
event.get_over_tag()
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
@pytest.mark.parametrize(
|
|
83
|
+
"how_over, who_is_winner",
|
|
84
|
+
[
|
|
85
|
+
(HowOver.WIN, Winner.WHITE),
|
|
86
|
+
(HowOver.WIN, Winner.BLACK),
|
|
87
|
+
],
|
|
88
|
+
)
|
|
89
|
+
def test_test_method_for_wins(how_over, who_is_winner):
|
|
90
|
+
event = OverEvent(how_over=how_over, who_is_winner=who_is_winner)
|
|
91
|
+
|
|
92
|
+
event.test()
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def test_test_method_invalid_draw_configuration():
|
|
96
|
+
event = OverEvent(how_over=HowOver.DRAW, who_is_winner=Winner.NO_KNOWN_WINNER)
|
|
97
|
+
|
|
98
|
+
with pytest.raises(AssertionError):
|
|
99
|
+
event.test()
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def test_bool_raises():
|
|
103
|
+
event = OverEvent()
|
|
104
|
+
|
|
105
|
+
with pytest.raises(ValueError):
|
|
106
|
+
bool(event)
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
from collections import Counter
|
|
2
|
+
|
|
3
|
+
from valanga.representation_factory import RepresentationFactory
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class DummyRepresentation:
|
|
7
|
+
pass
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def test_create_from_transition_root_uses_full_creator():
|
|
11
|
+
calls = Counter()
|
|
12
|
+
|
|
13
|
+
def create_from_state(state):
|
|
14
|
+
calls["create_from_state"] += 1
|
|
15
|
+
return ("state", state)
|
|
16
|
+
|
|
17
|
+
factory = RepresentationFactory(
|
|
18
|
+
create_from_state=create_from_state, create_from_state_and_modifications=None
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
result = factory.create_from_transition(
|
|
22
|
+
state="root", previous_state_representation=None, modifications=None
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
assert result == ("state", "root")
|
|
26
|
+
assert calls["create_from_state"] == 1
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def test_create_from_transition_no_modifications_falls_back_to_state():
|
|
30
|
+
calls = Counter()
|
|
31
|
+
|
|
32
|
+
def create_from_state(state):
|
|
33
|
+
calls["create_from_state"] += 1
|
|
34
|
+
return ("state", state)
|
|
35
|
+
|
|
36
|
+
factory = RepresentationFactory(
|
|
37
|
+
create_from_state=create_from_state, create_from_state_and_modifications=None
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
result = factory.create_from_transition(
|
|
41
|
+
state="child",
|
|
42
|
+
previous_state_representation=DummyRepresentation(),
|
|
43
|
+
modifications=None,
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
assert result == ("state", "child")
|
|
47
|
+
assert calls["create_from_state"] == 1
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def test_create_from_transition_with_modifications():
|
|
51
|
+
calls = Counter()
|
|
52
|
+
|
|
53
|
+
def create_from_state_and_modifications(
|
|
54
|
+
state, state_modifications, previous_state_representation
|
|
55
|
+
):
|
|
56
|
+
calls["create_from_state_and_modifications"] += 1
|
|
57
|
+
return (state, state_modifications, previous_state_representation)
|
|
58
|
+
|
|
59
|
+
factory = RepresentationFactory(
|
|
60
|
+
create_from_state=lambda state: ("unused", state),
|
|
61
|
+
create_from_state_and_modifications=create_from_state_and_modifications,
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
previous = DummyRepresentation()
|
|
65
|
+
result = factory.create_from_transition(
|
|
66
|
+
state="child", previous_state_representation=previous, modifications="delta"
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
assert result == ("child", "delta", previous)
|
|
70
|
+
assert calls["create_from_state_and_modifications"] == 1
|
valanga-0.1.1/README.md
DELETED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|