CoddyRun
Blog

Articles & Actualités

Les publications CoddyRun : nouveautés du logiciel, astuces de pseudo-code, retours d'expérience et actualités de l'environnement pédagogique.

74 publications

Construire un moteur 3D… en salle de classe : quand l’apprentissage du code devient une aventure créative
Article

Construire un moteur 3D

Construire un moteur 3D… en salle de classe : quand l’apprentissage du code devient une aventure créative

Lire l'articleReplier l'article

Un projet qui illustre parfaitement ce que signifie apprendre à coder en pensant différemment : un moteur 3D voxel isométrique, entièrement développé dans EduCode.

Ce programme permet aux élèves de manipuler un environnement 3D, de construire des blocs, de gérer une caméra, de zoomer, de tourner la scène, et même d’afficher un personnage animé. Le tout dans un langage pédagogique pensé pour rendre la logique accessible.

Un moteur 3D… expliqué simplement

Le cœur du moteur repose sur une boucle de rendu qui projette chaque bloc en isométrie. Comme le dit le code :

Un moteur 3D… expliqué simplement
Le cœur du moteur repose sur une boucle de rendu qui projette chaque bloc en isométrie. Comme le dit le code :

« graph est une toile » « tant que vrai… effacer(graph) »

Chaque frame reconstruit la scène : sol, blocs empilés, personnage, interface HUD. Les élèves découvrent ainsi les notions de :
- Projection isométrique
- Gestion de dictionnaires pour stocker les blocs
- Rendu multi‑faces (haut, gauche, droite)
- Caméra orientable (0°, 90°, 180°, 270°)
- Zoom dynamique
- Interaction clavier en temps réel
- Deuxième élément
- Troisième élément

Construire, détruire, expérimenter

Le moteur intègre une vraie logique de gameplay :
- E → Empiler un bloc
- R → Casser le bloc du dessus
- C → Changer de matériau (Pierre, Brique, Bois, Verre, Feuillage)
- Z / S / Q / D → Se déplacer
- T → Tourner la caméra
- + / - → Zoomer

Chaque bloc est stocké dans un dictionnaire avec sa hauteur et son type. Le code le montre très bien :

« typesBlocs[cle3D] vaut typeBlocConstruction »

Les élèves comprennent ainsi comment fonctionne un moteur de jeu : stockage, rendu, interaction.

Un rendu 3D immersif

Le moteur dessine chaque bloc avec trois faces colorées, créant un effet 3D convaincant. Le personnage lui-même est composé de formes simples : rectangles arrondis, ellipses, cercles… une manière ludique d’aborder la composition graphique.

Pourquoi ce projet compte

Parce qu’il montre que l’apprentissage du code peut être créatif, visuel, motivant.

EduCode n’est pas seulement un outil : c’est un terrain de jeu intellectuel où les élèves apprennent à :
- structurer leurs idées,
- comprendre la logique spatiale,
- manipuler des données,
- créer un univers interactif.

Et comme le rappelle le programme lui-même :
« Apprendre à coder, c'est avant tout apprendre à penser ! »

Conclusion

Ce moteur 3D n’est pas juste un exercice : c’est une preuve que, même avec un langage pédagogique, on peut créer des expériences riches, immersives et formatrices.

Programme :

graph est une toile
dimension(graph, 600, 500)

// Position du joueur
jX est un nombre
jY est un nombre
jX vaut 0
jY vaut 0

// Facteur de zoom (échelle de rendu)
echelle est un nombre
echelle vaut 1,0

// Orientation de la caméra (0: 0°, 1: 90°, 2: 180°, 3: 270°)
orientation est un nombre
orientation vaut 0

// Dictionnaires pour la hauteur et les types de blocs personnalisés
hauteursBlocs est un dictionnaire
hauteursBlocs vaut [:]

typesBlocs est un dictionnaire
typesBlocs vaut [:]

// Type de bloc actif (1: Pierre, 2: Brique, 3: Bois, 4: Verre, 5: Feuillage)
typeBlocConstruction est un nombre
typeBlocConstruction vaut 1

action est un nombre

