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 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()