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>
169 lines
4.9 KiB
Python
169 lines
4.9 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Tutorial Screenshot Generator
|
|
|
|
Usage:
|
|
./mcrogueface --headless --exec tests/demo/tutorial_screenshots.py
|
|
|
|
Extracts code from tutorial markdown files and generates screenshots.
|
|
"""
|
|
import mcrfpy
|
|
from mcrfpy import automation
|
|
import sys
|
|
import os
|
|
import re
|
|
|
|
# Paths
|
|
DOCS_REPO = "/opt/goblincorps/repos/mcrogueface.github.io"
|
|
TUTORIAL_DIR = os.path.join(DOCS_REPO, "tutorial")
|
|
OUTPUT_DIR = os.path.join(DOCS_REPO, "images", "tutorials")
|
|
|
|
# Tutorials to process (in order)
|
|
TUTORIALS = [
|
|
"part_01_grid_movement.md",
|
|
"part_02_tiles_collision.md",
|
|
"part_03_dungeon_generation.md",
|
|
"part_04_fov.md",
|
|
"part_05_enemies.md",
|
|
"part_06_combat.md",
|
|
"part_07_ui.md",
|
|
]
|
|
|
|
|
|
def extract_code_from_markdown(filepath):
|
|
"""Extract the main Python code block from a tutorial markdown file."""
|
|
with open(filepath, 'r') as f:
|
|
content = f.read()
|
|
|
|
# Find code blocks after "## The Complete Code" header
|
|
# Look for the first python code block after that header
|
|
complete_code_match = re.search(
|
|
r'##\s+The Complete Code.*?```python\s*\n(.*?)```',
|
|
content,
|
|
re.DOTALL | re.IGNORECASE
|
|
)
|
|
|
|
if complete_code_match:
|
|
return complete_code_match.group(1)
|
|
|
|
# Fallback: just get the first large python code block
|
|
code_blocks = re.findall(r'```python\s*\n(.*?)```', content, re.DOTALL)
|
|
if code_blocks:
|
|
# Return the largest code block (likely the main example)
|
|
return max(code_blocks, key=len)
|
|
|
|
return None
|
|
|
|
|
|
def add_screenshot_hook(code, screenshot_path):
|
|
"""Add screenshot capture code to the end of the script."""
|
|
# Add code to take screenshot after a brief delay
|
|
hook_code = f'''
|
|
|
|
# === Screenshot capture hook (added by tutorial_screenshots.py) ===
|
|
import mcrfpy
|
|
from mcrfpy import automation
|
|
import sys
|
|
|
|
_screenshot_taken = [False]
|
|
|
|
def _take_screenshot(timer, runtime):
|
|
if not _screenshot_taken[0]:
|
|
_screenshot_taken[0] = True
|
|
automation.screenshot("{screenshot_path}")
|
|
print(f"Screenshot saved: {screenshot_path}")
|
|
sys.exit(0)
|
|
|
|
# Wait a moment for scene to render, then capture
|
|
mcrfpy.Timer("_screenshot_hook", _take_screenshot, 200)
|
|
'''
|
|
return code + hook_code
|
|
|
|
|
|
class TutorialScreenshotter:
|
|
"""Manages tutorial screenshot generation."""
|
|
|
|
def __init__(self):
|
|
self.tutorials = []
|
|
self.current_index = 0
|
|
|
|
def load_tutorials(self):
|
|
"""Load and parse all tutorial files."""
|
|
for filename in TUTORIALS:
|
|
filepath = os.path.join(TUTORIAL_DIR, filename)
|
|
if not os.path.exists(filepath):
|
|
print(f"Warning: {filepath} not found, skipping")
|
|
continue
|
|
|
|
code = extract_code_from_markdown(filepath)
|
|
if code:
|
|
# Generate output filename
|
|
base = os.path.splitext(filename)[0]
|
|
screenshot_name = f"{base}.png"
|
|
self.tutorials.append({
|
|
'name': filename,
|
|
'code': code,
|
|
'screenshot': screenshot_name,
|
|
'filepath': filepath,
|
|
})
|
|
print(f"Loaded: {filename}")
|
|
else:
|
|
print(f"Warning: No code found in {filename}")
|
|
|
|
def run(self):
|
|
"""Generate all screenshots."""
|
|
# Ensure output directory exists
|
|
os.makedirs(OUTPUT_DIR, exist_ok=True)
|
|
|
|
print(f"\nGenerating {len(self.tutorials)} tutorial screenshots...")
|
|
print(f"Output directory: {OUTPUT_DIR}\n")
|
|
|
|
self.process_next()
|
|
|
|
def process_next(self):
|
|
"""Process the next tutorial."""
|
|
if self.current_index >= len(self.tutorials):
|
|
print("\nAll screenshots generated!")
|
|
sys.exit(0)
|
|
return
|
|
|
|
tutorial = self.tutorials[self.current_index]
|
|
print(f"[{self.current_index + 1}/{len(self.tutorials)}] Processing {tutorial['name']}...")
|
|
|
|
# Add screenshot hook to the code
|
|
screenshot_path = os.path.join(OUTPUT_DIR, tutorial['screenshot'])
|
|
modified_code = add_screenshot_hook(tutorial['code'], screenshot_path)
|
|
|
|
# Write to temp file and execute
|
|
temp_path = f"/tmp/tutorial_screenshot_{self.current_index}.py"
|
|
with open(temp_path, 'w') as f:
|
|
f.write(modified_code)
|
|
|
|
try:
|
|
# Execute the code
|
|
exec(compile(modified_code, temp_path, 'exec'), {'__name__': '__main__'})
|
|
except Exception as e:
|
|
print(f"Error processing {tutorial['name']}: {e}")
|
|
self.current_index += 1
|
|
self.process_next()
|
|
finally:
|
|
try:
|
|
os.unlink(temp_path)
|
|
except:
|
|
pass
|
|
|
|
|
|
def main():
|
|
"""Main entry point."""
|
|
screenshotter = TutorialScreenshotter()
|
|
screenshotter.load_tutorials()
|
|
|
|
if not screenshotter.tutorials:
|
|
print("No tutorials found to process!")
|
|
sys.exit(1)
|
|
|
|
screenshotter.run()
|
|
|
|
|
|
# Run when executed
|
|
main()
|