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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: valanga
3
- Version: 0.1.1
3
+ Version: 0.1.2
4
4
  Summary: Shared types and utilities for evalaution
5
5
  Requires-Python: >=3.13
6
6
  License-File: LICENSE
@@ -0,0 +1,43 @@
1
+ # valanga
2
+
3
+ ![Python 3.13](https://img.shields.io/badge/python-3.13-blue?logo=python)
4
+ [![Tests](https://github.com/victorgabillon/valanga/actions/workflows/ci.yaml/badge.svg?label=tests)](https://github.com/victorgabillon/valanga/actions/workflows/ci.yaml)
5
+ [![License: GPL v3](https://img.shields.io/github/license/victorgabillon/valanga)](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
+ ```
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "valanga"
7
- version = "0.1.1"
7
+ version = "0.1.2"
8
8
  description = "Shared types and utilities for evalaution"
9
9
  requires-python = ">=3.13"
10
10
  dependencies = []
@@ -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]):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: valanga
3
- Version: 0.1.1
3
+ Version: 0.1.2
4
4
  Summary: Shared types and utilities for evalaution
5
5
  Requires-Python: >=3.13
6
6
  License-File: LICENSE
@@ -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/test_placeholder.py
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
@@ -1,2 +0,0 @@
1
- # valanga
2
- Common objects for evaluations
File without changes
File without changes
File without changes
File without changes