# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Build Commands ```bash # Build the project (compiles to ./build directory) make # Or use the build script directly ./build.sh # Run the game make run # Clean build artifacts make clean # The executable and all assets are in ./build/ cd build ./mcrogueface ``` ## Project Architecture McRogueFace is a C++ game engine with Python scripting support, designed for creating roguelike games. The architecture consists of: ### Core Engine (C++) - **Entry Point**: `src/main.cpp` initializes the game engine - **Scene System**: `Scene.h/cpp` manages game states - **Entity System**: `UIEntity.h/cpp` provides game objects - **Python Integration**: `McRFPy_API.h/cpp` exposes engine functionality to Python - **UI Components**: `UIFrame`, `UICaption`, `UISprite`, `UIGrid` for rendering ### Game Logic (Python) - **Main Script**: `src/scripts/game.py` contains game initialization and scene setup - **Entity System**: `src/scripts/cos_entities.py` implements game entities (Player, Enemy, Boulder, etc.) - **Level Generation**: `src/scripts/cos_level.py` uses BSP for procedural dungeon generation - **Tile System**: `src/scripts/cos_tiles.py` implements Wave Function Collapse for tile placement ### Key Python API (`mcrfpy` module) The C++ engine exposes these primary functions to Python: - Scene Management: `createScene()`, `setScene()`, `sceneUI()` - Entity Creation: `Entity()` with position and sprite properties - Grid Management: `Grid()` for tilemap rendering - Input Handling: `keypressScene()` for keyboard events - Audio: `createSoundBuffer()`, `playSound()`, `setVolume()` - Timers: `setTimer()`, `delTimer()` for event scheduling ## Development Workflow ### Running the Game After building, the executable expects: - `assets/` directory with sprites, fonts, and audio - `scripts/` directory with Python game files - Python 3.12 shared libraries in `./lib/` ### Modifying Game Logic - Game scripts are in `src/scripts/` - Main game entry is `game.py` - Entity behavior in `cos_entities.py` - Level generation in `cos_level.py` ### Adding New Features 1. C++ API additions go in `src/McRFPy_API.cpp` 2. Expose to Python using the existing binding pattern 3. Update Python scripts to use new functionality ## Testing Game Changes Currently no automated test suite. Manual testing workflow: 1. Build with `make` 2. Run `make run` or `cd build && ./mcrogueface` 3. Test specific features through gameplay 4. Check console output for Python errors ### Quick Testing Commands ```bash # Test basic functionality make test # Run in Python interactive mode make python # Test headless mode cd build ./mcrogueface --headless -c "import mcrfpy; print('Headless test')" ``` ## Common Development Tasks ### Compiling McRogueFace ```bash # Standard build (to ./build directory) make # Full rebuild make clean && make # Manual CMake build mkdir build && cd build cmake .. -DCMAKE_BUILD_TYPE=Release make -j$(nproc) # The library path issue: if linking fails, check that libraries are in __lib/ # CMakeLists.txt expects: link_directories(${CMAKE_SOURCE_DIR}/__lib) ``` ### Running and Capturing Output ```bash # Run with timeout and capture output cd build timeout 5 ./mcrogueface 2>&1 | tee output.log # Run in background and kill after delay ./mcrogueface > output.txt 2>&1 & PID=$!; sleep 3; kill $PID 2>/dev/null # Just capture first N lines (useful for crashes) ./mcrogueface 2>&1 | head -50 ``` ### Debugging with GDB ```bash # Interactive debugging gdb ./mcrogueface (gdb) run (gdb) bt # backtrace after crash # Batch mode debugging (non-interactive) gdb -batch -ex run -ex where -ex quit ./mcrogueface 2>&1 # Get just the backtrace after a crash gdb -batch -ex "run" -ex "bt" ./mcrogueface 2>&1 | head -50 # Debug with specific commands echo -e "run\nbt 5\nquit\ny" | gdb ./mcrogueface 2>&1 ``` ### Testing Different Python Scripts ```bash # The game automatically runs build/scripts/game.py on startup # To test different behavior: # Option 1: Replace game.py temporarily cd build cp scripts/my_test_script.py scripts/game.py ./mcrogueface # Option 2: Backup original and test mv scripts/game.py scripts/game.py.bak cp my_test.py scripts/game.py ./mcrogueface mv scripts/game.py.bak scripts/game.py # Option 3: For quick tests, create minimal game.py echo 'import mcrfpy; print("Test"); mcrfpy.createScene("test")' > scripts/game.py ``` ### Understanding Key Macros and Patterns #### RET_PY_INSTANCE Macro (UIDrawable.h) This macro handles converting C++ UI objects to their Python equivalents: ```cpp RET_PY_INSTANCE(target); // Expands to a switch on target->derived_type() that: // 1. Allocates the correct Python object type (Frame, Caption, Sprite, Grid) // 2. Sets the shared_ptr data member // 3. Returns the PyObject* ``` #### Collection Patterns - `UICollection` wraps `std::vector>` - `UIEntityCollection` wraps `std::list>` - Different containers require different iteration code (vector vs list) #### Python Object Creation Patterns ```cpp // Pattern 1: Using tp_alloc (most common) auto o = (PyUIFrameObject*)type->tp_alloc(type, 0); o->data = std::make_shared(); // Pattern 2: Getting type from module auto type = (PyTypeObject*)PyObject_GetAttrString(McRFPy_API::mcrf_module, "Entity"); auto o = (PyUIEntityObject*)type->tp_alloc(type, 0); // Pattern 3: Direct shared_ptr assignment iterObj->data = self->data; // Shares the C++ object ``` ### Working Directory Structure ``` build/ ├── mcrogueface # The executable ├── scripts/ │ └── game.py # Auto-loaded Python script ├── assets/ # Copied from source during build └── lib/ # Python libraries (copied from __lib/) ``` ### Quick Iteration Tips - Keep a test script ready for quick experiments - Use `timeout` to auto-kill hanging processes - The game expects a window manager; use Xvfb for headless testing - Python errors go to stderr, game output to stdout - Segfaults usually mean Python type initialization issues ## Important Notes - The project uses SFML for graphics/audio and libtcod for roguelike utilities - Python scripts are loaded at runtime from the `scripts/` directory - Asset loading expects specific paths relative to the executable - The game was created for 7DRL 2025 as "Crypt of Sokoban" - Iterator implementations require careful handling of C++/Python boundaries ## Testing Guidelines ### Test-Driven Development - **Always write tests first**: Create automation tests in `./tests/` for all bugs and new features - **Practice TDD**: Write tests that fail to demonstrate the issue, then pass after the fix is applied - **Close the loop**: Reproduce issue → change code → recompile → verify behavior change ### Two Types of Tests #### 1. Direct Execution Tests (No Game Loop) For tests that only need class initialization or direct code execution: ```python # These tests can treat McRogueFace like a Python interpreter import mcrfpy # Test code here result = mcrfpy.some_function() assert result == expected_value print("PASS" if condition else "FAIL") ``` #### 2. Game Loop Tests (Timer-Based) For tests requiring rendering, game state, or elapsed time: ```python import mcrfpy from mcrfpy import automation import sys def run_test(runtime): """Timer callback - runs after game loop starts""" # Now rendering is active, screenshots will work automation.screenshot("test_result.png") # Run your tests here automation.click(100, 100) # Always exit at the end print("PASS" if success else "FAIL") sys.exit(0) # Set up the test scene mcrfpy.createScene("test") # ... add UI elements ... # Schedule test to run after game loop starts mcrfpy.setTimer("test", run_test, 100) # 0.1 seconds ``` ### Key Testing Principles - **Timer callbacks are essential**: Screenshots and UI interactions only work after the render loop starts - **Use automation API**: Always create and examine screenshots when visual feedback is required - **Exit properly**: Call `sys.exit()` at the end of timer-based tests to prevent hanging - **Headless mode**: Use `--exec` flag for automated testing: `./mcrogueface --headless --exec tests/my_test.py` ### Example Test Pattern ```bash # Run a test that requires game loop ./build/mcrogueface --headless --exec tests/issue_78_middle_click_test.py # The test will: # 1. Set up the scene during script execution # 2. Register a timer callback # 3. Game loop starts # 4. Timer fires after 100ms # 5. Test runs with full rendering available # 6. Test takes screenshots and validates behavior # 7. Test calls sys.exit() to terminate ```