McRogueFace/tests/snippets/060_bsp_basic.py
John McCardle cf9512e128 feat(docs): deterministic snippet screenshots — Phase 1 (STATIC capture)
Generate a byte-stable preview PNG for every docs snippet, as the visual-
regression oracle foundation of #381. A changed image means the snippet's
behaviour changed and wants review.

Pipeline (no C++ change; existing headless build suffices):
- _seed.py: chained as the FIRST --exec so random.seed(42) governs the
  snippet's RNG before it draws (snippets draw at import; seeding after is
  too late). Shared-interpreter seeding covers all 25 random-module snippets.
- _screenshot.py: chained as the LAST --exec; steps setup frames then
  automation.screenshot(). Kept separate from _harness.py so the pass/fail
  suite stays fast and untouched.
- tools/generate_snippet_shots.py: orchestrator; runs seed->snippet->shot,
  writes gitignored snippet-shots/ (images belong in the doc-site repo,
  which pulls them from here). Per-snippet capture params live in a sidecar
  OVERRIDES table, not the mcrf: header (which would fight stamp_snippets
  and can't hold Phase-3 interaction scripts).
- make snippet-shots target; snippet-shots/ gitignored.

The 4 BSP snippets that needed deterministic pixels (060, 102, 224, 243)
now pass seed=42 — libtcod's global RNG is time(0)-seeded and unreachable
from Python, and seed= also documents reproducible generation for readers.

Validated: 272 captured, 10 skipped (noshot opt-outs), 0 failed; all 272
byte-identical across two full runs (~49s).

Addresses #381.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LTh2ZW7bd3XSd9qK86Z2CE
2026-07-15 20:57:04 -04:00

56 lines
1.6 KiB
Python

# mcrf: objects=[BSP,Caption,Color,ColorLayer,Grid,Scene] verified=0.2.8-dev status=ok
# BSP Basic - Binary space partition for dungeon generation
import mcrfpy
scene = mcrfpy.Scene("demo")
mcrfpy.current_scene = scene
grid = mcrfpy.Grid(
grid_size=(16, 12),
texture=mcrfpy.default_texture,
pos=(0, 0),
size=(1024, 768),
zoom=4.0
)
scene.children.append(grid)
# Add color layer for room visualization
room_layer = mcrfpy.ColorLayer(z_index=1, name="rooms")
grid.add_layer(room_layer)
# Fill with walls
for y in range(12):
for x in range(16):
grid.at(x, y).tilesprite = 1
grid.at(x, y).walkable = False
# Create BSP tree (pos and size as tuples)
bsp = mcrfpy.BSP(pos=(0, 0), size=(16, 12))
bsp.split_recursive(depth=3, min_size=(3, 3), seed=42)
# Room colors
colors = [
mcrfpy.Color(255, 150, 150, 150),
mcrfpy.Color(150, 255, 150, 150),
mcrfpy.Color(150, 150, 255, 150),
mcrfpy.Color(255, 255, 150, 150),
mcrfpy.Color(255, 150, 255, 150),
mcrfpy.Color(150, 255, 255, 150),
]
# Carve rooms from leaves
for i, leaf in enumerate(bsp):
x, y = leaf.pos
w, h = leaf.size
# Shrink room slightly
for ry in range(y + 1, y + h - 1):
for rx in range(x + 1, x + w - 1):
if 0 <= rx < 16 and 0 <= ry < 12:
grid.at(rx, ry).tilesprite = 48
grid.at(rx, ry).walkable = True
room_layer.set((rx, ry), colors[i % len(colors)])
status = mcrfpy.Caption(text="BSP dungeon - each color is a leaf node", pos=(320, 720))
status.outline = 2
status.outline_color = mcrfpy.Color(0, 0, 0)
scene.children.append(status)