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

Magic Liquid Sort : concevoir un jeu de logique fluide avec animations, UX et algorithmes avancés
Article

Magic Liquid Sort

Concevoir un jeu de logique fluide avec animations, UX et algorithmes avancés

Lire l'articleReplier l'article

Développer un jeu de tri de liquides peut sembler simple… jusqu’à ce qu’on décide de le faire proprement, avec une interface claire, une logique interne robuste, une grille toujours solvable et une expérience utilisateur fluide.

Ce projet Magic Liquid Sort démontre comment une architecture bien pensée permet de créer un jeu stratégique, agréable et parfaitement fonctionnel — même sans animation.

Une interface graphique construite au pixel près

Le programme définit chaque élément visuel avec précision :

hautTube vaut hautCol + capacite * hautDose + 8
rectangle_arrondi(ecran, sX, sY, largTube, hautTube, 25, #e8f1ff)

Les tubes, les doses, les reflets, les couleurs… tout est dessiné manuellement.
La palette est soigneusement pensée :

  • teintes : couleurs principales des liquides,
  • teintesClaires : nuances pour les reflets,
  • libelles : noms des couleurs pour les messages.

Le résultat : une interface propre, moderne, lisible, qui fonctionne sans animation tout en restant agréable à l’œil.

Une logique de jeu solide et entièrement contrôlée

Le cœur du jeu repose sur deux structures essentielles :

  • contenu : tableau plat représentant chaque dose dans chaque tube,
  • niveaux : nombre de doses présentes dans chaque tube.

Le programme garantit que chaque grille générée est résolvable, grâce à une technique intelligente :

  • Mélange par COUPS INVERSES : la grille obtenue est donc toujours resolvable.

Autrement dit : le mélange est effectué uniquement via des mouvements qui pourraient être annulés.

Une gestion des interactions simple, intuitive et efficace Le système gère :

  • la sélection d’un tube,
  • la validation des règles de versement,
  • les incompatibilités de couleurs,
  • les tubes pleins ou vides,
  • l’annulation des coups via un historique,
  • la création d’une nouvelle grille.

Chaque action est accompagnée d’un message clair :

  • Ce tube est vide : rien à prendre.
  • Le tube est plein.
  • Coup annulé.

Même sans animation, l’expérience utilisateur reste fluide, compréhensible et agréable.

Un tri logique fidèle aux règles du genre

Le programme applique strictement les règles du tri :

  • On ne verse que sur une couleur identique ou dans un tube vide.
  • On ne dépasse jamais la capacité.
  • On verse plusieurs doses d’un coup si elles sont consécutives et identiques.
  • On ne casse jamais une séquence homogène.

Le code le montre clairement :

  • si contenu[cible * capacite + nDst - 1] = doseHaut alors okVerser vaut vrai

Une logique propre, lisible, efficace.

Détection automatique de la victoire

La fonction de vérification est simple et rigoureuse :

  • si contenu[...] <> gRef alors retourne faux

Le jeu est gagné uniquement si :

  • chaque tube est rempli,
  • chaque tube contient une seule couleur,
  • aucune dose n’est mal placée.

Une bannière de victoire apparaît alors, accompagnée du nombre de coups réalisés.

Ce que ce projet met en valeur

  • Conception d’une interface graphique complète sans animation
  • Gestion avancée de l’état du jeu
  • Génération de grilles toujours solvables
  • Système d’annulation avec historique
  • UX claire, messages guidants, boutons interactifs
  • Architecture modulaire et extensible
  • Logique de tri fidèle aux jeux du genre

Programme :

// ============================================================
//  1. REGLAGES DU JEU
// ============================================================
nbTubes est un nombre
nbTubes vaut 8
nbCol est un nombre
nbCol vaut 4
capacite est un nombre
capacite vaut 4
nbCouleurs est un nombre
nbCouleurs vaut 6

// ---- Geometrie ----
largTube est un nombre
largTube vaut 62
hautDose est un nombre
hautDose vaut 32
hautCol est un nombre
hautCol vaut 16
hautTube est un nombre
hautTube vaut hautCol + capacite * hautDose + 8

espaceX est un nombre
espaceX vaut 26
espaceY est un nombre
espaceY vaut 48
margeH est un nombre
margeH vaut 26
hautEntete est un nombre
hautEntete vaut 62
margeB est un nombre
margeB vaut 112

largToile est un nombre
largToile vaut margeH * 2 + nbCol * largTube + (nbCol - 1) * espaceX
hautToile est un nombre
hautToile vaut hautEntete + 2 * hautTube + espaceY + margeB

// ---- Boutons ----
btnL est un nombre
btnL vaut 148
btnH est un nombre
btnH vaut 42
btnY est un nombre
btnY vaut hautToile - 58
btnX1 est un nombre
btnX1 vaut margeH
btnX2 est un nombre
btnX2 vaut largToile - margeH - btnL

// ============================================================
//  2. PALETTE (indice 0 = vide)
// ============================================================
teintes est un tableau
teintes ajoute #14243c
teintes ajoute #ff3d8b
teintes ajoute #a855f7
teintes ajoute #3b82f6
teintes ajoute #22d3ee
teintes ajoute #4ade80
teintes ajoute #fbbf24

teintesClaires est un tableau
teintesClaires ajoute #14243c
teintesClaires ajoute #ff85b5
teintesClaires ajoute #c99bff
teintesClaires ajoute #93bcff
teintesClaires ajoute #8beff9
teintesClaires ajoute #a7f3c4
teintesClaires ajoute #ffd96b

libelles est un tableau
libelles ajoute "vide"
libelles ajoute "rose"
libelles ajoute "violet"
libelles ajoute "bleu"
libelles ajoute "cyan"
libelles ajoute "vert"
libelles ajoute "jaune"

// ============================================================
//  3. ETAT DU JEU
//  contenu  : tableau PLAT de nbTubes * capacite cases
//             case (tube * capacite + dose) = numero de couleur (0 = vide)
//  niveaux  : nombre de doses presentes dans chaque tube
// ============================================================
contenu est un tableau
niveaux est un tableau

jeu est une toile

selection est un nombre
selection vaut - 1
coups est un nombre
coups vaut 0
gagne est un booleen
gagne vaut faux
message est un texte
message vaut "Clique sur un tube pour commencer."

histoSrc est un tableau
histoDst est un tableau
histoNb est un tableau

// ============================================================
//  4. FONCTIONS DE POSITION
// ============================================================
fonction posX(idxX)
    pCol est un nombre
    pCol vaut idxX mod nbCol
    retourne margeH + pCol * (largTube + espaceX)
fin fonction

fonction posY(idxY)
    pRang est un nombre
    pRang vaut arrondi_inferieur(idxY / nbCol)
    retourne hautEntete + pRang * (hautTube + espaceY)
fin fonction

// ============================================================
//  5. LE JEU EST-IL GAGNE ?
// ============================================================
fonction estGagne()
    gk est un nombre
    gd est un nombre
    gn est un nombre
    gRef est un nombre

    pour gk de 0 à nbTubes - 1
        gn vaut niveaux[gk]
        si gn > 0 alors
            si gn <> capacite alors
                retourne faux
            fin si
            gRef vaut contenu[gk * capacite]
            pour gd de 1 à gn - 1
                si contenu[gk * capacite + gd] <> gRef alors
                    retourne faux
                fin si
            fin pour
        fin si
    fin pour

    retourne vrai
fin fonction

// ============================================================
//  6. DESSIN COMPLET SUR UNE TOILE
//     tubeSel : tube selectionne (-1 si aucun)
// ============================================================
procédure dessinerSurToile(ecran, tubeSel, avecVictoire)
    dimension(ecran, largToile, hautToile)
    effacer(ecran)
    dégradé(ecran, [#080f1d, #17243f, #080f1d])

    sTxt est un texte
    sIdx est un nombre
    sD est un nombre
    sX est un nombre
    sY est un nombre
    sBase est un nombre
    sNb est un nombre
    sCoul est un nombre
    sYd est un nombre
    sVisible est un booleen

    // ---- Entete ----
    label(ecran, margeH, 20, "MAGIC LIQUID SORT", #eaf2ff, 15)
    sTxt vaut "Coups : " + convertir_texte(coups)
    label(ecran, largToile - margeH - label_largeur(sTxt, 14), 20, sTxt, #8fb3e0, 14)

    // ---- Les tubes ----
    pour sIdx de 0 à nbTubes - 1
        sX vaut posX(sIdx)
        sY vaut posY(sIdx)
        sBase vaut sY + hautTube - 5
        sNb vaut niveaux[sIdx]

        // Coque exterieure (blanche, ou doree si le tube est selectionne)
        si sIdx = tubeSel alors
            rectangle_arrondi(ecran, sX, sY, largTube, hautTube, 25, #ffd166)
        sinon
            rectangle_arrondi(ecran, sX, sY, largTube, hautTube, 25, #e8f1ff)
        fin si

        // Interieur du verre
        rectangle_arrondi(ecran, sX + 3, sY + 3, largTube - 6, hautTube - 6, 22, #14243c)

        // Les doses de liquide (de bas en haut)
        pour sD de 0 à sNb - 1
            sCoul vaut contenu[sIdx * capacite + sD]
            sYd vaut sBase - (sD + 1) * hautDose

            // la dose du haut du tube selectionne est dessinee plus loin (elle "flotte")
            sVisible vaut vrai
            si sIdx = tubeSel et sD = sNb - 1 alors
                sVisible vaut faux
            fin si

            si sVisible alors
                si sD = 0 alors
                    // fond arrondi comme un vrai tube a essai
                    rectangle_arrondi(ecran, sX + 5, sYd, largTube - 10, hautDose, 16, teintes[sCoul])
                    rectangle(ecran, sX + 5, sYd, largTube - 10, hautDose - 16, teintes[sCoul])
                sinon
                    rectangle(ecran, sX + 5, sYd, largTube - 10, hautDose, teintes[sCoul])
                fin si

                // surface plus claire sur la dose du dessus
                si sD = sNb - 1 alors
                    rectangle(ecran, sX + 5, sYd, largTube - 10, 4, teintesClaires[sCoul])
                fin si
            fin si
        fin pour

        // Reflet du verre
        rectangle_arrondi(ecran, sX + 12, sY + 30, 5, 46, 3, #cfe0f5)

        // Col du tube
        rectangle_arrondi(ecran, sX + 10, sY + 9, largTube - 20, 7, 3, #0b1526)

        // Dose qui flotte au-dessus du tube selectionne
        si sIdx = tubeSel et sNb > 0 alors
            sCoul vaut contenu[sIdx * capacite + sNb - 1]
            rectangle_arrondi(ecran, sX + 5, sY - 28, largTube - 10, hautDose, 12, teintes[sCoul])
            rectangle(ecran, sX + 5, sY - 24, largTube - 10, 4, teintesClaires[sCoul])
        fin si

        // Numero du tube
        sTxt vaut convertir_texte(sIdx + 1)
        label(ecran, sX + largTube / 2 - label_largeur(sTxt, 14) / 2, sY + hautTube + 20, sTxt, #6f8cb8, 14)
    fin pour

    // ---- Message ----
    label(ecran, margeH, btnY - 14, message, #9fb8dd, 13)

    // ---- Boutons ----
    rectangle_arrondi(ecran, btnX1, btnY, btnL, btnH, 12, #1d3557)
    sTxt vaut "Annuler"
    label(ecran, btnX1 + btnL / 2 - label_largeur(sTxt, 15) / 2, btnY + 27, sTxt, #cfe3ff, 15)

    rectangle_arrondi(ecran, btnX2, btnY, btnL, btnH, 12, #1d3557)
    sTxt vaut "Nouveau"
    label(ecran, btnX2 + btnL / 2 - label_largeur(sTxt, 15) / 2, btnY + 27, sTxt, #cfe3ff, 15)

    // ---- Banniere de victoire ----
    si avecVictoire alors
        rectangle_arrondi(ecran, margeH, hautToile / 2 - 46, largToile - margeH * 2, 92, 18, #0e2c47)
        sTxt vaut "GAGNE !"
        label(ecran, largToile / 2 - label_largeur(sTxt, 26) / 2, hautToile / 2 - 6, sTxt, #ffd166, 26)
        sTxt vaut "Tubes tries en " + convertir_texte(coups) + " coups"
        label(ecran, largToile / 2 - label_largeur(sTxt, 14) / 2, hautToile / 2 + 24, sTxt, #cfe3ff, 14)
    fin si
fin procédure

// ============================================================
//  7. AFFICHAGE DE L'ETAT COURANT
// ============================================================
procédure dessiner(selVue, gagneVue)
    appelle dessinerSurToile(jeu, selVue, gagneVue)
    affiche jeu
fin procédure

// ============================================================
//  8. VARIABLES DE LA BOUCLE PRINCIPALE
// ============================================================
partieEnCours est un booleen
nouvelleDemande est un booleen
rejouer est un nombre

idx est un nombre
dd est un nombre
essai est un nombre
tD est un nombre
tS est un nombre
nD est un nombre
couleurBouge est un nombre
okBouge est un booleen

clicX est un nombre
clicY est un nombre
cible est un nombre

nSrc est un nombre
nDst est un nombre
doseHaut est un nombre
okVerser est un booleen
nbVerse est un nombre
placeLibre est un nombre
poursuivre est un booleen

dernierIdx est un nombre
uSrc est un nombre
uDst est un nombre
uNb est un nombre
coulRetour est un nombre

// ============================================================
//  9. PARTIE
// ============================================================
partieEnCours vaut vrai

tant que partieEnCours

    // ---------- Distribution : tubes tries ----------
    vide contenu
    vide niveaux
    vide histoSrc
    vide histoDst
    vide histoNb

    pour idx de 0 à nbTubes - 1
        niveaux ajoute 0
        pour dd de 0 à capacite - 1
            contenu ajoute 0
        fin pour
    fin pour

    pour idx de 0 à nbCouleurs - 1
        niveaux[idx] vaut capacite
        pour dd de 0 à capacite - 1
            contenu[idx * capacite + dd] vaut idx + 1
        fin pour
    fin pour

    // ---------- Melange par COUPS INVERSES ----------
    // (on ne fait que des coups qui peuvent etre annules :
    //  la grille obtenue est donc toujours resolvable)
    tant que estGagne()
        pour essai de 1 à 500
            tD vaut hasard(0, nbTubes - 1)
            tS vaut hasard(0, nbTubes - 1)
            si tD <> tS alors
                nD vaut niveaux[tD]
                si nD > 0 alors
                    si niveaux[tS] < capacite alors
                        couleurBouge vaut contenu[tD * capacite + nD - 1]
                        okBouge vaut faux
                        si nD = 1 alors
                            okBouge vaut vrai
                        sinon si contenu[tD * capacite + nD - 2] = couleurBouge alors
                            okBouge vaut vrai
                        fin si
                        si okBouge alors
                            contenu[tD * capacite + nD - 1] vaut 0
                            niveaux[tD] vaut nD - 1
                            contenu[tS * capacite + niveaux[tS]] vaut couleurBouge
                            niveaux[tS] vaut niveaux[tS] + 1
                        fin si
                    fin si
                fin si
            fin si
        fin pour
    fin tant que

    coups vaut 0
    selection vaut - 1
    gagne vaut faux
    nouvelleDemande vaut faux
    message vaut "Clique sur un tube pour commencer."

    // ---------- Boucle de jeu ----------
    tant que non gagne et non nouvelleDemande

        appelle dessiner(selection, faux)
        cliquer jeu dans clicX, clicY

        // Sur quel tube a-t-on clique ?
        cible vaut - 1
        pour idx de 0 à nbTubes - 1
            si clique(clicX, clicY, posX(idx) - 4, posY(idx) - 28, largTube + 8, hautTube + 36) alors
                cible vaut idx
            fin si
        fin pour

        si clique(clicX, clicY, btnX1, btnY, btnL, btnH) alors
            // ---- BOUTON ANNULER ----
            si longueur(histoSrc) = 0 alors
                message vaut "Rien a annuler."
            sinon
                dernierIdx vaut longueur(histoSrc) - 1
                uSrc vaut histoSrc[dernierIdx]
                uDst vaut histoDst[dernierIdx]
                uNb vaut histoNb[dernierIdx]
                pour dd de 1 à uNb
                    niveaux[uDst] vaut niveaux[uDst] - 1
                    coulRetour vaut contenu[uDst * capacite + niveaux[uDst]]
                    contenu[uDst * capacite + niveaux[uDst]] vaut 0
                    contenu[uSrc * capacite + niveaux[uSrc]] vaut coulRetour
                    niveaux[uSrc] vaut niveaux[uSrc] + 1
                fin pour
                histoSrc supprime dernierIdx
                histoDst supprime dernierIdx
                histoNb supprime dernierIdx
                coups vaut coups + 1
                selection vaut - 1
                message vaut "Coup annule."
            fin si

        sinon si clique(clicX, clicY, btnX2, btnY, btnL, btnH) alors
            // ---- BOUTON NOUVEAU ----
            nouvelleDemande vaut vrai
            affiche ">> Nouvelle grille."

        sinon si cible = -1 alors
            selection vaut - 1
            message vaut "Clique sur un tube."

        sinon si selection = -1 alors
            // ---- PREMIER CLIC : on prend ----
            si niveaux[cible] = 0 alors
                message vaut "Ce tube est vide : rien a prendre."
            sinon
                selection vaut cible
                message vaut "Tube " + convertir_texte(cible + 1) + " pris. Ou le verser ?"
            fin si

        sinon si selection = cible alors
            // ---- MEME TUBE : on repose ----
            selection vaut - 1
            message vaut "Selection annulee."

        sinon
            // ---- DEUXIEME CLIC : on verse ----
            nSrc vaut niveaux[selection]
            nDst vaut niveaux[cible]
            doseHaut vaut contenu[selection * capacite + nSrc - 1]

            okVerser vaut faux
            si nDst >= capacite alors
                message vaut "Le tube " + convertir_texte(cible + 1) + " est plein."
            sinon
                si nDst = 0 alors
                    okVerser vaut vrai
                sinon si contenu[cible * capacite + nDst - 1] = doseHaut alors
                    okVerser vaut vrai
                sinon
                    message vaut "Le " + libelles[doseHaut] + " ne va pas sur le " + libelles[contenu[cible * capacite + nDst - 1]] + "."
                fin si
            fin si

            si okVerser alors
                // combien de doses identiques a la suite ?
                nbVerse vaut 1
                dd vaut nSrc - 2
                poursuivre vaut vrai
                tant que poursuivre
                    si dd < 0 alors
                        poursuivre vaut faux
                    sinon si contenu[selection * capacite + dd] <> doseHaut alors
                        poursuivre vaut faux
                    sinon
                        nbVerse vaut nbVerse + 1
                        dd vaut dd - 1
                    fin si
                fin tant que

                placeLibre vaut capacite - nDst
                si nbVerse > placeLibre alors
                    nbVerse vaut placeLibre
                fin si

                // on retire du tube de depart
                pour dd de 1 à nbVerse
                    contenu[selection * capacite + niveaux[selection] - 1] vaut 0
                    niveaux[selection] vaut niveaux[selection] - 1
                fin pour

                // on ajoute dans le tube d'arrivee
                pour dd de 1 à nbVerse
                    contenu[cible * capacite + niveaux[cible]] vaut doseHaut
                    niveaux[cible] vaut niveaux[cible] + 1
                fin pour

                histoSrc ajoute selection
                histoDst ajoute cible
                histoNb ajoute nbVerse
                coups vaut coups + 1
                message vaut convertir_texte(nbVerse) + " dose(s) de " + libelles[doseHaut] + " versee(s) dans le tube " + convertir_texte(cible + 1) + "."
            fin si

            selection vaut - 1
        fin si

        si estGagne() alors
            gagne vaut vrai
        fin si
    fin tant que

    // ---------- Fin de partie ----------
    si gagne alors
        message vaut "Bravo, tous les tubes sont tries !"
        appelle dessiner(-1, vrai)
        affiche "========================================"
        affiche "GAGNE ! Tubes tries en " + convertir_texte(coups) + " coups."
        rejouer vaut 0
        choisir ["Rejouer une partie", "Quitter"] dans rejouer
        si rejouer = 2 alors
            partieEnCours vaut faux
        fin si
    fin si

fin tant que
Pas encore noté
Développer un Reversi complet avec IA : un projet qui mêle stratégie, algorithmie et design d’interface
Article

Développer un Reversi complet avec IA

Un projet qui mêle stratégie, algorithmie et design d’interface

Lire l'articleReplier l'article

Créer un jeu de Reversi (Othello) de A à Z, avec une interface claire, une logique de retournement fidèle aux règles officielles, une IA débutante et une gestion complète de partie, c’est un exercice qui combine algorithmie, UX, mathématiques, et structure logicielle.

Ce projet démontre comment un code bien pensé peut transformer un plateau 8×8 en une expérience de jeu fluide, pédagogique et agréable.

Une architecture de jeu claire et efficace

Le programme repose sur une structure simple mais puissante :

  • un tableau plateau représentant les 64 cases,
  • un système de directions pour analyser les 8 axes du jeu,
  • une fonction gainCoup() qui calcule combien de pions seraient retournés,

une fonction peutJouer() qui détecte si un joueur a au moins un coup valide.

Le code illustre parfaitement cette logique :

si plateau[posTest] <> 0 alors retourne 0
gTotal vaut gTotal + gNb

Chaque coup est évalué avec précision avant d’être autorisé.

Une IA débutante mais fonctionnelle

L’intelligence artificielle joue en niveau "débutant" : elle choisit un coup valide au hasard parmi les meilleurs candidats.

  • Tirage au sort pur parmi les coups valides

Ce comportement donne un adversaire accessible, idéal pour l’apprentissage ou pour des parties rapides.

Une interface graphique soignée

Le jeu affiche :

  • un damier vert clair / vert foncé,
  • des pions noirs et blancs avec reflets,
  • un contour arrondi du plateau,
  • un bouton "Nouvelle partie",
  • des indications visuelles des coups possibles,
  • un message dynamique pour guider le joueur.

Le code montre cette attention au détail :

rectangle_arrondi(ecran, xPlateau - 4, yPlateau - 4, cote * nbCases + 8, cote * nbCases + 8, 10, #004d00)
cercle(ecran, dCx, dCy, rPion, coulPion[dVal])

Le résultat : une interface claire, lisible, agréable à utiliser.

Une gestion complète du déroulement de la partie

Le programme gère :

  • le tour du joueur,
  • le tour de l’ordinateur,
  • les passes obligatoires,
  • les coups interdits,
  • le retournement des pions dans toutes les directions,
  • la détection automatique de la fin de partie.

La logique de retournement est particulièrement bien construite :

plateau[fLL * nbCases + fCC] vaut joueurCourant

Chaque pion adverse pris en sandwich est retourné, comme dans le vrai Reversi.

Une fin de partie claire et élégante

Lorsque plus aucun joueur ne peut jouer, le jeu affiche :

  • une bannière de fin,
  • le score final,
  • le résultat (victoire, défaite ou égalité),
  • une option pour rejouer.
  • Score final : X noirs / Y blancs
  • BRAVO, tu as gagné !

Une conclusion propre, professionnelle et motivante.

Ce que ce projet met en valeur

  • Maîtrise des structures de données
  • Gestion des directions et des retournements
  • IA débutante fonctionnelle
  • Interface graphique complète et agréable
  • Détection automatique des coups valides
  • Gestion du tour par tour et des passes
  • Fin de partie claire et informative
  • Code structuré, lisible et extensible

Programme :

// ============================================================
//  1. REGLAGES ET DIMENSIONS
// ============================================================
nbCases est un nombre
nbCases vaut 8
cote est un nombre
cote vaut 44

xPlateau est un nombre
xPlateau vaut 20
yPlateau est un nombre
yPlateau vaut 48

largToile est un nombre
largToile vaut xPlateau * 2 + cote * nbCases

btnL est un nombre
btnL vaut 130
btnH est un nombre
btnH vaut 34

btnY est un nombre
btnY vaut yPlateau + cote * nbCases + 34
btnX est un nombre
btnX vaut largToile / 2 - btnL / 2

hautToile est un nombre
hautToile vaut btnY + btnH + 14


// ============================================================
//  2. COULEURS DES PIONS (indice 1 = noir, 2 = blanc)
// ============================================================
coulPion est un tableau
coulPion ajoute #000000
coulPion ajoute #141414
coulPion ajoute #eeeeee

coulEclat est un tableau
coulEclat ajoute #000000
coulEclat ajoute #3d3d3d
coulEclat ajoute #ffffff

libelles est un tableau
libelles ajoute "personne"
libelles ajoute "Noir"
libelles ajoute "Blanc"


// ============================================================
//  3. LES 8 DIRECTIONS (ligne, colonne)
// ============================================================
dirL est un tableau
dirC est un tableau

dirL ajoute -1
dirC ajoute -1
dirL ajoute -1
dirC ajoute 0
dirL ajoute -1
dirC ajoute 1
dirL ajoute 0
dirC ajoute -1
dirL ajoute 0
dirC ajoute 1
dirL ajoute 1
dirC ajoute -1
dirL ajoute 1
dirC ajoute 0
dirL ajoute 1
dirC ajoute 1


// ============================================================
//  4. ETAT DU JEU
// ============================================================
plateau est un tableau

jeu est une toile

joueurCourant est un nombre
joueurCourant vaut 1
dernierCoup est un nombre
dernierCoup vaut -1
message est un texte
message vaut "Clique sur un cercle pour jouer."


// ============================================================
//  5. COMBIEN DE PIONS CE COUP RETOURNERAIT-IL ?
// ============================================================
fonction gainCoup(posTest, joueurTest)
    gTotal est un nombre
    gDir est un nombre
    gLig est un nombre
    gCol est un nombre
    gLL est un nombre
    gCC est un nombre
    gNb est un nombre
    gAdv est un nombre
    gStop est un booleen

    si plateau[posTest] <> 0 alors
        retourne 0
    fin si

    gAdv vaut 3 - joueurTest
    gTotal vaut 0
    gLig vaut arrondi_inferieur(posTest / nbCases)
    gCol vaut posTest mod nbCases

    pour gDir de 0 à 7
        gLL vaut gLig + dirL[gDir]
        gCC vaut gCol + dirC[gDir]
        gNb vaut 0
        gStop vaut faux

        tant que non gStop
            si gLL < 0 ou gLL > 7 ou gCC < 0 ou gCC > 7 alors
                gNb vaut 0
                gStop vaut vrai
            sinon si plateau[gLL * nbCases + gCC] = gAdv alors
                gNb vaut gNb + 1
                gLL vaut gLL + dirL[gDir]
                gCC vaut gCC + dirC[gDir]
            sinon si plateau[gLL * nbCases + gCC] = joueurTest alors
                gStop vaut vrai
            sinon
                gNb vaut 0
                gStop vaut vrai
            fin si
        fin tant que

        gTotal vaut gTotal + gNb
    fin pour

    retourne gTotal
fin fonction


// ============================================================
//  6. CE JOUEUR A-T-IL AU MOINS UN COUP ?
// ============================================================
fonction peutJouer(joueurTeste)
    pIdx est un nombre
    pour pIdx de 0 à nbCases * nbCases - 1
        si gainCoup(pIdx, joueurTeste) > 0 alors
            retourne vrai
        fin si
    fin pour
    retourne faux
fin fonction


// ============================================================
//  7. ROBOT NIVEAU DEBUTANT (Choix aléatoire des coups)
// ============================================================
fonction meilleurCoup(joueurIA)
    mIdx est un nombre
    mGain est un nombre
    mNote est un nombre
    mMeilleure est un nombre
    mChoix est un nombre

    mChoix vaut -1
    mMeilleure vaut -9999

    pour mIdx de 0 à nbCases * nbCases - 1
        mGain vaut gainCoup(mIdx, joueurIA)
        si mGain > 0 alors
            // Tirage au sort pur parmi les coups valides
            mNote vaut hasard(1, 1000)
            si mNote > mMeilleure alors
                mMeilleure vaut mNote
                mChoix vaut mIdx
            fin si
        fin si
    fin pour

    retourne mChoix
fin fonction


// ============================================================
//  8. NOMBRE DE PIONS D'UN JOUEUR
// ============================================================
fonction nbPions(joueurCompte)
    cIdx est un nombre
    cTotal est un nombre
    cTotal vaut 0
    pour cIdx de 0 à nbCases * nbCases - 1
        si plateau[cIdx] = joueurCompte alors
            cTotal vaut cTotal + 1
        fin si
    fin pour
    retourne cTotal
fin fonction


// ============================================================
//  9. DESSIN DU JEU (Damier + Interface aérée)
// ============================================================
procédure dessinerJeu(ecran, avecFin)
    dimension(ecran, largToile, hautToile)
    effacer(ecran)

    remplir(ecran, #00a800)

    dTxt est un texte
    dIdx est un nombre
    dLig est un nombre
    dCol est un nombre
    dCx est un nombre
    dCy est un nombre
    dVal est un nombre
    dNoir est un nombre
    dBlanc est un nombre
    rPion est un nombre

    rPion vaut 17
    dNoir vaut nbPions(1)
    dBlanc vaut nbPions(2)

    // ---- En-tête ----
    label(ecran, largToile / 2 - label_largeur("Reversi - EduCode", 16) / 2, 25, "Reversi - EduCode", #ffffff, 18)

    // Score Noir
    cercle(ecran, xPlateau + 10, 16, 7, #141414)
    label(ecran, xPlateau + 22, 21, convertir_texte(dNoir), #ffffff, 13)

    // Score Blanc
    cercle(ecran, largToile - xPlateau - 28, 16, 7, #eeeeee)
    label(ecran, largToile - xPlateau - 16, 21, convertir_texte(dBlanc), #ffffff, 13)

    // ---- Contour du plateau ----
    rectangle_arrondi(ecran, xPlateau - 4, yPlateau - 4, cote * nbCases + 8, cote * nbCases + 8, 10, #004d00)

    // ---- Damier vert clair / vert foncé ----
    pour dLig de 0 à nbCases - 1
        pour dCol de 0 à nbCases - 1
            dCx vaut xPlateau + dCol * cote
            dCy vaut yPlateau + dLig * cote
            si (dLig + dCol) mod 2 = 0 alors
                rectangle(ecran, dCx, dCy, cote, cote, #00993d)
            sinon
                rectangle(ecran, dCx, dCy, cote, cote, #007a30)
            fin si
        fin pour
    fin pour

    // ---- Pions et anneaux d'aide ----
    pour dIdx de 0 à nbCases * nbCases - 1
        dLig vaut arrondi_inferieur(dIdx / nbCases)
        dCol vaut dIdx mod nbCases
        dCx vaut xPlateau + dCol * cote + cote / 2
        dCy vaut yPlateau + dLig * cote + cote / 2
        dVal vaut plateau[dIdx]

        si dVal = 0 alors
            si joueurCourant = 1 et non avecFin alors
                si gainCoup(dIdx, 1) > 0 alors
                    cercle(ecran, dCx, dCy, 13, #00421a)
                    si (dLig + dCol) mod 2 = 0 alors
                        cercle(ecran, dCx, dCy, 10, #00993d)
                    sinon
                        cercle(ecran, dCx, dCy, 10, #007a30)
                    fin si
                fin si
            fin si
        sinon
            si dIdx = dernierCoup alors
                cercle(ecran, dCx, dCy, rPion + 3, #f59e0b)
            fin si

            cercle(ecran, dCx, dCy + 2, rPion, #004017)
            cercle(ecran, dCx, dCy, rPion, coulPion[dVal])

            si dVal = 1 alors
                cercle(ecran, dCx - 4, dCy - 4, 5, #3d3d3d)
            sinon
                cercle(ecran, dCx, dCy, rPion - 1, #f5f5f5)
                cercle(ecran, dCx - 3, dCy - 3, 5, #ffffff)
            fin si
        fin si
    fin pour

    // ---- Message ----
    label(ecran, largToile / 2 - label_largeur(message, 11) / 2, btnY - 14, message, #ffffff, 11)

    // ---- Bouton ----
    rectangle_arrondi(ecran, btnX, btnY, btnL, btnH, 8, #005a00)
    dTxt vaut "Nouvelle partie"
    label(ecran, btnX + btnL / 2 - label_largeur(dTxt, 12) / 2, btnY + 21, dTxt, #ffffff, 14)

    // ---- Bannière de fin ----
    si avecFin alors
        rectangle_arrondi(ecran, xPlateau, hautToile / 2 - 40, largToile - xPlateau * 2, 80, 10, #004d00)
        dTxt vaut "ÉGALITÉ !"
        si dNoir > dBlanc alors
            dTxt vaut "TU AS GAGNÉ !"
        fin si
        si dBlanc > dNoir alors
            dTxt vaut "L'ORDINATEUR GAGNE"
        fin si
        label(ecran, largToile / 2 - label_largeur(dTxt, 18) / 2, hautToile / 2 - 8, dTxt, #ffd166, 18)
        dTxt vaut convertir_texte(dNoir) + " noirs contre " + convertir_texte(dBlanc) + " blancs"
        label(ecran, largToile / 2 - label_largeur(dTxt, 12) / 2, hautToile / 2 + 16, dTxt, #ffffff, 12)
    fin si
fin procédure


// ============================================================
//  10. AFFICHAGE
// ============================================================
procédure dessiner(finVue)
    appelle dessinerJeu(jeu, finVue)
    affiche jeu
fin procédure


// ============================================================
//  11. VARIABLES DE LA BOUCLE PRINCIPALE
// ============================================================
partieEnCours est un booleen
nouvelleDemande est un booleen
finPartie est un booleen
rejouer est un nombre

idx est un nombre
clicX est un nombre
clicY est un nombre
ligClic est un nombre
colClic est un nombre
posJoue est un nombre

fLig est un nombre
fCol est un nombre
fD est un nombre
fLL est un nombre
fCC est un nombre
fNb est un nombre
fPas est un nombre
fAdv est un nombre
fStop est un booleen
totalRetourne est un nombre

noirFinal est un nombre
blancFinal est un nombre


// ============================================================
//  12. PARTIE
// ============================================================
partieEnCours vaut vrai

tant que partieEnCours

    vide plateau
    pour idx de 0 à nbCases * nbCases - 1
        plateau ajoute 0
    fin pour
    plateau[27] vaut 2
    plateau[28] vaut 1
    plateau[35] vaut 1
    plateau[36] vaut 2

    joueurCourant vaut 1
    dernierCoup vaut -1
    finPartie vaut faux
    nouvelleDemande vaut faux
    message vaut "Clique sur un cercle pour jouer."

    tant que non finPartie et non nouvelleDemande

        appelle dessiner(faux)
        posJoue vaut -1

        si non peutJouer(1) et non peutJouer(2) alors
            finPartie vaut vrai

        sinon si non peutJouer(joueurCourant) alors
            message vaut libelles[joueurCourant] + " passe son tour."
            joueurCourant vaut 3 - joueurCourant

        sinon si joueurCourant = 1 alors
            cliquer jeu dans clicX, clicY

            si clique(clicX, clicY, btnX, btnY, btnL, btnH) alors
                nouvelleDemande vaut vrai
            sinon si clique(clicX, clicY, xPlateau, yPlateau, cote * nbCases, cote * nbCases) alors
                colClic vaut limiter(arrondi_inferieur((clicX - xPlateau) / cote), 0, nbCases - 1)
                ligClic vaut limiter(arrondi_inferieur((clicY - yPlateau) / cote), 0, nbCases - 1)
                posJoue vaut ligClic * nbCases + colClic
                si gainCoup(posJoue, 1) = 0 alors
                    message vaut "Coup interdit : choisis une case valide."
                    posJoue vaut -1
                fin si
            sinon
                message vaut "Clique sur le plateau."
            fin si

        sinon
            posJoue vaut meilleurCoup(2)
        fin si

        si posJoue >= 0 alors
            plateau[posJoue] vaut joueurCourant
            fLig vaut arrondi_inferieur(posJoue / nbCases)
            fCol vaut posJoue mod nbCases
            fAdv vaut 3 - joueurCourant
            totalRetourne vaut 0

            pour fD de 0 à 7
                fLL vaut fLig + dirL[fD]
                fCC vaut fCol + dirC[fD]
                fNb vaut 0
                fStop vaut faux

                tant que non fStop
                    si fLL < 0 ou fLL > 7 ou fCC < 0 ou fCC > 7 alors
                        fNb vaut 0
                        fStop vaut vrai
                    sinon si plateau[fLL * nbCases + fCC] = fAdv alors
                        fNb vaut fNb + 1
                        fLL vaut fLL + dirL[fD]
                        fCC vaut fCC + dirC[fD]
                    sinon si plateau[fLL * nbCases + fCC] = joueurCourant alors
                        fStop vaut vrai
                    sinon
                        fNb vaut 0
                        fStop vaut vrai
                    fin si
                fin tant que

                si fNb > 0 alors
                    pour fPas de 1 à fNb
                        fLL vaut fLig + dirL[fD] * fPas
                        fCC vaut fCol + dirC[fD] * fPas
                        plateau[fLL * nbCases + fCC] vaut joueurCourant
                    fin pour
                    totalRetourne vaut totalRetourne + fNb
                fin si
            fin pour

            dernierCoup vaut posJoue
            si joueurCourant = 1 alors
                message vaut "Tu retournes " + convertir_texte(totalRetourne) + " pion(s)."
            sinon
                message vaut "L'ordinateur retourne " + convertir_texte(totalRetourne) + " pion(s)."
            fin si
            joueurCourant vaut 3 - joueurCourant
        fin si

    fin tant que

    si finPartie alors
        noirFinal vaut nbPions(1)
        blancFinal vaut nbPions(2)
        message vaut "Partie terminée."
        appelle dessiner(vrai)

        affiche "========================================"
        affiche "Score final : " + convertir_texte(noirFinal) + " noirs / " + convertir_texte(blancFinal) + " blancs"
        si noirFinal > blancFinal alors
            affiche "BRAVO, tu as gagné !"
        sinon si blancFinal > noirFinal alors
            affiche "L'ordinateur gagne cette fois."
        sinon
            affiche "Égalité parfaite !"
        fin si

        rejouer vaut 0
        choisir ["Rejouer une partie", "Quitter"] dans rejouer
        si rejouer = 2 alors
            partieEnCours vaut faux
        fin si
    fin si

fin tant que
Pas encore noté
Construire un outil interactif de tracé graphique : un projet mêlant mathématiques, interface et logique utilisateur
Article

Construire un outil interactif de tracé graphique

Un projet mêlant mathématiques, interface et logique utilisateur

Lire l'articleReplier l'article

Développer un système capable de tracer des points, dessiner des segments, calculer des distances et gérer des interactions en temps réel est un excellent exercice pour combiner algorithmie, visualisation, gestion d’événements et ergonomie.

Ce programme illustre parfaitement cette synergie : une interface claire, un repère mathématique précis et une logique utilisateur intuitive.

Un repère graphique complet et dynamique

Le système commence par générer un repère cartésien propre et lisible :

  • Grille de fond (25 pixels = 1 unité)
  • Dessin des axes X et Y

Les graduations sont calculées automatiquement, avec affichage des valeurs uniquement lorsque cela apporte de la clarté :

  • si demi = arrondi(demi) alors label(... convertir_texte(k))

Ce choix permet d’obtenir un repère visuel professionnel, comparable à ceux utilisés en enseignement ou en ingénierie.

Une interface pensée pour l’utilisateur

Chaque clic dans la zone graphique ajoute ou supprime un point, avec une logique simple et intuitive :

  • clic sur un point existant : suppression,
  • clic ailleurs : ajout,
  • clic sur le premier point lorsque la forme est presque complète : fermeture automatique.

Le programme gère même les cas particuliers :

  • Lecture de listeX[0] uniquement si au moins 3 points existent
  • Vérification si la forme est déjà fermée

Une attention aux détails qui améliore nettement l’expérience utilisateur.

Calcul automatique des distances

À chaque mise à jour, le programme :

  • relie les points entre eux,
  • calcule la distance entre chaque segment via l’hypoténuse,
  • additionne les longueurs,

affiche la distance totale arrondie :

distance_totale vaut distance_totale + hypotenuse(diffX, diffY)
dist_arrondie est un nombre vaut decimal(distance_totale, 2)

Un outil idéal pour visualiser des trajectoires, des polygones ou des mesures géométriques.

Une boucle interactive fluide

Le système fonctionne en boucle :

  • affichage du graphique,
  • attente d’un clic,
  • mise à jour des données,
  • redessin complet.

Un bouton “Quitter” permet de sortir proprement :

si clique(xc, yc, 400, 510, 80, 30) alors continuer vaut faux

Une structure simple, robuste et efficace.

Ce que ce projet met en valeur

  • Maîtrise des repères mathématiques et de la conversion pixel ? unité
  • Gestion d’une interface graphique interactive
  • Calculs géométriques automatisés (distances, segments, fermeture de forme)
  • Logique utilisateur claire et intuitive
  • Architecture propre, modulaire et évolutive

Programme :

@ Déclaration des variables globales
listeX, listeY est un tableau
Graphe est une toile
dimension(Graphe, 500, 550)

@ procédure de dessin
procédure tracerGraphique(tabX, tabY)
    // 1. On efface la toile complètement avant de tout redessiner
    effacer(Graphe)
    remplir(Graphe, #ffffff)

    // Grille de fond (25 pixels = 1 unité)
    grille(Graphe, 25, #eeeeee)

    // Centre du repère
    centreX, centreY est un nombre vaut 250

    // Échelle
    echX, echY est un nombre vaut 25

    // Dessin des axes X et Y
    ligne(Graphe, 0, centreY, 500, centreY, #000000, 2)
    ligne(Graphe, centreX, 0, centreX, 500, #000000, 2)
    label(Graphe, 480, centreY - 10, "X", #000000,16)
    label(Graphe, centreX + 15, 20, "Y", #000000,16)

    // Graduations sur les axes
    k est un nombre
    pour k de - 10 à 10
        si k = 0 alors
            label(Graphe, centreX - 15, centreY + 15, "0", #666666,12)
        sinon
            px, py est un nombre
            px vaut centreX + (k * echX)
            py vaut centreY - (k * echY)

            ligne(Graphe, px, centreY - 4, px, centreY + 4, #000000, 2)
            ligne(Graphe, centreX - 4, py, centreX + 4, py, #000000, 2)

            demi est un nombre
            demi vaut k / 2
            si demi = arrondi(demi) alors
                label(Graphe, px - 8, centreY + 20, convertir_texte(k), #666666, 12)
                label(Graphe, centreX - 25, py - 6, convertir_texte(k), #666666, 12)
            fin si
        fin si
    fin pour

    // Variables pour tracer les segments et calculer la distance
    nbPoints est un nombre
    nbPoints vaut longueur(tabX)
    x_prec, y_prec est un nombre
    distance_totale est un nombre vaut 0

    // Dessin des points et des lignes
    i est un nombre
    pour i de 0 à nbPoints - 1
        posX, posY est un nombre
        posX vaut tabX[i]
        posY vaut tabY[i]

        px, py est un nombre
        px vaut centreX + (posX * echX)
        py vaut centreY - (posY * echY)

        si i > 0 alors
            ligne(Graphe, x_prec, y_prec, px, py, #3b82f6, 3)
            diffX, diffY est un nombre
            diffX vaut posX - tabX[i - 1]
            diffY vaut posY - tabY[i - 1]
            distance_totale vaut distance_totale + hypotenuse(diffX, diffY)
        fin si

        cercle(Graphe, px, py, 6, #ef4444)

        texte_val est un texte
        texte_val vaut "(" + convertir_texte(posX) + ";" + convertir_texte(posY) + ")"
        label(Graphe, px + 8, py - 12, texte_val, #ef4444, 14)

        x_prec vaut px
        y_prec vaut py
    fin pour

    // Zone d'affichage du résultat
    dist_arrondie est un nombre vaut decimal(distance_totale, 2)

    rectangle(Graphe, 0, 500, 500, 50, #f8fafc)
    label(Graphe, 20, 530, "Distance totale : " + convertir_texte(dist_arrondie) + " unités", #000000,16)

    // Bouton Quitter
    rectangle(Graphe, 400, 510, 75, 30, #ef4444)
    label(Graphe, 415, 530, "Quitter", #ffffff, 14)

    affiche Graphe
fin procédure

@ Boucle principale
continuer est un booléen
continuer vaut vrai

tant que continuer
    appelle tracerGraphique(listeX, listeY)

    xc, yc est un nombre
    cliquer Graphe dans xc, yc

    // Bouton Quitter
    si clique(xc, yc, 400, 510, 80, 30) alors
        continuer vaut faux
        continue
    fin si

    // Clic dans la zone du graphique
    si yc < 500 alors
        gx est un nombre vaut arrondi((xc - 250) / 25)
        gy est un nombre vaut arrondi((250 - yc) / 25)

        nb est un nombre vaut longueur(listeX)

        // Vérification si la forme est déjà fermée
        estFerme est un booléen
        estFerme vaut faux
        si nb >= 4 alors
            si listeX[0] = listeX[nb - 1] et listeY[0] = listeY[nb - 1] alors
                estFerme vaut vrai
            fin si
        fin si

        actionFermeture est un booléen
        actionFermeture vaut faux

        // Lecture de listeX[0] uniquement si au moins 3 points existent
        si nb >= 3 alors
            si estFerme = faux alors
                si gx = listeX[0] et gy = listeY[0] alors
                    listeX ajoute listeX[0]
                    listeY ajoute listeY[0]
                    actionFermeture vaut vrai
                fin si
            fin si
        fin si

        // Si aucune fermeture n'a été effectuée, on traite la suppression ou l'ajout
        si actionFermeture = faux alors
            trouve est un booléen
            trouve vaut faux

            si nb > 0 alors
                idx est un nombre
                pour idx de nb - 1 à 0 pas - 1
                    si listeX[idx] = gx et listeY[idx] = gy alors
                        listeX supprime idx
                        listeY supprime idx
                        trouve vaut vrai
                        arrête
                    fin si
                fin pour
            fin si

            si trouve = faux alors
                listeX ajoute gx
                listeY ajoute gy
            fin si
        fin si
    fin si
fin tant que
Pas encore noté
Concevoir un jeu de dames avec IA
Article

Concevoir un jeu de dames avec IA

Un projet mêlant stratégie, algorithmie

Lire l'articleReplier l'article

Créer un jeu de dames complet, avec interface graphique et intelligence artificielle, est un excellent moyen d’explorer des problématiques concrètes : logique de déplacement, gestion d’état, interactions utilisateur et prise de décision automatisée.
Ce type de projet met en lumière la richesse qu’il y a derrière un jeu en apparence simple.

Un plateau interactif pensé pour la clarté

Le système repose sur un damier 8×8 où chaque case, chaque pion et chaque action est dessinée en temps réel.

Les rôles sont clairement définis :

  • 0 = vide,
  • 1 = pion blanc,
  • 2 = pion noir,
  • 3 = dame blanche,
  • 4 = dame noire

La sélection du pion est affichée sous le pion, afin de préserver la lisibilité :

  • Surbrillance de la sélection, dessinée SOUS le pion

Un choix qui améliore nettement l’expérience utilisateur.

Une IA qui agit selon des priorités stratégiques

L’intelligence artificielle ne se contente pas de déplacer ses pions : elle suit une logique de décision structurée.

Sa première priorité est la capture :

  • PRIORITÉ : le bot cherche d'abord à MANGER

Elle gère également :

  • les déplacements simples,
  • les promotions en dame,
  • les reculs autorisés pour les dames,
  • les situations de blocage.

Cette hiérarchie donne au bot un comportement crédible, cohérent et parfois surprenant.

Détection automatique des conditions de victoire

Le système identifie :

  • la victoire du joueur,
  • la victoire du bot,
  • les blocages,
  • les promotions,
  • l’état final du plateau.

Les messages de fin sont explicites :

  • VICTOIRE ! Vous avez gagné !
  • DÉFAITE ! Le bot a gagné !

Un cycle complet, du premier clic à l’écran final.

Ce que ce projet met en valeur

  • Gestion avancée des structures de données
  • Logique de déplacement et de capture fidèle aux règles
  • Conception d’une IA priorisée et réactive
  • Intégration d’une interface graphique cohérente
  • Construction d’un projet complet mêlant moteur, stratégie et rendu visuel

Programme :

t est une toile
dimension(t, 450, 460)
p est un tableau

// Initialisation du plateau (8x8)
// 0 = vide, 1 = pion blanc (joueur), 2 = pion noir (bot), 3 = dame blanche, 4 = dame noire
pour r de 0 à 7
    l est un tableau
    pour c de 0 à 7
        si (r + c) mod 2 = 1 alors
            si r < 3 alors
                l ajoute 2
            sinon si r > 4 alors
                l ajoute 1
            sinon
                l ajoute 0
            fin si
        sinon
            l ajoute 0
        fin si
    fin pour
    p ajoute l
fin pour

sl est un nombre
sc est un nombre
sl vaut -1
sc vaut -1
cx est un nombre
cy est un nombre
cl est un nombre
cc est un nombre

pions_captures_joueur est un nombre
pions_captures_bot est un nombre
pions_captures_joueur vaut 0
pions_captures_bot vaut 0

partie_finie est un booléen
partie_finie vaut faux
message_fin est un texte
message_fin vaut ""

tant que partie_finie = faux
    effacer(t)

    // Fond du plateau
    remplir(t, #2C3E50)

    // Affichage des scores
    label(t, 30, 24, "Vos captures : " + pions_captures_joueur, #FFFFFF, 14)
    label(t, 250, 24, "Captures bot : " + pions_captures_bot, #FFFFFF, 14)

    // Dessin du damier et des pions
    pour r de 0 à 7
        pour c de 0 à 7
            xp est un nombre
            yp est un nombre
            xp vaut c * 50 + 25
            yp vaut r * 50 + 35

            si (r + c) mod 2 = 0 alors
                rectangle(t, xp, yp, 50, 50, #ECF0F1)
            sinon
                rectangle(t, xp, yp, 50, 50, #7F8C8D)
            fin si

            // Surbrillance de la sélection, dessinée SOUS le pion
            // (avant, elle était dessinée par-dessus et masquait le pion)
            si r = sl et c = sc alors
                rectangle(t, xp, yp, 50, 50, rgba(46, 204, 113, 0.5))
            fin si

            val est un nombre
            val vaut p[r][c]

            si val = 1 alors
                cercle(t, xp + 25, yp + 25, 20, #BDC3C7)
                cercle(t, xp + 25, yp + 25, 16, #FFFFFF)
            sinon si val = 2 alors
                cercle(t, xp + 25, yp + 25, 20, #111111)
                cercle(t, xp + 25, yp + 25, 16, #34495E)
            sinon si val = 3 alors
                cercle(t, xp + 25, yp + 25, 20, #F39C12)
                cercle(t, xp + 25, yp + 25, 16, #FFFFFF)
            sinon si val = 4 alors
                cercle(t, xp + 25, yp + 25, 20, #F39C12)
                cercle(t, xp + 25, yp + 25, 16, #2C3E50)
            fin si
        fin pour
    fin pour

    affiche t

    cliquer t dans cx, cy
    cx vaut cx - 25
    cy vaut cy - 35

    // Conversion clic -> case en 2 lignes (au lieu de 14 "si")
    si cx >= 0 et cx < 400 et cy >= 0 et cy < 400 alors
        cc vaut (cx - (cx mod 50)) / 50
        cl vaut (cy - (cy mod 50)) / 50

        si sl = -1 alors
            // Premier clic : on sélectionne un pion du joueur
            si p[cl][cc] = 1 ou p[cl][cc] = 3 alors
                sl vaut cl
                sc vaut cc
            fin si
        sinon si cl = sl et cc = sc alors
            // Re-clic sur le même pion : désélection
            sl vaut -1
            sc vaut -1
        sinon si p[cl][cc] = 1 ou p[cl][cc] = 3 alors
            // Clic sur un autre de ses pions : on change de sélection
            sl vaut cl
            sc vaut cc
        sinon
            deplacement_valide est un booléen
            deplacement_valide vaut faux
            manger_pion est un booléen
            manger_pion vaut faux

            ml_inter est un nombre
            mc_inter est un nombre
            ml_inter vaut 0
            mc_inter vaut 0

            // Gestion du pion normal (1) et de la Dame (3) du joueur
            si absolue(sl - cl) = absolue(sc - cc) alors
                dist est un nombre
                dist vaut absolue(sl - cl)

                si dist = 1 et p[cl][cc] = 0 alors
                    si p[sl][sc] = 3 ou (p[sl][sc] = 1 et sl - cl = 1) alors
                        deplacement_valide vaut vrai
                    fin si
                sinon si dist = 2 et p[cl][cc] = 0 alors
                    ml_inter vaut (sl + cl) / 2
                    mc_inter vaut (sc + cc) / 2
                    si p[ml_inter][mc_inter] = 2 ou p[ml_inter][mc_inter] = 4 alors
                        manger_pion vaut vrai
                        deplacement_valide vaut vrai
                    fin si
                fin si
            fin si

            // Exécution du mouvement du joueur
            // (si le coup est invalide, le pion RESTE sélectionné pour réessayer)
            si deplacement_valide = vrai alors
                type_pion est un nombre
                type_pion vaut p[sl][sc]
                p[sl][sc] vaut 0

                si manger_pion = vrai alors
                    p[ml_inter][mc_inter] vaut 0
                    pions_captures_joueur vaut pions_captures_joueur + 1
                fin si

                si cl = 0 et type_pion = 1 alors
                    p[cl][cc] vaut 3
                sinon
                    p[cl][cc] vaut type_pion
                fin si

                sl vaut -1
                sc vaut -1

                // Vérification de la victoire du joueur
                nb_pions_bot est un nombre
                nb_pions_bot vaut 0
                pour r_v de 0 à 7
                    pour c_v de 0 à 7
                        si p[r_v][c_v] = 2 ou p[r_v][c_v] = 4 alors
                            nb_pions_bot vaut nb_pions_bot + 1
                        fin si
                    fin pour
                fin pour

                si nb_pions_bot = 0 alors
                    partie_finie vaut vrai
                    message_fin vaut "VICTOIRE ! Vous avez gagné !"
                fin si

                // --- TOUR DU BOT ---
                si partie_finie = faux alors
                    bot_agi est un booléen
                    bot_agi vaut faux

                    // 1. PRIORITÉ : le bot cherche d'abord à MANGER
                    // (avant, il ne mangeait que s'il ne pouvait pas bouger : jamais !)
                    pour br de 0 à 7
                        si bot_agi = faux alors
                            pour bc de 0 à 7
                                si bot_agi = faux alors
                                    vb est un nombre
                                    vb vaut p[br][bc]
                                    si vb = 2 ou vb = 4 alors

                                        // Manger Bas-Gauche
                                        si br <= 5 et bc >= 2 alors
                                            si (p[br + 1][bc - 1] = 1 ou p[br + 1][bc - 1] = 3) et p[br + 2][bc - 2] = 0 alors
                                                p[br][bc] vaut 0
                                                p[br + 1][bc - 1] vaut 0
                                                pions_captures_bot vaut pions_captures_bot + 1
                                                si br + 2 = 7 et vb = 2 alors
                                                    p[br + 2][bc - 2] vaut 4
                                                sinon
                                                    p[br + 2][bc - 2] vaut vb
                                                fin si
                                                bot_agi vaut vrai
                                            fin si
                                        fin si

                                        // Manger Bas-Droite
                                        si bot_agi = faux et br <= 5 et bc <= 5 alors
                                            si (p[br + 1][bc + 1] = 1 ou p[br + 1][bc + 1] = 3) et p[br + 2][bc + 2] = 0 alors
                                                p[br][bc] vaut 0
                                                p[br + 1][bc + 1] vaut 0
                                                pions_captures_bot vaut pions_captures_bot + 1
                                                si br + 2 = 7 et vb = 2 alors
                                                    p[br + 2][bc + 2] vaut 4
                                                sinon
                                                    p[br + 2][bc + 2] vaut vb
                                                fin si
                                                bot_agi vaut vrai
                                            fin si
                                        fin si

                                        // Manger Haut-Gauche (DAME uniquement)
                                        si bot_agi = faux et vb = 4 et br >= 2 et bc >= 2 alors
                                            si (p[br - 1][bc - 1] = 1 ou p[br - 1][bc - 1] = 3) et p[br - 2][bc - 2] = 0 alors
                                                p[br][bc] vaut 0
                                                p[br - 1][bc - 1] vaut 0
                                                pions_captures_bot vaut pions_captures_bot + 1
                                                p[br - 2][bc - 2] vaut 4
                                                bot_agi vaut vrai
                                            fin si
                                        fin si

                                        // Manger Haut-Droite (DAME uniquement)
                                        si bot_agi = faux et vb = 4 et br >= 2 et bc <= 5 alors
                                            si (p[br - 1][bc + 1] = 1 ou p[br - 1][bc + 1] = 3) et p[br - 2][bc + 2] = 0 alors
                                                p[br][bc] vaut 0
                                                p[br - 1][bc + 1] vaut 0
                                                pions_captures_bot vaut pions_captures_bot + 1
                                                p[br - 2][bc + 2] vaut 4
                                                bot_agi vaut vrai
                                            fin si
                                        fin si

                                    fin si
                                fin si
                            fin pour
                        fin si
                    fin pour

                    // 2. Sinon : déplacement simple
                    // (les pions du bot n'avancent QUE vers le bas ;
                    //  seules les dames peuvent reculer, comme aux vraies dames)
                    si bot_agi = faux alors
                        pour br de 0 à 7
                            si bot_agi = faux alors
                                pour bc de 0 à 7
                                    si bot_agi = faux alors
                                        vb2 est un nombre
                                        vb2 vaut p[br][bc]
                                        si vb2 = 2 ou vb2 = 4 alors

                                            // Bas-Gauche
                                            si br < 7 et bc > 0 alors
                                                si p[br + 1][bc - 1] = 0 alors
                                                    p[br][bc] vaut 0
                                                    si br + 1 = 7 et vb2 = 2 alors
                                                        p[br + 1][bc - 1] vaut 4
                                                    sinon
                                                        p[br + 1][bc - 1] vaut vb2
                                                    fin si
                                                    bot_agi vaut vrai
                                                fin si
                                            fin si

                                            // Bas-Droite
                                            si bot_agi = faux et br < 7 et bc < 7 alors
                                                si p[br + 1][bc + 1] = 0 alors
                                                    p[br][bc] vaut 0
                                                    si br + 1 = 7 et vb2 = 2 alors
                                                        p[br + 1][bc + 1] vaut 4
                                                    sinon
                                                        p[br + 1][bc + 1] vaut vb2
                                                    fin si
                                                    bot_agi vaut vrai
                                                fin si
                                            fin si

                                            // Haut-Gauche (DAME uniquement)
                                            si bot_agi = faux et vb2 = 4 et br > 0 et bc > 0 alors
                                                si p[br - 1][bc - 1] = 0 alors
                                                    p[br][bc] vaut 0
                                                    p[br - 1][bc - 1] vaut 4
                                                    bot_agi vaut vrai
                                                fin si
                                            fin si

                                            // Haut-Droite (DAME uniquement)
                                            si bot_agi = faux et vb2 = 4 et br > 0 et bc < 7 alors
                                                si p[br - 1][bc + 1] = 0 alors
                                                    p[br][bc] vaut 0
                                                    p[br - 1][bc + 1] vaut 4
                                                    bot_agi vaut vrai
                                                fin si
                                            fin si

                                        fin si
                                    fin si
                                fin pour
                            fin si
                        fin pour
                    fin si

                    // 3. Bot totalement bloqué : le joueur gagne
                    si bot_agi = faux alors
                        partie_finie vaut vrai
                        message_fin vaut "VICTOIRE ! Le bot est bloqué !"
                    fin si

                    // Vérification de la victoire du bot
                    nb_pions_joueur est un nombre
                    nb_pions_joueur vaut 0
                    pour r_v2 de 0 à 7
                        pour c_v2 de 0 à 7
                            si p[r_v2][c_v2] = 1 ou p[r_v2][c_v2] = 3 alors
                                nb_pions_joueur vaut nb_pions_joueur + 1
                            fin si
                        fin pour
                    fin pour

                    si nb_pions_joueur = 0 alors
                        partie_finie vaut vrai
                        message_fin vaut "DÉFAITE ! Le bot a gagné !"
                    fin si
                fin si
            fin si
        fin si
    fin si
fin tant que

// --- AFFICHAGE FINAL : la partie est terminée ---
effacer(t)
remplir(t, #2C3E50)
label(t, 30, 24, "Vos captures : " + pions_captures_joueur, #FFFFFF, 14)
label(t, 250, 24, "Captures bot : " + pions_captures_bot, #FFFFFF, 14)

pour r de 0 à 7
    pour c de 0 à 7
        xp est un nombre
        yp est un nombre
        xp vaut c * 50 + 25
        yp vaut r * 50 + 35

        si (r + c) mod 2 = 0 alors
            rectangle(t, xp, yp, 50, 50, #ECF0F1)
        sinon
            rectangle(t, xp, yp, 50, 50, #7F8C8D)
        fin si

        val est un nombre
        val vaut p[r][c]

        si val = 1 alors
            cercle(t, xp + 25, yp + 25, 20, #BDC3C7)
            cercle(t, xp + 25, yp + 25, 16, #FFFFFF)
        sinon si val = 2 alors
            cercle(t, xp + 25, yp + 25, 20, #111111)
            cercle(t, xp + 25, yp + 25, 16, #34495E)
        sinon si val = 3 alors
            cercle(t, xp + 25, yp + 25, 20, #F39C12)
            cercle(t, xp + 25, yp + 25, 16, #FFFFFF)
        sinon si val = 4 alors
            cercle(t, xp + 25, yp + 25, 20, #F39C12)
            cercle(t, xp + 25, yp + 25, 16, #2C3E50)
        fin si
    fin pour
fin pour

// Écran de fin recentré
rectangle(t, 50, 205, 360, 110, #111111)
rectangle(t, 45, 200, 360, 110, #ECF0F1)
label(t, 85, 250, message_fin, #2C3E50, 18)

affiche t
Pas encore noté
Animation des Tours de Hanoï en EduCode : un programme complet et pédagogique
Article

Animation des Tours de Hanoï

Animation des Tours de Hanoï en EduCode : un programme complet et pédagogique

Lire l'articleReplier l'article

Les Tours de Hanoï constituent l’un des problèmes classiques de l’algorithmique.

Elles permettent d’illustrer de manière simple mais puissante :

  • la récursivité,
  • la décomposition modulaire,
  • la gestion d’états successifs,
  • et ici, la génération d’une animation image par image.

Le programme ci-dessous, écrit en EduCode, propose une version entièrement animée du célèbre puzzle, avec 5 disques et 31 mouvements.
Chaque étape est capturée dans une toile, puis assemblée en une animation fluide.

Structure générale du programme

Le programme repose sur trois éléments clés :

  • Les tours, représentées par trois tableaux : tourA, tourB, tourC.
  • Les frames, stockées dans mesFrames, qui contiennent chaque état visuel du puzzle.
  • L’algorithme récursif de Hanoï, qui déplace les disques selon les règles du jeu.

Dès le début, la tour A est initialisée avec les disques de taille 5 à 1 :

pour k de numDisques à 1 pas - 1
    tourA ajoute k
fin pour

Chaque disque est identifié par un nombre, qui détermine sa largeur et sa couleur.

Dessiner un piquet : une procédure modulaire

La procédure dessinerPiquet est un excellent exemple de programmation modulaire.
Elle reçoit :

  • une toile,
  • une tour (tableau),
  • la position horizontale du piquet.

Elle calcule ensuite :

  • la largeur du disque (proportionnelle à sa valeur),
  • sa position verticale (empilée du bas vers le haut),
  • sa couleur (déterminée par un modulo sur la taille).

Chaque disque est dessiné par un simple rectangle coloré :

rectangle(uneToile, posX, posY, largeurDisque, 16, coul)

ette modularité permet de redessiner facilement les trois piquets à chaque état.

Génération d’un état visuel

La procédure dessinerEtat crée une nouvelle toile de 600×300 pixels, dessine :

  • le socle,
  • les trois piquets,
  • les labels “Tour A”, “Tour B”, “Tour C”,
  • puis les disques via dessinerPiquet.

Chaque toile est ensuite ajoutée au tableau mesFrames.

Cette approche transforme le programme en une machine à produire des images successives, parfaites pour une animation.

Déplacement d’un disque : la procédure bouger

La procédure bouger(pSource, pDest) :

  • identifie le disque au sommet de la tour source,
  • le retire,
  • l’ajoute à la tour destination,
  • génère un nouvel état visuel.

Chaque mouvement produit donc une nouvelle image, ce qui rend l’animation fidèle et fluide.

L’algorithme récursif des Tours de Hanoï

La procédure hanoi(n, dep, arr, aux) implémente l’algorithme classique :

  • si n = 1, on déplace directement le disque,

sinon :

  • on déplace les n-1 disques vers la tour auxiliaire,
  • on déplace le disque principal,
  • on déplace les n-1 disques vers la tour d’arrivée.

Cette structure récursive est élégante et parfaitement adaptée à EduCode.

Animation finale

Après avoir capturé l’état initial, le programme appelle :

appelle hanoi(numDisques, 1, 3, 2)

Puis assemble les 31 images générées en une animation :

animation(mesFrames, 400, 0)

400 ms entre chaque frame : un rythme idéal pour visualiser clairement chaque mouvement.

Conclusion : un programme complet, visuel et pédagogique

Ce programme EduCode est un excellent exemple de :

  • récursivité appliquée,
  • gestion d’états successifs,
  • modularité,
  • animation image par image,
  • visualisation algorithmique.

Il permet de comprendre les Tours de Hanoï non seulement en théorie, mais aussi en les observant se résoudre pas à pas, ce qui renforce l’apprentissage.

Programme :

@ Programme des Tours de Hanoï animé en EduCode (5 disques)

// Déclarations des variables globales
numDisques est un nombre
numDisques vaut 5

mesFrames est un tableau

tourA est un tableau
tourB est un tableau
tourC est un tableau

// Initialisation de la tour source avec 5 disques
pour k de numDisques à 1 pas - 1
    tourA ajoute k
fin pour

// Procédure modulaire pour dessiner les disques d'un piquet
procédure dessinerPiquet(uneToile, laTour, centreX)
    nbDisques est un nombre
    nbDisques vaut longueur(laTour)

    si nbDisques > 0 alors
        pour idx de 0 à nbDisques - 1
            tailleDisque est un nombre
            tailleDisque vaut laTour[idx]

            // Calcul de la largeur proportionnelle
            largeurDisque est un nombre
            largeurDisque vaut (tailleDisque * 22) + 20

            posX est un nombre
            posX vaut centreX - (largeurDisque / 2)

            posY est un nombre
            posY vaut 250 - ((idx + 1) * 18)

            // Couleur dynamique selon le disque
            coul est un texte
            si tailleDisque mod 5 = 1 alors
                coul vaut rgb(231, 76, 60) // Rouge
            sinon si tailleDisque mod 5 = 2 alors
                coul vaut rgb(52, 152, 219) // Bleu
            sinon si tailleDisque mod 5 = 3 alors
                coul vaut rgb(46, 204, 113) // Vert
            sinon si tailleDisque mod 5 = 4 alors
                coul vaut rgb(241, 196, 15) // Jaune
            sinon
                coul vaut rgb(155, 89, 182) // Violet
            fin si

            rectangle(uneToile, posX, posY, largeurDisque, 16, coul)
        fin pour
    fin si
fin procédure

// Procédure pour générer chaque image de l'animation
procédure dessinerEtat()
    uneToile est une toile
    dimension(uneToile, 600, 300)
    remplir(uneToile, #F0F4F8)

    // Socle et piquets
    rectangle(uneToile, 20, 250, 560, 15, #333333)
    rectangle(uneToile, 95, 70, 10, 180, #777777)
    rectangle(uneToile, 295, 70, 10, 180, #777777)
    rectangle(uneToile, 495, 70, 10, 180, #777777)

    // Libellés
    label(uneToile, 80, 285, "Tour A", #000000,14)
    label(uneToile, 280, 285, "Tour B", #000000,14)
    label(uneToile, 480, 285, "Tour C", #000000,14)

    // Dessin des 3 piquets
    appelle dessinerPiquet(uneToile, tourA, 100)
    appelle dessinerPiquet(uneToile, tourB, 300)
    appelle dessinerPiquet(uneToile, tourC, 500)

    mesFrames ajoute uneToile
fin procédure

// Procédure de déplacement d'un disque
procédure bouger(pSource, pDest)
    disqueBouge est un nombre
    disqueBouge vaut 0
    dernierIdx est un nombre

    si pSource = 1 alors
        dernierIdx vaut longueur(tourA) - 1
        disqueBouge vaut tourA[dernierIdx]
        tourA supprime dernierIdx
    sinon si pSource = 2 alors
        dernierIdx vaut longueur(tourB) - 1
        disqueBouge vaut tourB[dernierIdx]
        tourB supprime dernierIdx
    sinon
        dernierIdx vaut longueur(tourC) - 1
        disqueBouge vaut tourC[dernierIdx]
        tourC supprime dernierIdx
    fin si

    si pDest = 1 alors
        tourA ajoute disqueBouge
    sinon si pDest = 2 alors
        tourB ajoute disqueBouge
    sinon
        tourC ajoute disqueBouge
    fin si

    appelle dessinerEtat()
fin procédure

// Algorithme récursif
procédure hanoi(n, dep, arr, aux)
    si n = 1 alors
        appelle bouger(dep, arr)
    sinon
        appelle hanoi(n - 1, dep, aux, arr)
        appelle bouger(dep, arr)
        appelle hanoi(n - 1, aux, arr, dep)
    fin si
fin procédure

// Capture de l'état initial
appelle dessinerEtat()

// Résolution pour 5 disques
appelle hanoi(numDisques, 1, 3, 2)

// Animation avec un intervalle de 400 ms (31 mouvements)
animation(mesFrames, 400, 0)
Pas encore noté
Visualiser la logique numérique autrement : animation d’un circuit combinatoire
Article

Visualiser la logique numérique

Visualiser la logique numérique autrement : animation d’un circuit combinatoire

Lire l'articleReplier l'article

La logique numérique est souvent perçue comme abstraite. Pourtant, lorsqu’on la rend visuelle, animée et intuitive, elle devient un formidable outil de compréhension.
Voici une représentation dynamique d’un circuit combinatoire basé sur l’expression :

S = (A ET B) OU (NON C)

L’objectif : montrer comment les signaux se propagent dans un circuit, étape par étape, jusqu’à la sortie finale.

Une mise en scène graphique pensée pour la pédagogie

La construction repose sur une palette harmonisée et un style technique :

  • dégradé de fond pour la profondeur visuelle
  • grille discrète pour rappeler les schémas électroniques
  • pastilles colorées pour les bits (1 / 0)
  • fils dont la couleur change selon la propagation du signal
  • portes logiques stylisées (ET, OU, NON)

Chaque élément visuel contribue à rendre la logique lisible et attractive.

Des composants dessinés avec précision

Chaque porte logique possède sa propre procédure de dessin :

  • ET : rectangle arrondi
  • OU : forme plus organique
  • NON : triangle + bulle de négation

Les pastilles affichent la valeur des entrées et de la sortie, tandis que les fils s’illuminent progressivement selon les phases de propagation.

Une animation en plusieurs phases

Pour les 8 combinaisons possibles de A, B et C :

  • Extraction des bits
  • Calcul des signaux intermédiaires (g1, g2)
  • Propagation animée en 4 étapes
  • Stabilisation du résultat final avec pause visuelle

Cette progression permet d’observer le circuit comme s’il "réfléchissait", en montrant clairement comment chaque entrée influence la sortie.

Un film complet du fonctionnement du circuit

Chaque image générée est ajoutée à une séquence, formant une animation fluide.

Ce type de visualisation est idéal pour :

  • l’enseignement de la logique numérique
  • des démonstrations techniques
  • des présentations en entreprise ou en formation
  • des supports pédagogiques interactifs

Une autre manière d’apprendre

En rendant la logique numérique vivante, on facilite :

  • la compréhension des mécanismes internes
  • la mémorisation des concepts
  • l’envie d’explorer plus loin

Une approche qui montre que même les circuits les plus simples peuvent devenir clairs, esthétiques et engageants lorsqu’ils sont présentés autrement.

Programme :

@ Configuration initiale et palette
film est un tableau

largeurToile est un nombre
largeurToile vaut 560
hauteurToile est un nombre
hauteurToile vaut 360

fond1 est un texte
fond1 vaut #0d1b2a
fond2 est un texte
fond2 vaut #1b263b
cFilOff est un texte
cFilOff vaut #37485e
cFilOn est un texte
cFilOn vaut #ffd60a
cPorte est un texte
cPorte vaut #4cc9f0
cTexte est un texte
cTexte vaut #e0e1dd
cUn est un texte
cUn vaut #06d6a0
cZero est un texte
cZero vaut #ef476f

@ Coordonnées des éléments
// Entrées A, B, C
ax est un nombre
ax vaut 60
ay est un nombre
ay vaut 80
bx est un nombre
bx vaut 60
by est un nombre
by vaut 140
cx est un nombre
cx vaut 60
cy est un nombre
cy vaut 250

// Portes
etX est un nombre
etX vaut 230
etY est un nombre
etY vaut 110
nonX est un nombre
nonX vaut 230
nonY est un nombre
nonY vaut 250
ouX est un nombre
ouX vaut 400
ouY est un nombre
ouY vaut 180

// Sortie
sx est un nombre
sx vaut 510
sy est un nombre
sy vaut 180

@ Procédures de dessin utilitaires
fonction couleurFil(valeur)
    si valeur = 1 alors
        retourne cFilOn
    sinon
        retourne cFilOff
    fin si
fin fonction

procédure pastille(img, px, py, valeur)
    teinteP est un texte
    si valeur = 1 alors
        teinteP vaut cUn
    sinon
        teinteP vaut cZero
    fin si
    cercle(img, px, py, 13, teinteP)
    label(img, px - 4, py + 5, convertir_texte(valeur), #0d1b2a, 14)
fin procédure

@ Procédures de dessin des portes logiques
procédure dessinePorteET(img, px, py)
    rectangle_arrondi(img, px - 35, py - 30, 60, 60, 8, cPorte)
    label(img, px - 14, py + 6, "ET", #0d1b2a, 16)
fin procédure

procédure dessinePorteOU(img, px, py)
    rectangle_arrondi(img, px - 35, py - 30, 60, 60, 20, cPorte)
    label(img, px - 15, py + 6, "OU", #0d1b2a, 16)
fin procédure

procédure dessinePorteNON(img, px, py)
    // Utilisation d'un polygone plein pour le triangle
    polygone(img, px - 25, py - 25, px - 25, py + 25, px + 25, py, cPorte)
    cercle(img, px + 32, py, 6, cPorte)
    label(img, px - 18, py + 4, "NON", #0d1b2a, 10)
fin procédure

@ Boucle principale d'animation
combo est un nombre
pour combo de 0 à 7
    // Extraction des bits
    entreeA est un nombre
    entreeA vaut arrondi_inferieur(combo / 4) mod 2
    entreeB est un nombre
    entreeB vaut arrondi_inferieur(combo / 2) mod 2
    entreeC est un nombre
    entreeC vaut combo mod 2

    // Calcul de la logique
    g1 est un nombre
    si entreeA = 1 et entreeB = 1 alors
        g1 vaut 1
    sinon
        g1 vaut 0
    fin si

    g2 est un nombre
    si entreeC = 1 alors
        g2 vaut 0
    sinon
        g2 vaut 1
    fin si

    S est un nombre
    si g1 = 1 ou g2 = 1 alors
        S vaut 1
    sinon
        S vaut 0
    fin si

    // Animation de propagation
    phase est un nombre
    pour phase de 0 à 4
        img est une toile
        dimension(img, largeurToile, hauteurToile)
        dégradé(img, [fond1, fond2])

        // Grille de fond pour le style technique
        grille(img, 20, #1c2e4a)

        label(img, 130, 30, "Circuit : S = (A ET B) OU (NON C)", cTexte, 18)

        // ---- DESSIN DES FILS ----
        valFilA est un nombre
        valFilA vaut 0
        si phase >= 1 alors
            valFilA vaut entreeA
        fin si
        ligne(img, ax + 15, ay, etX - 40, ay, couleurFil(valFilA), 4)
        ligne(img, etX - 40, ay, etX - 40, etY - 15, couleurFil(valFilA), 4)

        valFilB est un nombre
        valFilB vaut 0
        si phase >= 1 alors
            valFilB vaut entreeB
        fin si
        ligne(img, bx + 15, by, etX - 40, by, couleurFil(valFilB), 4)
        ligne(img, etX - 40, by, etX - 40, etY + 15, couleurFil(valFilB), 4)

        valFilC est un nombre
        valFilC vaut 0
        si phase >= 1 alors
            valFilC vaut entreeC
        fin si
        ligne(img, cx + 15, cy, nonX - 30, cy, couleurFil(valFilC), 4)

        valG1 est un nombre
        valG1 vaut 0
        si phase >= 2 alors
            valG1 vaut g1
        fin si
        ligne(img, etX + 45, etY, ouX - 40, etY, couleurFil(valG1), 4)
        ligne(img, ouX - 40, etY, ouX - 40, ouY - 15, couleurFil(valG1), 4)

        valG2 est un nombre
        valG2 vaut 0
        si phase >= 2 alors
            valG2 vaut g2
        fin si
        ligne(img, nonX + 45, nonY, ouX - 40, nonY, couleurFil(valG2), 4)
        ligne(img, ouX - 40, nonY, ouX - 40, ouY + 15, couleurFil(valG2), 4)

        valS est un nombre
        valS vaut 0
        si phase >= 3 alors
            valS vaut S
        fin si
        ligne(img, ouX + 45, ouY, sx - 15, ouY, couleurFil(valS), 4)

        // ---- DESSIN DES PORTES ----
        appelle dessinePorteET(img, etX, etY)
        appelle dessinePorteNON(img, nonX, nonY)
        appelle dessinePorteOU(img, ouX, ouY)

        // ---- DESSIN DES PASTILLES ----
        label(img, ax - 30, ay + 5, "A", cTexte, 16)
        appelle pastille(img, ax, ay, entreeA)

        label(img, bx - 30, by + 5, "B", cTexte, 16)
        appelle pastille(img, bx, by, entreeB)

        label(img, cx - 30, cy + 5, "C", cTexte, 16)
        appelle pastille(img, cx, cy, entreeC)

        label(img, sx + 20, sy + 5, "S", cTexte, 18)
        si phase >= 3 alors
            appelle pastille(img, sx, sy, S)
        sinon
            cercle(img, sx, sy, 13, #37485e)
        fin si

        // Panneau explicatif
        texteExpl est un texte
        texteExpl vaut "A=" + convertir_texte(entreeA) + "  B=" + convertir_texte(entreeB) + "  C=" + convertir_texte(entreeC) + "   =>   S=" + convertir_texte(S)
        label(img, 130, 335, texteExpl, cTexte, 16)

        film ajoute img
    fin pour

    // Pause sur le résultat complet
    pauseC est un nombre
    pour pauseC de 0 à 5
        film ajoute film[longueur(film) - 1]
    fin pour
fin pour

@ Lancement de l'animation
animation(film, 200, 0)
Pas encore noté