[Date Prev][Date Next][Thread Prev][Thread Next] [Search] [Date Index] [Thread Index]

[FWP] Maze



Hi there,

I'm trying to create a maze. The result is to be in a 2
dimensions array, and the values are either WALL (=1) or
FREE (=0). Of course, I want the maze to have a solution...
And I also want it to be kind of difficult to solve.
I haven't found anything such as an algorithm to create such
a maze, so here is how I have done : I create some "snakes"
that go randomly north, south, east or west if they can, and
if there is already a wall in every direction, they go back
one step, as long as they can. When they can't go back one
step and still can't go in a new direction, I destroy the
snake. When all the snakes are dead, my maze is created.

A little drawing is better than a long speech, so the file
attached contains a modified version that draws the maze
during its creation in a canvas.


Here is the code of the non-Tk version.
-- maze.pl --
#!/usr/bin/perl -w

use strict;
use Snake;

use constant NORTH => 1; use constant EAST  => 2;
use constant SOUTH => 4; use constant WEST  => 8;
use constant FREE => 0;  use constant WALL  => 1;
use constant NB_COLS => 51; use constant NB_ROWS => 51;
use constant NB_SNAKES => 15;
my @Maze;       # The 2 dims array.

create_maze();
print "Maze created\n";
exit(0);

sub find_free_neighbours {
    my ($j, $i) = @_;
    my @free = ();
    $i > 2         && $Maze[$i-2][$j] != WALL and push
@free, NORTH;
    $i < NB_ROWS-3 && $Maze[$i+2][$j] != WALL and push
@free, SOUTH;
    $j > 2         && $Maze[$i][$j-2] != WALL and push
@free, WEST;
    $j < NB_COLS-3 && $Maze[$i][$j+2] != WALL and push
@free, EAST;
    return @free;
}

sub create_maze {
    my @snakes;

    { # Clear the actual maze
        for ( my $i = 0 ; $i < NB_ROWS ; $i++) {
            for ( my $j = 0 ; $j < NB_COLS ; $j++) {
                $Maze[$i][$j] = FREE;
            }
        }
    }


    { # Create the snakes.
        for ( my $i = 0; $i < NB_SNAKES ; $i++ ) {
            my ($x, $y);
            do {
                $x = int( rand((NB_COLS-4)/2) ) * 2 + 2;
                $y = int( rand((NB_ROWS-4)/2) ) * 2 + 2;
            } while ($Maze[$y][$x] != FREE);
            my $snake = new Snake($x, $y);
            push @snakes, $snake;
            $Maze[$y][$x] = WALL;
        }
    }


    { # Create the new maze.
        while ( my $snake = shift @snakes ) { # Cycle
through the snakes.
            my $x = $snake->get_pos_X();
            my $y = $snake->get_pos_Y();
            my @allowed = find_free_neighbours($x, $y);
            my $dir = $snake->move(@allowed);

            if ( defined($dir) ) {
                # The snake can move : we store it again.
                push @snakes, $snake;
              switch: {
                  $dir == NORTH && do {
                      $Maze[$y-1][$x] = WALL;
                      $Maze[$y-2][$x] = WALL;
                      last;
                  };
                  $dir == SOUTH && do {
                      $Maze[$y+1][$x] = WALL;
                      $Maze[$y+2][$x] = WALL;
                      last;
                  };
                  $dir == EAST  && do {
                      $Maze[$y][$x+1] = WALL;
                      $Maze[$y][$x+2] = WALL;
                      last;
                  };
                  $dir == WEST  && do {
                      $Maze[$y][$x-1] = WALL;
                      $Maze[$y][$x-2] = WALL;
                      last;
                  };
              }

            }
        }
    }
}
-- end of maze.pl --
-- Snake.pm --
package Snake;

use strict;
use constant NORTH => 1; use constant EAST  => 2;
use constant SOUTH => 4; use constant WEST  => 8;

sub new {
    my $proto = shift;
    my $class = ref($proto) || $proto;
    my $self  = {};
    bless ($self, $class);

    # Initialisation stuff...
    $self->{X} = shift;
    $self->{Y} = shift;
    $self->{PATH}  = ();

    return $self;
}

sub get_pos_X { return $_[0]->{X}; }
sub get_pos_Y { return $_[0]->{Y}; }

sub move {
    my $self = shift;
    my @allowed = @_;
    my $dir;

    if ( $#allowed == -1 ) {
        # Can't move one step back.
        $#{ $self->{PATH} } == -1 and return undef;

        # No more free neighbours : move one step back.
        $dir = pop @{ $self->{PATH} };
        $dir = ($dir << 2) % (NORTH|EAST|SOUTH|WEST);

    } else {
        # Choose randomly a new direction in those allowed.
        $dir = $allowed[ int(rand(@allowed)) ];
        push @{ $self->{PATH} }, $dir;
    }

    # Update the snake coordinates.
    $dir == WEST  and $self->{X} -= 2;
    $dir == EAST  and $self->{X} += 2;
    $dir == NORTH and $self->{Y} -= 2;

    $dir == SOUTH and $self->{Y} += 2;

    return $dir;
}

1;
-- end of Snake.pm


What do you think of this way of creating a maze ? How would
*you* have done if you were to create a maze ? (Do you know
an algorithm that create a maze ?)
And of course, I'm always happy to know of your
optimisations on my code...

Jerome
--
jerome.quelin@insalien.org

maze.tgz