McRogueFace/tests/demo/procgen_showcase.py
Frick a1b692bb1f Add cookbook and tutorial showcase demos
tests/demo/:
- cookbook_showcase.py: Interactive demo of cookbook recipes
- tutorial_showcase.py: Visual walkthrough of tutorial content
- tutorial_screenshots.py: Automated screenshot generation
- new_features_showcase.py: Demo of modern API features
- procgen_showcase.py: Procedural generation examples
- simple_showcase.py: Minimal working examples

Created during docs modernization to verify cookbook examples work.

🤖 Generated with Claude Code (https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-15 04:06:24 +00:00

286 lines
8.8 KiB
Python

#!/usr/bin/env python3
"""Generate screenshots for procgen cookbook recipes.
Uses Frame-based visualization since Grid cell colors use ColorLayer API.
"""
import mcrfpy
from mcrfpy import automation
import sys
OUTPUT_DIR = "/opt/goblincorps/repos/mcrogueface.github.io/images/cookbook"
# Simple PRNG
_seed = 42
def random():
global _seed
_seed = (_seed * 1103515245 + 12345) & 0x7fffffff
return (_seed >> 16) / 32768.0
def seed(n):
global _seed
_seed = n
def choice(lst):
return lst[int(random() * len(lst))]
def screenshot_cellular_caves():
"""Generate cellular automata caves visualization."""
print("Generating cellular automata caves...")
scene = mcrfpy.Scene("caves")
scene.activate()
mcrfpy.step(0.1)
# Background
bg = mcrfpy.Frame(pos=(0, 0), size=(640, 500))
bg.fill_color = mcrfpy.Color(15, 15, 25)
scene.children.append(bg)
width, height = 50, 35
cell_size = 12
seed(42)
# Store cell data
cells = [[False for _ in range(width)] for _ in range(height)]
# Step 1: Random noise (45% walls)
for y in range(height):
for x in range(width):
if x == 0 or x == width-1 or y == 0 or y == height-1:
cells[y][x] = True # Border walls
else:
cells[y][x] = random() < 0.45
# Step 2: Smooth with cellular automata (5 iterations)
for _ in range(5):
new_cells = [[cells[y][x] for x in range(width)] for y in range(height)]
for y in range(1, height - 1):
for x in range(1, width - 1):
wall_count = sum(
1 for dy in [-1, 0, 1] for dx in [-1, 0, 1]
if not (dx == 0 and dy == 0) and cells[y + dy][x + dx]
)
if wall_count >= 5:
new_cells[y][x] = True
elif wall_count <= 3:
new_cells[y][x] = False
cells = new_cells
# Find largest connected region
visited = set()
regions = []
def flood_fill(start_x, start_y):
result = []
stack = [(start_x, start_y)]
while stack:
x, y = stack.pop()
if (x, y) in visited or x < 0 or x >= width or y < 0 or y >= height:
continue
if cells[y][x]: # Wall
continue
visited.add((x, y))
result.append((x, y))
stack.extend([(x+1, y), (x-1, y), (x, y+1), (x, y-1)])
return result
for y in range(height):
for x in range(width):
if (x, y) not in visited and not cells[y][x]:
region = flood_fill(x, y)
if region:
regions.append(region)
largest = max(regions, key=len) if regions else []
largest_set = set(largest)
# Draw cells as colored frames
for y in range(height):
for x in range(width):
px = 20 + x * cell_size
py = 20 + y * cell_size
cell = mcrfpy.Frame(pos=(px, py), size=(cell_size - 1, cell_size - 1))
if cells[y][x]:
cell.fill_color = mcrfpy.Color(60, 40, 30) # Wall
elif (x, y) in largest_set:
cell.fill_color = mcrfpy.Color(50, 90, 100) # Main cave
else:
cell.fill_color = mcrfpy.Color(45, 35, 30) # Filled region
scene.children.append(cell)
# Title
title = mcrfpy.Caption(text="Cellular Automata Caves", pos=(20, 445))
title.fill_color = mcrfpy.Color(200, 200, 200)
title.font_size = 18
scene.children.append(title)
subtitle = mcrfpy.Caption(text="45% fill, 5 iterations, largest region preserved", pos=(20, 468))
subtitle.fill_color = mcrfpy.Color(130, 130, 140)
subtitle.font_size = 12
scene.children.append(subtitle)
mcrfpy.step(0.1)
automation.screenshot(OUTPUT_DIR + "/procgen_cellular_caves.png")
print("Saved: procgen_cellular_caves.png")
def screenshot_wfc():
"""Generate WFC pattern visualization."""
print("Generating WFC patterns...")
scene = mcrfpy.Scene("wfc")
scene.activate()
mcrfpy.step(0.1)
# Background
bg = mcrfpy.Frame(pos=(0, 0), size=(640, 500))
bg.fill_color = mcrfpy.Color(15, 20, 15)
scene.children.append(bg)
width, height = 40, 28
cell_size = 15
seed(123)
GRASS, DIRT, WATER, SAND = 0, 1, 2, 3
colors = {
GRASS: mcrfpy.Color(60, 120, 50),
DIRT: mcrfpy.Color(100, 70, 40),
WATER: mcrfpy.Color(40, 80, 140),
SAND: mcrfpy.Color(180, 160, 90)
}
rules = {
GRASS: {'N': [GRASS, DIRT, SAND], 'S': [GRASS, DIRT, SAND],
'E': [GRASS, DIRT, SAND], 'W': [GRASS, DIRT, SAND]},
DIRT: {'N': [GRASS, DIRT], 'S': [GRASS, DIRT],
'E': [GRASS, DIRT], 'W': [GRASS, DIRT]},
WATER: {'N': [WATER, SAND], 'S': [WATER, SAND],
'E': [WATER, SAND], 'W': [WATER, SAND]},
SAND: {'N': [GRASS, WATER, SAND], 'S': [GRASS, WATER, SAND],
'E': [GRASS, WATER, SAND], 'W': [GRASS, WATER, SAND]}
}
tiles = set(rules.keys())
possibilities = {(x, y): set(tiles) for y in range(height) for x in range(width)}
result = {}
# Seed water lake
for x in range(22, 32):
for y in range(8, 18):
possibilities[(x, y)] = {WATER}
result[(x, y)] = WATER
# Seed dirt path
for y in range(10, 18):
possibilities[(3, y)] = {DIRT}
result[(3, y)] = DIRT
directions = {'N': (0, -1), 'S': (0, 1), 'E': (1, 0), 'W': (-1, 0)}
def propagate(sx, sy):
stack = [(sx, sy)]
while stack:
x, y = stack.pop()
current = possibilities[(x, y)]
for dir_name, (dx, dy) in directions.items():
nx, ny = x + dx, y + dy
if not (0 <= nx < width and 0 <= ny < height):
continue
neighbor = possibilities[(nx, ny)]
if len(neighbor) == 1:
continue
allowed = set()
for tile in current:
if dir_name in rules[tile]:
allowed.update(rules[tile][dir_name])
new_opts = neighbor & allowed
if new_opts and new_opts != neighbor:
possibilities[(nx, ny)] = new_opts
stack.append((nx, ny))
# Propagate from seeds
for x in range(22, 32):
for y in range(8, 18):
propagate(x, y)
for y in range(10, 18):
propagate(3, y)
# Collapse
for _ in range(width * height):
best, best_e = None, 1000.0
for pos, opts in possibilities.items():
if len(opts) > 1:
e = len(opts) + random() * 0.1
if e < best_e:
best_e, best = e, pos
if best is None:
break
x, y = best
opts = list(possibilities[(x, y)])
if not opts:
break
weights = {GRASS: 5, DIRT: 2, WATER: 1, SAND: 2}
weighted = []
for t in opts:
weighted.extend([t] * weights.get(t, 1))
chosen = choice(weighted) if weighted else GRASS
possibilities[(x, y)] = {chosen}
result[(x, y)] = chosen
propagate(x, y)
# Fill remaining
for y in range(height):
for x in range(width):
if (x, y) not in result:
opts = list(possibilities[(x, y)])
result[(x, y)] = choice(opts) if opts else GRASS
# Draw
for y in range(height):
for x in range(width):
px = 20 + x * cell_size
py = 20 + y * cell_size
cell = mcrfpy.Frame(pos=(px, py), size=(cell_size - 1, cell_size - 1))
cell.fill_color = colors[result[(x, y)]]
scene.children.append(cell)
# Title
title = mcrfpy.Caption(text="Wave Function Collapse", pos=(20, 445))
title.fill_color = mcrfpy.Color(200, 200, 200)
title.font_size = 18
scene.children.append(title)
subtitle = mcrfpy.Caption(text="Constraint-based terrain (seeded lake + path)", pos=(20, 468))
subtitle.fill_color = mcrfpy.Color(130, 140, 130)
subtitle.font_size = 12
scene.children.append(subtitle)
# Legend
for i, (name, tid) in enumerate([("Grass", GRASS), ("Dirt", DIRT), ("Sand", SAND), ("Water", WATER)]):
lx, ly = 480, 445 + i * 14
swatch = mcrfpy.Frame(pos=(lx, ly), size=(12, 12))
swatch.fill_color = colors[tid]
scene.children.append(swatch)
label = mcrfpy.Caption(text=name, pos=(lx + 16, ly))
label.fill_color = mcrfpy.Color(150, 150, 150)
label.font_size = 11
scene.children.append(label)
mcrfpy.step(0.1)
automation.screenshot(OUTPUT_DIR + "/procgen_wfc.png")
print("Saved: procgen_wfc.png")
if __name__ == "__main__":
screenshot_cellular_caves()
screenshot_wfc()
print("\nDone!")
sys.exit(0)