Skip to content

dungeon

dungeon

The dungeon module contains the Dungeon, Location, and Exit classes, and some custom exceptions related to working with them.

A Dungeon contains one or more locations, each of which contains one or more exits leading to other locations in the dungeon.

Stock a dungeon

To create and stock a dungeon, a typical order of operations might be:

  1. Create the Location objects you'll add to a Dungeon you'll create in a later step.
  2. Add Exit objects to the locations. Each location must be bidirectionally connected to at least one other location by an exit.
  3. Create Encounter objects and add a MonsterStatsBlock to the encounters that should result in battle (or, depending on the block's properties, the chance of a battle).
  4. Add the encounters to the locations.
  5. Create the Dungeon and add the locations to it.
Tip

Before you send a party in to explore a dungeon, ensure the party can navigate to its location by calling the dungeon's validate_connection_locations() method. Doing so returns a bool indicating whether the dungeon's locations are each bidirectionally connected to at least one other Location.

DestinationLocationNotFoundError

Bases: Exception

Raised when a destination Location of an Exit doesn't exist in the Dungeon.

Dungeon

Dungeon(name: str = None, description: str = None, locations: List[Location] = [], start_location_id: int = None, id: str = str(uuid.uuid4()))

Contains a collection of interconnected Location objects and can validate the integrity of those connections.

Attributes:

  • id (UUID) –

    Unique identifier for the dungeon.

  • name (str) –

    The name of the dungeon.

  • description (str) –

    A brief description providing context or history for the dungeon.

  • locations (List[Location]) –

    List of locations within the dungeon. A location must have at least one exit, and all exits must have valid destinations within the dungeon. No locations should be islands unless the dungeon only contains that single location.

Example:

>>> location1 = Location(1, 10, 10, [Exit(Direction.NORTH, 2)], keywords=["rust", "armory"])
>>> location2 = Location(2, 3, 40, [Exit(Direction.SOUTH, 1)], keywords=["cold", "corridor", "narrow"])
>>> dungeon = Dungeon("Example Dungeon", "An example dungeon.", [location1, location2])
>>> if dungeon.validate_connection_locations():
...     start_location = dungeon.set_start_location(1)
...     new_location = dungeon.move(Direction.NORTH)
...     new_location.id
2

Parameters:

  • name (str, default: None ) –

    The name of the dungeon. Should be unique within an Adventure.

  • description (str, default: None ) –

    A description of the dungeon appropriate for display to the player.

  • locations (List[Location], default: [] ) –

    The collection of locations within the dungeon.

  • start_location_id (int, default: None ) –

    The ID of the Location the Party should begin exploration of the dungeon. If you supply this argument, the current_party_location is set to this location automatically.

  • id (str, default: str(uuid4()) ) –

    The ID of the dungeon; must be unique within the Adventure.

add_location

add_location(location: Location) -> None

Adds the location to the dungeon.

Parameters:

  • location (Location) –

    The location to add to the dungeon.

from_dict classmethod

from_dict(data)

Returns a Dungeon instance from a dictionary representation of the dungeon. Useful as a post-deserialization step when loading from a permanent data store.

get_location_by_direction

get_location_by_direction(location: Location, direction: Direction) -> Location

Get the location in the specified direction from the given location.

Parameters:

  • location (Location) –

    The location containing the exit whose destination should be returned.

  • direction (Direction) –

    The direction of the give location's exit whose destination should be returned.

Returns:

  • Location ( Location ) –

    The location that is the destination of the exit in the specified direction, otherwise None if there is no exit in that direction.

get_location_by_id

get_location_by_id(location_id: int) -> Location

Get the location with the specified ID.

Parameters:

  • location_id (int) –

    The ID of the location to return.

Returns:

  • Location ( Location ) –

    The location with the specified ID if it exists, otherwise None.

get_random_dungeon staticmethod

get_random_dungeon(name: str = 'Random Dungeon', description: str = '', num_locations: int = 10, level: int = 1, openai_model: OpenAIModelVersion = OpenAIModelVersion.DEFAULT) -> Dungeon

Generates a random dungeon with the specified number of locations.

Parameters:

  • name (str, default: 'Random Dungeon' ) –

    The name of the dungeon.

  • description (str, default: '' ) –

    A brief description providing context or history for the dungeon.

  • num_locations (int, default: 10 ) –

    The number of locations to generate in the dungeon.

  • level (int, default: 1 ) –

    The level of the dungeon. Determines the hit die (and thus the difficulty) of monsters in encounters in the dungeon.

  • openai_model (OpenAIModelVersion, default: DEFAULT ) –

    The OpenAI model to use when generating keywords for the locations in the dungeon.

Returns:

  • Dungeon ( Dungeon ) –

    A randomly generated dungeon with the specified number of locations, each with a random size and possibly containing an Encounter.

Examples:

    # Create a random dungeon without populating its keywords
    random_dungeon = Dungeon.get_random_dungeon(
        num_locations=20, openai_model=OpenAIModelVersion.NONE
    )

    # Create a random dungeon with second-level monsters and 20 locations
    random_dungeon = Dungeon.get_random_dungeon(
        "Dungeon of the Mad Mage",
        "The second level of the underground lair of Glofarnux.",
        level=2,
        num_locations=20,
        openai_model=OpenAIModelVersion.DEFAULT,
    )

move

move(direction: Direction) -> Location

Moves the party to the location in the specified direction if there's an exit in that direction and sets the dungeon's current location to the new location.

You should set the new location's is_visited attribute to True after you've completed any processing you like when the party arrives at a location for the first time.

Example:

An example of using the is_visited property is to present a detailed description of the location when a party arrives at a location with is_visited == False' and a brief one-liner description on subsequent visits whenis_visited == true`.

>>> exit1 = Exit(Direction.NORTH, 2)
>>> exit2 = Exit(Direction.SOUTH, 1)
>>> location1 = Location(1, 10, 10, [exit1])
>>> location2 = Location(2, 10, 10, [exit2], keywords=["rust", "armory"])
>>> dungeon = Dungeon("Example Dungeon", "An example dungeon.", [location1, location2])
>>> start_location = dungeon.set_start_location(1)
>>> new_location = dungeon.move(Direction.NORTH)
>>> if new_location:
...     if new_location.is_visited:
...         print(f"Party moved to previously visited location {new_location}.")
...     else:
...         print(f"The party moved to new location {new_location}, and here's a super detailed description...")
...         new_location.is_visited = True
Party moved to new location LOC ID: 2 Size: 10'W x 10'L Exits: [NORTH:1] Keywords: ['rust', 'armory'], and here's a super detailed description...
>>> dungeon.current_party_location == new_location
True

Parameters:

  • direction (Direction) –

    The direction of the exit the party should move through.

Returns:

  • Location ( Location ) –

    The location the party moved to if they were able to move in the specified direction, otherwise None.

set_start_location

set_start_location(location_id: int) -> Location

Sets the Location where Party starts exploring the dungeon if a location with the specified ID exists within the dungeon.

If it exists, this method also sets the current_party_location to that location.

Parameters:

  • location_id (int) –

    The ID of the Location where the party should begin their exploration of the dungeon.

Returns:

  • Location ( Location ) –

    The starting location if it exists, otherwise None.

to_dict

to_dict() -> dict

Returns a dictionary representation of the dungeon. Useful as a pre-serialization step when saving to a permanent data store.

to_json

to_json() -> str

Gets a JSON string representation of the Dungeon instance.

This method uses the to_dict method to first convert the dungeon instance into a dictionary, which is then serialized into a JSON string using json.dumps. ]]

