mache 0.1.0__py3-none-any.whl
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.
- mache/__init__.py +27 -0
- mache/book_slice.py +66 -0
- mache/match_estimate.py +764 -0
- mache/match_terminations.py +197 -0
- mache/rating_estimate.py +334 -0
- mache-0.1.0.dist-info/METADATA +127 -0
- mache-0.1.0.dist-info/RECORD +11 -0
- mache-0.1.0.dist-info/WHEEL +5 -0
- mache-0.1.0.dist-info/entry_points.txt +5 -0
- mache-0.1.0.dist-info/licenses/LICENSE +21 -0
- mache-0.1.0.dist-info/top_level.txt +1 -0
mache/__init__.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (C) 2022-2026 Andrew Wright
|
|
3
|
+
|
|
4
|
+
"""The tools a match is read with: the pooled estimate, the ccrl fit, the
|
|
5
|
+
terminations count and the book slice.
|
|
6
|
+
|
|
7
|
+
They are one package because they read one thing. The fit owns the two regular
|
|
8
|
+
expressions a fastchess pgn is split into games with, and the estimate reads
|
|
9
|
+
the terminations count as well as the fit.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
__version__ = "0.1.0"
|
|
13
|
+
|
|
14
|
+
# The version of the --json shape, and the one thing every --json object says
|
|
15
|
+
# about itself. It is 1 because the shape is released: a tagged version is what
|
|
16
|
+
# another repository can pin, and a shape nobody can pin is not a contract.
|
|
17
|
+
# Fields are added from here on. None is removed, and none is given a new
|
|
18
|
+
# meaning under the name it already has. A change that cannot be made that way
|
|
19
|
+
# raises this number, and a reader that finds a number it does not know should
|
|
20
|
+
# say so rather than read the object anyway.
|
|
21
|
+
JSON_FORMAT = 1
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def tool(command: str) -> dict[str, str]:
|
|
25
|
+
"""What produced a --json object, so a figure can be read back against the
|
|
26
|
+
version of the tooling that produced it."""
|
|
27
|
+
return {"name": "mache", "version": __version__, "command": command}
|
mache/book_slice.py
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (C) 2022-2026 Andrew Wright
|
|
3
|
+
|
|
4
|
+
"""Print the opening a shard of a match starts at.
|
|
5
|
+
|
|
6
|
+
The shards of a match play at the same time and are pooled afterwards, so no
|
|
7
|
+
two of them may play the same opening: a position played twice would be counted
|
|
8
|
+
twice and the games would not be the independent sample the estimate reads them
|
|
9
|
+
as. The book is taken in order rather than drawn, and each shard is given a
|
|
10
|
+
slice of its own, which is where its games begin.
|
|
11
|
+
|
|
12
|
+
The run's first shard starts at a remainder of the seed, so two runs of the same
|
|
13
|
+
size play different regions of a book far larger than either of them uses, and
|
|
14
|
+
each shard after it starts a slice further along. The starts are worked out from
|
|
15
|
+
the seed and the counts alone, so the manifest is enough to play the schedule
|
|
16
|
+
again, without depending on how fastchess draws its own openings.
|
|
17
|
+
|
|
18
|
+
The index printed is one based, which is what fastchess's `start=` takes.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
import argparse
|
|
22
|
+
import sys
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def first_opening(openings: int, wanted: int, seed: int) -> int:
|
|
26
|
+
"""Where the first shard begins. The offset leaves room for every shard, so
|
|
27
|
+
the last one still ends inside the book."""
|
|
28
|
+
room = openings - wanted
|
|
29
|
+
if room < 1:
|
|
30
|
+
return 1
|
|
31
|
+
return 1 + seed % room
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def start(openings: int, pairs: int, shards: int, shard: int, seed: int) -> int:
|
|
35
|
+
"""The opening this shard begins at, one based."""
|
|
36
|
+
return first_opening(openings, pairs * shards, seed) + shard * pairs
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def main() -> None:
|
|
40
|
+
parser = argparse.ArgumentParser(description=__doc__)
|
|
41
|
+
parser.add_argument("--openings", type=int, required=True, help="what the book has")
|
|
42
|
+
parser.add_argument("--pairs", type=int, required=True, help="openings per shard")
|
|
43
|
+
parser.add_argument("--shards", type=int, required=True, help="shards in the run")
|
|
44
|
+
parser.add_argument("--shard", type=int, required=True, help="which one, from zero")
|
|
45
|
+
parser.add_argument("--seed", type=int, required=True, help="the run's seed")
|
|
46
|
+
args = parser.parse_args()
|
|
47
|
+
|
|
48
|
+
if min(args.openings, args.pairs, args.shards) < 1 or args.seed < 0:
|
|
49
|
+
sys.exit("the counts are all at least one and the seed is not negative")
|
|
50
|
+
if not 0 <= args.shard < args.shards:
|
|
51
|
+
sys.exit(f"shard {args.shard} is not one of {args.shards}")
|
|
52
|
+
|
|
53
|
+
wanted = args.pairs * args.shards
|
|
54
|
+
if args.openings - wanted < 1:
|
|
55
|
+
# fastchess reads on around the end of the book, so the match still
|
|
56
|
+
# plays. It is the shards no longer being disjoint that is worth saying
|
|
57
|
+
print(
|
|
58
|
+
f"the book holds {args.openings} openings and the run wants {wanted},"
|
|
59
|
+
" so the shards repeat each other",
|
|
60
|
+
file=sys.stderr,
|
|
61
|
+
)
|
|
62
|
+
print(start(args.openings, args.pairs, args.shards, args.shard, args.seed))
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
if __name__ == "__main__":
|
|
66
|
+
main()
|