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:
- Create the Location objects you'll add to a
Dungeon
you'll create in a later step. - Add Exit objects to the locations. Each location must be bidirectionally connected to at least one other location by an exit.
- 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).
- Add the encounters to the locations.
- 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
theParty
should begin exploration of the dungeon. If you supply this argument, thecurrent_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
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 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_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:
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 when
is_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
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, otherwiseFalse
.
Exit
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
Deserializes a dictionary representation of an Exit
object. Typically done after getting the dictionary from persistent storage.
to_dict
Serializes the Exit
to a dictionary, typically in preparation for writing it to persistent storage in a downstream operation.
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 aDungeon
. -
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.
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
Deserializes a dictionary representation of a Location
object. Typically done after getting the dictionary from persistent storage.
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.