Returns:

  • str ( str ) –

    A JSON string representation of the dungeon instance.

Example:

    # Create a random dungeon without populating its keywords
    random_dungeon = Dungeon.get_random_dungeon(
        num_locations=20, openai_model=OpenAIModelVersion.NONE
    )

    # Get the JSON representation (you could write this to disk)
    dungeon_json = random_dungeon.to_json()

    # Parse it to a Python dict
    dungeon_dict = json.loads(dungeon_json)

    # Turn it back into a Dungeon
    dungeon = Dungeon.from_dict(dungeon_dict)

validate_location_connections

validate_location_connections() -> bool

Verifies whether every Location in the dungeon is connected to at least one other location and that a connection in the opposite direction exists. For example, if location A has an exit EAST to location B, then location B must have an exit WEST to location A.

Every location in a dungeon must be part of an interconnected graph where each "source" location has at least one exit leading a "destination" location in the dungeon. Each destination location must also have a corresponding exit in the opposite direction whose destination is the source location.

Empty dungeons and those with only one location are considered valid.

Returns:

  • bool ( bool ) –

    True if all locations in the dungeon are connected by at least one bi-directional exit to another location, otherwise False.

Exit

Exit(direction: Direction, destination: int, locked: bool = False)

Represents an exit leading from one Location to another in a Dungeon.

An exit can be locked, which allows you to control access to its destination based on some condition. For example, you could require that one of the player's characters has a specific Item in their Inventory that acts as the "key" before allowing them to move to the exit's destination.

