AMazeIng

Que fait ce bout de code selon vous? Et en quel langage est-il écrit?

char*M,A,Z,E=40,J[40],T[40];main(C){for(*J=A=scanf(M="%d",&C);
--            E;             J[              E]             =T
[E   ]=  E)   printf("._");  for(;(A-=Z=!Z)  ||  (printf("\n|"
)    ,   A    =              39              ,C             --
)    ;   Z    ||    printf   (M   ))M[Z]=Z[A-(E   =A[J-Z])&&!C
&    A   ==             T[                                  A]
|6<<27<rand()||!C&!Z?J[T[E]=T[A]]=E,J[T[A]=A-Z]=A,"_.":" |"];}

On a Maze program - The art of obfuscation par John Tromp

Mon tout premier projet professionnel 3D - Parcours 3D dans un labyrinthe, commande par la voix

/*========================================================================*\
||                                                                        ||
|| MODULE     : MAZE.C                                                    ||
||                                                                        ||
||                                                                        ||
|| DESCRIPTION: Programme qui genere un labyrinthe 2-D a l'aide de        ||
||              l'algorithme du "spanning tree" (arbre de recouvrement).  ||
||                                                                        ||
|| REMARQUES  : L'impression est en mode texte.                           ||
||                                                                        ||
||                                                                        ||
|| AUTEUR     : Karim Aktouf                                              ||
||                                                                        ||
|| CREATION   : le 10 aout 1993 <-- VOUS N'ÉTIEZ PAS NÉS!                 ||
||                                                                        ||
|| REVISION   : le 13 aout 1993                                           ||
||              le 03 juin 1996 - Les parametres ont maintenant une       ||
||                                valeur par defaut, et ajout de deux     ||
||                                autres parametres pour specifier le     ||
||                                charactere pour les lignes horizontales ||
||                                et celui des lignes verticales.         ||
||                                                                        ||
\*========================================================================*/
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
 
 
typedef enum _bool {FALSE, TRUE} BOOL;
 
typedef unsigned TypedeCell;
#define AUCUN     0
#define SPAN      1
#define FRONTIERE 2
 
typedef unsigned TypedeCase;
#define FERMEE  0
#define OUVERTE 1
 
 
/* La norme ANSI veut pas de enum dans des bit fields !!!
 * Dommage, c'est elegant...
 *
 * typedef enum _TypedeCell {AUCUN, SPAN, FRONTIERE} TypedeCell;
 * typedef enum _TypedeCase {FERMEE, OUVERTE}        TypedeCase;
 */
 
typedef struct _Cell  Cell;
typedef struct _FCell FCell;
 
 
struct _Cell
{
  TypedeCell Type:2;
  TypedeCase Bas:1,
             Droite:1;
};
 
 
struct _FCell                 /* Element de la liste circulaire de cellules */
{                             /* frontiere                                  */
  int L;
  int C;
  FCell *Next;
  FCell *Prev;
};
 
 
 
/*========================================================================*\
||                                                                        ||
||  Prototypes des fonctions utilisees                                    ||
||                                                                        ||
\*========================================================================*/
 
Cell  **InitMaze      (void);
void  GenerateMaze    (int L, int C);
void  PrintMaze       (char H, char V);
void  AddToFList      (FCell *FPtr);
void  RemoveFromFList (FCell *FPtr);
void  MarkAdj         (int L, int C, TypedeCell T);
FCell *ChooseRandomF  (void);
void  ConnectToSPAN   (int L, int C);
 
 
/*========================================================================*\
||                                                                        ||
||  Variables globables                                                   ||
||                                                                        ||
\*========================================================================*/
 
Cell  **MazePtr     = NULL;
FCell *FList        = NULL;
 
int  NbL=10,                    /* Nombre de lignes              */
     NbC=10,                    /* Nombre de colonnes            */
     NbF=0;                     /* Nombre d'elements dans FList  */
 
char H='|',                     /* Char. des lignes horizontales */
     V='|';                     /* Char. des lignes verticales   */
 
 
int main(int argc, char **argv)
{
  time_t t;
  int i=0;
 
  if (argc >= 2)
    if ((NbL = atoi(argv[1])) < 2)
    {
      printf("\nNombre de lignes invalide (%d) doit etre au moins 2.\n", NbL);
      return -1;
    }
 
  if (argc >= 3)
    if ((NbC = atoi(argv[2])) < 2)
    {
      printf("\nNombre de colonnes (%d) doit etre au moins 2.\n", NbC);
      return -1;
    }
 
  if (argc >= 4)
    H=*argv[3];
 
  if (argc >= 5)
    V=*argv[4];
 
  if (!(MazePtr = InitMaze()))
    return -1;
 
  /* On choisit une cellule au hasard entre i=1..NbL-1 et j=1..NbC-1 */
 
  srand((unsigned) time(&t));
  GenerateMaze((rand() % (NbL-1)) + 1, (rand() % (NbC-1)) + 1);
 
  PrintMaze(H, V);
 
  return 0;
}
 
 
/*========================================================================*\
||                                                                        ||
||   Fonction   : InitMaze                                                ||
||                                                                        ||
||   Description: Alloue l'espace memoire necessaire et initialise aux    ||
||                valeurs par defaut chaque cellule du labyrinthe.        ||
||                                                                        ||
||   Parametres : nil                                                     ||
||                                                                        ||
||   Retour     : Pointeur a l'espace memoire occupe par le labyrinthe    ||
||                                                                        ||
||   Remarques  : Le labyrinthe est organise en matrice 2-D dynamique,    ||
||                c'est-a-dire que le pointeur pointe a une COLONNE de    ||
||                pointeurs contenant NbL pointeurs, chacuns pointant sur ||
||                une LIGNE de NbC elements. l'acces a la cellule de la   ||
||                LIGNE i, COLONNE J se fait alors par [i][j] tout        ||
||                simplement, comme s'il s'agissait d'un tableau 2-D      ||
||                statique.                                               ||
||                                                                        ||
||   Auteur     : Karim Aktouf                                            ||
||                                                                        ||
||   Creation   : le 10 aout 1993                                         ||
||                                                                        ||
||   Revision   : le 13 aout 1993                                         ||
||                                                                        ||
\*========================================================================*/
Cell **InitMaze(void)
{
  Cell **Maze;
  int i, j;
 
  if (!(Maze = (Cell **) malloc(sizeof(Cell *) * NbL)))
    return NULL;
 
  for (i=0; i < NbL; i++)
  {
    if (!(Maze[i] = (Cell *) malloc(sizeof(Cell) * NbC)))
      return NULL;
 
    for (j=0; j < NbC; j++)
    {
      Maze[i][j].Type   = AUCUN;
      Maze[i][j].Bas    = FERMEE;
      Maze[i][j].Droite = FERMEE;
    }
  }
 
  /* Le mur du haut est un peu special */
  for (j=0; j < NbC; j++)
    Maze[0][j].Droite = OUVERTE;
 
  /* Meme chose pour le mur de droite  */
  for (i=0; i < NbL; i++)
    Maze[i][0].Bas    = OUVERTE;
 
  return Maze;
 
} /* InitMaze */
 
 
 
/*========================================================================*\
||                                                                        ||
||   Fonction   : GenerateMaze                                            ||
||                                                                        ||
||   Description: Genere le labyrinthe, en partant de la cellule L,C      ||
||                choisie prealablement au hasard.                        ||
||                                                                        ||
||   Parametres : L : Ligne de la cellule choisie au hasard               ||
||                C : Colonne de la cellule choisie au hasard.            ||
||                                                                        ||
||   Retour     : nil                                                     ||
||                                                                        ||
||   Remarques  : Pour ne pas generer a chaque fois le meme labyrinthe    ||
||                avec les meme parametres, il faut s'assurer             ||
||                d'initialiser le generateur de nombre aleatoires a une  ||
||                valeur differente a chaque fois.  Pour ce faire, il faut||
||                se baser sur la valeur de l'horloge de l'ordinateur, qui||
||                varie tout le temps:                                    ||
||                                                                        ||
||                                                                        ||
||                #include <time.h>                                       ||
||                #include <stdio.h>                                      ||
||                                                                        ||
||                void main(void)                                         ||
||                {                                                       ||
||                  time_t t; int i;                                      ||
||                                                                        ||
||                  srand ((unsigned) time(&t));                          ||
||                                                                        ||
||                  for (i=0; i < 100; i++)                               ||
||                    printf("%d ",rand() % 100);                         ||
||                }                                                       ||
||                                                                        ||
||                                                                        ||
||                Ce bout de code generera 100 nombres aleatoires         ||
||                differents a chaque appel du programme. Si on omet le   ||
||                srand(nombre associe a l'horloge), la meme serie de     ||
||                nombres pseudo-aleatoires sera generee a chaque fois.   ||
||                                                                        ||
||   Auteur     : Karim Aktouf                                            ||
||                                                                        ||
||   Creation   : le 10 aout 1993                                         ||
||                                                                        ||
||   Revision   : le 13 aout 1993                                         ||
||                                                                        ||
\*========================================================================*/
void GenerateMaze(int L, int C)
{
  FCell *FPtr;
  int LL, CC;
 
 /* On marque la cellule choisie au hasard comme etant SPAN        */
 MazePtr[L][C].Type = SPAN;
 
 /* On marque toutes les cellules adjacentes comme etant FRONTIERE */
 MarkAdj(L, C, FRONTIERE);
 
 while (FList)    /* Tant que la liste des cellules frontieres n'est pas vide */
 {
   /* On choisit une cellule frontiere au hasard */
   FPtr = ChooseRandomF();
 
   /* On la marque comme etant SPAN */
   LL = FPtr->L;
   CC = FPtr->C;
   RemoveFromFList(FPtr);
   MazePtr[LL][CC].Type = SPAN;
 
   /* On la connecte a une cellule SPAN adjacente prise au hasard */
   ConnectToSPAN(LL, CC);
 
   /* On marque les cellules adjacentes de type AUCUN a FRONTIERE */
   MarkAdj(LL, CC, FRONTIERE);
 }
 /* On choisit l'entree... */
 MazePtr[1][0].Droite     = OUVERTE;
 
 /* et la sortie.          */
 MazePtr[NbL-1][NbC-1].Droite = OUVERTE;
 
} /* GenerateMaze */
 
 
/*========================================================================*\
||                                                                        ||
||   Fonction   : ChooseRandomF                                           ||
||                                                                        ||
||   Description: Choisit au hasard une cellule frontiere parmis la liste ||
||                de cellules frontieres.                                 ||
||                                                                        ||
||   Parametres : nil                                                     ||
||                                                                        ||
||   Retour     : Pointeur a la cellule choisie.                          ||
||                                                                        ||
||   Remarques  : nil                                                     ||
||                                                                        ||
||   Auteur     : Karim Aktouf                                            ||
||                                                                        ||
||   Creation   : le 10 aout 1993                                         ||
||                                                                        ||
||   Revision   : le 11 aout 1993                                         ||
||                                                                        ||
\*========================================================================*/
 
