pipeworks_mud_mapper.models.room ================================ .. py:module:: pipeworks_mud_mapper.models.room .. autoapi-nested-parse:: Room and coordinate models for PipeWorks MUD Mapper. This module defines the core room-related data structures used by the mapper. Two room types exist to support the two-file workflow: - **Room**: Game truth representation (no coordinates) - **MapRoom**: Authoring representation (includes coordinates) The separation reflects the principle that coordinates are "authoring scaffolding" - they help humans visualize the map but are not part of the game state. Direction System ---------------- MUDs traditionally use cardinal directions for navigation. This mapper supports six directions as decided in ``goblin_cartography.md`` Section 1.7: ============ =================== ==================== Direction Axis Typical Use ============ =================== ==================== north +Y Horizontal movement south -Y Horizontal movement east +X Horizontal movement west -X Horizontal movement up +Z Vertical (stairs, ladders) down -Z Vertical (trapdoors, basements) ============ =================== ==================== The decision to use 6 directions (not 4) allows rooms to have both a "south" exit AND a "down" exit going to different places - essential for multi-story structures like The Crooked Pipe pub. Coordinate System ----------------- The mapper uses a 3D Cartesian coordinate system:: +Y (North) │ │ -X ───────┼─────── +X (West) │ (East) │ -Y (South) Z: +Z (Up) / -Z (Down) The origin ``[0, 0, 0]`` is conventionally the zone's spawn room. .. admonition:: Examples Creating a room for game truth (Zone export):: room = Room( id="spawn", name="The Crooked Pipe", description="A low-ceilinged goblin pub...", exits={"north": "front_parlour", "down": "cellar"}, items=["ale_mug", "chalk"], ) Creating a room for authoring (MapFile):: map_room = MapRoom( id="spawn", name="The Crooked Pipe", description="A low-ceilinged goblin pub...", coords=Coords(x=0, y=0, z=0), exits={"north": "front_parlour", "down": "cellar"}, items=["ale_mug", "chalk"], ) Converting MapRoom to Room (strips coordinates):: room = map_room.to_room() .. seealso:: :py:obj:`-`, :py:obj:`-` Attributes ---------- .. autoapisummary:: pipeworks_mud_mapper.models.room.Direction pipeworks_mud_mapper.models.room.DIRECTIONS pipeworks_mud_mapper.models.room.OPPOSITE_DIRECTION pipeworks_mud_mapper.models.room.DIRECTION_OFFSETS pipeworks_mud_mapper.models.room.DIRECTION_SHORT pipeworks_mud_mapper.models.room.SHORT_TO_DIRECTION Classes ------- .. autoapisummary:: pipeworks_mud_mapper.models.room.Coords pipeworks_mud_mapper.models.room.Room pipeworks_mud_mapper.models.room.MapRoom Module Contents --------------- .. py:data:: Direction Valid MUD movement directions. Constrained to six values matching the decision in goblin_cartography.md: - Horizontal: north, south, east, west - Vertical: up, down Up/Down are semantically distinct from North/South - they represent vertical traversal (stairs, ladders, trapdoors) rather than horizontal movement on a conceptual map. .. py:data:: DIRECTIONS :type: tuple[Direction, Ellipsis] :value: ('north', 'south', 'east', 'west', 'up', 'down') All valid directions as a tuple for iteration. .. py:data:: OPPOSITE_DIRECTION :type: dict[Direction, Direction] Mapping of each direction to its opposite. Used for bidirectional exit creation - when creating an exit from A to B, the opposite direction is used for the return exit from B to A. .. py:data:: DIRECTION_OFFSETS :type: dict[Direction, tuple[int, int, int]] Coordinate offsets for each direction. These represent the unit vector for movement in each direction: - X axis: East (+1) / West (-1) - Y axis: North (+1) / South (-1) - Z axis: Up (+1) / Down (-1) Note: Actual room spacing may vary. These offsets indicate direction only, not distance. Use for determining which direction a target room lies relative to a source room. .. py:data:: DIRECTION_SHORT :type: dict[Direction, str] Short labels used in the UI for direction indicators. .. py:data:: SHORT_TO_DIRECTION :type: dict[str, Direction] Reverse mapping for checkbox values back to full direction names. .. py:class:: Coords(/, **data) Bases: :py:obj:`pydantic.BaseModel` Three-dimensional coordinates for room placement. Coordinates are authoring metadata - they help humans visualize and validate the map layout but are not used by the game engine. The MUD server operates on pure topology (room connections) not geometry. .. attribute:: x East/West position. Positive = East, Negative = West. :type: :py:class:`int` .. attribute:: y North/South position. Positive = North, Negative = South. :type: :py:class:`int` .. attribute:: z Up/Down level. Positive = Up (upper floors), Negative = Down (basements). :type: :py:class:`int` .. admonition:: Examples >>> coords = Coords(x=0, y=0, z=0) # Origin (typically spawn room) >>> coords = Coords(x=5, y=-5, z=0) # 5 units east, 5 units south >>> coords = Coords(x=0, y=0, z=-1) # One level below origin (basement) .. admonition:: Notes The coordinate system follows standard mathematical conventions: - Origin at (0, 0, 0) - Right-hand rule for axis orientation - Integers only (no fractional positions) .. py:method:: to_tuple() Convert coordinates to a tuple. :returns: Coordinates as (x, y, z) tuple. :rtype: :py:class:`tuple[int`, :py:class:`int`, :py:class:`int]` .. admonition:: Examples >>> Coords(x=1, y=2, z=3).to_tuple() (1, 2, 3) .. py:method:: to_list() Convert coordinates to a list. This format matches the JSON serialization used in map files. :returns: Coordinates as [x, y, z] list. :rtype: :py:class:`list[int]` .. admonition:: Examples >>> Coords(x=1, y=2, z=3).to_list() [1, 2, 3] .. py:method:: from_list(coords) :classmethod: Create Coords from a list. :param coords: Coordinates as [x, y, z] list. :type coords: :py:class:`list[int]` :returns: New Coords instance. :rtype: :py:class:`Coords` :raises ValueError: If coords does not have exactly 3 elements. .. admonition:: Examples >>> Coords.from_list([1, 2, 3]) Coords(x=1, y=2, z=3) .. py:method:: offset(direction) Return new coordinates offset in the given direction. :param direction: The direction to offset towards. :type direction: :py:class:`Direction` :returns: New Coords offset by one unit in the given direction. :rtype: :py:class:`Coords` .. admonition:: Examples >>> origin = Coords(x=0, y=0, z=0) >>> origin.offset("north") Coords(x=0, y=1, z=0) >>> origin.offset("down") Coords(x=0, y=0, z=-1) .. py:class:: Room(/, **data) Bases: :py:obj:`pydantic.BaseModel` A room in game truth format (no coordinates). This model represents a room as it appears in exported zone files - the format consumed by the MUD server. Coordinates are excluded because the game engine operates on topology (connections) not geometry (positions). .. attribute:: id Unique identifier within the zone. Used as dictionary key and in exit references. Should be lowercase with underscores (e.g., "front_parlour"). :type: :py:class:`str` .. attribute:: name Human-readable display name shown to players (e.g., "Front Parlour"). :type: :py:class:`str` .. attribute:: description Room description shown when a player enters or looks. Can be multiple sentences. Defaults to empty string. :type: :py:class:`str` .. attribute:: exits Mapping of direction to target room ID. Target can be same-zone (e.g., "front_parlour") or cross-zone (e.g., "docks:east_pier"). :type: :py:class:`dict[Direction`, :py:class:`str]` .. attribute:: items List of item IDs present in this room. Items must be defined in the zone's items dictionary. :type: :py:class:`list[str]` .. admonition:: Examples >>> room = Room( ... id="spawn", ... name="The Crooked Pipe", ... description="A low-ceilinged goblin pub with sticky floors.", ... exits={"north": "front_parlour", "down": "cellar"}, ... items=["ale_mug"], ... ) .. seealso:: :py:obj:`MapRoom` Room with coordinates for authoring. :py:obj:`Zone` Container for rooms in game truth format. .. py:method:: validate_id(v) :classmethod: Validate room ID format. Room IDs must start with a letter and contain only lowercase letters, numbers, and underscores. :param v: The room ID to validate. :type v: :py:class:`str` :returns: The validated room ID. :rtype: :py:class:`str` :raises ValueError: If the ID format is invalid. .. py:class:: MapRoom(/, **data) Bases: :py:obj:`pydantic.BaseModel` A room in authoring format (includes coordinates and LLM metadata). This model represents a room as it appears in map files - the format used by the mapper tool during authoring. It extends the game truth Room with: - **Coordinates**: Position in 3D space for visualization - **LLM generation metadata**: Provenance info for AI-generated descriptions - **Description validation metadata**: Latest validator output for the description Both fields are "authoring scaffolding" - they help humans create and understand the map but are not part of the game state. When exporting to a zone file, both are stripped using ``to_room()``. .. attribute:: id Unique identifier within the zone. :type: :py:class:`str` .. attribute:: name Human-readable display name. :type: :py:class:`str` .. attribute:: description Room description text. :type: :py:class:`str` .. attribute:: coords Position in 3D space (x, y, z). Used for visualization only. :type: :py:class:`Coords` .. attribute:: exits Mapping of direction to target room ID. :type: :py:class:`dict[Direction`, :py:class:`str]` .. attribute:: items List of item IDs present in this room. :type: :py:class:`list[str]` .. attribute:: llm_generation Metadata about the last LLM generation for this room's description. Only present if the description was generated by Ollama. Contains model name, seed, parameters, and prompts for reproducibility. Stripped during zone export (like coords). :type: :py:class:`OllamaGenerationInfo | None` .. attribute:: description_validation Metadata about the last validator run for this description. Stripped during zone export (like coords). :type: :py:class:`DescriptionValidationInfo | None` .. admonition:: Examples >>> map_room = MapRoom( ... id="spawn", ... name="The Crooked Pipe", ... description="A low-ceilinged goblin pub.", ... coords=Coords(x=0, y=0, z=0), ... exits={"north": "front_parlour"}, ... items=["ale_mug"], ... ) Converting to game truth format (strips coords and llm_generation):: >>> room = map_room.to_room() >>> hasattr(room, 'coords') # False - stripped False >>> hasattr(room, 'llm_generation') # Also stripped False .. seealso:: :py:obj:`Room` Game truth format (no coordinates or LLM metadata). :py:obj:`MapFile` Container for MapRooms. :py:obj:`OllamaGenerationInfo` LLM generation metadata model. .. py:method:: validate_id(v) :classmethod: Validate room ID format. Room IDs must start with a letter and contain only lowercase letters, numbers, and underscores. .. py:method:: to_room() Convert to game truth format by stripping authoring metadata. Removes coordinates and LLM generation metadata, producing a Room suitable for zone export. The MUD server only needs topology (room connections) and content (description, items), not authoring aids. :returns: A Room instance without coordinates or LLM metadata. :rtype: :py:class:`Room` .. admonition:: Notes The following fields are intentionally NOT copied to the output: - ``coords``: Visualization aid, not game state - ``llm_generation``: Provenance tracking, not game state - ``description_validation``: Validator output, not game state .. admonition:: Examples >>> map_room = MapRoom( ... id="spawn", ... name="Spawn", ... coords=Coords(x=0, y=0, z=0), ... ) >>> room = map_room.to_room() >>> hasattr(room, 'coords') False .. py:method:: from_dict(data) :classmethod: Create MapRoom from a dictionary (legacy format support). This method handles various input formats: - Legacy coords as ``[x, y, z]`` list (converted to Coords object) - Files without ``llm_generation`` field (defaults to None) - Files with ``llm_generation`` as dict (validated by Pydantic) :param data: Room data dictionary. May contain: - ``coords`` as ``[x, y, z]`` list or ``Coords`` object - ``llm_generation`` as dict, ``None``, or missing entirely :type data: :py:class:`dict` :returns: New MapRoom instance with all fields properly typed. :rtype: :py:class:`MapRoom` .. admonition:: Examples Basic room without LLM metadata (legacy format):: >>> data = { ... "id": "spawn", ... "name": "Spawn", ... "coords": [0, 0, 0], ... "exits": {}, ... "items": [], ... } >>> room = MapRoom.from_dict(data) >>> room.coords Coords(x=0, y=0, z=0) >>> room.llm_generation is None True Room with LLM generation metadata:: >>> data = { ... "id": "spawn", ... "name": "Spawn", ... "coords": [0, 0, 0], ... "exits": {}, ... "items": [], ... "llm_generation": { ... "model": "gemma2:2b", ... "actual_seed": 12345, ... "template_id": "custom", ... "temperature": 0.7, ... "top_k": 40, ... "top_p": 0.9, ... "num_ctx": 4096, ... "num_predict": 512, ... "system_prompt": "...", ... "user_prompt": "...", ... "generated_at": "2024-01-15T10:30:00Z", ... }, ... } >>> room = MapRoom.from_dict(data) >>> room.llm_generation.model 'gemma2:2b'