Code : Génération et résolution de labyrinthe
Contents
Code : Génération et résolution de labyrinthe#
Génération d’un labyrinthe par parcours en profondeur#
On souhaite construire un labyrinthe, sous la forme d’un graphe dont l’ensemble des sommets est \(\{ (i, j) | 0 \leq i < n, 0 \leq j < p \}\) (une grille rectangulaire de taille \(n \times p\)), et où chaque sommet est adjacent à un ou plusieurs des \(4\) sommets autour de lui.
L’objectif est de trouver un chemin du sommet \((0, 0)\) (en bas à gauche) au sommet \((n - 1, p - 1)\) (en haut à droite).
Pour générer un tel labyrinthe, on peut utiliser un parcours en profondeur sur une grille \(n\times p\) (contenant toutes les arêtes possibles) et où on ne conserve que les arêtes visitées par ce parcours :
import matplotlib.pyplot as plt
import networkx as nx
import random
def generate_labyrinth(n, p):
G = nx.Graph()
G.add_nodes_from((i, j) for i in range(n) for j in range(p))
visited = [[False]*p for _ in range(n)]
def dfs(i, j):
visited[i][j] = True
neighbors = [(i+1, j), (i-1, j), (i, j+1), (i, j-1)]
random.shuffle(neighbors)
for x, y in neighbors:
if 0 <= x < n and 0 <= y < p and not visited[x][y]:
G.add_edge((i, j), (x, y))
dfs(x, y)
dfs(n - 1, 0)
return G
def draw(G, **keywords):
plt.clf()
nx.draw(G, pos={p: p for p in G.nodes()}, node_size=20, **keywords)
plt.show()
n = 10
G = generate_labyrinth(n, n)
draw(G)
Résolution par parcours en profondeur#
Pour trouver un chemin du sommet en bas à gauche au sommet en bas à droite, on peut utiliser un parcours (ici en profondeur) :
def solve_labyrinth(G, n):
visited = [[False]*n for _ in range(n)]
path = []
for i,e in enumerate(G.edges):
G.edges[e]['index'] = i
def dfs(i, j):
if i == n - 1 == j:
return True
visited[i][j] = True
neighbors = [(i+1, j), (i-1, j), (i, j+1), (i, j-1)]
random.shuffle(neighbors)
for x, y in neighbors:
if 0 <= x < n and 0 <= y < n and (x, y) in G[(i, j)] and not visited[x][y]:
if dfs(x, y):
path.append(G[(i, j)][(x, y)]['index'])
return True
return False
dfs(0, 0)
return path
path = solve_labyrinth(G, n)
widths = [4 if i in path else 1 for i in range(len(G.edges))]
draw(G, width=widths)
Animation de parcours du labyrinthe#
Pour voir dans quel ordre sont considérées les arêtes, on peut utiliser la fonction d’animation suivante :
import networkx as nx
import matplotlib.pyplot as plt
import matplotlib.animation
from IPython.display import HTML
def anim_graph(G, widths):
fig, ax = plt.subplots(figsize=(20,12))
plt.close()
def update(frame):
ax.clear()
nx.draw(G, ax=ax, pos={p: p for p in G.nodes()}, node_size=700, width=widths[frame])
ani = matplotlib.animation.FuncAnimation(fig, update, frames=len(widths), interval=80)
return HTML(ani.to_jshtml(default_mode="once"))
Animation du parcours en profondeur#
On affiche le parcours en profondeur en affichant en gras, à chaque étape, l’arête considérée :
def solve_labyrinth_dfs(G, n):
widths = [1]*len(G.edges)
visited = [[False]*n for _ in range(n)]
frame_widths = []
for i,e in enumerate(G.edges):
G.edges[e]['index'] = i
def dfs(i, j):
if i == n - 1 == j:
return True
visited[i][j] = True
neighbors = [(i+1, j), (i-1, j), (i, j+1), (i, j-1)]
random.shuffle(neighbors)
for x, y in neighbors:
if 0 <= x < n and 0 <= y < n and (x, y) in G[(i, j)] and not visited[x][y]:
widths[G[(i, j)][(x, y)]['index']] = 6
frame_widths.append(widths.copy())
if dfs(x, y):
return True
widths[G[(i, j)][(x, y)]['index']] = 1
frame_widths.append(widths.copy())
return False
dfs(0, 0)
return frame_widths
anim_graph(G, solve_labyrinth_dfs(G, n))
Animation du parcours en largeur#
Et voici l’animation du parcours en largeur :
from collections import deque
def solve_labyrinth_bfs(G, n):
widths = [1]*len(G.edges)
visited = [[False]*n for _ in range(n)]
frame_widths = []
for i, e in enumerate(G.edges):
G.edges[e]['index'] = i
q = deque([(0, 0)])
while len(q) > 0:
i, j = q.pop()
if i == n - 1 == j:
break
visited[i][j] = True
neighbors = [(i+1, j), (i-1, j), (i, j+1), (i, j-1)]
random.shuffle(neighbors)
for x, y in neighbors:
if 0 <= x < n and 0 <= y < n and (x, y) in G[(i, j)] and not visited[x][y]:
widths[G[(i, j)][(x, y)]['index']] = 6
frame_widths.append(widths.copy())
q.appendleft((x, y))
return frame_widths
anim_graph(G, solve_labyrinth_bfs(G, n))