pkcore-py 0.9.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.
- pkcore_py-0.9.0/.github/workflows/ci.yml +43 -0
- pkcore_py-0.9.0/.github/workflows/publish.yml +93 -0
- pkcore_py-0.9.0/.gitignore +11 -0
- pkcore_py-0.9.0/CHANGELOG.md +225 -0
- pkcore_py-0.9.0/CLAUDE.md +7 -0
- pkcore_py-0.9.0/Cargo.lock +1688 -0
- pkcore_py-0.9.0/Cargo.toml +20 -0
- pkcore_py-0.9.0/LICENSE-APACHE +201 -0
- pkcore_py-0.9.0/LICENSE-MIT +21 -0
- pkcore_py-0.9.0/Makefile +94 -0
- pkcore_py-0.9.0/PKG-INFO +907 -0
- pkcore_py-0.9.0/README.md +886 -0
- pkcore_py-0.9.0/demo.py +218 -0
- pkcore_py-0.9.0/docs/STACK.md +35 -0
- pkcore_py-0.9.0/docs/pypi-publishing-plan.md +228 -0
- pkcore_py-0.9.0/docs/superpowers/plans/2026-04-28-pkpy-0.0.52-upgrade.md +1742 -0
- pkcore_py-0.9.0/docs/superpowers/plans/2026-04-29-pkpy-pokersession-tablenocell.md +1215 -0
- pkcore_py-0.9.0/docs/superpowers/specs/2026-04-28-pkpy-0.0.52-upgrade-design.md +137 -0
- pkcore_py-0.9.0/docs/superpowers/specs/2026-04-29-pkpy-pokersession-tablenocell-design.md +319 -0
- pkcore_py-0.9.0/examples/calc.py +150 -0
- pkcore_py-0.9.0/examples/gto.py +90 -0
- pkcore_py-0.9.0/examples/the_hand.py +125 -0
- pkcore_py-0.9.0/pyproject.toml +35 -0
- pkcore_py-0.9.0/python/pkcore/__init__.py +143 -0
- pkcore_py-0.9.0/src/bot.rs +7 -0
- pkcore_py-0.9.0/src/hand_history.rs +7 -0
- pkcore_py-0.9.0/src/lib.rs +4089 -0
- pkcore_py-0.9.0/src/session.rs +236 -0
- pkcore_py-0.9.0/src/stats.rs +7 -0
- pkcore_py-0.9.0/src/table_no_cell.rs +231 -0
- pkcore_py-0.9.0/tests/test_pkcore.py +1142 -0
- pkcore_py-0.9.0/tests/test_session.py +174 -0
- pkcore_py-0.9.0/tests/test_table_no_cell.py +140 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
|
|
15
|
+
- name: Set up Python
|
|
16
|
+
uses: actions/setup-python@v5
|
|
17
|
+
with:
|
|
18
|
+
python-version: "3.13"
|
|
19
|
+
|
|
20
|
+
- name: Set up Rust
|
|
21
|
+
uses: dtolnay/rust-toolchain@stable
|
|
22
|
+
|
|
23
|
+
- name: Cache Rust build
|
|
24
|
+
uses: actions/cache@v4
|
|
25
|
+
with:
|
|
26
|
+
path: |
|
|
27
|
+
~/.cargo/registry
|
|
28
|
+
~/.cargo/git
|
|
29
|
+
target
|
|
30
|
+
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
|
31
|
+
restore-keys: ${{ runner.os }}-cargo-
|
|
32
|
+
|
|
33
|
+
- name: Create virtualenv
|
|
34
|
+
run: python -m venv .venv
|
|
35
|
+
|
|
36
|
+
- name: Install maturin and pytest
|
|
37
|
+
run: .venv/bin/pip install maturin pytest
|
|
38
|
+
|
|
39
|
+
- name: Build and install wheel
|
|
40
|
+
run: .venv/bin/maturin develop
|
|
41
|
+
|
|
42
|
+
- name: Run tests
|
|
43
|
+
run: .venv/bin/pytest
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
id-token: write # required for PyPI trusted publishing
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
linux:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
strategy:
|
|
15
|
+
matrix:
|
|
16
|
+
target: [x86_64, aarch64]
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v4
|
|
19
|
+
- uses: PyO3/maturin-action@v1
|
|
20
|
+
with:
|
|
21
|
+
target: ${{ matrix.target }}
|
|
22
|
+
args: --release --out dist --find-interpreter
|
|
23
|
+
sccache: "true"
|
|
24
|
+
manylinux: auto
|
|
25
|
+
- uses: actions/upload-artifact@v4
|
|
26
|
+
with:
|
|
27
|
+
name: wheels-linux-${{ matrix.target }}
|
|
28
|
+
path: dist
|
|
29
|
+
|
|
30
|
+
macos:
|
|
31
|
+
runs-on: macos-latest
|
|
32
|
+
strategy:
|
|
33
|
+
matrix:
|
|
34
|
+
target: [x86_64, aarch64]
|
|
35
|
+
steps:
|
|
36
|
+
- uses: actions/checkout@v4
|
|
37
|
+
- uses: PyO3/maturin-action@v1
|
|
38
|
+
with:
|
|
39
|
+
target: ${{ matrix.target }}
|
|
40
|
+
args: --release --out dist --find-interpreter
|
|
41
|
+
sccache: "true"
|
|
42
|
+
- uses: actions/upload-artifact@v4
|
|
43
|
+
with:
|
|
44
|
+
name: wheels-macos-${{ matrix.target }}
|
|
45
|
+
path: dist
|
|
46
|
+
|
|
47
|
+
windows:
|
|
48
|
+
runs-on: windows-latest
|
|
49
|
+
strategy:
|
|
50
|
+
matrix:
|
|
51
|
+
target: [x86_64]
|
|
52
|
+
steps:
|
|
53
|
+
- uses: actions/checkout@v4
|
|
54
|
+
- uses: PyO3/maturin-action@v1
|
|
55
|
+
with:
|
|
56
|
+
target: ${{ matrix.target }}
|
|
57
|
+
args: --release --out dist --find-interpreter
|
|
58
|
+
sccache: "true"
|
|
59
|
+
- uses: actions/upload-artifact@v4
|
|
60
|
+
with:
|
|
61
|
+
name: wheels-windows-${{ matrix.target }}
|
|
62
|
+
path: dist
|
|
63
|
+
|
|
64
|
+
sdist:
|
|
65
|
+
runs-on: ubuntu-latest
|
|
66
|
+
steps:
|
|
67
|
+
- uses: actions/checkout@v4
|
|
68
|
+
- uses: PyO3/maturin-action@v1
|
|
69
|
+
with:
|
|
70
|
+
command: sdist
|
|
71
|
+
args: --out dist
|
|
72
|
+
- uses: actions/upload-artifact@v4
|
|
73
|
+
with:
|
|
74
|
+
name: wheels-sdist
|
|
75
|
+
path: dist
|
|
76
|
+
|
|
77
|
+
publish:
|
|
78
|
+
name: Publish to PyPI
|
|
79
|
+
runs-on: ubuntu-latest
|
|
80
|
+
needs: [linux, macos, windows, sdist]
|
|
81
|
+
environment:
|
|
82
|
+
name: pypi
|
|
83
|
+
url: https://pypi.org/project/pkcore.py/
|
|
84
|
+
steps:
|
|
85
|
+
- uses: actions/download-artifact@v4
|
|
86
|
+
with:
|
|
87
|
+
pattern: wheels-*
|
|
88
|
+
merge-multiple: true
|
|
89
|
+
path: dist
|
|
90
|
+
- uses: PyO3/maturin-action@v1
|
|
91
|
+
with:
|
|
92
|
+
command: upload
|
|
93
|
+
args: --non-interactive --skip-existing dist/*
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project tracks [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
The crate version is kept in lockstep with the underlying `pkcore` dependency.
|
|
8
|
+
|
|
9
|
+
## [Unreleased]
|
|
10
|
+
|
|
11
|
+
### Breaking (package rename)
|
|
12
|
+
|
|
13
|
+
- Renamed the project from `pkpy` to `pkcore.py`. The PyPI distribution is now
|
|
14
|
+
`pkcore.py` (was `pkpython`) and the Python import is `pkcore` (was `pkpy`):
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pip install pkcore.py
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
```python
|
|
21
|
+
from pkcore import Card # was: from pkpy import Card
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
The Rust crate is `pkcore-py` (Cargo names cannot contain a dot; the crate is
|
|
25
|
+
not published to crates.io). The compiled extension module is
|
|
26
|
+
`pkcore._pkcore` (was `pkpy._pkpy`). The repository moved to
|
|
27
|
+
`ImperialBower/pkcore.py`; the old GitHub URL redirects. `pkpython` on PyPI
|
|
28
|
+
receives no further releases.
|
|
29
|
+
|
|
30
|
+
### Changed
|
|
31
|
+
|
|
32
|
+
- Bumped `pkcore` dependency from `0.8.0` to `0.9.0`. No code changes were
|
|
33
|
+
needed; the crate compiles cleanly against the new version.
|
|
34
|
+
- Bumped `pkpy` crate version to `0.9.0` to stay in lockstep with `pkcore`.
|
|
35
|
+
|
|
36
|
+
- Relicensed from `GPL-3.0-or-later` to `MIT OR Apache-2.0`, matching `pkcore`.
|
|
37
|
+
Replaced `LICENSE` with `LICENSE-MIT` and `LICENSE-APACHE`; updated
|
|
38
|
+
`Cargo.toml`, `pyproject.toml` classifiers, and the README badges.
|
|
39
|
+
|
|
40
|
+
## [0.7.0] - 2026-08-22
|
|
41
|
+
|
|
42
|
+
### Changed
|
|
43
|
+
|
|
44
|
+
- Bumped `pkcore` dependency from `0.2.1` to `0.7.0`, skipping the `0.3`, `0.4`,
|
|
45
|
+
`0.5` and `0.6` lines.
|
|
46
|
+
- Bumped `pkpy` crate version to `0.7.0` to stay in lockstep with `pkcore`.
|
|
47
|
+
|
|
48
|
+
### Breaking (Python API)
|
|
49
|
+
|
|
50
|
+
- **`Deck.get(index)` returns `Card | None` instead of `Card`.** `pkcore` 0.7.0
|
|
51
|
+
made `Deck::get` return `Option<Card>` rather than indexing out of bounds, so
|
|
52
|
+
an index outside `0..=51` is now `None` in Python instead of an abort. Callers
|
|
53
|
+
that trusted the index are unaffected; callers that pass computed indices
|
|
54
|
+
should check for `None`.
|
|
55
|
+
- **`PokerSession.next_actor()` raises instead of returning `None` on a failed
|
|
56
|
+
street advance.** `pkcore` 0.7.0 changed `next_actor` to
|
|
57
|
+
`Result<Option<u8>, PKError>` (`DEFECT_019`). Previously a dry deck collapsed
|
|
58
|
+
to `None`, which every `while (actor := session.next_actor()) is not None`
|
|
59
|
+
loop reads as "hand over" — `end_hand()` then raised `ActionIsntFinished` and
|
|
60
|
+
the pot was stranded. Only "no streets remain" is still `None`; a real failure
|
|
61
|
+
now raises `ValueError`. Recover with `abort_hand()`.
|
|
62
|
+
- **`KuhnCfr.train(iterations)` can raise.** `pkcore` 0.7.0 changed
|
|
63
|
+
`KuhnCfr::train` to return `Result<(), PKError>`. pkpy previously discarded
|
|
64
|
+
that result, so a training error produced a silently half-trained strategy.
|
|
65
|
+
- **`SevenFiveBCM.from_cards` and `IndexCardMap.from_cards` raise `ValueError` on
|
|
66
|
+
an unsupported card count.** `pkcore` 0.6.0 replaced `Ok(Self::default())` with
|
|
67
|
+
`Err(PKError::InvalidCardCount)`. Code that relied on a rank-0 default for a
|
|
68
|
+
three-card input must catch the error instead.
|
|
69
|
+
|
|
70
|
+
### Added
|
|
71
|
+
|
|
72
|
+
- **`PokerSession.abort_hand()` → `int`.** Abandons a hand that cannot continue,
|
|
73
|
+
returning each player's committed chips and resetting the table. This is the
|
|
74
|
+
documented escape hatch for a `Failed` step and for a raising `next_actor()`;
|
|
75
|
+
`end_hand()` cannot settle such a hand because there was no showdown.
|
|
76
|
+
- **`SessionStep` gained the `"Failed"` kind and a `SessionStep.error()`
|
|
77
|
+
accessor.** `pkcore` 0.7.0 added `SessionStep::Failed(PKError)` for a hand that
|
|
78
|
+
cannot continue (dealing or chip collection failed mid-hand). `kind()` now
|
|
79
|
+
returns `"Failed"` for it and `error()` returns the message as a `str`; it
|
|
80
|
+
returns `None` for every other kind.
|
|
81
|
+
|
|
82
|
+
### Migration notes
|
|
83
|
+
|
|
84
|
+
Drive-a-hand loops should handle the new failure kind:
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
session.start_hand()
|
|
88
|
+
while True:
|
|
89
|
+
step = session.next_step()
|
|
90
|
+
if step.kind() == "Failed":
|
|
91
|
+
session.abort_hand() # NOT end_hand() — no showdown to resolve
|
|
92
|
+
break
|
|
93
|
+
if step.kind() == "HandComplete":
|
|
94
|
+
session.end_hand()
|
|
95
|
+
break
|
|
96
|
+
if step.kind() == "PlayerToAct":
|
|
97
|
+
session.apply_action(step.seat(), decide(step.seat()))
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Verified with `make ayce`: 228 pytest tests pass, `demo.py` and all three
|
|
101
|
+
examples (`the_hand.py`, `calc.py`, `gto.py`) run clean.
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## [0.2.1] - 2026-07-10
|
|
106
|
+
|
|
107
|
+
### Changed
|
|
108
|
+
|
|
109
|
+
- Bumped `pkcore` dependency from `0.2.0` to `0.2.1`.
|
|
110
|
+
- Bumped `pkpy` crate version to `0.2.1` to stay in lockstep with `pkcore`.
|
|
111
|
+
|
|
112
|
+
### Security (inherited from `pkcore` 0.2.1)
|
|
113
|
+
|
|
114
|
+
`pkcore` 0.2.1 is a dependency-hygiene patch with **no public API, behavior, or
|
|
115
|
+
wire-format changes** — the postcard binary encoding is byte-identical, so solver
|
|
116
|
+
caches and hand-history data are unaffected. It carries two supply-chain fixes that
|
|
117
|
+
flow through to pkpy's dependency tree:
|
|
118
|
+
|
|
119
|
+
- **`crossbeam-epoch` 0.9.18 → 0.9.20 (RUSTSEC-2026-0204).** Fixes an invalid pointer
|
|
120
|
+
dereference in `crossbeam-epoch`'s `fmt::Pointer`/`Display` impl. Pulled in
|
|
121
|
+
transitively via `rayon`; a lockfile-only change.
|
|
122
|
+
- **`atomic-polyfill` (RUSTSEC-2023-0089) removed from the tree.** `pkcore` now builds
|
|
123
|
+
`postcard` with `default-features = false`, dropping the default `heapless-cas`
|
|
124
|
+
feature that pulled the unmaintained `atomic-polyfill` crate. It is gone from
|
|
125
|
+
pkpy's dependency graph after this bump.
|
|
126
|
+
|
|
127
|
+
### Migration notes
|
|
128
|
+
|
|
129
|
+
- Public Rust API of `pkcore` is **unchanged**. No method signatures, types, or
|
|
130
|
+
imports moved. pkpy compiles clean against `pkcore 0.2.1` with no source changes
|
|
131
|
+
(verified via `cargo check` against local `pkcore 0.2.1`).
|
|
132
|
+
- No Python-facing behavior changes: the same bindings, method names, and return
|
|
133
|
+
values as under `pkcore 0.2.0`.
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## [0.2.0] - 2026-07-08
|
|
138
|
+
|
|
139
|
+
> **Never published to PyPI.** This version was tagged in-tree but not released; its
|
|
140
|
+
> changes reach users for the first time bundled into 0.2.1. It is the first pkpy build
|
|
141
|
+
> on the `pkcore` 0.2.x line — pkpy migrated **directly from `pkcore` 0.0.54**, skipping
|
|
142
|
+
> the 0.1.x series.
|
|
143
|
+
|
|
144
|
+
### Changed
|
|
145
|
+
|
|
146
|
+
- Bumped `pkcore` dependency from `0.0.54` to `0.2.0`, and enabled pkcore's **`store`
|
|
147
|
+
feature** explicitly. `pkcore` 0.2.0 moved storage/BCM and solver persistence behind
|
|
148
|
+
cargo features (previously always compiled in); pkpy now opts into `store` so BCM
|
|
149
|
+
loading and `SolverResult` save/load continue to work.
|
|
150
|
+
- Bumped `pkpy` crate version to `0.2.0` to stay in lockstep with `pkcore`.
|
|
151
|
+
- **Internal migration to pkcore 0.2.0's reorganized module tree** (the `casino` package
|
|
152
|
+
reorg and the `TableNoCell → Table` type rename). Import paths in the Rust binding layer
|
|
153
|
+
were updated (`casino::table::event` → `casino::action` / `casino::table_celled::event`,
|
|
154
|
+
`casino::table::seats::*` → `casino::equity::*`, `casino::table::winnings` →
|
|
155
|
+
`casino::winnings`, `casino::table_no_cell::TableNoCell` → `casino::table::Table`, etc.).
|
|
156
|
+
|
|
157
|
+
**No Python-facing class or method names changed.** pkpy deliberately preserves its
|
|
158
|
+
existing Python names — `TableNoCell`, `PlayerNoCell`, `SeatNoCell`, `SeatsNoCell`,
|
|
159
|
+
`TableAction`, `TableLog`, `SeatEquity`, `Seatbit`, `Winnings`, `PotWin`, and the rest —
|
|
160
|
+
so existing Python code imports and calls exactly the same symbols. The pkcore rename is
|
|
161
|
+
invisible from Python.
|
|
162
|
+
|
|
163
|
+
### Behavior change (inherited from `pkcore` 0.2.0)
|
|
164
|
+
|
|
165
|
+
- **`DealEval(hole_cards)` is now fallible.** The constructor previously always succeeded;
|
|
166
|
+
it now raises a Python exception when the hole cards are invalid. This follows pkcore
|
|
167
|
+
0.2.0 changing `DealEval::new` to return a `Result` as part of the panic-boundary /
|
|
168
|
+
error de-leak audit work. Wrap `DealEval(...)` in `try/except` if you pass unvalidated
|
|
169
|
+
input; the happy path is unchanged.
|
|
170
|
+
- Errors surfaced from the dealer/eval paths are now pkcore's own error enums (the 0.2.0
|
|
171
|
+
"no format-crate leak" change). pkpy still maps them to Python exceptions via the same
|
|
172
|
+
`to_py_err` path, so raised exception messages may differ slightly from 0.0.54.
|
|
173
|
+
|
|
174
|
+
### Migration notes
|
|
175
|
+
|
|
176
|
+
- **No Python source changes required** for typical usage: same class names, same methods,
|
|
177
|
+
same imports.
|
|
178
|
+
- The one behavioral gotcha is `DealEval(...)` now raising on invalid hole cards instead of
|
|
179
|
+
being infallible.
|
|
180
|
+
- Card `Display` / `FromStr` string forms (e.g. `"6♠ 6♥"`) and serialized representations
|
|
181
|
+
are unchanged, so data produced under 0.0.54 remains readable.
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## [0.0.54] - 2026-04-30
|
|
186
|
+
|
|
187
|
+
### Changed
|
|
188
|
+
|
|
189
|
+
- Bumped `pkcore` dependency from `0.0.53` to `0.0.54`.
|
|
190
|
+
- Bumped `pkpy` crate version to `0.0.54` to stay in lockstep with `pkcore`.
|
|
191
|
+
|
|
192
|
+
### Fixed (inherited from `pkcore` 0.0.54)
|
|
193
|
+
|
|
194
|
+
No pkpy code was modified for this release, but the upstream fix changes
|
|
195
|
+
observable behavior on one Python-exposed method, `TableNoCell.to_call()`.
|
|
196
|
+
|
|
197
|
+
- **Short-stacked big blind — call target now anchored to the configured BB.**
|
|
198
|
+
When the BB is all-in for less than the configured big blind (e.g. BB=100
|
|
199
|
+
but stack=30), `TableNoCell.to_call()` now returns the full configured BB
|
|
200
|
+
(`100`) instead of the amount the BB physically posted (`30`). Other
|
|
201
|
+
players must call the full configured amount; chip conservation is
|
|
202
|
+
preserved at showdown via side-pot stratification (multiway) or
|
|
203
|
+
uncalled-bet returns (heads-up / no second contestant at that tier).
|
|
204
|
+
This matches standard cardroom rules (TDA, WSOP).
|
|
205
|
+
- **`act_call` now degrades gracefully when the caller is short.** When a
|
|
206
|
+
caller cannot cover the call target, the action is converted to an
|
|
207
|
+
all-in for the caller's remaining stack rather than erroring on
|
|
208
|
+
insufficient chips. Surfaced through pkpy via `PokerSession.apply_action`.
|
|
209
|
+
- **`min_raise` stays anchored to the configured BB** even when the BB is
|
|
210
|
+
all-in for less. Prior behavior could allow under-sized raises in the
|
|
211
|
+
short-BB scenario.
|
|
212
|
+
|
|
213
|
+
### Migration notes
|
|
214
|
+
|
|
215
|
+
- Public Rust API of `pkcore` is **unchanged**. No method signatures, types,
|
|
216
|
+
or imports moved. pkpy compiles clean against `pkcore 0.0.54` with no
|
|
217
|
+
source changes.
|
|
218
|
+
- If you have Python code that asserts specific chip math against a
|
|
219
|
+
short-stacked-BB scenario built on pkcore 0.0.53 semantics, those
|
|
220
|
+
assertions will need to be updated. The pkpy test suite does not
|
|
221
|
+
currently exercise this scenario, so the in-tree tests remain green.
|
|
222
|
+
|
|
223
|
+
---
|
|
224
|
+
|
|
225
|
+
Earlier releases pre-date this changelog. See `git log` for prior history.
|