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

Un Jeu de l’Oie revisité
Article

Un Jeu de l’Oie revisité

Un Jeu de l’Oie revisité : immersion dans un programme entre tradition et stratégie

Lire l'articleReplier l'article

Le Jeu de l’Oie est un classique intemporel. Pourtant, derrière sa simplicité apparente se cache un terrain fertile pour l’apprentissage de la logique, de la programmation et de la gestion d’événements. Le programme présenté ici propose une version numérique modernisée, jouable sur une toile graphique, avec animations, emojis, cases spéciales et une véritable boucle de jeu interactive.

Un plateau isométrique soigneusement construit

Dès l’initialisation, le programme prépare une toile qui servira de plateau. Celui-ci est dessiné en perspective isométrique, donnant un effet 3D élégant.
Chaque case est représentée par un losange, positionné selon un calcul précis mêlant lignes, colonnes et décalages visuels.

Les couleurs et les emojis permettent d’identifier immédiatement les cases importantes :

  • Case 0 : départ
  • Case 63 : arrivée
  • Case 6 : pont
  • Case 31 : puits
  • Case 42 : labyrinthe
  • Case 52 : prison
  • Case 58 : mort
  • Cases d’oie : une série de cases qui déclenchent un bonus de déplacement

Les positions du joueur et de l’ordinateur sont affichées directement sur le plateau.

Une mécanique de jeu fidèle mais enrichie

Le cœur du programme repose sur une boucle de jeu qui s’exécute tant que la partie n’est pas terminée.
Chaque tour commence par un clic du joueur sur la toile, déclenchant le lancer des deux dés.

Lancer de dés et déplacement

Le programme génère deux valeurs aléatoires entre 1 et 6, puis calcule la somme.
Le déplacement suit les règles traditionnelles :

  • Avancer du total des dés
  • Si l’on dépasse la case 63, on recule du surplus
  • Si l’on tombe sur une case d’oie, on rejoue immédiatement et avance à nouveau du même total

Chaque action est accompagnée d’un message affiché sur le plateau, créant une narration dynamique.

Les cases spéciales : obstacles et rebondissements Le programme gère plusieurs événements emblématiques du Jeu de l’Oie :

- Pont (case 6)
Le joueur saute directement à la case 12.

- Puits (case 31)
Le joueur est bloqué pendant deux tours.

- Labyrinthe (case 42)
Retour forcé à la case 30.

- Prison (case 52)
Encore un blocage de deux tours.

- Mort (case 58)
Retour au départ, un coup dur qui peut renverser la partie.

Ces événements sont traités dans une fonction dédiée, garantissant une structure claire et réutilisable.

Un adversaire automatisé mais équitable

L’ordinateur joue selon les mêmes règles que le joueur :

  • Lancer de dés
  • Gestion des cases spéciales
  • Blocages
  • Déplacements et rebonds

Le duel est équilibré, et la progression des deux participants est visible en temps réel sur le plateau.

Fin de partie et déclaration du vainqueur

La partie s’arrête lorsque l’un des joueurs atteint exactement la case 63.
Un panneau final s’affiche alors :

  • Vainqueur : Toi ou Ordi

Si la partie dépasse 200 tours, elle est automatiquement interrompue pour éviter les boucles infinies.

Un programme pédagogique et ludique

Ce Jeu de l’Oie numérique est bien plus qu’une simple reproduction du jeu traditionnel.
Il illustre :

  • La gestion d’une interface graphique
  • Le dessin isométrique
  • Les événements aléatoires
  • Les structures conditionnelles
  • Les boucles de jeu
  • La modularité via des fonctions
  • L’interactivité par clic utilisateur

C’est un excellent exemple de projet mêlant logique, créativité, et plaisir de jeu.

Programme :

@ Initialisation
plateau est une toile

posJoueur, posOrdi est un nombre
posJoueur vaut 0
posOrdi vaut 0

bloqueJoueur, bloqueOrdi est un nombre
bloqueJoueur vaut 0
bloqueOrdi vaut 0

de1, de2 est un nombre
de1 vaut 1
de2 vaut 1

clicX, clicY est un nombre
clicX vaut 0
clicY vaut 0

fini est un booléen
fini vaut faux

gagnant est un texte
gagnant vaut ""

messages est un tableau
messages ajoute "Bienvenue dans le Jeu de l'Oie !"
messages ajoute "Atteins la case 63 exactement."
messages ajoute "Clique sur la toile pour lancer les 2 dés."

