rust-pgn-reader-python-binding 3.0.0__tar.gz → 3.2.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.
- {rust_pgn_reader_python_binding-3.0.0 → rust_pgn_reader_python_binding-3.2.0}/Cargo.lock +43 -51
- {rust_pgn_reader_python_binding-3.0.0 → rust_pgn_reader_python_binding-3.2.0}/Cargo.toml +6 -5
- rust_pgn_reader_python_binding-3.2.0/PKG-INFO +7 -0
- {rust_pgn_reader_python_binding-3.0.0 → rust_pgn_reader_python_binding-3.2.0}/README.md +9 -2
- {rust_pgn_reader_python_binding-3.0.0 → rust_pgn_reader_python_binding-3.2.0}/benches/parquet_bench.rs +0 -7
- rust_pgn_reader_python_binding-3.2.0/rust_pgn_reader_python_binding.pyi +49 -0
- {rust_pgn_reader_python_binding-3.0.0 → rust_pgn_reader_python_binding-3.2.0}/src/comment_parsing.rs +96 -76
- {rust_pgn_reader_python_binding-3.0.0 → rust_pgn_reader_python_binding-3.2.0}/src/example_str.py +1 -0
- {rust_pgn_reader_python_binding-3.0.0 → rust_pgn_reader_python_binding-3.2.0}/src/lib.rs +158 -34
- rust_pgn_reader_python_binding-3.0.0/PKG-INFO +0 -47
- {rust_pgn_reader_python_binding-3.0.0 → rust_pgn_reader_python_binding-3.2.0}/.github/workflows/CI.yml +0 -0
- {rust_pgn_reader_python_binding-3.0.0 → rust_pgn_reader_python_binding-3.2.0}/.gitignore +0 -0
- {rust_pgn_reader_python_binding-3.0.0 → rust_pgn_reader_python_binding-3.2.0}/pyproject.toml +0 -0
- {rust_pgn_reader_python_binding-3.0.0 → rust_pgn_reader_python_binding-3.2.0}/src/bench.py +0 -0
- {rust_pgn_reader_python_binding-3.0.0 → rust_pgn_reader_python_binding-3.2.0}/src/bench_parquet.py +0 -0
- {rust_pgn_reader_python_binding-3.0.0 → rust_pgn_reader_python_binding-3.2.0}/src/bench_parquet_arrow.py +0 -0
- {rust_pgn_reader_python_binding-3.0.0 → rust_pgn_reader_python_binding-3.2.0}/src/bench_parquet_parallel.py +0 -0
- {rust_pgn_reader_python_binding-3.0.0 → rust_pgn_reader_python_binding-3.2.0}/src/example_rowgroup.py +0 -0
- {rust_pgn_reader_python_binding-3.0.0 → rust_pgn_reader_python_binding-3.2.0}/src/test.py +0 -0
|
@@ -341,9 +341,9 @@ dependencies = [
|
|
|
341
341
|
|
|
342
342
|
[[package]]
|
|
343
343
|
name = "btoi"
|
|
344
|
-
version = "0.
|
|
344
|
+
version = "0.5.0"
|
|
345
345
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
346
|
-
checksum = "
|
|
346
|
+
checksum = "3b5ab9db53bcda568284df0fd39f6eac24ad6f7ba7ff1168b9e76eba6576b976"
|
|
347
347
|
dependencies = [
|
|
348
348
|
"num-traits",
|
|
349
349
|
]
|
|
@@ -449,12 +449,6 @@ dependencies = [
|
|
|
449
449
|
"half",
|
|
450
450
|
]
|
|
451
451
|
|
|
452
|
-
[[package]]
|
|
453
|
-
name = "circular"
|
|
454
|
-
version = "0.3.0"
|
|
455
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
456
|
-
checksum = "b0fc239e0f6cb375d2402d48afb92f76f5404fd1df208a41930ec81eda078bea"
|
|
457
|
-
|
|
458
452
|
[[package]]
|
|
459
453
|
name = "clap"
|
|
460
454
|
version = "4.5.39"
|
|
@@ -602,9 +596,9 @@ dependencies = [
|
|
|
602
596
|
|
|
603
597
|
[[package]]
|
|
604
598
|
name = "either"
|
|
605
|
-
version = "1.
|
|
599
|
+
version = "1.13.0"
|
|
606
600
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
607
|
-
checksum = "
|
|
601
|
+
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
|
|
608
602
|
|
|
609
603
|
[[package]]
|
|
610
604
|
name = "equivalent"
|
|
@@ -721,9 +715,9 @@ dependencies = [
|
|
|
721
715
|
|
|
722
716
|
[[package]]
|
|
723
717
|
name = "indoc"
|
|
724
|
-
version = "2.0.
|
|
718
|
+
version = "2.0.5"
|
|
725
719
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
726
|
-
checksum = "
|
|
720
|
+
checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5"
|
|
727
721
|
|
|
728
722
|
[[package]]
|
|
729
723
|
name = "integer-encoding"
|
|
@@ -757,11 +751,10 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
|
|
757
751
|
|
|
758
752
|
[[package]]
|
|
759
753
|
name = "jobserver"
|
|
760
|
-
version = "0.1.
|
|
754
|
+
version = "0.1.32"
|
|
761
755
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
762
|
-
checksum = "
|
|
756
|
+
checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0"
|
|
763
757
|
dependencies = [
|
|
764
|
-
"getrandom 0.3.3",
|
|
765
758
|
"libc",
|
|
766
759
|
]
|
|
767
760
|
|
|
@@ -847,9 +840,9 @@ dependencies = [
|
|
|
847
840
|
|
|
848
841
|
[[package]]
|
|
849
842
|
name = "libc"
|
|
850
|
-
version = "0.2.
|
|
843
|
+
version = "0.2.169"
|
|
851
844
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
852
|
-
checksum = "
|
|
845
|
+
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
|
|
853
846
|
|
|
854
847
|
[[package]]
|
|
855
848
|
name = "libm"
|
|
@@ -1048,9 +1041,9 @@ dependencies = [
|
|
|
1048
1041
|
|
|
1049
1042
|
[[package]]
|
|
1050
1043
|
name = "once_cell"
|
|
1051
|
-
version = "1.
|
|
1044
|
+
version = "1.20.2"
|
|
1052
1045
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1053
|
-
checksum = "
|
|
1046
|
+
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
|
|
1054
1047
|
|
|
1055
1048
|
[[package]]
|
|
1056
1049
|
name = "oorandom"
|
|
@@ -1117,12 +1110,11 @@ checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
|
|
1117
1110
|
|
|
1118
1111
|
[[package]]
|
|
1119
1112
|
name = "pgn-reader"
|
|
1120
|
-
version = "0.
|
|
1113
|
+
version = "0.28.0"
|
|
1121
1114
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1122
|
-
checksum = "
|
|
1115
|
+
checksum = "d0e07db84c19e093741643903738788e6f773237c7eeb60f9a40191afc2a815a"
|
|
1123
1116
|
dependencies = [
|
|
1124
1117
|
"btoi",
|
|
1125
|
-
"circular",
|
|
1126
1118
|
"memchr",
|
|
1127
1119
|
"shakmaty",
|
|
1128
1120
|
]
|
|
@@ -1201,9 +1193,9 @@ dependencies = [
|
|
|
1201
1193
|
|
|
1202
1194
|
[[package]]
|
|
1203
1195
|
name = "portable-atomic"
|
|
1204
|
-
version = "1.
|
|
1196
|
+
version = "1.10.0"
|
|
1205
1197
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1206
|
-
checksum = "
|
|
1198
|
+
checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6"
|
|
1207
1199
|
|
|
1208
1200
|
[[package]]
|
|
1209
1201
|
name = "portable-atomic-util"
|
|
@@ -1216,18 +1208,18 @@ dependencies = [
|
|
|
1216
1208
|
|
|
1217
1209
|
[[package]]
|
|
1218
1210
|
name = "proc-macro2"
|
|
1219
|
-
version = "1.0.
|
|
1211
|
+
version = "1.0.92"
|
|
1220
1212
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1221
|
-
checksum = "
|
|
1213
|
+
checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
|
|
1222
1214
|
dependencies = [
|
|
1223
1215
|
"unicode-ident",
|
|
1224
1216
|
]
|
|
1225
1217
|
|
|
1226
1218
|
[[package]]
|
|
1227
1219
|
name = "pyo3"
|
|
1228
|
-
version = "0.25.
|
|
1220
|
+
version = "0.25.1"
|
|
1229
1221
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1230
|
-
checksum = "
|
|
1222
|
+
checksum = "8970a78afe0628a3e3430376fc5fd76b6b45c4d43360ffd6cdd40bdde72b682a"
|
|
1231
1223
|
dependencies = [
|
|
1232
1224
|
"chrono",
|
|
1233
1225
|
"chrono-tz",
|
|
@@ -1264,9 +1256,9 @@ dependencies = [
|
|
|
1264
1256
|
|
|
1265
1257
|
[[package]]
|
|
1266
1258
|
name = "pyo3-build-config"
|
|
1267
|
-
version = "0.25.
|
|
1259
|
+
version = "0.25.1"
|
|
1268
1260
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1269
|
-
checksum = "
|
|
1261
|
+
checksum = "458eb0c55e7ece017adeba38f2248ff3ac615e53660d7c71a238d7d2a01c7598"
|
|
1270
1262
|
dependencies = [
|
|
1271
1263
|
"once_cell",
|
|
1272
1264
|
"target-lexicon",
|
|
@@ -1274,9 +1266,9 @@ dependencies = [
|
|
|
1274
1266
|
|
|
1275
1267
|
[[package]]
|
|
1276
1268
|
name = "pyo3-ffi"
|
|
1277
|
-
version = "0.25.
|
|
1269
|
+
version = "0.25.1"
|
|
1278
1270
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1279
|
-
checksum = "
|
|
1271
|
+
checksum = "7114fe5457c61b276ab77c5055f206295b812608083644a5c5b2640c3102565c"
|
|
1280
1272
|
dependencies = [
|
|
1281
1273
|
"libc",
|
|
1282
1274
|
"pyo3-build-config",
|
|
@@ -1284,9 +1276,9 @@ dependencies = [
|
|
|
1284
1276
|
|
|
1285
1277
|
[[package]]
|
|
1286
1278
|
name = "pyo3-macros"
|
|
1287
|
-
version = "0.25.
|
|
1279
|
+
version = "0.25.1"
|
|
1288
1280
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1289
|
-
checksum = "
|
|
1281
|
+
checksum = "a8725c0a622b374d6cb051d11a0983786448f7785336139c3c94f5aa6bef7e50"
|
|
1290
1282
|
dependencies = [
|
|
1291
1283
|
"proc-macro2",
|
|
1292
1284
|
"pyo3-macros-backend",
|
|
@@ -1296,9 +1288,9 @@ dependencies = [
|
|
|
1296
1288
|
|
|
1297
1289
|
[[package]]
|
|
1298
1290
|
name = "pyo3-macros-backend"
|
|
1299
|
-
version = "0.25.
|
|
1291
|
+
version = "0.25.1"
|
|
1300
1292
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1301
|
-
checksum = "
|
|
1293
|
+
checksum = "4109984c22491085343c05b0dbc54ddc405c3cf7b4374fc533f5c3313a572ccc"
|
|
1302
1294
|
dependencies = [
|
|
1303
1295
|
"heck",
|
|
1304
1296
|
"proc-macro2",
|
|
@@ -1309,9 +1301,9 @@ dependencies = [
|
|
|
1309
1301
|
|
|
1310
1302
|
[[package]]
|
|
1311
1303
|
name = "quote"
|
|
1312
|
-
version = "1.0.
|
|
1304
|
+
version = "1.0.37"
|
|
1313
1305
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1314
|
-
checksum = "
|
|
1306
|
+
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
|
|
1315
1307
|
dependencies = [
|
|
1316
1308
|
"proc-macro2",
|
|
1317
1309
|
]
|
|
@@ -1394,7 +1386,7 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
|
|
1394
1386
|
|
|
1395
1387
|
[[package]]
|
|
1396
1388
|
name = "rust_pgn_reader_python_binding"
|
|
1397
|
-
version = "3.
|
|
1389
|
+
version = "3.2.0"
|
|
1398
1390
|
dependencies = [
|
|
1399
1391
|
"arrow",
|
|
1400
1392
|
"arrow-array",
|
|
@@ -1491,9 +1483,9 @@ dependencies = [
|
|
|
1491
1483
|
|
|
1492
1484
|
[[package]]
|
|
1493
1485
|
name = "shakmaty"
|
|
1494
|
-
version = "0.
|
|
1486
|
+
version = "0.29.1"
|
|
1495
1487
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1496
|
-
checksum = "
|
|
1488
|
+
checksum = "334bb41280a79854707d79c117c9818ddeaead15108e951c0b082ba2acedd0d1"
|
|
1497
1489
|
dependencies = [
|
|
1498
1490
|
"arrayvec",
|
|
1499
1491
|
"bitflags",
|
|
@@ -1534,9 +1526,9 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
|
|
1534
1526
|
|
|
1535
1527
|
[[package]]
|
|
1536
1528
|
name = "syn"
|
|
1537
|
-
version = "2.0.
|
|
1529
|
+
version = "2.0.91"
|
|
1538
1530
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1539
|
-
checksum = "
|
|
1531
|
+
checksum = "d53cbcb5a243bd33b7858b1d7f4aca2153490815872d86d955d6ea29f743c035"
|
|
1540
1532
|
dependencies = [
|
|
1541
1533
|
"proc-macro2",
|
|
1542
1534
|
"quote",
|
|
@@ -1617,15 +1609,15 @@ checksum = "e7b17f197b3050ba473acf9181f7b1d3b66d1cf7356c6cc57886662276e65908"
|
|
|
1617
1609
|
|
|
1618
1610
|
[[package]]
|
|
1619
1611
|
name = "unicode-ident"
|
|
1620
|
-
version = "1.0.
|
|
1612
|
+
version = "1.0.14"
|
|
1621
1613
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1622
|
-
checksum = "
|
|
1614
|
+
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
|
|
1623
1615
|
|
|
1624
1616
|
[[package]]
|
|
1625
1617
|
name = "unindent"
|
|
1626
|
-
version = "0.2.
|
|
1618
|
+
version = "0.2.3"
|
|
1627
1619
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1628
|
-
checksum = "
|
|
1620
|
+
checksum = "c7de7d73e1754487cb58364ee906a499937a0dfabd86bcb980fa99ec8c8fa2ce"
|
|
1629
1621
|
|
|
1630
1622
|
[[package]]
|
|
1631
1623
|
name = "version_check"
|
|
@@ -1913,18 +1905,18 @@ dependencies = [
|
|
|
1913
1905
|
|
|
1914
1906
|
[[package]]
|
|
1915
1907
|
name = "zstd-safe"
|
|
1916
|
-
version = "7.2.
|
|
1908
|
+
version = "7.2.1"
|
|
1917
1909
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1918
|
-
checksum = "
|
|
1910
|
+
checksum = "54a3ab4db68cea366acc5c897c7b4d4d1b8994a9cd6e6f841f8964566a419059"
|
|
1919
1911
|
dependencies = [
|
|
1920
1912
|
"zstd-sys",
|
|
1921
1913
|
]
|
|
1922
1914
|
|
|
1923
1915
|
[[package]]
|
|
1924
1916
|
name = "zstd-sys"
|
|
1925
|
-
version = "2.0.
|
|
1917
|
+
version = "2.0.13+zstd.1.5.6"
|
|
1926
1918
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1927
|
-
checksum = "
|
|
1919
|
+
checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa"
|
|
1928
1920
|
dependencies = [
|
|
1929
1921
|
"cc",
|
|
1930
1922
|
"pkg-config",
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "rust_pgn_reader_python_binding"
|
|
3
|
-
version = "3.
|
|
4
|
-
edition = "
|
|
3
|
+
version = "3.2.0"
|
|
4
|
+
edition = "2024"
|
|
5
|
+
readme = "README.md"
|
|
5
6
|
|
|
6
7
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
7
8
|
[lib]
|
|
@@ -10,9 +11,9 @@ crate-type = ["lib", "cdylib"]
|
|
|
10
11
|
# lib is needed for the native benchmark
|
|
11
12
|
|
|
12
13
|
[dependencies]
|
|
13
|
-
pyo3 = "0.25.
|
|
14
|
-
shakmaty = "0.
|
|
15
|
-
pgn-reader = "0.
|
|
14
|
+
pyo3 = "0.25.1"
|
|
15
|
+
shakmaty = "0.29"
|
|
16
|
+
pgn-reader = "0.28.0"
|
|
16
17
|
nom = "8.0"
|
|
17
18
|
rayon = "1.10"
|
|
18
19
|
num_cpus = "1.17"
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: rust_pgn_reader_python_binding
|
|
3
|
+
Version: 3.2.0
|
|
4
|
+
Classifier: Programming Language :: Rust
|
|
5
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
6
|
+
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
7
|
+
Requires-Python: >=3.8
|
|
@@ -14,15 +14,21 @@ Below are some benchmarks on Lichess's 2013-07 chess games (293,459 games) on an
|
|
|
14
14
|
| rust_pgn_reader_python_binding | PGN | 4.7s |
|
|
15
15
|
| rust_pgn_reader_python_binding, parse_game (single_threaded) | parquet | 3.3s |
|
|
16
16
|
| rust_pgn_reader_python_binding, parse_games (multithreaded) | parquet | 0.5s |
|
|
17
|
+
| rust_pgn_reader_python_binding, parse_game_moves_arrow_chunked_array (multithreaded) | parquet | 0.35s |
|
|
17
18
|
| [chess-library](https://github.com/Disservin/chess-library) | PGN | 2s |
|
|
18
19
|
| [python-chess](https://github.com/niklasf/python-chess) | PGN | 3+ min |
|
|
19
20
|
|
|
20
|
-
|
|
21
|
+
To replicate, download `2013-07-train-00000-of-00001.parquet` and then run:
|
|
21
22
|
|
|
22
|
-
|
|
23
|
+
`python bench_parquet.py` (single-threaded parse_game)
|
|
24
|
+
|
|
25
|
+
`python bench_parquet_parallel.py` (multithreaded parse_games)
|
|
26
|
+
|
|
27
|
+
`python bench_parquet_arrow.py` (multithreaded parse_game_moves_arrow_chunked_array)
|
|
23
28
|
|
|
24
29
|
## Building
|
|
25
30
|
`maturin develop`
|
|
31
|
+
|
|
26
32
|
`maturin develop --release`
|
|
27
33
|
|
|
28
34
|
For a more thorough tutorial, follow https://lukesalamone.github.io/posts/how-to-create-rust-python-bindings/
|
|
@@ -35,4 +41,5 @@ Linux/WSL-only:
|
|
|
35
41
|
|
|
36
42
|
## Testing
|
|
37
43
|
`cargo test`
|
|
44
|
+
|
|
38
45
|
`python -m unittest src/test.py`
|
|
@@ -1,16 +1,9 @@
|
|
|
1
1
|
use arrow::array::{Array, StringArray};
|
|
2
|
-
|
|
3
2
|
use parquet::arrow::arrow_reader::ParquetRecordBatchReaderBuilder;
|
|
4
|
-
|
|
5
|
-
// use parquet::file::reader::{FileReader, SerializedFileReader};
|
|
6
|
-
// use parquet::record::RowAccessor;
|
|
7
|
-
|
|
8
3
|
use std::fs::File;
|
|
9
4
|
use std::path::Path;
|
|
10
5
|
use std::time::Instant;
|
|
11
6
|
|
|
12
|
-
// use criterion::{criterion_group, criterion_main, Criterion};
|
|
13
|
-
|
|
14
7
|
use rust_pgn_reader_python_binding::parse_multiple_games_native;
|
|
15
8
|
|
|
16
9
|
pub fn bench_parquet() {
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
from typing import List, Optional, Tuple
|
|
2
|
+
import pyarrow
|
|
3
|
+
|
|
4
|
+
class PyUciMove:
|
|
5
|
+
from_square: int
|
|
6
|
+
to_square: int
|
|
7
|
+
promotion: Optional[int]
|
|
8
|
+
|
|
9
|
+
def __init__(
|
|
10
|
+
self, from_square: int, to_square: int, promotion: Optional[int]
|
|
11
|
+
) -> None: ...
|
|
12
|
+
@property
|
|
13
|
+
def get_from_square_name(self) -> str: ...
|
|
14
|
+
@property
|
|
15
|
+
def get_to_square_name(self) -> str: ...
|
|
16
|
+
@property
|
|
17
|
+
def get_promotion_name(self) -> Optional[str]: ...
|
|
18
|
+
def __str__(self) -> str: ...
|
|
19
|
+
def __repr__(self) -> str: ...
|
|
20
|
+
|
|
21
|
+
class PositionStatus:
|
|
22
|
+
is_checkmate: bool
|
|
23
|
+
is_stalemate: bool
|
|
24
|
+
legal_move_count: int
|
|
25
|
+
is_game_over: bool
|
|
26
|
+
insufficient_material: Tuple[bool, bool]
|
|
27
|
+
turn: bool
|
|
28
|
+
|
|
29
|
+
class MoveExtractor:
|
|
30
|
+
moves: List[PyUciMove]
|
|
31
|
+
valid_moves: bool
|
|
32
|
+
comments: List[str]
|
|
33
|
+
evals: List[float]
|
|
34
|
+
clock_times: List[Tuple[int, int, float]]
|
|
35
|
+
outcome: Optional[str]
|
|
36
|
+
headers: List[Tuple[str, str]]
|
|
37
|
+
castling_rights: List[Tuple[bool, bool, bool, bool]]
|
|
38
|
+
position_status: Optional[PositionStatus]
|
|
39
|
+
|
|
40
|
+
def __init__(self) -> None: ...
|
|
41
|
+
def turn(self) -> bool: ...
|
|
42
|
+
|
|
43
|
+
def parse_game(pgn: str) -> MoveExtractor: ...
|
|
44
|
+
def parse_games(
|
|
45
|
+
pgns: List[str], num_threads: Optional[int] = None
|
|
46
|
+
) -> List[MoveExtractor]: ...
|
|
47
|
+
def parse_game_moves_arrow_chunked_array(
|
|
48
|
+
pgn_chunked_array: pyarrow.ChunkedArray, num_threads: Optional[int] = None
|
|
49
|
+
) -> List[MoveExtractor]: ...
|
{rust_pgn_reader_python_binding-3.0.0 → rust_pgn_reader_python_binding-3.2.0}/src/comment_parsing.rs
RENAMED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
use nom::{
|
|
2
2
|
branch::alt,
|
|
3
3
|
bytes::complete::{is_not, tag},
|
|
4
|
-
character::complete::{char, digit1
|
|
4
|
+
character::complete::{char, digit1},
|
|
5
5
|
combinator::{map, map_res, not, opt, peek, recognize},
|
|
6
6
|
multi::{many0, many1},
|
|
7
7
|
sequence::{delimited, pair, preceded},
|
|
8
8
|
IResult, Parser,
|
|
9
9
|
};
|
|
10
|
+
use std::borrow::Cow;
|
|
11
|
+
// use nom::character::complete::multispace1;
|
|
12
|
+
use nom::bytes::complete::take_while1;
|
|
10
13
|
|
|
11
14
|
#[derive(Debug, PartialEq)]
|
|
12
15
|
pub enum ParsedTag {
|
|
@@ -20,35 +23,43 @@ pub enum ParsedTag {
|
|
|
20
23
|
}
|
|
21
24
|
|
|
22
25
|
#[derive(Debug, PartialEq)]
|
|
23
|
-
pub enum CommentContent {
|
|
24
|
-
Text(
|
|
26
|
+
pub enum CommentContent<'a> {
|
|
27
|
+
Text(Cow<'a, str>),
|
|
25
28
|
Tag(ParsedTag),
|
|
26
29
|
}
|
|
27
30
|
|
|
28
|
-
|
|
31
|
+
fn as_str(bytes: &[u8]) -> Result<&str, std::str::Utf8Error> {
|
|
32
|
+
std::str::from_utf8(bytes)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
fn to_cow_str(bytes: &[u8]) -> CommentContent<'_> {
|
|
36
|
+
CommentContent::Text(String::from_utf8_lossy(bytes))
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
pub fn parse_comments(input: &[u8]) -> IResult<&[u8], Vec<CommentContent<'_>>> {
|
|
29
40
|
many0(alt((
|
|
30
41
|
// Attempt to parse a known structured tag first
|
|
31
42
|
map(parse_structured_tag, CommentContent::Tag),
|
|
32
43
|
// If not a known tag, but looks like a tag (e.g. [%unknown ...]), parse as text
|
|
33
44
|
map(
|
|
34
45
|
recognize(delimited(tag("[%"), is_not("]"), char(']'))),
|
|
35
|
-
|
|
46
|
+
to_cow_str,
|
|
36
47
|
),
|
|
37
48
|
// Otherwise, parse as regular text content. This must not be empty.
|
|
38
49
|
map(
|
|
39
50
|
recognize(many1(alt((
|
|
40
|
-
is_not(
|
|
51
|
+
is_not([b'[']), // Takes any char except '['
|
|
41
52
|
// Takes a '[' if it's NOT followed by '%' (to allow "[abc]" as text)
|
|
42
53
|
recognize(preceded(char('['), peek(not(char('%'))))),
|
|
43
54
|
)))),
|
|
44
|
-
|
|
55
|
+
to_cow_str,
|
|
45
56
|
),
|
|
46
57
|
)))
|
|
47
58
|
.parse(input)
|
|
48
59
|
}
|
|
49
60
|
|
|
50
61
|
/// Parser for a complete tag like [%eval ...] or [%clk ...]
|
|
51
|
-
fn parse_structured_tag(input: &
|
|
62
|
+
fn parse_structured_tag(input: &[u8]) -> IResult<&[u8], ParsedTag> {
|
|
52
63
|
delimited(
|
|
53
64
|
tag("[%"),
|
|
54
65
|
alt((parse_eval_content, parse_clk_content)),
|
|
@@ -58,7 +69,7 @@ fn parse_structured_tag(input: &str) -> IResult<&str, ParsedTag> {
|
|
|
58
69
|
}
|
|
59
70
|
|
|
60
71
|
/// Parses the content of an eval tag, e.g., "eval 12.3" or "eval #3"
|
|
61
|
-
fn parse_eval_content(input: &
|
|
72
|
+
fn parse_eval_content(input: &[u8]) -> IResult<&[u8], ParsedTag> {
|
|
62
73
|
preceded(
|
|
63
74
|
tag("eval"),
|
|
64
75
|
preceded(
|
|
@@ -73,7 +84,7 @@ fn parse_eval_content(input: &str) -> IResult<&str, ParsedTag> {
|
|
|
73
84
|
}
|
|
74
85
|
|
|
75
86
|
/// Parses the content of a clk tag, e.g., "clk 1:23:45.6"
|
|
76
|
-
fn parse_clk_content(input: &
|
|
87
|
+
fn parse_clk_content(input: &[u8]) -> IResult<&[u8], ParsedTag> {
|
|
77
88
|
preceded(
|
|
78
89
|
tag("clk"),
|
|
79
90
|
preceded(
|
|
@@ -89,23 +100,29 @@ fn parse_clk_content(input: &str) -> IResult<&str, ParsedTag> {
|
|
|
89
100
|
}
|
|
90
101
|
|
|
91
102
|
/// Parser for a signed floating-point number, e.g., "-123.45", "+3.0", "7"
|
|
92
|
-
fn parse_signed_float(input: &
|
|
103
|
+
fn parse_signed_float(input: &[u8]) -> IResult<&[u8], f64> {
|
|
93
104
|
map_res(
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
105
|
+
map_res(
|
|
106
|
+
recognize(pair(
|
|
107
|
+
opt(alt((char('+'), char('-')))),
|
|
108
|
+
recognize(pair(digit1, opt(preceded(char('.'), digit1)))),
|
|
109
|
+
)),
|
|
110
|
+
as_str,
|
|
111
|
+
),
|
|
98
112
|
|s: &str| s.parse::<f64>(),
|
|
99
113
|
)
|
|
100
114
|
.parse(input)
|
|
101
115
|
}
|
|
102
116
|
|
|
103
117
|
/// Parser for a mate value, e.g., "#-3", "#5"
|
|
104
|
-
fn parse_mate_value(input: &
|
|
118
|
+
fn parse_mate_value(input: &[u8]) -> IResult<&[u8], i32> {
|
|
105
119
|
preceded(
|
|
106
120
|
char('#'),
|
|
107
121
|
map_res(
|
|
108
|
-
|
|
122
|
+
map_res(
|
|
123
|
+
recognize(pair(opt(char('-')), digit1)), // Recognizes signed integer
|
|
124
|
+
as_str,
|
|
125
|
+
),
|
|
109
126
|
|s: &str| s.parse::<i32>(),
|
|
110
127
|
),
|
|
111
128
|
)
|
|
@@ -113,12 +130,12 @@ fn parse_mate_value(input: &str) -> IResult<&str, i32> {
|
|
|
113
130
|
}
|
|
114
131
|
|
|
115
132
|
/// Parser for a time value in H:M:S format, e.g., "12:34:56" or "1:2:3.45"
|
|
116
|
-
fn parse_hms_time(input: &
|
|
133
|
+
fn parse_hms_time(input: &[u8]) -> IResult<&[u8], (u32, u8, f64)> {
|
|
117
134
|
map(
|
|
118
135
|
(
|
|
119
|
-
map_res(digit1, |s: &str| s.parse::<u32>()), // Hours
|
|
136
|
+
map_res(map_res(digit1, as_str), |s: &str| s.parse::<u32>()), // Hours
|
|
120
137
|
char(':'),
|
|
121
|
-
map_res(digit1, |s: &str| s.parse::<u8>()), // Minutes
|
|
138
|
+
map_res(map_res(digit1, as_str), |s: &str| s.parse::<u8>()), // Minutes
|
|
122
139
|
char(':'),
|
|
123
140
|
parse_seconds_with_fraction, // Seconds with optional fraction
|
|
124
141
|
),
|
|
@@ -128,17 +145,20 @@ fn parse_hms_time(input: &str) -> IResult<&str, (u32, u8, f64)> {
|
|
|
128
145
|
}
|
|
129
146
|
|
|
130
147
|
/// Parser for seconds, which can be an integer or have a fractional part
|
|
131
|
-
fn parse_seconds_with_fraction(input: &
|
|
148
|
+
fn parse_seconds_with_fraction(input: &[u8]) -> IResult<&[u8], f64> {
|
|
132
149
|
map_res(
|
|
133
|
-
|
|
150
|
+
map_res(
|
|
151
|
+
recognize(pair(digit1, opt(preceded(char('.'), digit1)))),
|
|
152
|
+
as_str,
|
|
153
|
+
),
|
|
134
154
|
|s: &str| s.parse::<f64>(),
|
|
135
155
|
)
|
|
136
156
|
.parse(input)
|
|
137
157
|
}
|
|
138
158
|
|
|
139
159
|
/// Parser for one or more whitespace characters (spaces, newlines, tabs, etc.)
|
|
140
|
-
fn spacing(input: &
|
|
141
|
-
|
|
160
|
+
fn spacing(input: &[u8]) -> IResult<&[u8], &[u8]> {
|
|
161
|
+
take_while1(|c: u8| c.is_ascii_whitespace())(input)
|
|
142
162
|
}
|
|
143
163
|
|
|
144
164
|
#[cfg(test)]
|
|
@@ -147,7 +167,7 @@ mod tests {
|
|
|
147
167
|
|
|
148
168
|
#[test]
|
|
149
169
|
fn test_comments1() {
|
|
150
|
-
let input = "[%eval 123] some text [%clk 12:34:56]";
|
|
170
|
+
let input = b"[%eval 123] some text [%clk 12:34:56]";
|
|
151
171
|
let result = parse_comments(input);
|
|
152
172
|
assert!(result.is_ok());
|
|
153
173
|
let (remaining, parsed) = result.unwrap();
|
|
@@ -155,7 +175,7 @@ mod tests {
|
|
|
155
175
|
parsed,
|
|
156
176
|
vec![
|
|
157
177
|
CommentContent::Tag(ParsedTag::Eval(123.0)),
|
|
158
|
-
CommentContent::Text(" some text "
|
|
178
|
+
CommentContent::Text(Cow::from(" some text ")),
|
|
159
179
|
CommentContent::Tag(ParsedTag::ClkTime {
|
|
160
180
|
hours: 12,
|
|
161
181
|
minutes: 34,
|
|
@@ -163,12 +183,12 @@ mod tests {
|
|
|
163
183
|
})
|
|
164
184
|
]
|
|
165
185
|
);
|
|
166
|
-
assert_eq!(remaining, "");
|
|
186
|
+
assert_eq!(remaining, b"");
|
|
167
187
|
}
|
|
168
188
|
|
|
169
189
|
#[test]
|
|
170
190
|
fn test_comments2() {
|
|
171
|
-
let input = "[%clk 12:34:56] some text ";
|
|
191
|
+
let input = b"[%clk 12:34:56] some text ";
|
|
172
192
|
let result = parse_comments(input);
|
|
173
193
|
assert!(result.is_ok());
|
|
174
194
|
let (remaining, parsed) = result.unwrap();
|
|
@@ -180,15 +200,15 @@ mod tests {
|
|
|
180
200
|
minutes: 34,
|
|
181
201
|
seconds: 56.0
|
|
182
202
|
}),
|
|
183
|
-
CommentContent::Text(" some text "
|
|
203
|
+
CommentContent::Text(Cow::from(" some text "))
|
|
184
204
|
]
|
|
185
205
|
);
|
|
186
|
-
assert_eq!(remaining, "");
|
|
206
|
+
assert_eq!(remaining, b"");
|
|
187
207
|
}
|
|
188
208
|
|
|
189
209
|
#[test]
|
|
190
210
|
fn test_comment_fractional_sec() {
|
|
191
|
-
let input = "[%clk 12:34:56.0123]";
|
|
211
|
+
let input = b"[%clk 12:34:56.0123]";
|
|
192
212
|
let result = parse_comments(input);
|
|
193
213
|
assert!(result.is_ok());
|
|
194
214
|
let (remaining, parsed) = result.unwrap();
|
|
@@ -200,32 +220,32 @@ mod tests {
|
|
|
200
220
|
seconds: 56.0123
|
|
201
221
|
})]
|
|
202
222
|
);
|
|
203
|
-
assert_eq!(remaining, "");
|
|
223
|
+
assert_eq!(remaining, b"");
|
|
204
224
|
}
|
|
205
225
|
|
|
206
226
|
#[test]
|
|
207
227
|
fn test_parse_structured_tag_eval() {
|
|
208
|
-
let input = "[%eval 123]";
|
|
228
|
+
let input = b"[%eval 123]";
|
|
209
229
|
let result = parse_structured_tag(input);
|
|
210
230
|
assert!(result.is_ok());
|
|
211
231
|
let (remaining, parsed) = result.unwrap();
|
|
212
232
|
assert_eq!(parsed, ParsedTag::Eval(123.0));
|
|
213
|
-
assert_eq!(remaining, "");
|
|
233
|
+
assert_eq!(remaining, b"");
|
|
214
234
|
}
|
|
215
235
|
|
|
216
236
|
#[test]
|
|
217
237
|
fn test_parse_structured_tag_eval_mate() {
|
|
218
|
-
let input = "[%eval #-3]";
|
|
238
|
+
let input = b"[%eval #-3]";
|
|
219
239
|
let result = parse_structured_tag(input);
|
|
220
240
|
assert!(result.is_ok());
|
|
221
241
|
let (remaining, parsed) = result.unwrap();
|
|
222
242
|
assert_eq!(parsed, ParsedTag::Mate(-3));
|
|
223
|
-
assert_eq!(remaining, "");
|
|
243
|
+
assert_eq!(remaining, b"");
|
|
224
244
|
}
|
|
225
245
|
|
|
226
246
|
#[test]
|
|
227
247
|
fn test_parse_structured_tag_clk() {
|
|
228
|
-
let input = "[%clk 1:2:3.45]";
|
|
248
|
+
let input = b"[%clk 1:2:3.45]";
|
|
229
249
|
let result = parse_structured_tag(input);
|
|
230
250
|
assert!(result.is_ok());
|
|
231
251
|
let (remaining, parsed) = result.unwrap();
|
|
@@ -237,13 +257,13 @@ mod tests {
|
|
|
237
257
|
seconds: 3.45
|
|
238
258
|
}
|
|
239
259
|
);
|
|
240
|
-
assert_eq!(remaining, "");
|
|
260
|
+
assert_eq!(remaining, b"");
|
|
241
261
|
}
|
|
242
262
|
|
|
243
263
|
// clk expects a particular format: H:M:S - this test ensures incorrect formats fail
|
|
244
264
|
#[test]
|
|
245
265
|
fn test_tag_parser_incorrect_clk_value() {
|
|
246
|
-
let input = "[%clk 123]"; // Incorrect format for clk time
|
|
266
|
+
let input = b"[%clk 123]"; // Incorrect format for clk time
|
|
247
267
|
let result = parse_structured_tag(input);
|
|
248
268
|
assert!(
|
|
249
269
|
result.is_err(),
|
|
@@ -253,7 +273,7 @@ mod tests {
|
|
|
253
273
|
|
|
254
274
|
#[test]
|
|
255
275
|
fn test_tag_parser_incorrect_eval_value() {
|
|
256
|
-
let input = "[%eval notanumber]";
|
|
276
|
+
let input = b"[%eval notanumber]";
|
|
257
277
|
let result = parse_structured_tag(input);
|
|
258
278
|
assert!(
|
|
259
279
|
result.is_err(),
|
|
@@ -263,7 +283,7 @@ mod tests {
|
|
|
263
283
|
|
|
264
284
|
#[test]
|
|
265
285
|
fn test_parse_clk_content_correct() {
|
|
266
|
-
let input = "clk 12:34:56";
|
|
286
|
+
let input = b"clk 12:34:56";
|
|
267
287
|
let result = parse_clk_content(input);
|
|
268
288
|
assert!(result.is_ok());
|
|
269
289
|
let (remaining, parsed) = result.unwrap();
|
|
@@ -275,134 +295,134 @@ mod tests {
|
|
|
275
295
|
seconds: 56.0
|
|
276
296
|
}
|
|
277
297
|
);
|
|
278
|
-
assert_eq!(remaining, "");
|
|
298
|
+
assert_eq!(remaining, b"");
|
|
279
299
|
}
|
|
280
300
|
|
|
281
301
|
#[test]
|
|
282
302
|
fn test_parse_clk_content_incorrect_name() {
|
|
283
303
|
// This tests if parse_clk_content correctly fails if "clk" is not the prefix
|
|
284
|
-
let input = "eval 123";
|
|
304
|
+
let input = b"eval 123";
|
|
285
305
|
let result = parse_clk_content(input);
|
|
286
306
|
assert!(result.is_err());
|
|
287
307
|
}
|
|
288
308
|
|
|
289
309
|
#[test]
|
|
290
310
|
fn test_parse_eval_content_correct_float() {
|
|
291
|
-
let input = "eval -42.5";
|
|
311
|
+
let input = b"eval -42.5";
|
|
292
312
|
let result = parse_eval_content(input);
|
|
293
313
|
assert!(result.is_ok());
|
|
294
314
|
let (remaining, parsed) = result.unwrap();
|
|
295
315
|
assert_eq!(parsed, ParsedTag::Eval(-42.5));
|
|
296
|
-
assert_eq!(remaining, "");
|
|
316
|
+
assert_eq!(remaining, b"");
|
|
297
317
|
}
|
|
298
318
|
|
|
299
319
|
#[test]
|
|
300
320
|
fn test_parse_eval_content_correct_mate() {
|
|
301
|
-
let input = "eval #5";
|
|
321
|
+
let input = b"eval #5";
|
|
302
322
|
let result = parse_eval_content(input);
|
|
303
323
|
assert!(result.is_ok());
|
|
304
324
|
let (remaining, parsed) = result.unwrap();
|
|
305
325
|
assert_eq!(parsed, ParsedTag::Mate(5));
|
|
306
|
-
assert_eq!(remaining, "");
|
|
326
|
+
assert_eq!(remaining, b"");
|
|
307
327
|
}
|
|
308
328
|
|
|
309
329
|
#[test]
|
|
310
330
|
fn test_parse_signed_float_positive() {
|
|
311
|
-
let input = "123.45";
|
|
331
|
+
let input = b"123.45";
|
|
312
332
|
let result = parse_signed_float(input);
|
|
313
333
|
assert!(result.is_ok());
|
|
314
334
|
let (remaining, parsed) = result.unwrap();
|
|
315
335
|
assert_eq!(parsed, 123.45);
|
|
316
|
-
assert_eq!(remaining, "");
|
|
336
|
+
assert_eq!(remaining, b"");
|
|
317
337
|
}
|
|
318
338
|
|
|
319
339
|
#[test]
|
|
320
340
|
fn test_parse_signed_float_negative() {
|
|
321
|
-
let input = "-0.5";
|
|
341
|
+
let input = b"-0.5";
|
|
322
342
|
let result = parse_signed_float(input);
|
|
323
343
|
assert!(result.is_ok());
|
|
324
344
|
let (remaining, parsed) = result.unwrap();
|
|
325
345
|
assert_eq!(parsed, -0.5);
|
|
326
|
-
assert_eq!(remaining, "");
|
|
346
|
+
assert_eq!(remaining, b"");
|
|
327
347
|
}
|
|
328
348
|
|
|
329
349
|
#[test]
|
|
330
350
|
fn test_parse_mate_value_positive() {
|
|
331
|
-
let input = "#7";
|
|
351
|
+
let input = b"#7";
|
|
332
352
|
let result = parse_mate_value(input);
|
|
333
353
|
assert!(result.is_ok());
|
|
334
354
|
let (remaining, parsed) = result.unwrap();
|
|
335
355
|
assert_eq!(parsed, 7);
|
|
336
|
-
assert_eq!(remaining, "");
|
|
356
|
+
assert_eq!(remaining, b"");
|
|
337
357
|
}
|
|
338
358
|
|
|
339
359
|
#[test]
|
|
340
360
|
fn test_parse_mate_value_negative() {
|
|
341
|
-
let input = "#-2";
|
|
361
|
+
let input = b"#-2";
|
|
342
362
|
let result = parse_mate_value(input);
|
|
343
363
|
assert!(result.is_ok());
|
|
344
364
|
let (remaining, parsed) = result.unwrap();
|
|
345
365
|
assert_eq!(parsed, -2);
|
|
346
|
-
assert_eq!(remaining, "");
|
|
366
|
+
assert_eq!(remaining, b"");
|
|
347
367
|
}
|
|
348
368
|
|
|
349
369
|
#[test]
|
|
350
370
|
fn test_parse_hms_time_simple() {
|
|
351
|
-
let input = "12:34:56";
|
|
371
|
+
let input = b"12:34:56";
|
|
352
372
|
let result = parse_hms_time(input);
|
|
353
373
|
assert!(result.is_ok());
|
|
354
374
|
let (remaining, parsed) = result.unwrap();
|
|
355
375
|
assert_eq!(parsed, (12, 34, 56.0));
|
|
356
|
-
assert_eq!(remaining, "");
|
|
376
|
+
assert_eq!(remaining, b"");
|
|
357
377
|
}
|
|
358
378
|
|
|
359
379
|
#[test]
|
|
360
380
|
fn test_parse_hms_time_fractional() {
|
|
361
|
-
let input = "01:02:03.123";
|
|
381
|
+
let input = b"01:02:03.123";
|
|
362
382
|
let result = parse_hms_time(input);
|
|
363
383
|
assert!(result.is_ok());
|
|
364
384
|
let (remaining, parsed) = result.unwrap();
|
|
365
385
|
assert_eq!(parsed, (1, 2, 3.123));
|
|
366
|
-
assert_eq!(remaining, "");
|
|
386
|
+
assert_eq!(remaining, b"");
|
|
367
387
|
}
|
|
368
388
|
|
|
369
389
|
#[test]
|
|
370
390
|
fn test_parse_hms_time_invalid_char_in_hour() {
|
|
371
|
-
let input = "1a:00:00";
|
|
391
|
+
let input = b"1a:00:00";
|
|
372
392
|
let result = parse_hms_time(input);
|
|
373
393
|
assert!(result.is_err());
|
|
374
394
|
}
|
|
375
395
|
|
|
376
396
|
#[test]
|
|
377
397
|
fn test_parse_hms_time_missing_minutes() {
|
|
378
|
-
let input = "1::00";
|
|
398
|
+
let input = b"1::00";
|
|
379
399
|
let result = parse_hms_time(input); // digit1 for minutes will fail on ':'
|
|
380
400
|
assert!(result.is_err());
|
|
381
401
|
}
|
|
382
402
|
|
|
383
403
|
#[test]
|
|
384
404
|
fn test_parse_seconds_with_fraction_integer() {
|
|
385
|
-
let input = "42";
|
|
405
|
+
let input = b"42";
|
|
386
406
|
let result = parse_seconds_with_fraction(input);
|
|
387
407
|
assert!(result.is_ok());
|
|
388
408
|
let (remaining, parsed) = result.unwrap();
|
|
389
409
|
assert_eq!(parsed, 42.0);
|
|
390
|
-
assert_eq!(remaining, "");
|
|
410
|
+
assert_eq!(remaining, b"");
|
|
391
411
|
}
|
|
392
412
|
|
|
393
413
|
#[test]
|
|
394
414
|
fn test_parse_seconds_with_fraction_decimal() {
|
|
395
|
-
let input = "3.141";
|
|
415
|
+
let input = b"3.141";
|
|
396
416
|
let result = parse_seconds_with_fraction(input);
|
|
397
417
|
assert!(result.is_ok());
|
|
398
418
|
let (remaining, parsed) = result.unwrap();
|
|
399
419
|
assert_eq!(parsed, 3.141);
|
|
400
|
-
assert_eq!(remaining, "");
|
|
420
|
+
assert_eq!(remaining, b"");
|
|
401
421
|
}
|
|
402
422
|
|
|
403
423
|
#[test]
|
|
404
424
|
fn test_unknown_tag_is_parsed_as_text() {
|
|
405
|
-
let input = "[%timestamp 12345] some text";
|
|
425
|
+
let input = b"[%timestamp 12345] some text";
|
|
406
426
|
let result = parse_comments(input);
|
|
407
427
|
assert!(result.is_ok());
|
|
408
428
|
let (remaining, parsed) = result.unwrap();
|
|
@@ -410,16 +430,16 @@ mod tests {
|
|
|
410
430
|
parsed,
|
|
411
431
|
vec![
|
|
412
432
|
// The whole unknown tag becomes a text element
|
|
413
|
-
CommentContent::Text("[%timestamp 12345]"
|
|
414
|
-
CommentContent::Text(" some text"
|
|
433
|
+
CommentContent::Text(Cow::from("[%timestamp 12345]")),
|
|
434
|
+
CommentContent::Text(Cow::from(" some text")) // Note: leading space is part of the text
|
|
415
435
|
]
|
|
416
436
|
);
|
|
417
|
-
assert_eq!(remaining, "");
|
|
437
|
+
assert_eq!(remaining, b"");
|
|
418
438
|
}
|
|
419
439
|
|
|
420
440
|
#[test]
|
|
421
441
|
fn test_clk_tag_with_newline_spacing() {
|
|
422
|
-
let input = "[%clk\n0:09:36.2]";
|
|
442
|
+
let input = b"[%clk\n0:09:36.2]";
|
|
423
443
|
let result = parse_comments(input);
|
|
424
444
|
assert!(result.is_ok());
|
|
425
445
|
let (remaining, parsed) = result.unwrap();
|
|
@@ -431,29 +451,29 @@ mod tests {
|
|
|
431
451
|
seconds: 36.2
|
|
432
452
|
})]
|
|
433
453
|
);
|
|
434
|
-
assert_eq!(remaining, "");
|
|
454
|
+
assert_eq!(remaining, b"");
|
|
435
455
|
}
|
|
436
456
|
|
|
437
457
|
#[test]
|
|
438
458
|
fn test_eval_tag_with_newline_spacing() {
|
|
439
|
-
let input = "[%eval\n-0.5]";
|
|
459
|
+
let input = b"[%eval\n-0.5]";
|
|
440
460
|
let result = parse_comments(input);
|
|
441
461
|
assert!(result.is_ok());
|
|
442
462
|
let (remaining, parsed) = result.unwrap();
|
|
443
463
|
assert_eq!(parsed, vec![CommentContent::Tag(ParsedTag::Eval(-0.5))]);
|
|
444
|
-
assert_eq!(remaining, "");
|
|
464
|
+
assert_eq!(remaining, b"");
|
|
445
465
|
}
|
|
446
466
|
|
|
447
467
|
#[test]
|
|
448
468
|
fn test_text_containing_brackets_not_tag() {
|
|
449
|
-
let input = "Text with [normal brackets] and then [%clk 1:2:3]";
|
|
469
|
+
let input = b"Text with [normal brackets] and then [%clk 1:2:3]";
|
|
450
470
|
let result = parse_comments(input);
|
|
451
471
|
assert!(result.is_ok());
|
|
452
472
|
let (remaining, parsed) = result.unwrap();
|
|
453
473
|
assert_eq!(
|
|
454
474
|
parsed,
|
|
455
475
|
vec![
|
|
456
|
-
CommentContent::Text("Text with [normal brackets] and then "
|
|
476
|
+
CommentContent::Text(Cow::from("Text with [normal brackets] and then ")),
|
|
457
477
|
CommentContent::Tag(ParsedTag::ClkTime {
|
|
458
478
|
hours: 1,
|
|
459
479
|
minutes: 2,
|
|
@@ -461,6 +481,6 @@ mod tests {
|
|
|
461
481
|
})
|
|
462
482
|
]
|
|
463
483
|
);
|
|
464
|
-
assert_eq!(remaining, "");
|
|
484
|
+
assert_eq!(remaining, b"");
|
|
465
485
|
}
|
|
466
486
|
}
|
{rust_pgn_reader_python_binding-3.0.0 → rust_pgn_reader_python_binding-3.2.0}/src/example_str.py
RENAMED
|
@@ -24,6 +24,7 @@ print(extractor.valid_moves)
|
|
|
24
24
|
print(extractor.evals)
|
|
25
25
|
print(extractor.clock_times)
|
|
26
26
|
print(extractor.outcome)
|
|
27
|
+
assert extractor.position_status is not None
|
|
27
28
|
print(extractor.position_status.is_checkmate)
|
|
28
29
|
print(extractor.position_status.is_stalemate)
|
|
29
30
|
print(extractor.position_status.is_game_over)
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
use crate::comment_parsing::{parse_comments, CommentContent, ParsedTag};
|
|
2
2
|
use arrow_array::{Array, LargeStringArray, StringArray};
|
|
3
|
-
use pgn_reader::{
|
|
3
|
+
use pgn_reader::{KnownOutcome, Outcome, RawComment, RawTag, Reader, SanPlus, Skip, Visitor};
|
|
4
4
|
use pyo3::prelude::*;
|
|
5
5
|
use pyo3_arrow::PyChunkedArray;
|
|
6
6
|
use rayon::prelude::*;
|
|
7
7
|
use rayon::ThreadPoolBuilder;
|
|
8
8
|
use shakmaty::Color;
|
|
9
|
-
use shakmaty::{uci::UciMove, Chess,
|
|
9
|
+
use shakmaty::{uci::UciMove, Chess, Position, Role, Square};
|
|
10
10
|
use std::io::Cursor;
|
|
11
|
+
use std::ops::ControlFlow;
|
|
11
12
|
|
|
12
13
|
mod comment_parsing;
|
|
13
14
|
|
|
@@ -17,13 +18,13 @@ mod comment_parsing;
|
|
|
17
18
|
pub struct PyUciMove {
|
|
18
19
|
pub from_square: u8,
|
|
19
20
|
pub to_square: u8,
|
|
20
|
-
pub promotion: Option<
|
|
21
|
+
pub promotion: Option<u8>,
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
#[pymethods]
|
|
24
25
|
impl PyUciMove {
|
|
25
26
|
#[new]
|
|
26
|
-
fn new(from_square: u8, to_square: u8, promotion: Option<
|
|
27
|
+
fn new(from_square: u8, to_square: u8, promotion: Option<u8>) -> Self {
|
|
27
28
|
PyUciMove {
|
|
28
29
|
from_square,
|
|
29
30
|
to_square,
|
|
@@ -41,24 +42,42 @@ impl PyUciMove {
|
|
|
41
42
|
Square::new(self.to_square as u32).to_string()
|
|
42
43
|
}
|
|
43
44
|
|
|
45
|
+
#[getter]
|
|
46
|
+
fn get_promotion_name(&self) -> Option<String> {
|
|
47
|
+
self.promotion.and_then(|p_u8| {
|
|
48
|
+
Role::try_from(p_u8)
|
|
49
|
+
.map(|role| format!("{:?}", role)) // Get the debug representation (e.g., "Queen")
|
|
50
|
+
.ok()
|
|
51
|
+
})
|
|
52
|
+
}
|
|
53
|
+
|
|
44
54
|
// __str__ method for Python representation
|
|
45
55
|
fn __str__(&self) -> String {
|
|
46
|
-
let
|
|
56
|
+
let promo_str = self.promotion.map_or("".to_string(), |p_u8| {
|
|
57
|
+
Role::try_from(p_u8)
|
|
58
|
+
.map(|role| role.char().to_string())
|
|
59
|
+
.unwrap_or_else(|_| "".to_string()) // Handle potential error if u8 is not a valid Role
|
|
60
|
+
});
|
|
47
61
|
format!(
|
|
48
62
|
"{}{}{}",
|
|
49
63
|
Square::new(self.from_square as u32),
|
|
50
64
|
Square::new(self.to_square as u32),
|
|
51
|
-
|
|
65
|
+
promo_str
|
|
52
66
|
)
|
|
53
67
|
}
|
|
54
68
|
|
|
55
69
|
// __repr__ for a more developer-friendly representation
|
|
56
70
|
fn __repr__(&self) -> String {
|
|
71
|
+
let promo_repr = self.promotion.map_or("None".to_string(), |p_u8| {
|
|
72
|
+
Role::try_from(p_u8)
|
|
73
|
+
.map(|role| format!("Some('{}')", role.char()))
|
|
74
|
+
.unwrap_or_else(|_| format!("Some(InvalidRole({}))", p_u8))
|
|
75
|
+
});
|
|
57
76
|
format!(
|
|
58
|
-
"PyUciMove(from_square={}, to_square={}, promotion={
|
|
77
|
+
"PyUciMove(from_square={}, to_square={}, promotion={})",
|
|
59
78
|
Square::new(self.from_square as u32),
|
|
60
79
|
Square::new(self.to_square as u32),
|
|
61
|
-
|
|
80
|
+
promo_repr
|
|
62
81
|
)
|
|
63
82
|
}
|
|
64
83
|
}
|
|
@@ -176,15 +195,29 @@ impl MoveExtractor {
|
|
|
176
195
|
}
|
|
177
196
|
|
|
178
197
|
impl Visitor for MoveExtractor {
|
|
179
|
-
type
|
|
198
|
+
type Tags = Vec<(String, String)>;
|
|
199
|
+
type Movetext = ();
|
|
200
|
+
type Output = bool;
|
|
180
201
|
|
|
181
|
-
fn
|
|
202
|
+
fn begin_tags(&mut self) -> ControlFlow<Self::Output, Self::Tags> {
|
|
203
|
+
self.headers.clear();
|
|
204
|
+
ControlFlow::Continue(Vec::with_capacity(10))
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
fn tag(
|
|
208
|
+
&mut self,
|
|
209
|
+
tags: &mut Self::Tags,
|
|
210
|
+
key: &[u8],
|
|
211
|
+
value: RawTag<'_>,
|
|
212
|
+
) -> ControlFlow<Self::Output> {
|
|
182
213
|
let key_str = String::from_utf8_lossy(key).into_owned();
|
|
183
214
|
let value_str = String::from_utf8_lossy(value.as_bytes()).into_owned();
|
|
184
|
-
|
|
215
|
+
tags.push((key_str, value_str));
|
|
216
|
+
ControlFlow::Continue(())
|
|
185
217
|
}
|
|
186
218
|
|
|
187
|
-
fn
|
|
219
|
+
fn begin_movetext(&mut self, tags: Self::Tags) -> ControlFlow<Self::Output, Self::Movetext> {
|
|
220
|
+
self.headers = tags;
|
|
188
221
|
self.moves.clear();
|
|
189
222
|
self.pos = Chess::default();
|
|
190
223
|
self.valid_moves = true;
|
|
@@ -193,14 +226,19 @@ impl Visitor for MoveExtractor {
|
|
|
193
226
|
self.clock_times.clear();
|
|
194
227
|
|
|
195
228
|
self.push_castling_bitboards();
|
|
229
|
+
ControlFlow::Continue(())
|
|
196
230
|
}
|
|
197
231
|
|
|
198
|
-
fn san(
|
|
232
|
+
fn san(
|
|
233
|
+
&mut self,
|
|
234
|
+
_movetext: &mut Self::Movetext,
|
|
235
|
+
san_plus: SanPlus,
|
|
236
|
+
) -> ControlFlow<Self::Output> {
|
|
199
237
|
if self.valid_moves {
|
|
200
238
|
match san_plus.san.to_move(&self.pos) {
|
|
201
239
|
Ok(m) => {
|
|
202
|
-
self.pos.play_unchecked(
|
|
203
|
-
let uci_move_obj = UciMove::from_standard(
|
|
240
|
+
self.pos.play_unchecked(m);
|
|
241
|
+
let uci_move_obj = UciMove::from_standard(m);
|
|
204
242
|
|
|
205
243
|
match uci_move_obj {
|
|
206
244
|
UciMove::Normal {
|
|
@@ -211,7 +249,7 @@ impl Visitor for MoveExtractor {
|
|
|
211
249
|
let py_uci_move = PyUciMove {
|
|
212
250
|
from_square: from as u8,
|
|
213
251
|
to_square: to as u8,
|
|
214
|
-
promotion: promo_opt.map(|
|
|
252
|
+
promotion: promo_opt.map(|p_role| p_role as u8),
|
|
215
253
|
};
|
|
216
254
|
self.moves.push(py_uci_move);
|
|
217
255
|
self.push_castling_bitboards();
|
|
@@ -231,15 +269,19 @@ impl Visitor for MoveExtractor {
|
|
|
231
269
|
}
|
|
232
270
|
}
|
|
233
271
|
}
|
|
272
|
+
ControlFlow::Continue(())
|
|
234
273
|
}
|
|
235
274
|
|
|
236
|
-
fn comment(
|
|
237
|
-
|
|
238
|
-
|
|
275
|
+
fn comment(
|
|
276
|
+
&mut self,
|
|
277
|
+
_movetext: &mut Self::Movetext,
|
|
278
|
+
_comment: RawComment<'_>,
|
|
279
|
+
) -> ControlFlow<Self::Output> {
|
|
280
|
+
match parse_comments(_comment.as_bytes()) {
|
|
239
281
|
Ok((remaining_input, parsed_comments)) => {
|
|
240
282
|
if !remaining_input.is_empty() {
|
|
241
283
|
eprintln!("Unparsed remaining input: {:?}", remaining_input);
|
|
242
|
-
return;
|
|
284
|
+
return ControlFlow::Continue(());
|
|
243
285
|
}
|
|
244
286
|
let mut eval_encountered = false;
|
|
245
287
|
let mut clk_time_encountered = false;
|
|
@@ -300,28 +342,40 @@ impl Visitor for MoveExtractor {
|
|
|
300
342
|
eprintln!("Error parsing comment: {:?}", e);
|
|
301
343
|
}
|
|
302
344
|
}
|
|
345
|
+
ControlFlow::Continue(())
|
|
303
346
|
}
|
|
304
347
|
|
|
305
|
-
fn begin_variation(
|
|
306
|
-
|
|
348
|
+
fn begin_variation(
|
|
349
|
+
&mut self,
|
|
350
|
+
_movetext: &mut Self::Movetext,
|
|
351
|
+
) -> ControlFlow<Self::Output, Skip> {
|
|
352
|
+
ControlFlow::Continue(Skip(true)) // stay in the mainline
|
|
307
353
|
}
|
|
308
354
|
|
|
309
|
-
fn outcome(
|
|
310
|
-
self
|
|
311
|
-
|
|
312
|
-
|
|
355
|
+
fn outcome(
|
|
356
|
+
&mut self,
|
|
357
|
+
_movetext: &mut Self::Movetext,
|
|
358
|
+
_outcome: Outcome,
|
|
359
|
+
) -> ControlFlow<Self::Output> {
|
|
360
|
+
self.outcome = Some(match _outcome {
|
|
361
|
+
Outcome::Known(known) => match known {
|
|
362
|
+
KnownOutcome::Decisive { winner } => format!("{:?}", winner),
|
|
363
|
+
KnownOutcome::Draw => "Draw".to_string(),
|
|
364
|
+
},
|
|
365
|
+
Outcome::Unknown => "Unknown".to_string(),
|
|
313
366
|
});
|
|
314
367
|
self.update_position_status();
|
|
368
|
+
ControlFlow::Continue(())
|
|
315
369
|
}
|
|
316
370
|
|
|
317
|
-
fn end_game(&mut self) -> Self::
|
|
371
|
+
fn end_game(&mut self, _movetext: Self::Movetext) -> Self::Output {
|
|
318
372
|
self.valid_moves
|
|
319
373
|
}
|
|
320
374
|
}
|
|
321
375
|
|
|
322
376
|
// --- Native Rust versions (no PyResult) ---
|
|
323
377
|
pub fn parse_single_game_native(pgn: &str) -> Result<MoveExtractor, String> {
|
|
324
|
-
let mut reader =
|
|
378
|
+
let mut reader = Reader::new(Cursor::new(pgn));
|
|
325
379
|
let mut extractor = MoveExtractor::new();
|
|
326
380
|
match reader.read_game(&mut extractor) {
|
|
327
381
|
Ok(Some(_)) => Ok(extractor),
|
|
@@ -334,7 +388,7 @@ pub fn parse_multiple_games_native(
|
|
|
334
388
|
pgns: &Vec<String>,
|
|
335
389
|
num_threads: Option<usize>,
|
|
336
390
|
) -> Result<Vec<MoveExtractor>, String> {
|
|
337
|
-
let num_threads = num_threads.unwrap_or_else(
|
|
391
|
+
let num_threads = num_threads.unwrap_or_else(num_cpus::get);
|
|
338
392
|
|
|
339
393
|
// Build a custom Rayon thread pool with the desired number of threads
|
|
340
394
|
let thread_pool = ThreadPoolBuilder::new()
|
|
@@ -363,7 +417,7 @@ fn _parse_game_moves_from_arrow_chunks_native(
|
|
|
363
417
|
pgn_chunked_array: &PyChunkedArray,
|
|
364
418
|
num_threads: Option<usize>,
|
|
365
419
|
) -> Result<Vec<MoveExtractor>, String> {
|
|
366
|
-
let num_threads = num_threads.unwrap_or_else(
|
|
420
|
+
let num_threads = num_threads.unwrap_or_else(num_cpus::get);
|
|
367
421
|
let thread_pool = ThreadPoolBuilder::new()
|
|
368
422
|
.num_threads(num_threads)
|
|
369
423
|
.build()
|
|
@@ -409,15 +463,14 @@ fn _parse_game_moves_from_arrow_chunks_native(
|
|
|
409
463
|
#[pyfunction]
|
|
410
464
|
/// Parses a single PGN game string.
|
|
411
465
|
fn parse_game(pgn: &str) -> PyResult<MoveExtractor> {
|
|
412
|
-
parse_single_game_native(pgn).map_err(
|
|
466
|
+
parse_single_game_native(pgn).map_err(pyo3::exceptions::PyValueError::new_err)
|
|
413
467
|
}
|
|
414
468
|
|
|
415
469
|
/// In parallel, parse a set of games
|
|
416
470
|
#[pyfunction]
|
|
417
471
|
#[pyo3(signature = (pgns, num_threads=None))]
|
|
418
472
|
fn parse_games(pgns: Vec<String>, num_threads: Option<usize>) -> PyResult<Vec<MoveExtractor>> {
|
|
419
|
-
parse_multiple_games_native(&pgns, num_threads)
|
|
420
|
-
.map_err(|err| pyo3::exceptions::PyValueError::new_err(err))
|
|
473
|
+
parse_multiple_games_native(&pgns, num_threads).map_err(pyo3::exceptions::PyValueError::new_err)
|
|
421
474
|
}
|
|
422
475
|
|
|
423
476
|
#[pyfunction]
|
|
@@ -427,7 +480,7 @@ fn parse_game_moves_arrow_chunked_array(
|
|
|
427
480
|
num_threads: Option<usize>,
|
|
428
481
|
) -> PyResult<Vec<MoveExtractor>> {
|
|
429
482
|
_parse_game_moves_from_arrow_chunks_native(&pgn_chunked_array, num_threads)
|
|
430
|
-
.map_err(
|
|
483
|
+
.map_err(pyo3::exceptions::PyValueError::new_err)
|
|
431
484
|
}
|
|
432
485
|
|
|
433
486
|
/// Parser for chess PGN notation
|
|
@@ -441,3 +494,74 @@ fn rust_pgn_reader_python_binding(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
|
|
441
494
|
m.add_class::<PyUciMove>()?;
|
|
442
495
|
Ok(())
|
|
443
496
|
}
|
|
497
|
+
|
|
498
|
+
#[cfg(test)]
|
|
499
|
+
mod pyucimove_tests {
|
|
500
|
+
use super::*;
|
|
501
|
+
use shakmaty::{Role, Square};
|
|
502
|
+
|
|
503
|
+
#[test]
|
|
504
|
+
fn test_py_uci_move_no_promotion() {
|
|
505
|
+
let uci_move = PyUciMove::new(Square::E2 as u8, Square::E4 as u8, None);
|
|
506
|
+
assert_eq!(uci_move.from_square, Square::E2 as u8);
|
|
507
|
+
assert_eq!(uci_move.to_square, Square::E4 as u8);
|
|
508
|
+
assert_eq!(uci_move.promotion, None);
|
|
509
|
+
assert_eq!(uci_move.get_from_square_name(), "e2");
|
|
510
|
+
assert_eq!(uci_move.get_to_square_name(), "e4");
|
|
511
|
+
assert_eq!(uci_move.get_promotion_name(), None);
|
|
512
|
+
assert_eq!(uci_move.__str__(), "e2e4");
|
|
513
|
+
assert_eq!(
|
|
514
|
+
uci_move.__repr__(),
|
|
515
|
+
"PyUciMove(from_square=e2, to_square=e4, promotion=None)"
|
|
516
|
+
);
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
#[test]
|
|
520
|
+
fn test_py_uci_move_with_queen_promotion() {
|
|
521
|
+
let uci_move = PyUciMove::new(Square::E7 as u8, Square::E8 as u8, Some(Role::Queen as u8));
|
|
522
|
+
assert_eq!(uci_move.from_square, Square::E7 as u8);
|
|
523
|
+
assert_eq!(uci_move.to_square, Square::E8 as u8);
|
|
524
|
+
assert_eq!(uci_move.promotion, Some(Role::Queen as u8));
|
|
525
|
+
assert_eq!(uci_move.get_from_square_name(), "e7");
|
|
526
|
+
assert_eq!(uci_move.get_to_square_name(), "e8");
|
|
527
|
+
assert_eq!(uci_move.get_promotion_name(), Some("Queen".to_string()));
|
|
528
|
+
assert_eq!(uci_move.__str__(), "e7e8q");
|
|
529
|
+
assert_eq!(
|
|
530
|
+
uci_move.__repr__(),
|
|
531
|
+
"PyUciMove(from_square=e7, to_square=e8, promotion=Some('q'))"
|
|
532
|
+
);
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
#[test]
|
|
536
|
+
fn test_py_uci_move_with_rook_promotion() {
|
|
537
|
+
let uci_move = PyUciMove::new(Square::A7 as u8, Square::A8 as u8, Some(Role::Rook as u8));
|
|
538
|
+
assert_eq!(uci_move.from_square, Square::A7 as u8);
|
|
539
|
+
assert_eq!(uci_move.to_square, Square::A8 as u8);
|
|
540
|
+
assert_eq!(uci_move.promotion, Some(Role::Rook as u8));
|
|
541
|
+
assert_eq!(uci_move.get_from_square_name(), "a7");
|
|
542
|
+
assert_eq!(uci_move.get_to_square_name(), "a8");
|
|
543
|
+
assert_eq!(uci_move.get_promotion_name(), Some("Rook".to_string()));
|
|
544
|
+
assert_eq!(uci_move.__str__(), "a7a8r");
|
|
545
|
+
assert_eq!(
|
|
546
|
+
uci_move.__repr__(),
|
|
547
|
+
"PyUciMove(from_square=a7, to_square=a8, promotion=Some('r'))"
|
|
548
|
+
);
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
#[test]
|
|
552
|
+
fn test_py_uci_move_invalid_promotion_val() {
|
|
553
|
+
// Test with a u8 value that doesn't correspond to a valid Role
|
|
554
|
+
let uci_move = PyUciMove::new(Square::B7 as u8, Square::B8 as u8, Some(99)); // 99 is not a valid Role
|
|
555
|
+
assert_eq!(uci_move.from_square, Square::B7 as u8);
|
|
556
|
+
assert_eq!(uci_move.to_square, Square::B8 as u8);
|
|
557
|
+
assert_eq!(uci_move.promotion, Some(99));
|
|
558
|
+
assert_eq!(uci_move.get_from_square_name(), "b7");
|
|
559
|
+
assert_eq!(uci_move.get_to_square_name(), "b8");
|
|
560
|
+
assert_eq!(uci_move.get_promotion_name(), None); // Should be None as 99 is invalid
|
|
561
|
+
assert_eq!(uci_move.__str__(), "b7b8"); // Should produce no promotion char
|
|
562
|
+
assert_eq!(
|
|
563
|
+
uci_move.__repr__(),
|
|
564
|
+
"PyUciMove(from_square=b7, to_square=b8, promotion=Some(InvalidRole(99)))"
|
|
565
|
+
);
|
|
566
|
+
}
|
|
567
|
+
}
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: rust_pgn_reader_python_binding
|
|
3
|
-
Version: 3.0.0
|
|
4
|
-
Classifier: Programming Language :: Rust
|
|
5
|
-
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
6
|
-
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
7
|
-
Requires-Python: >=3.8
|
|
8
|
-
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
9
|
-
|
|
10
|
-
# rust_pgn_reader_python_binding
|
|
11
|
-
## Fast PGN parsing bindings for Python
|
|
12
|
-
This project adds Python bindings to [rust-pgn-reader](https://github.com/niklasf/rust-pgn-reader). In addition, it also parses and extracts [%clk ..] and [%eval ..] tags from comments.
|
|
13
|
-
|
|
14
|
-
## Installing
|
|
15
|
-
`pip install rust_pgn_reader_python_binding`
|
|
16
|
-
|
|
17
|
-
## Benchmarks
|
|
18
|
-
Below are some benchmarks on Lichess's 2013-07 chess games (293,459 games) on an 7800X3D.
|
|
19
|
-
|
|
20
|
-
| Parser | File format | Time |
|
|
21
|
-
|----------------------------------------------------------------------------|-------------|--------|
|
|
22
|
-
| [rust-pgn-reader](https://github.com/niklasf/rust-pgn-reader/tree/master) | PGN | 1s |
|
|
23
|
-
| rust_pgn_reader_python_binding | PGN | 4.7s |
|
|
24
|
-
| rust_pgn_reader_python_binding, parse_game (single_threaded) | parquet | 3.3s |
|
|
25
|
-
| rust_pgn_reader_python_binding, parse_games (multithreaded) | parquet | 0.5s |
|
|
26
|
-
| [chess-library](https://github.com/Disservin/chess-library) | PGN | 2s |
|
|
27
|
-
| [python-chess](https://github.com/niklasf/python-chess) | PGN | 3+ min |
|
|
28
|
-
|
|
29
|
-
The main reason for rust_pgn_reader_python_binding being slower (at least in single threaded mode) than rust-pgn-reader is because of vector / string allocations (that show up as malloc's and free's in profiling).
|
|
30
|
-
|
|
31
|
-
To replicate, download `2013-07-train-00000-of-00001.parquet` and then run `python bench_parquet_parallel.py`
|
|
32
|
-
|
|
33
|
-
## Building
|
|
34
|
-
`maturin develop`
|
|
35
|
-
`maturin develop --release`
|
|
36
|
-
|
|
37
|
-
For a more thorough tutorial, follow https://lukesalamone.github.io/posts/how-to-create-rust-python-bindings/
|
|
38
|
-
|
|
39
|
-
## Profiling
|
|
40
|
-
`py-spy record -s -F -f speedscope --output profile.speedscope -- python ./src/bench_parquet.py`
|
|
41
|
-
|
|
42
|
-
Linux/WSL-only:
|
|
43
|
-
`py-spy record -s -F -n -f speedscope --output profile.speedscope -- python ./src/bench_parquet.py`
|
|
44
|
-
|
|
45
|
-
## Testing
|
|
46
|
-
`cargo test`
|
|
47
|
-
`python -m unittest src/test.py`
|
|
File without changes
|
|
File without changes
|
{rust_pgn_reader_python_binding-3.0.0 → rust_pgn_reader_python_binding-3.2.0}/pyproject.toml
RENAMED
|
File without changes
|
|
File without changes
|
{rust_pgn_reader_python_binding-3.0.0 → rust_pgn_reader_python_binding-3.2.0}/src/bench_parquet.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|