Refactor dungeons into reusable component for any story type #116

Merged
Copilot merged 10 commits from copilot/refactor-dungeons-to-be-reusable into update-v0.41.0 2025-11-16 12:04:44 -05:00
Copilot commented 2025-11-12 15:03:56 -05:00 (Migrated from github.com)

Dungeons were hardcoded into dungeon_story via the add_zone method, limiting them to a single story type. This refactors dungeon generation into standalone components that can be attached to any story.

Core Changes

  • tale/dungeon/dungeon.py: New Dungeon class encapsulates level generation, mob/item population, and LLM-based room descriptions with fallback
  • tale/dungeon/DungeonEntrance.py: New DungeonEntrance exit class binds dungeons to normal locations via standard exit mechanism
  • tale/dungeon/dungeon_config.py: New DungeonConfig class defines dungeon properties (name, description, races, items, max_depth)
  • tale/zone.py: Added dungeon_config property to Zone class for storing dungeon configuration in JSON
  • stories/dungeon/story.py: Refactored to delegate to Dungeon class, removing 80+ lines of duplicate logic
  • tale/dungeon/dungeon_generator.py: Fixed MobPopulator to filter connector cells without locations, preventing KeyError
  • tale/driver.py: Updated to pass dungeon config from zone to dungeon entrance when building dungeons

DungeonConfig

Dungeon properties are now configurable per zone via DungeonConfig:

{
  "zones": {
    "town": {
      "dungeon_config": {
        "name": "Ancient Crypt",
        "description": "A dark and ancient crypt",
        "races": ["bat", "wolf", "skeleton"],
        "items": ["torch", "sword"],
        "max_depth": 5
      }
    }
  }
}

Usage

# In any story's init method:
self.dungeon = Dungeon(
    name="Ancient Crypt",
    story=self,
    llm_util=driver.llm_util,
    max_depth=5
)

# Add entrance to any location:
entrance = DungeonEntrance(
    directions=["down", "crypt"],
    short_descr="Stone archway descends into darkness",
    target_location=town_square
)

# Build with zone's config (driver does this automatically)
dungeon = entrance.build_dungeon(story, llm_util, zone.dungeon_config)

Example

See stories/dungeon_example/ for a complete implementation showing dungeon integration into a normal town story with dungeon configuration.

Testing

  • Added tests/test_dungeon.py for core Dungeon and DungeonEntrance functionality (5 tests)

  • Added tests/test_dungeon_config.py for DungeonConfig and Zone integration (7 tests)

  • Added tests/test_dungeon_example_story.py for integration verification

  • All 23 dungeon tests passing, including existing test_dungeon_story.py and test_dungeon_generator.py

  • Fixes neph1/LlamaTale#115

Original prompt

This section details on the original issue you should resolve

<issue_title>Refactor dungeons to be reusable</issue_title>
<issue_description>Currently, dungeons are limited to dungeon_story type stories.
These emulate classical dungeon crawlers with multiple levels (each level is a Zone), randomly generated loot and mobs.

I'd like to refactor these to be reusable in any story.

They would function much as they do now, and for simplicity, the will be single entrance, and not be connected to the normal world in any way, other than the entrance.

Tasks:

  • Break out the relevant code (most will be to decouple it from dungeon_story).

  • For verification: Make an example story which has a normal Location, which also has an entrance to a dungeon.
    </issue_description>

