Generate Mazes via API

Creating labyrinths programmatically via the API offers the same level of customization and control as the command-line tool, but with the flexibility and integration capabilities of a Python script. Below is a concise guide to generating mazes using the Erbsland Maze API.

Start by importing the necessary classes from the erbsland_maze module. This includes setup for the SVG output, layout configurations, modifiers to customize the maze, and the generator itself. The pathlib module is used for file path operations, ensuring compatibility across different operating systems.

>>> from erbsland_maze import SvgSetup, SvgLayout, BlankModifier, Placement, RoomSize, GeneratorSetup, Generator
>>> from pathlib import Path
>>> svg_setup = SvgSetup(width=100.0, height=100.0, side_length=5.0)
>>> svg_layout = SvgLayout(svg_setup)
>>> modifiers = [BlankModifier(Placement.CENTER, RoomSize(5, 5))]
>>> generator_setup = GeneratorSetup(modifiers=modifiers)
>>> generator = Generator(svg_layout)
>>> svg_file = Path('maze.svg')
>>> generator.generate_and_save(svg_file)
Erbsland Maze - V1.1
  Layout: 100.00 x 100.00 mm / thickness 1.70 mm / calculated side length: 4.76 mm
  Room count: 21 x 21
Preparing rooms...
1. attempt to find a solution.
Generating the paths for the maze...
Filling islands...
Verifying generated paths...
Connect all paths...
  - successfully joined paths 1-2
Rendering image...

In this example, a maze with a dimension of 100x100 mm is created, with a specified side length of 5.0 mm for each room. A BlankModifier is used to create a blank area in the center of the maze, demonstrating how to apply customizations. The maze generation process is initiated, and the resulting SVG is saved to a file named “maze.svg”.

_images/example_api_initial.svg

This approach provides a powerful way to integrate maze generation into applications, allowing for dynamic creation and manipulation of mazes based on user input or other runtime conditions.

Step by Step

Generating a maze programmatically involves a series of steps that allow you to customize every aspect of the maze, from its layout and dimensions to specific features like end points and blank spaces. Here’s a step-by-step guide to creating a maze using the Erbsland Maze API:

  1. Initialize the Layout: Start by creating an instance of the Layout class for the Generator. The API provides an SvgLayout class, which is designed to work with SVG output. This class requires an instance of SvgSetup that contains the initial setup parameters, including dimensions and side length.

  2. Configure the Generator: If you wish to customize your maze, prepare a GeneratorSetup instance next. This setup allows you to add modifications such as end points, blank spaces, or any other modifiers to the maze’s layout.

  3. Instantiate the Generator: With your SvgLayout and GeneratorSetup ready, create a new Generator instance. Pass the previously prepared SvgLayout and GeneratorSetup to the constructor of the Generator.

  4. Generate and Save the Maze: To kick off the maze generation process and save the output, call the Generator.generate_and_save() method on the Generator instance, providing a pathlib.Path object that specifies the filename and location for the generated SVG file.

For subsequent mazes with the same layout but different configurations or to introduce randomness, you can call the Generator.generate_and_save() method multiple times. Each call generates a new maze according to the current GeneratorSetup configuration.

Feel free to adjust the settings in your GeneratorSetup between generations to explore different maze layouts and features. However, should you need to modify the maze’s dimensions or fundamental layout, you will need to instantiate a new Layout and Generator.

The Generator

At the heart of the maze generation process lies the Generator, which leverages an abstract algorithm to construct the maze. This component works hand-in-hand with a Layout class, tasked with transforming the conceptual room layout into a tangible image.

class erbsland_maze.Generator(layout: Layout, setup: GeneratorSetup)

Essential to the maze generation, the Generator orchestrates the creation process, guided by a Layout instance for graphical representation and a GeneratorSetup for procedural directives.

Parameters:
  • layout (Layout) – Specifies the graphical framework for maze generation.

  • setup (GeneratorSetup) – Dictates the generation strategy and maze configuration.

generate_and_save(path)

Executes the maze generation and commits the final image to file.

Parameters:

