"""Inspect snapshots from the real MASM reel renderer, not a Python simulation.""" import hashlib import struct from pathlib import Path from PIL import Image, ImageChops ROOT = Path(__file__).resolve().parents[2] OUTPUT = ROOT / "build/actions" NAMES = ["slots-ready", "slots-600", "slots-1799", "slots-1800", "slots-2400", "slots-win", "slots-paytable", "slots-exit"] def main(): packs = ROOT / "assets/animations/slots" raw = (packs / "cabinet-v1.bin").read_bytes() assert struct.unpack_from("<4s4I", raw) == (b"ANM1", 640, 480, 1, 640 * 480 * 4) cabinet = Image.frombytes("RGBA", (640, 480), raw[20:], "raw", "BGRA").convert("RGB") assert set(raw[23::4]) == {255}, "Fast cabinet copy requires fully opaque pixels" symbols = (packs / "symbols-v1.bin").read_bytes() assert struct.unpack_from("<4s4I", symbols) == (b"ANM1", 64, 64, 8, 64 * 64 * 4 * 8) assert len(symbols) == 20 + 64 * 64 * 4 * 8 assert set(symbols[23::4]) == {0, 255} stride = 64 * 64 * 4 assert len({hashlib.sha256(symbols[20 + index * stride:20 + (index + 1) * stride]).digest() for index in range(8)}) == 8 frames = {} for name in NAMES: frame = Image.open(OUTPUT / (name + ".bmp")).convert("RGB") assert frame.size == (640, 480) assert len(frame.getcolors(640 * 480)) > 2000, name frames[name] = frame frame.save(OUTPUT / (name + ".png")) assert len({hashlib.sha256(frame.tobytes()).digest() for frame in frames.values()}) == len(NAMES) # No symbol may cover the reel separators, chrome frame or marquee. for name in NAMES: if name == "slots-paytable": continue difference = ImageChops.difference(frames[name], cabinet) for y in range(100, 272): if 188 <= y < 194: continue # Payline markers and win illumination are intentional. for x in range(170, 477): in_reel = 124 <= y < 260 and any(left <= x < left + 88 for left in (180, 278, 376)) if not in_reel: assert difference.getpixel((x, y)) == (0, 0, 0), (name, x, y) first = (180, 124, 268, 260) second = (278, 124, 366, 260) third = (376, 124, 464, 260) assert frames["slots-1800"].crop(first).tobytes() == frames["slots-2400"].crop(first).tobytes() # The gold winning line is the only permitted difference on a stopped drum. for rect in (first, second): left, top, right, bottom = rect for box in ((left, top, right, 188), (left, 194, right, bottom)): assert frames["slots-2400"].crop(box).tobytes() == frames["slots-win"].crop(box).tobytes() assert frames["slots-2400"].crop(third).tobytes() != frames["slots-win"].crop(third).tobytes() contact = Image.new("RGB", (640 * 4, 480 * 2)) for index, name in enumerate(NAMES): contact.paste(frames[name], ((index % 4) * 640, (index // 4) * 480)) contact.save(OUTPUT / "slots-contact.png") print("PASS: 8 distinct native slot frames; pixel-accurate reel clipping; sequential stops; opaque cabinet and 8 real symbol sprites") if __name__ == "__main__": main()