BuildFun und SolveFun
Nun, das hat eine Weile gedauert und ich bin mir nicht ganz sicher, ob der Solver schummelt oder nicht. Während es die ganze Zeit über Zugriff auf das gesamte Labyrinth hat, betrachtet es nur die Zelle, in der es sich befindet, die Wände, die es umgeben, und, wenn es keine Wand zwischen ihnen gibt, die angrenzenden Zellen. Wenn dies gegen die Regeln verstößt, lass es mich bitte wissen und ich werde versuchen, es zu ändern.
Wie auch immer, hier ist der Code:
#Architect function
def BuildFun(size,seed):
#Initialise grid and ensure inputs are valid
if size<15:size=15
if size>50:size=50
if seed<4:seed=4
if seed>size:seed=size
grid=[]
for x in range(size):
gridbuilder=[]
for y in range(size):gridbuilder.append([0,1,1])
grid.append(gridbuilder)
coords=[0,0]
grid[0][0][0]=1
#Generate maze
while 1:
#Choose a preffered direction based on location in grid and seed
pref=((((coords[0]+coords[1]+2)*int(size/2))%seed)+(seed%(abs(coords[0]-coords[1])+1)))%4
#Find legal moves
opt=[]
if coords[0]>0:opt+=[0] if grid[coords[0]-1][coords[1]][0]==0 else []
if coords[1]<size-1:opt+=[1] if grid[coords[0]][coords[1]+1][0]==0 else []
if coords[0]<size-1:opt+=[2] if grid[coords[0]+1][coords[1]][0]==0 else []
if coords[1]>0:opt+=[3] if grid[coords[0]][coords[1]-1][0]==0 else []
#There are legal moves
if len(opt)>0:
moved=False
while not moved:
#Try to move in preffered direction
if pref in opt:
if pref==0:
coords[0]-=1
grid[coords[0]][coords[1]][0]=1
grid[coords[0]][coords[1]][2]=0
elif pref==1:
grid[coords[0]][coords[1]][1]=0
coords[1]+=1
grid[coords[0]][coords[1]][0]=1
elif pref==2:
grid[coords[0]][coords[1]][2]=0
coords[0]+=1
grid[coords[0]][coords[1]][0]=1
else:
coords[1]-=1
grid[coords[0]][coords[1]][0]=1
grid[coords[0]][coords[1]][1]=0
moved=True
#Change preferred direction if unable to move
else:
pref+=1
if pref==4:pref=0
#There aren't legal moves
else:
moved=False
#Return to a previously visited location
if not moved:
try:
if grid[coords[0]-1][coords[1]][0]==1 and grid[coords[0]-1][coords[1]][2]==0:
grid[coords[0]][coords[1]][0]=2
coords[0]-=1
moved=True
except:pass
if not moved:
try:
if grid[coords[0]][coords[1]+1][0]==1 and grid[coords[0]][coords[1]][1]==0:
grid[coords[0]][coords[1]][0]=2
coords[1]+=1
moved=True
except:pass
if not moved:
try:
if grid[coords[0]+1][coords[1]][0]==1 and grid[coords[0]][coords[1]][2]==0:
grid[coords[0]][coords[1]][0]=2
coords[0]+=1
moved=True
except:pass
if not moved:
try:
if grid[coords[0]][coords[1]-1][0]==1 and grid[coords[0]][coords[1]-1][1]==0:
grid[coords[0]][coords[1]][0]=2
coords[1]-=1
moved=True
except:pass
#Check if finished
fin=True
for x in grid:
for y in x:
if y[0]==0:
fin=False
break
if not fin:break
if fin:break
for x in grid:
for y in x:
y[0]=0
#Find positions for start and finish such that the route between them is as long as possible
lsf=[[0,0],[0,0],0]
for y in range(size):
for x in range(size):
#Check all start positions
lengths=[]
coords=[[y,x,4,0]]
while len(coords)>0:
#Spread tracers out from start to the rest of the maze
for coord in coords:
opt=[]
if coord[0]>0:opt+=[0] if grid[coord[0]-1][coord[1]][2]==0 else []
opt+=[1] if grid[coord[0]][coord[1]][1]==0 else []
opt+=[2] if grid[coord[0]][coord[1]][2]==0 else []
if coord[1]>0:opt+=[3] if grid[coord[0]][coord[1]-1][1]==0 else []
try:opt.remove(coord[2])
except:pass
#Dead end, tracer dies and possible end point is recorded along with length
if len(opt)==0:
lengths.append([coord[0],coord[1],coord[3]])
coords.remove(coord)
else:
#Create more tracers at branch points
while len(opt)>1:
if opt[0]==0:coords.append([coord[0]-1,coord[1],2,coord[3]+1])
elif opt[0]==1:coords.append([coord[0],coord[1]+1,3,coord[3]+1])
elif opt[0]==2:coords.append([coord[0]+1,coord[1],0,coord[3]+1])
else:coords.append([coord[0],coord[1]-1,1,coord[3]+1])
del opt[0]
if opt[0]==0:
coord[0]-=1
coord[2]=2
coord[3]+=1
elif opt[0]==1:
coord[1]+=1
coord[2]=3
coord[3]+=1
elif opt[0]==2:
coord[0]+=1
coord[2]=0
coord[3]+=1
else:
coord[1]-=1
coord[2]=1
coord[3]+=1
#Find furthest distance and, if it's longer than the previous one, the start/end positions get updated
lengths=sorted(lengths,key=lambda x:x[2],reverse=True)
if lengths[0][2]>lsf[2]:lsf=[[y,x],[lengths[0][0],lengths[0][1]],lengths[0][2]]
#Find number of walls and output maze
w=draw(grid,size,lsf[0],lsf[1])
#Output maze information
print('Start = '+str(lsf[0]))
print('End = '+str(lsf[1]))
print('Distance = '+str(lsf[2]))
print('Walls = '+str(w))
print('Score = '+str(float(lsf[2])/float(w))[:5])
#Convert array grid to binary strings horizontal and vertical
horizontal=vertical=''
for y in range(size):
for x in range(size-1):vertical+=str(grid[y][x][1])
for y in range(size-1):
for x in range(size):horizontal+=str(grid[y][x][2])
#Save maze information to text file for use with SolveFun
save=open('Maze.txt','w')
save.write(str(size)+'\n'+str(lsf[0][0])+' '+str(lsf[0][1])+'\n'+str(lsf[1][0])+' '+str(lsf[1][1])+'\n'+horizontal+'\n'+vertical)
save.close()
#Solver function
def SolveFun():
try:
#Get maze information from text file
save=open('Maze.txt','r')
data=save.readlines()
save.close()
size=int(data[0])
s=data[1].rsplit(' ')
start=[int(s[0]),int(s[1])]
e=data[2].rsplit(' ')
end=[int(e[0]),int(e[1])]
horizontal=data[3].rstrip('\n')
vertical=data[4]
#Build maze from information
grid=[]
for y in range(size):
grid.append([])
for x in range(size):
grid[y].append([0,1,1])
for y in range(size):
for x in range(size-1):
grid[y][x][1]=int(vertical[y*(size-1)+x])
for y in range(size-1):
for x in range(size):
grid[y][x][2]=int(horizontal[y*size+x])
path=''
cpath=''
bs=0
pos=start[:]
grid[pos[0]][pos[1]][0]=1
while pos!=end:
#Want to move in direction of finish
if end[0]<pos[0] and pos[0]-end[0]>=abs(pos[1]-end[1]):pref=0
elif end[1]>pos[1] and end[1]-pos[1]>=abs(pos[0]-end[0]):pref=1
elif end[0]>pos[0] and end[0]-pos[0]>=abs(pos[1]-end[1]):pref=2
else:pref=3
#Find legal moves
opt=[]
if pos[0]>0:
if grid[pos[0]-1][pos[1]][2]==0:opt+=[0]if grid[pos[0]-1][pos[1]][0]==0 else[]
if pos[1]>0:
if grid[pos[0]][pos[1]-1][1]==0:opt+=[3]if grid[pos[0]][pos[1]-1][0]==0 else[]
if grid[pos[0]][pos[1]][2]==0:opt+=[2]if grid[pos[0]+1][pos[1]][0]==0 else[]
if grid[pos[0]][pos[1]][1]==0:opt+=[1]if grid[pos[0]][pos[1]+1][0]==0 else[]
if len(opt)>0:
moved=False
while not moved:
#Try to move in preferred direction
if pref in opt:
if pref==0:
pos[0]-=1
path+='0'
cpath+='0'
elif pref==1:
pos[1]+=1
path+='1'
cpath+='1'
elif pref==2:
pos[0]+=1
path+='2'
cpath+='2'
else:
pos[1]-=1
path+='3'
cpath+='3'
grid[pos[0]][pos[1]][0]=1
moved=True
#Change preferred direction by 1
else:
pref=(pref+1)%4
#No legal moves, backtrack
else:
bs+=1
grid[pos[0]][pos[1]][0]=2
if int(cpath[len(cpath)-1])==0:
pos[0]+=1
path+='2'
elif int(cpath[len(cpath)-1])==1:
pos[1]-=1
path+='3'
elif int(cpath[len(cpath)-1])==2:
pos[0]-=1
path+='0'
else:
pos[1]+=1
path+='1'
cpath=cpath[:len(cpath)-1]
#Output maze with solution as well as total steps and wasted steps
draw(grid,size,start,end)
print('\nPath taken:')
print(str(len(path))+' steps')
print(str(bs)+' backsteps')
print(str(bs*2)+' wasted steps')
except:print('Could not find maze')
def draw(grid,size,start,end):
#Build output in string d
d=' '
for x in range(size):d+=' '+str(x)[0]
d+='\n '
for x in range(size):d+=' ' if len(str(x))==1 else ' '+str(x)[1]
d+='\n '+'_'*(size*2-1)
w=0
for y in range(size):
d+='\n'+str(y)+' |' if len(str(y))==1 else '\n'+str(y)+' |'
for x in range(size):
if grid[y][x][2]:
if start==[y,x]:d+=UL.S+'S'+UL.E
elif end==[y,x]:d+=UL.S+'F'+UL.E
elif grid[y][x][0]==1:d+=UL.S+'*'+UL.E
else:d+='_'
w+=1
else:
if start==[y,x]:d+='S'
elif end==[y,x]:d+='F'
elif grid[y][x][0]==1:d+='*'
else:d+=' '
if grid[y][x][1]:
d+='|'
w+=1
else:d+=' '
#Output maze and return number of walls
print(d)
w-=size*2
return w
#Underlines text
class UL:
S = '\033[4m'
E = '\033[0m'
Mir ist klar, dass dies lächerlich lang und nicht besonders einfach zu lesen ist, aber ich bin faul, also bleibt es so.
BuildFun
Der Architekt BuildFun ist ein relativ einfaches Programm zur Erzeugung von Labyrinthen, das immer ein 'perfektes' Labyrinth erzeugt (eines ohne Schleifen und bei dem zwei beliebige Punkte immer genau einen Pfad zwischen sich haben). Es basiert seine Logik auf der Sameneingabe, was bedeutet, dass die generierten Labyrinthe pseudozufällig sind und häufig sich wiederholende Muster aufweisen. Mit demselben Samen und derselben Größe wird dasselbe Labyrinth erstellt.
Sobald das Labyrinth generiert wurde, versucht das Programm, die Punktzahl des Labyrinths zu maximieren, indem es nach dem Start- und Endpunkt sucht, die den längsten Pfad zwischen ihnen ergeben. Dazu durchläuft es jeden Startpunkt, verteilt die Tracer, um den am weitesten entfernten Endpunkt zu finden, und wählt die Kombination mit dem längsten Pfad aus.
Danach zeichnet es das Labyrinth, zählt die Wände und gibt die Informationen des Labyrinths aus. Dies ist der Startpunkt, der Endpunkt, der Abstand zwischen ihnen, die Anzahl der Wände und die Punktzahl. Außerdem werden diese Informationen in den oben beschriebenen Stil für Größe, Anfang und Ende, horizontale Wände und vertikale Wände formatiert und zur späteren Verwendung in einer Textdatei mit dem Namen Maze.txt gespeichert.
SolveFun
Der Solver SolveFun verwendet die Textdatei Maze.txt als Eingabe und funktioniert ähnlich wie der Architekt. Für jede Bewegung wählt es eine Richtung, die es gehen möchte, basierend auf seiner relativen Position zum Ende, und dann schaut es auf die Wände, die es umgeben. Wenn eine Wand nicht vorhanden ist, wird geprüft, ob sie sich in der angrenzenden Zelle befindet. Andernfalls wird sie als mögliche Option hinzugefügt. Es bewegt sich dann in die Richtung, die seiner Vorzugsrichtung am nächsten kommt, sofern es über Optionen verfügt. Wenn es keine Optionen gibt, wird es zurückverfolgt, bis dies der Fall ist. Dies geht so lange weiter, bis das Ende erreicht ist.
Während der Bewegung zeichnet es den Pfad, den es nimmt, in dem variablen Pfad auf, der am Ende zur Ausgabe der Gesamtzahl der Schritte verwendet wird. Es zeichnet auch auf, wie oft es zurückverfolgt werden musste, um die verschwendeten Schritte am Ende zu berechnen. Wenn es das Ende erreicht, gibt es das Labyrinth mit dem kürzesten Weg vom Anfang zum Ende aus, der mit *
s markiert ist .
Wie man läuft
Aufgrund der Methode zur Ausgabe des Labyrinths (die das Unterstreichen bestimmter Zeichen umfasst) muss dies über eine Befehlszeile im Formular ausgeführt werden
python -c 'import filename;filename.BuildFun(Size, Seed)'
und
python -c 'import filename;filename.SolveFun()'
Dabei ist Size eine ganze Zahl zwischen 15 und 50 (einschließlich) und Seed eine ganze Zahl zwischen 4 und Size (einschließlich).