path (pathlib.Path) – Designates the file path for the saved maze image.

The GeneratorSetup class is instrumental in fine-tuning the generation process, offering a wide array of parameters to customize the maze’s layout, appearance, and complexity.

class erbsland_maze.GeneratorSetup(path_ends=None, modifiers=None, allow_islands=True, maximum_attempts=20, verbose=True, ignore_errors=False, layout_only=False)

Configures the operational parameters for the Generator, influencing everything from path dynamics to visual modifiers.

Parameters:
  • path_ends (list[PathEnd]) – A list of path-ends in the maze..

  • modifiers (list[Modifier]) – A list of modifiers to customize the maze layout..

  • allow_islands (bool) – Allows isolated sections in the maze. They will filled with random paths as decorations. If set to False, isolated sections raise an exception.

  • maximum_attempts (int) – Caps the attempts to resolve the maze.

  • verbose (bool) – Enables or disables console updates throughout the generation process.

  • ignore_errors (bool) – Allows the process to continue past errors, useful for debugging.

  • layout_only (bool) – If enabled, there is no maze generated and just the room layout saved into the image.

The SVG Layout

This project currently provides only built-in support to generate SVG files using the PyCairo library. In order to render the mazes in SVG files, you have to create an instance of the SvgLayout class and pass it to the constructor of Generator.

class erbsland_maze.SvgLayout(setup: SvgSetup)

The SVG layout class renders the generated maze into a SVG file using PyCairo. It takes one parameter setup that contains all dimensions and settings defining the rendered maze.

Parameters:

setup (SvgSetup) – The setup values for the SVG layout.

class erbsland_maze.SvgSetup

This class provides all attributes for the SVG layout.

width: float

This mandatory attribute specifies the width of the maze in millimeters (mm). The width impacts the final dimensions of the generated SVG file. When used alongside the side_length, the layout calculates the optimal number of rooms along the X axis.

height: float

This required attribute sets the height of the maze in millimeters (mm). Similar to width, height determines the dimensions of the resulting SVG file. In combination with the side_length attribute, it helps in determining the ideal number of rooms along the Y axis.

wall_thickness: float = 1.7

Defines the thickness of the maze’s walls with this option, measured in millimeters (mm). If not specified, a default thickness of 1.7 mm is applied.

side_length: float = 4.0

This parameter establishes the side length of each room within the maze, including wall thickness, measured in millimeters (mm). The default value is 4 mm.

Note

If the maze’s specified width and height do not proportionately match the room length set by this option, the outer rooms will be adjusted in size to ensure the entire area is filled.

width_parity: Parity = Parity.ODD

This option allows you to determine the parity (odd or even) of the room count along the X axis.

height_parity: Parity = Parity.ODD

Similarly, this option lets you set the parity (odd or even) of the room count along the Y axis.

start_end_mark: bool = True

this flag controls if the colored rectangle markers at the path ends will be painted or not. Setting this attribute to False is particularly beneficial for integrating the maze generation into automated workflows, where such markers may not be needed.

svg_unit: SvgUnit = SvgUnit.MM

Select the unit of measurement for the SVG file’s dimensions.

Note

Switching to SvgUnit.PX (pixels) modifies the unit within the SVG file only and does not affect the input dimensions (width, height, or side_length), which should always be provided in millimeters.

svg_dpi: float = 96.0

Define the DPI (dots per inch) for converting millimeter measurements to pixels in the SVG output. The default setting is 96 DPI, and the acceptable range spans from 60 to 10,000 DPI.

svg_zero: SvgZeroPoint = SvgZeroPoint.CENTER

Adjust the origin point of the SVG canvas.

Parameter

Meaning

SvgZeroPoint.CENTER

Places the maze’s center at the middle of the canvas, ensuring all SVG coordinates are positive from the document’s top-left corner. This is the default and best choice for viewing the SVG or using it for designs, web or print.

SvgZeroPoint.TOP_LEFT

Moves the zero point to the top-left corner of the document, beneficial for workflows requiring the maze’s center at the document’s origin, though it may not display correctly in all viewers.

svg_background: bool = True

