/machines/1RB---_1LC1RE_1LD0LC_1LE1LC_1RF0RE_1RA0RB/phaseview.py · raw
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 | """Phase view for 1RB---_1LC1RE_1LD0LC_1LE1LC_1RF0RE_1RA0RB (machine #14, observe stage). Rows come from tools/diagram.py (rows_events: records / every-K / state rows). Each cell is coloured by the period-5 pattern it sits in: dark form (11000, written by C sweeping left) or light form (11110, written by B sweeping right), hue = dark-equivalent phase (0-4), so a wall (phase step) is a hue boundary. Period-4 right region (0001 / 1111) is grey. Anything else: black 0 / white 1. Head = red. usage: python3 phaseview.py OUT.png (records[:L|R] | every:K | state:X) STEPS [--from @cfg --pos P --step S] [--frame left|abs] [--max-rows N] [--scale K] [--solid 1] [--head 0] --solid 1: one flat colour per phase (dark and light forms alike), period-4 zone flat grey. This hides the half-converted blocks of the moment, so sampled rows (every:K) show only where the walls are. --head 0: do not draw the head. """ import re import sys sys.path.insert(0, "[local working file]") import diagram as dg # noqa: E402 import sim # noqa: E402 TM = "1RB---_1LC1RE_1LD0LC_1LE1LC_1RF0RE_1RA0RB" HUES = [(230, 60, 60), (240, 170, 30), (60, 170, 70), (40, 120, 230), (160, 70, 200)] def classify(cells: bytes, lo: int): """Per cell: (kind, phase) with kind 'D' dark, 'L' light, '4' period-4, None unclassified.""" t = "".join("1" if c else "0" for c in cells) n = len(t) out = [None] * n for kind, word in (("D", "11000"), ("L", "11110"), ("4", "0001"), ("4", "1111")): P = len(word) for ph in range(P): tile = "".join(word[(i + lo - ph) % P] for i in range(n)) eq = "".join("1" if a == b else "0" for a, b in zip(t, tile)) for mt in re.finditer(r"1{10,}", eq): for i in range(mt.start(), mt.end()): if out[i] is None: dph = ph if kind == "D" else (ph - 2) % 5 if kind == "L" else 0 out[i] = (kind, dph) return out SOLID = False def colour(cls, sym): if SOLID and cls is not None: kind, ph = cls return (128, 128, 128) if kind == "4" else HUES[ph] if cls is None: return (255, 255, 255) if sym else (0, 0, 0) kind, ph = cls if kind == "4": return (150, 150, 150) if sym else (90, 90, 90) r, g, b = HUES[ph] if kind == "D": return (r * 55 // 100, g * 55 // 100, b * 55 // 100) return (min(255, r + 60), min(255, g + 60), min(255, b + 60)) def main(argv): out, mode, steps = argv[0], argv[1], int(float(argv[2])) opts = dict(zip(argv[3::2], argv[4::2])) global SOLID SOLID = opts.get("--solid", "0") == "1" draw_head = opts.get("--head", "1") == "1" cfg = None if "--from" in opts: src = opts["--from"] text = open(src[1:]).read() if src.startswith("@") else src cfg = sim.Config.parse(text, pos=int(opts.get("--pos", 0)), step=int(opts.get("--step", 0))) kw = {} if mode.startswith("records"): kw["records"] = mode.split(":")[1] if ":" in mode else "LR" elif mode.startswith("every:"): kw["every"] = int(float(mode.split(":")[1])) elif mode.startswith("state:"): kw["state"] = mode.split(":", 1)[1] rows, info = dg.rows_events(TM, cfg, steps, max_rows=int(opts.get("--max-rows", 5000)), **kw) frame = opts.get("--frame", "abs") lo = min(r.lo for r in rows) hi = max(r.hi for r in rows) if frame == "left": width = max(r.hi - r.lo for r in rows) + 1 else: width = hi - lo + 1 data = bytearray() for r in rows: cls = classify(r.cells, r.clo) line = bytearray(b"\x10\x10\x10" * width) base = r.lo if frame == "left" else lo for i, c in enumerate(r.cells): x = r.clo + i - base if 0 <= x < width: line[3 * x:3 * x + 3] = bytes(colour(cls[i], c)) x = r.pos - base if draw_head and 0 <= x < width: line[3 * x:3 * x + 3] = b"\xff\x00\x00" data += line k = int(opts.get("--scale", 1)) h = len(rows) if k > 1: big = bytearray() for y in range(h): row = data[3 * width * y:3 * width * (y + 1)] wide = bytearray() for x in range(width): wide += row[3 * x:3 * x + 3] * k big += wide * k data, width, h = big, width * k, h * k open(out, "wb").write(dg.encode_png(width, h, bytes(data), 3)) print({"out": out, "rows": len(rows), "width": width, "lo": lo, "hi": hi, "frame": frame, "first_step": rows[0].step, "last_step": rows[-1].step}) if __name__ == "__main__": main(sys.argv[1:]) |