@ Boucle principale
tant que vrai
  effacer(graph)
  remplir(graph, #090d16) // Fond ciel de nuit profond

  // Calcul des pas isométriques dynamiques selon le zoom
  dx est un nombre
  dy est un nombre
  dx vaut 24 * echelle
  dy vaut 12 * echelle

  // Balayage du champ de vision (rayon autour du joueur)
  rY est un nombre
  rX est un nombre
  pour rY de - 10 à 10
    pour rX de - 10 à 10

      // Rotation des coordonnées du monde autour du joueur selon l'orientation
      offX est un nombre
      offY est un nombre

      si orientation = 0 alors
        offX vaut rX
        offY vaut rY
      sinon si orientation = 1 alors
        offX vaut - rY
        offY vaut rX
      sinon si orientation = 2 alors
        offX vaut - rX
        offY vaut - rY
      sinon
        offX vaut rY
        offY vaut - rX
      fin si

      gx est un nombre
      gy est un nombre
      gx vaut jX + offX
      gy vaut jY + offY

      // Projection isométrique centrée sur la toile (300, 250)
      cx est un nombre
      cy est un nombre
      cx vaut 300 + (rX - rY) * dx
      cy vaut 250 + (rX + rY) * dy

      // Culling élargi pour le rendu
      si cx >= - 100 et cx <= 700 et cy >= - 100 et cy <= 600 alors

        cleCoord est un texte
        cleCoord vaut convertir_texte(gx) + "_" + convertir_texte(gy)

        hauteurTotale est un nombre
        hauteurTotale vaut 0
        si contient(hauteursBlocs, cleCoord) alors
          hauteurTotale vaut hauteursBlocs[cleCoord]
        fin si

        // --- 1. RENDU DU SOL DE BASE ---
        baseYVal est un nombre
        baseYVal vaut cy

        profondeurSol est un nombre
        profondeurSol vaut 20 * echelle

        // Face Gauche du sol de base
        ptsSolFG est un tableau
        ptsSolFG ajoute cx - dx
        ptsSolFG ajoute baseYVal
        ptsSolFG ajoute cx
        ptsSolFG ajoute baseYVal + dy
        ptsSolFG ajoute cx
        ptsSolFG ajoute baseYVal + dy + profondeurSol
        ptsSolFG ajoute cx - dx
        ptsSolFG ajoute baseYVal + profondeurSol
        polygone(graph, ptsSolFG, #1e293b)

        // Face Droite du sol de base
        ptsSolFD est un tableau
        ptsSolFD ajoute cx
        ptsSolFD ajoute baseYVal + dy
        ptsSolFD ajoute cx + dx
        ptsSolFD ajoute baseYVal
        ptsSolFD ajoute cx + dx
        ptsSolFD ajoute baseYVal + profondeurSol
        ptsSolFD ajoute cx
        ptsSolFD ajoute baseYVal + dy + profondeurSol
        polygone(graph, ptsSolFD, #0f172a)

        // Face Supérieure du sol de base (Herbe)
        ptsSolTop est un tableau
        ptsSolTop ajoute cx
        ptsSolTop ajoute baseYVal - dy
        ptsSolTop ajoute cx + dx
        ptsSolTop ajoute baseYVal
        ptsSolTop ajoute cx
        ptsSolTop ajoute baseYVal + dy
        ptsSolTop ajoute cx - dx
        ptsSolTop ajoute baseYVal
        polygone(graph, ptsSolTop, #22c55e)
        contour(graph, #15803d, 1)

        // --- 2. RENDU DES BLOCS EMPILÉS EN HAUTEUR ---
        si hauteurTotale > 0 alors
          hauteurCube est un nombre
          hauteurCube vaut 24 * echelle

          pour k de 0 à hauteurTotale - 1
            cle3D_rendu est un texte
            cle3D_rendu vaut cleCoord + "_" + convertir_texte(k)

            typeBlocVal est un nombre
            si contient(typesBlocs, cle3D_rendu) alors
              typeBlocVal vaut typesBlocs[cle3D_rendu]
            sinon
              typeBlocVal vaut 1
            fin si

            couleurTop est un texte
            couleurGauche est un texte
            couleurDroite est un texte

            // Matériaux : 1=Pierre, 2=Brique, 3=Bois, 4=Verre, 5=Feuillage
            si typeBlocVal = 1 alors
              couleurTop vaut #f1f5f9
              couleurGauche vaut #94a3b8
              couleurDroite vaut #64748b
            sinon si typeBlocVal = 2 alors
              couleurTop vaut #f87171
              couleurGauche vaut #dc2626
              couleurDroite vaut #b91c1c
            sinon si typeBlocVal = 3 alors
              couleurTop vaut #fbbf24
              couleurGauche vaut #d97706
              couleurDroite vaut #b45309
            sinon si typeBlocVal = 4 alors
              // Verre (semi-transparent)
              couleurTop vaut rgba(224, 242, 254, 0.65)
              couleurGauche vaut rgba(148, 163, 184, 0.55)
              couleurDroite vaut rgba(100, 116, 139, 0.55)
            sinon
              // Feuillage (semi-transparent)
              couleurTop vaut rgba(74, 222, 128, 0.8)
              couleurGauche vaut rgba(22, 163, 74, 0.75)
              couleurDroite vaut rgba(21, 128, 61, 0.75)
            fin si

            lCY est un nombre
            lCY vaut cy - hauteurCube - (k * hauteurCube)

            // Face Gauche
            ptsFG est un tableau
            ptsFG ajoute cx - dx
            ptsFG ajoute lCY
            ptsFG ajoute cx
            ptsFG ajoute lCY + dy
            ptsFG ajoute cx
            ptsFG ajoute lCY + dy + hauteurCube
            ptsFG ajoute cx - dx
            ptsFG ajoute lCY + hauteurCube
            polygone(graph, ptsFG, couleurGauche)

            // Face Droite
            ptsFD est un tableau
            ptsFD ajoute cx
            ptsFD ajoute lCY + dy
            ptsFD ajoute cx + dx
            ptsFD ajoute lCY
            ptsFD ajoute cx + dx
            ptsFD ajoute lCY + hauteurCube
            ptsFD ajoute cx
            ptsFD ajoute lCY + dy + hauteurCube
            polygone(graph, ptsFD, couleurDroite)

            // Face Supérieure
            ptsTop est un tableau
            ptsTop ajoute cx
            ptsTop ajoute lCY - dy
            ptsTop ajoute cx + dx
            ptsTop ajoute lCY
            ptsTop ajoute cx
            ptsTop ajoute lCY + dy
            ptsTop ajoute cx - dx
            ptsTop ajoute lCY
            polygone(graph, ptsTop, couleurTop)

            contour(graph, #475569, 1)
          fin pour
        fin si

        // --- 3. PERSONNAGE PRINCIPAL 3D ---
        si rX = 0 et rY = 0 alors
          decalagePersoY est un nombre
          decalagePersoY vaut hauteurTotale * 24 * echelle

          pCY est un nombre
          pCY vaut cy - decalagePersoY

          // Ombre au sol
          ellipse(graph, cx, pCY + (2 * echelle), 12 * echelle, 6 * echelle, rgba(0, 0, 0, 0.4))

          // Jambes
          rectangle_arrondi(graph, cx - (4 * echelle), pCY - (10 * echelle), 8 * echelle, 10 * echelle, 2, #1e3a8a)

          // Bras gauche
          rectangle_arrondi(graph, cx - (9 * echelle), pCY - (21 * echelle), 4 * echelle, 9 * echelle, 2, #fcd34d)

          // Corps
          rectangle_arrondi(graph, cx - (5 * echelle), pCY - (22 * echelle), 10 * echelle, 13 * echelle, 2, #38bdf8)
          rectangle(graph, cx - (1.5 * echelle), pCY - (22 * echelle), 3 * echelle, 13 * echelle, #e0f2fe)

          // Bras droit
          rectangle_arrondi(graph, cx + (5 * echelle), pCY - (21 * echelle), 4 * echelle, 9 * echelle, 2, #fcd34d)

          // Tête
          rectangle_arrondi(graph, cx - (6 * echelle), pCY - (32 * echelle), 12 * echelle, 11 * echelle, 3, #fcd34d)

          // Cheveux
          rectangle_arrondi(graph, cx - (6.5 * echelle), pCY - (34 * echelle), 13 * echelle, 5 * echelle, 2, #ea580c)

          // Yeux
          cercle(graph, cx - (2.5 * echelle), pCY - (27 * echelle), 1.5 * echelle, #0f172a)
          cercle(graph, cx + (2.5 * echelle), pCY - (27 * echelle), 1.5 * echelle, #0f172a)
        fin si

      fin si
    fin pour
  fin pour

  // --- INTERFACE HUD & INFORMATIONS ---
  contour(graph, #0)
  rectangle(graph, 0, 0, 600, 65, rgba(15, 23, 42, 0.55))
  label(graph, 12, 18, "Voxel 3D Engine", #ffffff, 12)

  // Nom du matériau actif
  nomMateriau est un texte
  si typeBlocConstruction = 1 alors
    nomMateriau vaut "Pierre"
  sinon si typeBlocConstruction = 2 alors
    nomMateriau vaut "Brique"
  sinon si typeBlocConstruction = 3 alors
    nomMateriau vaut "Bois"
  sinon si typeBlocConstruction = 4 alors
    nomMateriau vaut "Verre"
  sinon
    nomMateriau vaut "Feuillage"
  fin si

  txtInfos est un texte
  txtInfos vaut "X : " + jX + " / Y : " + jY + " # Matériau : " + nomMateriau + " # Zoom : " + convertir_texte(arrondi(echelle * 100)) + "% # Rotation : " + (orientation * 90) + "°"
  label(graph, 12, 34, txtInfos, #38bdf8, 11)
  label(graph, 12, 52, " Bouger [Z S Q D] # Empiler [E] # Casser [R] # Matériau [C] # Zoom [ + / - ] # Rotation [T]", #ffffff, 10)

  // Barre inférieure
  rectangle(graph, 0, 470, 600, 30, rgba(15, 23, 42, 0.55))
  label(graph, 12, 490, "EduCode v3.1", #FFFFFF, 14)
  label(graph, 150, 490, "Apprendre à coder, c'est avant tout apprendre à penser !", #FFFFFF, 14)

  // --- ENCADRÉ D'INVENTAIRE (EN BAS À DROITE) ---
  rectangle_arrondi(graph, 510, 385, 75, 75, 6, rgba(15, 23, 42, 0.55))
  contour(graph, #ffffff, 1)

  // Couleurs du bloc pour l'aperçu
  couleurSelTop est un texte
  couleurSelGauche est un texte
  couleurSelDroite est un texte

  si typeBlocConstruction = 1 alors
    couleurSelTop vaut #f1f5f9
    couleurSelGauche vaut #94a3b8
    couleurSelDroite vaut #64748b
  sinon si typeBlocConstruction = 2 alors
    couleurSelTop vaut #f87171
    couleurSelGauche vaut #dc2626
    couleurSelDroite vaut #b91c1c
  sinon si typeBlocConstruction = 3 alors
    couleurSelTop vaut #fbbf24
    couleurSelGauche vaut #d97706
    couleurSelDroite vaut #b45309
  sinon si typeBlocConstruction = 4 alors
    couleurSelTop vaut rgba(224, 242, 254, 0.65)
    couleurSelGauche vaut rgba(148, 163, 184, 0.55)
    couleurSelDroite vaut rgba(100, 116, 139, 0.55)
  sinon
    couleurSelTop vaut rgba(74, 222, 128, 0.8)
    couleurSelGauche vaut rgba(22, 163, 74, 0.75)
    couleurSelDroite vaut rgba(21, 128, 61, 0.75)
  fin si

  // Rendu 3D du mini-bloc d'aperçu
  mcx est un nombre
  mcy est un nombre
  mcx vaut 547
  mcy vaut 415

  mdx est un nombre
  mdy est un nombre
  mh est un nombre
  mdx vaut 16
  mdy vaut 8
  mh vaut 16

  // Face Gauche aperçu
  pSelFG est un tableau
  pSelFG ajoute mcx - mdx
  pSelFG ajoute mcy
  pSelFG ajoute mcx
  pSelFG ajoute mcy + mdy
  pSelFG ajoute mcx
  pSelFG ajoute mcy + mdy + mh
  pSelFG ajoute mcx - mdx
  pSelFG ajoute mcy + mh
  polygone(graph, pSelFG, couleurSelGauche)

  // Face Droite aperçu
  pSelFD est un tableau
  pSelFD ajoute mcx
  pSelFD ajoute mcy + mdy
  pSelFD ajoute mcx + mdx
  pSelFD ajoute mcy
  pSelFD ajoute mcx + mdx
  pSelFD ajoute mcy + mh
  pSelFD ajoute mcx
  pSelFD ajoute mcy + mdy + mh
  polygone(graph, pSelFD, couleurSelDroite)

  // Face Supérieure aperçu
  pSelTop est un tableau
  pSelTop ajoute mcx
  pSelTop ajoute mcy - mdy
  pSelTop ajoute mcx + mdx
  pSelTop ajoute mcy
  pSelTop ajoute mcx
  pSelTop ajoute mcy + mdy
  pSelTop ajoute mcx - mdx
  pSelTop ajoute mcy
  polygone(graph, pSelTop, couleurSelTop)

  contour(graph, #ffffff, 1)

  affiche graph

  // --- GESTION DES ENTRÉES CLAVIER ---
  appuyer ["z", "s", "q", "d", "e", "r", "c", "+", "-", "t"] dans action

  mvtX est un nombre
  mvtY est un nombre
  mvtX vaut 0
  mvtY vaut 0

  si action = 1 alors
    // Z (Avancer)
    si orientation = 0 alors
      mvtX vaut 0
      mvtY vaut - 1
    sinon si orientation = 1 alors
      mvtX vaut 1
      mvtY vaut 0
    sinon si orientation = 2 alors
      mvtX vaut 0
      mvtY vaut 1
    sinon
      mvtX vaut - 1
      mvtY vaut 0
    fin si
  sinon si action = 2 alors
    // S (Reculer)
    si orientation = 0 alors
      mvtX vaut 0
      mvtY vaut 1
    sinon si orientation = 1 alors
      mvtX vaut - 1
      mvtY vaut 0
    sinon si orientation = 2 alors
      mvtX vaut 0
      mvtY vaut - 1
    sinon
      mvtX vaut 1
      mvtY vaut 0
    fin si
  sinon si action = 3 alors
    // Q (Aller à gauche)
    si orientation = 0 alors
      mvtX vaut - 1
      mvtY vaut 0
    sinon si orientation = 1 alors
      mvtX vaut 0
      mvtY vaut - 1
    sinon si orientation = 2 alors
      mvtX vaut 1
      mvtY vaut 0
    sinon
      mvtX vaut 0
      mvtY vaut 1
    fin si
  sinon si action = 4 alors
    // D (Aller à droite)
    si orientation = 0 alors
      mvtX vaut 1
      mvtY vaut 0
    sinon si orientation = 1 alors
      mvtX vaut 0
      mvtY vaut 1
    sinon si orientation = 2 alors
      mvtX vaut - 1
      mvtY vaut 0
    sinon
      mvtX vaut 0
      mvtY vaut - 1
    fin si
  sinon si action = 5 alors
    // EMPILER UN BLOC SOUS LE JOUEUR
    cleAction est un texte
    cleAction vaut convertir_texte(jX) + "_" + convertir_texte(jY)

    zActuel est un nombre

    si contient(hauteursBlocs, cleAction) alors
      zActuel vaut hauteursBlocs[cleAction]
      hauteursBlocs[cleAction] vaut hauteursBlocs[cleAction] + 1
    sinon
      zActuel vaut 0
      hauteursBlocs[cleAction] vaut 1
    fin si

    cle3D est un texte
    cle3D vaut cleAction + "_" + convertir_texte(zActuel)
    typesBlocs[cle3D] vaut typeBlocConstruction
  sinon si action = 6 alors
    // CASSER LE BLOC DU DESSUS
    cleAction2 est un texte
    cleAction2 vaut convertir_texte(jX) + "_" + convertir_texte(jY)

    si contient(hauteursBlocs, cleAction2) alors
      zSommet est un nombre
      zSommet vaut hauteursBlocs[cleAction2] - 1

      cle3D_suppr est un texte
      cle3D_suppr vaut cleAction2 + "_" + convertir_texte(zSommet)
      typesBlocs supprime cle3D_suppr

      hauteursBlocs[cleAction2] vaut hauteursBlocs[cleAction2] - 1
      si hauteursBlocs[cleAction2] <= 0 alors
        hauteursBlocs supprime cleAction2
      fin si
    fin si
  sinon si action = 7 alors
    // CHANGER DE TYPE DE MATÉRIAU (1 à 5)
    typeBlocConstruction vaut typeBlocConstruction + 1
    si typeBlocConstruction > 5 alors
      typeBlocConstruction vaut 1
    fin si
  sinon si action = 8 alors
    // ZOOM AVANT (+)
    echelle vaut limiter(echelle + 0.15, 0.6, 2.0)
  sinon si action = 9 alors
    // ZOOM ARRIÈRE (-)
    echelle vaut limiter(echelle - 0.15, 0.6, 2.0)
  sinon si action = 10 alors
    // ROTATION CAMÉRA (T)
    orientation vaut orientation + 1
    si orientation > 3 alors
      orientation vaut 0
    fin si
  fin si

  // Application des déplacements libres
  si action >= 1 et action <= 4 alors
    jX vaut jX + mvtX
    jY vaut jY + mvtY
  fin si
fin tant que
Pas encore noté
Agent 47 Go : un exercice de game design tactique et d’algorithmie appliquée
Article

Agent 47 Go

Agent 47 Go : un exercice de game design tactique et d’algorithmie appliquée

Lire l'articleReplier l'article

Dans le domaine du game design, il existe une zone fascinante : celle où des mécaniques simples, presque brutes, parviennent à créer une expérience stratégique riche. Agent 47 Go illustre parfaitement cette idée en combinant génération procédurale, IA déterministe et rendu isométrique minimaliste pour produire un gameplay d’infiltration au tour par tour.

Une carte générée dynamiquement, toujours jouable

Le système repose sur une grille 8×8, entièrement reconstruite à chaque partie. La logique garantit un chemin viable entre le point de départ et l’objectif, tout en ajoutant des embranchements secondaires uniquement lorsqu’ils restent connectés.

« carte[indexChemin] vaut 1 » « si adj = 1 alors carte[idxCourant] vaut 1 »

Ce choix permet de créer des niveaux variés, sans jamais sacrifier la jouabilité.

Des gardes qui patrouillent selon une IA simple mais efficace
Les gardes suivent une direction fixe, qu’ils inversent lorsqu’ils rencontrent un obstacle ou un autre garde. Ce comportement, entièrement déterministe, crée une patrouille crédible, lisible, mais suffisamment imprévisible pour obliger le joueur à anticiper.

« gardesDir[i] vaut (d + 2) mod 4 »

Une mécanique légère, mais qui génère une vraie tension tactique.

Un gameplay d’infiltration au tour par tour

Le joueur se déplace avec ZQSD, chaque action déclenchant la réaction des gardes. Les collisions, éliminations et conditions de victoire/défaite sont évaluées à chaque tour.

« si gardesX[i] = pX et gardesY[i] = pY alors etatJeu vaut 2 »

Une boucle de jeu simple, mais qui récompense l’observation et la planification.

Un rendu isométrique épuré et lisible

Le moteur graphique repose sur des formes géométriques (ellipses, rectangles, lignes) pour représenter les cases, le joueur, les gardes et l’objectif. Le résultat : une lecture immédiate de la scène, même sans assets complexes.

Un terrain d’expérimentation riche
Ce programme met en lumière plusieurs axes techniques :

  • génération procédurale contrôlée,
  • gestion d’état dans un moteur de jeu,
  • IA comportementale simple mais cohérente,
  • rendu isométrique minimaliste,
  • mécaniques d’infiltration au tour par tour.

Autant de briques qui peuvent servir de base à des systèmes plus avancés : pathfinding, IA adaptative, niveaux multi-étages, ou encore gestion de vision et de bruit.

Conclusion

Agent 47 Go démontre qu’avec une architecture claire et des règles bien pensées, il est possible de créer une expérience tactique engageante, même dans un environnement minimaliste. Un bel exemple de ce que l’algorithmie peut apporter au game design.

Programme :

@ Agent 47 Go
toile1 est une toile
dimension(toile1, 600, 450)

dimensionGrille vaut 8
etatJeu est un nombre
etatJeu vaut 3 // 0 = En jeu, 1 = Victoire, 2 = Défaite, 3 = Génération

carte est un tableau
gardesX est un tableau
gardesY est un tableau
gardesDir est un tableau
gardesActif est un tableau

pX est un nombre
pY est un nombre
action est un nombre

tant que vrai

    @ 1. GÉNÉRATION SÉCURISÉE de LA MAP
    si etatJeu = 3 alors
        tant que longueur(gardesX) > 0
            gardesX supprime 0
        fin tant que
        tant que longueur(gardesY) > 0
            gardesY supprime 0
        fin tant que
        tant que longueur(gardesDir) > 0
            gardesDir supprime 0
        fin tant que
        tant que longueur(gardesActif) > 0
            gardesActif supprime 0
        fin tant que

        tant que longueur(carte) < dimensionGrille * dimensionGrille
            carte ajoute 0
        fin tant que
        tant que longueur(carte) > dimensionGrille * dimensionGrille
            carte supprime 0
        fin tant que

        pour i de 0 à (dimensionGrille * dimensionGrille) - 1
            carte[i] vaut 0
        fin pour

        cx est un nombre
        cy est un nombre
        cx vaut 0
        cy vaut 0
        carte[0] vaut 1

        tant que cx < dimensionGrille - 1 ou cy < dimensionGrille - 1
            si cx = dimensionGrille - 1 alors
                cy ajoute 1
            sinon si cy = dimensionGrille - 1 alors
                cx ajoute 1
            sinon
                r vaut hasard(0, 1)
                si r = 0 alors
                    cx ajoute 1
                sinon
                    cy ajoute 1
                fin si
            fin si

            indexChemin vaut (cy * dimensionGrille) + cx
            si indexChemin >= 0 et indexChemin < longueur(carte) alors
                carte[indexChemin] vaut 1
            fin si
        fin tant que

        pour i de 1 à 15
            rx vaut hasard(0, dimensionGrille - 1)
            ry vaut hasard(0, dimensionGrille - 1)

            si rx >= 0 et rx < dimensionGrille et ry >= 0 et ry < dimensionGrille alors
                idxCourant vaut (ry * dimensionGrille) + rx
                si idxCourant >= 0 et idxCourant < longueur(carte) et carte[idxCourant] = 0 alors
                    adj vaut 0

                    si rx > 0 alors
                        idxG vaut (ry * dimensionGrille) + rx - 1
                        si idxG >= 0 et idxG < longueur(carte) et carte[idxG] > 0 alors adj vaut 1 fin si
                    fin si
                    si rx < dimensionGrille - 1 alors
                        idxD vaut (ry * dimensionGrille) + rx + 1
                        si idxD >= 0 et idxD < longueur(carte) et carte[idxD] > 0 alors adj vaut 1 fin si
                    fin si
                    si ry > 0 alors
                        idxH vaut ((ry - 1) * dimensionGrille) + rx
                        si idxH >= 0 et idxH < longueur(carte) et carte[idxH] > 0 alors adj vaut 1 fin si
                    fin si
                    si ry < dimensionGrille - 1 alors
                        idxB vaut ((ry + 1) * dimensionGrille) + rx
                        si idxB >= 0 et idxB < longueur(carte) et carte[idxB] > 0 alors adj vaut 1 fin si
                    fin si

                    si adj = 1 alors
                        carte[idxCourant] vaut 1
                    fin si
                fin si
            fin si
        fin pour

        indexArrivee vaut ((dimensionGrille - 1) * dimensionGrille) + (dimensionGrille - 1)
        si indexArrivee >= 0 et indexArrivee < longueur(carte) alors
            carte[indexArrivee] vaut 2
        fin si

        pX vaut 0
        pY vaut 0

        nbG vaut hasard(2, 4)
        pour i de 1 à nbG
            trouve vaut faux
            essais vaut 0
            tant que non trouve et essais < 40
                gxRand vaut hasard(0, dimensionGrille - 1)
                gyRand vaut hasard(0, dimensionGrille - 1)

                si gxRand >= 0 et gxRand < dimensionGrille et gyRand >= 0 et gyRand < dimensionGrille alors
                    idxGrd vaut (gyRand * dimensionGrille) + gxRand
                    si idxGrd >= 0 et idxGrd < longueur(carte) alors
                        si carte[idxGrd] = 1 et (gxRand <> 0 ou gyRand <> 0) et (gxRand <> dimensionGrille - 1 ou gyRand <> dimensionGrille - 1) alors
                            libre vaut vrai
                            pour j de 0 à longueur(gardesX) - 1
                                si gardesX[j] = gxRand et gardesY[j] = gyRand alors libre vaut faux fin si
                            fin pour

                            si libre alors
                                gardesX ajoute gxRand
                                gardesY ajoute gyRand
                                gardesDir ajoute hasard(0, 3)
                                gardesActif ajoute 1
                                trouve vaut vrai
                            fin si
                        fin si
                    fin si
                fin si
                essais ajoute 1
            fin tant que
        fin pour

        etatJeu vaut 0
    fin si

    @ 2. RENDU VISUEL AMÉLIORÉ & AGRANDI (ZOOM ISOMÉTRIQUE)
    effacer(toile1)
    remplir(toile1, #0b131d)

    offX vaut 280
    offY vaut 80

    pour gy de 0 à dimensionGrille - 1
        pour gx de 0 à dimensionGrille - 1
            idxRendu1 vaut (gy * dimensionGrille) + gx
            si idxRendu1 >= 0 et idxRendu1 < longueur(carte) et carte[idxRendu1] > 0 alors
                c1x vaut offX + (gx - gy) * 36
                c1y vaut offY + (gx + gy) * 20

                si gx < dimensionGrille - 1 alors
                    idxR2 vaut (gy * dimensionGrille) + gx + 1
                    si idxR2 >= 0 et idxR2 < longueur(carte) et carte[idxR2] > 0 alors
                        c2x vaut offX + (gx + 1 - gy) * 36
                        c2y vaut offY + (gx + 1 + gy) * 20
                        ligne(toile1, c1x, c1y, c2x, c2y, rgba(255, 255, 255, 0.15), 6)
                    fin si
                fin si

                si gy < dimensionGrille - 1 alors
                    idxR3 vaut ((gy + 1) * dimensionGrille) + gx
                    si idxR3 >= 0 et idxR3 < longueur(carte) et carte[idxR3] > 0 alors
                        c2x vaut offX + (gx - (gy + 1)) * 36
                        c2y vaut offY + (gx + (gy + 1)) * 20
                        ligne(toile1, c1x, c1y, c2x, c2y, rgba(255, 255, 255, 0.15), 6)
                    fin si
                fin si
            fin si
        fin pour
    fin pour

    pour gy de 0 à dimensionGrille - 1
        pour gx de 0 à dimensionGrille - 1
            idxRendu4 vaut (gy * dimensionGrille) + gx
            si idxRendu4 >= 0 et idxRendu4 < longueur(carte) alors
                valN vaut carte[idxRendu4]
                si valN > 0 alors
                    cx vaut offX + (gx - gy) * 36
                    cy vaut offY + (gx + gy) * 20

                    si valN = 2 alors
                        ellipse(toile1, cx, cy + 3, 20, 11, rgba(0, 0, 0, 0.4))
                        ellipse(toile1, cx, cy, 18, 10, #145a32)
                        ellipse(toile1, cx, cy - 3, 15, 8, #27ae60)
                    sinon
                        ellipse(toile1, cx, cy + 3, 18, 10, rgba(0, 0, 0, 0.4))
                        ellipse(toile1, cx, cy, 16, 9, #34495e)
                        ellipse(toile1, cx, cy - 3, 13, 7, #bdc3c7)
                    fin si

                    @ Affichage du Joueur (Agent 47) avec position fixe et cohérente sur sa case
                    si pX = gx et pY = gy alors
                        ellipse(toile1, cx, cy + 2, 10, 5, rgba(0, 0, 0, 0.6))
                        rectangle(toile1, cx - 5, cy - 24, 10, 20, #17202a)
                        rectangle(toile1, cx - 2, cy - 20, 4, 10, #c0392b)
                        cercle(toile1, cx, cy - 25, 5, #f5cba7)
                    fin si

                    pour i de 0 à longueur(gardesX) - 1
                        si gardesActif[i] = 1 et gardesX[i] = gx et gardesY[i] = gy alors
                            ellipse(toile1, cx, cy + 2, 10, 5, rgba(0, 0, 0, 0.6))
                            rectangle(toile1, cx - 5, cy - 22, 10, 18, #d4ac0d)
                            cercle(toile1, cx, cy - 26, 6, #eaeded)

                            vx vaut 0
                            vy vaut 0
                            dir vaut gardesDir[i]
                            si dir = 0 alors vx vaut 18 vy vaut 10 fin si
                            si dir = 1 alors vx vaut - 18 vy vaut 10 fin si
                            si dir = 2 alors vx vaut - 18 vy vaut - 10 fin si
                            si dir = 3 alors vx vaut 18 vy vaut - 10 fin si

                            ligne(toile1, cx, cy - 16, cx + vx, cy - 16 + vy, rgba(231, 76, 60, 0.85), 2.5)
                        fin si
                    fin pour
                fin si
            fin si
        fin pour
    fin pour

    @ 3. INTERFACE UTILISATEUR
    rectangle(toile1, 0, 0, 600, 55, rgba(11, 19, 29, 0.85))
    si etatJeu = 0 alors
        label(toile1, 15, 22, "AGENT 47 : Infiltration tactique", #ffffff, 14)
        label(toile1, 15, 42, "Mouvements: Z Q S D  |  Attendre: A", #95a5a6, 11)
    sinon si etatJeu = 1 alors
        rectangle(toile1, 130, 140, 340, 110, rgba(11, 19, 29, 0.95))
        label(toile1, 205, 185, "OBJECTIF ATTEINT", #27ae60, 18)
        label(toile1, 160, 215, "Appuyez sur ESPACE pour continuer", #ffffff, 11)
    sinon si etatJeu = 2 alors
        rectangle(toile1, 150, 140, 300, 110, rgba(11, 19, 29, 0.95))
        label(toile1, 235, 185, "REPÉRÉ / ÉLIMINÉ", #c0392b, 18)
        label(toile1, 165, 215, "Appuyez sur ESPACE pour réessayer", #ffffff, 11)
    fin si

    affiche toile1

    @ 4. GESTION DES TOURS et COMBAT
    appuyer ["z", "s", "q", "d", " ", "a"] dans action

    si etatJeu = 1 ou etatJeu = 2 alors
        si action = 5 alors
            etatJeu vaut 3
        fin si
    sinon si etatJeu = 0 alors

        nx vaut pX
        ny vaut pY
        tourJoue vaut faux

        si action = 1 alors ny soustrait 1 fin si
        si action = 2 alors ny ajoute 1 fin si
        si action = 3 alors nx soustrait 1 fin si
        si action = 4 alors nx ajoute 1 fin si
        si action = 6 alors tourJoue vaut vrai fin si

        si nx >= 0 et nx < dimensionGrille et ny >= 0 et ny < dimensionGrille alors
            idxJoueur vaut (ny * dimensionGrille) + nx
            si idxJoueur >= 0 et idxJoueur < longueur(carte) alors
                si carte[idxJoueur] > 0 alors
                    pX vaut nx
                    pY vaut ny
                    tourJoue vaut vrai
                fin si
            fin si
        fin si

        si tourJoue = vrai alors

            pour i de 0 à longueur(gardesX) - 1
                si gardesActif[i] = 1 et gardesX[i] = pX et gardesY[i] = pY alors
                    gardesActif[i] vaut 0
                fin si
            fin pour

            pour i de 0 à longueur(gardesX) - 1
                si gardesActif[i] = 1 alors
                    si gardesX[i] >= 0 et gardesX[i] < dimensionGrille et gardesY[i] >= 0 et gardesY[i] < dimensionGrille alors
                        d vaut gardesDir[i]
                        gnx vaut gardesX[i]
                        gny vaut gardesY[i]

                        si d = 0 alors gnx ajoute 1 fin si
                        si d = 1 alors gny ajoute 1 fin si
                        si d = 2 alors gnx soustrait 1 fin si
                        si d = 3 alors gny soustrait 1 fin si

                        si gnx >= 0 et gnx < dimensionGrille et gny >= 0 et gny < dimensionGrille alors
                            idxG2 vaut (gny * dimensionGrille) + gnx
                            si idxG2 >= 0 et idxG2 < longueur(carte) alors
                                si carte[idxG2] > 0 alors
                                    caseLibre vaut vrai
                                    pour j de 0 à longueur(gardesX) - 1
                                        si i <> j et gardesActif[j] = 1 et gardesX[j] = gnx et gardesY[j] = gny alors
                                            caseLibre vaut faux
                                        fin si
                                    fin pour

                                    si caseLibre alors
                                        gardesX[i] vaut gnx
                                        gardesY[i] vaut gny
                                    sinon
                                        gardesDir[i] vaut (d + 2) mod 4
                                    fin si
                                sinon
                                    gardesDir[i] vaut (d + 2) mod 4
                                fin si
                            sinon
                                gardesDir[i] vaut (d + 2) mod 4
                            fin si
                        sinon
                            gardesDir[i] vaut (d + 2) mod 4
                        fin si
                    fin si
                fin si
            fin pour

            pour i de 0 à longueur(gardesX) - 1
                si gardesActif[i] = 1 alors
                    si gardesX[i] >= 0 et gardesX[i] < dimensionGrille et gardesY[i] >= 0 et gardesY[i] < dimensionGrille alors
                        si gardesX[i] = pX et gardesY[i] = pY alors
                            etatJeu vaut 2
                        fin si

                        fx vaut gardesX[i]
                        fy vaut gardesY[i]
                        si gardesDir[i] = 0 alors fx ajoute 1 fin si
                        si gardesDir[i] = 1 alors fy ajoute 1 fin si
                        si gardesDir[i] = 2 alors fx soustrait 1 fin si
                        si gardesDir[i] = 3 alors fy soustrait 1 fin si

                        si fx >= 0 et fx < dimensionGrille et fy >= 0 et fy < dimensionGrille alors
                            idxF vaut (fy * dimensionGrille) + fx
                            si idxF >= 0 et idxF < longueur(carte) alors
                                si pX = fx et pY = fy alors
                                    etatJeu vaut 2
                                fin si
                            fin si
                        fin si
                    fin si
                fin si
            fin pour

            idxFinValidation vaut (pY * dimensionGrille) + pX
            si etatJeu <> 2 et idxFinValidation >= 0 et idxFinValidation < longueur(carte) et carte[idxFinValidation] = 2 alors
                etatJeu vaut 1
            fin si

        fin si
    fin si

fin tant que
Pas encore noté