FCell *ChooseRandomF(void)
{
  int tmp = (rand() % NbF) + 1;
  int i=1;
  FCell *FPtr = FList;
 
  while (i++ != tmp)
    FPtr = FPtr->Next;
 
  return FPtr;
 
} /* ChooseRandomF */
 
 
 
/*========================================================================*\
||                                                                        ||
||   Fonction   : ConnectToSPAN                                           ||
||                                                                        ||
||                                                                        ||
||   Description: Connecte la cellule L,C a une cellule SPAN adjacente,   ||
||                prise au hasard.                                        ||
||                                                                        ||
||   Parametres : L : Ligne de la cellule a connecter                     ||
||                C : Colonne de la cellule a connecter                   ||
||                                                                        ||
||   Retour     : nil                                                     ||
||                                                                        ||
||   Remarques  : nil                                                     ||
||                                                                        ||
||   Auteur     : Karim Aktouf                                            ||
||                                                                        ||
||   Creation   : le 12 aout 1993                                         ||
||                                                                        ||
||   Revision   : le 13 aout 1993                                         ||
||                                                                        ||
\*========================================================================*/
void ConnectToSPAN(int L, int C)
{
  BOOL Span_Adj[4] = {FALSE};
  int NbSpanAdj = 0;
  int tmp, i, j, SpanToConnect;
 
  /* cellule adjacente du haut   */
  if ((L-1 > 0) && (MazePtr[L-1][C].Type == SPAN))
  {
    Span_Adj[0] = TRUE;
    NbSpanAdj++;
  }
 
  /* cellule adjacente de droite */
  if ((C+1 < NbC) && (MazePtr[L][C+1].Type == SPAN))
  {
    Span_Adj[1] = TRUE;
    NbSpanAdj++;
  }
 
  /* cellule adjacente du bas    */
  if ((L+1 < NbL) && (MazePtr[L+1][C].Type == SPAN))
  {
    Span_Adj[2] = TRUE;
    NbSpanAdj++;
  }
 
  /* cellule adjacente de gauche */
  if ((C-1 > 0) && (MazePtr[L][C-1].Type == SPAN))
  {
    Span_Adj[3] = TRUE;
    NbSpanAdj++;
  }
 
  if (NbSpanAdj < 1)     /* Ne devrais jamais arriver!!! */
  {
     printf("\nWow!!!  Pas de cellule SPAN adjacente a Cell(%d, %d)!!!!\n", L, C);
     exit(-1);
  }
 
  if (NbSpanAdj == 1)
  {
    tmp=-1;
    while(Span_Adj[++tmp] == FALSE)
      ;
 
    SpanToConnect = tmp;
  }
  else
  {
    tmp = (rand() % NbSpanAdj) + 1;
 
    i=0;
    for (j=0; j < 4; j++)
    {
      if (Span_Adj[j] == TRUE)
      {
        if (++i == tmp)
        {
          SpanToConnect = j;
          break;
        }
      }
    }
  }
 
  /* Ok, La cellule adjacente numero SpanToConnect est la SPAN a connecter */
  switch(SpanToConnect)
  {
    case 0: MazePtr[L-1][C].Bas    = OUVERTE;
            break;
 
    case 1: MazePtr[L][C].Droite   = OUVERTE;
            break;
 
    case 2: MazePtr[L][C].Bas      = OUVERTE;
            break;
 
    case 3: MazePtr[L][C-1].Droite = OUVERTE;
            break;
  }
 
} /* ConnectToSPAN */
 
 
 
/*========================================================================*\
||                                                                        ||
||   Fonction   : MarkAdj                                                 ||
||                                                                        ||
||   Description: Marque comme etant de type FRONTIERE toutes les cellules||
||                de type AUCUN voisines de la cellule L,C                ||
||                                                                        ||
||   Parametres : L : Ligne de la cellule dont on doit marquer les        ||
||                    cellules adjacentes.                                ||
||                C : Colonne de la cellule dont on doit marquer les      ||
||                    cellules adjacentes.                                ||
||                                                                        ||
||   Retour     : nil                                                     ||
||                                                                        ||
||   Remarques  : nil                                                     ||
||                                                                        ||
||   Auteur     : Karim Aktouf                                            ||
||                                                                        ||
||   Creation   : le 11 aout 1993                                         ||
||                                                                        ||
||   Revision   : le 11 aout 1993                                         ||
||                                                                        ||
\*========================================================================*/
void MarkAdj(int L, int C, TypedeCell T)
{
  FCell *FPtr;
 
  /* Cellule adjacente du haut   */
  if (L-1 > 0)
  {
    if (MazePtr[L-1][C].Type == AUCUN)
    {
      MazePtr[L-1][C].Type = T;
 
      if (!(FPtr = (FCell *) malloc(sizeof(FCell))))
      {
        printf("\nPas assez de memoire pour FPtr\n");
        exit(-1);
      }
      FPtr->L = L-1;
      FPtr->C = C;
      AddToFList(FPtr);
    }
  }
 
  /* Cellule adjacente du bas   */
  if (L+1 < NbL)
  {
    if (MazePtr[L+1][C].Type == AUCUN)
    {
      MazePtr[L+1][C].Type = T;
 
      if (!(FPtr = (FCell *) malloc(sizeof(FCell))))
      {
        printf("\nPas assez de memoire pour FPtr\n");
        exit(-1);
      }
      FPtr->L = L+1;
      FPtr->C = C;
      AddToFList(FPtr);
    }
  }
 
  /* Cellule adjacente de gauche */
  if (C-1 > 0)
  {
    if (MazePtr[L][C-1].Type == AUCUN)
    {
      MazePtr[L][C-1].Type = T;
 
      if (!(FPtr = (FCell *) malloc(sizeof(FCell))))
      {
        printf("\nPas assez de memoire pour FPtr\n");
        exit(-1);
      }
      FPtr->L = L;
      FPtr->C = C-1;
      AddToFList(FPtr);
    }
  }
 
  /* Cellule adjacente de droite */
  if (C+1 < NbC)
  {
    if (MazePtr[L][C+1].Type == AUCUN)
    {
      MazePtr[L][C+1].Type = T;
 
      if (!(FPtr = (FCell *) malloc(sizeof(FCell))))
      {
        printf("\nPas assez de memoire pour FPtr\n");
        exit(-1);
      }
      FPtr->L = L;
      FPtr->C = C+1;
      AddToFList(FPtr);
    }
  }
} /* MarkAdj */
 
 
 
/*========================================================================*\
||                                                                        ||
||   Fonction   : AddToFList                                              ||
||                                                                        ||
||   Description: Ajoute l'element pointe par FPtr dans la liste          ||
||                circulaire a double liens des cellules de type FRONTIERE||
||                                                                        ||
||   Parametres : FPtr : Pointeur a l'element a ajouter.                  ||
||                                                                        ||
||   Retour     : nil                                                     ||
||                                                                        ||
||   Remarques  : nil                                                     ||
||                                                                        ||
||   Auteur     : Karim Aktouf                                            ||
||                                                                        ||
||   Creation   : le 10 aout 1993                                         ||
||                                                                        ||
||   Revision   : le 10 aout 1993                                         ||
||                                                                        ||
\*========================================================================*/
void AddToFList(FCell *FPtr)
{
  if (!FList)
  {
    FList       = FPtr;
    FList->Next = FList;
    FList->Prev = FList;
  }
  else
  {
    FList->Prev->Next = FPtr;
    FPtr->Next        = FList;
    FPtr->Prev        = FList->Prev;
    FList->Prev       = FPtr;
  }
 
  NbF++;
 
} /* AddToFList */
 
 
 
/*========================================================================*\
||                                                                        ||
||   Fonction   : RemoveFromFList                                         ||
||                                                                        ||
||   Description: retire de la liste circulaire l'element pointe par FPtr.||
||                                                                        ||
||   Parametres : FPtr : Pointeur a l'element a retirer.                  ||
||                                                                        ||
||   Retour     : nil                                                     ||
||                                                                        ||
||   Remarques  : nil                                                     ||
||                                                                        ||
||   Auteur     : Karim Aktouf                                            ||
||                                                                        ||
||   Creation   : le 10 aout 1993                                         ||
||                                                                        ||
||   Revision   : le 10 aout 1993                                         ||
||                                                                        ||
\*========================================================================*/
void RemoveFromFList(FCell *FPtr)
{
  if (FList->Next == FList)
  {
    free(FList);
    FList = NULL;
  }
  else
  {
    if (FList == FPtr)
       FList = FPtr->Next;
 
    FPtr->Prev->Next = FPtr->Next;
    FPtr->Next->Prev = FPtr->Prev;
 
    free(FPtr);
  }
 
  NbF--;
 
} /* RemoveFromFList */
 
 
 