Comments on the Issue (you are @copilot in this section)


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Dungeons were hardcoded into `dungeon_story` via the `add_zone` method, limiting them to a single story type. This refactors dungeon generation into standalone components that can be attached to any story. ## Core Changes - **`tale/dungeon/dungeon.py`**: New `Dungeon` class encapsulates level generation, mob/item population, and LLM-based room descriptions with fallback - **`tale/dungeon/DungeonEntrance.py`**: New `DungeonEntrance` exit class binds dungeons to normal locations via standard exit mechanism - **`tale/dungeon/dungeon_config.py`**: New `DungeonConfig` class defines dungeon properties (name, description, races, items, max_depth) - **`tale/zone.py`**: Added `dungeon_config` property to Zone class for storing dungeon configuration in JSON - **`stories/dungeon/story.py`**: Refactored to delegate to `Dungeon` class, removing 80+ lines of duplicate logic - **`tale/dungeon/dungeon_generator.py`**: Fixed `MobPopulator` to filter connector cells without locations, preventing KeyError - **`tale/driver.py`**: Updated to pass dungeon config from zone to dungeon entrance when building dungeons ## DungeonConfig Dungeon properties are now configurable per zone via `DungeonConfig`: ```json { "zones": { "town": { "dungeon_config": { "name": "Ancient Crypt", "description": "A dark and ancient crypt", "races": ["bat", "wolf", "skeleton"], "items": ["torch", "sword"], "max_depth": 5 } } } } ``` ## Usage ```python # In any story's init method: self.dungeon = Dungeon( name="Ancient Crypt", story=self, llm_util=driver.llm_util, max_depth=5 ) # Add entrance to any location: entrance = DungeonEntrance( directions=["down", "crypt"], short_descr="Stone archway descends into darkness", target_location=town_square ) # Build with zone's config (driver does this automatically) dungeon = entrance.build_dungeon(story, llm_util, zone.dungeon_config) ``` ## Example See `stories/dungeon_example/` for a complete implementation showing dungeon integration into a normal town story with dungeon configuration. ## Testing - Added `tests/test_dungeon.py` for core `Dungeon` and `DungeonEntrance` functionality (5 tests) - Added `tests/test_dungeon_config.py` for `DungeonConfig` and Zone integration (7 tests) - Added `tests/test_dungeon_example_story.py` for integration verification - All 23 dungeon tests passing, including existing `test_dungeon_story.py` and `test_dungeon_generator.py` - Fixes neph1/LlamaTale#115 <!-- START COPILOT CODING AGENT SUFFIX --> <details> <summary>Original prompt</summary> > > ---- > > *This section details on the original issue you should resolve* > > <issue_title>Refactor dungeons to be reusable</issue_title> > <issue_description>Currently, dungeons are limited to dungeon_story type stories. > These emulate classical dungeon crawlers with multiple levels (each level is a Zone), randomly generated loot and mobs. > > I'd like to refactor these to be reusable in any story. > > They would function much as they do now, and for simplicity, the will be single entrance, and not be connected to the normal world in any way, other than the entrance. > > Tasks: > > * Break out the relevant code (most will be to decouple it from dungeon_story). > > * For verification: Make an example story which has a normal Location, which also has an entrance to a dungeon. > </issue_description> > > ## Comments on the Issue (you are @copilot in this section) > > <comments> > </comments> > </details> - Fixes neph1/LlamaTale#115 <!-- START COPILOT CODING AGENT TIPS --> --- 💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more [Copilot coding agent tips](https://gh.io/copilot-coding-agent-tips) in the docs.
neph1 (Migrated from github.com) reviewed 2025-11-12 15:03:56 -05:00
neph1 (Migrated from github.com) requested changes 2025-11-16 01:44:54 -05:00
neph1 (Migrated from github.com) left a comment

This is a great start. I think it's close to testable. I've pushed some changes and fixes. Can you please address my request for a DungeonConfig file, and pass it where needed.

The DungeonConfig should reside in the Zone class (and be stored there in the json).

This means a Zone can only have one Dungeon, or at least all dungeons would use the same config.

@copilot

This is a great start. I think it's close to testable. I've pushed some changes and fixes. Can you please address my request for a DungeonConfig file, and pass it where needed. The DungeonConfig should reside in the Zone class (and be stored there in the json). This means a Zone can only have one Dungeon, or at least all dungeons would use the same config. @copilot
@ -0,0 +1,65 @@
from typing import Sequence, Union
neph1 (Migrated from github.com) commented 2025-11-15 15:53:39 -05:00

Please create a DungeonConfig class, where name, description, items and races can be defined

Please create a DungeonConfig class, where name, description, items and races can be defined
neph1 commented 2025-11-16 01:48:37 -05:00 (Migrated from github.com)

@copilot please check the review comments

@copilot please check the review comments
copilot-swe-agent[bot] (Migrated from github.com) reviewed 2025-11-16 01:55:51 -05:00
@ -0,0 +1,65 @@
from typing import Sequence, Union
copilot-swe-agent[bot] (Migrated from github.com) commented 2025-11-16 01:55:51 -05:00

Created DungeonConfig class in tale/dungeon/dungeon_config.py with name, description, items, and races properties. It's integrated into the Zone class and can be serialized to/from JSON. Commit: a3b16c3

Created DungeonConfig class in `tale/dungeon/dungeon_config.py` with name, description, items, and races properties. It's integrated into the Zone class and can be serialized to/from JSON. Commit: a3b16c3
Copilot commented 2025-11-16 01:55:52 -05:00 (Migrated from github.com)

This is a great start. I think it's close to testable. I've pushed some changes and fixes. Can you please address my request for a DungeonConfig file, and pass it where needed.

The DungeonConfig sh...

DungeonConfig class has been created and integrated:

  • Resides in Zone class as zone.dungeon_config
  • Stored in JSON via zone.get_info() and loaded via from_json()
  • Passed to DungeonEntrance.build_dungeon() from driver.py
  • Includes name, description, races, items, and max_depth
  • All tests passing (23 dungeon-related tests)

Commit: a3b16c3

> This is a great start. I think it's close to testable. I've pushed some changes and fixes. Can you please address my request for a DungeonConfig file, and pass it where needed. > > The DungeonConfig sh... DungeonConfig class has been created and integrated: - Resides in Zone class as `zone.dungeon_config` - Stored in JSON via `zone.get_info()` and loaded via `from_json()` - Passed to DungeonEntrance.build_dungeon() from driver.py - Includes name, description, races, items, and max_depth - All tests passing (23 dungeon-related tests) Commit: a3b16c3
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
neph1/LlamaTale!116
No description provided.