This flag controls if the SVG background shall be drawn opaque.

class erbsland_maze.Parity(value, names=None, *values, module=None, qualname=None, type=None, start=1, boundary=None)

Requesting a parity of a number of elements.

EVEN = 'even'

Even parity requested.

NONE = 'none'

No parity requested.

ODD = 'odd'

Odd parity requested.

class erbsland_maze.SvgFillMode(value, names=None, *values, module=None, qualname=None, type=None, start=1, boundary=None)

The mode how the actual side length is calculated.

FIXED_CENTER = 'fixed_center'

Use a square room size with the exact side length as configured and place the maze in the center. If the room size doesn’t divide the with and height evenly, there will be a gap around the maze.

FIXED_TOP_LEFT = 'fixed_top_left'

Use a square room size with the exact side length as configured and place the maze in the top left corner. If the room size doesn’t divide the with and height evenly, there will be a gap at the bottom or right side.

SQUARE_CENTER = 'square_center'

Use a square room size that fills at least one dimension perfectly, and align the maze at the top left corner. If the room size doesn’t divide the with and height evenly, there will be a gap around the maze.

SQUARE_TOP_LEFT = 'square_top_left'

Use a square room size that fills at least one dimension perfectly, and align the maze at the top left corner. If the room size doesn’t divide the with and height evenly, there will be a gap at the bottom or right side.

STRETCH = 'stretch'

Stretch the rooms into rectangles that completely fill the specified width and height.

STRETCH_EDGE = 'stretch_edge'

Calculate the best square room size from the side lengths and parity and stretch the rooms at the edges to completely fill the specified width and height.

does_center_rooms() bool

Tests if this value centers the room.

does_proportionally_scale_room() bool

Tests if this value scales the room size to fill the area.

does_scale_room() bool

Tests if this value scales the room, proportionally or not.

does_stretch_edge() bool

Tests if this value stretches the maze edge.

classmethod from_name(name: str) Self

Get an instance from the given name.

Parameters:

name – The name.

Returns:

The enum value.

classmethod from_text(text: str) Self

Parse a text and convert it to a fill mode.

Parameters:

text – The text to parse.

Returns:

The enum value.

classmethod get_all_names() list[str]

Get all possible names for the fill modes.

class erbsland_maze.SvgUnit(value, names=None, *values, module=None, qualname=None, type=None, start=1, boundary=None)

SVG units to use for a document.

MM = 0

Millimetre unit.

PX = 1

Pixel unit.

class erbsland_maze.SvgZeroPoint(value, names=None, *values, module=None, qualname=None, type=None, start=1, boundary=None)

Where the SVG zero point shall be placed, which is the center of the maze.

CENTER = 'center'

Create a SVG file for print, where the center is at the center of the canvas.

TOP_LEFT = 'top_left'

Create a technical SVG file, where the center is at the top left of the canvas.

Path Ends

class erbsland_maze.PathEnd(placement: ~erbsland_maze.placement.Placement, offset: ~erbsland_maze.room_offset.RoomOffset = <factory>, is_dead_end: bool = False, name: str = '')

Represents the end of a connected path. This can also be an intermediate point of the path, that must be connected.

classmethod from_text(text: str)

Create a path end by parsing the given text.

Parameters:

text – The text to parse.

Returns:

A new path end.

is_dead_end: bool = False

If this path end is a dead-end and does not connect any other path ends. A such path can be cut short to allow connecting the real paths.

name: str = ''

The name that was used to configure this path. Used in error messages.

offset: RoomOffset

The offset from this placement.

placement: Placement

The placement of the path end.

Modifier Classes

class erbsland_maze.Modifier(modifier_type: ModifierType, placement: Placement = None, size: RoomSize = None, insets: RoomInsets = None, offset: RoomOffset = None, closing: Closing = None, name: str = None)

The base class of all modifiers to style the maze in custom ways.

Do not use this class directly. Use the derived classes instead.

abstract classmethod from_text(text: str) Self

Create a modifier from a string.

Parameters:

text – The text that will be parsed.

Returns:

The modifier object.

