Ce projet explore la création d’un labyrinthe circulaire en combinant algorithmique, géométrie polaire et rendu graphique.
L’objectif : transformer une structure mathématique en une représentation visuelle cohérente et esthétique.
Structure du labyrinthe
Le labyrinthe repose sur une grille polaire composée :
- d’anneaux (r),
- de secteurs angulaires (s),
- de murs circulaires (arcs),
- de murs radiaux (lignes).
Chaque cellule (r, s) possède deux types de murs :
- murArc pour les arcs internes/externes,
- murRadial pour les séparations angulaires.
Génération : un DFS avec backtracking
L’algorithme utilise une exploration en profondeur (DFS) :
- Départ au centre.
- Sélection aléatoire d’un voisin non visité.
- Suppression du mur correspondant.
- Retour arrière lorsqu’aucune option n’est disponible.
Ce processus produit un labyrinthe parfait, sans cycles et entièrement connecté.
Le chemin solution est capturé automatiquement au moment où l’algorithme atteint la cellule de sortie.
Rendu graphique : arcs et radiales
Le tracé repose sur une procédure dédiée :
- dessinerArc(), qui subdivise un arc en segments pour obtenir une courbe fluide.
Les murs sont ensuite dessinés selon leur type, et la solution est mise en valeur par une couleur spécifique (#F59E0B).
Le résultat : un labyrinthe circulaire lisible, harmonieux et entièrement généré par le code.
Ce que ce projet met en lumière
La richesse des représentations polaires dans la génération procédurale.
La capacité d’un DFS à produire des structures visuelles complexes.
L’intérêt de combiner mathématiques, algorithmique et graphisme pour créer des outils pédagogiques ou interactifs.
Programme :
@ procédure de dessin d'un arc filaire
procédure dessinerArc(laToile, x, y, r, angleDeb, angleFin, coul, ep)
pasAngle est un nombre
pasAngle vaut (angleFin - angleDeb) / 8
k est un nombre
pour k de 0 à 7
aA est un nombre
aB est un nombre
aA vaut radians(angleDeb + k * pasAngle)
aB vaut radians(angleDeb + (k + 1) * pasAngle)
x1 est un nombre
y1 est un nombre
x2 est un nombre
y2 est un nombre
x1 vaut x + r * cosinus(aA)
y1 vaut y + r * sinus(aA)
x2 vaut x + r * cosinus(aB)
y2 vaut y + r * sinus(aB)
ligne(laToile, x1, y1, x2, y2, coul, ep)
fin pour
fin procédure
@ Configuration et toile
maToile est une toile
dimension(maToile, 400, 400)
remplir(maToile, #0F172A)
cx est un nombre
cy est un nombre
rMin est un nombre
largeurAnneau est un nombre
nbAnneaux est un nombre
nbSecteurs est un nombre
cx vaut 200
cy vaut 200
rMin vaut 25
largeurAnneau vaut 28
nbAnneaux vaut 5
nbSecteurs vaut 12
nbTotal est un nombre
nbTotal vaut nbAnneaux * nbSecteurs
// Définition de la sortie
sExit est un nombre
sExit vaut hasard(0, nbSecteurs - 1)
targetIdx est un nombre
targetIdx vaut (nbAnneaux - 1) * nbSecteurs + sExit
@ Initialisation des Structures
estVisite est un tableau
murArc est un tableau
murRadial est un tableau
i est un nombre
pour i de 0 à nbTotal - 1
estVisite ajoute 0
murArc ajoute 1
murRadial ajoute 1
fin pour
pileR est un tableau
pileS est un tableau
solR est un tableau
solS est un tableau
solTrouvee est un nombre
solTrouvee vaut 0
// Départ au centre (0, 0)
pileR ajoute 0
pileS ajoute 0
estVisite[0] vaut 1
nbVisites est un nombre
nbVisites vaut 1
@ Algorithme de Génération (DFS) et Capture de la Solution
tant que nbVisites < nbTotal
idxPile est un nombre
idxPile vaut longueur(pileR) - 1
currR est un nombre
currS est un nombre
currR vaut pileR[idxPile]
currS vaut pileS[idxPile]
voisinsR est un tableau
voisinsS est un tableau
voisinsType est un tableau
// Voisin extérieur (r + 1)
si currR < nbAnneaux - 1 alors
idxExt est un nombre
idxExt vaut (currR + 1) * nbSecteurs + currS
si estVisite[idxExt] = 0 alors
voisinsR ajoute (currR + 1)
voisinsS ajoute currS
voisinsType ajoute 1
fin si
fin si
// Voisin intérieur (r - 1)
si currR > 0 alors
idxInt est un nombre
idxInt vaut (currR - 1) * nbSecteurs + currS
si estVisite[idxInt] = 0 alors
voisinsR ajoute (currR - 1)
voisinsS ajoute currS
voisinsType ajoute 2
fin si
fin si
// Voisin horaire (s + 1)
sHor est un nombre
sHor vaut (currS + 1) mod nbSecteurs
idxHor est un nombre
idxHor vaut currR * nbSecteurs + sHor
si estVisite[idxHor] = 0 alors
voisinsR ajoute currR
voisinsS ajoute sHor
voisinsType ajoute 3
fin si
// Voisin anti-horaire (s - 1)
sAnti est un nombre
sAnti vaut (currS - 1 + nbSecteurs) mod nbSecteurs
idxAnti est un nombre
idxAnti vaut currR * nbSecteurs + sAnti
si estVisite[idxAnti] = 0 alors
voisinsR ajoute currR
voisinsS ajoute sAnti
voisinsType ajoute 4
fin si
nbVoisins est un nombre
nbVoisins vaut longueur(voisinsR)
si nbVoisins > 0 alors
choix est un nombre
choix vaut hasard(0, nbVoisins - 1)
prochainR est un nombre
prochainS est un nombre
typeMouv est un nombre
prochainR vaut voisinsR[choix]
prochainS vaut voisinsS[choix]
typeMouv vaut voisinsType[choix]
idxCourant est un nombre
idxCourant vaut currR * nbSecteurs + currS
si typeMouv = 1 alors
murArc[idxCourant] vaut 0
sinon si typeMouv = 2 alors
idxVoisest est un nombre
idxVoisest vaut prochainR * nbSecteurs + prochainS
murArc[idxVoisest] vaut 0
sinon si typeMouv = 3 alors
murRadial[idxCourant] vaut 0
sinon si typeMouv = 4 alors
idxVoisest est un nombre
idxVoisest vaut currR * nbSecteurs + prochainS
murRadial[idxVoisest] vaut 0
fin si
idxSuivant est un nombre
idxSuivant vaut prochainR * nbSecteurs + prochainS
estVisite[idxSuivant] vaut 1
nbVisites ajoute 1
pileR ajoute prochainR
pileS ajoute prochainS
// Capture du chemin de la solution lors de la découverte
si idxSuivant = targetIdx et solTrouvee = 0 alors
kp est un nombre
pour kp de 0 à longueur(pileR) - 1
solR ajoute pileR[kp]
solS ajoute pileS[kp]
fin pour
solTrouvee vaut 1
fin si
sinon
idxDernier est un nombre
idxDernier vaut longueur(pileR) - 1
pileR supprime idxDernier
pileS supprime idxDernier
fin si
fin tant que
// Ouverture du mur de sortie
murArc[targetIdx] vaut 0
@ Dessin des Murs du Labyrinthe
appelle dessinerArc(maToile, cx, cy, rMin, 0, 360, #38BDF8, 2)
an est un nombre
sec est un nombre
pour an de 0 à nbAnneaux - 1
pour sec de 0 à nbSecteurs - 1
idxCell est un nombre
idxCell vaut an * nbSecteurs + sec
a1deg est un nombre
a2deg est un nombre
a1deg vaut (sec * 360) / nbSecteurs
a2deg vaut ((sec + 1) * 360) / nbSecteurs
rayonInt est un nombre
rayonExt est un nombre
rayonInt vaut rMin + an * largeurAnneau
rayonExt vaut rayonInt + largeurAnneau
// Mur arc extérieur
si murArc[idxCell] = 1 alors
appelle dessinerArc(maToile, cx, cy, rayonExt, a1deg, a2deg, #38BDF8, 2)
fin si
// Mur radial horaire
si murRadial[idxCell] = 1 alors
radAngle est un nombre
radAngle vaut radians(a2deg)
xStart est un nombre
yStart est un nombre
xEnd est un nombre
yEnd est un nombre
xStart vaut cx + rayonInt * cosinus(radAngle)
yStart vaut cy + rayonInt * sinus(radAngle)
xEnd vaut cx + rayonExt * cosinus(radAngle)
yEnd vaut cy + rayonExt * sinus(radAngle)
ligne(maToile, xStart, yStart, xEnd, yEnd, #38BDF8, 2)
fin si
fin pour
fin pour
@ Tracé de la Solution (Lignes droites en radial, arcs en circulaires)
nbSol est un nombre
nbSol vaut longueur(solR)
si nbSol > 0 alors
ks est un nombre
pour ks de 0 à nbSol - 2
r1 est un nombre
s1 est un nombre
r2 est un nombre
s2 est un nombre
r1 vaut solR[ks]
s1 vaut solS[ks]
r2 vaut solR[ks + 1]
s2 vaut solS[ks + 1]
si r1 = r2 alors
// Déplacement le long d'un même anneau -> Tracé d'un arc curviligne
radCell est un nombre
radCell vaut rMin + (r1 + 0.5) * largeurAnneau
a1deg est un nombre
a1deg vaut (s1 + 0.5) * 360 / nbSecteurs
pasSec est un nombre
pasSec vaut 360 / nbSecteurs
a2deg est un nombre
si (s1 + 1) mod nbSecteurs = s2 alors
a2deg vaut a1deg + pasSec
sinon
a2deg vaut a1deg - pasSec
fin si
appelle dessinerArc(maToile, cx, cy, radCell, a1deg, a2deg, #F59E0B, 3)
sinon
// Déplacement entre deux anneaux -> Tracé d'une ligne radiale
angRad est un nombre
angRad vaut radians((s1 + 0.5) * 360 / nbSecteurs)
rad1 est un nombre
rad2 est un nombre
rad1 vaut rMin + (r1 + 0.5) * largeurAnneau
rad2 vaut rMin + (r2 + 0.5) * largeurAnneau
x1 est un nombre
y1 est un nombre
x2 est un nombre
y2 est un nombre
x1 vaut cx + rad1 * cosinus(angRad)
y1 vaut cy + rad1 * sinus(angRad)
x2 vaut cx + rad2 * cosinus(angRad)
y2 vaut cy + rad2 * sinus(angRad)
ligne(maToile, x1, y1, x2, y2, #F59E0B, 3)
fin si
fin pour
// Extension finale vers l'extérieur du labyrinthe
rDernier est un nombre
sDernier est un nombre
rDernier vaut solR[nbSol - 1]
sDernier vaut solS[nbSol - 1]
angFin est un nombre
angFin vaut radians((sDernier + 0.5) * 360 / nbSecteurs)
radCenterCell est un nombre
radOut est un nombre
radCenterCell vaut rMin + (rDernier + 0.5) * largeurAnneau
radOut vaut rMin + nbAnneaux * largeurAnneau + 12
xStartSol est un nombre
yStartSol est un nombre
xEndSol est un nombre
yEndSol est un nombre
xStartSol vaut cx + radCenterCell * cosinus(angFin)
yStartSol vaut cy + radCenterCell * sinus(angFin)
xEndSol vaut cx + radOut * cosinus(angFin)
yEndSol vaut cy + radOut * sinus(angFin)
ligne(maToile, xStartSol, yStartSol, xEndSol, yEndSol, #F59E0B, 3)
fin si
affiche maToile