/*========================================================================*\
||                                                                        ||
||   Fonction   : PrintMaze                                               ||
||                                                                        ||
||   Description: Imprime le labyrinthe en mode texte. Chaque cellule     ||
||                est representee par:                                    ||
||                                                                        ||
||       Case toujours ouverte->    HV <- Case droite, OUVERTE, FERMEE    ||
||                                                                        ||
||       Case Bas, OUVERTE,   -> VH HV <- Case toujours fermee            ||
||                 FERMEE                                                 ||
||                                                                        ||
||   Parametres : nil                                                     ||
||                                                                        ||
||   Retour     : nil                                                     ||
||                                                                        ||
||   Remarques  : nil                                                     ||
||                                                                        ||
||   Auteur     : Karim Aktouf                                            ||
||                                                                        ||
||   Creation   : le 12 aout 1993                                         ||
||                                                                        ||
||   Revision   : le 12 aout 1993                                         ||
||                                                                        ||
\*========================================================================*/
void PrintMaze(char H, char V)
{
  int i, j;
 
  printf("\n\nMaze %dx%d:", NbL, NbC);
  for (i=0; i < NbL; i++)
  {
    printf("\n");
    for (j=0; j < NbC; j++)
      if (MazePtr[i][j].Droite == OUVERTE)
        printf("    ");
      else
	printf("  %c%c", H, V);
 
    printf("\n");
    for (j=0; j < NbC; j++)
      if (MazePtr[i][j].Bas == OUVERTE)
        printf("  %c%c", H, V);
      else
	printf("%c%c%c%c", V, H, H, V);
  }
  printf("\n");
 
 
} /* PrintMaze */
 

Générateur de Labyrinthes

Un peu de théorie Here's some info I wrote up once for a C class assignment. I can send you C code if you want, but your request asked for the algorithm...

…Rickperry@vill.edu == r.perry@ieee.org

Dr. Rick Perry, ECE Department, Villanova University, Villanova, PA 19085

215-645-4969, fax: 215-645-4436, hm: 215-259-8734

Creating a maze

Here is an example of a maze of size 5 by 8:

	        0   1   2   3   4   5   6   7
	     XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
     0 XX  XX              XX          XX
	     XX  XX  XXXXXX  XXXXXX  XXXXXX  XX
     1 XX  XX  XX                  XX
	     XX  XXXXXXXXXXXXXX  XXXXXXXXXX  XX
     2 XX  XX              XX  XX
	     XX  XXXXXX  XXXXXX  XX  XX  XX  XX
     3 XX                  XX  XX  XX  XX
	     XX  XX  XXXXXX  XXXXXX  XX  XXXXXX
     4 XX  XX  XX          XX  XX      XX
       XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 

Highlight of the cells:

           0    1    2    3    4    5    6    7
       XX|XXXX|XXXX|XXXX|XXXX|XXXX|XXXX|XXXX|XXXX|
       --|----|----|----|----|----|----|----|----|
     0 XX|  XX|    |    |    |  XX|    |    |  XX|
       XX|  XX|  XX|XXXX|  XX|XXXX|  XX|XXXX|  XX|
       --|----|----|----|----|----|----|----|----|
     1 XX|  XX|  XX|    |    |    |    |  XX|    |
       XX|  XX|XXXX|XXXX|XXXX|  XX|XXXX|XXXX|  XX|
       --|----|----|----|----|----|----|----|----|
     2   |    |  XX|  XX|    |    |    |  XX|  XX|
       XX|  XX|XXXX|  XX|XXXX|  XX|  XX|  XX|  XX|
       --|----|----|----|----|----|----|----|----|
     3 XX|    |    |    |    |  XX|  XX|  XX|  XX|
       XX|  XX|  XX|XXXX|  XX|XXXX|  XX|  XX|XXXX|
       --|----|----|----|----|----|----|----|----|
     4 XX|  XX|  XX|    |    |  XX|  XX|    |  XX|
       XX|XXXX|XXXX|XXXX|XXXX|XXXX|XXXX|XXXX|XXXX|
       --|----|----|----|----|----|----|----|----|
 

The maze is a two-dimensional array of cells. In this display, each cell
is two-characters high and 4 characters wide. There are two blanks in
the top left corner of each cell, and two X’s in the bottom right corner
of each cell. Each cell has a right wall and a bottom wall which may or
may not be open. For example, cell (0,0) has its right wall closed and
its bottom wall open, cell (0,1) has both walls open, cell (1,1) has both
walls closed.

In addition to the cells of the maze, a top wall of X’s and a left wall of XX’s
(with one opening for the maze entrance) comprise the display of the maze.
The bottom wall of the maze is formed by the last row of cells, none of which
have their bottom wall open. The right wall of the maze is formed by the last
column of cells, one of which has its right wall open for the maze exit.

Note that there is only one path between any two cells of the maze. Any cell
can be reached from another cell by a unique path. Such a set of paths between
cells is called a spanning tree. The creation of a maze starts with the all
the cell walls closed and systematically opens cell walls until the complete
spanning tree is formed. The maze entrance and exit are then chosen at
random or fixed locations on opposite sides of the maze.

During the formation of the maze, there will be three types of cells in the
array:

-  those that are already in the spanning tree.
 
-  those that are not in the spanning tree but are immediately
   adjacent to some cell in the spanning tree.  These cells are
   called Frontier Cells.
 
-  all the other cells, which are neither in the spanning tree
   nor the frontier.
 

A bit-field structure, together with some symbolic constants for the
allowed values of the fields, is appropriate to represent a maze cell,
for example:

#define NEITHER  0	/* kinds of cells */
#define SPANTREE 1
#define FRONTIER 2
 
#define CLOSED 0	/* walls */
#define OPEN   1
 
typedef struct {
	unsigned int kind : 2;	/* NEITHER, SPANTREE, or FRONTIER */
	unsigned int down : 1;	/* CLOSED or OPEN */
	unsigned int right: 1;	/* CLOSED or OPEN */
} Cell;
 

If cell is a two-dimensional array of these Cell’s, then typical statements
using cell would be:

cell[i][j].kind = FRONTIER;
 
cell[i][j].down = OPEN;
 
if( cell[i][j].kind == NEITHER) ...
 

The algorithm to create a maze is:

1.  Randomly choose any cell of the array and mark it as a spanning
    tree cell.  The four cells immediately adjacent to it (fewer if
    it is on an edge or in a corner) are then marked as frontier cells.
 
Repeat Steps 2 and 3 until there are no frontier cells left:
 
2.  Randomly choose a frontier cell and connect it to one cell of the
    current spanning tree by opening one wall.  If it is adjacent to
    more than one cell of the spanning tree (it could be adjacent to
    as many as four), randomly choose one of them to connect it to
    and mark the appropriate wall as open.  Note: to connect cell[i][j]
    to the cell above it means  cell[i-1][j].down = OPEN;
 
3.  Check the cells adjacent to the cell just added to the spanning
    tree.  Any such cells that are neither part of the spanning tree
    nor the frontier must now be marked as frontier cells.
 

To be able to easily and efficiently choose a random frontier cell in Step 2,
the row and column indices of the frontier cells must be kept in a circularly
linked list. Such a data structure would be declared like:

typedef struct element {
	int row, col;
	struct element *next;
} Node;
 

Example illustrating maze creation

In this example, ++ is used instead of XX for the walls so that the F and S
characters, indicating Frontier and Spanning tree cells, stand out clearly.

     0   1   2   3
   ++++++++++++++++++
 0 ++  ++  ++F ++  ++	Cell (1,2) chosen as the initial spanning tree.
   ++++++++++++++++++
 1 ++  ++F ++S ++F ++	Adjacent cells (0,2), (1,1), (1,3), and (2,2)
   ++++++++++++++++++
 2 ++  ++  ++F ++  ++	added to frontier list.
   ++++++++++++++++++
 3 ++  ++  ++  ++  ++
   ++++++++++++++++++
 4 ++  ++  ++  ++  ++
   ++++++++++++++++++
 
     0   1   2   3
   ++++++++++++++++++
 0 ++  ++  ++F ++  ++
   ++++++++++++++++++
 1 ++  ++F ++S ++F ++
   ++++++++++  ++++++
 2 ++  ++F ++S ++F ++	Frontier cell (2,2) added to the spanning tree.
   ++++++++++++++++++
 3 ++  ++  ++F ++  ++	Adjacent cells (2,1), (2,3) and (3,2)
   ++++++++++++++++++
 4 ++  ++  ++  ++  ++	added to the frontier list.
   ++++++++++++++++++
 
     0   1   2   3
   ++++++++++++++++++
 0 ++  ++  ++F ++  ++
   ++++++++++++++++++
 1 ++  ++F ++S ++F ++
   ++++++++++  ++++++
 2 ++F ++S   S ++F ++	Frontier cell (1,2) added to the spanning tree.
   ++++++++++++++++++
 3 ++  ++F ++F ++  ++	Adjacent cells (0,2) and (1,3) added to the
   ++++++++++++++++++
 4 ++  ++  ++  ++  ++	frontier list.
   ++++++++++++++++++
 

L'Algorithme de Backtracking Récursif

Principe Fondamental L'algorithme de backtracking récursif est une méthode de génération de labyrinthes basée sur la recherche en profondeur (Depth-First Search - DFS). Il crée un labyrinthe en "creusant" des passages à travers une grille initialement remplie de murs.

Fonctionnement Étape par Étape

  1. Initialisation
    • On commence avec une grille où toutes les cellules ont leurs quatre murs (Nord, Sud, Est, Ouest)
    • Toutes les cellules sont marquées comme “non visitées”
    • On choisit une cellule de départ (généralement (0,0))
  2. Exploration
    • Marquer la cellule courante comme “visitée”
    • Obtenir la liste des cellules voisines non visitées
  3. Décision
    • Si des voisins non visités existent:
      • Choisir un voisin aléatoire
      • Empiler la cellule courante (pour pouvoir y revenir)
      • Supprimer le mur entre la cellule courante et le voisin choisi
      • Faire du voisin la nouvelle cellule courante
      • Répéter depuis l’étape 2
    • Si aucun voisin non visité n’existe:
      • Dépiler une cellule (backtracking/retour en arrière)
      • Faire de cette cellule la cellule courante
      • Répéter depuis l’étape 2
  4. Terminaison
    • L’algorithme se termine quand la pile est vide et qu’il n’y a plus de cellule à explorer

