Welcome, guest | Sign In | My Account | Store | Cart

Random maze generator using depth-first search.

It starts the maze path from a random cell and there is no exit defined but actually any 2 cells on the path (white cells) can be assigned to be entry and exit locations. (I could just add code to randomly select 2 white cells and change their colors to something else but I thought it looks better this way.)

Python, 43 lines
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# Random Maze Generator using Depth-first Search
# http://en.wikipedia.org/wiki/Maze_generation_algorithm
# FB - 20121214
import random
from PIL import Image
imgx = 500; imgy = 500
image = Image.new("RGB", (imgx, imgy))
pixels = image.load()
mx = 50; my = 50 # width and height of the maze
maze = [[0 for x in range(mx)] for y in range(my)]
dx = [0, 1, 0, -1]; dy = [-1, 0, 1, 0] # 4 directions to move in the maze
color = [(0,0, 0), (255, 255, 255)] # RGB colors of the maze
# start the maze from a random cell
stack = [(random.randint(0, mx - 1), random.randint(0, my - 1))]

while len(stack) > 0:
    (cx, cy) = stack[-1]
    maze[cy][cx] = 1
    # find a new cell to add
    nlst = [] # list of available neighbors
    for i in range(4):
        nx = cx + dx[i]; ny = cy + dy[i]
        if nx >= 0 and nx < mx and ny >= 0 and ny < my:
            if maze[ny][nx] == 0:
                # of occupied neighbors must be 1
                ctr = 0
                for j in range(4):
                    ex = nx + dx[j]; ey = ny + dy[j]
                    if ex >= 0 and ex < mx and ey >= 0 and ey < my:
                        if maze[ey][ex] == 1: ctr += 1
                if ctr == 1: nlst.append(i)
    # if 1 or more neighbors available then randomly select one and move
    if len(nlst) > 0:
        ir = nlst[random.randint(0, len(nlst) - 1)]
        cx += dx[ir]; cy += dy[ir]
        stack.append((cx, cy))
    else: stack.pop()

# paint the maze
for ky in range(imgy):
    for kx in range(imgx):
        pixels[kx, ky] = color[maze[my * ky / imgy][mx * kx / imgx]]
image.save("Maze_" + str(mx) + "x" + str(my) + ".png", "PNG")

4 comments

Matt Jones 11 years, 5 months ago  # | flag

This would have been easier to read if you used more meaningful variable names. Looks good otherwise.

FB36 (author) 11 years, 4 months ago  # | flag

This is the recursive version (so it uses a stack implicitly instead of explicitly):

# Random Maze Generator using Depth-first Search (Recursive Version)
# http://en.wikipedia.org/wiki/Maze_generation_algorithm
# FB - 20121205
import random
from PIL import Image
imgx
= 512; imgy = 512
image
= Image.new("RGB", (imgx, imgy))
pixels
= image.load()
mx
= 40; my = 40 # width and height of the maze
maze
= [[0 for x in range(mx)] for y in range(my)]
dx
= [0, 1, 0, -1]; dy = [-1, 0, 1, 0] # directions to move in the maze

def GenerateMaze(cx, cy):
    maze
[cy][cx] = 1
   
while True:
       
# find a new cell to add
        nlst
= [] # list of available neighbors
       
for i in range(4):
            nx
= cx + dx[i]; ny = cy + dy[i]
           
if nx >= 0 and nx < mx and ny >= 0 and ny < my:
               
if maze[ny][nx] == 0:
                   
# of occupied neighbors of the candidate cell must be 1
                    ctr
= 0
                   
for j in range(4):
                        ex
= nx + dx[j]; ey = ny + dy[j]
                       
if ex >= 0 and ex < mx and ey >= 0 and ey < my:
                           
if maze[ey][ex] == 1: ctr += 1
                   
if ctr == 1: nlst.append(i)
       
# if 1 or more available neighbors then randomly select one and add
       
if len(nlst) > 0:
            ir
= nlst[random.randint(0, len(nlst) - 1)]
            cx
+= dx[ir]; cy += dy[ir]
           
GenerateMaze(cx, cy)
       
else: break

GenerateMaze(0, 0)
# paint the maze
for ky in range(imgy):
   
for kx in range(imgx):
        m
= maze[my * ky / imgy][mx * kx / imgx] * 255
        pixels
[kx, ky] = (m, m, m)
image
.save("RandomMaze_" + str(mx) + "x" + str(my) + ".png", "PNG")
FB36 (author) 11 years, 3 months ago  # | flag

This version does not allow zigzag corridors, so the maze looks more conventional:

# Random Maze Generator using Depth-first Search
# http://en.wikipedia.org/wiki/Maze_generation_algorithm
# FB36 - 20130106
import random
from PIL import Image
imgx
= 500; imgy = 500
image
= Image.new("RGB", (imgx, imgy))
pixels
= image.load()
mx
= 100; my = 100 # width and height of the maze
maze
= [[0 for x in range(mx)] for y in range(my)]
dx
= [0, 1, 0, -1]; dy = [-1, 0, 1, 0] # 4 directions to move in the maze
color
= [(0, 0, 0), (255, 255, 255)] # RGB colors of the maze
# start the maze from a random cell
cx
= random.randint(0, mx - 1); cy = random.randint(0, my - 1)
maze
[cy][cx] = 1; stack = [(cx, cy, 0)] # stack element: (x, y, direction)

while len(stack) > 0:
   
(cx, cy, cd) = stack[-1]
   
# to prevent zigzags:
   
# if changed direction in the last move then cannot change again
   
if len(stack) > 2:
       
if cd != stack[-2][2]: dirRange = [cd]
       
else: dirRange = range(4)
   
else: dirRange = range(4)

   
# find a new cell to add
    nlst
= [] # list of available neighbors
   
for i in dirRange:
        nx
= cx + dx[i]; ny = cy + dy[i]
       
if nx >= 0 and nx < mx and ny >= 0 and ny < my:
           
if maze[ny][nx] == 0:
                ctr
= 0 # of occupied neighbors must be 1
               
for j in range(4):
                    ex
= nx + dx[j]; ey = ny + dy[j]
                   
if ex >= 0 and ex < mx and ey >= 0 and ey < my:
                       
if maze[ey][ex] == 1: ctr += 1
               
if ctr == 1: nlst.append(i)

   
# if 1 or more neighbors available then randomly select one and move
   
if len(nlst) > 0:
        ir
= nlst[random.randint(0, len(nlst) - 1)]
        cx
+= dx[ir]; cy += dy[ir]; maze[cy][cx] = 1
        stack
.append((cx, cy, ir))
   
else: stack.pop()

# paint the maze
for ky in range(imgy):
   
for kx in range(imgx):
        pixels
[kx, ky] = color[maze[my * ky / imgy][mx * kx / imgx]]
image
.save("Maze_" + str(mx) + "x" + str(my) + ".png", "PNG")
Muhammad Khalifa 9 years, 8 months ago  # | flag

Why do you have to check every neighbor if it has only one visited neighbor? Can't we just choose a random neighbor x,y where maze[y][x]=0 , set maze[y][x] to 1 and add it the stack?

Created by FB36 on Wed, 5 Dec 2012 (MIT)
Python recipes (4591)
FB36's recipes (148)

Required Modules

Other Information and Tasks