class erbsland_maze.BlankModifier(placement: Placement, size: RoomSize = RoomSize(width=1, height=1), offset: RoomOffset = RoomLocation(0, 0, is_relative=False), name: str = None)

A modifier to create blank areas in the maze.

classmethod from_text(text: str) Self

Create a modifier from a string.

Parameters:

text – The text that will be parsed.

Returns:

The modifier object.

class erbsland_maze.FrameModifier(insets: RoomInsets = RoomInsets(north=1, east=1, south=1, west=1), name: str = None)

A modifier to create a frame of blank rooms around the maze.

classmethod from_text(text: str) Self

Create a modifier from a string.

Parameters:

text – The text that will be parsed.

Returns:

The modifier object.

class erbsland_maze.ClosingModifier(closing: Closing, placement: Placement, size: RoomSize = RoomSize(width=1, height=1), offset: RoomOffset = RoomLocation(0, 0, is_relative=False), name: str = None)

A modifier to close paths in the maze.

classmethod from_text(text: str) Self

Create a modifier from a string.

Parameters:

text – The text that will be parsed.

Returns:

The modifier object.

class erbsland_maze.Closing(closing_type: ClosingType, invert: bool = False)

A closing specification.

class erbsland_maze.MergeModifier(placement: Placement, size: RoomSize = RoomSize(width=1, height=1), offset: RoomOffset = RoomLocation(0, 0, is_relative=False), name: str = None)

A modifier to merge rooms in the maze.

classmethod from_text(text: str) Self

Create a modifier from a string.

Parameters:

text – The text that will be parsed.

Returns:

The modifier object.

Modifier Enumerations

class erbsland_maze.ModifierType(value, names=None, *values, module=None, qualname=None, type=None, start=1, boundary=None)

The type of modifier.

BLANK = 'blank'

Create a blank space at the specified location.

CLOSING = 'close'

Permanently closes connections in portions of the maze

FRAME = 'frame'

Creates a blank space all around the maze with the specified size.

MERGE = 'merge'

Merge rooms into a larger room at the specified location.

class erbsland_maze.Placement(value, names=None, *values, module=None, qualname=None, type=None, start=1, boundary=None)

Logical placement of an element.

direction_normals() Tuple[int, int]

Get normals to detect the direction of this placement.

offset(value: int) RoomLocation

Get an offset that points towards the center.

Parameters:

value – The number of rooms toward the center.

Returns:

The offset as room location.

property order_value: int

Return a value to order modifications in a sequence to case the least amount of conflicts.

placement_normals() Tuple[float, float]

Get normals to get a placement.

size_offset(size: RoomSize) RoomLocation

A size offset to correct a placement according to the size of the affected locations.

Parameters:

size – The size.

Returns:

The offset as room location.

class erbsland_maze.ClosingType(value, names=None, *values, module=None, qualname=None, type=None, start=1, boundary=None)

Room Layout Classes

class erbsland_maze.Room(location: RoomLocation)

A room in the maze.

add_connection(location: RoomLocation, direction: Direction, target_room: Room) None

Add a connection to another room.

Parameters:
  • location – The location inside of this room.

  • direction – The direction for the wall in which this connection is anchored.

  • target_room – The target room for the connection.

close_blocked_connections() None

Remove all connections from this room, that are blocked because they lead to already allocated paths.

get_connection(wall: Wall) RoomConnection | None

Get a connection at the given wall.

Parameters:

wall – The wall.

Returns:

The connection at the given wall or None if there isn’t one.

get_connections_at_location(location: RoomLocation) list[RoomConnection]

Get all connections from a given location inside this room.

Parameters:

location – The location inside of this room.

Returns:

A list of connections.

get_connections_in_direction(direction: Direction) list[RoomConnection]

Get all connections that lead to the given direction.

Parameters:

direction – The direction.

Returns:

A list of connections.

get_walls() list[Wall]

Get all walls of this room.

Returns:

A list of walls.

is_connected_to_room(room: Room) bool

Test if this room has a connection to another room.

Parameters:

room – The other room to test.

Returns:

True if there is a connection between this and the other room.