Visualisation de l’Algorithme

Étape 1: Départ en (0,0)
┌─┬─┬─┐
│S│ │ │    S = Start (cellule de départ)
├─┼─┼─┤
│ │ │ │
├─┼─┼─┤
│ │ │ │
└─┴─┴─┘
 
Étape 2: Exploration vers l'Est
┌─┬─┬─┐
│S X│ │    X = cellule visitée
├─┼─┼─┤
│ │ │ │
├─┼─┼─┤
│ │ │ │
└─┴─┴─┘
 
Étape 3: Backtracking quand bloqué
┌─┬─┬─┐
│S X X│    On revient en arrière
├─┼ ┼─┤    puis on explore Sud
│ │X│ │
├─┼─┼─┤
│ │ │ │
└─┴─┴─┘
 
... et ainsi de suite jusqu'à ce que
toutes les cellules soient visitées.
 

Propriétés Mathématiques

  • Complexité temporelle: O(n) où n est le nombre de cellules
  • Complexité spatiale: O(n) pour la pile de backtracking
  • Garanties:
    • Chaque cellule est visitée exactement une fois
    • Le labyrinthe est connexe (tous les points sont accessibles)
    • Le labyrinthe est “parfait” (sans boucles, un seul chemin entre deux points)
    • La génération est uniforme (tous les labyrinthes possibles ont la même probabilité)

Avantages et Inconvénients

Avantages:

  • Simple à mettre en oeuvre
  • Génère des labyrinthes avec de longs couloirs sinueux
  • Utilisation efficace de la mémoire
  • Visuellement intéressant (structure organique)

Inconvénients:

  • Peut créer des labyrinthes avec un biais directionnel
  • Les chemins ont tendance à être longs et tortueux
  • Moins de contrôle sur la difficulté du labyrinthe

Histoire de l'Algorithme

Origines et Développement

L’algorithme de backtracking pour la génération de labyrinthes trouve ses racines dans les années 1960-1970, période dorée de l’exploration des algorithmes de graphes.

Contexte Historique

Années 1960 - Les Prémices

  • L’algorithme de recherche en profondeur (DFS), base du backtracking, est formalisé par les pionniers de l’informatique
  • Les premiers ordinateurs permettent d’explorer des algorithmes de génération procédurale
  • Les labyrinthes deviennent un terrain de jeu pour tester les algorithmes de graphes

Années 1970 - Popularisation

  • Les ordinateurs personnels émergent, rendant la programmation accessible
  • Les magazines d’informatique publient des algorithmes de génération de labyrinthes
  • Les jeux vidéo utilisent des labyrinthes procéduraux (début des roguelikes)

Années 1980-1990 - Raffinement

  • Étude systématique des différentes méthodes de génération
  • Comparaison des propriétés visuelles et mathématiques
  • Le recursive backtracking devient l’algorithme de référence pour les labyrinthes “parfaits”

Pourquoi le Backtracking Récursif?

L’algorithme s’est imposé pour plusieurs raisons:

  1. Élégance conceptuelle - L’idée de “creuser” des tunnels en explorant puis en revenant en arrière est intuitive
  2. Simplicité d’implémentation - Quelques dizaines de lignes suffisent
  3. Qualité esthétique - Produit des labyrinthes avec de longs passages sinueux, visuellement satisfaisants
  4. Performance - Complexité linéaire, très rapide même sur de grandes grilles

Variantes et Évolution

Au fil du temps, d’autres algorithmes ont été développés:

  • Algorithme de Prim - Croissance aléatoire
  • Algorithme de Kruskal - Union-find sur les arêtes
  • Division récursive - Diviser et conquérir
  • Algorithme d’Eller - Génération ligne par ligne

Mais le backtracking récursif reste le plus populaire pour sa simplicité et ses résultats esthétiques.

Architecture du Code

Structure Globale

MazeGenerator (Classe principale)

├── __init__()              # Initialisation de la grille
├── generate_maze()         # Algorithme de backtracking
├── create_entrance_exit()  # Création des accès
└── create_maze_geometry()  # Génération 3D dans Blender

    ├── _create_wall()      # Création d'un mur individuel
    ├── _create_floor()     # Création du sol
    ├── _create_markers()   # Marqueurs entrée/sortie
    └── _get_marker_position() # Calcul de position
 

Diagramme de Flux

┌─────────────────────────┐
│  Initialisation         │
│  - Créer grille         │
│  - Tous murs présents   │
└───────────┬─────────────┘


┌─────────────────────────┐
│  Génération Labyrinthe  │
│  - Backtracking DFS     │
│  - Suppression de murs  │
└───────────┬─────────────┘


┌─────────────────────────┐
│  Création Entrée/Sortie │
│  - Sélection aléatoire  │
│  - Sur périmètre        │
└───────────┬─────────────┘


┌─────────────────────────┐
│  Génération Géométrie   │
│  - Création murs 3D     │
│  - Sol et marqueurs     │
└─────────────────────────┘
 

Explication Détaillée du Code

1. Initialisation (__init__)

def __init__(self, width=10, height=10, wall_thickness=0.1,
             wall_height=2.0, cell_size=2.0):
    self.width = width
    self.height = height
    self.wall_thickness = wall_thickness
    self.wall_height = wall_height
    self.cell_size = cell_size
 
    # Grille: chaque cellule a 4 murs possibles
    self.grid = [[{'N': True, 'S': True, 'E': True, 'W': True,
                   'visited': False}
                 for _ in range(width)] for _ in range(height)]
 
    self.entrance = None
    self.exit = None
 

Explication:
- La grille est une matrice 2D de dictionnaires
- Chaque cellule stocke l’état de ses 4 murs (True = présent)
- ‘N’, ‘S’, ‘E’, ‘W’ correspondent aux directions cardinales
- visited permet de suivre la progression de l’algorithme

Structure de données:

grid[y][x] = {
    'N': True,      # Mur Nord
    'S': True,      # Mur Sud
    'E': True,      # Mur Est
    'W': True,      # Mur Ouest
    'visited': False # État de visite
}
 

2. Génération du Labyrinthe (generate_maze)

def generate_maze(self):
    stack = []
    current = (0, 0)
    self.grid[current[1]][current[0]]['visited'] = True
 
    while True:
        neighbors = self._get_unvisited_neighbors(current)
 
        if neighbors:
            # Avancer: choisir un voisin
            next_cell = random.choice(neighbors)
            stack.append(current)
            self._remove_wall(current, next_cell)
            self.grid[next_cell[1]][next_cell[0]]['visited'] = True
            current = next_cell
        elif stack:
            # Reculer: backtracking
            current = stack.pop()
        else:
            # Terminé: tous les chemins explorés
            break
 

Analyse détaillée:

  1. Pile (Stack):
    • Stocke le chemin parcouru
    • Permet de revenir en arrière (backtracking)
    • Structure LIFO (Last In, First Out)
  2. Boucle principale:
    • Continue tant qu’il reste des cellules dans la pile
    • Gère deux cas: exploration et backtracking
  3. Choix aléatoire:
    • random.choice(neighbors) assure l’uniformité
    • Chaque labyrinthe généré est unique

Invariants:

  • Une cellule visitée l’est définitivement
  • La pile contient toujours un chemin valide vers l’origine
  • À la fin, toutes les cellules sont visitées

3. Recherche de Voisins (_get_unvisited_neighbors)

def _get_unvisited_neighbors(self, cell):
    x, y = cell
    neighbors = []
 
    # Nord (y-1)
    if y > 0 and not self.grid[y-1][x]['visited']:
        neighbors.append((x, y-1))
 
    # Sud (y+1)
    if y < self.height - 1 and not self.grid[y+1][x]['visited']:
        neighbors.append((x, y+1))
 
    # Est (x+1)
    if x < self.width - 1 and not self.grid[y][x+1]['visited']:
        neighbors.append((x+1, y))
 
    # Ouest (x-1)
    if x > 0 and not self.grid[y][x-1]['visited']:
        neighbors.append((x-1, y))
 
    return neighbors
 

Points importants:

  • Vérification des limites de la grille (0 ≤ x < width, 0 ≤ y < height)
  • Seules les cellules non visitées sont retournées
  • L’ordre de vérification n’affecte pas l’uniformité (choix aléatoire après)

Système de coordonnées:

     x →
y  ┌─────┬─────┬─────┐
↓  │(0,0)│(1,0)│(2,0)│
   ├─────┼─────┼─────┤
   │(0,1)│(1,1)│(2,1)│
   ├─────┼─────┼─────┤
   │(0,2)│(1,2)│(2,2)│
   └─────┴─────┴─────┘
 

4. Suppression de Murs (_remove_wall)

def _remove_wall(self, current, next_cell):
    x1, y1 = current
    x2, y2 = next_cell
 
    if x2 == x1 + 1:  # Mouvement Est
        self.grid[y1][x1]['E'] = False
        self.grid[y2][x2]['W'] = False
    elif x2 == x1 - 1:  # Mouvement Ouest
        self.grid[y1][x1]['W'] = False
        self.grid[y2][x2]['E'] = False
    elif y2 == y1 + 1:  # Mouvement Sud
        self.grid[y1][x1]['S'] = False
        self.grid[y2][x2]['N'] = False
    elif y2 == y1 - 1:  # Mouvement Nord
        self.grid[y1][x1]['N'] = False
        self.grid[y2][x2]['S'] = False
 

Principe de symétrie:

  • Supprimer un mur affecte deux cellules
  • Le mur Est de A est le mur Ouest de B
  • Maintient la cohérence de la structure

Visualisation:

Avant:                Après:
┌───┬───┐            ┌───┬───┐
│ A │ B │    →       │ A   B │
└───┴───┘            └───┴───┘
     ↑                    ↑
  Mur entre A et B    Mur supprimé
 

