92 lines
2.3 KiB
Python
92 lines
2.3 KiB
Python
"""Enumerate flattened redstone delay-chains for a given total delay."""
|
|
|
|
import argparse
|
|
from pprint import pprint
|
|
import sys
|
|
from collections import Counter, defaultdict
|
|
from functools import cache
|
|
from typing import NamedTuple
|
|
|
|
|
|
class Comp(NamedTuple):
|
|
delay: int
|
|
priority: int
|
|
blocks: tuple
|
|
|
|
@property
|
|
def signature(self):
|
|
return (self.priority, -self.delay)
|
|
|
|
def __repr__(self):
|
|
return "C" + repr(self.blocks)
|
|
|
|
|
|
# Insert custom subcomponents here, e.g. Comp(6, -1, ("-", "R2", "C")).
|
|
COMPONENTS = [
|
|
Comp(8, -3, ("R4",)),
|
|
Comp(6, -3, ("R3",)),
|
|
Comp(4, -3, ("R2",)),
|
|
Comp(2, -3, ("R1",)),
|
|
Comp(8, -1, ("-", "R4")),
|
|
Comp(6, -1, ("-", "R3")),
|
|
Comp(4, -1, ("-", "R2")),
|
|
Comp(2, -1, ("C",)),
|
|
Comp(2, -1, ("-", "R1")),
|
|
Comp(2, 0, ("-", "C")),
|
|
]
|
|
|
|
assert all(c.blocks for c in COMPONENTS), "components must have >= 1 block"
|
|
ORDER = tuple(sorted(COMPONENTS, key=lambda c: (c.priority, -c.delay)))
|
|
START = tuple([c for c in ORDER if c.blocks[0] == "-"])
|
|
|
|
|
|
@cache
|
|
def _body(remaining: int, order=ORDER):
|
|
out = defaultdict(list)
|
|
|
|
for c in order:
|
|
if c.delay > remaining:
|
|
continue
|
|
if remaining == c.delay:
|
|
out[(c.signature,)].append(c.blocks)
|
|
else:
|
|
for signature, rests in _body(remaining - c.delay).items():
|
|
for rest in rests:
|
|
out[(c.signature, *signature)].append(c.blocks + rest)
|
|
|
|
return dict(out)
|
|
|
|
|
|
def chains(total: int):
|
|
return _body(total, START)
|
|
|
|
|
|
def main():
|
|
ap = argparse.ArgumentParser(description=__doc__)
|
|
ap.add_argument("total", type=int, help="total delay to enumerate")
|
|
ap.add_argument("count", nargs="*", type=int, help="total element count")
|
|
args = ap.parse_args()
|
|
|
|
spellings = chains(args.total)
|
|
|
|
counts = Counter(len(opt) for opts in spellings.values() for opt in opts)
|
|
|
|
tileset = []
|
|
for sig in sorted(spellings):
|
|
opts = spellings[sig]
|
|
if args.count:
|
|
opts = [opt for opt in opts if len(opt) in args.count]
|
|
if opts:
|
|
tileset.append(min(opts, key=len))
|
|
|
|
for chain in tileset:
|
|
print(*chain)
|
|
|
|
print(len(tileset), "chains", file=sys.stderr)
|
|
for length in sorted(counts):
|
|
print(f"length {length} chains: {counts[length]}", file=sys.stderr)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|