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.

76 publications

Make7 Puzzle Hexa : Analyse technique complète d’un puzzle hexagonal à fusions en chaîne
Article

Make7 Puzzle Hexa

Analyse technique complète d’un puzzle hexagonal à fusions en chaîne

Lire l'articleReplier l'article

Make7 est un jeu de puzzle basé sur un pavage hexagonal irrégulier, où le joueur place des pièces composées de deux blocs. Le programme repose sur une architecture claire : géométrie hexagonale, système de tirage probabiliste, algorithme de propagation, gestion d’état, et pipeline de rendu graphique.

Voici une analyse détaillée de sa conception.

Structure du plateau : un hexagone irrégulier mais régulier dans sa logique

Le plateau est défini par :

  • larg = [3, 4, 5, 4, 3]
  • NBRANG = 4

Chaque rangée possède un nombre différent d’alvéoles, ce qui crée un hexagone parfait.
Le programme calcule la position de chaque alvéole via deux fonctions clés :

abscisseDe(rang, col, nbCases)

Calcule la position horizontale en centrant chaque rangée sur CENX.

ordonneeDe(rang)

Décale verticalement chaque rangée selon PASY.

Ces deux fonctions sont la base de toute la géométrie du jeu :
elles permettent de dessiner, détecter les clics, trouver les voisins et propager les fusions.

Tirage des pièces : distribution pondérée

Le tirage utilise une distribution volontairement biaisée :

  • 1 sort 40 % du temps
  • 2 sort 32 %
  • 3 sort 18 %
  • 4 sort 10 %

Une pièce sur quatre contient deux fois le même numéro.

Ce système est reproduit pour la pièce suivante (sA, sB), ce qui crée un flux constant de pièces faibles, mais avec des opportunités de monter en puissance.

Rendu graphique : pipeline de dessin

Le jeu utilise une série de primitives graphiques :

  • dessinerHexa pour les hexagones pleins
  • dessinerCaseVide pour les alvéoles libres
  • dessinerBloc pour les blocs numérotés
  • dessinerPiece pour la pièce en main
  • dessinerPoubelle pour l’UI
  • dessinerPieceOr pour les pièces (monnaie)

Chaque bloc possède :

  • une couleur claire (couleurDeBloc)
  • une couleur sombre (couleurSombreDeBloc)
  • un numéro ou un rendu spécial (bombe)

Le rendu est propre, lisible, et entièrement vectoriel.

Placement des pièces : gestion des 6 directions hexagonales

La pièce comporte deux blocs.
Le premier se place sur l’alvéole cliquée, le second dans une direction parmi six orientations :

0 : droite
1 : bas-droite
2 : bas-gauche
3 : gauche
4 : haut-gauche
5 : haut-droite

Le calcul du voisin (l2, c2) dépend :

  • de la direction,
  • de la longueur de la rangée d’arrivée,
  • du fait que les rangées plus longues commencent plus à gauche.

Cette partie du code est l’une des plus techniques : elle gère un pavage hexagonal irrégulier sans jamais perdre la cohérence géométrique.

Algorithme de fusion : propagation hexagonale

Une fois la pièce posée, le programme recherche des groupes de blocs identiques.

Étape 1 : marquage
Un tableau parallèle marque[][] est utilisé pour marquer les blocs connectés.

Étape 2 : propagation
Pour chaque bloc marqué, le programme teste les six voisins, en recalculant les décalages selon la géométrie du plateau.

Étape 3 : fusion
Si un groupe contient = 3 blocs :

les blocs disparaissent,
un bloc supérieur apparaît à un emplacement choisi,

le score augmente selon :
val × nbGroupe × 10 × (chaine + 1),

des pièces sont ajoutées.

Cas spéciaux
Trois 7 ? une BOMBE

Trois bombes ? explosion totale du plateau
? tous les blocs sont supprimés
? gain massif
? reset des coordonnées de fusion

Réactions en chaîne
Le programme relance la recherche tant qu’une fusion est possible.
Cela permet des cascades spectaculaires.

Détection de fin de partie

Après chaque pose, le programme vérifie s’il reste au moins deux alvéoles voisines libres.

Il teste chaque alvéole vide, puis ses six voisins.
Si aucune paire libre n’existe :

  • finPartie = vrai
  • affichage de l’écran de fin
  • mise à jour du record

Gestion de l’état du jeu

Le jeu maintient un ensemble de variables globales :

  • plateau[][] : état du plateau
  • marque[][] : marquage pour les fusions
  • valA, valB : pièce en main
  • sA, sB : pièce suivante
  • orient : orientation de la pièce
  • pieces : monnaie
  • score, record
  • meilleurBloc : plus haut bloc obtenu
  • derLig, derCol : dernière pose (pour fusion)
  • finPartie, aide : états d’interface

Cette structure est simple mais efficace :

  • elle permet de gérer le jeu sans objets complexes ni classes.

Conclusion

Make7 est un excellent exemple de jeu de puzzle hexagonal :

  • géométrie maîtrisée,
  • algorithme de propagation robuste,
  • gestion d’état claire,
  • interface vectorielle propre,
  • système de fusion riche,
  • réactions en chaîne,
  • mécaniques spéciales (bombes, explosion totale).

Le code est dense mais bien structuré, et démontre une compréhension fine des pavages hexagonaux et des mécaniques de puzzle modernes.

Programme :

@ CONFIGURATION
graph est une toile
dimension(graph, 400, 550)

NHEX est un nombre
NHEX vaut 2
NBRANG est un nombre
NBRANG vaut 4

// Géométrie d un hexagone à pointe en haut
RAY est un nombre
DEMIL est un nombre
PASX est un nombre
PASY est un nombre
CENX est un nombre
CENY est un nombre
RAY vaut 38
DEMIL vaut 33
PASX vaut 66
PASY vaut 57
CENX vaut 200
CENY vaut 216

COUT_POUBELLE est un nombre
COUT_POUBELLE vaut 10

// 8 est la bombe : trois bombes réunies font sauter tout le plateau
BOMBE est un nombre
BOMBE vaut 8

score est un nombre
score vaut 0
record est un nombre
record vaut 0
pieces est un nombre
pieces vaut 20
meilleurBloc est un nombre
meilleurBloc vaut 1

// La pièce en main : deux blocs et une direction de 0 à 5
valA est un nombre
valB est un nombre
sA est un nombre
sB est un nombre
orient est un nombre
orient vaut 0

derLig est un nombre
derCol est un nombre
der2Lig est un nombre
der2Col est un nombre
derLig vaut - 1
derCol vaut - 1
der2Lig vaut - 1
der2Col vaut - 1

finPartie est un booléen
finPartie vaut faux
aide est un booléen
aide vaut faux

message est un texte
message vaut "Clique la pièce pour la tourner, une alvéole pour la poser"

@ LE PLATEAU EN NID D ABEILLE
// larg[i] donne le nombre d alvéoles de la rangée i.
// Les rangées n ont pas toutes la même longueur : c est ce qui
// donne au plateau sa forme hexagonale.
larg est un tableau
plateau est un tableau
marque est un tableau
larg ajoute 3
larg ajoute 4
larg ajoute 5
larg ajoute 4
larg ajoute 3
pour i de 0 à NBRANG
    ligG est un tableau
    ligM est un tableau
    pour j de 0 à larg[i] - 1
        ligG ajoute 0
        ligM ajoute faux
    fin pour
    plateau ajoute ligG
    marque ajoute ligM
fin pour

@ TIRAGE DES PIÈCES
// Les petits numéros sortent bien plus souvent que les grands,
// et une pièce sur quatre porte deux fois le même numéro.
tirage est un nombre
tirage vaut hasard(1, 100)
si tirage <= 40 alors
    valA vaut 1
sinon si tirage <= 72 alors
    valA vaut 2
sinon si tirage <= 90 alors
    valA vaut 3
sinon
    valA vaut 4
fin si
si hasard(1, 100) <= 25 alors
    valB vaut valA
sinon
    tirage vaut hasard(1, 100)
    si tirage <= 40 alors
        valB vaut 1
    sinon si tirage <= 72 alors
        valB vaut 2
    sinon si tirage <= 90 alors
        valB vaut 3
    sinon
        valB vaut 4
    fin si
fin si

tirage vaut hasard(1, 100)
si tirage <= 40 alors
    sA vaut 1
sinon si tirage <= 72 alors
    sA vaut 2
sinon si tirage <= 90 alors
    sA vaut 3
sinon
    sA vaut 4
fin si
si hasard(1, 100) <= 25 alors
    sB vaut sA
sinon
    tirage vaut hasard(1, 100)
    si tirage <= 40 alors
        sB vaut 1
    sinon si tirage <= 72 alors
        sB vaut 2
    sinon si tirage <= 90 alors
        sB vaut 3
    sinon
        sB vaut 4
    fin si
fin si

@ LES COULEURS DES BLOCS
fonction couleurDeBloc(val)
    si val = 1 alors
        retourne #A855F7
    fin si
    si val = 2 alors
        retourne #FF8A2B
    fin si
    si val = 3 alors
        retourne #FF3B6B
    fin si
    si val = 4 alors
        retourne #2D9CFF
    fin si
    si val = 5 alors
        retourne #12C7B0
    fin si
    si val = 6 alors
        retourne #FFC93C
    fin si
    si val = 7 alors
        retourne #7CE04A
    fin si
    retourne #4B4C52
fin fonction

fonction couleurSombreDeBloc(val)
    si val = 1 alors
        retourne #7E3BC4
    fin si
    si val = 2 alors
        retourne #CC6A15
    fin si
    si val = 3 alors
        retourne #C92B52
    fin si
    si val = 4 alors
        retourne #1B76CC
    fin si
    si val = 5 alors
        retourne #0C9285
    fin si
    si val = 6 alors
        retourne #CC9A18
    fin si
    si val = 7 alors
        retourne #5AA832
    fin si
    retourne #2E2F34
fin fonction

@ L ABSCISSE D UNE ALVÉOLE
// Chaque rangée est centrée sur CENX : sa première alvéole se
// décale donc de la moitié de sa longueur. C est cette formule,
// et elle seule, qui donne au plateau sa forme d hexagone.
fonction abscisseDe(rang, col, nbCases)
    retourne CENX - (nbCases - 1) * PASX / 2 + col * PASX
fin fonction

fonction ordonneeDe(rang)
    retourne CENY + (rang - NHEX) * PASY
fin fonction

@ UN HEXAGONE PLEIN
procédure dessinerHexa(t, cx, cy, dl, ha, coul)
    p est un tableau
    p ajoute cx + dl
    p ajoute cy - ha / 2
    p ajoute cx + dl
    p ajoute cy + ha / 2
    p ajoute cx
    p ajoute cy + ha
    p ajoute cx - dl
    p ajoute cy + ha / 2
    p ajoute cx - dl
    p ajoute cy - ha / 2
    p ajoute cx
    p ajoute cy - ha
    polygone(t, p, coul)
fin procédure