5. Création Entrée/Sortie (create_entrance_exit)

def create_entrance_exit(self):
    perimeter_walls = []
 
    # Mur Nord (haut)
    for x in range(self.width):
        perimeter_walls.append((x, 0, 'N'))
 
    # Mur Sud (bas)
    for x in range(self.width):
        perimeter_walls.append((x, self.height - 1, 'S'))
 
    # Mur Ouest (gauche)
    for y in range(self.height):
        perimeter_walls.append((0, y, 'W'))
 
    # Mur Est (droite)
    for y in range(self.height):
        perimeter_walls.append((self.width - 1, y, 'E'))
 
    # Sélection aléatoire
    random.shuffle(perimeter_walls)
    entrance_wall = perimeter_walls[0]
    exit_wall = perimeter_walls[1]
 
    # Suppression des murs
    x, y, direction = entrance_wall
    self.grid[y][x][direction] = False
    self.entrance = entrance_wall
 
    x, y, direction = exit_wall
    self.grid[y][x][direction] = False
    self.exit = exit_wall
 

Stratégie:

  1. Collecter tous les murs du périmètre
  2. Mélanger aléatoirement la liste
  3. Prendre les deux premiers (garantit qu’ils sont différents)

Nombre de positions possibles:

  • Mur Nord: width positions
  • Mur Sud: width positions
  • Mur Est: height positions
  • Mur Ouest: height positions
  • Total: 2 × (width + height) positions

6. Génération de la Géométrie 3D

a) Création des Murs (create_maze_geometry)

for y in range(self.height):
    for x in range(self.width):
        cell = self.grid[y][x]
        pos_x = x * self.cell_size
        pos_y = y * self.cell_size
 
        # Mur Nord
        if cell['N']:
            self._create_wall(pos_x, pos_y,
                            self.cell_size, self.wall_thickness,
                            maze_collection, mat)
 
        # Mur Ouest
        if cell['W']:
            self._create_wall(pos_x, pos_y,
                            self.wall_thickness, self.cell_size,
                            maze_collection, mat)
 

Optimisation:

  • Seuls les murs intérieurs sont créés lors du parcours
  • Les murs Sud et Est sont créés uniquement pour la dernière ligne/colonne
  • Évite la duplication de géométrie

Calcul de position:

Cellule (x, y) → Position 3D (pos_x, pos_y)
pos_x = x × cell_size
pos_y = y × cell_size
 
Exemple: cellule (2, 1) avec cell_size=2.0
→ Position (4.0, 2.0) dans l'espace Blender
 

b) Création d’un Mur (_create_wall)

def _create_wall(self, x, y, size_x, size_y, collection, material):
    # Créer un cube unitaire
    bpy.ops.mesh.primitive_cube_add(
        size=1,
        location=(x + size_x/2, y + size_y/2, self.wall_height/2)
    )
    wall = bpy.context.active_object
 
    # Mise à l'échelle pour la taille désirée
    wall.scale = (size_x, size_y, self.wall_height)
 
    # Application du matériau
    wall.data.materials.append(material)
 
    # Déplacement vers la collection du labyrinthe
    for coll in wall.users_collection:
        coll.objects.unlink(wall)
    collection.objects.link(wall)
 

Processus Blender:

  1. Création d’un cube avec primitive_cube_add
  2. Positionnement au centre du mur
  3. Mise à l’échelle pour obtenir les bonnes dimensions
  4. Application du matériau bleu
  5. Organisation dans la collection “Maze”

Position du centre:

  • Pour un mur horizontal: (x + size_x/2, y + wall_thickness/2, height/2)
  • Pour un mur vertical: (x + wall_thickness/2, y + size_y/2, height/2)

c) Création des Marqueurs (_create_markers)

def _create_markers(self, collection):
    # Marqueur d'entrée (vert)
    entrance_pos = self._get_marker_position(self.entrance)
    bpy.ops.mesh.primitive_cylinder_add(
        radius=0.5,
        depth=0.3,
        location=(entrance_pos[0], entrance_pos[1], 0.15)
    )
    entrance_marker = bpy.context.active_object
    entrance_marker.name = "Entrance_Marker"
 
    # Matériau vert avec émission
    mat_entrance = bpy.data.materials.new(name="EntranceMaterial")
    mat_entrance.use_nodes = True
    bsdf = mat_entrance.node_tree.nodes["Principled BSDF"]
    bsdf.inputs["Base Color"].default_value = (0.0, 1.0, 0.0, 1.0)
    bsdf.inputs["Emission Color"].default_value = (0.0, 1.0, 0.0, 1.0)
    bsdf.inputs["Emission Strength"].default_value = 2.0
 

Matériaux avec émission:

  • Base Color: Couleur de base de l’objet
  • Emission Color: Couleur de la lumière émise
  • Emission Strength: Intensité lumineuse (0 = pas d’émission)

Effet visuel:

  • Les marqueurs brillent légèrement dans le rendu
  • Facilement repérables même dans des labyrinthes complexes
  • Vert = début (go!), Rouge = fin (stop!)

d) Calcul de Position des Marqueurs

def _get_marker_position(self, wall_info):
    x, y, direction = wall_info
    pos_x = x * self.cell_size + self.cell_size / 2
    pos_y = y * self.cell_size + self.cell_size / 2
 
    # Ajustement selon la direction
    if direction == 'N':
        pos_y = y * self.cell_size
    elif direction == 'S':
        pos_y = (y + 1) * self.cell_size
    elif direction == 'W':
        pos_x = x * self.cell_size
    elif direction == 'E':
        pos_x = (x + 1) * self.cell_size
 
    return (pos_x, pos_y)
 

Logique de positionnement:

  • Par défaut: centre de la cellule
  • Ajustement: déplacer vers le mur concerné
  • Résultat: marqueur exactement sur l’ouverture

Exemple:

Cellule (2, 1), direction 'E', cell_size = 2.0
→ Centre: (5.0, 3.0)
→ Ajustement Est: pos_x = 3 × 2.0 = 6.0
→ Position finale: (6.0, 3.0)
 

Utilisation et Personnalisation

Paramètres de Base

maze = MazeGenerator(
    width=15,           # Largeur (nombre de cellules en X)
    height=15,          # Hauteur (nombre de cellules en Y)
    wall_thickness=0.2, # Épaisseur des murs (en unités Blender)
    wall_height=3.0,    # Hauteur des murs
    cell_size=2.0       # Taille d'une cellule
)
 

Exemples de Configuration

Labyrinthe Compact

# Petit labyrinthe, murs fins, basse hauteur
maze = MazeGenerator(width=8, height=8, wall_thickness=0.1,
                     wall_height=2.0, cell_size=1.5)
 
  • Idéal pour: tests rapides, visualisation simple
  • Temps de génération: < 1 seconde
  • Dimensions finales: 12 × 12 unités Blender

Labyrinthe Épique

# Grand labyrinthe, murs épais, haute hauteur
maze = MazeGenerator(width=50, height=50, wall_thickness=0.3,
                     wall_height=5.0, cell_size=3.0)
 
  • Idéal pour: jeux vidéo, explorations complexes
  • Temps de génération: quelques secondes
  • Dimensions finales: 150 × 150 unités Blender

Labyrinthe Artistique

# Proportions asymétriques
maze = MazeGenerator(width=30, height=15, wall_thickness=0.15,
                     wall_height=4.0, cell_size=2.5)
 
  • Idéal pour: designs uniques, compositions visuelles
  • Forme rectangulaire plutôt que carrée

Modifications Avancées

1. Changer les Couleurs

Murs:

# Dans create_maze_geometry(), ligne créant le matériau
mat.node_tree.nodes["Principled BSDF"].inputs[0].default_value = (R, G, B, 1.0)
 
# Exemples:
# Murs rouges: (1.0, 0.0, 0.0, 1.0)
# Murs dorés: (1.0, 0.84, 0.0, 1.0)
# Murs noirs: (0.0, 0.0, 0.0, 1.0)
 

Marqueurs:

# Entrée en bleu au lieu de vert
bsdf.inputs["Base Color"].default_value = (0.0, 0.0, 1.0, 1.0)
bsdf.inputs["Emission Color"].default_value = (0.0, 0.0, 1.0, 1.0)
 

2. Ajouter un Plafond

def _create_ceiling(self, collection):
    """Créer un plafond pour le labyrinthe."""
    floor_size_x = self.width * self.cell_size
    floor_size_y = self.height * self.cell_size
 
    bpy.ops.mesh.primitive_plane_add(
        size=1,
        location=(floor_size_x/2, floor_size_y/2, self.wall_height)
    )
    ceiling = bpy.context.active_object
    ceiling.scale = (floor_size_x, floor_size_y, 1)
    ceiling.name = "MazeCeiling"
    # ... (appliquer matériau et lier à la collection)
 

Puis dans create_maze_geometry():

self._create_floor(maze_collection)
self._create_ceiling(maze_collection)  # Ajouter cette ligne
 

3. Formes de Marqueurs Différentes

Remplacer les cylindres par d’autres formes:

# Sphère pour l'entrée
bpy.ops.mesh.primitive_uv_sphere_add(
    radius=0.5,
    location=(entrance_pos[0], entrance_pos[1], 0.5)
)
 
# Cône pour la sortie
bpy.ops.mesh.primitive_cone_add(
    radius1=0.5,
    depth=1.0,
    location=(exit_pos[0], exit_pos[1], 0.5)
)
 

4. Ajouter de l’Éclairage

def _create_lighting(self):
    """Ajouter une lumière au-dessus du labyrinthe."""
    bpy.ops.object.light_add(
        type='SUN',
        location=(self.width * self.cell_size / 2,
                 self.height * self.cell_size / 2,
                 self.wall_height + 10)
    )
    light = bpy.context.active_object
    light.data.energy = 5.0
 

Extensions Possibles

Algorithmes Alternatifs

  1. Algorithme de Prim (Croissance)
