Random Maze

Taking a short break from the adventure game posts to show how to create simple random mazes in BB4W.  This algorithm was described to me by Clive Fraser, and thanks to Amy for showing me her implementation of it.

The algorithm creates a random maze on screen and garantees a complete path from start to finish.

In pseudocode it works as follows:

START WITH A GRID CONTAIN NOTHING BUT WALLS
TAKE A RANDOM START POINT AND CHANGE TO A PATH
REPEATEDLY
    CHOOSE A RANDOM POSITION
    IF THE  POSITION HAS ONLY ONE CONNECTED PATH
        CHANGE IT TO A PATH

A random maze with start and end points.

Source code follows:
   
     REM Random Maze
     REM Timothy Goliath Street
     REM 2014/12/18
     
MODE 10 : OFF

     
REM setup the maze grid
     
MAZE_WIDTH% = 60
     MAZE_HEIGHT% = 48
     CELL_WIDTH% = 24
     MAZE_COUNTER_LIMIT% = MAZE_WIDTH% * MAZE_HEIGHT% * 30

     DIM Maze%( MAZE_WIDTH%-1, MAZE_HEIGHT%-1 )

     REPEAT

       PRINT
"Creating a maze..."
       PROC_createMaze( Maze%() )

       PROC_drawMaze( Maze%() )

       REPEAT
         MOUSE
xclick%, yclick%, click%
       UNTIL  click% >0


       PROC_clearMaze( Maze%() )

     UNTIL FALSE
     END



     
DEFPROC_createMaze( m%() )
     LOCAL x%, y%
     LOCAL mazeCounter%
     REM create one empty space
     REM somewhere in the grid

     
x% = RND(MAZE_WIDTH%-2)
     y% = RND(MAZE_HEIGHT%-2)
     m%(x%,y%) = 1 : REM set to path

     
REPEAT
       
x% = RND(MAZE_WIDTH%-2)
       y% = RND(MAZE_HEIGHT%-2)

       REM look around this space and
       REM create a path if it links
       REM to only one other space

       
IF m%(x%,y%+1)+m%(x%,y%-1)+m%(x%-1,y%)+m%(x%+1, y%) = 1 THEN
         
m%(x%,y%) = 1 : REM set to path
       
ENDIF

       
REM increment the maze pointer
       
mazeCounter% += 1

     UNTIL mazeCounter% = MAZE_COUNTER_LIMIT%

     REM place an exit on the top row
     
PROC_placeExit( m%(), MAZE_HEIGHT%-1, MAZE_HEIGHT%-2, -1)

     REM place an exit on the bottom row
     
PROC_placeExit( m%(), 0, 1, 1 )
     ENDPROC


     
DEFPROC_placeExit( m%(), placeRow%, checkRow%, dir% )
     REM looks through the row for the first opportunity to
     REM place an exit that joins up with existing path
     REM dir -1 = check from right hand side
     REM dir  1 = check from left hand side
     
LOCAL x%
     LOCAL found%
     IF dir% = -1 THEN x% = MAZE_WIDTH%-1
     WHILE NOTfound% AND x%<MAZE_WIDTH% AND x%>-1
       IF m%(x%, checkRow% ) = 1 THEN
         
found% = TRUE
         
m%(x%, placeRow%) = 1 : REM place exit
       
ELSE
         
x% += dir%
       ENDIF
     ENDWHILE
     ENDPROC



     
DEFPROC_drawMaze( m%() )
     LOCAL x%, y%
     COLOUR 2, 0, 45, 178 : COLOUR 130 : CLS
     COLOUR
1, 0, 255, 30 : GCOL 0, 1

     OSCLI "refresh off"
     FOR x% = 0 TO MAZE_WIDTH%-1
       FOR y% = 0 TO MAZE_HEIGHT% -1
         IF m%(x%,y%)= 0 THEN
           RECTANGLE FILL
x%*CELL_WIDTH%,  y%*CELL_WIDTH%, CELL_WIDTH%, CELL_WIDTH%
         ENDIF
       NEXT
     NEXT
     OSCLI
"refresh"
     ENDPROC



     
DEFPROC_clearMaze( m%() )
     LOCAL x%, y%

     FOR x% = 0 TO MAZE_WIDTH%-1
       FOR y% = 0 TO MAZE_HEIGHT% -1
         m%(x%, y%) = 0
       NEXT
     NEXT
     ENDPROC