@ Affichage du Plateau
procédure afficherPlateau()
    // Efface tout le contenu de la toile avant de redessiner
    effacer(plateau)

    dimension(plateau, 600, 450)
    remplir(plateau, #F5F5DC)

    contour(plateau, #FFFFFF, 2)

    TILE_W, TILE_H est un nombre
    TILE_W vaut 64
    TILE_H vaut 32

    OFFSET_X, OFFSET_Y est un nombre
    OFFSET_X vaut 300
    // Décalage vers le bas (OFFSET_Y augmenté à 110)
    OFFSET_Y vaut 110

    rangee est un nombre
    pour rangee de 0 à 7
        colonne est un nombre
        pour colonne de 0 à 7
            i est un nombre
            si rangee mod 2 = 0 alors
                i vaut rangee * 8 + colonne
            sinon
                i vaut rangee * 8 + (7 - colonne)
            fin si

            isoX, isoY est un nombre
            isoX vaut OFFSET_X + (colonne - rangee) * (TILE_W / 2)
            isoY vaut OFFSET_Y + (colonne + rangee) * (TILE_H / 2)

            coul est un texte
            coul vaut #E8E4D0
            si i = 0 alors
                coul vaut #22C55E
            sinon si i = 63 alors
                coul vaut #FACC15
            sinon si i = 6 alors
                coul vaut #38BDF8
            sinon si i = 31 alors
                coul vaut #3B82F6
            sinon si i = 42 alors
                coul vaut #A855F7
            sinon si i = 52 alors
                coul vaut #6B7280
            sinon si i = 58 alors
                coul vaut #1F2937
            sinon si contient([5, 9, 14, 18, 23, 27, 32, 36, 41, 45, 50, 54, 59], i) alors
                coul vaut #FB923C
            fin si

            polygone(plateau, isoX, isoY - (TILE_H / 2), isoX + (TILE_W / 2), isoY, isoX, isoY + (TILE_H / 2), isoX - (TILE_W / 2), isoY, coul)

            label(plateau, isoX - 6, isoY - 3, convertir_texte(i), #334155, 10)

            emoji est un texte
            emoji vaut ""
            si i = 0 alors
                emoji vaut "🏁"
            sinon si i = 63 alors
                emoji vaut "🏆"
            sinon si i = 6 alors
                emoji vaut "🌉"
            sinon si i = 31 alors
                emoji vaut "💧"
            sinon si i = 42 alors
                emoji vaut "🌀"
            sinon si i = 52 alors
                emoji vaut "🔒"
            sinon si i = 58 alors
                emoji vaut "💀"
            sinon si contient([5, 9, 14, 18, 23, 27, 32, 36, 41, 45, 50, 54, 59], i) alors
                emoji vaut "🦢"
            fin si

            si emoji <> "" alors
                label(plateau, isoX - 8, isoY + 8, emoji, #000000, 13)
            fin si

            si i = posJoueur et i = posOrdi alors
                label(plateau, isoX - 14, isoY - 16, "😎🤖", #000000, 14)
            sinon si i = posJoueur alors
                label(plateau, isoX - 8, isoY - 16, "😎", #000000, 14)
            sinon si i = posOrdi alors
                label(plateau, isoX - 8, isoY - 16, "🤖", #000000, 14)
            fin si
        fin pour
    fin pour

    contour(plateau, #0)

    // Panneau de notifications
    nbMsg est un nombre
    nbMsg vaut longueur(messages)
    pour k de 0 à nbMsg - 1
        label(plateau, 10, 20 + k * 18, messages[k], #1E293B, 12)
    fin pour

    // Bouton d'interaction replacé en bas
    si fini = faux alors
        rectangle_arrondi(plateau, 170, 390, 140, 40, 8, #2563EB)
        label(plateau, 185, 415, "🎲 Lancer les dés", #FFFFFF, 13)
    fin si

    // Dessin des 2 dés replacés en bas
    contour(plateau, #334155, 2)
    rectangle_arrondi(plateau, 330, 390, 38, 38, 6, #FFFFFF)
    label(plateau, 343, 415, convertir_texte(de1), #000000, 18)

    rectangle_arrondi(plateau, 375, 390, 38, 38, 6, #FFFFFF)
    label(plateau, 388, 415, convertir_texte(de2), #000000, 18)
    contour(plateau, #0)

    affiche plateau
fin procédure

@ Fonctions
fonction caseSpeciale(p)
    si p = 6 alors
        messages ajoute " 🌉 PONT ! Saut à la case 12."
        retourne 12
    fin si
    si p = 31 alors
        messages ajoute " 💧 PUITS ! Bloqué."
        retourne 31
    fin si
    si p = 42 alors
        messages ajoute " 🌀 LABYRINTHE ! Retour à 30."
        retourne 30
    fin si
    si p = 52 alors
        messages ajoute " 🔒 PRISON ! Bloqué."
        retourne 52
    fin si
    si p = 58 alors
        messages ajoute " 💀 MORT ! Retour au départ."
        retourne 0
    fin si
    retourne p
fin fonction

fonction jouerTour(nom, pos)
    de1 vaut hasard(1, 6)
    de2 vaut hasard(1, 6)
    desTotal est un nombre
    desTotal vaut de1 + de2

    messages ajoute nom + " lance 2 dés : 🎲 " + convertir_texte(de1) + " + 🎲 " + convertir_texte(de2) + " = " + convertir_texte(desTotal)

    nouvellePos est un nombre
    nouvellePos vaut pos + desTotal

    si nouvellePos > 63 alors
        surplus est un nombre
        surplus vaut nouvellePos - 63
        nouvellePos vaut 63 - surplus
        messages ajoute " ↩️ Dépassement ! Recule à " + convertir_texte(nouvellePos)
    fin si

    si contient([5, 9, 14, 18, 23, 27, 32, 36, 41, 45, 50, 54, 59], nouvellePos) alors
        messages ajoute " 🦢 OIE ! Rejoue et avance de " + convertir_texte(desTotal)
        nouvellePos vaut nouvellePos + desTotal
        si nouvellePos > 63 alors
            surplus2 est un nombre
            surplus2 vaut nouvellePos - 63
            nouvellePos vaut 63 - surplus2
        fin si
    fin si

    nouvellePos vaut caseSpeciale(nouvellePos)
    messages ajoute " -> " + nom + " est sur la case " + convertir_texte(nouvellePos)
    retourne nouvellePos
fin fonction

@ Boucle de jeu
tour est un nombre
tour vaut 0

appelle afficherPlateau()

tant que fini = faux
    cliquer plateau dans clicX, clicY

    tour vaut tour + 1

    vide messages
    messages ajoute "--- Tour " + convertir_texte(tour) + " ---"

    si bloqueJoueur > 0 alors
        messages ajoute "😎 Tu es bloqué, tu passes."
        bloqueJoueur vaut bloqueJoueur - 1
    sinon
        posJoueur vaut jouerTour("😎 Toi", posJoueur)
        si posJoueur = 31 alors
            bloqueJoueur vaut 2
        fin si
        si posJoueur = 52 alors
            bloqueJoueur vaut 2
        fin si
        si posJoueur = 63 alors
            gagnant vaut "😎 Toi"
            fini vaut vrai
        fin si
    fin si

    si fini = faux alors
        si bloqueOrdi > 0 alors
            messages ajoute "🤖 L'ordinateur est bloqué, il passe."
            bloqueOrdi vaut bloqueOrdi - 1
        sinon
            posOrdi vaut jouerTour("🤖 Ordi", posOrdi)
            si posOrdi = 31 alors
                bloqueOrdi vaut 2
            fin si
            si posOrdi = 52 alors
                bloqueOrdi vaut 2
            fin si
            si posOrdi = 63 alors
                gagnant vaut "🤖 Ordi"
                fini vaut vrai
            fin si
        fin si
    fin si

    appelle afficherPlateau()

    si tour > 200 alors
        fini vaut vrai
    fin si
fin tant que

vide messages
messages ajoute "🏁 === FIN DE LA PARTIE === 🏁"
si gagnant = "" alors
    messages ajoute "Partie interrompue."
sinon
    messages ajoute "🎉 Vainqueur : " + gagnant + " ! 🎉"
fin si

appelle afficherPlateau()
Pas encore noté
RPG Exploration 3D : un moteur de jeu minimaliste mais riche, entièrement généré procéduralement
Article

RPG Exploration 3D

RPG Exploration 3D : un moteur de jeu minimaliste mais riche, entièrement généré procéduralement

Lire l'articleReplier l'article

Ce projet illustre comment un moteur de jeu peut être construit sans assets pré-fabriqués, uniquement grâce à des calculs mathématiques, des règles procédurales et un rendu 2.5D.
Le résultat : une expérience d’exploration dynamique, cohérente et entièrement générée à la volée.

Une carte infinie, générée par formules

Le monde du jeu repose sur une logique simple mais puissante : aucune carte n’est stockée, tout est calculé à partir des coordonnées du joueur.

Grâce à quelques opérations modulaires, chaque case peut devenir :

  • herbe (zone traversable)
  • terre (frontière naturelle)
  • eau (obstacle)

Et selon des règles de hash :

  • arbre
  • rocher
  • orbe d’énergie
  • coffre d’or

Cette approche permet un monde infini, cohérent et sans répétition visible.

Une sortie générée aléatoirement… mais toujours atteignable

Le jeu place une porte de sortie sur une case valide, en vérifiant qu’elle n’est ni eau, ni arbre, ni rocher.
Le joueur doit l’atteindre avec au moins 50 pièces d’or, ce qui crée un objectif clair et motivant.

« Trouve la porte aux coordonnées (X, Y) avec 50 or ! »

Gestion d’énergie et interactions

Chaque déplacement consomme de l’énergie.

Le joueur doit donc :

  • éviter les obstacles,
  • ramasser des orbes pour recharger,
  • ouvrir des coffres pour accumuler de l’or,
  • surveiller son HUD pour ne pas tomber à zéro.

L’énergie devient une ressource stratégique, transformant l’exploration en véritable gestion de survie.

Un rendu pseudo-3D basé sur la perspective

Le moteur utilise une projection simple mais efficace :

  • 15 couches de profondeur,
  • un élargissement ou rétrécissement des polygones selon la distance,
  • un décor qui se dessine en temps réel autour du joueur.

Chaque élément (arbre, rocher, coffre, orbe, porte) possède sa propre forme 2.5D, calculée à partir de la distance et de la taille projetée.

Le résultat : une impression de relief et de profondeur, sans recourir à un vrai moteur 3D.

Un personnage animé par la logique

Le personnage n’est pas statique :

  • ses jambes bougent selon la somme des coordonnées, créant une animation de marche minimaliste mais expressive.
  • Son ombre, son torse, ses bras et sa tête sont dessinés à partir de formes simples, mais l’ensemble donne une vraie présence visuelle.

Un HUD complet pour guider le joueur

Le jeu affiche :

  • la position du joueur,
  • la position de la sortie,
  • la direction optimale,
  • l’énergie restante,
  • l’or collecté,
  • un message dynamique selon les interactions.

« CAP SORTIE : NORD (Z) et EST (D) »

Ce guidage transforme l’exploration en quête structurée, tout en laissant la liberté de se déplacer.

Une boucle de gameplay simple, mais efficace

Le joueur avance, explore, collecte, évite, recharge…
Jusqu’à atteindre la porte avec suffisamment d’or.

Lorsqu’il y parvient :

  • « Porte déverrouillée ! Victoire ! »

Une boucle courte, mais parfaitement cohérente et gratifiante.

Un projet qui démontre la puissance du procédural

Ce moteur RPG minimaliste prouve qu’avec :

  • des règles mathématiques,
  • un rendu 2.5D intelligent,
  • une gestion d’état claire,
  • et une logique d’interaction bien pensée,
  • on peut créer une expérience de jeu immersive, dynamique et entièrement générée en temps réel.

Un excellent exemple de ce que l’on peut accomplir en combinant créativité, algorithmique et design.

Programme :

@ Initialisation
toile1 est une toile
dimension(toile1, 550, 400)

jX est un nombre
jY est un nombre
jX vaut 0
jY vaut 0

// Génération de la porte sur une case libre (ni eau, ni arbre, ni rocher)
exitX est un nombre
exitY est un nombre
porteValide est un booléen
porteValide vaut faux

tant que porteValide = faux
    exitX vaut hasard(40, 200)
    exitY vaut hasard(40, 200)

    eNX est un nombre
    eNY est un nombre
    eNX vaut exitX + 50000
    eNY vaut exitY + 50000

    eSol est un nombre
    eSol vaut 0
    si eNX mod 14 = 0 ou eNY mod 14 = 0 alors
        eSol vaut 1
    sinon si (eNX * 3 + eNY * 2) mod 31 < 2 alors
        eSol vaut 2
    fin si

    eDecor est un nombre
    eDecor vaut 0
    si eSol = 0 alors
        eHash est un nombre
        eHash vaut (eNX * 17 + eNY * 37) mod 100
        si eHash < 12 alors eDecor vaut 1
        sinon si eHash < 20 alors eDecor vaut 2
        fin si
    fin si

    // On valide seulement si ce n'est pas de l'eau, un arbre ou un rocher
    si eSol <> 2 et eDecor <> 1 et eDecor <> 2 alors
        porteValide vaut vrai
    fin si
fin tant que

orRequis est un nombre
orRequis vaut 50

action est un nombre
derniereAction est un nombre
derniereAction vaut 1

@ Inventaire et Énergie
orJoueur est un nombre
orJoueur vaut 0

objetsDetruits est un dictionnaire
objetsDetruits vaut [:]

energie est un nombre
energie vaut 100

victoire est un booléen
victoire vaut faux

messageHUD est un texte
messageHUD vaut "Trouve la porte aux coordonnées (" + convertir_texte(exitX) + ", " + convertir_texte(exitY) + ") avec 50 or !"

@ Boucle principale
tant que vrai
    effacer(toile1)
    remplir(toile1, #0f172a)

    // 1. Ciel et Soleil
    rectangle(toile1, 0, 0, 550, 120, #0f172a)
    cercle(toile1, 460, 40, 20, #f1c40f)

    // 2. Rendu du terrain 3D
    iY est un nombre
    pour iY de 0 à 15
        rY est un nombre
        rY vaut 15 - iY

        rX est un nombre
        pour rX de - 8 à 8
            gx est un nombre
            gy est un nombre
            gx vaut jX + rX
            gy vaut jY + rY

            nx est un nombre
            ny est un nombre
            nx vaut gx + 50000
            ny vaut gy + 50000

            dLoin est un nombre
            dPres est un nombre
            dLoin vaut rY + 0.5 + 2.0
            dPres vaut rY - 0.5 + 2.0

            sLoin est un nombre
            sPres est un nombre
            sLoin vaut 180 / dLoin
            sPres vaut 180 / dPres

            xHautG est un nombre
            yHautG est un nombre
            xHautD est un nombre
            yHautD est un nombre
            xBasD est un nombre
            yBasD est un nombre
            xBasG est un nombre
            yBasG est un nombre

            xHautG vaut 275 + (rX - 0.5) * sLoin * 1.3
            yHautG vaut 120 + sLoin * 2.5
            xHautD vaut 275 + (rX + 0.5) * sLoin * 1.3
            yHautD vaut 120 + sLoin * 2.5

            xBasD vaut 275 + (rX + 0.5) * sPres * 1.3
            yBasD vaut 120 + sPres * 2.5
            xBasG vaut 275 + (rX - 0.5) * sPres * 1.3
            yBasG vaut 120 + sPres * 2.5

            dCentre est un nombre
            dCentre vaut rY + 2.0
            sCentre est un nombre
            sCentre vaut 180 / dCentre

            cx est un nombre
            cy est un nombre
            cx vaut 275 + rX * sCentre * 1.3
            cy vaut 120 + sCentre * 2.5

            typeSol est un nombre
            typeSol vaut 0

            si nx mod 14 = 0 ou ny mod 14 = 0 alors
                typeSol vaut 1
            sinon si (nx * 3 + ny * 2) mod 31 < 2 alors
                typeSol vaut 2
            fin si

            ptsSol est un tableau
            ptsSol ajoute xHautG
            ptsSol ajoute yHautG
            ptsSol ajoute xHautD
            ptsSol ajoute yHautD
            ptsSol ajoute xBasD
            ptsSol ajoute yBasD
            ptsSol ajoute xBasG
            ptsSol ajoute yBasG

            si typeSol = 0 alors
                polygone(toile1, ptsSol, #27ae60)
            sinon si typeSol = 1 alors
                polygone(toile1, ptsSol, #d35400)
            sinon
                polygone(toile1, ptsSol, #2980b9)
            fin si

            contour(toile1, #1a252f, 1)

            // Génération des éléments sur la carte
            typeDecor est un nombre
            typeDecor vaut 0

            si gx = exitX et gy = exitY alors
                typeDecor vaut 5
            sinon si typeSol = 0 alors
                hash est un nombre
                hash vaut (nx * 17 + ny * 37) mod 100
                si hash < 12 alors typeDecor vaut 1        // Arbre
                sinon si hash < 20 alors typeDecor vaut 2   // Rocher
                sinon si hash < 26 alors typeDecor vaut 3   // Orbe d'Énergie
                sinon si hash < 31 alors typeDecor vaut 4   // Coffre d me d'Or
                fin si
            fin si

            cleCase est un texte
            cleCase vaut convertir_texte(nx) + "," + convertir_texte(ny)
            si contient(objetsDetruits, cleCase) et typeDecor <> 5 alors
                typeDecor vaut 0
            fin si

            hTaille est un nombre
            hTaille vaut sCentre * 0.85

            si typeDecor = 1 alors
                // Arbre (Obstacle)
                ellipse(toile1, cx, cy, hTaille * 0.3, hTaille * 0.1, #0e171e)
                rectangle(toile1, cx - hTaille * 0.08, cy - hTaille * 0.6, hTaille * 0.16, hTaille * 0.6, #5d4037)
                cercle(toile1, cx, cy - hTaille * 0.85, hTaille * 0.35, #1e8449)
            sinon si typeDecor = 2 alors
                // Rocher (Obstacle)
                ellipse(toile1, cx, cy, hTaille * 0.25, hTaille * 0.1, #0e171e)
                cercle(toile1, cx, cy - hTaille * 0.15, hTaille * 0.2, #7f8c8d)
            sinon si typeDecor = 3 alors
                // Orbe d'Énergie
                cercle(toile1, cx, cy - hTaille * 0.2, hTaille * 0.22, rgba(56, 189, 248, 0.4))
                cercle(toile1, cx, cy - hTaille * 0.2, hTaille * 0.14, #00d2ff)
                cercle(toile1, cx, cy - hTaille * 0.2, hTaille * 0.06, #ffffff)
            sinon si typeDecor = 4 alors
                // Coffre d'Or
                rectangle(toile1, cx - hTaille * 0.2, cy - hTaille * 0.3, hTaille * 0.4, hTaille * 0.3, #d35400)
                rectangle(toile1, cx - hTaille * 0.2, cy - hTaille * 0.3, hTaille * 0.4, hTaille * 0.08, #f1c40f)
                cercle(toile1, cx, cy - hTaille * 0.15, hTaille * 0.05, #f1c40f)
            sinon si typeDecor = 5 alors
                // Porte de Sortie
                rectangle(toile1, cx - hTaille * 0.25, cy - hTaille * 0.7, hTaille * 0.5, hTaille * 0.7, #f1c40f)
                rectangle(toile1, cx - hTaille * 0.18, cy - hTaille * 0.63, hTaille * 0.36, hTaille * 0.6, #0f172a)
                cercle(toile1, cx, cy - hTaille * 0.35, hTaille * 0.08, #f1c40f)
            fin si

        fin pour
    fin pour

    // 3. Personnage principal
    pX est un nombre
    pY est un nombre
    pX vaut 275
    pY vaut 340

    etapeMarche est un nombre
    etapeMarche vaut absolue(jX + jY) mod 4

    rebond est un nombre
    rebond vaut 0
    si etapeMarche = 1 ou etapeMarche = 3 alors rebond vaut -2 fin si

    ellipse(toile1, pX, pY + 16, 22, 6, rgba(0, 0, 0, 0.4))

    jambeG_Y est un nombre
    jambeD_Y est un nombre
    jambeG_Y vaut pY + 4
    jambeD_Y vaut pY + 4

    si etapeMarche = 1 alors jambeG_Y vaut pY
    sinon si etapeMarche = 3 alors jambeD_Y vaut pY
    fin si

    rectangle_arrondi(toile1, pX - 7, jambeG_Y, 6, 12, 2, #34495e)
    rectangle_arrondi(toile1, pX - 8, jambeG_Y + 10, 8, 5, 2, #111111)

    rectangle_arrondi(toile1, pX + 1, jambeD_Y, 6, 12, 2, #34495e)
    rectangle_arrondi(toile1, pX + 0, jambeD_Y + 10, 8, 5, 2, #111111)

    cY est un nombre
    cY vaut pY - 12 + rebond

    rectangle_arrondi(toile1, pX - 12, cY - 2, 6, 14, 3, #2980b9)
    cercle(toile1, pX - 9, cY + 12, 3, #f1c27d)

    rectangle_arrondi(toile1, pX + 6, cY - 2, 6, 14, 3, #2980b9)
    cercle(toile1, pX + 9, cY + 12, 3, #f1c27d)

    rectangle_arrondi(toile1, pX - 8, cY - 5, 16, 18, 4, #3498db)

    si derniereAction = 2 alors
        rectangle(toile1, pX - 8, cY + 9, 16, 4, #2c3e50)
        rectangle(toile1, pX - 2, cY + 8, 4, 6, #f1c40f)

        tY est un nombre
        tY vaut cY - 12
        cercle(toile1, pX, tY, 9, #f1c27d)
        arc(toile1, pX, tY + 2, 9, 180, 360, #6e2c00)
        cercle(toile1, pX - 3, tY - 1, 1, #000000)
        cercle(toile1, pX + 3, tY - 1, 1, #000000)
    sinon
        rectangle_arrondi(toile1, pX - 6, cY - 2, 12, 14, 3, #d35400)
        rectangle(toile1, pX - 6, cY + 3, 12, 2, #873600)

        tY est un nombre
        tY vaut cY - 12
        cercle(toile1, pX, tY, 9, #f1c27d)
        cercle(toile1, pX, tY - 2, 8, #6e2c00)
    fin si

    // 4. Interface HUD & Guidage
    rectangle(toile1, 0, 0, 550, 90, rgba(15, 23, 42, 0.95))
    contour(toile1, #334155, 1)

    label(toile1, 10, 15, "Or: " + convertir_texte(orJoueur) + " / " + convertir_texte(orRequis), #f1c40f, 12)

    rectangle(toile1, 140, 7, 120, 10, #334155)
    si energie > 0 alors
        colNRG est un texte
        colNRG vaut #38bdf8
        si energie < 30 alors colNRG vaut #ef4444 fin si
        rectangle(toile1, 140, 7, (energie / 100) * 120, 10, colNRG)
    fin si
    label(toile1, 270, 15, "Énergie: " + convertir_texte(energie) + "%", #38bdf8, 10)

    distX est un nombre
    distY est un nombre
    distX vaut exitX - jX
    distY vaut exitY - jY

    distTotale est un nombre
    distTotale vaut absolue(distX) + absolue(distY)

    txtPos est un texte
    txtPos vaut "Position: (" + convertir_texte(jX) + ", " + convertir_texte(jY) + ")  |  Sortie: (" + convertir_texte(exitX) + ", " + convertir_texte(exitY) + ")"
    label(toile1, 10, 36, txtPos, #94a3b8, 10)

    dirConseil est un texte
    dirConseil vaut "CAP SORTIE : "

    si distY > 0 et distX > 0 alors dirConseil vaut dirConseil + "NORD (Z) et EST (D)"
    sinon si distY > 0 et distX < 0 alors dirConseil vaut dirConseil + "NORD (Z) et OUEST (Q)"
    sinon si distY < 0 et distX > 0 alors dirConseil vaut dirConseil + "SUD (S) et EST (D)"
    sinon si distY < 0 et distX < 0 alors dirConseil vaut dirConseil + "SUD (S) et OUEST (Q)"
    sinon si distY > 0 alors dirConseil vaut dirConseil + "NORD (Touche Z)"
    sinon si distY < 0 alors dirConseil vaut dirConseil + "SUD (Touche S)"
    sinon si distX > 0 alors dirConseil vaut dirConseil + "EST (Touche D)"
    sinon si distX < 0 alors dirConseil vaut dirConseil + "OUEST (Touche Q)"
    sinon dirConseil vaut "SUR LA PORTE !"
    fin si

    label(toile1, 10, 56, dirConseil + " (" + convertir_texte(distTotale) + " pas)", #f59e0b, 11)
    label(toile1, 10, 78, ">> " + messageHUD, #22c55e, 11)

    si victoire alors
        rectangle(toile1, 75, 130, 400, 110, rgba(15, 23, 42, 0.95))
        rectangle(toile1, 75, 130, 400, 4, #f1c40f)
        label(toile1, 155, 175, "VICTOIRE !", #f1c40f, 20)
        label(toile1, 105, 210, "Porte atteinte aux coordonnées (" + convertir_texte(exitX) + ", " + convertir_texte(exitY) + ") !", #ffffff, 12)
    fin si

    affiche toile1

    // 5. Mouvements et Interactions
    appuyer ["z", "s", "q", "d"] dans action

    si action > 0 et victoire = faux alors
        derniereAction vaut action

        nouvX est un nombre
        nouvY est un nombre
        nouvX vaut jX
        nouvY vaut jY

        si action = 1 alors nouvY vaut jY + 1 fin si
        si action = 2 alors nouvY vaut jY - 1 fin si
        si action = 3 alors nouvX vaut jX - 1 fin si
        si action = 4 alors nouvX vaut jX + 1 fin si

        chkX est un nombre
        chkY est un nombre
        chkX vaut nouvX + 50000
        chkY vaut nouvY + 50000

        solFutur est un nombre
        solFutur vaut 0
        si chkX mod 14 = 0 ou chkY mod 14 = 0 alors
            solFutur vaut 1
        sinon si (chkX * 3 + chkY * 2) mod 31 < 2 alors
            solFutur vaut 2
        fin si

        decorFutur est un nombre
        decorFutur vaut 0

        si nouvX = exitX et nouvY = exitY alors
            decorFutur vaut 5
        sinon si solFutur = 0 alors
            hFutur est un nombre
            hFutur vaut (chkX * 17 + chkY * 37) mod 100
            si hFutur < 12 alors decorFutur vaut 1
            sinon si hFutur < 20 alors decorFutur vaut 2
            sinon si hFutur < 26 alors decorFutur vaut 3
            sinon si hFutur < 31 alors decorFutur vaut 4
            fin si
        fin si

        cleDest est un texte
        cleDest vaut convertir_texte(chkX) + "," + convertir_texte(chkY)
        si contient(objetsDetruits, cleDest) et decorFutur <> 5 alors
            decorFutur vaut 0
        fin si

        si solFutur = 2 alors
            messageHUD vaut "L'eau bloque le passage !"

        sinon si decorFutur = 1 alors
            messageHUD vaut "Un arbre bloque le passage !"

        sinon si decorFutur = 2 alors
            messageHUD vaut "Un rocher bloque le passage !"

        sinon si decorFutur = 5 alors
            si orJoueur >= orRequis alors
                victoire vaut vrai
                jX vaut nouvX
                jY vaut nouvY
                messageHUD vaut "Porte déverrouillée ! Victoire !"
            sinon
                messageHUD vaut "Il te faut " + convertir_texte(orRequis) + " pièces d'or ! (Tu en as " + convertir_texte(orJoueur) + ")"
            fin si

        sinon si decorFutur = 3 alors
            objetsDetruits[cleDest] vaut vrai
            energie vaut energie + 35
            si energie > 100 alors energie vaut 100 fin si
            jX vaut nouvX
            jY vaut nouvY
            messageHUD vaut "Orbe d'Énergie ramassé ! +35% Énergie !"

        sinon si decorFutur = 4 alors
            orJoueur vaut orJoueur + 10
            objetsDetruits[cleDest] vaut vrai
            jX vaut nouvX
            jY vaut nouvY
            messageHUD vaut "Coffre ouvert ! +10 Or récolté !"

        sinon
            jX vaut nouvX
            jY vaut nouvY
            energie vaut energie - 1

            si energie <= 0 alors
                energie vaut 0
                messageHUD vaut "Énergie épuisée ! Trouve un orbe bleu !"
            sinon
                messageHUD vaut "En route..."
            fin si
        fin si

    fin si
fin tant que
Pas encore noté
Pixel Art Binaire : un mini‑jeu interactif pour apprendre le codage visuel
Article

Pixel Art Binaire

Pixel Art Binaire : un mini‑jeu interactif pour apprendre le codage visuel

Lire l'articleReplier l'article

Ce programme propose une expérience ludique et pédagogique : reproduire un smiley en pixel art à partir d’un modèle binaire. Il combine interface graphique, interactions utilisateur et vérification automatique du résultat, offrant une manière intuitive d’aborder la logique informatique.

Initialisation du jeu

Le programme commence par définir les éléments essentiels :

  • Une grille de jeu affichée sur une toile graphique.
  • Une matrice 8×8, soit 64 cases, représentant les pixels du dessin.

Un modèle binaire composé de 8 chaînes de 8 caractères (0 ou 1) formant un smiley stylisé.

Chaque chiffre correspond à un pixel :

  • 0 : pixel blanc
  • 1 : pixel violet

La grille du joueur est initialisée avec 64 zéros, prête à être remplie.

Des paramètres d’affichage (taille des cases, position, couleurs) structurent l’interface et garantissent une présentation claire.

Panneau de gauche : le modèle à reproduire

On y trouve :

  • Les 8 lignes du code binaire
  • Une légende expliquant la signification des chiffres
  • Une présentation propre et arrondie pour faciliter la lecture

Grille de dessin

À droite, la grille 8×8 affiche :

  • La numérotation des lignes et colonnes
  • Des cases blanches par défaut
  • Des cases violettes lorsqu’on clique dessus

Chaque clic inverse la valeur : 0 : 1 ou 1 : 0.

Score en temps réel

Le programme compare en continu la grille du joueur au modèle :

  • Chaque pixel correct ajoute un point
  • Le score est affiché sur le bouton "Vérifier"

Boutons d’action

Deux boutons permettent :

Vérifier la grille (score final sur 64)

Effacer pour recommencer

Un message d’état accompagne le joueur tout au long de la partie.

La boucle de jeu : interaction et logique

La boucle tant que fini = faux gère les interactions :

Le programme attend un clic.

Il détecte si ce clic concerne :

  • une case de la grille
  • le bouton "Vérifier"
  • le bouton "Effacer"

Il met à jour la grille ou le message d’état.
Il redessine l’interface pour refléter les changements.

Validation du dessin

Lorsqu’on clique sur "Vérifier", le programme calcule un score final :

64/64 : message de réussite :

  • 🎉 Bravo ! Tu as parfaitement décodé l'image binaire !
  • Sinon : message indiquant le nombre de pixels corrects.

Un outil idéal pour apprendre le binaire autrement

Ce programme est un excellent support pour :

  • comprendre la représentation des images en binaire
  • manipuler des tableaux et des boucles
  • découvrir la logique des interfaces interactives
  • expérimenter le pixel art numérique

Il transforme un concept abstrait, le codage binaire, en une activité visuelle, intuitive et amusante.

Programme :

@ Initialisation
grilleJeu est une toile

colonnes, lignes est un nombre
colonnes vaut 8
lignes vaut 8

t est un nombre
t vaut 36

offsetX, offsetY est un nombre
offsetX vaut 340
offsetY vaut 80

teinte est un texte
teinte vaut #8B5CF6

// Modèle binaire (smiley)
modele est un tableau
modele ajoute "00111100"
modele ajoute "01000010"
modele ajoute "10100101"
modele ajoute "10000001"
modele ajoute "10100101"
modele ajoute "10011001"
modele ajoute "01000010"
modele ajoute "00111100"

// Grille du joueur (64 cases initialisées à 0)
grilleJoueur est un tableau
pour i de 0 à 63
    grilleJoueur ajoute 0
fin pour

clicX, clicY est un nombre
clicX vaut 0
clicY vaut 0

fini est un booléen
fini vaut faux

messageStatut est un texte
messageStatut vaut "Clique sur les cases de la grille pour dessiner le code !"

@ Affichage de l'interface
procédure dessinerJeu()
    effacer(grilleJeu)
    dimension(grilleJeu, 680, 430)
    remplir(grilleJeu, #F8FAFC)

    // En-tête
    rectangle(grilleJeu, 0, 0, 680, 50, #1E293B)
    label(grilleJeu, 20, 32, "🎨 Pixel Art Binaire — Décodage", #FFFFFF, 16)

    // Panneau de gauche : Modèle Binaire
    contour(grilleJeu, #CBD5E1, 1)
    rectangle_arrondi(grilleJeu, 20, 65, 280, 315, 8, #FFFFFF)
    contour(grilleJeu, #0)

    label(grilleJeu, 35, 90, "Code Binaire à reproduire :", #0F172A, 14)

    pour l de 0 à lignes - 1
        texteLigne est un texte
        texteLigne vaut "Ligne " + convertir_texte(l) + " :  " + modele[l]
        label(grilleJeu, 35, 120 + l * 26, texteLigne, #334155, 13)
    fin pour

    label(grilleJeu, 35, 360, "0 = Case blanche  |  1 = Pixel violet", #64748B, 11)

    // Numérotation des colonnes et lignes de la grille
    pour c de 0 à colonnes - 1
        label(grilleJeu, offsetX + c * t + 12, offsetY - 10, convertir_texte(c), #64748B, 12)
    fin pour

    pour l de 0 à lignes - 1
        label(grilleJeu, offsetX - 20, offsetY + l * t + 22, convertir_texte(l), #64748B, 12)
    fin pour

    // Dessin des cases de la grille
    contour(grilleJeu, #94A3B8, 1)
    pour l de 0 à lignes - 1
        pour c de 0 à colonnes - 1
            px, py, idx est un nombre
            px vaut offsetX + c * t
            py vaut offsetY + l * t
            idx vaut l * colonnes + c

            si grilleJoueur[idx] = 1 alors
                rectangle(grilleJeu, px, py, t, t, teinte)
            sinon
                rectangle(grilleJeu, px, py, t, t, #FFFFFF)
            fin si
        fin pour
    fin pour
    contour(grilleJeu, #0)

    // Calcul en temps réel du score
    scoreActuel est un nombre
    scoreActuel vaut 0
    pour l de 0 à lignes - 1
        chaineLigne est un texte
        chaineLigne vaut modele[l]
        pour c de 0 à colonnes - 1
            idx vaut l * colonnes + c
            attendu est un texte
            attendu vaut chaineLigne[c + 1]
            valeurJoueur est un texte
            valeurJoueur vaut convertir_texte(grilleJoueur[idx])

            si valeurJoueur = attendu alors
                scoreActuel vaut scoreActuel + 1
            fin si
        fin pour
    fin pour

    // Boutons d'action
    contour(grilleJeu, #2563EB, 1)
    rectangle_arrondi(grilleJeu, 340, 380, 150, 35, 6, #2563EB)
    label(grilleJeu, 355, 402, "Vérifier (" + convertir_texte(scoreActuel) + " / 64)", #FFFFFF, 12)

    contour(grilleJeu, #EF4444, 1)
    rectangle_arrondi(grilleJeu, 505, 380, 120, 35, 6, #EF4444)
    label(grilleJeu, 530, 402, "Effacer", #FFFFFF, 12)
    contour(grilleJeu, #0)

    // Message d'information
    label(grilleJeu, 20, 410, messageStatut, #1E293B, 12)

    affiche grilleJeu
fin procédure

@ Boucle de jeu
appelle dessinerJeu()

tant que fini = faux
    cliquer grilleJeu dans clicX, clicY

    // Clic sur une case de la grille
    pour l de 0 à lignes - 1
        pour c de 0 à colonnes - 1
            px, py est un nombre
            px vaut offsetX + c * t
            py vaut offsetY + l * t

            si clique(clicX, clicY, px, py, t, t) alors
                idx est un nombre
                idx vaut l * colonnes + c

                si grilleJoueur[idx] = 0 alors
                    grilleJoueur[idx] vaut 1
                sinon
                    grilleJoueur[idx] vaut 0
                fin si
            fin si
        fin pour
    fin pour

    // Clic sur le bouton "Vérifier"
    si clique(clicX, clicY, 340, 380, 150, 35) alors
        scoreTotal est un nombre
        scoreTotal vaut 0

        pour l de 0 à lignes - 1
            chaineLigne est un texte
            chaineLigne vaut modele[l]
            pour c de 0 à colonnes - 1
                idx vaut l * colonnes + c
                attendu est un texte
                attendu vaut chaineLigne[c + 1]
                valeurJoueur est un texte
                valeurJoueur vaut convertir_texte(grilleJoueur[idx])

                si valeurJoueur = attendu alors
                    scoreTotal vaut scoreTotal + 1
                fin si
            fin pour
        fin pour

        si scoreTotal = 64 alors
            messageStatut vaut "🎉 Bravo ! Tu as parfaitement décodé l'image binaire !"
            fini vaut vrai
        sinon
            messageStatut vaut "Encore des erreurs (" + convertir_texte(scoreTotal) + " / 64 pixels corrects)."
        fin si
    fin si

    // Clic sur le bouton "Effacer"
    si clique(clicX, clicY, 505, 380, 120, 35) alors
        pour i de 0 à 63
            grilleJoueur[i] vaut 0
        fin pour
        messageStatut vaut "Grille réinitialisée."
    fin si

    appelle dessinerJeu()
fin tant que
Pas encore noté
Dictionnaire vs Tableau : pourquoi la recherche n’a pas le même coût ?
Astuce

Dictionnaire vs Tableau

Dictionnaire vs Tableau : pourquoi la recherche n’a pas le même coût ?

Lire l'articleReplier l'article

Dans le monde de la programmation, il existe plusieurs façons de stocker et de retrouver des informations. Deux structures de données très courantes sont :

  • le tableau (array)
  • le dictionnaire (map, hash table)

À première vue, elles semblent similaires : on y range des données, puis on les consulte. Pourtant, lorsqu’il s’agit de retrouver une valeur à partir d’une clé, leurs performances sont radicalement différentes.

La recherche dans un tableau : un parcours obligatoire

Dans la première partie du programme, on stocke :

  • les noms dans un tableau noms
  • les numéros dans un tableau numeros

Pour retrouver le numéro associé à un nom, la fonction :

fonction chercheDansTableau(noms, numeros, cible)
    pour i de 0 à longueur(noms) - 1
        si noms[i] = cible alors
            retourne numeros[i]
        fin si
    fin pour
    retourne "introuvable"
fin fonction

Cette méthode impose un parcours élément par élément.
Si la cible se trouve :

  • au début : rapide
  • au milieu : moyen
  • à la fin : très lent

Dans ton test, tu ajoutes volontairement "Zoé" à la toute fin, ce qui force le tableau à parcourir 5001 entrées avant de trouver la bonne.

  • Complexité : O(n)

Plus il y a d’éléments, plus la recherche devient lente.

Le dictionnaire : l’accès direct par clé

Ensuite, tu construis un dictionnaire :

annuaire[noms[k]] vaut numeros[k]

Ici, chaque nom devient une clé, et chaque numéro une valeur.
Pour retrouver le numéro de Zoé, il suffit de faire :

annuaire["Zoé"]

Aucun parcours, aucune boucle : le dictionnaire utilise une fonction de hachage qui calcule directement l’emplacement de la clé.

  • Complexité : O(1)

Ce que démontre ce programme

Ce petit test met en lumière une vérité fondamentale en algorithmique :

Les tableaux sont parfaits pour stocker des listes ordonnées
Mais pas optimisés pour rechercher une valeur à partir d’une clé.

Les dictionnaires sont conçus pour la recherche rapide
Ils sont idéaux pour :

  • des annuaires
  • des index
  • des correspondances clé : valeur
  • des bases de données en mémoire

Le temps d’accès est constant, quelle que soit la taille de l’annuaire.

Pourquoi c’est important ?

Dans un programme réel, la différence entre O(n) et O(1) peut devenir énorme :

  • 5 000 entrées : quelques millisecondes
  • 5 millions : plusieurs secondes
  • 50 millions : des minutes

Les dictionnaires permettent de garder des performances stables, même avec des volumes massifs.

Programme :

@ Dictionnaire vs tableau : la recherche

// On cherche une valeur associée à une clé, de deux façons.

// --- MÉTHODE TABLEAU : parcours élément par élément (O(n)) ---
// On stocke les noms dans un tableau et les numéros dans un autre.
fonction chercheDansTableau(noms, numeros, cible)
    pour i de 0 à longueur(noms) - 1
        si noms[i] = cible alors
            retourne numeros[i]
        fin si
    fin pour
    retourne "introuvable"
fin fonction

// --- Remplissage des données de test ---
noms est un tableau
numeros est un tableau
pour k de 1 à 5000
    noms ajoute "Personne" + convertir_texte(k)
    numeros ajoute "06-00-00-" + convertir_texte(k)
fin pour

// On ajoute la cible tout à la fin : le PIRE cas pour le tableau
noms ajoute "Zoé"
numeros ajoute "06-99-99-99"

// --- Le même annuaire, version DICTIONNAIRE (accès par clé, O(1)) ---
annuaire est un dictionnaire
pour k de 0 à longueur(noms) - 1
    annuaire[noms[k]] vaut numeros[k]
fin pour

affiche "Recherche du numéro de Zoé..."
affiche ""

// --- Test 1 : le TABLEAU (doit tout parcourir) ---
debut est un nombre
debut vaut chrono()
resultatA est un texte
resultatA vaut chercheDansTableau(noms, numeros, "Zoé")
finA est un nombre
finA vaut chrono()

affiche "?? Tableau     -> " + resultatA
affiche "   Temps : " + convertir_texte(finA - debut) + " ms (a parcouru jusqu'à 5001 fiches)"
affiche ""

// --- Test 2 : le DICTIONNAIRE (accès direct par clé) ---
depart est un nombre
depart vaut chrono()
resultatB est un texte
resultatB vaut annuaire["Zoé"]
finB est un nombre
finB vaut chrono()

affiche "? Dictionnaire -> " + resultatB
affiche "   Temps : " + convertir_texte(finB - depart) + " ms (accès direct, aucun parcours)"
affiche ""
affiche "?? Même résultat, mais le dictionnaire va droit au but !"
affiche "   Tableau = O(n) | Dictionnaire = O(1)"
Pas encore noté
Créer un mini jeu d’escape-room : analyse complète d’un programme interactif
Article

Créer un mini jeu d’escape-room

Créer un mini jeu d’escape-room : analyse complète d’un programme interactif

Lire l'articleReplier l'article

Ce programme met en scène un petit jeu d’escape-room en 2D, entièrement géré dans une toile graphique. Le joueur explore une pièce, fouille des objets, trouve une clé, découvre un code secret et finit par déverrouiller une porte pour s’échapper.
Voyons comment ce jeu est construit, scène par scène, et comment les variables orchestrent la progression.

Les variables : le cœur de la logique du jeu

Dès le début, plusieurs variables essentielles sont déclarées :

sceneCourante : indique dans quelle scène se trouve le joueur (1 = pièce principale, 2 = tableau, etc.).

  • aCle : booléen qui indique si le joueur a trouvé la clé.
  • estGagne : booléen qui devient vrai lorsque le joueur s’échappe.
  • clicX / clicY : coordonnées du clic du joueur.
  • saisieCode : texte entré dans le digicode.

Ces variables permettent de suivre l’état du jeu et de réagir aux actions du joueur.

La boucle principale : le moteur du jeu
Le jeu fonctionne dans une boucle :

tant que non estGagne

À chaque tour :

  • La toile est effacée.
  • La scène correspondant à sceneCourante est dessinée.
  • Le programme attend un clic du joueur.
  • Il vérifie si ce clic correspond à un élément interactif.
  • Il met à jour l’état du jeu (changement de scène, clé trouvée, code entré…).

Cette boucle continue jusqu’à ce que le joueur atteigne la scène de victoire.

Scène 1 : La pièce principale

La première scène est une salle d’escape-room avec quatre éléments interactifs :

  • Le tableau : mène à la scène 2
  • La commode : mène à la scène 3
  • La fenêtre : mène à la scène 5
  • La porte : mène à la scène 4

Une barre d’inventaire affiche si le joueur possède la clé.
Cette scène sert de hub central : le joueur y revient souvent.

Scène 2 : Le tableau et l’indice

  • Le tableau contient un code secret gravé : 421.
  • Le joueur ne peut pas interagir davantage ici, mais cet indice sera crucial pour le digicode de la porte.

Un bouton « Retour » permet de revenir à la pièce principale.

Scène 3 : La commode et la clé

La commode contient un tiroir interactif :

  • Si le joueur n’a pas encore la clé : un bouton « Fouiller le tiroir » apparaît.
  • Après le clic : aCle devient vrai et un message confirme la découverte.
  • Si la clé est déjà prise : le tiroir affiche un état « fouillé ».

Encore une fois, un bouton « Retour » ramène à la scène 1.

Scène 4 : La porte et le digicode

La porte est verrouillée par deux systèmes :

  • Une serrure : nécessite la clé.
  • Un boîtier CODE : nécessite d’entrer le bon code.

Si le joueur clique sur le boîtier :

Sans clé : message d’erreur.
Avec clé : une demande de saisie apparaît.
Si le joueur entre 421 : passage à la scène 6 (victoire).
Sinon : « Code incorrect ! ».

Scène 5 : La fenêtre

La fenêtre permet simplement d’observer l’extérieur.
Elle ne contient aucun indice, ce qui ajoute un peu de fausse piste au jeu.

Scène 6 : La victoire

Si le joueur a :

  • trouvé la clé
  • découvert le code
  • déverrouillé la porte

Alors la scène finale s’affiche : un écran de victoire avec animations et messages de félicitations.

La variable estGagne passe à vrai, ce qui met fin à la boucle du jeu.

Conclusion : un escape-room simple mais complet

Ce programme illustre parfaitement la structure d’un mini-jeu :

  • Gestion d’état via des variables
  • Navigation entre scènes
  • Interactions par clic
  • Inventaire minimaliste
  • Indices et énigmes
  • Condition de victoire

Programme :

@ Déclaration des variables

jeuToile est une toile
dimension(jeuToile, 550, 500)

sceneCourante est un nombre
sceneCourante vaut 1

aCle est un booléen
aCle vaut faux

estGagne est un booléen
estGagne vaut faux

clicX, clicY est un nombre
saisieCode est un texte

@ Boucle principale du jeu

tant que non estGagne
    effacer(jeuToile)

    // SCÈNE 1 : Pièce principale
    si sceneCourante = 1 alors
        // Mur & parquet
        remplir(jeuToile, #2C3E50)
        rectangle(jeuToile, 0, 400, 550, 100, #3E2723)
        ligne(jeuToile, 0, 400, 550, 400, #1B0000, 3)

        label(jeuToile, 160, 40, "SCÈNE 1 : La Pièce", #ECF0F1, 22)
        label(jeuToile, 140, 65, "Trouvez le moyen de sortir !", #BDC3C7, 13)

        // Élément 1 : Tableau (vers scène 2)
        rectangle(jeuToile, 40, 110, 160, 110, #D35400)
        rectangle(jeuToile, 50, 120, 140, 90, #F39C12)
        cercle(jeuToile, 120, 165, 25, #E67E22)
        label(jeuToile, 85, 172, "Tableau", #FFFFFF, 16)

        // Élément 2 : Commode (vers scène 3)
        rectangle(jeuToile, 40, 260, 160, 130, #5D4037)
        rectangle_arrondi(jeuToile, 50, 275, 140, 40, 5, #8D6E63)
        rectangle_arrondi(jeuToile, 50, 335, 140, 40, 5, #8D6E63)
        cercle(jeuToile, 120, 295, 5, #F1C40F)
        cercle(jeuToile, 120, 355, 5, #F1C40F)
        label(jeuToile, 75, 245, "Commode", #FFFFFF, 15)

        // Élément 3 : Fenêtre (vers scène 5)
        rectangle(jeuToile, 350, 100, 160, 120, #ECF0F1)
        rectangle(jeuToile, 358, 108, 68, 48, #3498DB)
        rectangle(jeuToile, 434, 108, 68, 48, #3498DB)
        rectangle(jeuToile, 358, 164, 68, 48, #3498DB)
        rectangle(jeuToile, 434, 164, 68, 48, #3498DB)
        cercle(jeuToile, 460, 130, 12, #F4D03F)
        label(jeuToile, 395, 85, "Fenêtre", #FFFFFF, 15)

        // Élément 4 : Porte (vers scène 4)
        rectangle(jeuToile, 350, 240, 160, 160, #145A32)
        rectangle(jeuToile, 360, 250, 140, 140, #1E8449)
        cercle(jeuToile, 375, 320, 7, #F1C40F)
        rectangle(jeuToile, 470, 305, 20, 30, #F39C12)
        label(jeuToile, 405, 320, "Porte", #FFFFFF, 16)

        // Barre d'inventaire
        rectangle_arrondi(jeuToile, 30, 445, 490, 45, 8, #1A252F)
        si aCle alors
            cercle(jeuToile, 60, 467, 8, #F1C40F)
            ligne(jeuToile, 60, 467, 80, 467, #F1C40F, 3)
            ligne(jeuToile, 75, 467, 75, 473, #F1C40F, 3)
            label(jeuToile, 95, 472, "Inventaire : Clé en métal", #F1C40F, 14)
        sinon
            label(jeuToile, 60, 472, "Inventaire : Vide (Cliquez sur un élément)", #BDC3C7, 13)
        fin si

        // SCÈNE 2 : Le Tableau (Indice)
    sinon si sceneCourante = 2 alors
        remplir(jeuToile, #34495E)
        label(jeuToile, 160, 45, "SCÈNE 2 : Le Tableau", #FFFFFF, 20)

        // Cadre et toile
        rectangle(jeuToile, 60, 80, 430, 290, #B9770E)
        rectangle(jeuToile, 75, 95, 400, 260, #FEF9E7)

        // Décoration peinte sur la toile
        cercle(jeuToile, 275, 170, 50, #E74C3C)
        polygone(jeuToile, [200, 280, 350, 280, 275, 180], #27AE60)

        // Indice gravé en bas
        rectangle_arrondi(jeuToile, 130, 300, 290, 45, 5, #E5E7E9)
        label(jeuToile, 145, 330, "Code secret : 421", #C0392B, 22)

        // Bouton Retour
        rectangle_arrondi(jeuToile, 200, 415, 150, 45, 10, #7F8C8D)
        label(jeuToile, 245, 443, "Retour", #FFFFFF, 16)

        // SCÈNE 3 : La Commode (La clé)
    sinon si sceneCourante = 3 alors
        remplir(jeuToile, #34495E)
        label(jeuToile, 150, 45, "SCÈNE 3 : La Commode", #FFFFFF, 20)

        rectangle(jeuToile, 80, 90, 390, 290, #5D4037)
        rectangle_arrondi(jeuToile, 100, 110, 350, 110, 5, #795548)
        rectangle_arrondi(jeuToile, 100, 240, 350, 120, 5, #795548)

        si non aCle alors
            rectangle_arrondi(jeuToile, 150, 265, 250, 70, 8, #E67E22)
            cercle(jeuToile, 275, 300, 10, #F1C40F)
            label(jeuToile, 185, 306, "Fouiller le tiroir", #FFFFFF, 16)
        sinon
            rectangle_arrondi(jeuToile, 150, 265, 250, 70, 8, #27AE60)
            label(jeuToile, 170, 306, "Tiroir fouillé (Clé prise)", #FFFFFF, 15)
        fin si

        // Bouton Retour
        rectangle_arrondi(jeuToile, 200, 415, 150, 45, 10, #7F8C8D)
        label(jeuToile, 245, 443, "Retour", #FFFFFF, 16)

        // SCÈNE 4 : La Porte (Verrou & Digicode)
    sinon si sceneCourante = 4 alors
        remplir(jeuToile, #2C3E50)
        label(jeuToile, 180, 45, "SCÈNE 4 : La Porte", #FFFFFF, 20)

        rectangle(jeuToile, 150, 80, 250, 320, #145A32)
        rectangle(jeuToile, 165, 95, 220, 290, #1E8449)

        // Serrures et Digicode
        cercle(jeuToile, 190, 240, 10, #F1C40F)
        rectangle_arrondi(jeuToile, 310, 200, 55, 75, 5, #34495E)
        rectangle(jeuToile, 318, 208, 39, 20, #2C3E50)
        label(jeuToile, 325, 222, "###", #2ECC71, 10)
        cercle(jeuToile, 337, 250, 12, #E74C3C)
        label(jeuToile, 323, 254, "CODE", #FFFFFF, 9)

        si non aCle alors
            rectangle_arrondi(jeuToile, 70, 410, 410, 35, 5, #C0392B)
            label(jeuToile, 100, 432, "La porte est verrouillée à clé !", #FFFFFF, 14)
        sinon
            rectangle_arrondi(jeuToile, 60, 410, 430, 35, 5, #27AE60)
            label(jeuToile, 85, 432, "Clé insérée ! Cliquez sur le boîtier 'CODE'.", #FFFFFF, 13)
        fin si

        // Bouton Retour
        rectangle_arrondi(jeuToile, 200, 450, 150, 40, 10, #7F8C8D)
        label(jeuToile, 245, 476, "Retour", #FFFFFF, 15)

        // SCÈNE 5 : La Fenêtre (Observation)
    sinon si sceneCourante = 5 alors
        remplir(jeuToile, #1B2631)
        label(jeuToile, 160, 45, "SCÈNE 5 : La Fenêtre", #FFFFFF, 20)

        // Cadre et ciel nocturne
        rectangle(jeuToile, 100, 80, 350, 290, #ECF0F1)
        rectangle(jeuToile, 115, 95, 320, 260, #0B132B)

        // Lune et étoiles
        cercle(jeuToile, 360, 160, 40, #F4D03F)
        cercle(jeuToile, 345, 160, 35, #0B132B)
        cercle(jeuToile, 160, 140, 2, #FFFFFF)
        cercle(jeuToile, 220, 180, 3, #FFFFFF)
        cercle(jeuToile, 280, 120, 2, #FFFFFF)

        label(jeuToile, 130, 395, "La nuit est claire, mais aucun passage ici.", #ECF0F1, 13)

        // Bouton Retour
        rectangle_arrondi(jeuToile, 200, 420, 150, 45, 10, #7F8C8D)
        label(jeuToile, 245, 448, "Retour", #FFFFFF, 16)

        // SCÈNE 6 : La Sortie (Victoire)
    sinon si sceneCourante = 6 alors
        remplir(jeuToile, #1E8449)

        // Rayons de victoire
        polygone(jeuToile, [275, 250, 0, 0, 150, 0], #27AE60)
        polygone(jeuToile, [275, 250, 400, 0, 550, 0], #27AE60)
        polygone(jeuToile, [275, 250, 550, 200, 550, 350], #27AE60)
        polygone(jeuToile, [275, 250, 0, 200, 0, 350], #27AE60)

        rectangle_arrondi(jeuToile, 75, 120, 400, 220, 15, #145A32)
        label(jeuToile, 145, 180, "VICTOIRE !", #F1C40F, 32)
        label(jeuToile, 115, 230, "Vous avez déverrouillé la porte", #FFFFFF, 16)
        label(jeuToile, 160, 260, "et réussi à vous échapper !", #FFFFFF, 16)

        estGagne vaut vrai
    fin si

    affiche jeuToile

    // Gestion des clics
    si non estGagne alors
        cliquer jeuToile dans clicX, clicY

        // Interaction Scène 1
        si sceneCourante = 1 alors
            si clique(clicX, clicY, 40, 110, 160, 110) alors
                sceneCourante vaut 2
            sinon si clique(clicX, clicY, 40, 260, 160, 130) alors
                sceneCourante vaut 3
            sinon si clique(clicX, clicY, 350, 100, 160, 120) alors
                sceneCourante vaut 5
            sinon si clique(clicX, clicY, 350, 240, 160, 160) alors
                sceneCourante vaut 4
            fin si

            // Interaction Scène 2
        sinon si sceneCourante = 2 alors
            si clique(clicX, clicY, 200, 415, 150, 45) alors
                sceneCourante vaut 1
            fin si

            // Interaction Scène 3
        sinon si sceneCourante = 3 alors
            si non aCle et clique(clicX, clicY, 150, 265, 250, 70) alors
                aCle vaut vrai
                affiche "Vous avez trouvé la clé en métal !"
            sinon si clique(clicX, clicY, 200, 415, 150, 45) alors
                sceneCourante vaut 1
            fin si

            // Interaction Scène 4
        sinon si sceneCourante = 4 alors
            si clique(clicX, clicY, 310, 200, 55, 75) alors
                si non aCle alors
                    affiche "La porte est verrouillée ! Il vous faut la clé."
                sinon
                    demande "Entrez le code secret : " dans saisieCode
                    si saisieCode = "421" alors
                        sceneCourante vaut 6
                    sinon
                        affiche "Code incorrect !"
                    fin si
                fin si
            sinon si clique(clicX, clicY, 200, 450, 150, 40) alors
                sceneCourante vaut 1
            fin si

            // Interaction Scène 5
        sinon si sceneCourante = 5 alors
            si clique(clicX, clicY, 200, 420, 150, 45) alors
                sceneCourante vaut 1
            fin si
        fin si
    fin si
fin tant que
Pas encore noté
Construire un moteur 3D minimaliste : quand l’algorithmique rencontre le rendu en temps réel
Article

Construire un moteur 3D minimaliste

Construire un moteur 3D minimaliste : quand l’algorithmique rencontre le rendu en temps réel

Lire l'articleReplier l'article

Développer un moteur 3D en raycasting à partir de zéro est un exercice fascinant : il combine algorithmique, mathématiques, génération procédurale et rendu graphique.
Ce projet illustre parfaitement comment des concepts simples, bien maîtrisés, peuvent produire une expérience immersive et dynamique.

Génération procédurale : un labyrinthe créé par DFS

La carte 24×24 est générée via un Depth-First Search (DFS), garantissant un labyrinthe unique et entièrement parcourable.

Points clés :

  • Grille initiale remplie de murs.
  • Exploration en profondeur avec backtracking.
  • Sélection aléatoire des voisins pour créer des chemins variés.
  • Cassage des murs intermédiaires pour former des couloirs cohérents.

Un algorithme simple, efficace, et idéal pour comprendre les bases de la génération procédurale.

Déplacements fluides et gestion des collisions

Le joueur est positionné au centre de la première cellule ouverte.
Les déplacements reposent sur un vecteur directionnel calculé à partir de l’angle de vue, avec une fonction dédiée pour éviter toute traversée de mur.

Contrôles :

  • Z / S : avancer / reculer
  • Q / D : pivoter à gauche / droite

La fonction tenteDeplacement() vérifie la cellule cible avant de valider le mouvement, assurant une navigation réaliste.

Raycasting : un rendu 3D inspiré des moteurs historiques

Le moteur utilise 150 rayons couvrant un champ de vision de 60°.

Chaque rayon calcule :

  • direction,
  • distance à l’impact,
  • hauteur du mur,
  • correction de perspective,
  • ombrage selon la distance et le côté touché.

Le rendu inclut :

  • un dégradé de ciel, un sol lissé, des murs ombrés, et une profondeur visuelle convaincante.

Mini-carte dynamique : comprendre l’espace en un coup d’œil

La mini-carte 24×24 affiche :

  • les murs en bleu,
  • les couloirs en gris foncé,
  • la position du joueur,
  • son orientation via un vecteur de direction.

Un outil essentiel pour visualiser la structure du labyrinthe généré.

Un projet complet et formateur

Ce moteur 3D réunit :

  • génération procédurale,
  • mathématiques appliquées,
  • optimisation en temps réel,
  • rendu graphique,
  • gestion d’interactions utilisateur.

Une excellente base pour comprendre les fondations des moteurs 3D modernes, tout en restant accessible et pédagogique.

Programme :

@ 0. CONSTANTES
constante TAILLE_CARTE vaut 24 // Carte 24x24
constante LARGEUR_ECRAN vaut 600
constante HAUTEUR_ECRAN vaut 400
constante NB_RAYONS vaut 150
constante EPAISSEUR_RAYON vaut 4

@ 1. CARTE DU MONDE (GÉNÉRATION ALÉATOIRE AVEC DFS)
carte est un tableau

// 1.1 Remplir toute la carte de murs (1)
pour i de 0 à (TAILLE_CARTE * TAILLE_CARTE) - 1
    carte ajoute 1
fin pour

// 1.2 Initialisation de l'algorithme DFS
pile est un tableau
startX est un nombre
startX vaut 1
startY est un nombre
startY vaut 1

// Marquer la position de départ comme chemin (0)
idxDepart est un nombre
idxDepart vaut (startY * TAILLE_CARTE) + startX
carte[idxDepart] vaut 0
pile ajoute idxDepart

// 1.3 Boucle principale du parcours en profondeur
tant que longueur(pile) > 0
    courantIdx est un nombre
    courantIdx vaut dernier(pile)
    cx est un nombre
    cx vaut courantIdx mod TAILLE_CARTE
    cy est un nombre
    cy vaut arrondi_inferieur(courantIdx / TAILLE_CARTE)

    voisins est un tableau

    // Vérifier le voisin du HAUT (y - 2)
    si cy - 2 > 0 alors
        idxVaut est un nombre
        idxVaut vaut ((cy - 2) * TAILLE_CARTE) + cx
        si carte[idxVaut] = 1 alors
            voisins ajoute 0
        fin si
    fin si

    // Vérifier le voisin du BAS (y + 2)
    si cy + 2 < TAILLE_CARTE - 1 alors
        idxVaut est un nombre
        idxVaut vaut ((cy + 2) * TAILLE_CARTE) + cx
        si carte[idxVaut] = 1 alors
            voisins ajoute 1
        fin si
    fin si

    // Vérifier le voisin de GAUCHE (x - 2)
    si cx - 2 > 0 alors
        idxVaut est un nombre
        idxVaut vaut (cy * TAILLE_CARTE) + (cx - 2)
        si carte[idxVaut] = 1 alors
            voisins ajoute 2
        fin si
    fin si

    // Vérifier le voisin de DROITE (x + 2)
    si cx + 2 < TAILLE_CARTE - 1 alors
        idxVaut est un nombre
        idxVaut vaut (cy * TAILLE_CARTE) + (cx + 2)
        si carte[idxVaut] = 1 alors
            voisins ajoute 3
        fin si
    fin si

    // Si on a des voisins non visités
    si longueur(voisins) > 0 alors
        rIdx est un nombre
        rIdx vaut hasard(0, longueur(voisins) - 1)
        dir est un nombre
        dir vaut voisins[rIdx]

        nx est un nombre
        nx vaut cx
        ny est un nombre
        ny vaut cy
        mx est un nombre // Position du mur à casser
        mx vaut cx
        my est un nombre
        my vaut cy

        si dir = 0 alors
            ny vaut cy - 2
            my vaut cy - 1
        sinon si dir = 1 alors
            ny vaut cy + 2
            my vaut cy + 1
        sinon si dir = 2 alors
            nx vaut cx - 2
            mx vaut cx - 1
        sinon si dir = 3 alors
            nx vaut cx + 2
            mx vaut cx + 1
        fin si

        // Casser le mur entre les deux cases
        idxMur est un nombre
        idxMur vaut (my * TAILLE_CARTE) + mx
        carte[idxMur] vaut 0

        // Marquer la nouvelle case comme chemin
        idxNouveau est un nombre
        idxNouveau vaut (ny * TAILLE_CARTE) + nx
        carte[idxNouveau] vaut 0

        // Empiler la nouvelle position
        pile ajoute idxNouveau
    sinon
        // Cul-de-sac : on dépile (backtracking)
        pile supprime longueur(pile) - 1
    fin si
fin tant que

@ 2. POSITION ET DIRECTION DU JOUEUR
px, py est un nombre
px vaut 1,5 // Départ centré dans la première case générée par le DFS
py vaut 1,5

angleDeg est un nombre
angleDeg vaut 0

enCours est un booléen
enCours vaut vrai

action est un nombre

maToile est une toile
dimension(maToile, LARGEUR_ECRAN, HAUTEUR_ECRAN)

tant que enCours
    effacer(maToile)

    @ --- CIEL : Dégradé lissé ---
    pour bandeC de 0 à 39
        tC est un nombre
        tC vaut bandeC / 39
        rC est un nombre
        rC vaut arrondi(5 + tC * 20)
        gC est un nombre
        gC vaut arrondi(15 + tC * 50)
        bC est un nombre
        bC vaut arrondi(35 + tC * 80)
        rectangle(maToile, 0, bandeC * 5, LARGEUR_ECRAN, 6, rgb(rC, gC, bC))
    fin pour

    @ --- SOL : Dégradé lissé ---
    pour bandeS de 0 à 39
        tS est un nombre
        tS vaut bandeS / 39
        rS est un nombre
        rS vaut arrondi(10 + tS * 40)
        gS est un nombre
        gS vaut arrondi(15 + tS * 40)
        bS est un nombre
        bS vaut arrondi(20 + tS * 45)
        rectangle(maToile, 0, 200 + bandeS * 5, LARGEUR_ECRAN, 6, rgb(rS, gS, bS))
    fin pour

    @ 3. BOUCLE de RENDU 3D (RAYCASTING)
    pour slice de 0 à NB_RAYONS - 1
        offsetAngle est un nombre
        offsetAngle vaut - 30 + (slice * (60 / (NB_RAYONS - 1)))

        rayAngle est un nombre
        rayAngle vaut angleDeg + offsetAngle
        radRay est un nombre
        radRay vaut radians(rayAngle)

        rDirX est un nombre
        rDirX vaut cosinus(radRay)
        rDirY est un nombre
        rDirY vaut sinus(radRay)

        si absolue(rDirX) < 0.0001 alors
            rDirX vaut 0.0001
        fin si
        si absolue(rDirY) < 0.0001 alors
            rDirY vaut 0.0001
        fin si

        mX est un nombre
        mX vaut arrondi_inferieur(px)
        mY est un nombre
        mY vaut arrondi_inferieur(py)

        deltaX est un nombre
        deltaX vaut absolue(1 / rDirX)
        deltaY est un nombre
        deltaY vaut absolue(1 / rDirY)

        sX, sY, sideX, sideY est un nombre

        si rDirX < 0 alors
            sX vaut - 1
            sideX vaut (px - mX) * deltaX
        sinon
            sX vaut 1
            sideX vaut (mX + 1 - px) * deltaX
        fin si

        si rDirY < 0 alors
            sY vaut - 1
            sideY vaut (py - mY) * deltaY
        sinon
            sY vaut 1
            sideY vaut (mY + 1 - py) * deltaY
        fin si

        toucher est un booléen
        toucher vaut faux
        cote est un nombre
        cote vaut 0

        tant que non toucher
            si sideX < sideY alors
                sideX vaut sideX + deltaX
                mX vaut mX + sX
                cote vaut 0
            sinon
                sideY vaut sideY + deltaY
                mY vaut mY + sY
                cote vaut 1
            fin si

            si (mX < 0) ou (mX > TAILLE_CARTE - 1) ou (mY < 0) ou (mY > TAILLE_CARTE - 1) alors
                toucher vaut vrai
            sinon
                idxC est un nombre
                idxC vaut (mY * TAILLE_CARTE) + mX
                si carte[idxC] > 0 alors
                    toucher vaut vrai
                fin si
            fin si
        fin tant que

        distBrute est un nombre
        si cote = 0 alors
            distBrute vaut sideX - deltaX
        sinon
            distBrute vaut sideY - deltaY
        fin si

        distCorr est un nombre
        distCorr vaut distBrute * cosinus(radians(offsetAngle))
        si distCorr < 0.1 alors
            distCorr vaut 0.1
        fin si

        hautMur est un nombre
        hautMur vaut 300 / distCorr
        hautMur vaut limiter(hautMur, 5, 450)

        topY est un nombre
        topY vaut 200 - (hautMur / 2)
        posX est un nombre
        posX vaut slice * EPAISSEUR_RAYON

        intensite est un nombre
        intensite vaut 1 - (distCorr / 18)
        intensite vaut limiter(intensite, 0.05, 1.0)

        si cote = 1 alors
            intensite vaut intensite * 0.6
        fin si

        rBase, vBase, bBase est un nombre
        si (mX + mY) mod 2 = 0 alors
            rBase vaut 200
            vBase vaut 80
            bBase vaut 40
        sinon
            rBase vaut 70
            vBase vaut 110
            bBase vaut 150
        fin si

        rMur est un nombre
        rMur vaut arrondi(rBase * intensite)
        vMur est un nombre
        vMur vaut arrondi(vBase * intensite)
        bMur est un nombre
        bMur vaut arrondi(bBase * intensite)

        couleurMur est un texte
        couleurMur vaut rgb(rMur, vMur, bMur)

        rectangle(maToile, posX, topY, EPAISSEUR_RAYON, hautMur, couleurMur)

        lisere est un texte
        lisere vaut rgb(arrondi(rMur * 0.3), arrondi(vMur * 0.3), arrondi(bMur * 0.3))
        rectangle(maToile, posX, topY, EPAISSEUR_RAYON, 3, lisere)
        rectangle(maToile, posX, topY + hautMur - 3, EPAISSEUR_RAYON, 3, lisere)
    fin pour

    @ 4. MINI-CARTE OPTIMISÉE (RADAR 24x24)
    rectangle(maToile, 8, 8, 126, 126, rgba(0, 0, 0, 0.75))

    pour my de 0 à TAILLE_CARTE - 1
        pour mx de 0 à TAILLE_CARTE - 1
            idxM est un nombre
            idxM vaut (my * TAILLE_CARTE) + mx
            si carte[idxM] = 1 alors
                rectangle(maToile, 11 + (mx * 5), 11 + (my * 5), 4, 4, #3B82F6)
            sinon
                rectangle(maToile, 11 + (mx * 5), 11 + (my * 5), 4, 4, #1B2437)
            fin si
        fin pour
    fin pour

    radVue est un nombre
    radVue vaut radians(angleDeg)
    joueurX est un nombre
    joueurX vaut 11 + (px * 5)
    joueurY est un nombre
    joueurY vaut 11 + (py * 5)

    ligne(maToile, joueurX, joueurY, joueurX + cosinus(radVue) * 8, joueurY + sinus(radVue) * 8, #FBBF24, 2)
    cercle(maToile, joueurX, joueurY, 2, #EF4444)

    rectangle(maToile, 142, 12, 290, 24, rgba(0, 0, 0, 0.6))
    label(maToile, 150, 29, "Z / S : Déplacer   |   Q / D : Orienter", #FFFFFF, 13)

    affiche maToile

    @ 5. GESTION DES COMMANDES
    appuyer ["x", "z", "s", "q", "d"] dans action

    radMove est un nombre
    radMove vaut radians(angleDeg)
    dirX est un nombre
    dirX vaut cosinus(radMove)
    dirY est un nombre
    dirY vaut sinus(radMove)

    si action = 2 alors
        appelle tenteDeplacement(1)
    sinon si action = 3 alors
        appelle tenteDeplacement(-1)
    sinon si action = 4 alors
        angleDeg soustrait 90
    sinon si action = 5 alors
        angleDeg ajoute 90
    fin si

fin tant que

@ 6. FONCTION DE DEPLACEMENT
fonction tenteDeplacement(sens)
    nextX est un nombre
    nextX vaut px + (dirX * 0.8 * sens)
    nextY est un nombre
    nextY vaut py + (dirY * 0.8 * sens)

    cX est un nombre
    cX vaut arrondi_inferieur(nextX)
    cY est un nombre
    cY vaut arrondi_inferieur(nextY)

    si (cX < 0) ou (cX > TAILLE_CARTE - 1) ou (cY < 0) ou (cY > TAILLE_CARTE - 1) alors
        retourne faux
    fin si

    idxCheck est un nombre
    idxCheck vaut (cY * TAILLE_CARTE) + cX
    si carte[idxCheck] = 0 alors
        px vaut cX + 0.5
        py vaut cY + 0.5
    fin si
    retourne vrai
fin fonction
Pas encore noté