def generate_maze_prim(self):
    # Commencer avec une cellule
    # Ajouter ses murs à une liste
    # Répéter:
    #   - Choisir un mur aléatoire
    #   - Si il connecte visité/non-visité: le supprimer
 
  1. Division Récursive
def generate_maze_recursive_division(self):
    # Diviser l'espace avec un mur
    # Créer une ouverture dans le mur
    # Répéter récursivement sur les deux sous-espaces
 

Fonctionnalités Additionnelles

  1. Génération de Solution
def find_solution(self):
    """Trouver le chemin de l'entrée à la sortie (BFS)."""
    # Implémentation du breadth-first search
    # Retourne une liste de cellules formant le chemin
 
  1. Niveaux de Difficulté
def set_difficulty(self, level):
    """Modifier la difficulté en ajoutant/supprimant des murs."""
    if level == "easy":
        # Supprimer 10% des murs restants
    elif level == "hard":
        # Garder tous les murs (labyrinthe parfait)
 
  1. Export vers d’Autres Formats
def export_to_json(self):
    """Exporter le labyrinthe en JSON."""
    return json.dumps({
        'width': self.width,
        'height': self.height,
        'grid': self.grid,
        'entrance': self.entrance,
        'exit': self.exit
    })
 

Conseils d’Optimisation

Performance pour Grands Labyrinthes

Pour des labyrinthes > 100×100 cellules:

  1. Utiliser des instances pour les murs:
# Créer un mur "template"
template_wall = bpy.data.objects["WallTemplate"]
 
# Créer des instances au lieu de nouveaux objets
instance = template_wall.copy()
instance.data = template_wall.data  # Partage la géométrie
instance.location = (x, y, z)
 
  1. Fusion des murs adjacents:
# Au lieu de créer un mur par segment,
# fusionner les murs alignés en un seul mesh
 
  1. LOD (Level of Detail):
# Pour la visualisation à distance,
# utiliser des murs low-poly
 

Intégration dans un Projet

Utilisation comme Module

# maze_generator.py (votre fichier)
from maze_generator import MazeGenerator
 
# Dans votre script principal
maze = MazeGenerator(20, 20)
maze.generate_maze()
maze.create_entrance_exit()
maze.create_maze_geometry("MyMaze")
 
# Accéder aux données
entrance_cell = maze.entrance
print(f"Entrée en position: {entrance_cell}")
 

Génération Procédurale dans un Jeu

# Générer un nouveau niveau à chaque partie
import time
 
seed = int(time.time())
random.seed(seed)
 
maze = MazeGenerator(25, 25)
maze.generate_maze()
# ... utiliser le labyrinthe dans votre jeu
 

Ressources Supplémentaires

Lectures Recommandées

  1. Algorithmes de Labyrinthes:
    • “Mazes for Programmers” par Jamis Buck
    • “Think Labyrinth!” par Walter D. Pullen
  2. Backtracking et Graphes:
    • “Introduction to Algorithms” par Cormen et al. (CLRS)
    • “The Algorithm Design Manual” par Steven Skiena
  3. Code Golf et Optimisation:
    • Les travaux de John Tromp sur la complexité de Kolmogorov
    • Code Golf Stack Exchange (communauté)

Communautés

  • r/proceduralgeneration - Reddit sur la génération procédurale
  • Blender Artists Forum - Discussion sur Python et Blender
  • Stack Overflow - Questions techniques

Outils Connexes

  • MazeSolver - Visualiseur d’algorithmes de recherche de chemins
  • Procedural Toolkit for Unity - Générateurs de labyrinthes pour Unity
  • Processing - Framework pour art génératif

Le code pour Blender Copiez/coller et exécutez dans un script Blender:

"""
Blender Maze Generator
A Python script to generate 3D mazes in Blender using recursive backtracking algorithm.
"""
 
import bpy
import random
 
 
class MazeGenerator:
    def __init__(self, width=10, height=10, wall_thickness=0.1, wall_height=2.0, cell_size=2.0):
        """
        Initialize the maze generator.
        
        Args:
            width: Number of cells in X direction
            height: Number of cells in Y direction
            wall_thickness: Thickness of walls
            wall_height: Height of walls
            cell_size: Size of each cell
        """
        self.width = width
        self.height = height
        self.wall_thickness = wall_thickness
        self.wall_height = wall_height
        self.cell_size = cell_size
        
        # Initialize maze grid (True = wall, False = path)
        self.grid = [[{'N': True, 'S': True, 'E': True, 'W': True, 'visited': False} 
                     for _ in range(width)] for _ in range(height)]
        
        # Store entrance and exit positions
        self.entrance = None
        self.exit = None
    
    def generate_maze(self):
        """Generate maze using recursive backtracking algorithm."""
        stack = []
        current = (0, 0)
        self.grid[current[1]][current[0]]['visited'] = True
        
        while True:
            neighbors = self._get_unvisited_neighbors(current)
            
            if neighbors:
                # Choose random neighbor
                next_cell = random.choice(neighbors)
                stack.append(current)
                
                # Remove wall between current and next cell
                self._remove_wall(current, next_cell)
                
                # Mark next cell as visited and move to it
                self.grid[next_cell[1]][next_cell[0]]['visited'] = True
                current = next_cell
            elif stack:
                # Backtrack
                current = stack.pop()
            else:
                # Maze complete
                break
    
    def _get_unvisited_neighbors(self, cell):
        """Get list of unvisited neighboring cells."""
        x, y = cell
        neighbors = []
        
        # Check North
        if y > 0 and not self.grid[y-1][x]['visited']:
            neighbors.append((x, y-1))
        # Check South
        if y < self.height - 1 and not self.grid[y+1][x]['visited']:
            neighbors.append((x, y+1))
        # Check East
        if x < self.width - 1 and not self.grid[y][x+1]['visited']:
            neighbors.append((x+1, y))
        # Check West
        if x > 0 and not self.grid[y][x-1]['visited']:
            neighbors.append((x-1, y))
        
        return neighbors
    
    def _remove_wall(self, current, next_cell):
        """Remove wall between two cells."""
        x1, y1 = current
        x2, y2 = next_cell
        
        if x2 == x1 + 1:  # Moving East
            self.grid[y1][x1]['E'] = False
            self.grid[y2][x2]['W'] = False
        elif x2 == x1 - 1:  # Moving West
            self.grid[y1][x1]['W'] = False
            self.grid[y2][x2]['E'] = False
        elif y2 == y1 + 1:  # Moving South
            self.grid[y1][x1]['S'] = False
            self.grid[y2][x2]['N'] = False
        elif y2 == y1 - 1:  # Moving North
            self.grid[y1][x1]['N'] = False
            self.grid[y2][x2]['S'] = False
    
    def create_entrance_exit(self):
        """Create random entrance and exit on perimeter walls."""
        perimeter_walls = []
        
        # Top wall (North side)
        for x in range(self.width):
            perimeter_walls.append((x, 0, 'N'))
        
        # Bottom wall (South side)
        for x in range(self.width):
            perimeter_walls.append((x, self.height - 1, 'S'))
        
        # Left wall (West side)
        for y in range(self.height):
            perimeter_walls.append((0, y, 'W'))
        
        # Right wall (East side)
        for y in range(self.height):
            perimeter_walls.append((self.width - 1, y, 'E'))
        
        # Randomly select entrance and exit
        random.shuffle(perimeter_walls)
        entrance_wall = perimeter_walls[0]
        exit_wall = perimeter_walls[1]
        
        # Remove walls for entrance and exit
        x, y, direction = entrance_wall
        self.grid[y][x][direction] = False
        self.entrance = entrance_wall
        
        x, y, direction = exit_wall
        self.grid[y][x][direction] = False
        self.exit = exit_wall
        
        print(f"Entrance: Cell ({self.entrance[0]}, {self.entrance[1]}) - {self.entrance[2]} wall")
        print(f"Exit: Cell ({self.exit[0]}, {self.exit[1]}) - {self.exit[2]} wall")
    
    def create_maze_geometry(self, collection_name="Maze"):
        """Create 3D geometry for the maze in Blender."""
        # Create new collection for maze
        if collection_name in bpy.data.collections:
            maze_collection = bpy.data.collections[collection_name]
        else:
            maze_collection = bpy.data.collections.new(collection_name)
            bpy.context.scene.collection.children.link(maze_collection)
        
        # Create material for walls
        mat = bpy.data.materials.new(name="MazeMaterial")
        mat.use_nodes = True
        mat.node_tree.nodes["Principled BSDF"].inputs[0].default_value = (0.2, 0.3, 0.5, 1.0)
        
        # Create walls
        for y in range(self.height):
            for x in range(self.width):
                cell = self.grid[y][x]
                pos_x = x * self.cell_size
                pos_y = y * self.cell_size
                
                # North wall
                if cell['N']:
                    self._create_wall(
                        pos_x, pos_y, 
                        self.cell_size, self.wall_thickness,
                        maze_collection, mat
                    )
                
                # West wall
                if cell['W']:
                    self._create_wall(
                        pos_x, pos_y,
                        self.wall_thickness, self.cell_size,
                        maze_collection, mat
                    )
                
                # South wall (only for last row)
                if y == self.height - 1 and cell['S']:
                    self._create_wall(
                        pos_x, pos_y + self.cell_size,
                        self.cell_size, self.wall_thickness,
                        maze_collection, mat
                    )
                
                # East wall (only for last column)
                if x == self.width - 1 and cell['E']:
                    self._create_wall(
                        pos_x + self.cell_size, pos_y,
                        self.wall_thickness, self.cell_size,
                        maze_collection, mat
                    )
        
        # Create floor
        self._create_floor(maze_collection)
        
        # Add entrance and exit markers
        self._create_markers(maze_collection)
        
        print(f"Maze generated: {self.width}x{self.height} cells")
    
    def _create_wall(self, x, y, size_x, size_y, collection, material):
        """Create a single wall segment."""
        bpy.ops.mesh.primitive_cube_add(
            size=1,
            location=(x + size_x/2, y + size_y/2, self.wall_height/2)
        )
        wall = bpy.context.active_object
        wall.scale = (size_x, size_y, self.wall_height)
        
        # Apply material
        if wall.data.materials:
            wall.data.materials[0] = material
        else:
            wall.data.materials.append(material)
        
        # Link to collection
        for coll in wall.users_collection:
            coll.objects.unlink(wall)
        collection.objects.link(wall)
    
    def _create_floor(self, collection):
        """Create floor for the maze."""
        floor_size_x = self.width * self.cell_size
        floor_size_y = self.height * self.cell_size
        
        bpy.ops.mesh.primitive_plane_add(
            size=1,
            location=(floor_size_x/2, floor_size_y/2, 0)
        )
        floor = bpy.context.active_object
        floor.scale = (floor_size_x, floor_size_y, 1)
        floor.name = "MazeFloor"
        
        # Create floor material
        mat = bpy.data.materials.new(name="FloorMaterial")
        mat.use_nodes = True
        mat.node_tree.nodes["Principled BSDF"].inputs[0].default_value = (0.8, 0.8, 0.8, 1.0)
        
        if floor.data.materials:
            floor.data.materials[0] = mat
        else:
            floor.data.materials.append(mat)
        
        # Link to collection
        for coll in floor.users_collection:
            coll.objects.unlink(floor)
        collection.objects.link(floor)
    
    def _create_markers(self, collection):
        """Create visual markers for entrance and exit."""
        if not self.entrance or not self.exit:
            print("Warning: Entrance or exit not set!")
            return
        
        print(f"Creating entrance marker at {self.entrance}")
        print(f"Creating exit marker at {self.exit}")
        
        # Create entrance marker (green)
        entrance_pos = self._get_marker_position(self.entrance)
        bpy.ops.mesh.primitive_cylinder_add(
            radius=0.5,
            depth=0.3,
            location=(entrance_pos[0], entrance_pos[1], 0.15)
        )
        entrance_marker = bpy.context.active_object
        entrance_marker.name = "Entrance_Marker"
        
        # Green material for entrance
        mat_entrance = bpy.data.materials.new(name="EntranceMaterial")
        mat_entrance.use_nodes = True
        bsdf = mat_entrance.node_tree.nodes["Principled BSDF"]
        bsdf.inputs["Base Color"].default_value = (0.0, 1.0, 0.0, 1.0)
        bsdf.inputs["Emission Color"].default_value = (0.0, 1.0, 0.0, 1.0)
        bsdf.inputs["Emission Strength"].default_value = 2.0
        
        entrance_marker.data.materials.append(mat_entrance)
        
        # Move to collection
        for coll in entrance_marker.users_collection:
            coll.objects.unlink(entrance_marker)
        collection.objects.link(entrance_marker)
        
        # Create exit marker (red)
        exit_pos = self._get_marker_position(self.exit)
        bpy.ops.mesh.primitive_cylinder_add(
            radius=0.5,
            depth=0.3,
            location=(exit_pos[0], exit_pos[1], 0.15)
        )
        exit_marker = bpy.context.active_object
        exit_marker.name = "Exit_Marker"
        
        # Red material for exit
        mat_exit = bpy.data.materials.new(name="ExitMaterial")
        mat_exit.use_nodes = True
        bsdf = mat_exit.node_tree.nodes["Principled BSDF"]
        bsdf.inputs["Base Color"].default_value = (1.0, 0.0, 0.0, 1.0)
        bsdf.inputs["Emission Color"].default_value = (1.0, 0.0, 0.0, 1.0)
        bsdf.inputs["Emission Strength"].default_value = 2.0
        
        exit_marker.data.materials.append(mat_exit)
        
        # Move to collection
        for coll in exit_marker.users_collection:
            coll.objects.unlink(exit_marker)
        collection.objects.link(exit_marker)
        
        print("Entrance and exit markers created successfully!")
    
    def _get_marker_position(self, wall_info):
        """Calculate position for entrance/exit marker."""
        x, y, direction = wall_info
        pos_x = x * self.cell_size + self.cell_size / 2
        pos_y = y * self.cell_size + self.cell_size / 2
        
        # Adjust position based on wall direction
        if direction == 'N':
            pos_y = y * self.cell_size
        elif direction == 'S':
            pos_y = (y + 1) * self.cell_size
        elif direction == 'W':
            pos_x = x * self.cell_size
        elif direction == 'E':
            pos_x = (x + 1) * self.cell_size
        
        return (pos_x, pos_y)
 
 