is_open_connection(wall: Wall) bool

Test if there is an open connection to the given wall.

Parameters:

wall – The wall.

Returns:

True if there is an open connection.

property is_surrounded_by_blanks: bool

Test if the room is surrounded by blank rooms.

This test is used to check if a room is suitable for a start position. For example, if there is a frame around the maze, and the start position is in the corner, this will never work.

remove_all_connections()

Remove all connections from and to this room.

remove_connection(connection: RoomConnection) None

Remove a connection from this room.

Parameters:

connection – The connection to remove.

reset()

Reset the room for another solution attempt.

unused_connections() list[RoomConnection]

Get all connections that can be potentially used to create a new path.

Returns:

A list of connections.

class erbsland_maze.RoomConnection(a: ConnectionSide, b: ConnectionSide)

A connection between two rooms.

property connects_primary_paths: bool

Test if this connection connects primary paths and no decoy paths.

property connects_two_paths: bool

Test if this connection connects two paths.

get_path_join_info() PathJoinInfo

A method, to get a key for the path joining info.

Returns:

A tuple containing the two path ids and the total path length.

local(my_room: Room) ConnectionSide

Get my local side of the connection.

Parameters:

my_room – My room to get the side of.

Returns:

The connection side.

remote(my_room: Room) ConnectionSide

Get the remote side of the connection.

Parameters:

my_room – My room, to get the remote side from.

Returns:

The connection side.

remote_room(my_room: Room) Room

Shortcut to get the remote room.

Parameters:

my_room – My room.

Returns:

The remote room.

replace_room(old_room: Room, new_room: Room) None

Replace a room in this connection.

Parameters:
  • old_room – The old room.

  • new_room – The new room.

reset()

Reset the connection for another solution attempt.

property total_path_length: int

Get the total path length at this connection.

class erbsland_maze.ConnectionSide(room: Room, wall: Wall)

Represents the end or side of a connection.

class erbsland_maze.RoomLocation(x: int = 0, y: int = 0)

The location of a room in room units.

advance(direction: Direction) RoomLocation

Get a new room location that is in the given direction.

Parameters:

direction – The direction.

Returns:

The new room location.

classmethod from_text(text: str) RoomLocation

Create a room location from a given text.

Parameters:

text – The text to parse.

Returns:

The room location.

translated(delta_x: int, delta_y: int) RoomLocation

Get a translated room location, with the given delta.

Parameters:
  • delta_x – The delta in the X direction.

  • delta_y – The delta in the Y direction.

Returns:

The new room location.

x: int = 0

The x coordinate of the room.

y: int = 0

The y coordinate of the room.

class erbsland_maze.RoomSize(width: int = 0, height: int = 0)

The size of a room in room units.

classmethod from_text(text: str) Self

Create a room size from the given text.

The text can be a name like “single”, “small”, “medium” or “large”, or a single positive integer, or two integer seperated by a comma.

Parameters:

text – The text to parse.

Returns:

A room size.

class erbsland_maze.Wall(location: RoomLocation, direction: Direction)

A wall in a room.

direction: Direction

The direction at this location where the wall is.

location: RoomLocation

The location in the room where the wall is.

matches_closing_type(closing_type: ClosingType, grid: LocationGrid) bool

Test if this wall matches the closing type in relation to the given room grid.

Parameters:
  • closing_type – The closing type.

  • grid – The room grid.

Returns:

True if the wall matches the closing type.

class erbsland_maze.RoomInsets(north: int = 0, east: int = 0, south: int = 0, west: int = 0)

Room location insets.

classmethod from_text(text: str) Self

Create an inset definition from the given text.

The text can be a single positive integer, or two, or four integer seperated by a comma.

Parameters:

text – The text to parse.

Returns:

A room size.

static value_from_text(text: str) int

Convert a text into an inset value.

Parameters:

text – The text to convert.

Returns:

The resulting value.

class erbsland_maze.RoomOffset(x: int = 0, y: int = 0, is_relative: bool = False)
is_relative: bool = False

A relative positive offset moves the room from the placement towards the center.