The second program works similarly, however there is some additonal code for producing chambers within the maze.  Students might like to take this code to produce the start an adventure game, or a pac-man-esque game.

Random maze with chambers.

Source code follows:

     REM Random Maze 02
     REM Timothy Goliath Street
     REM 2014/12/18
     
MODE 10 : OFF

     
REM setup the maze grid
     
MAZE_WIDTH% = 60
     MAZE_HEIGHT% = 48
     CELL_WIDTH% = 24
     MAZE_COUNTER_LIMIT% = MAZE_WIDTH% * MAZE_HEIGHT% * 30

     DIM Maze%( MAZE_WIDTH%-1, MAZE_HEIGHT%-1 )

     REPEAT

       PRINT
"Creating a maze..."
       PROC_createMaze( Maze%() )

       PROC_drawMaze( Maze%() )

       REPEAT
         MOUSE
xclick%, yclick%, click%
       UNTIL  click% >0


       PROC_clearMaze( Maze%() )

     UNTIL FALSE
     END



     
DEFPROC_createMaze( m%() )
     LOCAL x%, y%
     LOCAL mazeCounter%
     LOCAL roomCounter%, numRooms%

     REM create one empty space
     REM somewhere in the grid

     
x% = RND(MAZE_WIDTH%-2)
     y% = RND(MAZE_HEIGHT%-2)
     m%(x%,y%) = 1 : REM set to path

     
REPEAT
       
x% = RND(MAZE_WIDTH%-2)
       y% = RND(MAZE_HEIGHT%-2)

       REM look around this space and
       REM create a path if it links
       REM to only one other space

       
IF m%(x%,y%+1)+m%(x%,y%-1)+m%(x%-1,y%)+m%(x%+1, y%) = 1 THEN
         
m%(x%,y%) = 1 : REM set to path
       
ENDIF

       
REM increment the maze pointer
       
mazeCounter% += 1

     UNTIL mazeCounter% = MAZE_COUNTER_LIMIT%

     REM place some rooms
     
numRooms% = RND(MAZE_WIDTH%) + RND(MAZE_HEIGHT%)
     WHILE roomCounter% < numRooms%
       x% = RND(MAZE_WIDTH%-2)
       y% = RND(MAZE_HEIGHT%-2)
       IF m%(x%,y%+1)+m%(x%,y%-1)+m%(x%-1,y%)+m%(x%+1, y%) > 3 THEN
         
m%(x%,y%) = 1 : REM set to path
         
roomCounter% += 1
       ENDIF

     ENDWHILE

     
REM place an exit on the top row
     
PROC_placeExit( m%(), MAZE_HEIGHT%-1, MAZE_HEIGHT%-2, -1)

     REM place an exit on the bottom row
     
PROC_placeExit( m%(), 0, 1, 1 )
     ENDPROC


     
DEFPROC_placeExit( m%(), placeRow%, checkRow%, dir% )
     REM looks through the row for the first opportunity to
     REM place an exit that joins up with existing path
     REM dir -1 = check from right hand side
     REM dir  1 = check from left hand side
     
LOCAL x%
     LOCAL found%
     IF dir% = -1 THEN x% = MAZE_WIDTH%-1
     WHILE NOTfound% AND x%<MAZE_WIDTH% AND x%>-1
       IF m%(x%, checkRow% ) = 1 THEN
         
found% = TRUE
         
m%(x%, placeRow%) = 1 : REM place exit
       
ELSE
         
x% += dir%
       ENDIF
     ENDWHILE
     ENDPROC



     
DEFPROC_drawMaze( m%() )
     LOCAL x%, y%
     COLOUR 2, 0, 45, 178 : COLOUR 130 : CLS
     COLOUR
1, 0, 255, 30 : GCOL 0, 1

     OSCLI "refresh off"
     FOR x% = 0 TO MAZE_WIDTH%-1
       FOR y% = 0 TO MAZE_HEIGHT% -1
         IF m%(x%,y%)= 0 THEN
           RECTANGLE FILL
x%*CELL_WIDTH%,  y%*CELL_WIDTH%, CELL_WIDTH%, CELL_WIDTH%
         ENDIF
       NEXT
     NEXT
     OSCLI
"refresh"
     ENDPROC



     
DEFPROC_clearMaze( m%() )
     LOCAL x%, y%

     FOR x% = 0 TO MAZE_WIDTH%-1
       FOR y% = 0 TO MAZE_HEIGHT% -1
         m%(x%, y%) = 0
       NEXT
     NEXT
     ENDPROC