Les scripts suivants permettent de créer un labyrinthe de manière aléatoire, et d’exploiter la structure de données de ce labyrinthe. On pourra ainsi tester quelques algorithmes relatifs au parcours de ce labyrinthe.
Les scripts en permettent une visualisation.
defdirections(laby,i,j):"""les directions possibles pour la case courante
i : int numero de ligne
j : numero de la colonne
retourne une liste (tuple) des points cardinaux possibles
"""L=[]iflaby.tab[j][i].N==True:L.append('N')iflaby.tab[j][i].S==True:L.append('S')iflaby.tab[j][i].E==True:L.append('E')iflaby.tab[j][i].W==True:L.append('W')returntuple(L)
Un labyrinthe est un tableau de cases laby.tab[j][i] ayant chacune pour propriétés : N,S,E,W
i : int numero de ligne
j : numero de la colonne
Pour chacune de ces propriétés, par exemple laby.tab[j][i].Non renseigne une valeur True ou False
True : direction possible
False : impossible (mur)
Créer un labyrinthe vide
deflabyrinthe(lines,col):"""
Params :
--------
lines : int : nombre de lignes du labyrinthe
col : int : nombre de colonnes
Returns :
---------
tab2 : list : tuples de 1 à 4 éléments correspondants aux directions libres ('N', 'S', E', 'W')
sortie : tracé du labyrinthe
Variables :
-----------
p : nombre de colonnes (=largeur)
q : nombre de lignes (=hauteur)
"""laby=creation(col,lines)tab2=[[0]*laby.pforiinrange(laby.q)]foriinrange(laby.q):forjinrange(laby.p):tab2[i][j]=directions(laby,i,j)Labyrinthe.show(laby)returntab2tab=labyrinthe(5,6)
Remarque : Les lignes sont mises dans l’ordre inverse du dessin du labyrinthe :
Tracé du labyrinthe
defmur(dir,i,j):"""trace les murs des directions fermées
dir est un tuple contenant les directions libres
"""line=10ifnot('N'indir[i][j]):plt.plot([j,j+1],[i+1,i+1],'black',linewidth=line)ifnot('S'indir[i][j]):plt.plot([j,j+1],[i,i],'black',linewidth=line)ifnot('E'indir[i][j]):plt.plot([j+1,j+1],[i,i+1],'black',linewidth=line)ifnot('W'indir[i][j]):plt.plot([j,j],[i,i+1],'black',linewidth=line)defmurs(tab):foriinrange(len(tab)):forjinrange(len(tab[i])):mur(tab,i,j)plt.plot([0,0,len(tab[0]),len(tab[0]),0],[0,len(tab),len(tab),0,0],'blue',linewidth=10)plt.savefig('labyrinthe.png')
On peut faire l’économie du tracé eventuel côté S et côté W grace au tracé des cases adjacentes.
murs(tab)
Parcours du labyrinthe
fonctions utiles
defnexto(c,direc):"""retourne les coordonnées lors du deplacement
selon la position actuelle et la direction
Params :
--------
c : tuple (ligne,colonne) correspondant à (y,x) dans le plan cartesien
direct : str : 'N', 'S', E', 'W'
Returns :
---------
tuple : (int,int) correspondant à (ligne,colonne)
"""i,j=c[0],c[1]ifdirec=='N':return(i+1,j)ifdirec=='S':return(i-1,j)ifdirec=='E':return(i,j+1)ifdirec=='W':return(i,j-1)defvisited(c,L,couleur):"""retourne la couleur du noeud de coord c et
de liste de directions possibles L selon celle de ses voisins
Params :
--------
c : tuple : (ligne,colonne) correspondant à (y,x) dans le plan cartesien
L : list : tuples de 1 à 4 éléments correspondants aux directions libres ('N', 'S', E', 'W')
couleur : List dimension 2 contenant des elements str
'white' si noeud non visité,
'green' si le noeud est en cours de visite,
'red' si tous les noeuds ont été visités autour de lui
Returns :
---------
couleur : str : 'green' si le noeud est en cours de visite, 'red' si tous les noeuds
ont été visités autour de lui
"""i,j=c[0],c[1]coul='red'fordirecinL:coord=nexto(c,direc)ifcouleur[coord[0]][coord[1]]=='white':coul='green'print(c,coul)returncoul
une premiere idée : colorer les noeuds du chemin en vert
deftrouvercheminiter(start,end,tab):"""recherche du chemin jusqu'à la sortie dans le labyrinthe
en utilisant une technique qui s'apparente au parcours en profondeur
avec backtracking
on utilise une pile de noeuds visités
Params :
--------
start : tuple de coord dans le labyrinthe
end : tuple de coord dans le labyrinthe
tab : liste de liste contenant pour chaque tuple de coordonnées un tuple de directions possibles
Variables :
-----------
couleur : une table de la couleur du noeud ('red' si aucune nouvelle direction possible,
'green' si en cours de visite, 'white' si jamais visité)
Returns:
--------
la liste couleur
"""p=Pile()p.push(start)c=startcouleur=[['white']*len(tab[0])foriinrange(len(tab))]# precaution pour eviter debordementcompt=0while(notp.empty())and(notc==end)andcompt<100:compt+=1c=p.pop()L=tab[c[0]][c[1]]couleur[c[0]][c[1]]=visited(c,L,couleur)# c est retiré de la pile et coloré en green ou redfordirecinL:coord=nexto(c,direc)ifcouleur[coord[0]][coord[1]]=='white':# le noeud fils est coloré en blanc dans la direction Dfornintab[coord[0]][coord[1]]:p.push(coord)# alors on ajoute le noeud fils dans la direction D au sommet de la pile# n fois afin de reconsidérer sa couleur à chaque fois que l'on depilereturncouleur
(4, 0) green
(3, 0) green
(2, 0) green
(2, 1) green
(3, 1) green
(4, 1) green
(4, 2) green
(4, 3) green
(4, 4) green
(3, 4) green
(3, 5) green
(4, 5) red
(3, 5) red
(3, 4) red
(4, 4) red
(3, 3) green
(3, 2) green
(2, 2) green
(1, 2) green
(1, 1) green
(0, 1) green
(0, 2) green
(0, 3) green
(0, 4) green
(0, 5) green
[['white', 'green', 'green', 'green', 'green', 'green'],
['white', 'green', 'green', 'white', 'white', 'white'],
['green', 'green', 'green', 'white', 'white', 'white'],
['green', 'green', 'green', 'green', 'red', 'red'],
['green', 'green', 'green', 'green', 'red', 'red']]
Une autre approche : mémoriser les étapes de la solution
defsolution(start,end,tab):"""trouve la solution au labyrinthe et trace ce chemin
La recherche du chemin jusqu'à la sortie dans le labyrinthe
utilise une technique qui s'apparente au parcours en profondeur
avec backtracking
on utilise une pile de noeuds visités
Params :
--------
start : tuple de coord dans le labyrinthe
end : tuple de coord dans le labyrinthe
tab : liste de liste contenant pour chaque tuple de coordonnées un tuple de directions possibles
Returns:
--------
la liste couleur
Variables :
-----------
couleur : une table de la couleur du noeud ('red' si aucune nouvelle direction possible,
'green' si en cours de visite, 'white' si jamais visité)
p : Pile() utile pour la recherche du parcours
pchemin : Pile() utile pour memoriser ce chemin
"""p=Pile()pchemin=Pile()p.push(start)c=startcouleur=[['white']*len(tab[0])foriinrange(len(tab))]while(notp.empty())and(notc==end):c=p.pop()pchemin.push(c)L=tab[c[0]][c[1]]couleur[c[0]][c[1]]=visited(c,L,couleur)# c est retiré de la pile et coloré en green ou rednoeud=pchemin.pop()whilecouleur[noeud[0]][noeud[1]]=='red':noeud=pchemin.pop()# il peut y avoir plusieurs fois le noeud successivement dans la pilepchemin.push(noeud)# on remet le dernier noeud retiré non rougefordirecinL:coord=nexto(c,direc)ifcouleur[coord[0]][coord[1]]=='white':# le noeud fils est coloré en blanc dans la direction D#for n in tab[coord[0]][coord[1]]:p.push(c)p.push(coord)# alors on ajoute le noeud fils dans la direction D au sommet de la pile# ainsi que son noeud parentreturnpchemin.lstdefmurSolution(tab,L):xList=[]yList=[]foriinrange(len(tab)):forjinrange(len(tab[i])):mur(tab,i,j)plt.plot([0,0,len(tab[0]),len(tab[0]),0],[0,len(tab),len(tab),0,0],'blue',linewidth=10)forninrange(len(L)):yList.append(L[n][0]+0.5)xList.append(L[n][1]+0.5)plt.plot(xList,yList,'red',linewidth=2)
L=solution((len(tab)-1,0),(0,len(tab[0])-1),tab)L
(4, 0) green
(3, 0) green
(2, 0) green
(2, 1) green
(3, 1) green
(4, 1) green
(4, 2) green
(4, 3) green
(4, 4) green
(3, 4) green
(3, 5) green
(4, 5) red
(3, 5) red
(3, 4) red
(4, 4) red
(4, 3) green
(3, 3) green
(3, 2) green
(2, 2) green
(1, 2) green
(1, 1) green
(0, 1) green
(0, 2) green
(0, 3) green
(0, 4) green
(0, 5) green
[(4, 0),
(3, 0),
(2, 0),
(2, 1),
(3, 1),
(4, 1),
(4, 2),
(4, 3),
(4, 3),
(3, 3),
(3, 2),
(2, 2),
(1, 2),
(1, 1),
(0, 1),
(0, 2),
(0, 3),
(0, 4),
(0, 5)]
murSolution(tab,L)
mémoriser TOUTES les étapes du parcours
defparcours(start,end,tab):"""trouve la solution au labyrinthe et trace ce chemin
p : Pile() utile pour la recherche du parcours
pchemin : Pile() utile pour memoriser tout le chemin
on insère dans le chemin toutes les arêtes empruntées :
pour le chemin entre c1 et c2, on insère c1 puis c2
"""p=Pile()pchemin=Pile()p.push(start)c=startcouleur=[['white']*len(tab[0])foriinrange(len(tab))]while(notp.empty())and(notc==end):c=p.pop()pchemin.push(c)# chaque fois que l'on depile, on rempile dans pcheminL=tab[c[0]][c[1]]couleur[c[0]][c[1]]=visited(c,L,couleur)# c est retiré de la pile et coloré en green ou redfordirecinL:coord=nexto(c,direc)ifcouleur[coord[0]][coord[1]]=='white':# le noeud fils est coloré en blanc dans la direction Dp.push(c)# on remet le noeud parent afin de reconsidérer sa couleur à chaque fois que l'on depile# et le chemin arriere CONTINU pour le tracép.push(coord)# alors on ajoute le noeud fils dans la direction D au sommet de la pilereturnpchemin.lst
L=parcours((len(tab)-1,0),(0,len(tab[0])-1),tab)L
(4, 0) green
(3, 0) green
(2, 0) green
(2, 1) green
(3, 1) green
(4, 1) green
(4, 2) green
(4, 3) green
(4, 4) green
(3, 4) green
(3, 5) green
(4, 5) red
(3, 5) red
(3, 4) red
(4, 4) red
(4, 3) green
(3, 3) green
(3, 2) green
(2, 2) green
(1, 2) green
(1, 1) green
(0, 1) green
(0, 2) green
(0, 3) green
(0, 4) green
(0, 5) green
[(4, 0),
(3, 0),
(2, 0),
(2, 1),
(3, 1),
(4, 1),
(4, 2),
(4, 3),
(4, 4),
(3, 4),
(3, 5),
(4, 5),
(3, 5),
(3, 4),
(4, 4),
(4, 3),
(3, 3),
(3, 2),
(2, 2),
(1, 2),
(1, 1),
(0, 1),
(0, 2),
(0, 3),
(0, 4),
(0, 5)]