# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## What this is

A standalone, dependency-light Python prototype for a turn-based text RPG dungeon-crawler: generate rooms populated with random mobs, fight through them, quit or die. No relationship to the other Godot projects in `eliron-worlds` (`eliron-world-dungeons`, `POC/elirons_knights`) is documented — despite the mob roster below sharing race names with the `eliron-world-dungeons` GDD, treat that as coincidental/unverified, not an established asset or code link.

## Commands

- Run the game: `python app.py` (must run from this directory — save/load uses relative paths like `<name>.json` in the CWD)
- Run tests: `python -m unittest test_character_builder.py` (or `python test_character_builder.py`) — only `character_builder.py` has tests
- No `requirements.txt`/`pyproject.toml`. Only external dependency is `pydantic`, and it's optional — see below.

## Architecture

- **`base_mob.py`** — `BaseMob`, the single entity class for both players and enemies (`mob_type`: `FRIEND`/`NEUTRAL`/`ENEMY`). `change_pv()` applies `protection` as damage reduction on negative deltas; `perform_attack()` loops `speed` times per attack call.
- **`mobs_utils.py`** — a fixed roster of 3 hardcoded `BaseMob` instances (`Yanaar`, `Ulnarain`, `Skroll`), `generate_random_mob_list()` (picks a random subset), and `generate_random_rooms()` (distributes mobs into rooms — **mutates the input list via `pop(0)`**, so it consumes `mobs_in_level` as a side effect rather than just reading it).
- **`level_one.py`** — `Level`: owns `player`, generates `mobs_in_level`/`rooms` at construction, tracks `current_room_index`, and exposes `combat()` (turn order decided by `speed`). Its own `if __name__ == "__main__":` block duplicates a second, slightly different interactive game loop from `app.py` (this one skips the flee/combat submenu and always fights) — **don't assume one is canonical over the other**; check both if you change the room/combat flow.
- **`app.py`** — the actual entrypoint, with its own `action_menu()`/game loop (separate from `level_one.py`'s), plus a submenu per mob (`2` = fight, `3` = flee).
- **`menu.py`** — entirely commented-out dead code, not imported anywhere. Both `app.py` and `level_one.py` define their own local `action_menu()` instead.
- **`character_builder.py`** — `CharacterBuilder` is a thin dispatcher: `save_character_impl`/`load_character_impl` class attributes are wired at import time to either the module-level `save_character`/`load_character` (plain JSON) or, if `ELIRON_BUILDER=pydantic` is set **and** `pydantic` is actually importable (soft-detected via `importlib.util.find_spec`, no hard dependency), to `pydantic_character_builder.py`'s equivalents. Adding a third backend means implementing matching `save_character(character) -> str` / `load_character(name) -> BaseMob | None` functions and adding a branch to this wiring block.
- **`pydantic_character_builder.py`** — same persistence contract as above, implemented with a pydantic `CharacterModel` instead of manual `json.dump`/`json.load`.
- **`Hector.json` / `Jamin.json`** — pre-made saved characters checked into the repo root (loaded by name via `CharacterBuilder.load_character`); `test_character_builder.py` creates/removes its own `Arthur.json` fixture and doesn't touch these.