def clear_maze_collection(collection_name="Maze"):
    """Clear existing maze collection."""
    if collection_name in bpy.data.collections:
        collection = bpy.data.collections[collection_name]
        for obj in collection.objects:
            bpy.data.objects.remove(obj, do_unlink=True)
        bpy.data.collections.remove(collection)
 
 
# Main execution
if __name__ == "__main__":
    # Clear any existing maze
    clear_maze_collection("Maze")
    
    # Create and generate maze
    # Adjust parameters here:
    maze = MazeGenerator(
        width=15,           # Number of cells in X
        height=15,          # Number of cells in Y
        wall_thickness=0.2, # Wall thickness
        wall_height=3.0,    # Wall height
        cell_size=2.0       # Size of each cell
    )
    
    maze.generate_maze()
    maze.create_entrance_exit()
    maze.create_maze_geometry("Maze")
    
    print("Maze generation complete!")

Code python pour Unreal Engine Créez un fichier intitulé unreal_maze_generator.py

"""
Unreal Engine Maze Generator
A Python script to generate 3D mazes in Unreal Engine using recursive backtracking algorithm.
Requires: Unreal Engine with Python Editor Script Plugin enabled
"""
 
import unreal
import random
 
 
class UnrealMazeGenerator:
    def __init__(self, width=10, height=10, wall_thickness=20.0, wall_height=300.0, cell_size=200.0):
        """
        Initialize the maze generator for Unreal Engine.
        
        Args:
            width: Number of cells in X direction
            height: Number of cells in Y direction
            wall_thickness: Thickness of walls in cm (Unreal units)
            wall_height: Height of walls in cm
            cell_size: Size of each cell in cm
        """
        self.width = width
        self.height = height
        self.wall_thickness = wall_thickness
        self.wall_height = wall_height
        self.cell_size = cell_size
        
        # Initialize maze grid
        self.grid = [[{'N': True, 'S': True, 'E': True, 'W': True, 'visited': False} 
                     for _ in range(width)] for _ in range(height)]
        
        # Store entrance and exit positions
        self.entrance = None
        self.exit = None
        
        # Unreal Editor utilities
        self.editor_level_lib = unreal.EditorLevelLibrary()
        self.editor_actor_subsystem = unreal.get_editor_subsystem(unreal.EditorActorSubsystem)
        
        # Store spawned actors for organization
        self.maze_actors = []
    
    def generate_maze(self):
        """Generate maze using recursive backtracking algorithm."""
        stack = []
        current = (0, 0)
        self.grid[current[1]][current[0]]['visited'] = True
        
        while True:
            neighbors = self._get_unvisited_neighbors(current)
            
            if neighbors:
                # Choose random neighbor
                next_cell = random.choice(neighbors)
                stack.append(current)
                
                # Remove wall between current and next cell
                self._remove_wall(current, next_cell)
                
                # Mark next cell as visited and move to it
                self.grid[next_cell[1]][next_cell[0]]['visited'] = True
                current = next_cell
            elif stack:
                # Backtrack
                current = stack.pop()
            else:
                # Maze complete
                break
    
    def _get_unvisited_neighbors(self, cell):
        """Get list of unvisited neighboring cells."""
        x, y = cell
        neighbors = []
        
        # Check North
        if y > 0 and not self.grid[y-1][x]['visited']:
            neighbors.append((x, y-1))
        # Check South
        if y < self.height - 1 and not self.grid[y+1][x]['visited']:
            neighbors.append((x, y+1))
        # Check East
        if x < self.width - 1 and not self.grid[y][x+1]['visited']:
            neighbors.append((x+1, y))
        # Check West
        if x > 0 and not self.grid[y][x-1]['visited']:
            neighbors.append((x-1, y))
        
        return neighbors
    
    def _remove_wall(self, current, next_cell):
        """Remove wall between two cells."""
        x1, y1 = current
        x2, y2 = next_cell
        
        if x2 == x1 + 1:  # Moving East
            self.grid[y1][x1]['E'] = False
            self.grid[y2][x2]['W'] = False
        elif x2 == x1 - 1:  # Moving West
            self.grid[y1][x1]['W'] = False
            self.grid[y2][x2]['E'] = False
        elif y2 == y1 + 1:  # Moving South
            self.grid[y1][x1]['S'] = False
            self.grid[y2][x2]['N'] = False
        elif y2 == y1 - 1:  # Moving North
            self.grid[y1][x1]['N'] = False
            self.grid[y2][x2]['S'] = False
    
    def create_entrance_exit(self):
        """Create random entrance and exit on perimeter walls."""
        perimeter_walls = []
        
        # Top wall (North side)
        for x in range(self.width):
            perimeter_walls.append((x, 0, 'N'))
        
        # Bottom wall (South side)
        for x in range(self.width):
            perimeter_walls.append((x, self.height - 1, 'S'))
        
        # Left wall (West side)
        for y in range(self.height):
            perimeter_walls.append((0, y, 'W'))
        
        # Right wall (East side)
        for y in range(self.height):
            perimeter_walls.append((self.width - 1, y, 'E'))
        
        # Randomly select entrance and exit
        random.shuffle(perimeter_walls)
        entrance_wall = perimeter_walls[0]
        exit_wall = perimeter_walls[1]
        
        # Remove walls for entrance and exit
        x, y, direction = entrance_wall
        self.grid[y][x][direction] = False
        self.entrance = entrance_wall
        
        x, y, direction = exit_wall
        self.grid[y][x][direction] = False
        self.exit = exit_wall
        
        unreal.log(f"Entrance: Cell ({self.entrance[0]}, {self.entrance[1]}) - {self.entrance[2]} wall")
        unreal.log(f"Exit: Cell ({self.exit[0]}, {self.exit[1]}) - {self.exit[2]} wall")
    
    def create_maze_in_level(self, folder_name="/Game/Maze"):
        """Create the maze geometry in the Unreal level."""
        unreal.log("Creating maze geometry in Unreal Engine...")
        
        # Create materials
        wall_material = self._create_wall_material()
        floor_material = self._create_floor_material()
        
        # Create walls
        wall_count = 0
        for y in range(self.height):
            for x in range(self.width):
                cell = self.grid[y][x]
                pos_x = x * self.cell_size
                pos_y = y * self.cell_size
                
                # North wall
                if cell['N']:
                    self._create_wall(
                        pos_x, pos_y, 
                        self.cell_size, self.wall_thickness,
                        wall_material, f"Wall_N_{x}_{y}"
                    )
                    wall_count += 1
                
                # West wall
                if cell['W']:
                    self._create_wall(
                        pos_x, pos_y,
                        self.wall_thickness, self.cell_size,
                        wall_material, f"Wall_W_{x}_{y}"
                    )
                    wall_count += 1
                
                # South wall (only for last row)
                if y == self.height - 1 and cell['S']:
                    self._create_wall(
                        pos_x, pos_y + self.cell_size,
                        self.cell_size, self.wall_thickness,
                        wall_material, f"Wall_S_{x}_{y}"
                    )
                    wall_count += 1
                
                # East wall (only for last column)
                if x == self.width - 1 and cell['E']:
                    self._create_wall(
                        pos_x + self.cell_size, pos_y,
                        self.wall_thickness, self.cell_size,
                        wall_material, f"Wall_E_{x}_{y}"
                    )
                    wall_count += 1
        
        # Create floor
        self._create_floor(floor_material)
        
        # Create entrance and exit markers
        self._create_markers()
        
        unreal.log(f"Maze generation complete! Created {wall_count} walls")
        unreal.log(f"Maze dimensions: {self.width}x{self.height} cells")
        unreal.log(f"Total actors spawned: {len(self.maze_actors)}")
    
    def _create_wall_material(self):
        """Create a blue material for walls."""
        # In a real scenario, you would load or create a material asset
        # For simplicity, we'll return None and set color via actor properties
        return None
    
    def _create_floor_material(self):
        """Create a gray material for floor."""
        return None
    
    def _create_wall(self, x, y, size_x, size_y, material, name):
        """
        Create a single wall segment using a Cube static mesh.
        
        Note: In Unreal, coordinates are X=forward, Y=right, Z=up
        We're treating our maze as X-Y plane (top-down view)
        """
        # Calculate center position
        center_x = x + size_x / 2
        center_y = y + size_y / 2
        center_z = self.wall_height / 2
        
        # Create location (Unreal uses cm by default)
        location = unreal.Vector(center_x, center_y, center_z)
        rotation = unreal.Rotator(0, 0, 0)
        scale = unreal.Vector(size_x / 100.0, size_y / 100.0, self.wall_height / 100.0)
        
        # Spawn a cube actor
        actor_class = unreal.EditorAssetLibrary.load_blueprint_class('/Engine/BasicShapes/Cube.Cube')
        
        if actor_class:
            spawned_actor = self.editor_level_lib.spawn_actor_from_class(
                actor_class,
                location,
                rotation
            )
            
            if spawned_actor:
                # Set scale
                spawned_actor.set_actor_scale3d(scale)
                
                # Set name
                spawned_actor.set_actor_label(name)
                
                # Add to our list
                self.maze_actors.append(spawned_actor)
                
                # Set color (blue-ish)
                # This would require setting up material instances in practice
        else:
            # Fallback: create a static mesh actor with cube
            spawned_actor = self.editor_level_lib.spawn_actor_from_object(
                unreal.load_object(None, '/Engine/BasicShapes/Cube.Cube'),
                location,
                rotation
            )
            
            if spawned_actor:
                spawned_actor.set_actor_scale3d(scale)
                spawned_actor.set_actor_label(name)
                self.maze_actors.append(spawned_actor)
    
    def _create_floor(self, material):
        """Create floor for the maze."""
        floor_size_x = self.width * self.cell_size
        floor_size_y = self.height * self.cell_size
        
        # Center position
        center_x = floor_size_x / 2
        center_y = floor_size_y / 2
        center_z = -10.0  # Slightly below ground
        
        location = unreal.Vector(center_x, center_y, center_z)
        rotation = unreal.Rotator(0, 0, 0)
        scale = unreal.Vector(floor_size_x / 100.0, floor_size_y / 100.0, 0.2)
        
        # Spawn floor cube
        actor_class = unreal.EditorAssetLibrary.load_blueprint_class('/Engine/BasicShapes/Cube.Cube')
        
        if actor_class:
            floor_actor = self.editor_level_lib.spawn_actor_from_class(
                actor_class,
                location,
                rotation
            )
            
            if floor_actor:
                floor_actor.set_actor_scale3d(scale)
                floor_actor.set_actor_label("MazeFloor")
                self.maze_actors.append(floor_actor)
    
    def _create_markers(self):
        """Create visual markers for entrance and exit."""
        if not self.entrance or not self.exit:
            unreal.log_warning("Entrance or exit not set!")
            return
        
        unreal.log(f"Creating entrance marker at {self.entrance}")
        unreal.log(f"Creating exit marker at {self.exit}")
        
        # Create entrance marker (green cylinder)
        entrance_pos = self._get_marker_position(self.entrance)
        self._create_marker(entrance_pos[0], entrance_pos[1], "Entrance_Marker", is_entrance=True)
        
        # Create exit marker (red cylinder)
        exit_pos = self._get_marker_position(self.exit)
        self._create_marker(exit_pos[0], exit_pos[1], "Exit_Marker", is_entrance=False)
        
        unreal.log("Entrance and exit markers created successfully!")
    
    def _create_marker(self, x, y, name, is_entrance=True):
        """Create a cylinder marker at the specified position."""
        location = unreal.Vector(x, y, 15.0)  # 15cm above floor
        rotation = unreal.Rotator(0, 0, 0)
        scale = unreal.Vector(0.5, 0.5, 0.3)  # Radius 50cm, height 30cm
        
        # Load cylinder mesh
        actor_class = unreal.EditorAssetLibrary.load_blueprint_class('/Engine/BasicShapes/Cylinder.Cylinder')
        
        if actor_class:
            marker = self.editor_level_lib.spawn_actor_from_class(
                actor_class,
                location,
                rotation
            )
            
            if marker:
                marker.set_actor_scale3d(scale)
                marker.set_actor_label(name)
                self.maze_actors.append(marker)
                
                # In practice, you would apply a green/red emissive material here
    
    def _get_marker_position(self, wall_info):
        """Calculate position for entrance/exit marker."""
        x, y, direction = wall_info
        pos_x = x * self.cell_size + self.cell_size / 2
        pos_y = y * self.cell_size + self.cell_size / 2
        
        # Adjust position based on wall direction
        if direction == 'N':
            pos_y = y * self.cell_size
        elif direction == 'S':
            pos_y = (y + 1) * self.cell_size
        elif direction == 'W':
            pos_x = x * self.cell_size
        elif direction == 'E':
            pos_x = (x + 1) * self.cell_size
        
        return (pos_x, pos_y)
    
    def clear_maze(self):
        """Clear all spawned maze actors from the level."""
        unreal.log("Clearing previous maze actors...")
        
        for actor in self.maze_actors:
            if actor:
                self.editor_level_lib.destroy_actor(actor)
        
        self.maze_actors.clear()
        unreal.log("Maze cleared!")
    
    def select_all_maze_actors(self):
        """Select all maze actors for easy manipulation."""
        self.editor_level_lib.set_selected_level_actors(self.maze_actors)
        unreal.log(f"Selected {len(self.maze_actors)} maze actors")
 
 