Attributes:

  • direction (Direction) –

    The direction of the exit. Each location can have only one exit per direction.

  • destination (int) –

    The ID of the destination Location. Must exist within the dungeon.

  • locked (bool) –

    Indicates if the exit is locked or not.

  • opposite_direction (Direction) –

    The direction directly opposite this exit's direction. There is no contract that guarantees a corresponding return exit exists in the destination location's exits collection.

Example:

    # Create two Exits for a Location
    exit1 = Exit(Direction.NORTH, 2)
    exit2 = Exit(Direction.SOUTH, 1)

    # Create the Location and pass the Exits as params
    location = Location(
        id=1,
        width=10,
        length=10,
        exits=[exit1, exit2],
        keywords=["library", "ransacked"],
    )

Parameters:

  • direction (Direction) –

    The direction in which the exit faces. For example, the wall the exit is on in a room or the direction in which a tunnel turns.

  • destination (int) –

    The ID of the Location the exits leads to.

  • locked (bool, default: False ) –

    Whether the party is prevented from to going through the exit. Defaults to False.

from_dict classmethod

from_dict(data)

Deserializes a dictionary representation of an Exit object. Typically done after getting the dictionary from persistent storage.

lock

lock()

Locks the exit.

to_dict

to_dict()

Serializes the Exit to a dictionary, typically in preparation for writing it to persistent storage in a downstream operation.

unlock

unlock()

Unlocks the exit.

ExitAlreadyExistsError

Bases: Exception

Raised when trying to add an exit to a location, but an exit in that direction already exists.

Location

Location(id: int, width: int = 10, length: int = 10, exits: List[Exit] = [], keywords: List[str] = [], encounter: Encounter = None, is_visited: bool = False)

Represents a location of importance within a Dungeon.

Attributes:

  • id (int) –

    Unique identifier for the location. Must be unique within the dungeon.

  • dimensions (dict) –

    Dimensions of the location in a {"width": int, "length": int} format. Default dimensions are 10x10.

  • exits (List[Exit]) –

    List of exits leading to other locations. Each location must have at least one exit and each exit must have a unique direction.

  • keywords (List[str]) –

    Keywords associated with the location for search or identification.

  • encounter (Encounter) –

    An optional encounter that exists within this location.

Example:

 >>> exit1 = Exit(Direction.NORTH, 2)
 >>> exit2 = Exit(Direction.SOUTH, 1)
 >>> location1 = Location(1, 10, 10, [exit1])
 >>> location2 = Location(2, 8, 8, [exit2], keywords=["rust", "armory"])
 >>> dungeon = Dungeon("Example Dungeon", "An example dungeon.", [location1, location2])
 >>> # Validate the dungeon before proceeding with the game logic
 >>> dungeon.validate_connection_locations()
 True

Parameters:

  • id (int) –

    The ID of the Location; must be unique within a Dungeon.

  • width (int, default: 10 ) –

    The width of the location in feet.

  • length (int, default: 10 ) –

    The length of the location in feet.

  • exits (List[Exit], default: [] ) –

    The collection of exits leading from the location.

  • keywords (List[str], default: [] ) –

    The keywords that describe the location.

  • encounter (Encounter, default: None ) –

    An encounter the party can interact with when it arrives at the location.

  • is_visited (bool, default: False ) –

    Whether the party has previously been to the location.

json property

json

Returns a JSON representation of the location.

add_exit

add_exit(exit: Exit)

Adds an exit to the location.

Parameters:

  • exit (Exit) –

    The exit to add to the location.

Raises:

  • ValueError

    If an exit already exists in the same direction.

from_dict classmethod

from_dict(data)

Deserializes a dictionary representation of a Location object. Typically done after getting the dictionary from persistent storage.

get_exit

get_exit(direction: Direction) -> Exit

Returns the exit in the specified direction, if it exists.

Parameters:

  • direction (Direction) –

    The direction of the exit to return.

Returns:

  • Exit ( Exit ) –

    The exit in the specified direction, or None if there is no exit in that direction.

to_dict

to_dict()

Serializes the Location to a dictionary, typically in preparation for writing it to persistent storage in a downstream operation.

LocationAlreadyExistsError

Bases: Exception

Raised when trying to add a location to the dungeon's locations collection, but a location with the same ID already exists.

LocationNotFoundError

Bases: Exception

Raised when a location cannot be found in a dungeon.

NoMatchingExitError

Bases: Exception

Raised when an Exit in a Location doesn't have a corresponding Exit back to the source Location.

ReturnConnectionDestinationIncorrectError

Bases: Exception

Raised when an Exit in a Location leads to a destination Location whose corresponding return Exit direction is correct, but its destination Location is incorrect.