0

I made a Python program that creates a randomly generated maze using a dictionary called 'GRID'. It is a square grid containing coordinates and values of each tile. The tiles can be either walkable or empty.

import random as rnd

GRID={}#a square grid containing all tile coordinates of the maze and its corresponding values (empty or walkable)
RANGE=50#rnd.randint(150,300)#size of the grid's edges


e='O'#empty tile
w='X'#walkable tile

for x in range(RANGE):
    for y in range(RANGE):
        GRID.update({(x,y):e})

def writegrid(G,R,S):#writes the grid, its range and the start position into a text file because I can't deactivate python word wrap
    grid=open('GRID.txt','w')
    grid.write(str(S)+' '+str(R)+'\n')
    for y in reversed(range(R)):
        for x in range(R):
            grid.write(G[x,y])
            if x==R-1:
                grid.write('\n')
            else:
                grid.write(' ')

def printgrid(G,R,S):#prints the grid, range and start position
    print(S,R)
    for y in reversed(range(R)):
        for x in range(R):
            print(G[x,y],end='')
            if x==R-1:
                print('')
            else:
                print(' ',end='')

The start of the maze has a certain shape I would like to keep, so I made a new dictionary called 'NEVER', so that the maze generation doesn't affect the tiles added to that dictionary. When generating the maze, the program determines whether some directions cannot be generated (e.g. intersects with an old path, or is about to hit 'NEVER'). If there are no directions available (e.g. a dead end), it migrates to different coordinates and restarts the direction searching there.

start_area_range=rnd.randint(4,6)#the size of the starting area
#the start area is an empty space with a linear path into the maze
#the further in the path, the smaller the area gets, like a pyramid with a strike in the middle
first_path=rnd.randint(0,3)#determines which edge will the start area be on
NEVER={}#never place a walkable tile there, it will ruin the shape of certain areas, such as start

#start set
if first_path==0:#left
    START=(0,rnd.randint(start_area_range+1,RANGE-start_area_range-2))
elif first_path==1:#right
    START=(RANGE-1,rnd.randint(start_area_range+1,RANGE-start_area_range-2))
elif first_path==2:#bottom
    START=(rnd.randint(start_area_range+1,RANGE-start_area_range-2),0)
else:#top
    START=(rnd.randint(start_area_range+1,RANGE-start_area_range-2),RANGE-1)

prev=tuple(START)
path=['U','D','R','L']#possible paths

f=0#number of walkable tiles

#start area set
if START[0]==0 or START[0]==RANGE-1:#left or right edge
    for i in range(start_area_range):#moves position away from the start
        for j in range(1-start_area_range+i,start_area_range-i):#shrinks the empty area perpendicular to the path
            if j==0:#if it's on the path, make it walkable
                GRID.update({(int(START[0]+i-i*2*(START[0]/(RANGE-1))),START[1]):w})
                NEVER.update({(int(START[0]+i-i*2*(START[0]/(RANGE-1))),START[1]):w})
                f+=1
            else:#if it's not, make it empty
                GRID.update({(int(START[0]+i-i*2*(START[0]//(RANGE-1))),START[1]+j):e})
                NEVER.update({(int(START[0]+i-i*2*(START[0]//(RANGE-1))),START[1]+j):e})
else:#top or bottom edge
    for i in range(start_area_range):
        for j in range(1-start_area_range+i,start_area_range-i):
            if j==0:#same as previous
                GRID.update({(START[0],int(START[1]+i-i*2*(START[1]/(RANGE-1)))):w})
                NEVER.update({(START[0],int(START[1]+i-i*2*(START[1]/(RANGE-1)))):w})
                f+=1
            else:#same as previous
                GRID.update({(START[0]+j,int(START[1]+i-i*2*(START[0]/(RANGE-1)))):e})
                NEVER.update({(START[0]+j,int(START[1]+i-i*2*(START[0]/(RANGE-1)))):e})

GRID.update({START:'S'})#so I know where to start
NEVER.update({START:'S'})
f+=1

DEAD=[]#list of dead ends

#maze set
while f!=int((RANGE**2)//2+rnd.randint(-10,10)):
    curpath=list(path)#currently possible paths
    x,y=tuple(prev)

#which tiles to check when placing the next walkable tile
    u=[(x-1,y+1),(x,y+1),(x+1,y+1)]
    d=[(x-1,y-1),(x,y-1),(x+1,y-1)]
    r=[(x+1,y-1),(x+1,y),(x+1,y+1)]
    l=[(x-1,y-1),(x-1,y),(x-1,y+1)]

#direction set
    if y==RANGE-1:#if it's on the edge, it can't go further
        curpath.remove('U')
    else:#if it's not on the edge, check further
        for i in u:
            if i in NEVER or (i in GRID and GRID[i]==w):#if any of the checked tiles are walkable, stop checking
                curpath.remove('U')
                break
    if y==0:
        curpath.remove('D')
    else:
        for i in d:
            if i in NEVER or (i in GRID and GRID[i]==w):
                curpath.remove('D')
                break
    if x==RANGE-1:
        curpath.remove('R')
    else:
        for i in r:
            if i in NEVER or (i in GRID and GRID[i]==w):
                curpath.remove('R')
                break
    if x==0:
        curpath.remove('L')
    else:
        for i in l:
            if i in NEVER or (i in GRID and GRID[i]==w):
                curpath.remove('L')
                break

#next tile set
    if len(curpath)!=0:#if a walkable tile can be placed
        DIR=rnd.randint(0,len(curpath)-1)

        if curpath[DIR]=='U':
            y+=1
        elif curpath[DIR]=='D':
            y-=1
        elif curpath[DIR]=='R':
            x+=1
        else:
            x-=1

        GRID.update({(x,y):w})
        f+=1
        prev=tuple((x,y))

    else:#if a walkable tile can't be placed,-
        DEAD.append((x,y))#-add the position to the dead end list-
        while True:
            x,y=rnd.randint(0,RANGE-1),rnd.randint(0,RANGE-1)#-find a new position-
            if (x,y) not in NEVER:
# and GRID[x,y]==w:#why doesn't it accept both conditions
                prev=tuple((x,y))
                break

The conditions should be that if the new coordinates are walkable, AND they are not in 'NEVER', migrate there. For some reason, using those conditions together creates an infinite loop.

Why are these conditions never true?

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
  • 1
    There are lots of conditions in the code. Are you talking about `if (x, y) not in NEVER:`? – Barmar Aug 17 '22 at 12:43
  • 1
    Don't use `.update()` to assign a single entry to a dictionary. Use `dictionary[key] = value`. – Barmar Aug 17 '22 at 12:44
  • There's no need to write `tuple((x,y))`. `(x, y)` is already a tuple, you don't have to convert it. And you don't have to write `tuple(prev)` since `prev` always contains a tuple. – Barmar Aug 17 '22 at 12:45
  • I know this is not the question, but I think that your code could highly benefit from using classes, at least one class to represent a maze cell. This would prevent you from having to maintain a complex data structure, and would dramatically improve code readability and conciseness (thus perhaps bringing more answers) – Romain Reboulleau Aug 17 '22 at 12:57
  • Barmar: Thank you for your feedback! #1 Yes, I am talking about that condition. The comment below should be the second condition, but they don't both work. #2 I thought it's the same thing in this case. #3 https://stackoverflow.com/questions/29785084/changing-one-list-unexpectedly-changes-another-too <-- It's the same with tuples. Romain Reboulleau: Thank you for the suggestion! I forgot how classes work, so I will try and improve on it in time. – YayGrassBlock Aug 18 '22 at 15:22

0 Answers0