@ UNE ALVÉOLE VIDE
procédure dessinerCaseVide(t, cx, cy)
    appelle dessinerHexa(t, cx, cy, DEMIL - 4, RAY - 4, #3A3B43)
fin procédure

@ UN BLOC NUMÉROTÉ
// Un hexagone sombre décalé vers le bas sert de socle : c est ce
// qui donne l épaisseur. Le numéro est écrit par-dessus.
procédure dessinerBloc(t, cx, cy, dl, ha, val, taillePolice)
    appelle dessinerHexa(t, cx, cy + 4, dl, ha, couleurSombreDeBloc(val))
    appelle dessinerHexa(t, cx, cy, dl, ha, couleurDeBloc(val))

    si val = BOMBE alors
        cercle(t, cx, cy + 2, ha * 55 / 100, #1A1B20)
        rectangle_arrondi(t, cx + 2, cy - ha * 75 / 100, 4, ha * 40 / 100, 2, #8A6A3A)
        cercle(t, cx + 5, cy - ha * 85 / 100, ha * 12 / 100, #FF9A2B)
        cercle(t, cx - ha * 20 / 100, cy - ha * 10 / 100, ha * 12 / 100, rgba(255, 255, 255, 0.5))
    sinon
        label(t, cx - taillePolice * 30 / 100, cy + taillePolice * 35 / 100, "" + val, #1A1B20, taillePolice)
    fin si
fin procédure

@ LA PIÈCE À DEUX BLOCS
// En pixels, les six directions ont toujours le même décalage,
// quelle que soit la rangée.
procédure dessinerPiece(t, cx, cy, dl, ha, v1, v2, dir, taillePolice)
    // JEU : quelques pixels d écart pour que les deux hexagones de
    // la pièce ne se touchent pas et restent distincts à l oeil.
    jeu est un nombre
    jeu vaut 5
    ex est un nombre
    ey est un nombre
    si dir = 0 alors
        ex vaut dl * 2 + jeu
        ey vaut 0
    sinon si dir = 1 alors
        ex vaut dl + jeu / 2
        ey vaut ha * 3 / 2 + jeu
    sinon si dir = 2 alors
        ex vaut 0 - dl - jeu / 2
        ey vaut ha * 3 / 2 + jeu
    sinon si dir = 3 alors
        ex vaut 0 - dl * 2 - jeu
        ey vaut 0
    sinon si dir = 4 alors
        ex vaut 0 - dl - jeu / 2
        ey vaut 0 - ha * 3 / 2 - jeu
    sinon
        ex vaut dl + jeu / 2
        ey vaut 0 - ha * 3 / 2 - jeu
    fin si
    appelle dessinerBloc(t, cx, cy, dl, ha, v1, taillePolice)
    appelle dessinerBloc(t, cx + ex, cy + ey, dl, ha, v2, taillePolice)
fin procédure

@ UNE PIÈCE D OR
procédure dessinerPieceOr(t, cx, cy, r)
    cercle(t, cx, cy + 1, r, #C99A19)
    cercle(t, cx, cy, r, #FFC93C)
    cercle(t, cx, cy, r * 3 / 5, #FFE9A8)
fin procédure

@ LA POUBELLE
procédure dessinerPoubelle(t, x, y, actif)
    fond est un texte
    cIcone est un texte
    si actif = vrai alors
        fond vaut #55565C
        cIcone vaut #E8EAF0
    sinon
        fond vaut #414248
        cIcone vaut #6B6D75
    fin si
    rectangle_arrondi(t, x, y + 2, 44, 48, 11, rgba(0, 0, 0, 0.3))
    rectangle_arrondi(t, x, y, 44, 48, 11, fond)
    rectangle_arrondi(t, x + 8, y + 11, 28, 4, 2, cIcone)
    rectangle_arrondi(t, x + 17, y + 7, 10, 4, 2, cIcone)
    rectangle_arrondi(t, x + 11, y + 17, 22, 23, 4, cIcone)
    rectangle(t, x + 17, y + 22, 3, 13, fond)
    rectangle(t, x + 25, y + 22, 3, 13, fond)
fin procédure

@ BOUCLE PRINCIPALE

tant que vrai

    // ================= AFFICHAGE =================
    effacer(graph)
    contour(graph, #0)
    remplir(graph, #2E2F34)

    // ---------- BANDEAU DU HAUT ----------
    rectangle_arrondi(graph, 12, 8, 376, 46, 12, #35363C)
    appelle dessinerPieceOr(graph, 32, 30, 10)
    label(graph, 46, 36, "" + pieces, #FFFFFF, 14)
    label(graph, 150, 38, "" + score, #FF9A2B, 22)
    label(graph, 252, 36, "??", #FFFFFF, 14)
    label(graph, 274, 37, "" + record, #FF9A2B, 17)

    rectangle_arrondi(graph, 346, 12, 32, 32, 9, #4B4C52)
    rectangle_arrondi(graph, 354, 18, 5, 19, 2, #E8EAF0)
    rectangle_arrondi(graph, 365, 18, 5, 19, 2, #E8EAF0)

    // ---------- LE NID D ABEILLE ----------
    rectangle_arrondi(graph, 12, 62, 376, 308, 16, #35363C)
    pour i de 0 à NBRANG
        pour j de 0 à larg[i] - 1
            cxk est un nombre
            cyk est un nombre
            cxk vaut abscisseDe(i, j, larg[i])
            cyk vaut ordonneeDe(i)
            si plateau[i][j] = 0 alors
                appelle dessinerCaseVide(graph, cxk, cyk)
            sinon
                appelle dessinerBloc(graph, cxk, cyk, DEMIL - 4, RAY - 4, plateau[i][j], 30)
            fin si
        fin pour
    fin pour

    label(graph, 20, 384, message, #8A8C94, 10)

    // ---------- ZONE DU BAS ----------
    rectangle_arrondi(graph, 12, 392, 376, 148, 14, #35363C)

    actifP est un booléen
    actifP vaut faux
    si pieces >= COUT_POUBELLE alors
        actifP vaut vrai
    fin si
    appelle dessinerPoubelle(graph, 26, 424, actifP)
    appelle dessinerPieceOr(graph, 32, 490, 7)
    label(graph, 44, 495, "" + COUT_POUBELLE, #B9BCC4, 11)

    // La pièce en main : un clic dessus la fait tourner
    label(graph, 112, 414, "clique la pièce pour la tourner", #6B6D75, 9)
    appelle dessinerPiece(graph, 178, 468, 20, 23, valA, valB, orient, 20)

    // La pièce suivante, en petit
    label(graph, 320, 424, "suiv.", #8A8C94, 9)
    appelle dessinerPiece(graph, 332, 456, 12, 13, sA, sB, 1, 12)

    // ---------- SURCOUCHES ----------
    si aide = vrai alors
        rectangle(graph, 0, 0, 400, 550, rgba(0, 0, 0, 0.72))
        rectangle_arrondi(graph, 26, 96, 348, 358, 16, #4B4C52)
        label(graph, 54, 142, "COMMENT JOUER", #FFFFFF, 20)
        label(graph, 54, 182, "Chaque pièce porte deux blocs numérotés.", #D6D8DE, 12)
        label(graph, 54, 204, "Clique la pièce en bas pour la faire tourner :", #D6D8DE, 12)
        label(graph, 54, 226, "un nid d abeille offre six directions.", #D6D8DE, 12)
        label(graph, 54, 262, "Clique une alvéole libre pour la poser. Le", #D6D8DE, 12)
        label(graph, 54, 284, "second bloc va dans la direction choisie.", #D6D8DE, 12)
        label(graph, 54, 320, "Trois blocs identiques qui se touchent", #D6D8DE, 12)
        label(graph, 54, 342, "fusionnent en un numéro supérieur.", #D6D8DE, 12)
        label(graph, 54, 378, "Trois 7 donnent une BOMBE.", #FF9A2B, 13)
        label(graph, 54, 400, "Trois bombes font sauter tout le plateau !", #FF3B6B, 13)
        label(graph, 54, 434, "Clique pour fermer", #FFC93C, 13)
    fin si

    si finPartie = vrai alors
        rectangle(graph, 0, 0, 400, 550, rgba(0, 0, 0, 0.78))
        rectangle_arrondi(graph, 40, 160, 320, 230, 16, #4B4C52)
        label(graph, 86, 212, "PLUS DE PLACE !", #FFFFFF, 24)
        label(graph, 86, 256, "Score : " + score, #FF9A2B, 20)
        label(graph, 86, 300, "Meilleur bloc :", #D6D8DE, 14)
        appelle dessinerBloc(graph, 276, 292, 20, 23, meilleurBloc, 20)
        label(graph, 86, 356, "Clique pour rejouer", #FFC93C, 15)
    fin si

    affiche graph

    // ================= LA SOURIS =================
    xc est un nombre
    yc est un nombre
    cliquer graph dans xc, yc

    si finPartie = vrai alors
        pour i de 0 à NBRANG
            pour j de 0 à larg[i] - 1
                plateau[i][j] vaut 0
            fin pour
        fin pour
        score vaut 0
        pieces vaut 20
        meilleurBloc vaut 1
        orient vaut 0
        derLig vaut - 1
        derCol vaut - 1
        finPartie vaut faux
        message vaut "Clique la pièce pour la tourner, une alvéole pour la poser"

    sinon si aide = vrai alors
        aide vaut faux

    sinon si xc >= 346 et xc <= 378 et yc >= 12 et yc <= 44 alors
        aide vaut vrai

    sinon si xc >= 110 et xc <= 258 et yc >= 400 et yc <= 536 alors
        // ---------- TOURNER LA PIÈCE ----------
        orient vaut orient + 1
        si orient > 5 alors
            orient vaut 0
        fin si
        message vaut "Direction " + (orient + 1) + " sur 6"

    sinon si xc >= 26 et xc <= 70 et yc >= 424 et yc <= 472 alors
        // ---------- JETER LA PIÈCE ----------
        si pieces >= COUT_POUBELLE alors
            pieces vaut pieces - COUT_POUBELLE
            valA vaut sA
            valB vaut sB
            tirage vaut hasard(1, 100)
            si tirage <= 40 alors
                sA vaut 1
            sinon si tirage <= 72 alors
                sA vaut 2
            sinon si tirage <= 90 alors
                sA vaut 3
            sinon
                sA vaut 4
            fin si
            si hasard(1, 100) <= 25 alors
                sB vaut sA
            sinon
                tirage vaut hasard(1, 100)
                si tirage <= 40 alors
                    sB vaut 1
                sinon si tirage <= 72 alors
                    sB vaut 2
                sinon si tirage <= 90 alors
                    sB vaut 3
                sinon
                    sB vaut 4
                fin si
            fin si
            message vaut "Pièce jetée : - " + COUT_POUBELLE + " pièces"
        sinon
            message vaut "Il te faut " + COUT_POUBELLE + " pièces pour jeter"
        fin si

    sinon si yc >= 62 et yc <= 370 alors
        // ---------- QUELLE ALVÉOLE A ÉTÉ CLIQUÉE ? ----------
        // On cherche le centre le plus proche : sur un pavage
        // hexagonal, c est exact et bien plus simple qu un calcul.
        l1 est un nombre
        c1 est un nombre
        l1 vaut - 1
        c1 vaut - 1
        meilleureDist est un nombre
        meilleureDist vaut 9999

        pour i de 0 à NBRANG
            pour j de 0 à larg[i] - 1
                dist est un nombre
                dist vaut hypotenuse(xc - abscisseDe(i, j, larg[i]), yc - ordonneeDe(i))
                si dist < meilleureDist alors
                    meilleureDist vaut dist
                    l1 vaut i
                    c1 vaut j
                fin si
            fin pour
        fin pour

        si meilleureDist > RAY alors
            message vaut "Clique bien au centre d une alvéole"
        sinon
            // ---------- L ALVÉOLE VOISINE, SELON LA DIRECTION ----------
            // Vers le bas, la rangée suivante est plus longue tant qu on
            // n a pas dépassé le milieu ; ensuite elle est plus courte.
            // Le décalage des colonnes en dépend.
            dLi est un nombre
            dCo est un nombre

            si orient = 0 alors
                dLi vaut 0
                dCo vaut 1
            sinon si orient = 3 alors
                dLi vaut 0
                dCo vaut 0 - 1
            sinon si orient = 1 ou orient = 2 alors
                // Vers le bas. Le décalage dépend de la longueur de la
                // rangée d arrivée, pas de sa position : une rangée plus
                // longue commence une demi-alvéole plus à gauche.
                dLi vaut 1
                plusLongue est un booléen
                plusLongue vaut faux
                si l1 + 1 <= NBRANG alors
                    si larg[l1 + 1] > larg[l1] alors
                        plusLongue vaut vrai
                    fin si
                fin si
                si orient = 1 alors
                    si plusLongue = vrai alors
                        dCo vaut 1
                    sinon
                        dCo vaut 0
                    fin si
                sinon
                    si plusLongue = vrai alors
                        dCo vaut 0
                    sinon
                        dCo vaut 0 - 1
                    fin si
                fin si
            sinon
                // Vers le haut, même raisonnement
                dLi vaut 0 - 1
                plusLongue vaut faux
                si l1 - 1 >= 0 alors
                    si larg[l1 - 1] > larg[l1] alors
                        plusLongue vaut vrai
                    fin si
                fin si
                si orient = 5 alors
                    si plusLongue = vrai alors
                        dCo vaut 1
                    sinon
                        dCo vaut 0
                    fin si
                sinon
                    si plusLongue = vrai alors
                        dCo vaut 0
                    sinon
                        dCo vaut 0 - 1
                    fin si
                fin si
            fin si

            l2 est un nombre
            c2 est un nombre
            l2 vaut l1 + dLi
            c2 vaut c1 + dCo

            horsPlateau est un booléen
            horsPlateau vaut faux
            si l2 < 0 ou l2 > NBRANG alors
                horsPlateau vaut vrai
            sinon si c2 < 0 ou c2 > larg[l2] - 1 alors
                horsPlateau vaut vrai
            fin si

            si horsPlateau = vrai alors
                message vaut "La pièce sort du plateau : tourne-la"
            sinon si plateau[l1][c1] <> 0 ou plateau[l2][c2] <> 0 alors
                message vaut "Il faut deux alvéoles libres côte à côte"
            sinon
                plateau[l1][c1] vaut valA
                plateau[l2][c2] vaut valB
                derLig vaut l1
                derCol vaut c1
                der2Lig vaut l2
                der2Col vaut c2
                message vaut "Pièce posée"

                valA vaut sA
                valB vaut sB
                tirage vaut hasard(1, 100)
                si tirage <= 40 alors
                    sA vaut 1
                sinon si tirage <= 72 alors
                    sA vaut 2
                sinon si tirage <= 90 alors
                    sA vaut 3
                sinon
                    sA vaut 4
                fin si
                si hasard(1, 100) <= 25 alors
                    sB vaut sA
                sinon
                    tirage vaut hasard(1, 100)
                    si tirage <= 40 alors
                        sB vaut 1
                    sinon si tirage <= 72 alors
                        sB vaut 2
                    sinon si tirage <= 90 alors
                        sB vaut 3
                    sinon
                        sB vaut 4
                    fin si
                fin si

                // ---------- FUSIONS EN CHAÎNE ----------
                chaine est un nombre
                chaine vaut 0
                encore est un booléen
                encore vaut vrai

                tant que encore = vrai
                    encore vaut faux

                    pour i de 0 à NBRANG
                        pour j de 0 à larg[i] - 1
                            si plateau[i][j] > 0 et encore = faux alors
                                val est un nombre
                                val vaut plateau[i][j]

                                pour r de 0 à NBRANG
                                    pour c de 0 à larg[r] - 1
                                        marque[r][c] vaut faux
                                    fin pour
                                fin pour
                                marque[i][j] vaut vrai

                                propage est un booléen
                                propage vaut vrai
                                tant que propage = vrai
                                    propage vaut faux
                                    pour r de 0 à NBRANG
                                        pour c de 0 à larg[r] - 1
                                            si marque[r][c] = vrai alors
                                                // Les six voisines de l alvéole
                                                pour d de 0 à 5
                                                    eLi est un nombre
                                                    eCo est un nombre
                                                    si d = 0 alors
                                                        eLi vaut 0
                                                        eCo vaut 1
                                                    sinon si d = 3 alors
                                                        eLi vaut 0
                                                        eCo vaut 0 - 1
                                                    sinon
                                                        // Bas ou haut : le décalage dépend de la
                                                        // longueur de la rangée voisine.
                                                        longVoisine est un booléen
                                                        longVoisine vaut faux
                                                        si d = 1 ou d = 2 alors
                                                            eLi vaut 1
                                                        sinon
                                                            eLi vaut 0 - 1
                                                        fin si
                                                        si r + eLi >= 0 et r + eLi <= NBRANG alors
                                                            si larg[r + eLi] > larg[r] alors
                                                                longVoisine vaut vrai
                                                            fin si
                                                        fin si
                                                        si d = 1 ou d = 5 alors
                                                            si longVoisine = vrai alors
                                                                eCo vaut 1
                                                            sinon
                                                                eCo vaut 0
                                                            fin si
                                                        sinon
                                                            si longVoisine = vrai alors
                                                                eCo vaut 0
                                                            sinon
                                                                eCo vaut 0 - 1
                                                            fin si
                                                        fin si
                                                    fin si

                                                    vr est un nombre
                                                    vc est un nombre
                                                    vr vaut r + eLi
                                                    vc vaut c + eCo
                                                    si vr >= 0 et vr <= NBRANG alors
                                                        si vc >= 0 et vc <= larg[vr] - 1 alors
                                                            si plateau[vr][vc] = val et marque[vr][vc] = faux alors
                                                                marque[vr][vc] vaut vrai
                                                                propage vaut vrai
                                                            fin si
                                                        fin si
                                                    fin si
                                                fin pour
                                            fin si
                                        fin pour
                                    fin pour
                                fin tant que

                                nbGroupe est un nombre
                                nbGroupe vaut 0
                                ligNaiss est un nombre
                                colNaiss est un nombre
                                ligNaiss vaut i
                                colNaiss vaut j
                                pour r de 0 à NBRANG
                                    pour c de 0 à larg[r] - 1
                                        si marque[r][c] = vrai alors
                                            nbGroupe ajoute 1
                                        fin si
                                    fin pour
                                fin pour

                                si der2Lig >= 0 alors
                                    si marque[der2Lig][der2Col] = vrai alors
                                        ligNaiss vaut der2Lig
                                        colNaiss vaut der2Col
                                    fin si
                                fin si
                                si derLig >= 0 alors
                                    si marque[derLig][derCol] = vrai alors
                                        ligNaiss vaut derLig
                                        colNaiss vaut derCol
                                    fin si
                                fin si

                                si nbGroupe >= 3 alors
                                    pour r de 0 à NBRANG
                                        pour c de 0 à larg[r] - 1
                                            si marque[r][c] = vrai alors
                                                plateau[r][c] vaut 0
                                            fin si
                                        fin pour
                                    fin pour

                                    gain est un nombre
                                    gain vaut val * nbGroupe * 10 * (chaine + 1)

                                    si val = BOMBE alors
                                        // ===== TROIS BOMBES : TOUT SAUTE =====
                                        nbSautes est un nombre
                                        nbSautes vaut 0
                                        pour r de 0 à NBRANG
                                            pour c de 0 à larg[r] - 1
                                                si plateau[r][c] > 0 alors
                                                    nbSautes ajoute 1
                                                    plateau[r][c] vaut 0
                                                fin si
                                            fin pour
                                        fin pour
                                        gain vaut 500 + nbSautes * 50
                                        pieces ajoute 10
                                        derLig vaut - 1
                                        derCol vaut - 1
                                        der2Lig vaut - 1
                                        der2Col vaut - 1
                                        message vaut "EXPLOSION TOTALE ! " + nbSautes + " blocs pulvérisés, + " + gain

                                    sinon si val = 7 alors
                                        // ===== TROIS 7 : UNE BOMBE NAÎT =====
                                        plateau[ligNaiss][colNaiss] vaut BOMBE
                                        derLig vaut ligNaiss
                                        derCol vaut colNaiss
                                        der2Lig vaut - 1
                                        der2Col vaut - 1
                                        meilleurBloc vaut BOMBE
                                        pieces ajoute 5
                                        gain vaut gain * 2
                                        message vaut "UNE BOMBE ! Réunis-en trois pour tout faire sauter"

                                    sinon
                                        plateau[ligNaiss][colNaiss] vaut val + 1
                                        derLig vaut ligNaiss
                                        derCol vaut colNaiss
                                        der2Lig vaut - 1
                                        der2Col vaut - 1
                                        si val + 1 > meilleurBloc alors
                                            meilleurBloc vaut val + 1
                                        fin si
                                        si val >= 4 alors
                                            pieces ajoute 3
                                        sinon
                                            pieces ajoute 1
                                        fin si
                                        si chaine > 0 alors
                                            message vaut "Réaction en chaîne x" + (chaine + 1) + " : + " + gain
                                        sinon
                                            message vaut "Fusion de " + nbGroupe + " blocs : + " + gain
                                        fin si
                                    fin si

                                    score ajoute gain
                                    chaine ajoute 1
                                    encore vaut vrai
                                fin si
                            fin si
                        fin pour
                    fin pour
                fin tant que

                // ---------- RESTE-T-IL DEUX ALVÉOLES VOISINES LIBRES ? ----------
                placeRestante est un booléen
                placeRestante vaut faux
                pour i de 0 à NBRANG
                    pour j de 0 à larg[i] - 1
                        si plateau[i][j] = 0 alors
                            pour d de 0 à 5
                                fLi est un nombre
                                fCo est un nombre
                                si d = 0 alors
                                    fLi vaut 0
                                    fCo vaut 1
                                sinon si d = 3 alors
                                    fLi vaut 0
                                    fCo vaut 0 - 1
                                sinon
                                    longV2 est un booléen
                                    longV2 vaut faux
                                    si d = 1 ou d = 2 alors
                                        fLi vaut 1
                                    sinon
                                        fLi vaut 0 - 1
                                    fin si
                                    si i + fLi >= 0 et i + fLi <= NBRANG alors
                                        si larg[i + fLi] > larg[i] alors
                                            longV2 vaut vrai
                                        fin si
                                    fin si
                                    si d = 1 ou d = 5 alors
                                        si longV2 = vrai alors
                                            fCo vaut 1
                                        sinon
                                            fCo vaut 0
                                        fin si
                                    sinon
                                        si longV2 = vrai alors
                                            fCo vaut 0
                                        sinon
                                            fCo vaut 0 - 1
                                        fin si
                                    fin si
                                fin si
                                gr est un nombre
                                gc est un nombre
                                gr vaut i + fLi
                                gc vaut j + fCo
                                si gr >= 0 et gr <= NBRANG alors
                                    si gc >= 0 et gc <= larg[gr] - 1 alors
                                        si plateau[gr][gc] = 0 alors
                                            placeRestante vaut vrai
                                        fin si
                                    fin si
                                fin si
                            fin pour
                        fin si
                    fin pour
                fin pour

                si placeRestante = faux alors
                    finPartie vaut vrai
                    si score > record alors
                        record vaut score
                    fin si
                fin si
            fin si
        fin si
    fin si
fin tant que
5,0 / 5 (1 avis)
Créer un UNO jouable : Analyse complète d’un programme CoddyRun
Article

Créer un UNO jouable

Analyse complète d’un programme CoddyRun

Lire l'articleReplier l'article

Ce programme est une implémentation complète du jeu UNO, conçue dans l’environnement CoddyRun. Il combine logique de jeu, gestion d’interface, animations, et IA, pour offrir une expérience fluide et visuellement aboutie.

Voici une plongée détaillée dans son fonctionnement.

Configuration et variables globales

Le programme commence par définir les éléments fondamentaux du jeu :

Dimensions de la fenêtre :

LARG vaut 640
HAUT vaut 440

Couleurs des cartes (Rouge, Jaune, Vert, Bleu, Noir)

Valeurs des cartes : chiffres, +2, inversion, passe, joker, +4

Noms longs pour l’affichage :

  • « Inversion », « Passe », « Joker », « +4 »

Plusieurs tableaux sont ensuite créés pour stocker :

  • la pioche,
  • la main du joueur,
  • la main de l’IA,
  • la défausse,
  • les messages affichés à l’écran.

Ces structures constituent la base de données du jeu.

Création et mélange de la pioche

La procédure creerPioche() construit un paquet complet :

  • 4 couleurs × 13 valeurs (avec duplication des cartes non-zéro, comme dans le vrai UNO)
  • 4 jokers et 4 +4

Le mélange est effectué par 300 échanges aléatoires, garantissant une pioche imprévisible.

pa vaut hasard(0, nbCartes - 1)

Cette méthode simple mais efficace assure un brassage réaliste.

Mécaniques de base : piocher et vérifier la jouabilité

La fonction piocher(qui) ajoute une carte à la main du joueur ou de l’IA.
La fonction jouable(kc, kv) détermine si une carte peut être posée :

  • même couleur
  • même valeur
  • joker ou +4 (toujours jouables)
si kv >= 13 alors retourne vrai

Cette logique respecte fidèlement les règles du UNO.

Interface graphique et rendu des cartes

La procédure carte() dessine chaque carte avec :

  • un fond arrondi
  • un symbole central
  • des labels de valeur
  • des couleurs dynamiques

Les cartes spéciales ont un rendu unique :

  • +2, inversion, passe, joker, +4.

Le programme utilise des effets visuels : ombres, contours, ellipses, dégradés, pour un rendu moderne et lisible.

La procédure dessinerSurToile() assemble l’ensemble :

  • interface du joueur
  • interface de l’IA
  • pioche
  • défausse
  • messages
  • cartes jouables mises en évidence

Animations : pose et pioche

Deux animations enrichissent l’expérience :

animerPose()

Déplace une carte de la main vers la défausse avec interpolation.

animerPioche()

Anime la carte tirée depuis la pioche vers la main du joueur ou de l’IA.

Ces animations rendent le jeu plus vivant et agréable.

Intelligence artificielle

L’IA suit une stratégie simple mais efficace :

Si le joueur est en danger (=2 cartes)

  • elle privilégie les cartes spéciales.

Sinon, elle joue une carte normale compatible.

  • Si elle joue un joker ou +4, elle choisit la couleur où elle possède le plus de cartes.
cptC[iaCoul[tc]] vaut cptC[iaCoul[tc]] + 1

Elle gère aussi les effets :

  • +2 ? le joueur pioche 2
  • +4 ? le joueur pioche 4
  • Passe / Inversion ? elle rejoue

L’IA peut enchaîner plusieurs tours si elle joue des cartes spéciales.

Boucle principale du jeu

La boucle :

  • Affiche l’état du jeu
  • Attend un clic du joueur
  • Détermine si le joueur joue une carte, pioche, ou quitte
  • Applique les règles
  • Vérifie les conditions de victoire

Le programme gère :

  • les erreurs de jeu
  • les cartes jouables mises en surbrillance
  • les messages dynamiques
  • la fin de partie (victoire, défaite, abandon)

Écran de fin animé

Une animation finale affiche le résultat :

  • « GAGNÉ ! Tu n'as plus de cartes ! »
  • « PERDU ! L'IA n'a plus de cartes. »

Avec un fondu et une boîte stylisée, l’expérience se termine proprement.

Conclusion

Ce programme UNO est une réalisation complète et soignée :

  • Logique fidèle au jeu original
  • Interface graphique moderne
  • Animations fluides
  • IA cohérente et réactive
  • Gestion robuste des interactions

Il constitue un excellent exemple de jeu CoddyRun mêlant programmation, design, et gameplay.

Programme :

@ CONFIGURATION ET VARIABLES
LARG est un nombre
LARG vaut 640
HAUT est un nombre
HAUT vaut 440

teintes est un tableau
teintes ajoute #ff4757
teintes ajoute #ffa502
teintes ajoute #2ed573
teintes ajoute #3742fa
teintes ajoute #2f3542

nomsCoul est un tableau
nomsCoul ajoute "Rouge"
nomsCoul ajoute "Jaune"
nomsCoul ajoute "Vert"
nomsCoul ajoute "Bleu"
nomsCoul ajoute "Noir"

nomsVal est un tableau
nomsVal ajoute "0"
nomsVal ajoute "1"
nomsVal ajoute "2"
nomsVal ajoute "3"
nomsVal ajoute "4"
nomsVal ajoute "5"
nomsVal ajoute "6"
nomsVal ajoute "7"
nomsVal ajoute "8"
nomsVal ajoute "9"
nomsVal ajoute "+2"
nomsVal ajoute "INV"
nomsVal ajoute "PAS"
nomsVal ajoute "JOK"
nomsVal ajoute "+4"

nomsLong est un tableau
nomsLong ajoute "0"
nomsLong ajoute "1"
nomsLong ajoute "2"
nomsLong ajoute "3"
nomsLong ajoute "4"
nomsLong ajoute "5"
nomsLong ajoute "6"
nomsLong ajoute "7"
nomsLong ajoute "8"
nomsLong ajoute "9"
nomsLong ajoute "+2"
nomsLong ajoute "Inversion"
nomsLong ajoute "Passe"
nomsLong ajoute "Joker"
nomsLong ajoute "+4"

piocheCoul est un tableau
piocheVal est un tableau
mainCoul est un tableau
mainVal est un tableau
iaCoul est un tableau
iaVal est un tableau

défausseCoul est un nombre
défausseVal est un nombre

msgTour est un texte
msgTour vaut "À toi ! Clique sur une carte dorée, ou sur la pioche."
msgFin est un texte
msgFin vaut ""


@ INITIALISATION DU JEU
procédure creerPioche()
    vide piocheCoul
    vide piocheVal

    pc est un nombre
    pv est un nombre
    pour pc de 0 a 3
        pour pv de 0 a 12
            piocheCoul ajoute pc
            piocheVal ajoute pv
            si pv > 0 alors
                piocheCoul ajoute pc
                piocheVal ajoute pv
            fin si
        fin pour
    fin pour

    jj est un nombre
    pour jj de 1 a 4
        piocheCoul ajoute 4
        piocheVal ajoute 13
        piocheCoul ajoute 4
        piocheVal ajoute 14
    fin pour

    nbCartes est un nombre
    nbCartes vaut longueur(piocheCoul)
    mel est un nombre
    pour mel de 1 a 300
        pa est un nombre
        pa vaut hasard(0, nbCartes - 1)
        pb est un nombre
        pb vaut hasard(0, nbCartes - 1)

        tmpC est un nombre
        tmpC vaut piocheCoul[pa]
        piocheCoul[pa] vaut piocheCoul[pb]
        piocheCoul[pb] vaut tmpC

        tmpV est un nombre
        tmpV vaut piocheVal[pa]
        piocheVal[pa] vaut piocheVal[pb]
        piocheVal[pb] vaut tmpV
    fin pour
fin procédure


@ MÉCANIQUES DE BASE
procédure piocher(qui)
    si longueur(piocheCoul) = 0 alors
        appelle creerPioche()
    fin si

    si longueur(piocheCoul) > 0 alors
        idx est un nombre
        idx vaut longueur(piocheCoul) - 1
        pc est un nombre
        pc vaut piocheCoul[idx]
        pv est un nombre
        pv vaut piocheVal[idx]
        supprime piocheCoul idx
        supprime piocheVal idx

        si qui = 1 alors
            mainCoul ajoute pc
            mainVal ajoute pv
        sinon
            iaCoul ajoute pc
            iaVal ajoute pv
        fin si
    fin si
fin procédure

fonction jouable(kc, kv)
    si kv >= 13 alors
        retourne vrai
    fin si
    si kc = défausseCoul alors
        retourne vrai
    fin si
    si kv = défausseVal alors
        retourne vrai
    fin si
    retourne faux
fin fonction

fonction espacementMain()
    n est un nombre
    n vaut longueur(mainCoul)
    e est un nombre
    e vaut 40
    si n > 1 alors
        si (n - 1) * 40 > LARG - 94 alors
            e vaut (LARG - 94) / (n - 1)
        fin si
    fin si
    retourne e
fin fonction


@ DESSIN ET INTERFACE
procédure carte(t, x, y, kc, kv, visible)
    rectangle_arrondi(t, x + 4, y + 6, 54, 78, 10, rgba(0, 0, 0, 0.22))
    rectangle_arrondi(t, x + 2, y + 3, 54, 78, 10, rgba(0, 0, 0, 0.28))

    si visible alors
        rectangle_arrondi(t, x, y, 54, 78, 10, #ffffff)
        rectangle_arrondi(t, x + 3, y + 3, 48, 72, 8, teintes[kc])
        contour(t, rgba(255, 255, 255, 0.35), 1)

        ellipse(t, x + 27, y + 39, 21, 14, rgba(255, 255, 255, 0.9))
        ellipse(t, x + 27, y + 39, 19, 12, teintes[kc])
        ellipse(t, x + 27, y + 39, 17, 10, #ffffff)

        si kv < 10 alors
            label(t, x + 21, y + 46, nomsVal[kv], teintes[kc], 21)
            label(t, x + 6, y + 16, nomsVal[kv], #ffffff, 11)
            label(t, x + 42, y + 72, nomsVal[kv], #ffffff, 11)

        sinon si kv = 10 alors
            rectangle_arrondi(t, x + 17, y + 31, 10, 15, 3, teintes[kc])
            contour(t, #ffffff, 1)
            rectangle_arrondi(t, x + 26, y + 35, 10, 15, 3, #343a40)
            contour(t, #ffffff, 1)
            label(t, x + 6, y + 16, "+2", #ffffff, 11)

        sinon si kv = 11 alors
            ligne(t, x + 17, y + 34, x + 37, y + 34, teintes[kc], 3)
            polygone(t, [x + 37, y + 34, x + 31, y + 30, x + 31, y + 38], teintes[kc])
            ligne(t, x + 17, y + 44, x + 37, y + 44, teintes[kc], 3)
            polygone(t, [x + 17, y + 44, x + 23, y + 40, x + 23, y + 48], teintes[kc])
            label(t, x + 6, y + 16, "INV", #ffffff, 9)

        sinon si kv = 12 alors
            cercle(t, x + 27, y + 39, 12, teintes[kc])
            cercle(t, x + 27, y + 39, 8, #ffffff)
            ligne(t, x + 19, y + 31, x + 35, y + 47, teintes[kc], 3)
            label(t, x + 6, y + 16, "X", #ffffff, 11)

        sinon si kv = 13 alors
            rectangle_arrondi(t, x + 16, y + 28, 10, 10, 3, #ff4757)
            rectangle_arrondi(t, x + 28, y + 28, 10, 10, 3, #ffa502)
            rectangle_arrondi(t, x + 16, y + 40, 10, 10, 3, #3742fa)
            rectangle_arrondi(t, x + 28, y + 40, 10, 10, 3, #2ed573)
            label(t, x + 6, y + 16, "JOKER", #ffffff, 8)

        sinon
            rectangle_arrondi(t, x + 16, y + 27, 9, 9, 3, #ff4757)
            rectangle_arrondi(t, x + 26, y + 27, 9, 9, 3, #ffa502)
            rectangle_arrondi(t, x + 16, y + 37, 9, 9, 3, #3742fa)
            rectangle_arrondi(t, x + 26, y + 37, 9, 9, 3, #2ed573)
            label(t, x + 19, y + 59, "+4", #2f3542, 13)
            label(t, x + 6, y + 16, "+4", #ffffff, 11)
        fin si
    sinon
        rectangle_arrondi(t, x, y, 54, 78, 10, #16213e)
        contour(t, rgba(255, 255, 255, 0.18), 1)
        rectangle_arrondi(t, x + 3, y + 3, 48, 72, 8, #0f3460)
        ellipse(t, x + 27, y + 39, 19, 12, #e94560)
        label(t, x + 14, y + 44, "UNO", #ffffff, 12)
    fin si
fin procédure

procédure dessinerSurToile(t, msg, coulMsg, volCoul, volVal, volX, volY)
    dimension(t, LARG, HAUT)

    dégradé(t, [#20233a, #101322])
    ellipse(t, 320, 220, 300, 185, rgba(88, 101, 242, 0.07))
    ellipse(t, 320, 220, 220, 130, rgba(88, 101, 242, 0.09))
    ellipse(t, 320, 220, 150, 88, rgba(255, 255, 255, 0.04))

    rectangle(t, 0, 0, LARG, 46, rgba(0, 0, 0, 0.35))
    ligne(t, 0, 46, LARG, 46, rgba(255, 255, 255, 0.08), 1)
    rectangle_arrondi(t, 14, 10, 58, 26, 8, #e94560)
    label(t, 25, 29, "UNO", #ffffff, 15)
    label(t, 84, 28, "CoddyRun v3.3", #8b93b0, 11)

    rectangle_arrondi(t, LARG - 86, 10, 68, 24, 12, rgba(233, 69, 96, 0.18))
    contour(t, #e94560, 1)
    label(t, LARG - 71, 27, "Quitter", #ff8fa3, 12)

    nbIA est un nombre
    nbIA vaut longueur(iaCoul)
    rectangle_arrondi(t, 14, 56, 150, 30, 15, rgba(255, 255, 255, 0.07))
    cercle(t, 30, 71, 10, #e94560)
    label(t, 25, 76, "IA", #ffffff, 10)
    label(t, 48, 76, convertir_texte(nbIA) + " cartes", #cbd5e1, 12)

    si nbIA = 1 alors
        rectangle_arrondi(t, 172, 60, 58, 22, 11, #e94560)
        label(t, 182, 76, "UNO !", #ffffff, 12)
    fin si

    di est un nombre
    pour di de 0 a nbIA - 1
        ax est un nombre
        ax vaut 240 + di * 18
        si ax < LARG - 120 alors
            rectangle_arrondi(t, ax, 58, 16, 24, 4, #0f3460)
            contour(t, rgba(255, 255, 255, 0.2), 1)
        fin si
    fin pour

    appelle carte(t, 206, 171, 4, 0, faux)
    appelle carte(t, 208, 173, 4, 0, faux)
    appelle carte(t, 210, 175, 4, 0, faux)
    rectangle_arrondi(t, 192, 262, 92, 18, 9, rgba(0, 0, 0, 0.45))
    label(t, 203, 275, "Pioche (" + convertir_texte(longueur(piocheCoul)) + ")", #a5b4fc, 11)

    rectangle_arrondi(t, 344, 169, 66, 90, 12, rgba(255, 255, 255, 0.05))
    contour(t, rgba(255, 255, 255, 0.14), 1)
    appelle carte(t, 350, 175, défausseCoul, défausseVal, vrai)
    rectangle_arrondi(t, 348, 262, 60, 18, 9, rgba(0, 0, 0, 0.45))
    label(t, 356, 275, "Défausse", #a5b4fc, 11)

    nbMain est un nombre
    nbMain vaut longueur(mainCoul)
    espM est un nombre
    espM vaut espacementMain()
    dj est un nombre
    pour dj de 0 a nbMain - 1
        jx est un nombre
        jx vaut 20 + dj * espM
        yc est un nombre
        yc vaut HAUT - 105
        si jouable(mainCoul[dj], mainVal[dj]) alors
            yc vaut HAUT - 113
            rectangle_arrondi(t, jx - 5, yc - 5, 64, 88, 12, rgba(255, 212, 59, 0.22))
            rectangle_arrondi(t, jx - 2, yc - 2, 58, 82, 11, #ffd43b)
        fin si
        appelle carte(t, jx, yc, mainCoul[dj], mainVal[dj], vrai)
    fin pour

    rectangle_arrondi(t, LARG - 154, HAUT - 154, 142, 26, 13, rgba(255, 255, 255, 0.07))
    label(t, LARG - 130, HAUT - 136, "Toi : " + convertir_texte(nbMain) + " cartes", #cbd5e1, 12)

    si nbMain = 1 alors
        rectangle_arrondi(t, LARG - 156, HAUT - 186, 58, 22, 11, #e94560)
        label(t, LARG - 146, HAUT - 170, "UNO !", #ffffff, 12)
    fin si

    rectangle_arrondi(t, 12, HAUT - 154, LARG - 180, 26, 13, rgba(0, 0, 0, 0.5))
    contour(t, rgba(255, 255, 255, 0.1), 1)
    cercle(t, 28, HAUT - 141, 4, #ffd43b)
    label(t, 40, HAUT - 136, msg, coulMsg, 13)

    si volCoul >= 0 alors
        appelle carte(t, volX, volY, volCoul, volVal, vrai)
    fin si
fin procédure


@ ANIMATIONS DIRECTES
procédure animerPose(kc, kv, departX, departY, msg)
    arrX est un nombre
    arrX vaut 350
    arrY est un nombre
    arrY vaut 175

    nbImg est un nombre
    nbImg vaut 12
    ka est un nombre
    pour ka de 0 a nbImg
        ta est un nombre
        ta vaut ka / 12.0
        vx est un nombre
        vx vaut departX + (arrX - departX) * ta
        vy est un nombre
        vy vaut departY + (arrY - departY) * ta

        img est une toile
        appelle dessinerSurToile(img, msg, #ffffff, kc, kv, vx, vy)
        affiche img
    fin pour
fin procédure

procédure animerPioche(qui, msg)
    depX est un nombre
    depX vaut 210
    depY est un nombre
    depY vaut 175

    arrX est un nombre
    arrY est un nombre
    si qui = 1 alors
        arrX vaut LARG / 2
        arrY vaut HAUT - 80
    sinon
        arrX vaut 100
        arrY vaut 60
    fin si

    nbImg est un nombre
    nbImg vaut 10
    ka est un nombre
    pour ka de 0 a nbImg
        ta est un nombre
        ta vaut ka / 10.0
        vx est un nombre
        vx vaut depX + (arrX - depX) * ta
        vy est un nombre
        vy vaut depY + (arrY - depY) * ta

        img est une toile
        appelle dessinerSurToile(img, msg, #ffffff, 4, 0, vx, vy)
        affiche img
    fin pour
fin procédure


@ LOGIQUE DE JEU ET IA
fonction choisirCouleurClic()
    choixC est un nombre
    choixC vaut -1
    tant que choixC = -1
        gc est une toile
        appelle dessinerSurToile(gc, "Joker ! Clique sur la couleur de ton choix :", #ffd43b, -1, 0, 0, 0)

        rectangle(gc, 0, 0, LARG, HAUT, rgba(10, 11, 20, 0.6))
        rectangle_arrondi(gc, 158, 106, 324, 156, 16, #1c1f33)
        contour(gc, rgba(255, 255, 255, 0.14), 1)
        rectangle_arrondi(gc, 158, 106, 324, 8, 4, #e94560)
        label(gc, 234, 144, "Choisis une couleur", #ffffff, 15)

        rectangle_arrondi(gc, 182, 165, 60, 60, 14, teintes[0])
        contour(gc, rgba(255, 255, 255, 0.3), 1)
        rectangle_arrondi(gc, 254, 165, 60, 60, 14, teintes[1])
        contour(gc, rgba(255, 255, 255, 0.3), 1)
        rectangle_arrondi(gc, 326, 165, 60, 60, 14, teintes[2])
        contour(gc, rgba(255, 255, 255, 0.3), 1)
        rectangle_arrondi(gc, 398, 165, 60, 60, 14, teintes[3])
        contour(gc, rgba(255, 255, 255, 0.3), 1)

        label(gc, 196, 245, "Rouge", #cbd5e1, 10)
        label(gc, 268, 245, "Jaune", #cbd5e1, 10)
        label(gc, 344, 245, "Vert", #cbd5e1, 10)
        label(gc, 414, 245, "Bleu", #cbd5e1, 10)

        affiche gc

        ccx est un nombre
        ccy est un nombre
        cliquer gc dans ccx, ccy

        si ccy >= 165 et ccy < 225 alors
            si ccx >= 182 et ccx < 242 alors choixC vaut 0 fin si
            si ccx >= 254 et ccx < 314 alors choixC vaut 1 fin si
            si ccx >= 326 et ccx < 386 alors choixC vaut 2 fin si
            si ccx >= 398 et ccx < 458 alors choixC vaut 3 fin si
        fin si
    fin tant que
    retourne choixC
fin fonction

procédure tourIA()
    trouve est un nombre
    trouve vaut -1

    danger est un booléen
    danger vaut faux
    si longueur(mainCoul) <= 2 alors
        danger vaut vrai
    fin si

    si danger alors
        ta est un nombre
        pour ta de 0 a longueur(iaCoul) - 1
            si trouve = -1 et (iaVal[ta] >= 10) alors
                si jouable(iaCoul[ta], iaVal[ta]) alors trouve vaut ta fin si
            fin si
        fin pour
    fin si

    si trouve = -1 alors
        ta est un nombre
        pour ta de 0 a longueur(iaCoul) - 1
            si trouve = -1 et iaVal[ta] < 13 alors
                si jouable(iaCoul[ta], iaVal[ta]) alors trouve vaut ta fin si
            fin si
        fin pour
    fin si

    si trouve = -1 alors
        tb est un nombre
        pour tb de 0 a longueur(iaCoul) - 1
            si trouve = -1 alors
                si jouable(iaCoul[tb], iaVal[tb]) alors trouve vaut tb fin si
            fin si
        fin pour
    fin si

    si trouve = -1 alors
        appelle animerPioche(2, "L'IA piochait une carte...")
        appelle piocher(2)
        msgTour vaut "L'IA n'a rien pu jouer : elle a pioché. À toi !"
    sinon
        ic est un nombre
        ic vaut iaCoul[trouve]
        iv est un nombre
        iv vaut iaVal[trouve]
        supprime iaCoul trouve
        supprime iaVal trouve

        si iv >= 13 alors
            cptC est un tableau
            cptC ajoute 0
            cptC ajoute 0
            cptC ajoute 0
            cptC ajoute 0
            tc est un nombre
            pour tc de 0 a longueur(iaCoul) - 1
                si iaCoul[tc] <= 3 alors
                    cptC[iaCoul[tc]] vaut cptC[iaCoul[tc]] + 1
                fin si
            fin pour
            meilleure est un nombre
            meilleure vaut 0
            td est un nombre
            pour td de 1 a 3
                si cptC[td] > cptC[meilleure] alors meilleure vaut td fin si
            fin pour
            si cptC[meilleure] = 0 alors
                meilleure vaut hasard(0, 3)
            fin si
            ic vaut meilleure
        fin si

        appelle animerPose(ic, iv, 100, 20, "L'IA joue " + nomsCoul[ic] + " " + nomsLong[iv])

        défausseCoul vaut ic
        défausseVal vaut iv
        msgTour vaut "L'IA joue " + nomsCoul[ic] + " " + nomsLong[iv] + ". À toi !"

        si iv = 10 alors
            appelle animerPioche(1, "+2 : tu pioches...")
            appelle piocher(1)
            appelle animerPioche(1, "+2 : tu pioches...")
            appelle piocher(1)
            msgTour vaut "L'IA joue un +2 : tu pioches 2 cartes et tu passes ton tour !"
        fin si
        si iv = 14 alors
            pour k de 1 a 4
                appelle animerPioche(1, "+4 : tu pioches...")
                appelle piocher(1)
            fin pour
            msgTour vaut "L'IA joue un +4 " + nomsCoul[ic] + " : tu pioches 4 cartes et tu passes !"
        fin si

        si iv = 10 ou iv = 11 ou iv = 12 ou iv = 14 alors
            si longueur(iaCoul) > 0 alors
                appelle tourIA()
            fin si
        fin si
    fin si
fin procédure


@ BOUCLE PRINCIPALE
appelle creerPioche()

dd est un nombre
pour dd de 1 a 7
    appelle piocher(1)
    appelle piocher(2)
fin pour

okDep est un booléen
okDep vaut faux
tant que non okDep
    idep est un nombre
    idep vaut longueur(piocheCoul) - 1
    si piocheVal[idep] < 10 alors
        défausseCoul vaut piocheCoul[idep]
        défausseVal vaut piocheVal[idep]
        okDep vaut vrai
    fin si
    supprime piocheCoul idep
    supprime piocheVal idep
fin tant que

fini est un booléen
fini vaut faux
cx est un nombre
cy est un nombre

tant que non fini
    gJeu est une toile
    appelle dessinerSurToile(gJeu, msgTour, #ffffff, -1, 0, 0, 0)
    affiche gJeu

    cliquer gJeu dans cx, cy

    choixCarte est un nombre
    choixCarte vaut -1
    espC est un nombre
    espC vaut espacementMain()
    dj est un nombre
    pour dj de 0 a longueur(mainCoul) - 1
        jx est un nombre
        jx vaut 20 + dj * espC
        si cx >= jx et cx < jx + 54 et cy >= HAUT - 116 et cy < HAUT - 27 alors
            choixCarte vaut dj
        fin si
    fin pour

    si choixCarte >= 0 alors
        jc est un nombre
        jc vaut mainCoul[choixCarte]
        jv est un nombre
        jv vaut mainVal[choixCarte]

        si jouable(jc, jv) alors
            posX est un nombre
            posX vaut 20 + choixCarte * espC

            supprime mainCoul choixCarte
            supprime mainVal choixCarte

            si jv >= 13 alors
                jc vaut choisirCouleurClic()
            fin si

            appelle animerPose(jc, jv, posX, HAUT - 113, "Tu joues " + nomsCoul[jc] + " " + nomsLong[jv])

            défausseCoul vaut jc
            défausseVal vaut jv

            si jv = 10 alors
                pour k de 1 a 2
                    appelle animerPioche(2, "L'IA subit le +2...")
                    appelle piocher(2)
                fin pour
            fin si
            si jv = 14 alors
                pour k de 1 a 4
                    appelle animerPioche(2, "L'IA subit le +4...")
                    appelle piocher(2)
                fin pour
            fin si

            si longueur(mainCoul) = 0 alors
                msgFin vaut "GAGNÉ ! Tu n'as plus de cartes !"
                fini vaut vrai
            sinon
                si jv = 10 alors
                    msgTour vaut "+2 ! L'IA pioche 2 cartes et passe son tour : rejoue !"
                sinon si jv = 14 alors
                    msgTour vaut "+4 ! L'IA pioche 4 cartes et passe son tour : rejoue !"
                sinon si jv = 11 ou jv = 12 alors
                    msgTour vaut "L'IA passe son tour : rejoue !"
                sinon
                    appelle tourIA()
                fin si
            fin si
        sinon
            msgTour vaut "Impossible : " + nomsCoul[jc] + " " + nomsLong[jv] + " ne va pas sur " + nomsCoul[défausseCoul] + " " + nomsLong[défausseVal]
        fin si

    sinon si cx >= 206 et cx < 267 et cy >= 171 et cy < 256 alors
        appelle animerPioche(1, "Tu piochais une carte...")
        appelle piocher(1)
        appelle tourIA()

    sinon si cx >= LARG - 82 et cx < LARG - 14 et cy >= 10 et cy < 34 alors
        msgFin vaut "Partie abandonnée."
        fini vaut vrai

    sinon
        msgTour vaut "Clique sur une carte dorée (jouable), sur la pioche, ou sur Quitter."
    fin si

    si non fini alors
        si longueur(iaCoul) = 0 alors
            msgFin vaut "PERDU ! L'IA n'a plus de cartes."
            fini vaut vrai
        fin si
    fin si
fin tant que


@ ÉCRAN DE FIN ANIMÉ
pour f de 0 a 15
    gFin est une toile
    appelle dessinerSurToile(gFin, "", #ffffff, -1, 0, 0, 0)

    opac est un nombre
    opac vaut limiter(f / 10.0, 0.0, 0.6)
    rectangle(gFin, 0, 0, LARG, HAUT, rgba(10, 11, 20, opac))

    yBox est un nombre
    yBox vaut 138 + (15 - f) * 2
    rectangle_arrondi(gFin, 140, yBox, 360, 144, 18, #1c1f33)
    contour(gFin, rgba(255, 255, 255, 0.15), 1)
    rectangle_arrondi(gFin, 140, yBox, 360, 8, 4, #e94560)
    polygone(gFin, [320, yBox + 18, 324, yBox + 28, 335, yBox + 29, 327, yBox + 36, 329, yBox + 47, 320, yBox + 41, 311, yBox + 47, 313, yBox + 36, 305, yBox + 29, 316, yBox + 28], #ffd43b)
    label(gFin, 168, yBox + 88, msgFin, #ffffff, 19)

    affiche gFin
fin pour
5,0 / 5 (1 avis)
Prochaine mise à jour de CoddyRun v3.4
Nouveauté

Nouvelle fonction : paramètre()

Prochaine mise à jour de CoddyRun v3.4

Lire l'articleReplier l'article

Dans la prochaine mise à jour de CoddyRun v3.4 : Nouvelle fonction paramètre()

Une fonctionnalité majeure arrive dans CoddyRun : la possibilité de stocker, lire et organiser des données persistantes.

Cette nouvelle fonction permet de conserver des informations essentielles comme :

  • des scores,
  • des grilles de jeu,
  • des paramètres de progression,
  • des options personnalisées,
  • ou tout autre type de donnée utile à vos programmes.

Nouvelle fonction : paramètre(section, clé, valeur)

Elle permet de lire et écrire des valeurs dans des sections du fichier INI (CoddyRun.ini).

Écrire une valeur

paramètre('section', 'maCle', 52)
paramètre('section', 'maCle', "Mon texte")

Lire une valeur
La fonction renvoie la valeur associée à la clé :

x est un nombre vaut paramètre('section', 'maCle')

Gestion stricte des sections et clés

  • Si la section n’existe pas ? Erreur
  • Si la clé n’existe pas ? Erreur

Lors de la création, CoddyRun vérifie que la section n’existe pas déjà (pas de doublon)

Cette approche garantit une structure propre et évite les écritures accidentelles.

Synchronisation automatique au démarrage

À l’ouverture du programme :

  • CoddyRun parcourt le fichier CoddyRun.json, là où sont stockés vos programmes.
  • Il identifie toutes les sections déclarées dans vos scripts.
  • Il vérifie leur présence dans CoddyRun.ini.
  • Il purge automatiquement les sections du fichier INI qui ne sont plus utilisées.

Résultat : un fichier INI propre, cohérent, et parfaitement synchronisé avec vos programmes.

Une avancée qui rend CoddyRun plus puissant, plus flexible et idéal pour des projets de jeu ou d’outils évolués.

Pas encore noté
Domino Merge : un puzzle stratégique où chaque fusion compte
Article

Domino Merge

Un puzzle stratégique où chaque fusion compte

Lire l'articleReplier l'article

Domino Merge est un jeu de réflexion addictif qui combine placement, anticipation et réactions en chaîne. Le programme que tu as partagé décrit en détail le fonctionnement interne du jeu : tirage des dominos, règles de fusion, gestion du score, interface graphique et conditions de fin de partie. Voici une présentation complète et fidèle à ce que fait réellement le jeu.

Un plateau simple, une profondeur surprenante

Le jeu se déroule sur un plateau de 5 colonnes et 6 lignes, entièrement vide au départ.
Chaque domino occupe deux cases adjacentes, horizontalement ou verticalement.

Le joueur peut :

  • Placer le domino dans l’une des quatre orientations
  • Le jeter via la poubelle (coût : 10 pièces)
  • Afficher l’aide
  • Voir le domino suivant pour anticiper ses coups

Le message initial du jeu le dit clairement :

  • « Clique sur une case : le domino occupe deux cases »

Un tirage de dés pensé pour favoriser les fusions

Les valeurs des dés ne sont pas tirées uniformément. Le programme indique que :

  • Les valeurs 1, 2 et 3 sortent plus souvent
  • Les valeurs élevées sont plus rares
  • Un dé sur quatre est un double (valeurs identiques)

Ce système encourage les fusions fréquentes en début de partie, tout en rendant les valeurs hautes plus gratifiantes.

Rotation et placement : l’art d’optimiser l’espace

Le joueur peut tourner le domino grâce au bouton dédié.
Lors du placement, le jeu vérifie :

  • Si le domino dépasse du plateau
  • Si les deux cases sont libres
  • Si l’orientation est valide

En cas d’erreur, des messages clairs apparaissent :

  • « Il faut deux cases libres côte à côte »
  • « Le domino dépasse du plateau : tourne-le »

Le cœur du gameplay : les fusions en chaîne

Dès qu’un domino est posé, le jeu analyse le plateau pour détecter des groupes de trois dés identiques ou plus qui se touchent.
Le programme décrit précisément ce mécanisme :

  • « Trois dés identiques qui se touchent fusionnent en un dé supérieur. »

Lorsqu’un groupe est trouvé :

  • Les dés disparaissent
  • Un dé de valeur supérieure apparaît
  • Le joueur gagne des points

Une réaction en chaîne peut se produire si ce nouveau dé crée une nouvelle fusion

Les gains dépendent :

  • De la valeur du dé
  • Du nombre de dés fusionnés
  • Du multiplicateur de chaîne
  • Les valeurs élevées rapportent aussi davantage de pièces.

Score, pièces et progression

Le joueur commence avec 20 pièces.
Les fusions rapportent :

  • Des points, proportionnels à la valeur et à la taille du groupe
  • Des pièces, plus généreuses pour les valeurs élevées

Le jeu suit :

  • Le score actuel
  • Le record
  • Le meilleur dé obtenu

Fin de partie : quand il n’y a plus d’espace

La partie se termine lorsqu’il n’existe plus deux cases libres adjacentes.
Le jeu affiche alors un écran de fin avec :

  • Le score
  • Le meilleur dé
  • Un bouton pour recommencer

Une boucle de jeu fluide et intuitive

Le programme gère :

  • L’affichage complet
  • Les interactions de la souris
  • Les animations des dés
  • Les messages contextuels
  • Les transitions entre les états (jeu, aide, fin de partie)

Domino Merge est donc un jeu complet, cohérent, et très bien structuré.

Programme :

@ CONFIGURATION
graph est une toile
dimension(graph, 400, 550)

COLS est un nombre
LIGS est un nombre
COLS vaut 5
LIGS vaut 6

CS est un nombre
BX est un nombre
BY est un nombre
CS vaut 52
BX vaut 44
BY vaut 80

COUT_POUBELLE est un nombre
COUT_POUBELLE vaut 10

score est un nombre
score vaut 0
record est un nombre
record vaut 0
pieces est un nombre
pieces vaut 20
meilleurDomino est un nombre
meilleurDomino vaut 1

// Le domino en main : deux dés et une orientation de 0 à 3
valA est un nombre
valB est un nombre
sA est un nombre
sB est un nombre
orient est un nombre
orient vaut 0

derLig est un nombre
derCol est un nombre
der2Lig est un nombre
der2Col est un nombre
derLig vaut - 1
derCol vaut - 1
der2Lig vaut - 1
der2Col vaut - 1

finPartie est un booléen
finPartie vaut faux
aide est un booléen
aide vaut faux

message est un texte
message vaut "Clique sur une case : le domino occupe deux cases"

@ PLATEAU
plateau est un tableau
marque est un tableau
pour i de 0 à LIGS
    ligG est un tableau
    ligM est un tableau
    pour j de 0 à COLS
        ligG ajoute 0
        ligM ajoute faux
    fin pour
    plateau ajoute ligG
    marque ajoute ligM
fin pour

// Le plateau démarre entièrement vide

@ TIRAGE DES DOMINOS
// Les petites valeurs sortent bien plus souvent que les grandes,
// et un domino sur quatre est un double.
tirage est un nombre
tirage vaut hasard(1, 100)
si tirage <= 40 alors
    valA vaut 1
sinon si tirage <= 72 alors
    valA vaut 2
sinon si tirage <= 90 alors
    valA vaut 3
sinon
    valA vaut 4
fin si
si hasard(1, 100) <= 25 alors
    valB vaut valA
sinon
    tirage vaut hasard(1, 100)
    si tirage <= 40 alors
        valB vaut 1
    sinon si tirage <= 72 alors
        valB vaut 2
    sinon si tirage <= 90 alors
        valB vaut 3
    sinon
        valB vaut 4
    fin si
fin si

tirage vaut hasard(1, 100)
si tirage <= 40 alors
    sA vaut 1
sinon si tirage <= 72 alors
    sA vaut 2
sinon si tirage <= 90 alors
    sA vaut 3
sinon
    sA vaut 4
fin si
si hasard(1, 100) <= 25 alors
    sB vaut sA
sinon
    tirage vaut hasard(1, 100)
    si tirage <= 40 alors
        sB vaut 1
    sinon si tirage <= 72 alors
        sB vaut 2
    sinon si tirage <= 90 alors
        sB vaut 3
    sinon
        sB vaut 4
    fin si
fin si

@ DESSIN D'UN DÉ
procédure dessinerDomino(t, x, y, c, val, ombre)
    ray est un nombre
    ray vaut c / 4

    si ombre = vrai alors
        rectangle_arrondi(t, x + 1, y + 4, c, c, ray, rgba(0, 0, 0, 0.4))
    fin si

    coul est un texte
    sombre est un texte
    si val = 1 alors
        coul vaut #FF3B6B
        sombre vaut #D42B54
    sinon si val = 2 alors
        coul vaut #2D9CFF
        sombre vaut #1B76CC
    sinon si val = 3 alors
        coul vaut #FF9A2B
        sombre vaut #D97812
    sinon si val = 4 alors
        coul vaut #E040FB
        sombre vaut #B026C9
    sinon si val = 5 alors
        coul vaut #7C4DFF
        sombre vaut #5A2FCC
    sinon
        coul vaut #00E5C0
        sombre vaut #00B396
    fin si

    // Un socle plus foncé sous la face donne du volume au dé
    rectangle_arrondi(t, x, y, c, c, ray, sombre)
    rectangle_arrondi(t, x, y, c, c * 7 / 8, ray, coul)
    // Reflet du dessus
    rectangle_arrondi(t, x + c / 7, y + c / 9, c * 5 / 7, c / 9, 3, rgba(255, 255, 255, 0.3))

    // Les points, disposés comme sur un dé
    r est un nombre
    r vaut c / 11
    ax est un nombre
    bx est un nombre
    dx est un nombre
    ay est un nombre
    by est un nombre
    dy est un nombre
    ax vaut x + c / 4
    bx vaut x + c / 2
    dx vaut x + c * 3 / 4
    ay vaut y + c * 27 / 100
    by vaut y + c / 2
    dy vaut y + c * 73 / 100

    si val = 1 ou val = 3 ou val = 5 alors
        cercle(t, bx, by + 1, r, rgba(0, 0, 0, 0.18))
        cercle(t, bx, by, r, #FFFFFF)
    fin si
    si val >= 2 alors
        cercle(t, ax, ay + 1, r, rgba(0, 0, 0, 0.18))
        cercle(t, ax, ay, r, #FFFFFF)
        cercle(t, dx, dy + 1, r, rgba(0, 0, 0, 0.18))
        cercle(t, dx, dy, r, #FFFFFF)
    fin si
    si val >= 4 alors
        cercle(t, dx, ay + 1, r, rgba(0, 0, 0, 0.18))
        cercle(t, dx, ay, r, #FFFFFF)
        cercle(t, ax, dy + 1, r, rgba(0, 0, 0, 0.18))
        cercle(t, ax, dy, r, #FFFFFF)
    fin si
    si val = 6 alors
        cercle(t, ax, by + 1, r, rgba(0, 0, 0, 0.18))
        cercle(t, ax, by, r, #FFFFFF)
        cercle(t, dx, by + 1, r, rgba(0, 0, 0, 0.18))
        cercle(t, dx, by, r, #FFFFFF)
    fin si
fin procédure

@ UNE CASE VIDE
procédure dessinerCaseVide(t, x, y, c)
    rectangle_arrondi(t, x, y, c, c, c / 4, #45464C)
    rectangle_arrondi(t, x + c / 6, y + c / 8, c * 2 / 3, c / 10, 3, rgba(255, 255, 255, 0.04))
fin procédure

@ DESSIN D'UN DOMINO COMPLET (DEUX DÉS)
procédure dessinerCouple(t, x, y, c, v1, v2, dl, dc, ombre)
    // La barrette de liaison passe sous les deux dés
    si dc = 1 alors
        rectangle_arrondi(t, x + c - 3, y + c / 3, c / 4 + 6, c / 3, 3, #6B6D75)
    sinon
        rectangle_arrondi(t, x + c / 3, y + c - 3, c / 3, c / 4 + 6, 3, #6B6D75)
    fin si
    appelle dessinerDomino(t, x, y, c, v1, ombre)
    appelle dessinerDomino(t, x + dc * (c + 6), y + dl * (c + 6), c, v2, ombre)
fin procédure

@ UNE PIÈCE D'OR
procédure dessinerPiece(t, cx, cy, r)
    cercle(t, cx, cy + 1, r, #C99A19)
    cercle(t, cx, cy, r, #FFC93C)
    cercle(t, cx, cy, r * 3 / 5, #FFE9A8)
fin procédure

@ LA POUBELLE
procédure dessinerPoubelle(t, x, y, actif)
    fond est un texte
    cIcone est un texte
    si actif = vrai alors
        fond vaut #55565C
        cIcone vaut #E8EAF0
    sinon
        fond vaut #414248
        cIcone vaut #6B6D75
    fin si
    rectangle_arrondi(t, x, y + 2, 44, 48, 11, rgba(0, 0, 0, 0.3))
    rectangle_arrondi(t, x, y, 44, 48, 11, fond)
    rectangle_arrondi(t, x + 8, y + 11, 28, 4, 2, cIcone)
    rectangle_arrondi(t, x + 17, y + 7, 10, 4, 2, cIcone)
    rectangle_arrondi(t, x + 11, y + 17, 22, 23, 4, cIcone)
    rectangle(t, x + 17, y + 22, 3, 13, fond)
    rectangle(t, x + 25, y + 22, 3, 13, fond)
fin procédure

@ LE BOUTON DE ROTATION
procédure dessinerRotation(t, x, y)
    rectangle_arrondi(t, x, y + 2, 44, 48, 11, rgba(0, 0, 0, 0.3))
    rectangle_arrondi(t, x, y, 44, 48, 11, #55565C)
    // Anneau ouvert en haut à droite
    cercle(t, x + 22, y + 26, 14, #E8EAF0)
    cercle(t, x + 22, y + 26, 8, #55565C)
    rectangle(t, x + 22, y + 10, 15, 16, #55565C)
    // Pointe de la flèche
    pF est un tableau
    pF ajoute x + 18
    pF ajoute y + 10
    pF ajoute x + 34
    pF ajoute y + 10
    pF ajoute x + 26
    pF ajoute y + 24
    polygone(t, pF, #E8EAF0)
fin procédure

@ BOUCLE PRINCIPALE

tant que vrai

    // --- Forme du domino en main selon son orientation ---
    v1 est un nombre
    v2 est un nombre
    dl est un nombre
    dc est un nombre
    si orient = 0 alors
        v1 vaut valA
        v2 vaut valB
        dl vaut 0
        dc vaut 1
    sinon si orient = 1 alors
        v1 vaut valA
        v2 vaut valB
        dl vaut 1
        dc vaut 0
    sinon si orient = 2 alors
        v1 vaut valB
        v2 vaut valA
        dl vaut 0
        dc vaut 1
    sinon
        v1 vaut valB
        v2 vaut valA
        dl vaut 1
        dc vaut 0
    fin si

    // ================= AFFICHAGE =================
    effacer(graph)
    contour(graph, #0)
    remplir(graph, #2E2F34)

    // ---------- BANDEAU DU HAUT ----------
    rectangle_arrondi(graph, 12, 8, 376, 58, 14, #35363C)
    appelle dessinerPiece(graph, 34, 30, 11)
    label(graph, 50, 36, "" + pieces, #FFFFFF, 15)

    label(graph, 150, 38, "" + score, #FF9A2B, 22)
    label(graph, 244, 36, "??", #FFFFFF, 15)
    label(graph, 268, 37, "" + record, #FF9A2B, 18)

    rectangle_arrondi(graph, 344, 14, 34, 34, 9, #4B4C52)
    rectangle_arrondi(graph, 353, 21, 5, 20, 2, #E8EAF0)
    rectangle_arrondi(graph, 364, 21, 5, 20, 2, #E8EAF0)

    label(graph, 26, 60, message, #8A8C94, 10)

    // ---------- LE PLATEAU ----------
    rectangle_arrondi(graph, 36, 72, 328, 380, 16, #35363C)
    pour i de 0 à LIGS
        pour j de 0 à COLS
            xk est un nombre
            yk est un nombre
            xk vaut BX + j * CS + 3
            yk vaut BY + i * CS + 3
            si plateau[i][j] = 0 alors
                appelle dessinerCaseVide(graph, xk, yk, 46)
            sinon
                appelle dessinerDomino(graph, xk, yk, 46, plateau[i][j], vrai)
            fin si
        fin pour
    fin pour

    // ---------- ZONE DE JEU DU BAS ----------
    rectangle_arrondi(graph, 12, 458, 376, 84, 14, #35363C)

    actifP est un booléen
    actifP vaut faux
    si pieces >= COUT_POUBELLE alors
        actifP vaut vrai
    fin si
    appelle dessinerPoubelle(graph, 26, 466, actifP)
    appelle dessinerPiece(graph, 32, 528, 7)
    label(graph, 44, 533, "" + COUT_POUBELLE, #B9BCC4, 11)

    // Le domino en main, centré dans la zone quelle que soit son orientation
    xm est un nombre
    ym est un nombre
    si dc = 1 alors
        xm vaut 138
        ym vaut 478
    sinon
        xm vaut 160
        ym vaut 464
    fin si
    appelle dessinerCouple(graph, xm, ym, 38, v1, v2, dl, dc, vrai)

    appelle dessinerRotation(graph, 278, 466)

    // Le domino suivant, en petit
    label(graph, 338, 476, "suiv.", #8A8C94, 9)
    appelle dessinerCouple(graph, 344, 482, 18, sA, sB, 1, 0, faux)

    // ---------- SURCOUCHES ----------
    si aide = vrai alors
        rectangle(graph, 0, 0, 400, 550, rgba(0, 0, 0, 0.72))
        rectangle_arrondi(graph, 30, 110, 340, 330, 16, #4B4C52)
        label(graph, 58, 158, "COMMENT JOUER", #FFFFFF, 20)
        label(graph, 58, 200, "Chaque domino porte deux dés.", #D6D8DE, 13)
        label(graph, 58, 222, "Le bouton de droite le fait tourner", #D6D8DE, 13)
        label(graph, 58, 244, "dans les quatre positions.", #D6D8DE, 13)
        label(graph, 58, 282, "Clique sur une case libre : le premier", #D6D8DE, 13)
        label(graph, 58, 304, "dé s'y pose, le second juste à côté.", #D6D8DE, 13)
        label(graph, 58, 342, "Trois dés identiques qui se touchent", #D6D8DE, 13)
        label(graph, 58, 364, "fusionnent en un dé supérieur.", #D6D8DE, 13)
        label(graph, 58, 396, "Poubelle : " + COUT_POUBELLE + " pièces.", #D6D8DE, 13)
        label(graph, 58, 424, "Clique pour fermer", #FFC93C, 13)
    fin si

    si finPartie = vrai alors
        rectangle(graph, 0, 0, 400, 550, rgba(0, 0, 0, 0.78))
        rectangle_arrondi(graph, 40, 160, 320, 230, 16, #4B4C52)
        label(graph, 86, 212, "PLUS DE PLACE !", #FFFFFF, 24)
        label(graph, 86, 256, "Score : " + score, #FF9A2B, 20)
        label(graph, 86, 292, "Meilleur dé :", #D6D8DE, 14)
        appelle dessinerDomino(graph, 220, 268, 32, meilleurDomino, faux)
        label(graph, 86, 356, "Clique pour rejouer", #FFC93C, 15)
    fin si

    affiche graph

    // ================= LA SOURIS =================
    xc est un nombre
    yc est un nombre
    cliquer graph dans xc, yc

    si finPartie = vrai alors
        // ---------- NOUVELLE PARTIE ----------
        pour i de 0 à LIGS
            pour j de 0 à COLS
                plateau[i][j] vaut 0
            fin pour
        fin pour
        score vaut 0
        pieces vaut 20
        meilleurDomino vaut 1
        orient vaut 0
        derLig vaut - 1
        derCol vaut - 1
        finPartie vaut faux
        message vaut "Clique sur une case : le domino occupe deux cases"

    sinon si aide = vrai alors
        aide vaut faux

    sinon si xc >= 344 et xc <= 378 et yc >= 14 et yc <= 48 alors
        aide vaut vrai

    sinon si xc >= 278 et xc <= 322 et yc >= 466 et yc <= 514 alors
        // ---------- TOURNER LE DOMINO ----------
        orient vaut orient + 1
        si orient > 3 alors
            orient vaut 0
        fin si
        message vaut "Domino tourné"

    sinon si xc >= 26 et xc <= 70 et yc >= 466 et yc <= 514 alors
        // ---------- JETER LE DOMINO ----------
        si pieces >= COUT_POUBELLE alors
            pieces vaut pieces - COUT_POUBELLE
            valA vaut sA
            valB vaut sB
            tirage vaut hasard(1, 100)
            si tirage <= 40 alors
                sA vaut 1
            sinon si tirage <= 72 alors
                sA vaut 2
            sinon si tirage <= 90 alors
                sA vaut 3
            sinon
                sA vaut 4
            fin si
            si hasard(1, 100) <= 25 alors
                sB vaut sA
            sinon
                tirage vaut hasard(1, 100)
                si tirage <= 40 alors
                    sB vaut 1
                sinon si tirage <= 72 alors
                    sB vaut 2
                sinon si tirage <= 90 alors
                    sB vaut 3
                sinon
                    sB vaut 4
                fin si
            fin si
            message vaut "Domino jeté : - " + COUT_POUBELLE + " pièces"
        sinon
            message vaut "Il te faut " + COUT_POUBELLE + " pièces pour jeter"
        fin si

    sinon si xc >= BX et xc < BX + (COLS + 1) * CS et yc >= BY et yc < BY + (LIGS + 1) * CS alors
        // ---------- POSER LE DOMINO SUR DEUX CASES ----------
        l1 est un nombre
        c1 est un nombre
        c1 vaut arrondi_inferieur((xc - BX) / CS)
        l1 vaut arrondi_inferieur((yc - BY) / CS)
        c1 vaut limiter(c1, 0, COLS)
        l1 vaut limiter(l1, 0, LIGS)

        l2 est un nombre
        c2 est un nombre
        l2 vaut l1 + dl
        c2 vaut c1 + dc

        si l2 > LIGS ou c2 > COLS alors
            message vaut "Le domino dépasse du plateau : tourne-le"
        sinon si plateau[l1][c1] <> 0 ou plateau[l2][c2] <> 0 alors
            message vaut "Il faut deux cases libres côte à côte"
        sinon
            plateau[l1][c1] vaut v1
            plateau[l2][c2] vaut v2
            derLig vaut l1
            derCol vaut c1
            der2Lig vaut l2
            der2Col vaut c2
            message vaut "Domino posé"

            // Le suivant passe en main, et on tire un nouveau suivant
            valA vaut sA
            valB vaut sB
            tirage vaut hasard(1, 100)
            si tirage <= 40 alors
                sA vaut 1
            sinon si tirage <= 72 alors
                sA vaut 2
            sinon si tirage <= 90 alors
                sA vaut 3
            sinon
                sA vaut 4
            fin si
            si hasard(1, 100) <= 25 alors
                sB vaut sA
            sinon
                tirage vaut hasard(1, 100)
                si tirage <= 40 alors
                    sB vaut 1
                sinon si tirage <= 72 alors
                    sB vaut 2
                sinon si tirage <= 90 alors
                    sB vaut 3
                sinon
                    sB vaut 4
                fin si
            fin si

            // ---------- FUSIONS EN CHAÎNE ----------
            chaine est un nombre
            chaine vaut 0
            encore est un booléen
            encore vaut vrai

            tant que encore = vrai
                encore vaut faux

                pour i de 0 à LIGS
                    pour j de 0 à COLS
                        si plateau[i][j] > 0 et encore = faux alors
                            val est un nombre
                            val vaut plateau[i][j]

                            // --- Groupe connecté de même valeur, propagé par vagues ---
                            pour r de 0 à LIGS
                                pour c de 0 à COLS
                                    marque[r][c] vaut faux
                                fin pour
                            fin pour
                            marque[i][j] vaut vrai

                            propage est un booléen
                            propage vaut vrai
                            tant que propage = vrai
                                propage vaut faux
                                pour r de 0 à LIGS
                                    pour c de 0 à COLS
                                        si marque[r][c] = vrai alors
                                            pour d de 0 à 3
                                                dLi est un nombre
                                                dCo est un nombre
                                                si d = 0 alors
                                                    dLi vaut 0 - 1
                                                    dCo vaut 0
                                                sinon si d = 1 alors
                                                    dLi vaut 1
                                                    dCo vaut 0
                                                sinon si d = 2 alors
                                                    dLi vaut 0
                                                    dCo vaut 0 - 1
                                                sinon
                                                    dLi vaut 0
                                                    dCo vaut 1
                                                fin si
                                                vr est un nombre
                                                vc est un nombre
                                                vr vaut r + dLi
                                                vc vaut c + dCo
                                                si vr >= 0 et vr <= LIGS et vc >= 0 et vc <= COLS alors
                                                    si plateau[vr][vc] = val et marque[vr][vc] = faux alors
                                                        marque[vr][vc] vaut vrai
                                                        propage vaut vrai
                                                    fin si
                                                fin si
                                            fin pour
                                        fin si
                                    fin pour
                                fin pour
                            fin tant que

                            // --- Le groupe est-il assez grand ? ---
                            nbGroupe est un nombre
                            nbGroupe vaut 0
                            ligNaiss est un nombre
                            colNaiss est un nombre
                            ligNaiss vaut i
                            colNaiss vaut j
                            pour r de 0 à LIGS
                                pour c de 0 à COLS
                                    si marque[r][c] = vrai alors
                                        nbGroupe ajoute 1
                                        si r > ligNaiss alors
                                            ligNaiss vaut r
                                            colNaiss vaut c
                                        fin si
                                    fin si
                                fin pour
                            fin pour

                            // Le dé supérieur naît de préférence sur une case que tu viens de jouer
                            si der2Lig >= 0 alors
                                si marque[der2Lig][der2Col] = vrai alors
                                    ligNaiss vaut der2Lig
                                    colNaiss vaut der2Col
                                fin si
                            fin si
                            si derLig >= 0 alors
                                si marque[derLig][derCol] = vrai alors
                                    ligNaiss vaut derLig
                                    colNaiss vaut derCol
                                fin si
                            fin si

                            si nbGroupe >= 3 alors
                                pour r de 0 à LIGS
                                    pour c de 0 à COLS
                                        si marque[r][c] = vrai alors
                                            plateau[r][c] vaut 0
                                        fin si
                                    fin pour
                                fin pour

                                si val < 6 alors
                                    plateau[ligNaiss][colNaiss] vaut val + 1
                                    derLig vaut ligNaiss
                                    derCol vaut colNaiss
                                    der2Lig vaut - 1
                                    der2Col vaut - 1
                                    si val + 1 > meilleurDomino alors
                                        meilleurDomino vaut val + 1
                                    fin si
                                sinon
                                    derLig vaut - 1
                                    derCol vaut - 1
                                    der2Lig vaut - 1
                                    der2Col vaut - 1
                                fin si

                                gain est un nombre
                                gain vaut val * nbGroupe * 10 * (chaine + 1)
                                score ajoute gain
                                chaine ajoute 1

                                si val >= 4 alors
                                    pieces ajoute 3
                                sinon
                                    pieces ajoute 1
                                fin si

                                si val = 6 alors
                                    message vaut "Six sublimé ! + " + gain + " points"
                                sinon si chaine > 1 alors
                                    message vaut "Réaction en chaîne x" + chaine + " : + " + gain
                                sinon
                                    message vaut "Fusion de " + nbGroupe + " dés : + " + gain
                                fin si

                                encore vaut vrai
                            fin si
                        fin si
                    fin pour
                fin pour
            fin tant que

            // ---------- RESTE-T-IL DEUX CASES LIBRES CÔTE À CÔTE ? ----------
            placeRestante est un booléen
            placeRestante vaut faux
            pour i de 0 à LIGS
                pour j de 0 à COLS
                    si plateau[i][j] = 0 alors
                        si j < COLS alors
                            si plateau[i][j + 1] = 0 alors
                                placeRestante vaut vrai
                            fin si
                        fin si
                        si i < LIGS alors
                            si plateau[i + 1][j] = 0 alors
                                placeRestante vaut vrai
                            fin si
                        fin si
                    fin si
                fin pour
            fin pour
            si placeRestante = faux alors
                finPartie vaut vrai
                si score > record alors
                    record vaut score
                fin si
            fin si
        fin si
    fin si
fin tant que
5,0 / 5 (1 avis)
Bridge Constructor : Analyse et présentation d’un simulateur de construction de ponts
Article

Bridge Constructor

Analyse et présentation d’un simulateur de construction de ponts

Lire l'articleReplier l'article

Le programme présenté est un simulateur de construction de ponts où le joueur doit relier deux rives en respectant un budget, des contraintes physiques et la résistance des matériaux.

Il s’agit d’un moteur complet : gestion des points d’ancrage, des poutres, de la route, de la physique, du budget, des tests de résistance et de l’interface graphique.

L’objectif du jeu :

  • Relie la route de gauche à celle de droite

Structure générale du programme

Le code repose sur une boucle principale qui gère :

  • la mise en place du niveau
  • l’affichage du décor, du pont et de l’interface
  • les interactions du joueur (clics, choix de points, ajout de segments)
  • le test de résistance du pont
  • la progression entre les niveaux

Le programme utilise de nombreux tableaux pour stocker l’état du chantier : points, segments, tensions, chemin de la route, etc.

Décor et niveaux

Chaque niveau possède :

  • un nombre de colonnes (points d’ancrage)
  • une position d’arrivée
  • un éventuel pilier rocheux
  • un budget
  • un poids de camion

La fonction reglageNiveau définit ces paramètres.
Par exemple, pour le niveau 1 :

nbCol vaut 7… budg vaut 430… chg vaut 3

Chaque niveau possède aussi une consigne pédagogique, comme :

  • Le ravin s’élargit. Un tablier plat s’affaisse : triangule-le.

Construction du pont

Le joueur peut poser deux types de segments :

  • Route (genre = 1) : plus coûteuse, supporte le camion
  • Poutre (genre = 2) : moins chère, sert de structure

Le coût dépend de la longueur :

  • La route coûte plus cher que la poutre

Le programme empêche les segments trop longs :

  • Segment trop long : relie des points voisins

Simulation physique

La physique est un élément central du programme.
Chaque pas de simulation applique :

  • la gravité
  • un amortissement (xFrein)
  • la relaxation des poutres (xRelax)
  • la rupture si la tension dépasse xRupture

Le document précise :

si absolue(poutreTens[k]) > xRupture alors poutreVie[k] vaut faux

Le camion ajoute une charge sur le point qu’il traverse :

sy[noeudCharge] vaut sy[noeudCharge] + etat[9]

Test de résistance Lors du test :

  • Le programme cherche un chemin de route continu.
  • Le camion avance point par point.
  • La physique est simulée à chaque étape.
  • Les poutres cassées sont comptabilisées.
  • Un verdict est affiché.

Les trois verdicts possibles :

Pont rompu
"LE PONT S EST ROMPU… triangule davantage ta structure"

Pont solide mais trop cher
"Budget dépassé… une simple vis"

Pont homologué
"PONT HOMOLOGUÉ ! Coût… sur… autorisés"

Interface utilisateur

Le programme dessine :

  • le décor (falaises, pilier rocheux)
  • le pont
  • le camion
  • un bandeau d’information : niveau, budget, poids du camion, nombre de segments
  • des boutons : Route, Poutre, Annuler, Effacer, Tester, Niveau+

Un moteur complet de simulation

Ce programme est remarquable par :

  • la richesse de ses mécaniques
  • la gestion réaliste des contraintes physiques
  • la pédagogie des niveaux
  • la clarté de son architecture
  • l’intégration d’une interface graphique complète

Il constitue un véritable mini‑jeu de construction de ponts, entièrement codé dans un style procédural, avec une logique physique cohérente et des retours visuels immédiats.

Programme :

@ Configuration
scene est une toile
dimension(scene, 640, 430)

// Réglages de la physique
xGravite est un nombre
xGravite vaut 45 / 100
xFrein est un nombre
xFrein vaut 985 / 1000
xRelax est un nombre
xRelax vaut 1 / 2
xRupture est un nombre
xRupture vaut 12 / 100

// État général, rangé dans un tableau :
//   0 budget dépensé      1 mode (1 route, 2 poutre)
//   2 point choisi        3 nombre de segments
//   4 dernier résultat    5 nombre de points
//   6 budget maximal      7 poutres cassées
//   8 niveau courant      9 poids du camion
//  10 point de départ    11 point d arrivée
//  12 colonne du pilier (-1 si aucun)   13 nombre de colonnes
//  14 abscisse du bord gauche de la grille
//  15 longueur utile du chemin trouvé
etat est un tableau
pour k de 0 à 15
  etat ajoute 0
fin pour
etat[1] vaut 1
etat[2] vaut - 1
etat[8] vaut 1

message est un texte
message vaut "Relie la route de gauche à celle de droite"

@ Les tableaux du chantier
ptX est un tableau
ptY est un tableau
ptFixe est un tableau

poutreA est un tableau
poutreB est un tableau
poutreType est un tableau
poutreVie est un tableau
poutreRepos est un tableau
poutreTens est un tableau

sx est un tableau
sy est un tableau
oldx est un tableau
oldy est un tableau

chemin est un tableau
venantDe est un tableau
atteint est un tableau

@ Le nom du niveau
fonction nomDuNiveau(niv)
  si niv = 1 alors
    retourne "Le petit ravin"
  fin si
  si niv = 2 alors
    retourne "La brèche"
  fin si
  si niv = 3 alors
    retourne "Le dénivelé"
  fin si
  si niv = 4 alors
    retourne "Le pilier de roche"
  fin si
  si niv = 5 alors
    retourne "La longue portée"
  fin si
  si niv = 6 alors
    retourne "Le convoi lourd"
  fin si
  si niv = 7 alors
    retourne "Le grand canyon"
  fin si
  si niv = 8 alors
    retourne "L épreuve"
  fin si
  retourne "Chantier expert " + convertir_texte(niv - 8)
fin fonction

@ La consigne du niveau
fonction consigneDuNiveau(niv)
  si niv = 1 alors
    retourne "Une portée courte et un budget large : essaie un tablier soutenu par-dessous."
  fin si
  si niv = 2 alors
    retourne "Le ravin s élargit. Un tablier plat s affaisse : triangule-le."
  fin si
  si niv = 3 alors
    retourne "L arrivée est en hauteur. La route doit monter jusqu au point du haut."
  fin si
  si niv = 4 alors
    retourne "Un pilier de roche au milieu : appuie-toi dessus, c est un ancrage gratuit."
  fin si
  si niv = 5 alors
    retourne "Longue portée sans appui. Les triangles hauts travaillent mieux."
  fin si
  si niv = 6 alors
    retourne "Camion lourd. Chaque poutre doit être courte pour résister."
  fin si
  si niv = 7 alors
    retourne "Le grand canyon, et l arrivée en hauteur. Budget confortable."
  fin si
  retourne "Portée maximale, camion très lourd, budget serré. Bonne chance."
fin fonction

@ Le coût d un segment
// La route coûte plus cher que la poutre : c est ce qui pousse
// à ne poser du tablier que là où le camion doit passer.
fonction coutDuSegment(dlong, genre)
  si genre = 1 alors
    retourne arrondi(dlong * 40 / 100)
  fin si
  retourne arrondi(dlong * 24 / 100)
fin fonction

@ La couleur d une poutre selon sa tension
fonction couleurDeTension(tn, genre)
  ta est un nombre
  ta vaut absolue(tn)
  si ta > 9 / 100 alors
    retourne #7F1D1D
  fin si
  si ta > 6 / 100 alors
    retourne #EF4444
  fin si
  si ta > 3 / 100 alors
    retourne #F59E0B
  fin si
  si genre = 1 alors
    retourne #E2E8F0
  fin si
  retourne #38BDF8
fin fonction

@ Les réglages de chaque niveau
// Une fonction ne touche à aucune variable extérieure : elle se
// contente de renvoyer un nombre. quoi = 1 colonnes, 2 rangée
// d arrivée, 3 colonne du pilier, 4 budget, 5 poids du camion.
fonction reglageNiveau(niv, quoi)
  nbCol est un nombre
  ligneArr est un nombre
  colPil est un nombre
  budg est un nombre
  chg est un nombre

  si niv = 1 alors
    nbCol vaut 7
    ligneArr vaut 1
    colPil vaut - 1
    budg vaut 430
    chg vaut 3
  sinon si niv = 2 alors
    nbCol vaut 9
    ligneArr vaut 1
    colPil vaut - 1
    budg vaut 520
    chg vaut 4
  sinon si niv = 3 alors
    nbCol vaut 9
    ligneArr vaut 0
    colPil vaut - 1
    budg vaut 560
    chg vaut 4
  sinon si niv = 4 alors
    nbCol vaut 11
    ligneArr vaut 1
    colPil vaut 5
    budg vaut 560
    chg vaut 5
  sinon si niv = 5 alors
    nbCol vaut 11
    ligneArr vaut 1
    colPil vaut - 1
    budg vaut 640
    chg vaut 5
  sinon si niv = 6 alors
    nbCol vaut 11
    ligneArr vaut 1
    colPil vaut 5
    budg vaut 560
    chg vaut 7
  sinon si niv = 7 alors
    nbCol vaut 13
    ligneArr vaut 0
    colPil vaut - 1
    budg vaut 720
    chg vaut 6
  sinon si niv = 8 alors
    nbCol vaut 13
    ligneArr vaut 1
    colPil vaut - 1
    budg vaut 660
    chg vaut 8
  sinon
    nbCol vaut 13
    ligneArr vaut 1
    colPil vaut - 1
    budg vaut limiter(660 - (niv - 8) * 25, 420, 660)
    chg vaut 8 + (niv - 8)
  fin si

  si quoi = 1 alors
    retourne nbCol
  fin si
  si quoi = 2 alors
    retourne ligneArr
  fin si
  si quoi = 3 alors
    retourne colPil
  fin si
  si quoi = 4 alors
    retourne budg
  fin si
  retourne chg
fin fonction

@ Décor : la gorge, les falaises, le pilier éventuel
procédure dessinerDecor(t)
  remplir(t, #16233A)
  rectangle(t, 0, 30, 640, 200, #1E3A5F)
  rectangle(t, 0, 300, 640, 80, #0C1526)

  xG est un nombre
  xD est un nombre
  xG vaut etat[14]
  xD vaut etat[14] + (etat[13] - 1) * 44

  yD est un nombre
  yD vaut ptY[etat[11]]

  // Falaise de gauche
  rectangle(t, 0, 200, xG + 14, 180, #4B5563)
  rectangle(t, 0, 196, xG + 14, 8, #6B7280)
  rectangle(t, 0, 196, xG - 8, 6, #1F2937)

  // Falaise de droite, à la hauteur de l arrivée
  rectangle(t, xD - 14, yD - 10, 640 - xD + 14, 390 - yD, #4B5563)
  rectangle(t, xD - 14, yD - 14, 640 - xD + 14, 8, #6B7280)
  rectangle(t, xD + 8, yD - 14, 640 - xD, 6, #1F2937)

  // Pilier de roche, quand le niveau en comporte un
  si etat[12] >= 0 alors
    xP est un nombre
    xP vaut etat[14] + etat[12] * 44
    rectangle(t, xP - 13, 270, 26, 110, #4B5563)
    rectangle(t, xP - 16, 264, 32, 8, #6B7280)
  fin si
fin procédure

@ Le pont dans son état courant
procédure dessinerPont(t, avecTension)
  pour k de 0 à etat[3] - 1
    si poutreVie[k] = vrai alors
      ia est un nombre
      ib est un nombre
      ia vaut poutreA[k]
      ib vaut poutreB[k]
      coul est un texte
      si avecTension = vrai alors
        coul vaut couleurDeTension(poutreTens[k], poutreType[k])
      sinon si poutreType[k] = 1 alors
        coul vaut #E2E8F0
      sinon
        coul vaut #38BDF8
      fin si
      ep est un nombre
      si poutreType[k] = 1 alors
        ep vaut 6
      sinon
        ep vaut 3
      fin si
      ligne(t, sx[ia], sy[ia], sx[ib], sy[ib], coul, ep)
    fin si
  fin pour

  pour p de 0 à etat[5] - 1
    si ptFixe[p] = vrai alors
      cercle(t, sx[p], sy[p], 5, #FBBF24)
    sinon
      cercle(t, sx[p], sy[p], 4, #94A3B8)
    fin si
  fin pour

  si etat[2] >= 0 alors
    cercle(t, sx[etat[2]], sy[etat[2]], 9, #FDE68A)
    cercle(t, sx[etat[2]], sy[etat[2]], 5, #16233A)
  fin si
fin procédure

@ Le camion, dont la taille dit le poids
procédure dessinerCamion(t, cx, cy, poids)
  larg est un nombre
  larg vaut 34 + poids * 2
  rectangle_arrondi(t, cx - larg / 2, cy - 22, larg, 16, 3, #DC2626)
  rectangle_arrondi(t, cx - larg / 2, cy - 30, 18, 10, 3, #B91C1C)
  rectangle(t, cx - larg / 2 + 3, cy - 28, 10, 6, #BFDBFE)
  cercle(t, cx - larg / 2 + 9, cy - 4, 5, #0F172A)
  cercle(t, cx + larg / 2 - 9, cy - 4, 5, #0F172A)
fin procédure

@ Le bandeau d information
procédure dessinerInterface(t, msg)
  rectangle(t, 0, 0, 640, 30, #0B1020)
  label(t, 12, 21, "NIVEAU " + convertir_texte(etat[8]) + " - " + nomDuNiveau(etat[8]), #FDE68A, 14)

  coulBudget est un texte
  si etat[0] > etat[6] alors
    coulBudget vaut #EF4444
  sinon
    coulBudget vaut #4ADE80
  fin si
  label(t, 300, 21, "Budget " + convertir_texte(etat[0]) + " / " + convertir_texte(etat[6]), coulBudget, 13)
  label(t, 470, 21, "Camion " + convertir_texte(etat[9]) + " t", #93C5FD, 12)
  label(t, 560, 21, "Seg. " + convertir_texte(etat[3]), #64748B, 12)

  coulR est un texte
  coulP est un texte
  si etat[1] = 1 alors
    coulR vaut #E2E8F0
    coulP vaut #334155
  sinon
    coulR vaut #334155
    coulP vaut #38BDF8
  fin si
  rectangle_arrondi(t, 16, 388, 92, 32, 8, coulR)
  label(t, 34, 409, "ROUTE", #0F172A, 13)
  rectangle_arrondi(t, 116, 388, 92, 32, 8, coulP)
  label(t, 132, 409, "POUTRE", #0F172A, 13)
  rectangle_arrondi(t, 216, 388, 92, 32, 8, #64748B)
  label(t, 230, 409, "ANNULER", #FFFFFF, 12)
  rectangle_arrondi(t, 316, 388, 92, 32, 8, #64748B)
  label(t, 330, 409, "EFFACER", #FFFFFF, 12)
  rectangle_arrondi(t, 424, 388, 100, 32, 8, #16A34A)
  label(t, 444, 409, "TESTER", #FFFFFF, 14)
  rectangle_arrondi(t, 532, 388, 92, 32, 8, #7C3AED)
  label(t, 546, 409, "NIVEAU +", #FFFFFF, 12)

  label(t, 12, 378, msg, #FDE68A, 11)
fin procédure

@ Remise des points à leur place
procédure replacerPoints()
  pour p de 0 à etat[5] - 1
    sx[p] vaut ptX[p]
    sy[p] vaut ptY[p]
    oldx[p] vaut ptX[p]
    oldy[p] vaut ptY[p]
  fin pour
  pour k de 0 à etat[3] - 1
    poutreVie[k] vaut vrai
    poutreTens[k] vaut 0
  fin pour
  etat[7] vaut 0
fin procédure

@ Un pas de simulation
procédure unPasDePhysique(noeudCharge)
  pour p de 0 à etat[5] - 1
    si ptFixe[p] = faux alors
      vxp est un nombre
      vyp est un nombre
      vxp vaut (sx[p] - oldx[p]) * xFrein
      vyp vaut (sy[p] - oldy[p]) * xFrein
      oldx[p] vaut sx[p]
      oldy[p] vaut sy[p]
      sx[p] vaut sx[p] + vxp
      sy[p] vaut sy[p] + vyp + xGravite
    fin si
  fin pour

  // Le poids du camion s ajoute au point qu il traverse
  si noeudCharge >= 0 alors
    si ptFixe[noeudCharge] = faux alors
      sy[noeudCharge] vaut sy[noeudCharge] + etat[9]
    fin si
  fin si

  pour tour de 1 à 6
    pour k de 0 à etat[3] - 1
      si poutreVie[k] = vrai alors
        ia est un nombre
        ib est un nombre
        ia vaut poutreA[k]
        ib vaut poutreB[k]
        ex est un nombre
        ey est un nombre
        ex vaut sx[ib] - sx[ia]
        ey vaut sy[ib] - sy[ia]
        dd est un nombre
        dd vaut hypotenuse(ex, ey)
        si dd > 0 alors
          ecart est un nombre
          ecart vaut (dd - poutreRepos[k]) / dd * xRelax
          si ptFixe[ia] = faux alors
            sx[ia] vaut sx[ia] + ex * ecart
            sy[ia] vaut sy[ia] + ey * ecart
          fin si
          si ptFixe[ib] = faux alors
            sx[ib] vaut sx[ib] - ex * ecart
            sy[ib] vaut sy[ib] - ey * ecart
          fin si
        fin si
      fin si
    fin pour
  fin pour

  pour k de 0 à etat[3] - 1
    si poutreVie[k] = vrai alors
      ja est un nombre
      jb est un nombre
      ja vaut poutreA[k]
      jb vaut poutreB[k]
      fx est un nombre
      fy est un nombre
      fx vaut sx[jb] - sx[ja]
      fy vaut sy[jb] - sy[ja]
      dl est un nombre
      dl vaut hypotenuse(fx, fy)
      poutreTens[k] vaut (dl - poutreRepos[k]) / poutreRepos[k]
      si absolue(poutreTens[k]) > xRupture alors
        poutreVie[k] vaut faux
        etat[7] vaut etat[7] + 1
      fin si
    fin si
  fin pour
fin procédure

@ Recherche du chemin de la route
// Le tableau chemin est PRÉ-ALLOUÉ et sa longueur utile est
// rangée dans etat[15]. Une procédure ne peut pas ajouter
// d élément à un tableau global : elle n écrit que par index.
procédure chercherRoute()
  pour p de 0 à etat[5] - 1
    atteint[p] vaut faux
    venantDe[p] vaut - 1
  fin pour
  etat[15] vaut 0

  atteint[etat[10]] vaut vrai
  encore est un booléen
  encore vaut vrai
  tant que encore = vrai
    encore vaut faux
    pour k de 0 à etat[3] - 1
      si poutreType[k] = 1 et poutreVie[k] = vrai alors
        ia est un nombre
        ib est un nombre
        ia vaut poutreA[k]
        ib vaut poutreB[k]
        si atteint[ia] = vrai et atteint[ib] = faux alors
          atteint[ib] vaut vrai
          venantDe[ib] vaut ia
          encore vaut vrai
        fin si
        si atteint[ib] = vrai et atteint[ia] = faux alors
          atteint[ia] vaut vrai
          venantDe[ia] vaut ib
          encore vaut vrai
        fin si
      fin si
    fin pour
  fin tant que

  si atteint[etat[11]] = vrai alors
    // On compte d abord la longueur du chemin
    lg est un nombre
    lg vaut 0
    courant est un nombre
    courant vaut etat[11]
    tant que courant <> - 1
      lg vaut lg + 1
      courant vaut venantDe[courant]
    fin tant que
    etat[15] vaut lg

    // puis on le remplit de la fin vers le début
    idx est un nombre
    idx vaut lg - 1
    courant vaut etat[11]
    tant que courant <> - 1
      chemin[idx] vaut courant
      idx vaut idx - 1
      courant vaut venantDe[courant]
    fin tant que
  fin si
fin procédure


@ ===========================================================
@ Boucle principale
@ ===========================================================
aCharger est un booléen
aCharger vaut vrai

enMarche est un booléen
enMarche vaut vrai

tant que enMarche = vrai

  // =========================================================
  //   MISE EN PLACE DU NIVEAU
  //   Elle se fait ICI, au niveau principal : seule une
  //   instruction de ce niveau peut vider un tableau global
  //   ou y ajouter des éléments.
  // =========================================================
  si aCharger = vrai alors
    aCharger vaut faux

    nbColN est un nombre
    ligneArrN est un nombre
    colPilN est un nombre
    nbColN vaut reglageNiveau(etat[8], 1)
    ligneArrN vaut reglageNiveau(etat[8], 2)
    colPilN vaut reglageNiveau(etat[8], 3)

    etat[13] vaut nbColN
    etat[12] vaut colPilN
    etat[6] vaut reglageNiveau(etat[8], 4)
    etat[9] vaut reglageNiveau(etat[8], 5)
    etat[14] vaut arrondi((640 - (nbColN - 1) * 44) / 2)

    vide ptX
    vide ptY
    vide ptFixe
    pour cN de 0 à nbColN - 1
      pour rN de 0 à 2
        ptX ajoute etat[14] + cN * 44
        ptY ajoute 150 + rN * 60
        si cN = 0 ou cN = nbColN - 1 alors
          ptFixe ajoute vrai
        sinon si cN = colPilN et rN = 2 alors
          ptFixe ajoute vrai
        sinon
          ptFixe ajoute faux
        fin si
      fin pour
    fin pour

    etat[5] vaut longueur(ptX)
    etat[10] vaut 1
    etat[11] vaut (nbColN - 1) * 3 + ligneArrN

    vide poutreA
    vide poutreB
    vide poutreType
    vide poutreVie
    vide poutreRepos
    vide poutreTens
    etat[0] vaut 0
    etat[2] vaut - 1
    etat[3] vaut 0
    etat[7] vaut 0
    etat[15] vaut 0

    vide sx
    vide sy
    vide oldx
    vide oldy
    vide venantDe
    vide atteint
    vide chemin
    pour pN de 0 à etat[5] - 1
      sx ajoute ptX[pN]
      sy ajoute ptY[pN]
      oldx ajoute ptX[pN]
      oldy ajoute ptY[pN]
      venantDe ajoute - 1
      atteint ajoute faux
      chemin ajoute 0
    fin pour

    message vaut consigneDuNiveau(etat[8])
  fin si

  effacer(scene)
  appelle dessinerDecor(scene)
  appelle dessinerPont(scene, faux)
  appelle dessinerInterface(scene, message)
  affiche scene

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

  si clique(xc, yc, 16, 388, 92, 32) alors
    etat[1] vaut 1
    etat[2] vaut - 1
    message vaut "Mode route : le camion ne roule que sur ce type de segment"

  sinon si clique(xc, yc, 116, 388, 92, 32) alors
    etat[1] vaut 2
    etat[2] vaut - 1
    message vaut "Mode poutre : moins cher, sert à soutenir la route"

  sinon si clique(xc, yc, 216, 388, 92, 32) alors
    si etat[3] > 0 alors
      dernierK est un nombre
      dernierK vaut etat[3] - 1
      exA est un nombre
      eyA est un nombre
      exA vaut ptX[poutreB[dernierK]] - ptX[poutreA[dernierK]]
      eyA vaut ptY[poutreB[dernierK]] - ptY[poutreA[dernierK]]
      etat[0] vaut etat[0] - coutDuSegment(hypotenuse(exA, eyA), poutreType[dernierK])
      poutreA supprime dernierK
      poutreB supprime dernierK
      poutreType supprime dernierK
      poutreVie supprime dernierK
      poutreRepos supprime dernierK
      poutreTens supprime dernierK
      etat[3] vaut etat[3] - 1
      appelle replacerPoints()
      message vaut "Dernier segment retiré"
    fin si

  sinon si clique(xc, yc, 316, 388, 92, 32) alors
    aCharger vaut vrai
    message vaut "Chantier remis à zéro"

  sinon si clique(xc, yc, 532, 388, 92, 32) alors
    etat[8] vaut etat[8] + 1
    aCharger vaut vrai

  sinon si clique(xc, yc, 424, 388, 100, 32) alors
    // =====================================================
    //   LE TEST DE RÉSISTANCE
    // =====================================================
    appelle chercherRoute()

    si etat[15] = 0 alors
      message vaut "La route ne relie pas encore les deux rives"
    sinon
      appelle replacerPoints()
      images est un tableau
      vide images

      pour tour de 1 à 12
        appelle unPasDePhysique(- 1)
      fin pour

      etape est un nombre
      etape vaut 0
      tant que etape < etat[15]
        noeud est un nombre
        noeud vaut chemin[etape]
        appelle unPasDePhysique(noeud)
        appelle unPasDePhysique(noeud)

        effacer(scene)
        appelle dessinerDecor(scene)
        appelle dessinerPont(scene, vrai)
        appelle dessinerCamion(scene, sx[noeud], sy[noeud], etat[9])
        appelle dessinerInterface(scene, "Passage du camion... poutres cassées : " + convertir_texte(etat[7]))
        images ajoute scene

        etape vaut etape + 1
      fin tant que

      pour tour de 1 à 8
        appelle unPasDePhysique(- 1)
        effacer(scene)
        appelle dessinerDecor(scene)
        appelle dessinerPont(scene, vrai)
        appelle dessinerInterface(scene, "Fin du passage - appuie sur C ensuite")
        images ajoute scene
      fin pour

      // =====================================================
      //   LE VERDICT, peint sur les dernières images
      // =====================================================
      appelle chercherRoute()

      titreFin est un texte
      ligne1Fin est un texte
      ligne2Fin est un texte
      coulFin est un texte
      coulTitre est un texte
      reussi est un booléen
      reussi vaut faux

      si etat[15] = 0 alors
        coulFin vaut #7F1D1D
        coulTitre vaut #FECACA
        titreFin vaut "LE PONT S EST ROMPU"
        ligne1Fin vaut convertir_texte(etat[7]) + " poutres ont cédé sous la charge"
        ligne2Fin vaut "Appuie sur C : triangule davantage ta structure"
        message vaut "Effondrement : renforce la structure"
      sinon si etat[0] > etat[6] alors
        coulFin vaut #78350F
        coulTitre vaut #FDE68A
        titreFin vaut "IL TIENT, MAIS..."
        ligne1Fin vaut "Budget dépassé de " + convertir_texte(etat[0] - etat[6]) + " EUR"
        ligne2Fin vaut "Note : une simple vis. Appuie sur C pour recommencer."
        message vaut "Pont solide mais trop cher : recommence"
      sinon
        reussi vaut vrai
        coulFin vaut #14532D
        coulTitre vaut #86EFAC
        titreFin vaut "PONT HOMOLOGUÉ !"
        ligne1Fin vaut "Coût " + convertir_texte(etat[0]) + " EUR sur " + convertir_texte(etat[6]) + " autorisés"
        ligne2Fin vaut "Appuie sur C pour le niveau suivant"
        message vaut "Bravo, chantier suivant"
      fin si

      pour tour de 1 à 6
        effacer(scene)
        appelle dessinerDecor(scene)
        appelle dessinerPont(scene, vrai)
        rectangle_arrondi(scene, 110, 130, 420, 120, 14, coulFin)
        label(scene, 140, 175, titreFin, coulTitre, 24)
        label(scene, 140, 210, ligne1Fin, #FFFFFF, 14)
        label(scene, 140, 234, ligne2Fin, #FDE68A, 13)
        appelle dessinerInterface(scene, message)
        images ajoute scene
      fin pour

      animation(images, 200, 0)

      toucheFin est un nombre
      appuyer ["c"] dans toucheFin

      si reussi = vrai alors
        etat[8] vaut etat[8] + 1
        aCharger vaut vrai
      sinon
        appelle replacerPoints()
      fin si
    fin si

  sinon
    // ---------- Clic sur un point de construction ----------
    trouve est un nombre
    trouve vaut - 1
    pour p de 0 à etat[5] - 1
      si clique(xc, yc, ptX[p], ptY[p], 13) alors
        trouve vaut p
      fin si
    fin pour

    si trouve < 0 alors
      etat[2] vaut - 1
      message vaut "Clique sur un point gris ou jaune"

    sinon si etat[2] < 0 alors
      etat[2] vaut trouve
      message vaut "Premier point choisi, clique le second"

    sinon si etat[2] = trouve alors
      etat[2] vaut - 1
      message vaut "Choix annulé"

    sinon
      pA est un nombre
      pB est un nombre
      pA vaut etat[2]
      pB vaut trouve

      dejaLa est un booléen
      dejaLa vaut faux
      pour k de 0 à etat[3] - 1
        si poutreA[k] = pA et poutreB[k] = pB alors
          dejaLa vaut vrai
        fin si
        si poutreA[k] = pB et poutreB[k] = pA alors
          dejaLa vaut vrai
        fin si
      fin pour

      ex2 est un nombre
      ey2 est un nombre
      ex2 vaut ptX[pB] - ptX[pA]
      ey2 vaut ptY[pB] - ptY[pA]
      dlong est un nombre
      dlong vaut hypotenuse(ex2, ey2)

      si dejaLa = vrai alors
        message vaut "Ces deux points sont déjà reliés"
      sinon si dlong > 110 alors
        message vaut "Segment trop long : relie des points voisins"
      sinon
        poutreA ajoute pA
        poutreB ajoute pB
        poutreType ajoute etat[1]
        poutreVie ajoute vrai
        poutreRepos ajoute dlong
        poutreTens ajoute 0
        etat[3] vaut etat[3] + 1
        etat[0] vaut etat[0] + coutDuSegment(dlong, etat[1])
        si etat[0] > etat[6] alors
          message vaut "Attention, budget dépassé de " + convertir_texte(etat[0] - etat[6]) + " EUR"
        sinon si etat[1] = 1 alors
          message vaut "Route posée pour " + convertir_texte(coutDuSegment(dlong, 1)) + " EUR"
        sinon
          message vaut "Poutre posée pour " + convertir_texte(coutDuSegment(dlong, 2)) + " EUR"
        fin si
      fin si
      etat[2] vaut - 1
    fin si
  fin si
fin tant que
Pas encore noté
CoddyRun Visualiser et mesurer les algorithmes de tri
Article

Visualiser et mesurer les algorithmes de tri

Un programme pédagogique pour le cours de NSI

Lire l'articleReplier l'article

Ce programme CoddyRun est un outil d’apprentissage conçu pour permettre aux élèves de voir, comprendre et mesurer le fonctionnement de trois algorithmes de tri fondamentaux :

  • le tri par sélection,
  • le tri par insertion,
  • le tri fusion ascendant.

Il ne se contente pas de trier : il compte les comparaisons, compte les écritures, affiche les étapes, et propose même un banc d’essai pour confronter les mesures expérimentales aux complexités théoriques.

Une interface visuelle pour comprendre les tris

Dès l’exécution, le programme crée une toile graphique de 700 × 540 pixels et génère un tableau de 24 valeurs aléatoires.
Chaque valeur est représentée par une barre verticale, dont la hauteur correspond à la valeur du tableau.

L’écran principal affiche :

  • le nom de l’algorithme en cours,
  • sa complexité théorique,
  • le nombre de comparaisons et écritures effectuées,

l’étape en cours ou l’état « TRIÉ ».

Les barres changent de couleur selon l’algorithme et l’avancement :

  • bleu pour les éléments non traités,
  • vert pour la zone déjà triée,
  • jaune pour l’élément en cours de traitement.

Cette visualisation permet aux élèves de suivre pas à pas la logique interne de chaque tri.

Trois algorithmes, trois comportements

Tri par sélection — O(n²)
Le programme cherche le plus petit élément du sous-tableau restant et l’échange avec la position courante.
Chaque étape correspond à une recherche du minimum dans la partie non triée.

Extrait du code :
« On cherche le plus petit du reste et on l’amène en tête. »

Tri par insertion — O(n²)
L’élément courant est inséré dans la partie déjà triée en le décalant vers la gauche jusqu’à sa position correcte.

Tri fusion ascendant — O(n log n)
Le tableau est fusionné par blocs de largeur croissante : 1, 2, 4, 8…
Chaque étape double la taille des blocs fusionnés.

Le programme utilise un tableau auxiliaire aux pour stocker les résultats intermédiaires.

Interaction : apprendre en manipulant

L’utilisateur peut :

  • [P] exécuter une seule étape,
  • [E] trier entièrement,
  • [N] générer un nouveau tableau,
  • [A] changer d’algorithme,
  • [B] lancer le banc d’essai.

Cette approche interactive permet de comparer visuellement la vitesse des algorithmes et de comprendre leur logique interne.

Le banc d’essai : mesurer la complexité Le programme propose un mode « banc d’essai » où les trois algorithmes trient exactement le même tableau, pour des tailles croissantes :

« 8, 16, 32, 64, 128 »

Pour chaque taille, il mesure le nombre de comparaisons effectuées :

  • resSel pour le tri par sélection,
  • resIns pour le tri par insertion,
  • resFus pour le tri fusion.

Les résultats sont affichés dans un tableau comparatif, accompagnés des valeurs théoriques :

  • n² / 2 pour les tris quadratiques,
  • n × log2(n) pour le tri fusion.

Enfin, des courbes sont tracées pour visualiser l’évolution du nombre de comparaisons en fonction de n.

Un outil idéal pour la pédagogie NSI

Ce programme est un excellent support pour :

  • comprendre la notion de complexité algorithmique,
  • visualiser les différences entre tris quadratiques et tris efficaces,
  • expérimenter sur des données réelles,
  • confronter théorie et pratique,
  • développer une intuition algorithmique.

Il transforme un concept abstrait en une expérience concrète, interactive et mesurable.

Conclusion

Ce programme CoddyRun est bien plus qu’un simple tri :
c’est un laboratoire visuel pour explorer les algorithmes, un outil de mesure, et un support pédagogique puissant pour les cours de NSI.

Il permet aux élèves de voir ce que les algorithmes font réellement, de comprendre pourquoi certains sont plus rapides que d’autres, et d’expérimenter par eux-mêmes.

Programme :

// ===========================================================
//   ALGORITHMES DE TRI : VISUALISER ET MESURER
//   Programme d'appui pour le cours de NSI
//   - tri par sélection      O(n²)
//   - tri par insertion      O(n²)
//   - tri fusion ascendant   O(n log n)
//   On compte les comparaisons et les écritures, puis on
//   confronte la mesure à la complexité théorique.
// ===========================================================

@ CONFIGURATION
vue est une toile
dimension(vue, 700, 540)

NBVAL est un nombre
NBVAL vaut 23

algo est un nombre
algo vaut 1
etape est un nombre
etape vaut 0
termine est un booléen
termine vaut faux
nbComp est un nombre
nbComp vaut 0
nbEcr est un nombre
nbEcr vaut 0

ecran est un texte
ecran vaut "tri"

message est un texte
message vaut "Appuie sur P pour exécuter une étape"

touche est un nombre

@ LE TABLEAU À TRIER
tab est un tableau
aux est un tableau
pour k de 0 à 127
    tab ajoute 0
    aux ajoute 0
fin pour
pour k de 0 à NBVAL
    tab[k] vaut hasard(5, 100)
fin pour

@ RÉSULTATS DU BANC D'ESSAI
tailles est un tableau
resSel est un tableau
resIns est un tableau
resFus est un tableau
pour k de 0 à 4
    tailles ajoute 0
    resSel ajoute 0
    resIns ajoute 0
    resFus ajoute 0
fin pour
bancFait est un booléen
bancFait vaut faux

@ UN SEGMENT (le langage ne dessine que des polygones)
procédure segment(t, x1, y1, x2, y2, ep, coul)
    p est un tableau
    p ajoute x1
    p ajoute y1 - ep
    p ajoute x2
    p ajoute y2 - ep
    p ajoute x2
    p ajoute y2 + ep
    p ajoute x1
    p ajoute y1 + ep
    polygone(t, p, coul)
fin procédure

@ BOUCLE PRINCIPALE
tant que vrai

    effacer(vue)
    contour(vue, #0)
    remplir(vue, #EEF2F7)

    si ecran = "tri" alors

        // =======================================================
        //                 ÉCRAN DE VISUALISATION
        // =======================================================
        nomAlgo est un texte
        coutAlgo est un texte
        si algo = 1 alors
            nomAlgo vaut "TRI PAR SÉLECTION"
            coutAlgo vaut "O(n²)  —  environ n²/2 comparaisons"
        sinon si algo = 2 alors
            nomAlgo vaut "TRI PAR INSERTION"
            coutAlgo vaut "O(n²) au pire, O(n) sur un tableau déjà trié"
        sinon
            nomAlgo vaut "TRI FUSION"
            coutAlgo vaut "O(n log n)  —  environ n x log2(n) comparaisons"
        fin si

        rectangle_arrondi(vue, 12, 10, 676, 96, 14, #FFFFFF)
        label(vue, 28, 42, nomAlgo, #B45309, 20)
        label(vue, 28, 66, coutAlgo, #64748B, 12)
        label(vue, 28, 92, "n = " + (NBVAL + 1) + "  —  comparaisons : " + nbComp + "  —  écritures : " + nbEcr, #0F172A, 13)

        si termine = vrai alors
            label(vue, 520, 42, "TRIÉ", #16A34A, 20)
        sinon
            label(vue, 500, 42, "étape " + etape, #2563EB, 16)
        fin si

        // ---------- LES BARRES ----------
        rectangle_arrondi(vue, 12, 118, 676, 320, 14, #FFFFFF)
        pour k de 0 à NBVAL
            xb est un nombre
            hb est un nombre
            xb vaut 28 + k * 27
            hb vaut tab[k] * 26 / 10

            coulB est un texte
            coulB vaut #60A5FA
            // La zone déjà en place se colore différemment
            si algo <= 2 et k < etape alors
                coulB vaut #4ADE80
            fin si
            si algo <= 2 et k = etape et termine = faux alors
                coulB vaut #FBBF24
            fin si
            si termine = vrai alors
                coulB vaut #4ADE80
            fin si

            rectangle_arrondi(vue, xb, 410 - hb, 22, hb, 4, coulB)
            label(vue, xb + 2, 428, "" + tab[k], #94A3B8, 9)
        fin pour

        si algo = 3 et termine = faux alors
            label(vue, 30, 140, "Blocs de largeur " + etape + " en cours de fusion", #64748B, 12)
        fin si

        // ---------- COMMANDES ----------
        rectangle_arrondi(vue, 12, 450, 676, 78, 14, #FFFFFF)
        label(vue, 28, 478, "[P] une étape  —  [E] tout trier  —  [N] nouveau tableau", #2563EB, 13)
        label(vue, 28, 502, "[A] changer d'algorithme  —  [B] banc d'essai", #2563EB, 13)
        label(vue, 28, 522, message, #64748B, 11)

        affiche vue
        appuyer ["p", "e", "n", "a", "b"] dans touche

        // =======================================================
        //   EXÉCUTION : UNE ÉTAPE, C'EST UN TOUR DE BOUCLE EXTERNE
        // =======================================================
        faireEtapes est un nombre
        faireEtapes vaut 0
        si touche = 1 alors
            faireEtapes vaut 1
        sinon si touche = 2 alors
            faireEtapes vaut 999
        sinon si touche = 3 alors
            pour k de 0 à NBVAL
                tab[k] vaut hasard(5, 100)
            fin pour
            etape vaut 0
            si algo = 2 alors
                etape vaut 1
            sinon si algo = 3 alors
                etape vaut 1
            fin si
            termine vaut faux
            nbComp vaut 0
            nbEcr vaut 0
            message vaut "Nouveau tableau tiré au hasard"
        sinon si touche = 4 alors
            algo vaut algo + 1
            si algo > 3 alors
                algo vaut 1
            fin si
            pour k de 0 à NBVAL
                tab[k] vaut hasard(5, 100)
            fin pour
            etape vaut 0
            si algo = 2 alors
                etape vaut 1
            sinon si algo = 3 alors
                etape vaut 1
            fin si
            termine vaut faux
            nbComp vaut 0
            nbEcr vaut 0
            message vaut "Algorithme changé, nouveau tableau"
        sinon
            ecran vaut "banc"
        fin si

        compteur est un nombre
        compteur vaut 0
        tant que compteur < faireEtapes et termine = faux
            compteur ajoute 1

            si algo = 1 alors
                // ---------- TRI PAR SÉLECTION ----------
                // On cherche le plus petit du reste et on l'amène en tête.
                imin est un nombre
                imin vaut etape
                pour k de etape + 1 à NBVAL
                    nbComp ajoute 1
                    si tab[k] < tab[imin] alors
                        imin vaut k
                    fin si
                fin pour
                si imin <> etape alors
                    tmp est un nombre
                    tmp vaut tab[etape]
                    tab[etape] vaut tab[imin]
                    tab[imin] vaut tmp
                    nbEcr ajoute 3
                fin si
                etape ajoute 1
                si etape >= NBVAL alors
                    termine vaut vrai
                fin si

            sinon si algo = 2 alors
                // ---------- TRI PAR INSERTION ----------
                // On glisse l'élément courant à sa place dans le début trié.
                val est un nombre
                val vaut tab[etape]
                k est un nombre
                k vaut etape - 1
                continuer est un booléen
                continuer vaut vrai
                tant que continuer = vrai et k >= 0
                    nbComp ajoute 1
                    si tab[k] > val alors
                        tab[k + 1] vaut tab[k]
                        nbEcr ajoute 1
                        k vaut k - 1
                    sinon
                        continuer vaut faux
                    fin si
                fin tant que
                tab[k + 1] vaut val
                nbEcr ajoute 1
                etape ajoute 1
                si etape > NBVAL alors
                    termine vaut vrai
                fin si

            sinon
                // ---------- TRI FUSION ASCENDANT ----------
                // Une étape fusionne deux à deux tous les blocs de largeur "etape".
                deb est un nombre
                deb vaut 0
                tant que deb <= NBVAL
                    mil est un nombre
                    fin2 est un nombre
                    mil vaut deb + etape
                    fin2 vaut deb + 2 * etape
                    si mil > NBVAL + 1 alors
                        mil vaut NBVAL + 1
                    fin si
                    si fin2 > NBVAL + 1 alors
                        fin2 vaut NBVAL + 1
                    fin si

                    i1 est un nombre
                    i2 est un nombre
                    idx est un nombre
                    i1 vaut deb
                    i2 vaut mil
                    idx vaut deb
                    tant que i1 < mil ou i2 < fin2
                        prendre1 est un booléen
                        prendre1 vaut faux
                        si i1 < mil et i2 < fin2 alors
                            nbComp ajoute 1
                            si tab[i1] <= tab[i2] alors
                                prendre1 vaut vrai
                            fin si
                        sinon si i1 < mil alors
                            prendre1 vaut vrai
                        fin si
                        si prendre1 = vrai alors
                            aux[idx] vaut tab[i1]
                            i1 ajoute 1
                        sinon
                            aux[idx] vaut tab[i2]
                            i2 ajoute 1
                        fin si
                        nbEcr ajoute 1
                        idx ajoute 1
                    fin tant que
                    deb vaut fin2
                fin tant que

                pour k de 0 à NBVAL
                    tab[k] vaut aux[k]
                    nbEcr ajoute 1
                fin pour
                etape vaut etape * 2
                si etape > NBVAL alors
                    termine vaut vrai
                fin si
            fin si
        fin tant que

        si termine = vrai et faireEtapes > 0 alors
            message vaut "Tri terminé en " + nbComp + " comparaisons et " + nbEcr + " écritures"
        fin si

    sinon

        // =======================================================
        //                    BANC D'ESSAI
        //   Les trois algorithmes trient le MÊME tableau, pour
        //   des tailles qui doublent : 8, 16, 32, 64, 128.
        // =======================================================
        si bancFait = faux alors
            bancFait vaut vrai
            t est un nombre
            t vaut 8

            pour idT de 0 à 4
                tailles[idT] vaut t

                // Un tableau de référence, retiré à l'identique pour chaque tri
                pour k de 0 à t - 1
                    aux[k] vaut hasard(1, 999)
                fin pour

                // --- Sélection ---
                pour k de 0 à t - 1
                    tab[k] vaut aux[k]
                fin pour
                cpt est un nombre
                cpt vaut 0
                pour i de 0 à t - 2
                    im est un nombre
                    im vaut i
                    pour j de i + 1 à t - 1
                        cpt ajoute 1
                        si tab[j] < tab[im] alors
                            im vaut j
                        fin si
                    fin pour
                    si im <> i alors
                        tm est un nombre
                        tm vaut tab[i]
                        tab[i] vaut tab[im]
                        tab[im] vaut tm
                    fin si
                fin pour
                resSel[idT] vaut cpt

                // --- Insertion ---
                pour k de 0 à t - 1
                    tab[k] vaut aux[k]
                fin pour
                cpt vaut 0
                pour i de 1 à t - 1
                    vv est un nombre
                    vv vaut tab[i]
                    jj est un nombre
                    jj vaut i - 1
                    cont est un booléen
                    cont vaut vrai
                    tant que cont = vrai et jj >= 0
                        cpt ajoute 1
                        si tab[jj] > vv alors
                            tab[jj + 1] vaut tab[jj]
                            jj vaut jj - 1
                        sinon
                            cont vaut faux
                        fin si
                    fin tant que
                    tab[jj + 1] vaut vv
                fin pour
                resIns[idT] vaut cpt

                // --- Fusion ---
                pour k de 0 à t - 1
                    tab[k] vaut aux[k]
                fin pour
                cpt vaut 0
                larg est un nombre
                larg vaut 1
                tmpF est un tableau
                pour k de 0 à t - 1
                    tmpF ajoute 0
                fin pour
                tant que larg < t
                    dd est un nombre
                    dd vaut 0
                    tant que dd < t
                        mm est un nombre
                        ff est un nombre
                        mm vaut dd + larg
                        ff vaut dd + 2 * larg
                        si mm > t alors
                            mm vaut t
                        fin si
                        si ff > t alors
                            ff vaut t
                        fin si
                        a1 est un nombre
                        a2 est un nombre
                        ix est un nombre
                        a1 vaut dd
                        a2 vaut mm
                        ix vaut dd
                        tant que a1 < mm ou a2 < ff
                            pr est un booléen
                            pr vaut faux
                            si a1 < mm et a2 < ff alors
                                cpt ajoute 1
                                si tab[a1] <= tab[a2] alors
                                    pr vaut vrai
                                fin si
                            sinon si a1 < mm alors
                                pr vaut vrai
                            fin si
                            si pr = vrai alors
                                tmpF[ix] vaut tab[a1]
                                a1 ajoute 1
                            sinon
                                tmpF[ix] vaut tab[a2]
                                a2 ajoute 1
                            fin si
                            ix ajoute 1
                        fin tant que
                        dd vaut ff
                    fin tant que
                    pour k de 0 à t - 1
                        tab[k] vaut tmpF[k]
                    fin pour
                    larg vaut larg * 2
                fin tant que
                resFus[idT] vaut cpt

                t vaut t * 2
            fin pour
        fin si

        // ---------- AFFICHAGE DU BANC D'ESSAI ----------
        rectangle_arrondi(vue, 12, 10, 676, 62, 14, #FFFFFF)
        label(vue, 28, 40, "BANC D'ESSAI : NOMBRE DE COMPARAISONS", #B45309, 19)
        label(vue, 28, 62, "Les trois algorithmes trient exactement le même tableau", #64748B, 12)

        // Tableau de résultats
        rectangle_arrondi(vue, 12, 82, 676, 190, 14, #FFFFFF)
        label(vue, 30, 108, "n", #0F172A, 13)
        label(vue, 90, 108, "sélection", #DC2626, 13)
        label(vue, 200, 108, "insertion", #2563EB, 13)
        label(vue, 310, 108, "fusion", #16A34A, 13)
        label(vue, 420, 108, "n² / 2", #94A3B8, 13)
        label(vue, 540, 108, "n x log2(n)", #94A3B8, 13)

        pour idT de 0 à 4
            yl est un nombre
            yl vaut 136 + idT * 26
            nn est un nombre
            nn vaut tailles[idT]
            lg2 est un nombre
            lg2 vaut idT + 3
            label(vue, 30, yl, "" + nn, #0F172A, 13)
            label(vue, 90, yl, "" + resSel[idT], #DC2626, 13)
            label(vue, 200, yl, "" + resIns[idT], #2563EB, 13)
            label(vue, 310, yl, "" + resFus[idT], #16A34A, 13)
            label(vue, 420, yl, "" + arrondi(nn * nn / 2), #94A3B8, 13)
            label(vue, 540, yl, "" + (nn * lg2), #94A3B8, 13)
        fin pour

        // Courbes
        rectangle_arrondi(vue, 12, 282, 676, 200, 14, #FFFFFF)
        label(vue, 30, 306, "Comparaisons en fonction de n", #64748B, 12)
        maxi est un nombre
        maxi vaut resSel[4]
        si resIns[4] > maxi alors
            maxi vaut resIns[4]
        fin si

        pour idT de 0 à 3
            x1 est un nombre
            x2 est un nombre
            x1 vaut 60 + idT * 150
            x2 vaut 60 + (idT + 1) * 150
            appelle segment(vue, x1, 460 - arrondi(resSel[idT] * 130 / maxi), x2, 460 - arrondi(resSel[idT + 1] * 130 / maxi), 2, #DC2626)
            appelle segment(vue, x1, 460 - arrondi(resIns[idT] * 130 / maxi), x2, 460 - arrondi(resIns[idT + 1] * 130 / maxi), 2, #2563EB)
            appelle segment(vue, x1, 460 - arrondi(resFus[idT] * 130 / maxi), x2, 460 - arrondi(resFus[idT + 1] * 130 / maxi), 2, #16A34A)
        fin pour
        pour idT de 0 à 4
            xp est un nombre
            xp vaut 60 + idT * 150
            cercle(vue, xp, 460 - arrondi(resSel[idT] * 130 / maxi), 4, #DC2626)
            cercle(vue, xp, 460 - arrondi(resIns[idT] * 130 / maxi), 4, #2563EB)
            cercle(vue, xp, 460 - arrondi(resFus[idT] * 130 / maxi), 4, #16A34A)
            label(vue, xp - 8, 476, "n=" + tailles[idT], #94A3B8, 10)
        fin pour

        rectangle_arrondi(vue, 12, 492, 676, 38, 14, #FFFFFF)
        label(vue, 28, 516, "[B] revenir  —  [N] refaire le banc d'essai avec d'autres tableaux", #2563EB, 13)

        affiche vue
        appuyer ["p", "e", "n", "a", "b"] dans touche
        si touche = 3 alors
            bancFait vaut faux
        sinon si touche = 5 alors
            ecran vaut "tri"
        fin si
    fin si
fin tant que
Pas encore noté