property is_zero: bool

Test if this offset does nothing.

translate_location(location: RoomLocation, placement: Placement) RoomLocation

Return a new location with this offset and placement.

Parameters:
  • location – The start location.

  • placement – The placement.

Returns:

The new location with this offset applied.

x: int = 0

The offset in x direction, or the length for a relative offset.

y: int = 0

The offset in y direction. Ignored for relative offsets.

class erbsland_maze.LocationGrid(location: RoomLocation, size: RoomSize)

A rectangular grid of room locations.

all_frame_locations(insets: RoomInsets = RoomInsets(north=1, east=1, south=1, west=1)) list[RoomLocation]

Get all locations in this grid that are part of the frame.

Parameters:

insets – The insets of the frame.

Returns:

All locations that are part of the frame.

all_locations()

Get all locations in this room grid.

contains(location: RoomLocation) bool

Test if the given location is part of this grid.

Parameters:

location – The tested location.

Returns:

True if it is inside of this grid.

get_corner(corner: Corner) RoomLocation

Get the location a corner.

Parameters:

corner – The corner.

Returns:

The location of the corner.

get_middle(direction: Direction) RoomLocation

Get a middle location in the given direction.

Parameters:

direction – The direction.

Returns:

The location.

is_corner(location: RoomLocation, corner: Corner = None) bool

Test if the location is a corner.

Parameters:
  • location – The location to compare.

  • corner – A specific corner, or None for any corner.

Returns:

True if the location is a corner.

is_frame(location: RoomLocation, insets: RoomInsets = RoomInsets(north=1, east=1, south=1, west=1)) bool

Test if the given location is part of the frame of this grid.

Parameters:
  • location – The tested location.

  • insets – The size of the frame.

Returns:

True if the room is part of the frame.

is_middle(location: RoomLocation, direction: Direction = None) bool

Test is the given location is at the middle location in the given direction.

Parameters:
  • location – The location.

  • direction – The specific direction, or None for all directions.

Returns:

True if the given location is at the middle.

location: RoomLocation

The top left corner of the room grid.

location_for_placement_and_size(placement: Placement, size: RoomSize) RoomLocation

Get the location of the given placement.

Parameters:
  • placement – The placement.

  • size – The size of the modification.

Returns:

The location of the given placement.

size: RoomSize

The size of the room grid.

Room Layout Enumerations

class erbsland_maze.RoomType(value, names=None, *values, module=None, qualname=None, type=None, start=1, boundary=None)

The type of room.

BLANK = 2

A blank spot in the maze.

END = 3

A path end.

PATH = 1

A path through the maze.

class erbsland_maze.Direction(value, names=None, *values, module=None, qualname=None, type=None, start=1, boundary=None)

A direction in the maze.

class erbsland_maze.Corner(value, names=None, *values, module=None, qualname=None, type=None, start=1, boundary=None)

A corner of a room or maze.

BOTTOM_LEFT = 'bottom_left'

The bottom left corner.

BOTTOM_RIGHT = 'bottom_right'

The bottom right corner.

TOP_LEFT = 'top_left'

The top left corner.

TOP_RIGHT = 'top_right'

The top right corner.

opposite()

Get the opposite corner.

The Graphical Layout Classes

class erbsland_maze.Layout

The base class for all graphical layout classes.

abstract get_dimension_info() str

Get dimensional information to be displayed on the console after initialization.

Returns:

Text that contains the most useful dimension information.

abstract initialize() RoomSize

Initializes the graphical layout and calculate the number of rooms required to fill the canvas.

Returns:

The number of rooms in the X and Y axis.

abstract render_image(file_path: Path, rooms: list[Room], path_end_rooms: list[Room]) None

Render the maze into an image.

Parameters:
  • file_path – The path for the resulting image.

  • rooms – A list of rooms to render.

  • path_end_rooms – The end points in the maze to be marked.

Exception Classes

exception erbsland_maze.GeneratorError

An error occurred while generating the maze.

exception erbsland_maze.ModifierError(modifier: Modifier, message: str)

An exception raised when the origin of the problem is based on a modifier.