pkpython 0.2.1__tar.gz → 0.7.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.
Files changed (31) hide show
  1. {pkpython-0.2.1 → pkpython-0.7.0}/CHANGELOG.md +65 -0
  2. {pkpython-0.2.1 → pkpython-0.7.0}/Cargo.lock +136 -118
  3. {pkpython-0.2.1 → pkpython-0.7.0}/Cargo.toml +2 -2
  4. {pkpython-0.2.1 → pkpython-0.7.0}/PKG-INFO +3 -2
  5. {pkpython-0.2.1 → pkpython-0.7.0}/README.md +2 -1
  6. {pkpython-0.2.1 → pkpython-0.7.0}/src/lib.rs +9 -5
  7. {pkpython-0.2.1 → pkpython-0.7.0}/src/session.rs +26 -2
  8. {pkpython-0.2.1 → pkpython-0.7.0}/tests/test_pkpy.py +14 -8
  9. {pkpython-0.2.1 → pkpython-0.7.0}/tests/test_session.py +17 -0
  10. {pkpython-0.2.1 → pkpython-0.7.0}/.github/workflows/ci.yml +0 -0
  11. {pkpython-0.2.1 → pkpython-0.7.0}/.github/workflows/publish.yml +0 -0
  12. {pkpython-0.2.1 → pkpython-0.7.0}/.gitignore +0 -0
  13. {pkpython-0.2.1 → pkpython-0.7.0}/LICENSE +0 -0
  14. {pkpython-0.2.1 → pkpython-0.7.0}/Makefile +0 -0
  15. {pkpython-0.2.1 → pkpython-0.7.0}/demo.py +0 -0
  16. {pkpython-0.2.1 → pkpython-0.7.0}/docs/STACK.md +0 -0
  17. {pkpython-0.2.1 → pkpython-0.7.0}/docs/pypi-publishing-plan.md +0 -0
  18. {pkpython-0.2.1 → pkpython-0.7.0}/docs/superpowers/plans/2026-04-28-pkpy-0.0.52-upgrade.md +0 -0
  19. {pkpython-0.2.1 → pkpython-0.7.0}/docs/superpowers/plans/2026-04-29-pkpy-pokersession-tablenocell.md +0 -0
  20. {pkpython-0.2.1 → pkpython-0.7.0}/docs/superpowers/specs/2026-04-28-pkpy-0.0.52-upgrade-design.md +0 -0
  21. {pkpython-0.2.1 → pkpython-0.7.0}/docs/superpowers/specs/2026-04-29-pkpy-pokersession-tablenocell-design.md +0 -0
  22. {pkpython-0.2.1 → pkpython-0.7.0}/examples/calc.py +0 -0
  23. {pkpython-0.2.1 → pkpython-0.7.0}/examples/gto.py +0 -0
  24. {pkpython-0.2.1 → pkpython-0.7.0}/examples/the_hand.py +0 -0
  25. {pkpython-0.2.1 → pkpython-0.7.0}/pyproject.toml +0 -0
  26. {pkpython-0.2.1 → pkpython-0.7.0}/python/pkpy/__init__.py +0 -0
  27. {pkpython-0.2.1 → pkpython-0.7.0}/src/bot.rs +0 -0
  28. {pkpython-0.2.1 → pkpython-0.7.0}/src/hand_history.rs +0 -0
  29. {pkpython-0.2.1 → pkpython-0.7.0}/src/stats.rs +0 -0
  30. {pkpython-0.2.1 → pkpython-0.7.0}/src/table_no_cell.rs +0 -0
  31. {pkpython-0.2.1 → pkpython-0.7.0}/tests/test_table_no_cell.py +0 -0
