Difference between revisions of "Dynamically Sized Maze"

From RogueBasin
Jump to navigation Jump to search
 
(15 intermediate revisions by 5 users not shown)
Line 1: Line 1:
This article will describe a technique to build mazes that can grow in all directions, creating a very organic appearance.
This article will describe a technique to build mazes that can grow without limits in all directions, differently from a traditional maze that is bounded by its outer walls.


=== Example ===
=== Example ===
Line 5: Line 5:
<pre>
<pre>
$ ./dynamic.pl 250 1419689539
$ ./dynamic.pl 250 1419689539
########## ! ! ! !################################
######################################################
########_    _  _!##############################
############ ! ! ! !##################################
##########_!_!  _ _! !############################
##########_    _  _!################################
##########_  _!  _  _! !########################
############_!_! _ _! !##############################
##########_   ! ! !_ _   _!######################
############_  _!  _   _! !##########################
############_! ! !   !  _ _!###### ! ! ! !########
############_   ! ! !_ _  _!########################
##########_  _!_ _! !_  _!## ! !  !    _! ! !##
##############_! ! !  _ _!###### ! ! ! !##########
##########_     _! _!  _ _!_  _ _!_ _!_ _ _  _!
############_  _!_ _! !_   _!## ! !  !    _! ! !####
############_!  _!  _!    _!_  ! ! !  _ _  !  _ _!
############_    _! _!  _ _!_  _ _!_ _!_ _ _   _!##
############_ ! _ _!__! !_ _ ! ! _! !   _!
##############_! _! _!   _!_  ! ! ! _ _ ! _ _!##
############_  !  _!##_  _! _ ! ! _ _!_! _!
##############_ _ _!_!  _! !_ _ ! ! _! !   _!##
############_    _!##_     !  _! ! !_ !   _!
##############_ !  _!##_  _!  _  ! ! !  _ _!_!  _!##
##############_!_!######_!_!_!_  ! ! !_   !   _!
##############_    _!##_      !  _! ! !_  !  !  _!##
##########################_   _ _! !  _!_!_!_!_!##
################_!_!######_!_!_!_  ! ! !_    !    _!##
##########################_       !  _!##########
############################_   _ _! !  _!_!_!_!_!####
############################_!_!_!_!  _!##########
############################_        !  _!############
################################_     _!##########
##############################_!_!_!_!  _!############
###################### ! ! ! ! !_ !_!############
##################################_     _!############
################## !_ _ _ ! _ !  _!############
######################## ! ! ! ! !_ !_!##############
################_     _  ! !_  !    _! ! !########
#################### !_ _ !  _ !  _!##############
################!! !! !_!_! _  _!######
##################_     ! !_ !   _! ! !##########
################_  ! ! !_ _! ! !  !  _!  _!######
##################_ !_ ! !_ ! !_!_! _  _!########
################_ _  !_     !   !   _! _!######
##################_  ! ! !_ _! ! !  !  _!  _!########
########## ! !##_   _ _!_!_!_!_!_!_!_!_  _!######
##################_ _  !_     !   !    __!########
######## !   _! !  _!################_  _!######
############ ! !##_  _ _!_!_!_!_!_!_!_!_  _!########
###### ! _!_ _    _!##############_    _!######
########## !    _! ! _!################_   _!########
## ! ! _!   _!_!_!################_  !_!########
######## _!_ _     _!##############_    _!########
_   _ _!  _!  _!#### ! ! !########_  _ _! !######
#### ! !  _!   _!_!_!################_  !_!##########
_ _ _  ! !_ ! ! !_   _ ! ! ! !##_    _  _!####
##_  _ _!  _! _!#### ! ! !########_   _ _! !########
##_  _!  !_        !_    _ ! !##_!_!    _!####
##_ _ _  ! !_  ! ! !_  _  ! ! ! !##_    _  _!######
##_ _  !_!  !_!_!_!_!##_!_! !_  !_      !_!######
####_  _!  !_        !_    _  ! !##_!_!    _!######
####_  ! !_! _!##########_  _ _!_ !_!_!########
####_ _  !_!  !_!_!_!_!##_!_! !_  !_      !_!########
####_         _!##########_  _  !    _!##########
######_  ! !_!  _!##########_  _ _!_  !_!_!##########
######_!_!_!_!##############_!_    !_!############
######_        _!##########_  _  !    _!############
################################_!_!##############
########_!_!_!_!##############_!_    !_!##############
##################################_!_!################
######################################################
</pre>
</pre>


