pycgt 0.1.0__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.
- pycgt-0.1.0/LICENSE +21 -0
- pycgt-0.1.0/PKG-INFO +153 -0
- pycgt-0.1.0/README.md +123 -0
- pycgt-0.1.0/pyproject.toml +80 -0
- pycgt-0.1.0/setup.cfg +4 -0
- pycgt-0.1.0/src/pycgt/__init__.py +130 -0
- pycgt-0.1.0/src/pycgt/_piecewise.py +208 -0
- pycgt-0.1.0/src/pycgt/game.py +341 -0
- pycgt-0.1.0/src/pycgt/notation.py +185 -0
- pycgt-0.1.0/src/pycgt/py.typed +1 -0
- pycgt-0.1.0/src/pycgt/reduced.py +64 -0
- pycgt-0.1.0/src/pycgt/rulesets/__init__.py +10 -0
- pycgt-0.1.0/src/pycgt/rulesets/cram.py +34 -0
- pycgt-0.1.0/src/pycgt/rulesets/domineering.py +42 -0
- pycgt-0.1.0/src/pycgt/rulesets/grid.py +188 -0
- pycgt-0.1.0/src/pycgt/rulesets/nim.py +62 -0
- pycgt-0.1.0/src/pycgt/stops.py +108 -0
- pycgt-0.1.0/src/pycgt/thermal.py +205 -0
- pycgt-0.1.0/src/pycgt/values.py +202 -0
- pycgt-0.1.0/src/pycgt.egg-info/PKG-INFO +153 -0
- pycgt-0.1.0/src/pycgt.egg-info/SOURCES.txt +27 -0
- pycgt-0.1.0/src/pycgt.egg-info/dependency_links.txt +1 -0
- pycgt-0.1.0/src/pycgt.egg-info/requires.txt +6 -0
- pycgt-0.1.0/src/pycgt.egg-info/top_level.txt +1 -0
- pycgt-0.1.0/tests/test_core.py +197 -0
- pycgt-0.1.0/tests/test_published_values.py +173 -0
- pycgt-0.1.0/tests/test_rulesets.py +172 -0
- pycgt-0.1.0/tests/test_stops_reduced_notation.py +175 -0
- pycgt-0.1.0/tests/test_thermal.py +146 -0
pycgt-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 nehalahmedshaikh
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
pycgt-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pycgt
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Combinatorial game theory in pure Python: exact canonical forms, thermography, and game values
|
|
5
|
+
Author: nehalahmedshaikh
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/nehalahmedshaikh/pycgt
|
|
8
|
+
Project-URL: Repository, https://github.com/nehalahmedshaikh/pycgt
|
|
9
|
+
Project-URL: Issues, https://github.com/nehalahmedshaikh/pycgt/issues
|
|
10
|
+
Project-URL: Changelog, https://github.com/nehalahmedshaikh/pycgt/blob/main/CHANGELOG.md
|
|
11
|
+
Keywords: combinatorial-game-theory,canonical-form,surreal-numbers,thermography,temperature,domineering,nim,partizan-games,exact-arithmetic
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Science/Research
|
|
14
|
+
Classifier: Intended Audience :: Education
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Topic :: Scientific/Engineering :: Mathematics
|
|
20
|
+
Classifier: Typing :: Typed
|
|
21
|
+
Requires-Python: >=3.11
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
Provides-Extra: dev
|
|
25
|
+
Requires-Dist: pytest>=7; extra == "dev"
|
|
26
|
+
Requires-Dist: pytest-cov>=4; extra == "dev"
|
|
27
|
+
Requires-Dist: mypy>=1.8; extra == "dev"
|
|
28
|
+
Requires-Dist: ruff>=0.3; extra == "dev"
|
|
29
|
+
Dynamic: license-file
|
|
30
|
+
|
|
31
|
+
# pycgt
|
|
32
|
+
|
|
33
|
+
**Combinatorial game theory in pure Python — exact canonical forms, thermography, and game values.**
|
|
34
|
+
|
|
35
|
+
```python
|
|
36
|
+
>>> from pycgt import parse, render, temperature, mean
|
|
37
|
+
>>> from pycgt.rulesets import domineering
|
|
38
|
+
|
|
39
|
+
>>> render(domineering.rectangle(2, 4))
|
|
40
|
+
'Miny(2)'
|
|
41
|
+
>>> temperature(domineering.rectangle(2, 3))
|
|
42
|
+
Fraction(5, 4)
|
|
43
|
+
>>> render(parse("{1|-1}") + parse("{1|-1}"))
|
|
44
|
+
'0'
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
`pip install pycgt`. Python 3.11+, **no dependencies**.
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## The one idea everything rests on
|
|
52
|
+
|
|
53
|
+
Short games are **exact objects built from the empty game up**. Two values are
|
|
54
|
+
equal, or one is greater, or they are *confused* with each other — there is no
|
|
55
|
+
tolerance to set, no rounding, and no floating point anywhere. Temperatures are
|
|
56
|
+
`Fraction`s found by bisection on an exact dyadic grid.
|
|
57
|
+
|
|
58
|
+
The operation that makes this usable is **canonical form**. Two games of the
|
|
59
|
+
same value reduce to *identical* canonical forms, so structural equality
|
|
60
|
+
becomes value equality and comparison becomes a dictionary lookup. Every `Game`
|
|
61
|
+
returned by this library is canonical, which is why `==` means what you want.
|
|
62
|
+
|
|
63
|
+
## Why it exists
|
|
64
|
+
|
|
65
|
+
[CGSuite](https://www.cgsuite.org/) is the established tool in this field and
|
|
66
|
+
is far more complete than this. But it is a Java desktop application: not
|
|
67
|
+
`pip install`-able, awkward to drive from a script, and unavailable in a
|
|
68
|
+
notebook or in CI. Getting it to answer one batch of questions headlessly took
|
|
69
|
+
a from-source Maven build and a JDK version pin.
|
|
70
|
+
|
|
71
|
+
`pycgt` is for when you want game values inside ordinary Python. It has no
|
|
72
|
+
dependencies, so it also runs unchanged under Pyodide in a browser.
|
|
73
|
+
|
|
74
|
+
## What's here
|
|
75
|
+
|
|
76
|
+
| | |
|
|
77
|
+
|---|---|
|
|
78
|
+
| **Core** | canonical form, the partial order, disjunctive sums, negation, birthdays |
|
|
79
|
+
| **Numbers** | dyadic rationals both ways, Conway's simplicity rule |
|
|
80
|
+
| **Named values** | `*`, `^`, `v`, nimbers, switches, `tiny`, `miny` |
|
|
81
|
+
| **Stops** | left/right stops, confusion intervals, infinitesimality, hot/tepid tests |
|
|
82
|
+
| **Reduced form** | reduced canonical form, and `ish` — the infinitesimal remainder |
|
|
83
|
+
| **Thermography** | heating, overheating, cooling, temperature, mean value |
|
|
84
|
+
| **Notation** | render to readable text, and parse it back |
|
|
85
|
+
| **Rulesets** | Domineering, Cram, Nim, Blue-Red Hackenbush |
|
|
86
|
+
|
|
87
|
+
Games are only **partially** ordered, so `<=` and `>=` are defined but `<` and
|
|
88
|
+
`>` deliberately are not — `not (G <= H)` does not imply `G > H`. Use
|
|
89
|
+
`compare()` for the four-way answer, which includes *confused*.
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
>>> from pycgt import compare, STAR, ZERO
|
|
93
|
+
>>> compare(STAR, ZERO)
|
|
94
|
+
<Relation.CONFUSED: '||'>
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## A worked example
|
|
98
|
+
|
|
99
|
+
[`examples/berlekamp_1988.py`](examples/berlekamp_1988.py) reproduces
|
|
100
|
+
Berlekamp's *Blockbusting and Domineering* (1988) — his Table III sequences,
|
|
101
|
+
their period-5/saltus-1 structure, and the overheating bound from Appendix B.1 —
|
|
102
|
+
and checks the results against the paper.
|
|
103
|
+
|
|
104
|
+
```console
|
|
105
|
+
$ python examples/berlekamp_1988.py
|
|
106
|
+
period 5, saltus 1: holds
|
|
107
|
+
x_1 is the only non-number: 1*
|
|
108
|
+
ok G_2 = tiny-2
|
|
109
|
+
ok G_3 = +-1 + 2*tiny-2
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Correctness
|
|
113
|
+
|
|
114
|
+
The interesting values in this field are easy to get subtly wrong, so nothing
|
|
115
|
+
here is trusted because it looks right. The test suite validates against
|
|
116
|
+
**external** sources:
|
|
117
|
+
|
|
118
|
+
- **CGSuite** — canonical forms, temperatures and means of Domineering 2×n
|
|
119
|
+
boards, generated by driving CGSuite 2.2 headlessly.
|
|
120
|
+
- **Berlekamp (1988)**, *Blockbusting and Domineering*, Appendix B.1 — his
|
|
121
|
+
exact values for the 2-wide Domineering rectangles, `G₂ = tiny-2` and
|
|
122
|
+
`G₃ = ±1 + 2·tiny-2`.
|
|
123
|
+
- **Wolfe**, via Guy's Problem 4 in *Games of No Chance* — the 4×5 board is `1`.
|
|
124
|
+
- **Uiterwijk**, [arXiv:1305.3257](https://arxiv.org/pdf/1305.3257) — the
|
|
125
|
+
value of the 11×2 board.
|
|
126
|
+
- **Closed forms** — Nim sums follow exclusive-or; Hackenbush strings give the
|
|
127
|
+
expected dyadic rationals; Cram values are nimbers.
|
|
128
|
+
|
|
129
|
+
Plus internal laws that catch real bugs: `G + (−G) = 0`, transposing a
|
|
130
|
+
Domineering board negates its value, `ish(G)` is *always* infinitesimal, and
|
|
131
|
+
canonical form is idempotent.
|
|
132
|
+
|
|
133
|
+
## Provenance
|
|
134
|
+
|
|
135
|
+
Implemented clean-room from the mathematics — *Winning Ways*, Siegel's
|
|
136
|
+
*Combinatorial Game Theory*, and Berlekamp (1988). CGSuite is GPL; its source
|
|
137
|
+
was consulted **only** for the API needed to run it as a test oracle, and none
|
|
138
|
+
of its implementation was used or ported. This library is MIT.
|
|
139
|
+
|
|
140
|
+
## Limitations
|
|
141
|
+
|
|
142
|
+
Honest about scope:
|
|
143
|
+
|
|
144
|
+
- **Short games only.** No loopy games, stoppers, or `on`/`off`.
|
|
145
|
+
- **No misère theory** and no misère quotients.
|
|
146
|
+
- **Slower than CGSuite**, which is a JVM program with far more optimisation.
|
|
147
|
+
Expect exact Domineering values up to about 2×16 comfortably.
|
|
148
|
+
- **The number-temperature convention** (a number with denominator `2**k` has
|
|
149
|
+
temperature `−1/2**k`) is checked against CGSuite for `0` and `1/2` only.
|
|
150
|
+
|
|
151
|
+
## Licence
|
|
152
|
+
|
|
153
|
+
MIT.
|
pycgt-0.1.0/README.md
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# pycgt
|
|
2
|
+
|
|
3
|
+
**Combinatorial game theory in pure Python — exact canonical forms, thermography, and game values.**
|
|
4
|
+
|
|
5
|
+
```python
|
|
6
|
+
>>> from pycgt import parse, render, temperature, mean
|
|
7
|
+
>>> from pycgt.rulesets import domineering
|
|
8
|
+
|
|
9
|
+
>>> render(domineering.rectangle(2, 4))
|
|
10
|
+
'Miny(2)'
|
|
11
|
+
>>> temperature(domineering.rectangle(2, 3))
|
|
12
|
+
Fraction(5, 4)
|
|
13
|
+
>>> render(parse("{1|-1}") + parse("{1|-1}"))
|
|
14
|
+
'0'
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
`pip install pycgt`. Python 3.11+, **no dependencies**.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## The one idea everything rests on
|
|
22
|
+
|
|
23
|
+
Short games are **exact objects built from the empty game up**. Two values are
|
|
24
|
+
equal, or one is greater, or they are *confused* with each other — there is no
|
|
25
|
+
tolerance to set, no rounding, and no floating point anywhere. Temperatures are
|
|
26
|
+
`Fraction`s found by bisection on an exact dyadic grid.
|
|
27
|
+
|
|
28
|
+
The operation that makes this usable is **canonical form**. Two games of the
|
|
29
|
+
same value reduce to *identical* canonical forms, so structural equality
|
|
30
|
+
becomes value equality and comparison becomes a dictionary lookup. Every `Game`
|
|
31
|
+
returned by this library is canonical, which is why `==` means what you want.
|
|
32
|
+
|
|
33
|
+
## Why it exists
|
|
34
|
+
|
|
35
|
+
[CGSuite](https://www.cgsuite.org/) is the established tool in this field and
|
|
36
|
+
is far more complete than this. But it is a Java desktop application: not
|
|
37
|
+
`pip install`-able, awkward to drive from a script, and unavailable in a
|
|
38
|
+
notebook or in CI. Getting it to answer one batch of questions headlessly took
|
|
39
|
+
a from-source Maven build and a JDK version pin.
|
|
40
|
+
|
|
41
|
+
`pycgt` is for when you want game values inside ordinary Python. It has no
|
|
42
|
+
dependencies, so it also runs unchanged under Pyodide in a browser.
|
|
43
|
+
|
|
44
|
+
## What's here
|
|
45
|
+
|
|
46
|
+
| | |
|
|
47
|
+
|---|---|
|
|
48
|
+
| **Core** | canonical form, the partial order, disjunctive sums, negation, birthdays |
|
|
49
|
+
| **Numbers** | dyadic rationals both ways, Conway's simplicity rule |
|
|
50
|
+
| **Named values** | `*`, `^`, `v`, nimbers, switches, `tiny`, `miny` |
|
|
51
|
+
| **Stops** | left/right stops, confusion intervals, infinitesimality, hot/tepid tests |
|
|
52
|
+
| **Reduced form** | reduced canonical form, and `ish` — the infinitesimal remainder |
|
|
53
|
+
| **Thermography** | heating, overheating, cooling, temperature, mean value |
|
|
54
|
+
| **Notation** | render to readable text, and parse it back |
|
|
55
|
+
| **Rulesets** | Domineering, Cram, Nim, Blue-Red Hackenbush |
|
|
56
|
+
|
|
57
|
+
Games are only **partially** ordered, so `<=` and `>=` are defined but `<` and
|
|
58
|
+
`>` deliberately are not — `not (G <= H)` does not imply `G > H`. Use
|
|
59
|
+
`compare()` for the four-way answer, which includes *confused*.
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
>>> from pycgt import compare, STAR, ZERO
|
|
63
|
+
>>> compare(STAR, ZERO)
|
|
64
|
+
<Relation.CONFUSED: '||'>
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## A worked example
|
|
68
|
+
|
|
69
|
+
[`examples/berlekamp_1988.py`](examples/berlekamp_1988.py) reproduces
|
|
70
|
+
Berlekamp's *Blockbusting and Domineering* (1988) — his Table III sequences,
|
|
71
|
+
their period-5/saltus-1 structure, and the overheating bound from Appendix B.1 —
|
|
72
|
+
and checks the results against the paper.
|
|
73
|
+
|
|
74
|
+
```console
|
|
75
|
+
$ python examples/berlekamp_1988.py
|
|
76
|
+
period 5, saltus 1: holds
|
|
77
|
+
x_1 is the only non-number: 1*
|
|
78
|
+
ok G_2 = tiny-2
|
|
79
|
+
ok G_3 = +-1 + 2*tiny-2
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Correctness
|
|
83
|
+
|
|
84
|
+
The interesting values in this field are easy to get subtly wrong, so nothing
|
|
85
|
+
here is trusted because it looks right. The test suite validates against
|
|
86
|
+
**external** sources:
|
|
87
|
+
|
|
88
|
+
- **CGSuite** — canonical forms, temperatures and means of Domineering 2×n
|
|
89
|
+
boards, generated by driving CGSuite 2.2 headlessly.
|
|
90
|
+
- **Berlekamp (1988)**, *Blockbusting and Domineering*, Appendix B.1 — his
|
|
91
|
+
exact values for the 2-wide Domineering rectangles, `G₂ = tiny-2` and
|
|
92
|
+
`G₃ = ±1 + 2·tiny-2`.
|
|
93
|
+
- **Wolfe**, via Guy's Problem 4 in *Games of No Chance* — the 4×5 board is `1`.
|
|
94
|
+
- **Uiterwijk**, [arXiv:1305.3257](https://arxiv.org/pdf/1305.3257) — the
|
|
95
|
+
value of the 11×2 board.
|
|
96
|
+
- **Closed forms** — Nim sums follow exclusive-or; Hackenbush strings give the
|
|
97
|
+
expected dyadic rationals; Cram values are nimbers.
|
|
98
|
+
|
|
99
|
+
Plus internal laws that catch real bugs: `G + (−G) = 0`, transposing a
|
|
100
|
+
Domineering board negates its value, `ish(G)` is *always* infinitesimal, and
|
|
101
|
+
canonical form is idempotent.
|
|
102
|
+
|
|
103
|
+
## Provenance
|
|
104
|
+
|
|
105
|
+
Implemented clean-room from the mathematics — *Winning Ways*, Siegel's
|
|
106
|
+
*Combinatorial Game Theory*, and Berlekamp (1988). CGSuite is GPL; its source
|
|
107
|
+
was consulted **only** for the API needed to run it as a test oracle, and none
|
|
108
|
+
of its implementation was used or ported. This library is MIT.
|
|
109
|
+
|
|
110
|
+
## Limitations
|
|
111
|
+
|
|
112
|
+
Honest about scope:
|
|
113
|
+
|
|
114
|
+
- **Short games only.** No loopy games, stoppers, or `on`/`off`.
|
|
115
|
+
- **No misère theory** and no misère quotients.
|
|
116
|
+
- **Slower than CGSuite**, which is a JVM program with far more optimisation.
|
|
117
|
+
Expect exact Domineering values up to about 2×16 comfortably.
|
|
118
|
+
- **The number-temperature convention** (a number with denominator `2**k` has
|
|
119
|
+
temperature `−1/2**k`) is checked against CGSuite for `0` and `1/2` only.
|
|
120
|
+
|
|
121
|
+
## Licence
|
|
122
|
+
|
|
123
|
+
MIT.
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "pycgt"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Combinatorial game theory in pure Python: exact canonical forms, thermography, and game values"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.11"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [{ name = "nehalahmedshaikh" }]
|
|
13
|
+
keywords = [
|
|
14
|
+
"combinatorial-game-theory",
|
|
15
|
+
"canonical-form",
|
|
16
|
+
"surreal-numbers",
|
|
17
|
+
"thermography",
|
|
18
|
+
"temperature",
|
|
19
|
+
"domineering",
|
|
20
|
+
"nim",
|
|
21
|
+
"partizan-games",
|
|
22
|
+
"exact-arithmetic",
|
|
23
|
+
]
|
|
24
|
+
classifiers = [
|
|
25
|
+
"Development Status :: 3 - Alpha",
|
|
26
|
+
"Intended Audience :: Science/Research",
|
|
27
|
+
"Intended Audience :: Education",
|
|
28
|
+
"License :: OSI Approved :: MIT License",
|
|
29
|
+
"Programming Language :: Python :: 3.11",
|
|
30
|
+
"Programming Language :: Python :: 3.12",
|
|
31
|
+
"Programming Language :: Python :: 3.13",
|
|
32
|
+
"Topic :: Scientific/Engineering :: Mathematics",
|
|
33
|
+
"Typing :: Typed",
|
|
34
|
+
]
|
|
35
|
+
# Intentionally empty and meant to stay that way: the library must run
|
|
36
|
+
# unchanged under Pyodide, so it works in a browser and in notebooks.
|
|
37
|
+
dependencies = []
|
|
38
|
+
|
|
39
|
+
[project.optional-dependencies]
|
|
40
|
+
dev = ["pytest>=7", "pytest-cov>=4", "mypy>=1.8", "ruff>=0.3"]
|
|
41
|
+
|
|
42
|
+
[project.urls]
|
|
43
|
+
Homepage = "https://github.com/nehalahmedshaikh/pycgt"
|
|
44
|
+
Repository = "https://github.com/nehalahmedshaikh/pycgt"
|
|
45
|
+
Issues = "https://github.com/nehalahmedshaikh/pycgt/issues"
|
|
46
|
+
Changelog = "https://github.com/nehalahmedshaikh/pycgt/blob/main/CHANGELOG.md"
|
|
47
|
+
|
|
48
|
+
[tool.setuptools.packages.find]
|
|
49
|
+
where = ["src"]
|
|
50
|
+
|
|
51
|
+
[tool.setuptools.package-data]
|
|
52
|
+
pycgt = ["py.typed"]
|
|
53
|
+
|
|
54
|
+
[tool.pytest.ini_options]
|
|
55
|
+
testpaths = ["tests"]
|
|
56
|
+
pythonpath = ["src"]
|
|
57
|
+
addopts = "--strict-markers"
|
|
58
|
+
markers = ["slow: takes more than a second to run"]
|
|
59
|
+
|
|
60
|
+
[tool.mypy]
|
|
61
|
+
python_version = "3.11"
|
|
62
|
+
strict = true
|
|
63
|
+
files = ["src/pycgt"]
|
|
64
|
+
|
|
65
|
+
[tool.ruff]
|
|
66
|
+
line-length = 88
|
|
67
|
+
target-version = "py311"
|
|
68
|
+
|
|
69
|
+
[tool.ruff.lint]
|
|
70
|
+
select = ["E", "F", "I", "N", "UP", "B", "C4", "RUF"]
|
|
71
|
+
ignore = [
|
|
72
|
+
# Single letters are the notation of the field: G, H, L, R, s, t, n.
|
|
73
|
+
"E741",
|
|
74
|
+
# __all__ is grouped by topic with comments, which is far more useful for
|
|
75
|
+
# a public API than alphabetical order.
|
|
76
|
+
"RUF022",
|
|
77
|
+
]
|
|
78
|
+
|
|
79
|
+
[tool.ruff.lint.per-file-ignores]
|
|
80
|
+
"tests/*" = ["E501"]
|
pycgt-0.1.0/setup.cfg
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"""pycgt: combinatorial game theory in pure Python.
|
|
2
|
+
|
|
3
|
+
Short games are exact objects built from the empty game up, so nothing here
|
|
4
|
+
approximates: two values are equal, or one is greater, or they are *confused*
|
|
5
|
+
with each other. There is no tolerance to set and no rounding.
|
|
6
|
+
|
|
7
|
+
>>> from pycgt import parse, render
|
|
8
|
+
>>> render(parse("{1|-1}") + parse("{1|-1}"))
|
|
9
|
+
'0'
|
|
10
|
+
>>> from pycgt.rulesets import domineering
|
|
11
|
+
>>> render(domineering.rectangle(2, 4))
|
|
12
|
+
'Miny(2)'
|
|
13
|
+
>>> from pycgt import temperature
|
|
14
|
+
>>> temperature(domineering.rectangle(2, 3))
|
|
15
|
+
Fraction(5, 4)
|
|
16
|
+
|
|
17
|
+
Every :class:`Game` returned by this package is in canonical form, so ``==``
|
|
18
|
+
means value equality.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from .game import (
|
|
22
|
+
ZERO,
|
|
23
|
+
Game,
|
|
24
|
+
Outcome,
|
|
25
|
+
Relation,
|
|
26
|
+
add,
|
|
27
|
+
birthday,
|
|
28
|
+
canonical,
|
|
29
|
+
compare,
|
|
30
|
+
confused,
|
|
31
|
+
equals,
|
|
32
|
+
game,
|
|
33
|
+
geq,
|
|
34
|
+
greater,
|
|
35
|
+
leq,
|
|
36
|
+
multiple,
|
|
37
|
+
negate,
|
|
38
|
+
outcome,
|
|
39
|
+
)
|
|
40
|
+
from .notation import parse, render
|
|
41
|
+
from .reduced import is_reduced, ish, reduced_canonical_form
|
|
42
|
+
from .stops import (
|
|
43
|
+
confusion_interval,
|
|
44
|
+
is_hot,
|
|
45
|
+
is_infinitesimal,
|
|
46
|
+
is_tepid,
|
|
47
|
+
left_stop,
|
|
48
|
+
number_part,
|
|
49
|
+
right_stop,
|
|
50
|
+
stops,
|
|
51
|
+
)
|
|
52
|
+
from .thermal import cool, heat, mean, overheat, temperature, thermograph
|
|
53
|
+
from .values import (
|
|
54
|
+
DOWN,
|
|
55
|
+
STAR,
|
|
56
|
+
UP,
|
|
57
|
+
as_number,
|
|
58
|
+
integer,
|
|
59
|
+
is_number,
|
|
60
|
+
miny,
|
|
61
|
+
nimber,
|
|
62
|
+
number,
|
|
63
|
+
plus_minus,
|
|
64
|
+
simplest_between,
|
|
65
|
+
switch,
|
|
66
|
+
tiny,
|
|
67
|
+
up_multiple,
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
__version__ = "0.1.0"
|
|
71
|
+
|
|
72
|
+
__all__ = [
|
|
73
|
+
# core
|
|
74
|
+
"Game",
|
|
75
|
+
"Outcome",
|
|
76
|
+
"Relation",
|
|
77
|
+
"ZERO",
|
|
78
|
+
"game",
|
|
79
|
+
"canonical",
|
|
80
|
+
"add",
|
|
81
|
+
"negate",
|
|
82
|
+
"multiple",
|
|
83
|
+
"birthday",
|
|
84
|
+
# order
|
|
85
|
+
"geq",
|
|
86
|
+
"leq",
|
|
87
|
+
"equals",
|
|
88
|
+
"greater",
|
|
89
|
+
"confused",
|
|
90
|
+
"compare",
|
|
91
|
+
"outcome",
|
|
92
|
+
# numbers and named values
|
|
93
|
+
"number",
|
|
94
|
+
"integer",
|
|
95
|
+
"as_number",
|
|
96
|
+
"is_number",
|
|
97
|
+
"simplest_between",
|
|
98
|
+
"STAR",
|
|
99
|
+
"UP",
|
|
100
|
+
"DOWN",
|
|
101
|
+
"nimber",
|
|
102
|
+
"up_multiple",
|
|
103
|
+
"switch",
|
|
104
|
+
"plus_minus",
|
|
105
|
+
"tiny",
|
|
106
|
+
"miny",
|
|
107
|
+
# stops
|
|
108
|
+
"left_stop",
|
|
109
|
+
"right_stop",
|
|
110
|
+
"stops",
|
|
111
|
+
"confusion_interval",
|
|
112
|
+
"is_infinitesimal",
|
|
113
|
+
"is_hot",
|
|
114
|
+
"is_tepid",
|
|
115
|
+
"number_part",
|
|
116
|
+
# reduced canonical form
|
|
117
|
+
"reduced_canonical_form",
|
|
118
|
+
"ish",
|
|
119
|
+
"is_reduced",
|
|
120
|
+
# thermography
|
|
121
|
+
"heat",
|
|
122
|
+
"overheat",
|
|
123
|
+
"cool",
|
|
124
|
+
"temperature",
|
|
125
|
+
"mean",
|
|
126
|
+
"thermograph",
|
|
127
|
+
# notation
|
|
128
|
+
"render",
|
|
129
|
+
"parse",
|
|
130
|
+
]
|