@@ -6,6 +6,71 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project tracks [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
  The crate version is kept in lockstep with the underlying `pkcore` dependency.
8
8
 
9
+ ## [0.7.0] - 2026-08-22
10
+
11
+ ### Changed
12
+
13
+ - Bumped `pkcore` dependency from `0.2.1` to `0.7.0`, skipping the `0.3`, `0.4`,
14
+ `0.5` and `0.6` lines.
15
+ - Bumped `pkpy` crate version to `0.7.0` to stay in lockstep with `pkcore`.
16
+
17
+ ### Breaking (Python API)
18
+
19
+ - **`Deck.get(index)` returns `Card | None` instead of `Card`.** `pkcore` 0.7.0
20
+ made `Deck::get` return `Option<Card>` rather than indexing out of bounds, so
21
+ an index outside `0..=51` is now `None` in Python instead of an abort. Callers
22
+ that trusted the index are unaffected; callers that pass computed indices
23
+ should check for `None`.
24
+ - **`PokerSession.next_actor()` raises instead of returning `None` on a failed
25
+ street advance.** `pkcore` 0.7.0 changed `next_actor` to
26
+ `Result<Option<u8>, PKError>` (`DEFECT_019`). Previously a dry deck collapsed
27
+ to `None`, which every `while (actor := session.next_actor()) is not None`
28
+ loop reads as "hand over" — `end_hand()` then raised `ActionIsntFinished` and
29
+ the pot was stranded. Only "no streets remain" is still `None`; a real failure
30
+ now raises `ValueError`. Recover with `abort_hand()`.
31
+ - **`KuhnCfr.train(iterations)` can raise.** `pkcore` 0.7.0 changed
32
+ `KuhnCfr::train` to return `Result<(), PKError>`. pkpy previously discarded
33
+ that result, so a training error produced a silently half-trained strategy.
34
+ - **`SevenFiveBCM.from_cards` and `IndexCardMap.from_cards` raise `ValueError` on
35
+ an unsupported card count.** `pkcore` 0.6.0 replaced `Ok(Self::default())` with
36
+ `Err(PKError::InvalidCardCount)`. Code that relied on a rank-0 default for a
37
+ three-card input must catch the error instead.
38
+
39
+ ### Added
40
+
41
+ - **`PokerSession.abort_hand()` → `int`.** Abandons a hand that cannot continue,
42
+ returning each player's committed chips and resetting the table. This is the
43
+ documented escape hatch for a `Failed` step and for a raising `next_actor()`;
44
+ `end_hand()` cannot settle such a hand because there was no showdown.
45
+ - **`SessionStep` gained the `"Failed"` kind and a `SessionStep.error()`
46
+ accessor.** `pkcore` 0.7.0 added `SessionStep::Failed(PKError)` for a hand that
47
+ cannot continue (dealing or chip collection failed mid-hand). `kind()` now
48
+ returns `"Failed"` for it and `error()` returns the message as a `str`; it
49
+ returns `None` for every other kind.
50
+
51
+ ### Migration notes
52
+
53
+ Drive-a-hand loops should handle the new failure kind:
54
+
55
+ ```python
56
+ session.start_hand()
57
+ while True:
58
+ step = session.next_step()
59
+ if step.kind() == "Failed":
60
+ session.abort_hand() # NOT end_hand() — no showdown to resolve
61
+ break
62
+ if step.kind() == "HandComplete":
63
+ session.end_hand()
64
+ break
65
+ if step.kind() == "PlayerToAct":
66
+ session.apply_action(step.seat(), decide(step.seat()))
67
+ ```
68
+
69
+ Verified with `make ayce`: 228 pytest tests pass, `demo.py` and all three
70
+ examples (`the_hand.py`, `calc.py`, `gto.py`) run clean.
71
+
72
+ ---
73
+
9
74
  ## [0.2.1] - 2026-07-10
10
75
 
11
76
  ### Changed
@@ -4,18 +4,18 @@ version = 4
4
4
 
5
5
  [[package]]
6
6
  name = "aho-corasick"
7
- version = "1.1.4"
7
+ version = "1.1.5"
8
8
  source = "registry+https://github.com/rust-lang/crates.io-index"
9
- checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
9
+ checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba"
10
10
  dependencies = [
11
11
  "memchr",
12
12
  ]
13
13
 
14
14
  [[package]]
15
15
  name = "android_system_properties"
16
- version = "0.1.5"
16
+ version = "0.1.6"
17
17
  source = "registry+https://github.com/rust-lang/crates.io-index"
18
- checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
18
+ checksum = "ae221649c9976a6f6c56ae1facf410f3ddb33cc661c4b7b61020a912d4237fbc"
19
19
  dependencies = [
20
20
  "libc",
21
21
  ]
@@ -72,9 +72,9 @@ dependencies = [
72
72
 
73
73
  [[package]]
74
74
  name = "anyhow"
75
- version = "1.0.103"
75
+ version = "1.0.104"
76
76
  source = "registry+https://github.com/rust-lang/crates.io-index"
77
- checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3"
77
+ checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470"
78
78
 
79
79
  [[package]]
80
80
  name = "arraydeque"
@@ -102,9 +102,9 @@ checksum = "3931fe4d65bb8cfe9a02d88e330083f4d2a882cbc7470e964168a0c580a32a5c"
102
102
 
103
103
  [[package]]
104
104
  name = "bitflags"
105
- version = "2.13.0"
105
+ version = "2.13.1"
106
106
  source = "registry+https://github.com/rust-lang/crates.io-index"
107
- checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8"
107
+ checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da"
108
108
 
109
109
  [[package]]
110
110
  name = "bitvec"
@@ -130,9 +130,9 @@ dependencies = [
130
130
 
131
131
  [[package]]
132
132
  name = "bstr"
133
- version = "1.12.3"
133
+ version = "1.13.1"
134
134
  source = "registry+https://github.com/rust-lang/crates.io-index"
135
- checksum = "5cee35f73844aa3014bb606320a6c1f010249dbdf43342fe54b5a4f6a8ed4b79"
135
+ checksum = "6bb31b46c14244e20ee9984b11bf5c992b91fb6939fea616e3512c8baecdbe5f"
136
136
  dependencies = [
137
137
  "memchr",
138
138
  "serde_core",
@@ -155,7 +155,7 @@ dependencies = [
155
155
  "getrandom 0.3.4",
156
156
  "itertools",
157
157
  "log",
158
- "rand 0.9.4",
158
+ "rand 0.9.5",
159
159
  "serde",
160
160
  "serde_norway",
161
161
  "thiserror",
@@ -163,9 +163,9 @@ dependencies = [
163
163
 
164
164
  [[package]]
165
165
  name = "cc"
166
- version = "1.2.66"
166
+ version = "1.4.4"
167
167
  source = "registry+https://github.com/rust-lang/crates.io-index"
168
- checksum = "f5d6cac793997bd970000024b2934968efe83b382de4fdcf4fcb46b6ee4ad996"
168
+ checksum = "0ad534f4357a5264cce5019c989cf66a4f0dc4e0d1b1d15f8aacec0ff7360273"
169
169
  dependencies = [
170
170
  "find-msvc-tools",
171
171
  "jobserver",
@@ -195,9 +195,9 @@ dependencies = [
195
195
 
196
196
  [[package]]
197
197
  name = "clap"
198
- version = "4.6.1"
198
+ version = "4.6.6"
199
199
  source = "registry+https://github.com/rust-lang/crates.io-index"
200
- checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51"
200
+ checksum = "473c7e07f409a8d772161724aa8db6a765a2532a70f9667eeb7b49d3d02fbdca"
201
201
  dependencies = [
202
202
  "clap_builder",
203
203
  "clap_derive",
@@ -205,9 +205,9 @@ dependencies = [
205
205
 
206
206
  [[package]]
207
207
  name = "clap_builder"
208
- version = "4.6.0"
208
+ version = "4.6.6"
209
209
  source = "registry+https://github.com/rust-lang/crates.io-index"
210
- checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f"
210
+ checksum = "7b48fea5a88e9ae728a2dcbedbfc0e730f7d60da42e1cb049a83c9fb8b789889"
211
211
  dependencies = [
212
212
  "anstream",
213
213
  "anstyle",
@@ -217,14 +217,14 @@ dependencies = [
217
217
 
218
218
  [[package]]
219
219
  name = "clap_derive"
220
- version = "4.6.1"
220
+ version = "4.6.4"
221
221
  source = "registry+https://github.com/rust-lang/crates.io-index"
222
- checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9"
222
+ checksum = "d012d2b9d65aca7f18f4d9878a045bc17899bba951561ba5ec3c2ba1eed9a061"
223
223
  dependencies = [
224
224
  "heck",
225
225
  "proc-macro2",
226
226
  "quote",
227
- "syn",
227
+ "syn 3.0.3",
228
228
  ]
229
229
 
230
230
  [[package]]
@@ -346,20 +346,20 @@ dependencies = [
346
346
 
347
347
  [[package]]
348
348
  name = "displaydoc"
349
- version = "0.2.6"
349
+ version = "0.2.7"
350
350
  source = "registry+https://github.com/rust-lang/crates.io-index"
351
- checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f"
351
+ checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8"
352
352
  dependencies = [
353
353
  "proc-macro2",
354
354
  "quote",
355
- "syn",
355
+ "syn 3.0.3",
356
356
  ]
357
357
 
358
358
  [[package]]
359
359
  name = "either"
360
- version = "1.16.0"
360
+ version = "1.18.0"
361
361
  source = "registry+https://github.com/rust-lang/crates.io-index"
362
- checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e"
362
+ checksum = "252afb9ae5eaa683babdc6a068b3f5726eb19e05070c731f9b2a23a7c3e8ed34"
363
363
 
364
364
  [[package]]
365
365
  name = "embedded-io"
@@ -393,9 +393,9 @@ checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
393
393
 
394
394
  [[package]]
395
395
  name = "find-msvc-tools"
396
- version = "0.1.9"
396
+ version = "0.1.11"
397
397
  source = "registry+https://github.com/rust-lang/crates.io-index"
398
- checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
398
+ checksum = "d45db016d36b838f563236e9193d0ee6ce38f3f68b6c94e914b4929c96bbb890"
399
399
 
400
400
  [[package]]
401
401
  name = "fluent-bundle"
@@ -442,7 +442,7 @@ dependencies = [
442
442
  "ignore",
443
443
  "proc-macro2",
444
444
  "quote",
445
- "syn",
445
+ "syn 2.0.119",
446
446
  "unic-langid",
447
447
  ]
448
448
 
@@ -487,21 +487,21 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
487
487
 
488
488
  [[package]]
489
489
  name = "futures-core"
490
- version = "0.3.32"
490
+ version = "0.3.34"
491
491
  source = "registry+https://github.com/rust-lang/crates.io-index"
492
- checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d"
492
+ checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e"
493
493
 
494
494
  [[package]]
495
495
  name = "futures-task"
496
- version = "0.3.32"
496
+ version = "0.3.34"
497
497
  source = "registry+https://github.com/rust-lang/crates.io-index"
498
- checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393"
498
+ checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd"
499
499
 
500
500
  [[package]]
501
501
  name = "futures-util"
502
- version = "0.3.32"
502
+ version = "0.3.34"
503
503
  source = "registry+https://github.com/rust-lang/crates.io-index"
504
- checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6"
504
+ checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc"
505
505
  dependencies = [
506
506
  "futures-core",
507
507
  "futures-task",
@@ -549,9 +549,9 @@ dependencies = [
549
549
 
550
550
  [[package]]
551
551
  name = "globset"
552
- version = "0.4.18"
552
+ version = "0.4.20"
553
553
  source = "registry+https://github.com/rust-lang/crates.io-index"
554
- checksum = "52dfc19153a48bde0cbd630453615c8151bce3a5adfac7a0aebfbf0a1e1f57e3"
554
+ checksum = "07c34a9410465b45bd9787443bc7370f37735bad04b0f0cd57ff1a3186c98988"
555
555
  dependencies = [
556
556
  "aho-corasick",
557
557
  "bstr",
@@ -562,9 +562,9 @@ dependencies = [
562
562
 
563
563
  [[package]]
564
564
  name = "granit-parser"
565
- version = "0.0.1"
565
+ version = "1.1.0"
566
566
  source = "registry+https://github.com/rust-lang/crates.io-index"
567
- checksum = "e354bf0eb3279a16e8f92fc1ba6ddd4420953fc064140b010844d21874b58113"
567
+ checksum = "4ccd1be9ebf2bd5520dbfcfb72b70ef492f654061299a097056f3e73410e38ec"
568
568
  dependencies = [
569
569
  "arraydeque",
570
570
  "smallvec",
@@ -602,9 +602,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
602
602
 
603
603
  [[package]]
604
604
  name = "hybrid-array"
605
- version = "0.4.13"
605
+ version = "0.4.14"
606
606
  source = "registry+https://github.com/rust-lang/crates.io-index"
607
- checksum = "818356c5132c1fede50f837ca96afbe78ff42413047f4abb886217845e1b6c8c"
607
+ checksum = "707114b52a152fa7bdb290cd7cd5912d9467273b6d74e21b8d81aca1f8533f6b"
608
608
  dependencies = [
609
609
  "typenum",
610
610
  ]
@@ -635,9 +635,9 @@ dependencies = [
635
635
 
636
636
  [[package]]
637
637
  name = "ignore"
638
- version = "0.4.28"
638
+ version = "0.4.33"
639
639
  source = "registry+https://github.com/rust-lang/crates.io-index"
640
- checksum = "2adf14691c72bcfc1058740436a35bdd3ae9c07d1a941ef00b749e9ea16aefa7"
640
+ checksum = "00b69833ed729dc5aa7d19541d96d6cf8e9137194207a04916d658e43168402f"
641
641
  dependencies = [
642
642
  "crossbeam-deque",
643
643
  "globset",
@@ -718,9 +718,9 @@ checksum = "72167d68f5fce3b8655487b8038691a3c9984ee769590f93f2a631f4ad64e4f5"
718
718
 
719
719
  [[package]]
720
720
  name = "js-sys"
721
- version = "0.3.103"
721
+ version = "0.3.104"
722
722
  source = "registry+https://github.com/rust-lang/crates.io-index"
723
- checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102"
723
+ checksum = "0e0c1080212aad755ea003d18543e8768dd432c48819efd73a7bf1e39b7a5a3a"
724
724
  dependencies = [
725
725
  "cfg-if",
726
726
  "futures-util",
@@ -735,9 +735,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
735
735
 
736
736
  [[package]]
737
737
  name = "libc"
738
- version = "0.2.186"
738
+ version = "0.2.189"
739
739
  source = "registry+https://github.com/rust-lang/crates.io-index"
740
- checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
740
+ checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2"
741
741
 
742
742
  [[package]]
743
743
  name = "libsqlite3-sys"
@@ -761,9 +761,9 @@ dependencies = [
761
761
 
762
762
  [[package]]
763
763
  name = "log"
764
- version = "0.4.33"
764
+ version = "0.4.34"
765
765
  source = "registry+https://github.com/rust-lang/crates.io-index"
766
- checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad"
766
+ checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6"
767
767
 
768
768
  [[package]]
769
769
  name = "memchr"
@@ -828,9 +828,9 @@ checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
828
828
 
829
829
  [[package]]
830
830
  name = "pkcore"
831
- version = "0.2.1"
831
+ version = "0.7.0"
832
832
  source = "registry+https://github.com/rust-lang/crates.io-index"
833
- checksum = "95e87238e5b4b7f5f710be6b6dc90a7557ad3117659752d3cf087378fbf02b0e"
833
+ checksum = "9c03fe516554011cc3c9a78e618a9f0ec9f7a7e30b644f83725c4d4ce4bf2f84"
834
834
  dependencies = [
835
835
  "bint",
836
836
  "bitvec",
@@ -844,7 +844,7 @@ dependencies = [
844
844
  "percent-encoding",
845
845
  "pkstate",
846
846
  "postcard",
847
- "rand 0.9.4",
847
+ "rand 0.9.5",
848
848
  "random_name_generator",
849
849
  "rayon",
850
850
  "regex",
@@ -863,13 +863,13 @@ dependencies = [
863
863
 
864
864
  [[package]]
865
865
  name = "pkg-config"
866
- version = "0.3.33"
866
+ version = "0.3.34"
867
867
  source = "registry+https://github.com/rust-lang/crates.io-index"
868
- checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e"
868
+ checksum = "f6b464fbc74e149a392436b17d523f769e057cb6877f6a5c4618bc6f11800548"
869
869
 
870
870
  [[package]]
871
871
  name = "pkpy"
872
- version = "0.2.1"
872
+ version = "0.7.0"
873
873
  dependencies = [
874
874
  "pkcore",
875
875
  "pyo3",
@@ -889,9 +889,9 @@ dependencies = [
889
889
 
890
890
  [[package]]
891
891
  name = "portable-atomic"
892
- version = "1.13.1"
892
+ version = "1.15.0"
893
893
  source = "registry+https://github.com/rust-lang/crates.io-index"
894
- checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49"
894
+ checksum = "05c8b63e8d9609db387f0324918f81d68fe27748f084ef092fb35954d0539a85"
895
895
 
896
896
  [[package]]
897
897
  name = "postcard"
@@ -922,9 +922,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068"
922
922
 
923
923
  [[package]]
924
924
  name = "proc-macro2"
925
- version = "1.0.106"
925
+ version = "1.0.107"
926
926
  source = "registry+https://github.com/rust-lang/crates.io-index"
927
- checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
927
+ checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
928
928
  dependencies = [
929
929
  "unicode-ident",
930
930
  ]
@@ -971,7 +971,7 @@ dependencies = [
971
971
  "proc-macro2",
972
972
  "pyo3-macros-backend",
973
973
  "quote",
974
- "syn",
974
+ "syn 2.0.119",
975
975
  ]
976
976
 
977
977
  [[package]]
@@ -984,14 +984,14 @@ dependencies = [
984
984
  "proc-macro2",
985
985
  "pyo3-build-config",
986
986
  "quote",
987
- "syn",
987
+ "syn 2.0.119",
988
988
  ]
989
989
 
990
990
  [[package]]
991
991
  name = "quote"
992
- version = "1.0.46"
992
+ version = "1.0.47"
993
993
  source = "registry+https://github.com/rust-lang/crates.io-index"
994
- checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368"
994
+ checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
995
995
  dependencies = [
996
996
  "proc-macro2",
997
997
  ]
@@ -1016,9 +1016,9 @@ checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
1016
1016
 
1017
1017
  [[package]]
1018
1018
  name = "rand"
1019
- version = "0.8.6"
1019
+ version = "0.8.7"
1020
1020
  source = "registry+https://github.com/rust-lang/crates.io-index"
1021
- checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a"
1021
+ checksum = "22f6172bdec972074665ed81ed53b71da00bfc44b65a753cfde883ec4c702a1a"
1022
1022
  dependencies = [
1023
1023
  "libc",
1024
1024
  "rand_chacha 0.3.1",
@@ -1027,9 +1027,9 @@ dependencies = [
1027
1027
 
1028
1028
  [[package]]
1029
1029
  name = "rand"
1030
- version = "0.9.4"
1030
+ version = "0.9.5"
1031
1031
  source = "registry+https://github.com/rust-lang/crates.io-index"
1032
- checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea"
1032
+ checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41"
1033
1033
  dependencies = [
1034
1034
  "rand_chacha 0.9.0",
1035
1035
  "rand_core 0.9.5",
@@ -1084,7 +1084,7 @@ dependencies = [
1084
1084
  "clap",
1085
1085
  "lazy_static",
1086
1086
  "log",
1087
- "rand 0.8.6",
1087
+ "rand 0.8.7",
1088
1088
  "regex",
1089
1089
  "rust-embed",
1090
1090
  "titlecase",
@@ -1124,9 +1124,9 @@ dependencies = [
1124
1124
 
1125
1125
  [[package]]
1126
1126
  name = "regex-automata"
1127
- version = "0.4.15"
1127
+ version = "0.4.18"
1128
1128
  source = "registry+https://github.com/rust-lang/crates.io-index"
1129
- checksum = "1f388202e4b80542a0921078cc23b6333bcf1409c1e3f86404cae4766a6131db"
1129
+ checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2"
1130
1130
  dependencies = [
1131
1131
  "aho-corasick",
1132
1132
  "memchr",
@@ -1174,7 +1174,7 @@ dependencies = [
1174
1174
  "proc-macro2",
1175
1175
  "quote",
1176
1176
  "rust-embed-utils",
1177
- "syn",
1177
+ "syn 2.0.119",
1178
1178
  "walkdir",
1179
1179
  ]
1180
1180
 
@@ -1223,15 +1223,15 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
1223
1223
 
1224
1224
  [[package]]
1225
1225
  name = "self_cell"
1226
- version = "1.2.2"
1226
+ version = "1.3.0"
1227
1227
  source = "registry+https://github.com/rust-lang/crates.io-index"
1228
- checksum = "b12e76d157a900eb52e81bc6e9f3069344290341720e9178cde2407113ac8d89"
1228
+ checksum = "2ab42ca02749e120097e328d91d415325bdf43b1c72c4c8badf37375fe40a813"
1229
1229
 
1230
1230
  [[package]]
1231
1231
  name = "serde"
1232
- version = "1.0.228"
1232
+ version = "1.0.229"
1233
1233
  source = "registry+https://github.com/rust-lang/crates.io-index"
1234
- checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
1234
+ checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba"
1235
1235
  dependencies = [
1236
1236
  "serde_core",
1237
1237
  "serde_derive",
@@ -1239,29 +1239,29 @@ dependencies = [
1239
1239
 
1240
1240
  [[package]]
1241
1241
  name = "serde_core"
1242
- version = "1.0.228"
1242
+ version = "1.0.229"
1243
1243
  source = "registry+https://github.com/rust-lang/crates.io-index"
1244
- checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
1244
+ checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48"
1245
1245
  dependencies = [
1246
1246
  "serde_derive",
1247
1247
  ]
1248
1248
 
1249
1249
  [[package]]
1250
1250
  name = "serde_derive"
1251
- version = "1.0.228"
1251
+ version = "1.0.229"
1252
1252
  source = "registry+https://github.com/rust-lang/crates.io-index"
1253
- checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
1253
+ checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
1254
1254
  dependencies = [
1255
1255
  "proc-macro2",
1256
1256
  "quote",
1257
- "syn",
1257
+ "syn 3.0.3",
1258
1258
  ]
1259
1259
 
1260
1260
  [[package]]
1261
1261
  name = "serde_json"
1262
- version = "1.0.150"
1262
+ version = "1.0.151"
1263
1263
  source = "registry+https://github.com/rust-lang/crates.io-index"
1264
- checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9"
1264
+ checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14"
1265
1265
  dependencies = [
1266
1266
  "itoa",
1267
1267
  "memchr",
@@ -1285,9 +1285,9 @@ dependencies = [
1285
1285
 
1286
1286
  [[package]]
1287
1287
  name = "serde_yaml_bw"
1288
- version = "2.5.6"
1288
+ version = "2.5.7"
1289
1289
  source = "registry+https://github.com/rust-lang/crates.io-index"
1290
- checksum = "d9559c696727e20fa0f661a1db638c7ae1be73419de8e1aed2642b796096ab53"
1290
+ checksum = "659d74d982f3ee009c27f34a1fc84d84828095d58ea210ce31705833188c8e27"
1291
1291
  dependencies = [
1292
1292
  "base64",
1293
1293
  "granit-parser",
@@ -1300,6 +1300,12 @@ dependencies = [
1300
1300
  "zmij",
1301
1301
  ]
1302
1302
 
1303
+ [[package]]
1304
+ name = "sha1_smol"
1305
+ version = "1.0.1"
1306
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1307
+ checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d"
1308
+
1303
1309
  [[package]]
1304
1310
  name = "sha2"
1305
1311
  version = "0.11.0"
@@ -1331,9 +1337,9 @@ checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90"
1331
1337
 
1332
1338
  [[package]]
1333
1339
  name = "spin"
1334
- version = "0.9.8"
1340
+ version = "0.9.9"
1335
1341
  source = "registry+https://github.com/rust-lang/crates.io-index"
1336
- checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
1342
+ checksum = "3763264f6b73151db08c50ff20d7d8a0b8796e021cdea7ceedad07b80155fa0e"
1337
1343
  dependencies = [
1338
1344
  "lock_api",
1339
1345
  ]
@@ -1362,14 +1368,25 @@ dependencies = [
1362
1368
  "heck",
1363
1369
  "proc-macro2",
1364
1370
  "quote",
1365
- "syn",
1371
+ "syn 2.0.119",
1372
+ ]
1373
+
1374
+ [[package]]
1375
+ name = "syn"
1376
+ version = "2.0.119"
1377
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1378
+ checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
1379
+ dependencies = [
1380
+ "proc-macro2",
1381
+ "quote",
1382
+ "unicode-ident",
1366
1383
  ]
1367
1384
 
1368
1385
  [[package]]
1369
1386
  name = "syn"
1370
- version = "2.0.118"
1387
+ version = "3.0.3"
1371
1388
  source = "registry+https://github.com/rust-lang/crates.io-index"
1372
- checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422"
1389
+ checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3"
1373
1390
  dependencies = [
1374
1391
  "proc-macro2",
1375
1392
  "quote",
@@ -1400,22 +1417,22 @@ dependencies = [
1400
1417
 
1401
1418
  [[package]]
1402
1419
  name = "thiserror"
1403
- version = "2.0.18"
1420
+ version = "2.0.20"
1404
1421
  source = "registry+https://github.com/rust-lang/crates.io-index"
1405
- checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
1422
+ checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f"
1406
1423
  dependencies = [
1407
1424
  "thiserror-impl",
1408
1425
  ]
1409
1426
 
1410
1427
  [[package]]
1411
1428
  name = "thiserror-impl"
1412
- version = "2.0.18"
1429
+ version = "2.0.20"
1413
1430
  source = "registry+https://github.com/rust-lang/crates.io-index"
1414
- checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
1431
+ checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af"
1415
1432
  dependencies = [
1416
1433
  "proc-macro2",
1417
1434
  "quote",
1418
- "syn",
1435
+ "syn 3.0.3",
1419
1436
  ]
1420
1437
 
1421
1438
  [[package]]
@@ -1426,9 +1443,9 @@ checksum = "3bf63baf9f5039dadc247375c29eb13706706cfde997d0330d05aa63a77d8820"
1426
1443
 
1427
1444
  [[package]]
1428
1445
  name = "tinystr"
1429
- version = "0.8.3"
1446
+ version = "0.8.4"
1430
1447
  source = "registry+https://github.com/rust-lang/crates.io-index"
1431
- checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d"
1448
+ checksum = "b1e27c91459209c2986af3dcf603a5a74a4368754ce37414f59acc971167f643"
1432
1449
  dependencies = [
1433
1450
  "displaydoc",
1434
1451
  "serde_core",
@@ -1500,7 +1517,7 @@ checksum = "a1249a628de3ad34b821ecb1001355bca3940bcb2f88558f1a8bd82e977f75b5"
1500
1517
  dependencies = [
1501
1518
  "proc-macro-hack",
1502
1519
  "quote",
1503
- "syn",
1520
+ "syn 2.0.119",
1504
1521
  "unic-langid-impl",
1505
1522
  ]
1506
1523
 
@@ -1530,13 +1547,14 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
1530
1547
 
1531
1548
  [[package]]
1532
1549
  name = "uuid"
1533
- version = "1.23.4"
1550
+ version = "1.25.0"
1534
1551
  source = "registry+https://github.com/rust-lang/crates.io-index"
1535
- checksum = "bf80a72845275afea99e7f2b434723d3bc7e38470fcd1c7ed39a599c73319a53"
1552
+ checksum = "f053576934f05a761a402421fbbe3d425d9366f75f978806a037b3ca481abecc"
1536
1553
  dependencies = [
1537
1554
  "getrandom 0.4.3",
1538
1555
  "js-sys",
1539
1556
  "serde_core",
1557
+ "sha1_smol",
1540
1558
  "wasm-bindgen",
1541
1559
  ]
1542
1560
 
@@ -1573,9 +1591,9 @@ dependencies = [
1573
1591
 
1574
1592
  [[package]]
1575
1593
  name = "wasm-bindgen"
1576
- version = "0.2.126"
1594
+ version = "0.2.127"
1577
1595
  source = "registry+https://github.com/rust-lang/crates.io-index"
1578
- checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4"
1596
+ checksum = "1b70935747edd64d89de3efa29d73789b806c15798f8e7dca4d8ac356b50ce70"
1579
1597
  dependencies = [
1580
1598
  "cfg-if",
1581
1599
  "once_cell",
@@ -1586,9 +1604,9 @@ dependencies = [
1586
1604
 
1587
1605
  [[package]]
1588
1606
  name = "wasm-bindgen-macro"
1589
- version = "0.2.126"
1607
+ version = "0.2.127"
1590
1608
  source = "registry+https://github.com/rust-lang/crates.io-index"
1591
- checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1"
1609
+ checksum = "77775f8f3f7217702089053b94958f8f54061a3f663417df76e19cbdcca29bc1"
1592
1610
  dependencies = [
1593
1611
  "quote",
1594
1612
  "wasm-bindgen-macro-support",
@@ -1596,22 +1614,22 @@ dependencies = [
1596
1614
 
1597
1615
  [[package]]
1598
1616
  name = "wasm-bindgen-macro-support"
1599
- version = "0.2.126"
1617
+ version = "0.2.127"
1600
1618
  source = "registry+https://github.com/rust-lang/crates.io-index"
1601
- checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e"
1619
+ checksum = "e11d33f857dc2fb11b8bc75aee111aa9cbeb12cd9f25efd3d4c2a3dd4e235284"
1602
1620
  dependencies = [
1603
1621
  "bumpalo",
1604
1622
  "proc-macro2",
1605
1623
  "quote",
1606
- "syn",
1624
+ "syn 2.0.119",
1607
1625
  "wasm-bindgen-shared",
1608
1626
  ]
1609
1627
 
1610
1628
  [[package]]
1611
1629
  name = "wasm-bindgen-shared"
1612
- version = "0.2.126"
1630
+ version = "0.2.127"
1613
1631
  source = "registry+https://github.com/rust-lang/crates.io-index"
1614
- checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24"
1632
+ checksum = "7ef64dbcc55df09c7e5a46182d181c2cfa3e925f3da937ea764728b4bbb9dcbf"
1615
1633
  dependencies = [
1616
1634
  "unicode-ident",
1617
1635
  ]
@@ -1656,7 +1674,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
1656
1674
  dependencies = [
1657
1675
  "proc-macro2",
1658
1676
  "quote",
1659
- "syn",
1677
+ "syn 2.0.119",
1660
1678
  ]
1661
1679
 
1662
1680
  [[package]]
@@ -1667,7 +1685,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
1667
1685
  dependencies = [
1668
1686
  "proc-macro2",
1669
1687
  "quote",
1670
- "syn",
1688
+ "syn 2.0.119",
1671
1689
  ]
1672
1690
 
1673
1691
  [[package]]
@@ -1720,22 +1738,22 @@ dependencies = [
1720
1738
 
1721
1739
  [[package]]
1722
1740
  name = "zerocopy"
1723
- version = "0.8.54"
1741
+ version = "0.8.56"
1724
1742
  source = "registry+https://github.com/rust-lang/crates.io-index"
1725
- checksum = "b7cbbc0a705a0fd05cc3676525980d2bf5a9bc4adac6d6475209a7887cf59d19"
1743
+ checksum = "556764e583adb45a9f8d413c2a147fa7e8d821e48e12b14fd560b607998b75eb"
1726
1744
  dependencies = [
1727
1745
  "zerocopy-derive",
1728
1746
  ]
1729
1747
 
1730
1748
  [[package]]
1731
1749
  name = "zerocopy-derive"
1732
- version = "0.8.54"
1750
+ version = "0.8.56"
1733
1751
  source = "registry+https://github.com/rust-lang/crates.io-index"
1734
- checksum = "e2e817b7b52d0c7358d3246da9d69935ebb18116b2b102b4230dac079b4862f5"
1752
+ checksum = "f2ab42fc20575779bd240faa45f94a74256f755c0fa9e89f0ede20d91d0cdfc1"
1735
1753
  dependencies = [
1736
1754
  "proc-macro2",
1737
1755
  "quote",
1738
- "syn",
1756
+ "syn 2.0.119",
1739
1757
  ]
1740
1758
 
1741
1759
  [[package]]
@@ -1746,9 +1764,9 @@ checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272"
1746
1764
 
1747
1765
  [[package]]
1748
1766
  name = "zerovec"
1749
- version = "0.11.6"
1767
+ version = "0.11.8"
1750
1768
  source = "registry+https://github.com/rust-lang/crates.io-index"
1751
- checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239"
1769
+ checksum = "bb0464e17806c1d976d5cba29399c7f08e516e279e2ba493f63123b5fca67dd8"
1752
1770
  dependencies = [
1753
1771
  "serde",
1754
1772
  "zerofrom",
@@ -1756,9 +1774,9 @@ dependencies = [
1756
1774
 
1757
1775
  [[package]]
1758
1776
  name = "zmij"
1759
- version = "1.0.21"
1777
+ version = "1.0.23"
1760
1778
  source = "registry+https://github.com/rust-lang/crates.io-index"
1761
- checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
1779
+ checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b"
1762
1780
 
1763
1781
  [[package]]
1764
1782
  name = "zstd"
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "pkpy"
3
- version = "0.2.1"
3
+ version = "0.7.0"
4
4
  edition = "2021"
5
5
  description = "Python bindings for pkcore, a high-performance poker analysis library"
6
6
  license = "GPL-3.0-or-later"
@@ -12,7 +12,7 @@ crate-type = ["cdylib"]
12
12
 
13
13
  [dependencies]
14
14
  pyo3 = { version = "0.28", features = ["extension-module"] }
15
- pkcore = { version = "0.2.1", features = ["store"] }
15
+ pkcore = { version = "0.7.0", features = ["store"] }
16
16
 
17
17
  [profile.release]
18
18
  lto = true
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pkpython
3
- Version: 0.2.1
3
+ Version: 0.7.0
4
4
  Classifier: Programming Language :: Rust
5
5
  Classifier: Programming Language :: Python :: Implementation :: CPython
6
6
  Classifier: Programming Language :: Python :: Implementation :: PyPy
@@ -128,7 +128,8 @@ from pkpy import Deck
128
128
 
129
129
  deck = Deck.poker_cards() # -> Cards, ordered A♠ down to 2♣
130
130
  shuffled = Deck.poker_cards_shuffled() # -> Cards, randomly shuffled
131
- Deck.get(0) # -> Card (A♠, the first card in deck order)
131
+ Deck.get(0) # -> Card | None (A♠, the first card in deck order)
132
+ Deck.get(52) # -> None (index out of range)
132
133
  Deck.len() # -> 52
133
134
  ```
134
135
 
@@ -110,7 +110,8 @@ from pkpy import Deck
110
110
 
111
111
  deck = Deck.poker_cards() # -> Cards, ordered A♠ down to 2♣
112
112
  shuffled = Deck.poker_cards_shuffled() # -> Cards, randomly shuffled
113
- Deck.get(0) # -> Card (A♠, the first card in deck order)
113
+ Deck.get(0) # -> Card | None (A♠, the first card in deck order)
114
+ Deck.get(52) # -> None (index out of range)
114
115
  Deck.len() # -> 52
115
116
  ```
116
117
 
@@ -2240,10 +2240,11 @@ impl Deck {
2240
2240
  Cards(PkDeck::poker_cards_shuffled())
2241
2241
  }
2242
2242
 
2243
- /// Returns the card at the given index (0–51) in deck order.
2243
+ /// Returns the card at the given index (0–51) in deck order, or
2244
+ /// `None` when the index is out of range.
2244
2245
  #[staticmethod]
2245
- fn get(index: usize) -> Card {
2246
- Card(PkDeck::get(index))
2246
+ fn get(index: usize) -> Option<Card> {
2247
+ PkDeck::get(index).map(Card)
2247
2248
  }
2248
2249
 
2249
2250
  /// Returns the number of cards in a standard deck (always 52).
@@ -3956,8 +3957,11 @@ impl KuhnCfr {
3956
3957
  /// Run ``iterations`` of vanilla CFR (traverses all 6 deals each iteration).
3957
3958
  ///
3958
3959
  /// Calling ``train`` multiple times accumulates on top of prior training.
3959
- fn train(&mut self, iterations: u32) {
3960
- self.0.train(iterations);
3960
+ ///
3961
+ /// Raises on a training error rather than reporting a silently
3962
+ /// half-trained strategy.
3963
+ fn train(&mut self, iterations: u32) -> PyResult<()> {
3964
+ self.0.train(iterations).map_err(to_py_err)
3961
3965
  }
3962
3966
 
3963
3967
  /// Average strategy accumulated over all training iterations.
@@ -97,6 +97,19 @@ impl SessionStep {
97
97
  PkSessionStep::PlayerToAct(_) => "PlayerToAct",
98
98
  PkSessionStep::StreetAdvanced => "StreetAdvanced",
99
99
  PkSessionStep::HandComplete => "HandComplete",
100
+ PkSessionStep::Failed(_) => "Failed",
101
+ }
102
+ }
103
+
104
+ /// The error message for a `Failed` step, else `None`.
105
+ ///
106
+ /// A `Failed` step means the hand cannot continue (a dry deck, a failed
107
+ /// chip collection). It is **not** resolvable with `end_hand()` — call
108
+ /// `abort_hand()` to refund committed chips and reset the table.
109
+ fn error(&self) -> Option<String> {
110
+ match self.0 {
111
+ PkSessionStep::Failed(e) => Some(e.to_string()),
112
+ _ => None,
100
113
  }
101
114
  }
102
115
 
@@ -112,6 +125,7 @@ impl SessionStep {
112
125
  PkSessionStep::PlayerToAct(s) => format!("SessionStep.PlayerToAct(seat={s})"),
113
126
  PkSessionStep::StreetAdvanced => "SessionStep.StreetAdvanced".to_string(),
114
127
  PkSessionStep::HandComplete => "SessionStep.HandComplete".to_string(),
128
+ PkSessionStep::Failed(e) => format!("SessionStep.Failed({e})"),
115
129
  }
116
130
  }
117
131
  }
@@ -169,8 +183,8 @@ impl PokerSession {
169
183
  self.0.is_hand_complete()
170
184
  }
171
185
 
172
- fn next_actor(&mut self) -> Option<u8> {
173
- self.0.next_actor()
186
+ fn next_actor(&mut self) -> PyResult<Option<u8>> {
187
+ self.0.next_actor().map_err(to_py_err)
174
188
  }
175
189
 
176
190
  fn next_step(&mut self) -> SessionStep {
@@ -181,6 +195,16 @@ impl PokerSession {
181
195
  self.0.apply_action(seat, action.0).map_err(to_py_err)
182
196
  }
183
197
 
198
+ /// Abandons a hand that cannot continue, returning the chips refunded.
199
+ ///
200
+ /// Use this when `next_step()` yields a `Failed` step or `next_actor()`
201
+ /// raises: each player's committed chips go back to their stack and the
202
+ /// table resets. `end_hand()` cannot settle such a hand — there was no
203
+ /// showdown to resolve.
204
+ fn abort_hand(&mut self) -> PyResult<usize> {
205
+ self.0.abort_hand().map_err(to_py_err)
206
+ }
207
+
184
208
  // ── Session-wide ────────────────────────────────────────────────────
185
209
 
186
210
  fn count_funded(&self) -> usize {
@@ -403,6 +403,12 @@ class TestDeck:
403
403
  cards = [Deck.get(i) for i in range(52)]
404
404
  assert len(set(str(c) for c in cards)) == 52
405
405
 
406
+ def test_get_out_of_range_is_none(self):
407
+ # pkcore 0.7.0: Deck::get returns Option<Card> instead of panicking.
408
+ assert Deck.get(52) is None
409
+ assert Deck.get(999) is None
410
+ assert Deck.get(51) is not None
411
+
406
412
  def test_shuffled_contains_same_cards(self):
407
413
  ordered = Deck.poker_cards()
408
414
  shuffled = Deck.poker_cards_shuffled()
@@ -827,10 +833,10 @@ class TestSevenFiveBCM:
827
833
  assert isinstance(bcm.bc, Bard)
828
834
  assert isinstance(bcm.best, Bard)
829
835
 
830
- def test_invalid_card_count_returns_default(self):
831
- # pkcore returns Ok(default) for unsupported counts rather than Err
832
- bcm = SevenFiveBCM.from_cards(Cards.parse("As Ks Qs"))
833
- assert bcm.rank == 0
836
+ def test_invalid_card_count_raises(self):
837
+ # pkcore 0.6.0 made this an error instead of Ok(default)
838
+ with pytest.raises(ValueError):
839
+ SevenFiveBCM.from_cards(Cards.parse("As Ks Qs"))
834
840
 
835
841
  def test_equality(self):
836
842
  bcm1 = SevenFiveBCM.from_cards(ROYAL_FLUSH_5)
@@ -878,10 +884,10 @@ class TestIndexCardMap:
878
884
  assert isinstance(icm.cards, str)
879
885
  assert isinstance(icm.best, str)
880
886
 
881
- def test_invalid_card_count_returns_default(self):
882
- # pkcore returns Ok(default) for unsupported counts rather than Err
883
- icm = IndexCardMap.from_cards(Cards.parse("As Ks Qs"))
884
- assert icm.rank == 0
887
+ def test_invalid_card_count_raises(self):
888
+ # pkcore 0.6.0 made this an error instead of Ok(default)
889
+ with pytest.raises(ValueError):
890
+ IndexCardMap.from_cards(Cards.parse("As Ks Qs"))
885
891
 
886
892
  def test_equality(self):
887
893
  icm1 = IndexCardMap.from_cards(ROYAL_FLUSH_5)
@@ -91,6 +91,23 @@ class TestPokerSession:
91
91
  assert step.kind() == "PlayerToAct"
92
92
  assert step.seat() is not None
93
93
 
94
+ def test_healthy_step_has_no_error(self):
95
+ # pkcore 0.7.0 added SessionStep::Failed; error() is None otherwise.
96
+ session = self._heads_up()
97
+ session.start_hand()
98
+ assert session.next_step().error() is None
99
+
100
+ def test_abort_hand_refunds_and_ends_hand(self):
101
+ # The escape hatch for a Failed step: refund committed chips and
102
+ # reset the table. Blinds are posted at start_hand, so the refund
103
+ # covers at least the small blind.
104
+ session = self._heads_up()
105
+ session.start_hand()
106
+ refunded = session.abort_hand()
107
+ assert isinstance(refunded, int)
108
+ assert refunded > 0
109
+ assert not session.is_hand_in_progress()
110
+
94
111
  def test_count_funded(self):
95
112
  session = self._heads_up()
96
113
  assert session.count_funded() == 2
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