Line 111: Line 113:
sub output {
sub output {
     my $output = '';
     my $output = '';
     for my $y ( $min_y .. $max_y ) {
     for my $y ( $min_y - 1 .. $max_y + 1 ) {
         for my $x ( $min_x .. $max_x ) {
         for my $x ( $min_x - 1 .. $max_x + 1 ) {
             if ( ref $map->{$y}{$x} ) {
             if ( ref $map->{$y}{$x} ) {
                 $output .= $map->{$y}{$x}{S} ? ' ' : '_';
                 $output .= $map->{$y}{$x}{S} ? ' ' : '_';
Line 130: Line 132:
=== Description ===
=== Description ===


This is a simple [[recursive backtracker]] that starts at position {0,0} and can grow into all directions.
# Start carving from (x0,y0) into a random direction.
 
# Start carving from (x0,y0) into a specific direction.
# Calculate the new coordinates (x1,y1).
# Calculate the new coordinates (x1,y1).
# Return if the new position was already visited.
# Return if the new position was already visited.
# Repeat the process from the new position, trying all possible directions.
# Repeat the process from the new position, trying all possible directions.


In most mazes, the process terminates when all coordinates were visited; but in our case there is no limit for growth, so we must terminate after an arbitrary limit.
It is important to count the number of steps ($count), so we can exit the routine when the maze reaches the desired size ($CELLS). (Otherwise the maze would keep growing forever). Since the maze can become very sparse, we store the positions in a hash table instead of an array.
 
Another important point is that, since the maze can become very sparse, we use a hash table instead of an array to store the cells.


=== Usage ===
=== Usage ===
Line 265: Line 263:
</pre>
</pre>


[[Category:Developing]]
=== Advantages ===
 
The main advantage of this technique is that, since cells are stored in a hash, memory usage grows only one cell at a time.
 
It is also easily customizable because this technique is just one (recursive backtracker covered by one cell long dead-ends) of the many possible implementations of the Growing tree algorithm described by Walter D. Pullen on Think Labyrinth [http://www.astrolog.org/labyrnth/algrithm.htm]. With only a few minor changes, the results can be very different. "For example, if you always pick the most recent cell added to it, this algorithm turns into the recursive backtracker. If you always pick cells at random, this will behave similarly but not exactly to Prim's algorithm. If you always pick the oldest cells added to the list, this will create Mazes with about as low a river factor as possible, even lower than Prim's algorithm. If you usually pick the most recent cell, but occasionally pick a random cell, the Maze will have a high river factor but a short direct solution. If you randomly pick among the most recent cells, the Maze will have a low river factor but a long windy solution."
 
If you remove the inner walls, you get a [[Dynamically Sized Cave]].
 
[[Category:Developing]][[Category:Articles]][[Category:Maps]]

Latest revision as of 15:25, 14 April 2021

This article will describe a technique to build mazes that can grow without limits in all directions, differently from a traditional maze that is bounded by its outer walls.

Example

$ ./dynamic.pl 250 1419689539
######################################################
############ ! ! ! !##################################
##########_     _   _!################################
############_!_!  _ _! !##############################
############_   _!  _   _! !##########################
############_    ! ! !_ _   _!########################
##############_! ! !   !  _ _!###### ! ! ! !##########
############_   _!_ _! !_   _!## ! !   !    _! ! !####
############_     _!  _!  _ _!_   _ _!_ _!_ _ _   _!##
##############_!  _!  _!    _!_  ! ! !  _ _  !  _ _!##
##############_  !  _ _!_!  _! !_ _  ! !  _! !    _!##
##############_  !  _!##_   _!  _  ! ! !  _ _!_!  _!##
##############_     _!##_      !  _! ! !_  !   !  _!##
################_!_!######_!_!_!_  ! ! !_    !    _!##
############################_   _ _! !  _!_!_!_!_!####
############################_        !  _!############
##############################_!_!_!_!  _!############
##################################_     _!############
######################## ! ! ! ! !_  !_!##############
#################### !_ _ _  !  _  !  _!##############
##################_     _  ! !_  !    _! ! !##########
##################_  !_  ! !_  ! !_!_!  _   _!########
##################_  ! ! !_ _! ! !   !  _!  _!########
##################_ _  !_      !   !    _!  _!########
############ ! !##_   _ _!_!_!_!_!_!_!_!_   _!########
########## !    _! !  _!################_   _!########
######## !  _!_ _     _!##############_     _!########
#### ! !  _!    _!_!_!################_  !_!##########
##_   _ _!  _!  _!#### ! ! !########_   _ _! !########
##_ _ _  ! !_  ! ! !_   _  ! ! ! !##_     _   _!######
####_   _!   !_        !_     _  ! !##_!_!    _!######
####_ _  !_!   !_!_!_!_!##_!_! !_  !_      !_!########
######_  ! !_!  _!##########_   _ _!_  !_!_!##########
######_         _!##########_   _  !    _!############
########_!_!_!_!##############_!_    !_!##############
##################################_!_!################
######################################################

Source code

#!/usr/bin/env perl

use strict;
use warnings;

no warnings 'uninitialized';
no warnings 'recursion';

use List::Util qw/min max shuffle/;

# Command-line options

my $CELLS = $ARGV[0] || 250;
my $SEED  = $ARGV[1] || time();

srand($SEED);

# Constants

my %DIRECTIONS = (
    N => { dy => -1, opposite => 'S' },
    S => { dy => 1,  opposite => 'N' },
    E => { dx => 1,  opposite => 'W' },
    W => { dx => -1, opposite => 'E' },
);

# Variables

my $map = {};
my ( $min_x, $min_y, $max_x, $max_y ) = ( 0, 0, 0, 0 );
my $count = 0;

# Code

carve( 0, 0, 'E' );
print output();

print "$0 $CELLS $SEED # => ( $min_x, $min_y, $max_x, $max_y )\n";

sub carve {
    my ( $x0, $y0, $direction ) = @_;

    my $x1 = $x0 + $DIRECTIONS{$direction}{dx};
    my $y1 = $y0 + $DIRECTIONS{$direction}{dy};

    return if defined $map->{$y1}{$x1};    # already visited

    $min_x = min( $min_x, $x1 );
    $max_x = max( $max_x, $x1 );

    $min_y = min( $min_y, $y1 );
    $max_y = max( $max_y, $y1 );

    $map->{$y0}{$x0}{$direction} = 1;
    $map->{$y1}{$x1}{ $DIRECTIONS{$direction}{opposite} } = 1;

    return if $count++ > $CELLS;

    for my $new_direction ( shuffle keys %DIRECTIONS ) {
        carve( $x1, $y1, $new_direction );
    }
}

sub output {
    my $output = '';
    for my $y ( $min_y - 1 .. $max_y + 1 ) {
        for my $x ( $min_x - 1 .. $max_x + 1 ) {
            if ( ref $map->{$y}{$x} ) {
                $output .= $map->{$y}{$x}{S} ? ' ' : '_';
                $output .= $map->{$y}{$x}{E} ? ' ' : '!';
            }
            else {
                $output .= '##';
            }
        }
        $output .= "\n";
    }
    return $output;
}

Description

  1. Start carving from (x0,y0) into a random direction.
  2. Calculate the new coordinates (x1,y1).
  3. Return if the new position was already visited.
  4. Repeat the process from the new position, trying all possible directions.

It is important to count the number of steps ($count), so we can exit the routine when the maze reaches the desired size ($CELLS). (Otherwise the maze would keep growing forever). Since the maze can become very sparse, we store the positions in a hash table instead of an array.

Usage

$ ./dynamic.pl [length] [random seed]

More examples

$ ./dynamic.pl 250 1419687187
################################## ! !########################################
################################_    ! ! ! !##################################
################################_  !_ _ _   _!################################
################################_  !  _!  _ _!################################
################################_  !  _   _!##################################
##############################_   _!  _!_!#################### ! ! !##########
##############################_  ! ! ! !####################_       _! !######
##############################_  !_ _  ! !####################_!_!      _!####
##############################_      !_   _!################_    !_!_!  _! !##
############################## !_!_! !  _ _!###### ! ! !##_   _!_  !_ _ _   _!
############################_   _ _ _!  _!######_   _   _!_    !_  !_   _   _!
##########################_   _!  _ _! ! ! !## !_  !  _ _!##_!_  ! !_ _  !_!##
##########################_  !  _  !  _ _  !_   _ _!  _! !_   _ _! !_     _!##
##########################_  !_ _!_ _!   !_    !_   _!_   _ _!  _ _!   !_!####
########################_   _!  _      !   !_!_!_  !    _!  _ _!_   _!_!######
########################_       _!_!_!_!_!    _!_    !_!_ _  !   !_ _   _!####
##########################_!_!_!######## !_!  _!##_!_!####_    !        _!####
################################ ! ! !_       _!############_!_!_!_!_!_!######
##############################_   _  !   !_!_!################################
################## ! !########_  ! ! ! !_!####################################
################_    ! ! !##_   _!_ _ _ _! ! !################################
################_  !  _   _!_   _ _   _ _ _   _!##############################
################_  !_! !  _!##_!   !_ _ _ _!  _!##############################
################_  !_   _! ! !_  !      _  !_   _!############################
############ ! !_ _  !    _  !_  !_!_!_!_       _!############################
##########_    !_    !_!_!_       _!######_!_!_!##############################
##########_  !     !_!######_!_!_!############################################
#### ! !##_  !_!_!_!##########################################################
##_    !_     _!##############################################################
_   _!_    !_!################################################################
_ _ _  !_!_!##################################################################
_    !  _!####################################################################
_  !_   _!####################################################################
_ _  !_!######################################################################
_     _!######################################################################
##_!_!########################################################################
$ ./dynamic.pl 250 1419687341
######## ! !############################################################
#### !_     _!##########################################################
##_   _ _! ! ! ! !######################################################
_   _!_ _     _  ! ! ! ! !##############################################
_        !_!_!_     _ _  ! ! ! !########################################
##_!_!_!  _! !##_!_!_   _!  _   _!######################################
######_       _!####_  !  _!  _! ! !####################################
########_!_!    _!##_   _!  _!  _   _!#################### ! !##########
############_!    _!##_!_  !_  !  _ _!########## ! !####_    ! ! ! ! !##
############ !_!  _!####_   _ _!  _!##########_    ! ! !_  !          _!
######## !_       _!######_!_   _ _!#### ! ! !_  !  _ _  ! !_!_!_!_!_!##
######_      !_!_!########## !_   _!##_   _  !   !_!_   _!  _!##########
####_    !_!_!######## ! ! !   !  _!##_    !   !_!##_       _!##########
####_  !_! !########_   _ _ _!_ _ _!####_! !_!_!######_!_!_!############
####_       _!######_   _  ! ! ! ! ! ! !_   _!##########################
######_!_!  _!########_!_           _  !    _!##########################
########_   _!############_!_!_!_!_!_    !_!############################
######_   _ _!#### ! ! ! ! !##########_!_!##############################
######_ _   _!##_   _       _!##########################################
####_   _ _ _! ! !  _!_!_!  _!##########################################
####_ _ _  !  _   _ _! !_   _!##########################################
####_     _!  _!_!  _       _!##########################################
####_  !_!  _ _!_    !_!_!_!############################################
####_       _!####_!  _!################################################
######_!_!_!####_   _ _!################################################
################_ _   _!################################################
###### ! ! ! !##_     _!################################################
## !_     _  !_   _!_!##################################################
_   _  !_!_  !_ _ _   _!################################################
_    !_! !_  !  _  !  _!################################################
##_!    _  ! ! ! ! !  _! !##############################################
####_!_!  _!_ _!  _!  _   _!############################################
######_ _!   !  _!_ _!    _!############################################
####_   _ _!_ _!_    ! !_!##############################################
####_   _  !   !_  !    _!##############################################
######_!_  ! ! !   !_!_!################################################
########_  ! !   !_!####################################################
########_    !_!_!######################################################
##########_!_!##########################################################
$ ./dynamic.pl 250 1419687903
############## ! !######################################################################################################
########## ! !    _!####################################################################################################
########_   _ _!  _!####################################################################################################
########_ _ _  !_   _! !######################################################## ! !####################################
########_   _ _ _! !    _!############################################## ! ! ! !    _!##################################
########_  ! !  _ _! !  _! ! ! !###### ! ! ! !######## ! ! ! !######## !   !  _  !    _! ! ! ! !########################
########_ _  !_  !  _! !   !    _! ! !  _     _! !##_   _ _   _!####_   _!    _!_!_!            _!######################
######## ! ! !  _!  _! ! !_  !     !  _ _!_!      _! !  _!  _ _!####_   _!_!_!######_!_!_!_!_!    _!########## ! ! ! !##
###### !  _ _!_ _ _ _!    _!_!_!_!    _!####_!_!        _! !    _!##_  ! ! !##################_!    _! !####_   _ _   _!
####_   _! ! ! ! ! !##_!_!########_!_!##########_!_!_!_!_    !    _! ! !    _!##################_!      _! !_   _!    _!
####_ _ _  !  _ _   _!####################################_!_!_!     !_ _!  _!####################_!_!      _! !  _!_!##
######_   _!_  ! !  _!##########################################_!_!        _!########################_!_! !  _!_   _!##
###### !_ _ _ _!  _ _!##############################################_!_!_!_!############################_    !      _!##
## ! !    _!   !_   _!################################################################################## !_!_! !_!_!####
_      !     ! !  _ _!################################################################################_    !    _!######
##_!_!_!_!_!_!    _!################################################################################_   _! ! !_!########
##############_!_!##################################################################################_  !_     _!########
####################################################################################################_    !_!_! ! !######
######################################################################################################_! !   !    _!####
######################################################################################################_    ! ! !  _!####
################################################################################################ ! ! !##_!_!_  !_   _!##
##############################################################################################_   _  !_   _  ! ! !  _!##
################################################################################################_!_      !_  ! ! !  _!##
####################################################################################################_!_!_! ! ! ! !  _!##
########################################################################################################_   _! ! !  _!##
########################################################################################################_ _  !_  !  _!##
##########################################################################################################_ _  !_!  _!##
############################################################################################################_       _!##
##############################################################################################################_!_!_!####

Advantages

The main advantage of this technique is that, since cells are stored in a hash, memory usage grows only one cell at a time.

It is also easily customizable because this technique is just one (recursive backtracker covered by one cell long dead-ends) of the many possible implementations of the Growing tree algorithm described by Walter D. Pullen on Think Labyrinth [1]. With only a few minor changes, the results can be very different. "For example, if you always pick the most recent cell added to it, this algorithm turns into the recursive backtracker. If you always pick cells at random, this will behave similarly but not exactly to Prim's algorithm. If you always pick the oldest cells added to the list, this will create Mazes with about as low a river factor as possible, even lower than Prim's algorithm. If you usually pick the most recent cell, but occasionally pick a random cell, the Maze will have a high river factor but a short direct solution. If you randomly pick among the most recent cells, the Maze will have a low river factor but a long windy solution."

If you remove the inner walls, you get a Dynamically Sized Cave.