def clear_existing_maze():
    """Helper function to clear any existing maze actors."""
    # Find all actors with "Wall_", "MazeFloor", or markers in their name
    all_actors = unreal.EditorLevelLibrary.get_all_level_actors()
    maze_actors = [actor for actor in all_actors 
                   if actor and (actor.get_actor_label().startswith("Wall_") or 
                                actor.get_actor_label().startswith("Maze") or
                                actor.get_actor_label().endswith("_Marker"))]
    
    for actor in maze_actors:
        unreal.EditorLevelLibrary.destroy_actor(actor)
    
    unreal.log(f"Cleared {len(maze_actors)} existing maze actors")
 
 
# Main execution
if __name__ == "__main__":
    unreal.log("=" * 60)
    unreal.log("UNREAL ENGINE MAZE GENERATOR")
    unreal.log("=" * 60)
    
    # Clear any existing maze
    clear_existing_maze()
    
    # Create and generate maze
    # Adjust parameters here:
    maze = UnrealMazeGenerator(
        width=15,              # Number of cells in X
        height=15,             # Number of cells in Y
        wall_thickness=20.0,   # Wall thickness in cm
        wall_height=300.0,     # Wall height in cm (3 meters)
        cell_size=200.0        # Size of each cell in cm (2 meters)
    )
    
    unreal.log("Generating maze structure...")
    maze.generate_maze()
    
    unreal.log("Creating entrance and exit...")
    maze.create_entrance_exit()
    
    unreal.log("Building maze geometry in level...")
    maze.create_maze_in_level()
    
    unreal.log("=" * 60)
    unreal.log("MAZE GENERATION COMPLETE!")
    unreal.log("=" * 60)
    unreal.log("TIP: Select all maze actors by uncommenting the line below:")
    unreal.log("# maze.select_all_maze_actors()")

Dans UE:

  • Activer Python dans Unreal: Edit → Plugins → “Python Editor Script Plugin”
  • Exécuter via: Tools → Execute Python Script
  • Personnaliser les paramètres (width, height, wall_height, etc.)

À vous de jouer, ajoutez le pack First Person et trouvez la sortie du labyrinthe!