Difference between revisions of "Dynamically Sized Maze"
Jump to navigation
Jump to search
Line 77: | Line 77: | ||
# Command-line options | # Command-line options | ||
my $ | my $CELLS = $ARGV[0] || 250; | ||
my $ | my $SEED = $ARGV[1] || time(); | ||
srand($SEED); | srand($SEED); | ||
Line 96: | Line 95: | ||
# Variables | # Variables | ||
my $map | my $map = {}; | ||
my ( $min_x, $min_y, $max_x, $max_y ) = ( 0, 0, 0, 0 ); | |||
my $count = 0; | my $count = 0; | ||
Line 108: | Line 108: | ||
carve( 0, 0, 'E' ); | carve( 0, 0, 'E' ); | ||
draw( 0, 0 ); | draw( 0, 0 ); | ||
print "$0 $ | print "$0 $CELLS $SEED # => ( $min_x, $min_y, $max_x, $max_y )\n"; | ||
} | } | ||
Line 118: | Line 118: | ||
return if defined $map->{$y1}{$x1}; # already visited | return if defined $map->{$y1}{$x1}; # already visited | ||
$min_y = $y1 if $y1 < $min_y; | |||
$max_y = $y1 if $y1 > $max_y; | |||
$min_x = $x1 if $x1 < $min_x; | |||
$max_x = $x1 if $x1 > $max_x; | |||
$map->{$y0}{$x0}{$direction} = 1; | $map->{$y0}{$x0}{$direction} = 1; | ||
$map->{$y1}{$x1} = { $OPPOSITE{$direction} => 1 }; | $map->{$y1}{$x1} = { $OPPOSITE{$direction} => 1 }; | ||
return if $count > $ | return if $count++ > $CELLS; | ||
for my $new_direction ( shuffle @DIRECTIONS ) { | for my $new_direction ( shuffle @DIRECTIONS ) { | ||
Line 133: | Line 138: | ||
my ( $x0, $y0 ) = @_; | my ( $x0, $y0 ) = @_; | ||
for my $y ( $ | for my $y ( $min_y .. $max_y ) { | ||
my ( $line1, $line2 ) = ( '', '' ); | my ( $line1, $line2 ) = ( '', '' ); | ||
for my $x ( $ | for my $x ( $min_x .. $max_x) { | ||
if ( ref $map->{$y}{$x} ) { | if ( ref $map->{$y}{$x} ) { | ||
$line1 .= $map->{$y}{$x}{E} ? ' ' : ' ##'; | $line1 .= $map->{$y}{$x}{E} ? ' ' : ' ##'; |
Revision as of 14:43, 3 October 2014
This article will describe a technique to build mazes that can grow in all directions.
Since the maze is not limited to a specific shape, it will end up with a very organic appearance.
Example
############################ ################################## ################################ #### #################################### ############################ ## ################################## #################################### #################################### ############################ ## ################################## ################################ #### #################################### ############################ ## ## ############################## ############################ #### #### ################################ ######################## ## ## ########################## ############################ ######## #### ############################ ######################## ## ## ########################## ############################ #### ############################ ######################## ## ## ## ## ###################### ############################ #### #### #### ######################## ######################## ## ## ## ## ###################### ############################ #### ################################ ######################## ## ## ## ## ###################### ############################ #### #### #################### ############################ ## ## ## ## ## ############## ######################################## #### #### ############ #################################### ## ## ## ## ## ###### ############################################ ######## #### ######################################## ## ## ## ## #################################################### ######## #### ######## ################## ## ## ## ## ## ## ## ## ## ######## ################ #### #### #### ########## ## ## ## ## ## ## ###### ######## ############ #### #### ############ #### ###### ## ## ## ## ## ############## ######## ############ ######## ############ #################### ###### ## ############## ## ###################### #### #################### ################################################ ###### ############################################## #### ############ #################################################### #### ## ## ## ## ################################################## ######## #### #################################################### #### ################################################## ######## #################################################### ######## ## ## ## ## ###################################################### ################################################################################
Algorithm
The algorithm is a simple recursive backtracker, starting at position {0,0} and growing into all directions.
Since the maze can become very sparse, we use a two-dimensional hash table, instead of an array, to store the cells.
- Start carving from (x0,y0) into a specific direction.
- Calculate the new coordinates (x1,y1).
- Return if the new position was already visited.
- Repeat the process from the new position, trying all possible directions.
In the normal algorithm the process will terminate when all coordinates were visited.
In our case, however, there is no limit for growth, so we must return after the number of cells reaches the limit.
Source code
#!/usr/bin/env perl
use strict;
use warnings;
no warnings 'recursion';
use List::Util qw/shuffle/;
# Command-line options
my $CELLS = $ARGV[0] || 250;
my $SEED = $ARGV[1] || time();
srand($SEED);
# Constants
my @DIRECTIONS = qw/N S E W/;
my %DELTA = (
y => { N => -1, S => 1, E => 0, W => 0 },
x => { N => 0, S => 0, E => 1, W => -1 },
);
my %OPPOSITE = ( N => 'S', S => 'N', E => 'W', W => 'E', );
# Variables
my $map = {};
my ( $min_x, $min_y, $max_x, $max_y ) = ( 0, 0, 0, 0 );
my $count = 0;
###
main();
###
sub main {
carve( 0, 0, 'E' );
draw( 0, 0 );
print "$0 $CELLS $SEED # => ( $min_x, $min_y, $max_x, $max_y )\n";
}
sub carve {
my ( $x0, $y0, $direction ) = @_;
my $x1 = $x0 + $DELTA{x}{$direction};
my $y1 = $y0 + $DELTA{y}{$direction};
return if defined $map->{$y1}{$x1}; # already visited
$min_y = $y1 if $y1 < $min_y;
$max_y = $y1 if $y1 > $max_y;
$min_x = $x1 if $x1 < $min_x;
$max_x = $x1 if $x1 > $max_x;
$map->{$y0}{$x0}{$direction} = 1;
$map->{$y1}{$x1} = { $OPPOSITE{$direction} => 1 };
return if $count++ > $CELLS;
for my $new_direction ( shuffle @DIRECTIONS ) {
carve( $x1, $y1, $new_direction );
}
}
sub draw {
my ( $x0, $y0 ) = @_;
for my $y ( $min_y .. $max_y ) {
my ( $line1, $line2 ) = ( '', '' );
for my $x ( $min_x .. $max_x) {
if ( ref $map->{$y}{$x} ) {
$line1 .= $map->{$y}{$x}{E} ? ' ' : ' ##';
$line2 .= $map->{$y}{$x}{S} ? ' ' : '####';
}
else {
$line1 .= '####';
$line2 .= '####';
}
}
print "$line1\n$line2\n";
}
}