CoddyRun
Blog

Articles & Actualités

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

74 publications

Concevoir un quiz interactif : de la logique au design
Article

Concevoir un quiz interactif

Concevoir un quiz interactif : de la logique au design

Lire l'articleReplier l'article

Créer un quiz efficace ne se résume pas à afficher des questions.
C’est un véritable travail de structure, de design, d’ergonomie et de gestion des interactions.
Voici un exemple concret de mise en œuvre : un quiz EduCode complet, animé, structuré et entièrement interactif.

Structurer les questions : la base du quiz

Tout commence par une procédure simple et robuste permettant d’ajouter une question :

  • un énoncé
  • quatre propositions
  • un index de bonne réponse

Chaque question est stockée dans un enregistrement, ce qui garantit :

  • une organisation claire
  • une manipulation facile
  • une extensibilité immédiate (ajout, modification, réutilisation)

Cette approche permet de gérer 20 questions sans complexité supplémentaire.

Construire une interface intuitive

Un quiz doit être agréable à parcourir.
L’interface repose sur plusieurs éléments visuels :

  • une barre d’en-tête avec le titre et le score
  • une barre de progression dynamique
  • un cadre d’énoncé
  • quatre boutons de réponse
  • un bouton "Suivant" affiché uniquement après validation

Les couleurs jouent un rôle essentiel :

  • Bleu pour les éléments interactifs
  • Gris foncé pour le fond
  • Vert pour une bonne réponse
  • Rouge pour une mauvaise réponse sélectionnée

Cette palette crée une ambiance moderne, proche des applications éducatives actuelles.

Gérer les interactions utilisateur

Le cœur du quiz repose sur la gestion du clic :

  • détection de la zone cliquée
  • identification de la réponse choisie
  • validation
  • mise à jour du score
  • affichage de la correction
  • passage à la question suivante

Chaque étape est pensée pour être fluide, sans temps mort, avec un retour visuel immédiat.

Une boucle de jeu simple et efficace

Pour chaque question :

  • Affichage de l’interface
  • Attente du clic utilisateur
  • Vérification de la réponse
  • Mise à jour du score
  • Affichage de la correction
  • Clic sur "Suivant" pour continuer

Cette boucle crée un rythme naturel, proche des applications de quiz professionnelles.

Un écran final motivant

À la fin des 20 questions, un écran de bilan affiche :

  • le score
  • le pourcentage de réussite
  • un message personnalisé selon le niveau

Trois niveaux :

  • Excellent (= 80 %)
  • Bon travail (= 50 %)
  • À revoir (< 50 %)

Un bouton REJOUER permet de recommencer immédiatement, favorisant l’apprentissage par répétition.

Programme :

@ Initialisation des procédures et structures de questions

procédure ajouterQ(tab, txt, o1, o2, o3, o4, r)
    item est un enregistrement
    item.enonce vaut txt
    item.opt1 vaut o1
    item.opt2 vaut o2
    item.opt3 vaut o3
    item.opt4 vaut o4
    item.rep vaut r
    tab ajoute item
fin procédure

procédure dessinerBouton(t, x, y, l, h, txt, numOpt, repOpt, choixOpt, valide)
    couleurBouton est un texte
    couleurBouton vaut #393E46

    si valide alors
        si numOpt = repOpt alors
            couleurBouton vaut #2E7D32 // Vert si bonne réponse
        sinon si numOpt = choixOpt alors
            couleurBouton vaut #C62828 // Rouge si mauvaise réponse choisie
        sinon
            couleurBouton vaut #252A34 // Assombri sinon
        fin si
    fin si

    rectangle_arrondi(t, x, y, l, h, 8, couleurBouton)
    label(t, x + 20, y + 30, txt, #FFFFFF, 14)
fin procédure

procédure dessinerQuiz(t, q, numQ, totalQ, score, choixUtilisateur, validationAffichee)
    effacer(t)
    remplir(t, #1A1D24)

    // Barre d'en-tête
    rectangle(t, 0, 0, 640, 50, #252A34)
    label(t, 20, 32, "QUIZ EDUCODE", #00ADB5, 20)
    label(t, 210, 32, "Question " + convertir_texte(numQ) + " / " + convertir_texte(totalQ), #EEEEEE, 16)
    label(t, 480, 32, "Score : " + convertir_texte(score), #FFD700, 16)

    // Barre de progression
    rectangle(t, 0, 50, 640, 6, #393E46)
    largeurBarre est un nombre
    largeurBarre vaut arrondi((numQ / totalQ) * 640)
    rectangle(t, 0, 50, largeurBarre, 6, #00ADB5)

    // Cadre de l'énoncé
    rectangle_arrondi(t, 30, 75, 580, 50, 8, #252A34)
    label(t, 45, 106, q.enonce, #FFFFFF, 14)

    // Boutons de réponses
    appelle dessinerBouton(t, 30, 140, 580, 48, q.opt1, 1, q.rep, choixUtilisateur, validationAffichee)
    appelle dessinerBouton(t, 30, 200, 580, 48, q.opt2, 2, q.rep, choixUtilisateur, validationAffichee)
    appelle dessinerBouton(t, 30, 260, 580, 48, q.opt3, 3, q.rep, choixUtilisateur, validationAffichee)
    appelle dessinerBouton(t, 30, 320, 580, 48, q.opt4, 4, q.rep, choixUtilisateur, validationAffichee)

    // Bouton Suivant / Info
    si validationAffichee alors
        rectangle_arrondi(t, 220, 380, 200, 40, 20, #00ADB5)
        label(t, 290, 405, "SUIVANT", #FFFFFF, 14)
    sinon
        label(t, 200, 405, "Clique sur la bonne réponse...", #AAAAAA, 13)
    fin si

    affiche t
fin procédure

@ Chargement des 20 questions

questions est un tableau

appelle ajouterQ(questions, "1. Quel est le séparateur décimal en EduCode ?", "A) La virgule (,)", "B) Le point (.)", "C) Deux-points (:)", "D) L'espace", 2)
appelle ajouterQ(questions, "2. Comment ferme-t-on une condition si ?", "A) finsi", "B) fin si", "C) fin", "D) }", 2)
appelle ajouterQ(questions, "3. À quel index commence un tableau ?", "A) Index 0", "B) Index 1", "C) Index -1", "D) Index 0.5", 1)
appelle ajouterQ(questions, "4. À quel index commence un texte ?", "A) Index 0", "B) Index 1", "C) Index 2", "D) Index 0.5", 2)
appelle ajouterQ(questions, "5. Quel mot-clé sert à exécuter une procédure ?", "A) lancer", "B) executer", "C) appelle", "D) fonction", 3)
appelle ajouterQ(questions, "6. Écriture correcte d'une couleur hexadécimale ?", "A) '#FF0000'", "B) #FF0000", "C) rouge", "D) rgb(rouge)", 2)
appelle ajouterQ(questions, "7. Déclaration correcte d'un nombre ?", "A) x = 0", "B) x est un nombre", "C) nombre x", "D) var x : nombre", 2)
appelle ajouterQ(questions, "8. Comment affecter une valeur à une variable ?", "A) x = 10", "B) x vaut 10", "C) x est 10", "D) x <- 10", 2)
appelle ajouterQ(questions, "9. Quelle syntaxe crée un dictionnaire vide ?", "A) []", "B) {}", "C) [:]", "D) dict()", 3)
appelle ajouterQ(questions, "10. Quel mot-clé permet de sortir d'une boucle ?", "A) arrete", "B) break", "C) stopper", "D) fin", 1)
appelle ajouterQ(questions, "11. Quelle fonction calcule la racine carrée ?", "A) sqrt(x)", "B) racine(x)", "C) rad(x)", "D) sqr(x)", 2)
appelle ajouterQ(questions, "12. Quel mot-clé arrête le programme ?", "A) stopper", "B) fin", "C) quitter", "D) terminer", 4)
appelle ajouterQ(questions, "13. Comment s'écrit un commentaire d'une ligne ?", "A) //", "B) #", "C) --", "D) /*", 1)
appelle ajouterQ(questions, "14. Fonction pour tester la zone d'un clic ?", "A) cliquer()", "B) clique()", "C) zone()", "D) collision()", 2)
appelle ajouterQ(questions, "15. Comment ajouter un élément à un tableau t ?", "A) t.push(x)", "B) t ajoute x", "C) t + x", "D) t insere x", 2)
appelle ajouterQ(questions, "16. Quel est le type pour une chaîne de caractères ?", "A) string", "B) texte", "C) char", "D) message", 2)
appelle ajouterQ(questions, "17. Comment se termine une boucle tant que ?", "A) fin tant que", "B) fintantque", "C) wend", "D) fin tantque", 1)
appelle ajouterQ(questions, "18. Fonction pour mesurer un temps en ms ?", "A) temps()", "B) horloge()", "C) chrono()", "D) timer()", 3)
appelle ajouterQ(questions, "19. Opérateur pour le reste d'une division ?", "A) %", "B) mod", "C) rem", "D) div", 2)
appelle ajouterQ(questions, "20. Fonction pour effacer le dessin d'une toile ?", "A) nettoyer(t)", "B) effacer(t)", "C) raz(t)", "D) vider(t)", 2)

@ Boucle principale du jeu

maToile est une toile
dimension(maToile, 640, 440)

rejouer est un booléen
rejouer vaut vrai

tant que rejouer
    score est un nombre
    score vaut 0

    i est un nombre
    pour i de 0 à 19
        actuelle est un enregistrement
        actuelle vaut questions[i]

        choix est un nombre
        choix vaut 0

        // 1. Afficher l'énoncé de la question
        appelle dessinerQuiz(maToile, actuelle, i + 1, 20, score, 0, faux)

        // 2. Attente de la sélection utilisateur
        attenteClic est un booléen
        attenteClic vaut vrai
        clicX, clicY est un nombre

        tant que attenteClic
            cliquer maToile dans clicX, clicY

            si clique(clicX, clicY, 30, 140, 580, 48) alors
                choix vaut 1
                attenteClic vaut faux
            sinon si clique(clicX, clicY, 30, 200, 580, 48) alors
                choix vaut 2
                attenteClic vaut faux
            sinon si clique(clicX, clicY, 30, 260, 580, 48) alors
                choix vaut 3
                attenteClic vaut faux
            sinon si clique(clicX, clicY, 30, 320, 580, 48) alors
                choix vaut 4
                attenteClic vaut faux
            fin si
        fin tant que

        // Mise à jour du score
        si choix = actuelle.rep alors
            score ajoute 1
        fin si

        // 3. Afficher la correction
        appelle dessinerQuiz(maToile, actuelle, i + 1, 20, score, choix, vrai)

        // 4. Attente du clic sur "SUIVANT"
        attenteSuivant est un booléen
        attenteSuivant vaut vrai

        tant que attenteSuivant
            cliquer maToile dans clicX, clicY
            si clique(clicX, clicY, 220, 380, 200, 40) alors
                attenteSuivant vaut faux
            fin si
        fin tant que
    fin pour

    // Écran final de bilan
    effacer(maToile)
    remplir(maToile, #1A1D24)
    rectangle_arrondi(maToile, 70, 40, 500, 360, 12, #252A34)

    label(maToile, 220, 90, "QUIZ TERMINÉ !", #00ADB5, 24)

    pct est un nombre
    pct vaut arrondi(pourcentage(score, 20))

    label(maToile, 180, 160, "Votre score : " + convertir_texte(score) + " / 20", #EEEEEE, 20)
    label(maToile, 210, 210, "Réussite : " + convertir_texte(pct) + " %", #FFD700, 20)

    msg est un texte
    si pct >= 80 alors
        msg vaut "Excellent ! Vous maîtrisez EduCode !"
    sinon si pct >= 50 alors
        msg vaut "Bon travail ! Encore un petit effort."
    sinon
        msg vaut "Revoyez la spécification EduCode !"
    fin si

    label(maToile, 140, 270, msg, #FFFFFF, 15)

    // Bouton Rejouer
    rectangle_arrondi(maToile, 220, 320, 200, 50, 25, #00ADB5)
    label(maToile, 275, 352, "REJOUER", #FFFFFF, 16)
    affiche maToile

    // Attente du clic sur "REJOUER"
    attenteRejouer est un booléen
    attenteRejouer vaut vrai

    tant que attenteRejouer
        cliquer maToile dans clicX, clicY
        si clique(clicX, clicY, 220, 320, 200, 50) alors
            attenteRejouer vaut faux
        fin si
    fin tant que
fin tant que
Pas encore noté
Cette mise à jour apporte des améliorations majeures au moteur et à l’environnement de développement
Actualité

Version 3.2 : en cours de finalisation !

Cette mise à jour apporte des améliorations majeures au moteur et à l’environnement de développement

Lire l'articleReplier l'article

Performances

Optimisation profonde de l’interpréteur : jusqu’à 30 % de gain sur les tâches intensives (animations, 3D, simulations, boucles lourdes).

Nouveau mot-clé : cliquer

Attendez un clic utilisateur sur une toile et récupérez directement les coordonnées :

tant que choix = 0
    affiche maToile
    cliquer maToile dans x, y

    si clique(x, y, 40, 80, 100, 45) alors
        choix vaut 1
    fin si
    si clique(x, y, 210, 102, 28) alors
        choix vaut 2
    fin si
fin tant que

Affectation directe dans les propriétés d’objet

Fini les variables temporaires :

classe joueur:
    nom est un texte
    score est un nombre
fin classe
demande "Ton nom ? " dans joueur.nom

Nouvelles fonctions intégrées

  • liste_fichiers : remplit un tableau avec les chemins complets des fichiers.
  • clique : vérifie si un clic se situe dans une zone définie de la toile.
maToile est une toile
x, y est un nombre
rectangle(maToile, 40, 80, 100, 45, #1976d2)
cliquer maToile dans  x, y

si clique(x, y, 40, 80, 100, 45) alors

Interface améliorée

  • Correction du positionnement du menu d’auto-complétion.
  • Nouveau panneau Structure du programme : vue hiérarchique, filtre, mise à jour en direct.

Nouveauté : Favoris

Ajoutez vos programmes à vos favoris, retrouvez vos projets en cours en un instant, et organisez votre liste selon vos besoins.

La version 3.2 arrive très bientôt. Préparez-vous à un vrai confort de développement.

Pas encore noté
Choisissez la puissance adaptée à vos projets éducatifs, animations et exercices de logique.
Actualité

Matériel recommandé pour une utilisation fluide d’EduCode

EduCode : Du simple projet aux animations avancées, quelle configuration choisir ?

Lire l'articleReplier l'article

Configuration minimale

Pour l’initiation, les projets simples et les petites animations.

  • CPU : Intel Core i3 / i5 (4–6 cœurs) ou AMD Ryzen 3 / 5
  • RAM : 8 Go

Configuration recommandée

Pour un usage fluide, des projets éducatifs intermédiaires et des animations plus complexes.

  • CPU : Intel Core i5 / i7 ou AMD Ryzen 5 / 7
  • RAM : 16 Go
Pas encore noté
Un projet inspiré du jeu Isotiles – Isometric Puzzle Game
Article

ISOTILES - Isometric Puzzle Game

ISOTILES : Quand l’algorithmique façonne un puzzle isométrique intelligent

Lire l'articleReplier l'article

Un projet inspiré du jeu Isotiles – Isometric Puzzle Game (Steam), revisité pour l’apprentissage et l’expérimentation.

ISOTILES – est un moteur de génération de puzzles isométriques conçu pour explorer la logique, l’algorithmique et le game design. Le concept s’appuie sur une idée simple : chaque tuile représente un nombre de passages possibles, et le joueur doit consommer ces passages avant d’atteindre la tuile d’arrivée.

Un système de tuiles basé sur la logique des passages

Chaque couleur correspond à une contrainte précise :

  • Bleu : 1 passage
  • Violet : 2 passages
  • Gris : pont neutre
  • Rouge : départ
  • Jaune : arrivée

L’objectif est de parcourir la carte en optimisant ses déplacements, tout en évitant les situations de blocage. Une fois tous les passages consommés, la tuile d’arrivée devient accessible.

Un générateur de cartes 100 % solvables

Le cœur du projet repose sur un algorithme qui :

  • construit un chemin aléatoire contrôlé,
  • évite les retours inutiles,
  • vérifie que la case d’arrivée n’est visitée qu’une seule fois,
  • calcule le nombre réel de passages par tuile,
  • attribue automatiquement les couleurs,
  • centre la carte dans l’espace isométrique,
  • garantit que chaque puzzle est solvable, sans être trivial.

Ce système permet de produire des cartes uniques, cohérentes et adaptées à un apprentissage progressif.

Un rendu isométrique généré entièrement par code

Le moteur graphique :

  • calcule les coordonnées isométriques,
  • dessine les faces des tuiles (haut, gauche, droite),
  • applique les couleurs et ombrages,
  • affiche un personnage stylisé,
  • met à jour l’affichage en temps réel selon les déplacements.

Le résultat est un environnement visuel propre, lisible et entièrement généré par le programme.

Un outil pédagogique pour comprendre l’algorithmique

ISOTILES – constitue un excellent support pour :

  • enseigner les structures de données,
  • comprendre la génération procédurale,
  • manipuler les coordonnées isométriques,
  • analyser les états de jeu (victoire, blocage),
  • expérimenter la logique de parcours et de contraintes.

Ce projet combine mathématiques, logique, créativité et programmation, et peut servir de base à des ateliers, des cours ou des projets personnels.

Programme :

@ Initialisation du moteur et des variables
graph est une toile
dimension(graph, 600, 500)

jX est un nombre
jY est un nombre
echelle est un nombre
echelle vaut 1.3
action est un nombre

map est un dictionnaire
nbBleu est un nombre
etatJeu est un nombre // 0 = En cours, 1 = Gagné, 2 = Bloqué

minX est un nombre
maxX est un nombre
minY est un nombre
maxY est un nombre
centreMapX est un nombre
centreMapY est un nombre

@ Générateur de cartes complexes et 100% solvables
procédure genererMap()
    map vaut [:]
    nbBleu vaut 0
    etatJeu vaut 0

    longueurChemin est un nombre
    longueurChemin vaut 24

    reussi est un booléen
    reussi vaut faux
    tentatives est un nombre
    tentatives vaut 0

    tant que non reussi et tentatives < 200
        tentatives ajoute 1
        map vaut [:]

        cheminX est un tableau
        cheminY est un tableau
        cheminX ajoute 0
        cheminY ajoute 0

        currX est un nombre
        currY est un nombre
        currX vaut 0
        currY vaut 0

        pasStep est un nombre
        pour pasStep de 1 à longueurChemin
            voisinsX est un tableau
            voisinsY est un tableau

            // Nord (interdit de revenir au départ 0,0)
            si currY - 1 <> 0 ou currX <> 0 alors
                voisinsX ajoute currX
                voisinsY ajoute currY - 1
            fin si
            // Sud
            si currY + 1 <> 0 ou currX <> 0 alors
                voisinsX ajoute currX
                voisinsY ajoute currY + 1
            fin si
            // Ouest
            si currY <> 0 ou currX - 1 <> 0 alors
                voisinsX ajoute currX - 1
                voisinsY ajoute currY
            fin si
            // Est
            si currY <> 0 ou currX + 1 <> 0 alors
                voisinsX ajoute currX + 1
                voisinsY ajoute currY
            fin si

            choixIdx est un nombre
            choixIdx vaut hasard(0, longueur(voisinsX) - 1)

            currX vaut voisinsX[choixIdx]
            currY vaut voisinsY[choixIdx]

            cheminX ajoute currX
            cheminY ajoute currY
        fin pour

        endX est un nombre
        endY est un nombre
        endX vaut currX
        endY vaut currY

        // Vérification que la case d'arrivée n'est visitée qu'à la fin
        nbFinVisites est un nombre
        nbFinVisites vaut 0
        idxStep est un nombre
        pour idxStep de 0 à longueurChemin
            si cheminX[idxStep] = endX et cheminY[idxStep] = endY alors
                nbFinVisites ajoute 1
            fin si
        fin pour

        si (endX <> 0 ou endY <> 0) et nbFinVisites = 1 alors
            reussi vaut vrai

            visites est un dictionnaire
            pour idxStep de 1 à longueurChemin - 1
                kVis est un texte
                kVis vaut convertir_texte(cheminX[idxStep]) + "_" + convertir_texte(cheminY[idxStep])
                si contient(visites, kVis) alors
                    vCount est un nombre
                    vCount vaut visites[kVis]
                    visites[kVis] vaut vCount + 1
                sinon
                    visites[kVis] vaut 1
                fin si
            fin pour

            tabKeysVisites est un tableau
            tabKeysVisites vaut cles(visites)

            iVis est un nombre
            pour iVis de 0 à longueur(tabKeysVisites) - 1
                kVis vaut tabKeysVisites[iVis]
                numPassages est un nombre
                numPassages vaut visites[kVis]

                si numPassages = 1 alors
                    map[kVis] vaut 1 // Bleue (1 passage)
                sinon si numPassages = 2 alors
                    map[kVis] vaut 5 // Violette (2 passages)
                sinon
                    map[kVis] vaut 3 // Grise (Pont / Neutre)
                fin si
            fin pour

            // Arrivée (Jaune = 4)
            cleFin est un texte
            cleFin vaut convertir_texte(endX) + "_" + convertir_texte(endY)
            map[cleFin] vaut 4
        fin si
    fin tant que

    // Départ (Rouge = 2)
    map["0_0"] vaut 2
    jX vaut 0
    jY vaut 0

    // Calcul du nombre de passages et centrage
    tabCles est un tableau
    tabCles vaut cles(map)
    nbBleu vaut 0

    i est un nombre
    pour i de 0 à longueur(tabCles) - 1
        kTmp est un texte
        kTmp vaut tabCles[i]
        tVal est un nombre
        tVal vaut map[kTmp]

        si tVal = 1 alors
            nbBleu ajoute 1
        sinon si tVal = 5 alors
            nbBleu ajoute 2
        fin si

        coords est un tableau
        coords vaut decouper(kTmp, "_")
        cxVal est un nombre
        cyVal est un nombre
        cxVal vaut convertir_nombre(coords[0])
        cyVal vaut convertir_nombre(coords[1])

        si i = 0 alors
            minX vaut cxVal
            maxX vaut cxVal
            minY vaut cyVal
            maxY vaut cyVal
        sinon
            minX vaut minimum(minX, cxVal)
            maxX vaut maximum(maxX, cxVal)
            minY vaut minimum(minY, cyVal)
            maxY vaut maximum(maxY, cyVal)
        fin si
    fin pour

    centreMapX vaut (minX + maxX) / 2
    centreMapY vaut (minY + maxY) / 2
fin procédure

// Génération initiale
appelle genererMap()

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

    dx est un nombre
    dy est un nombre
    dx vaut 24 * echelle
    dy vaut 12 * echelle

    sMin est un nombre
    sMax est un nombre
    sMin vaut (minX - 1) + (minY - 1)
    sMax vaut (maxX + 1) + (maxY + 1)

    sSum est un nombre
    gx est un nombre
    pour sSum de sMin à sMax
        pour gx de minX - 1 à maxX + 1
            gy est un nombre
            gy vaut sSum - gx

            si gy >= minY - 1 et gy <= maxY + 1 alors
                txtCleCoord est un texte
                txtCleCoord vaut convertir_texte(gx) + "_" + convertir_texte(gy)

                si contient(map, txtCleCoord) alors
                    typeTuile est un nombre
                    typeTuile vaut map[txtCleCoord]

                    relX est un nombre
                    relY est un nombre
                    relX vaut gx - centreMapX
                    relY vaut gy - centreMapY

                    cx est un nombre
                    cy est un nombre
                    cx vaut 300 + (relX - relY) * dx
                    cy vaut 250 + (relX + relY) * dy

                    hauteurBase est un nombre
                    hauteurBase vaut 10 * echelle

                    cTop est un texte
                    cGau est un texte
                    cDro est un texte

                    si typeTuile = 1 alors
                        cTop vaut #38bdf8 // Bleu (1 passage)
                        cGau vaut #0284c7
                        cDro vaut #0369a1
                    sinon si typeTuile = 2 alors
                        cTop vaut #f87171 // Rouge (Consommé)
                        cGau vaut #dc2626
                        cDro vaut #991b1b
                    sinon si typeTuile = 3 alors
                        cTop vaut #94a3b8 // Gris (Pont / Neutre)
                        cGau vaut #64748b
                        cDro vaut #475569
                    sinon si typeTuile = 4 alors
                        cTop vaut #facc15 // Jaune (Arrivée)
                        cGau vaut #ca8a04
                        cDro vaut #854d0e
                    sinon si typeTuile = 5 alors
                        cTop vaut #c084fc // Violet (2 passages)
                        cGau vaut #9333ea
                        cDro vaut #6b21a8
                    fin si

                    lCY est un nombre
                    lCY vaut cy - hauteurBase

                    ptsFG est un tableau
                    ptsFG ajoute cx - dx
                    ptsFG ajoute lCY
                    ptsFG ajoute cx
                    ptsFG ajoute lCY + dy
                    ptsFG ajoute cx
                    ptsFG ajoute lCY + dy + hauteurBase
                    ptsFG ajoute cx - dx
                    ptsFG ajoute lCY + hauteurBase
                    polygone(graph, ptsFG, cGau)

                    ptsFD est un tableau
                    ptsFD ajoute cx
                    ptsFD ajoute lCY + dy
                    ptsFD ajoute cx + dx
                    ptsFD ajoute lCY
                    ptsFD ajoute cx + dx
                    ptsFD ajoute lCY + hauteurBase
                    ptsFD ajoute cx
                    ptsFD ajoute lCY + dy + hauteurBase
                    polygone(graph, ptsFD, cDro)

                    ptsTop est un tableau
                    ptsTop ajoute cx
                    ptsTop ajoute lCY - dy
                    ptsTop ajoute cx + dx
                    ptsTop ajoute lCY
                    ptsTop ajoute cx
                    ptsTop ajoute lCY + dy
                    ptsTop ajoute cx - dx
                    ptsTop ajoute lCY
                    polygone(graph, ptsTop, cTop)

                    contour(graph, rgba(255, 255, 255, 0.15), 1)

                    // Joueur
                    si gx = jX et gy = jY alors
                        pCY est un nombre
                        pCY vaut lCY - (2 * echelle)

                        pDx est un nombre
                        pDy est un nombre
                        pDz est un nombre
                        pDx vaut 8 * echelle
                        pDy vaut 4 * echelle
                        pDz vaut 14 * echelle

                        ellipse(graph, cx, pCY + pDy, 14 * echelle, 7 * echelle, rgba(0, 0, 0, 0.4))

                        pJG est un tableau
                        pJG ajoute cx - pDx
                        pJG ajoute pCY - pDz
                        pJG ajoute cx
                        pJG ajoute pCY - pDz + pDy
                        pJG ajoute cx
                        pJG ajoute pCY + pDy
                        pJG ajoute cx - pDx
                        pJG ajoute pCY
                        polygone(graph, pJG, #cbd5e1)

                        pJD est un tableau
                        pJD ajoute cx
                        pJD ajoute pCY - pDz + pDy
                        pJD ajoute cx + pDx
                        pJD ajoute pCY - pDz
                        pJD ajoute cx + pDx
                        pJD ajoute pCY
                        pJD ajoute cx
                        pJD ajoute pCY + pDy
                        polygone(graph, pJD, #94a3b8)

                        pJT est un tableau
                        pJT ajoute cx
                        pJT ajoute pCY - pDz - pDy
                        pJT ajoute cx + pDx
                        pJT ajoute pCY - pDz
                        pJT ajoute cx
                        pJT ajoute pCY - pDz + pDy
                        pJT ajoute cx - pDx
                        pJT ajoute pCY - pDz
                        polygone(graph, pJT, #ffffff)

                        contour(graph, #0)

                        polygone(graph, cx + (1 * echelle), pCY - pDz + (2 * echelle), cx + (6 * echelle), pCY - pDz - (0.5 * echelle), cx + (6 * echelle), pCY - (3 * echelle), cx + (1 * echelle), pCY - (0.5 * echelle), #38bdf8)
                    fin si
                fin si
            fin si
        fin pour
    fin pour

    // Interface utilisateur
    rectangle(graph, 0, 0, 600, 65, rgba(15, 23, 42, 0.9))
    label(graph, 12, 18, "ISOTILES - PUZZLE EDUCODE v3.1", #ffffff, 13)

    si etatJeu = 1 alors
        label(graph, 12, 36, "VICTOIRE ! Appuie sur [R] pour la carte suivante.", #4ade80, 12)
    sinon si etatJeu = 2 alors
        label(graph, 12, 36, "BLOQUÉ ! Plus de coup possible. Appuie sur [R].", #f87171, 12)
    sinon
        txtInfos est un texte
        txtInfos vaut "Passages restants : " + convertir_texte(nbBleu)
        label(graph, 12, 36, txtInfos, #38bdf8, 12)
    fin si

    label(graph, 12, 52, "Bleu = 1 pass. | Violet = 2 pass. | Gris = Pont", #94a3b8, 12)

    affiche graph

    appuyer ["z", "s", "q", "d", "r"] dans action

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

    si action >= 1 et action <= 4 et etatJeu = 0 alors
        si action = 1 alors
            mvtY vaut - 1
        sinon si action = 2 alors
            mvtY vaut 1
        sinon si action = 3 alors
            mvtX vaut - 1
        sinon si action = 4 alors
            mvtX vaut 1
        fin si

        cibX est un nombre
        cibY est un nombre
        cibX vaut jX + mvtX
        cibY vaut jY + mvtY

        txtCleCib est un texte
        txtCleCib vaut convertir_texte(cibX) + "_" + convertir_texte(cibY)

        si contient(map, txtCleCib) alors
            typeCib est un nombre
            typeCib vaut map[txtCleCib]

            si typeCib = 1 ou typeCib = 3 ou typeCib = 4 ou typeCib = 5 alors
                jX vaut cibX
                jY vaut cibY

                si typeCib = 1 alors
                    map[txtCleCib] vaut 2
                    nbBleu soustrait 1
                sinon si typeCib = 5 alors
                    map[txtCleCib] vaut 1
                    nbBleu soustrait 1
                fin si

                si typeCib = 4 et nbBleu = 0 alors
                    etatJeu vaut 1
                fin si

                // Test de blocage
                si etatJeu = 0 alors
                    txtNord est un texte
                    txtNord vaut convertir_texte(jX) + "_" + convertir_texte(jY - 1)
                    txtSud est un texte
                    txtSud vaut convertir_texte(jX) + "_" + convertir_texte(jY + 1)
                    txtOuest est un texte
                    txtOuest vaut convertir_texte(jX - 1) + "_" + convertir_texte(jY)
                    txtEst est un texte
                    txtEst vaut convertir_texte(jX + 1) + "_" + convertir_texte(jY)

                    vN est un booléen
                    vN vaut faux
                    si contient(map, txtNord) alors
                        typeN est un nombre
                        typeN vaut map[txtNord]
                        si typeN = 1 ou typeN = 3 ou typeN = 5 ou (typeN = 4 et nbBleu = 0) alors
                            vN vaut vrai
                        fin si
                    fin si

                    vS est un booléen
                    vS vaut faux
                    si contient(map, txtSud) alors
                        typeS est un nombre
                        typeS vaut map[txtSud]
                        si typeS = 1 ou typeS = 3 ou typeS = 5 ou (typeS = 4 et nbBleu = 0) alors
                            vS vaut vrai
                        fin si
                    fin si

                    vO est un booléen
                    vO vaut faux
                    si contient(map, txtOuest) alors
                        typeO est un nombre
                        typeO vaut map[txtOuest]
                        si typeO = 1 ou typeO = 3 ou typeO = 5 ou (typeO = 4 et nbBleu = 0) alors
                            vO vaut vrai
                        fin si
                    fin si

                    vE est un booléen
                    vE vaut faux
                    si contient(map, txtEst) alors
                        typeE est un nombre
                        typeE vaut map[txtEst]
                        si typeE = 1 ou typeE = 3 ou typeE = 5 ou (typeE = 4 et nbBleu = 0) alors
                            vE vaut vrai
                        fin si
                    fin si

                    si non vN et non vS et non vO et non vE alors
                        etatJeu vaut 2
                    fin si
                fin si
            fin si
        fin si
    fin si

    si action = 5 alors
        appelle genererMap()
    fin si

fin tant que
Pas encore noté
Générer un Sudoku complet : un projet mêlant algorithmique, logique et rendu graphique
Article

Générer un Sudoku complet

Un projet mêlant algorithmique, logique et rendu graphique

Lire l'articleReplier l'article

Ce programme propose une approche complète pour créer un Sudoku jouable : génération d’une grille valide, création d’un puzzle avec cases manquantes, et rendu visuel sur une toile.
Il combine algorithmique, backtracking, manipulation de tableaux et dessin d’interface.

Construction de la grille : une structure 9×9 prête à être remplie

La première étape consiste à initialiser une grille vide de 9 lignes et 9 colonnes, chaque case contenant la valeur 0.
Cette structure sert de base à l’algorithme de résolution.

Vérification des contraintes : respecter les règles du Sudoku

La fonction de validation contrôle trois éléments essentiels :

  • la ligne,
  • la colonne,
  • le bloc 3×3.

Elle garantit qu’un chiffre proposé ne viole aucune règle du Sudoku.
Cette étape est indispensable pour permettre un remplissage correct de la grille.

Génération de la solution : un backtracking complet

L’algorithme de backtracking remplit la grille en cherchant la prochaine case vide, puis en essayant les chiffres de 1 à 9 dans un ordre aléatoire.

Pour chaque tentative :

  • si le chiffre est valide, il est placé,
  • l’algorithme continue,
  • en cas d’impasse, il revient en arrière.

Ce processus se poursuit jusqu’à ce que la grille soit entièrement remplie.
Le résultat est une solution complète et cohérente.

Création du puzzle : retirer des valeurs pour former l’énigme

Une fois la grille solutionnée, le programme retire un nombre défini de cases (ici 45).
Chaque retrait est aléatoire, ce qui permet de générer des puzzles variés.
Le niveau de difficulté dépend du nombre de cases supprimées.

Rendu graphique : dessiner un Sudoku lisible et structuré Le programme utilise une toile de 450×450 pixels pour afficher la grille.

Le rendu inclut :

  • les chiffres présents,
  • les lignes de séparation,
  • des traits plus épais pour les blocs 3×3,
  • une mise en page claire et minimaliste.

Chaque case est représentée par un espace de 50×50 pixels, ce qui assure une lecture confortable.

Exécution finale : un Sudoku généré et affiché automatiquement

Le programme :

  • génère une solution complète,
  • crée un puzzle en retirant des valeurs,
  • dessine la grille sur une toile,
  • affiche le résultat.

Il constitue une base solide pour aller plus loin : ajout d’interactions, vérification des réponses, interface utilisateur, ou export du puzzle.

Conclusion

Ce projet démontre comment un Sudoku peut être généré de manière algorithmique grâce au backtracking, puis rendu visuellement via une interface graphique.
Il combine logique, structure de données, aléatoire contrôlé et dessin, offrant un excellent exercice pour progresser en algorithmique et en développement d’outils interactifs.

Programme :

?@ Initialisation

// Déclaration de la grille de Sudoku 9x9
grille_sudoku est un tableau
pour lig de 0 à 8
    ligne_g est un tableau
    pour col de 0 à 8
        ligne_g ajoute 0
    fin pour
    grille_sudoku ajoute ligne_g
fin pour

@ Fonctions de résolution (Backtracking)

// Vérifie si placer 'val' aux coordonnées (l, c) est autorisé par les règles
fonction estValide(g, l, c, val)
    // 1. Vérification de la ligne
    pour i de 0 à 8
        si g[l][i] = val alors
            retourne faux
        fin si
    fin pour

    // 2. Vérification de la colonne
    pour i de 0 à 8
        si g[i][c] = val alors
            retourne faux
        fin si
    fin pour

    // 3. Vérification du bloc 3x3
    debutL est un nombre
    debutL vaut arrondi_inferieur(l / 3) * 3
    debutC est un nombre
    debutC vaut arrondi_inferieur(c / 3) * 3

    pour i de 0 à 2
        pour j de 0 à 2
            si g[debutL + i][debutC + j] = val alors
                retourne faux
            fin si
        fin pour
    fin pour

    retourne vrai
fin fonction

// Algorithme de Backtracking pour remplir la grille
fonction generer(g)
    l est un nombre
    c est un nombre
    trouve est un booléen
    trouve vaut faux

    // Trouver la prochaine case vide (valeur 0)
    pour i de 0 à 8
        pour j de 0 à 8
            si g[i][j] = 0 alors
                l vaut i
                c vaut j
                trouve vaut vrai
                arrête
            fin si
        fin pour
        si trouve = vrai alors
            arrête
        fin si
    fin pour

    // Condition d'arrêt : plus aucune case vide, le Sudoku est terminé
    si trouve = faux alors
        retourne vrai
    fin si

    // Préparer les chiffres de 1 à 9 et les mélanger pour la génération aléatoire
    possibles est un tableau
    pour n de 1 à 9
        possibles ajoute n
    fin pour
    possibles vaut melanger(possibles)

    num est un nombre
    valide est un booléen
    suite est un booléen

    pour k de 0 à 8
        num vaut possibles[k]
        valide vaut estValide(g, l, c, num)

        si valide = vrai alors
            // Essayer ce chiffre
            g[l][c] vaut num

            // Continuer avec la grille modifiée
            suite vaut generer(g)
            si suite = vrai alors
                retourne vrai
            fin si

            // Échec (impasse) : annuler (Backtrack) et essayer le suivant
            g[l][c] vaut 0
        fin si
    fin pour

    // Aucun chiffre n'a fonctionné, on doit reculer
    retourne faux
fin fonction

@ Création du puzzle

// Retire un nombre défini de chiffres pour le joueur
procédure faireTrous(g, nb)
    trous_faits est un nombre
    trous_faits vaut 0
    l est un nombre
    c est un nombre

    tant que trous_faits < nb
        l vaut hasard(0, 8)
        c vaut hasard(0, 8)
        si g[l][c] <> 0 alors
            g[l][c] vaut 0
            trous_faits ajoute 1
        fin si
    fin tant que
fin procédure

@ Affichage sur la toile

// Rendu visuel de la grille de Sudoku
procédure dessinerSudoku(t, g)
    remplir(t, #FFFFFF)

    x est un nombre
    y est un nombre
    val est un nombre
    txt est un texte

    // 1. Dessiner les cases et les chiffres
    pour l de 0 à 8
        pour c de 0 à 8
            x vaut c * 50
            y vaut l * 50
            val vaut g[l][c]

            si val <> 0 alors
                txt vaut convertir_texte(val)
                label(t, x + 16, y + 35, txt, #1E2430, 24)
            fin si
        fin pour
    fin pour

    // 2. Dessiner les lignes de la grille
    epaisseur est un nombre
    pour i de 0 à 9
        // Les lignes séparant les blocs 3x3 sont plus épaisses
        si i mod 3 = 0 alors
            epaisseur vaut 3
        sinon
            epaisseur vaut 1
        fin si

        // Lignes verticales
        ligne(t, i * 50, 0, i * 50, 450, #000000, epaisseur)
        // Lignes horizontales
        ligne(t, 0, i * 50, 450, i * 50, #000000, epaisseur)
    fin pour
fin procédure

@ Exécution principale

// 1. Remplir complètement la grille avec une solution valide
succes est un booléen
succes vaut generer(grille_sudoku)

// 2. Retirer 45 numéros pour créer le défi
appelle faireTrous(grille_sudoku, 45)

// 3. Préparer et afficher la toile (canevas)
ma_toile est une toile
dimension(ma_toile, 450, 450)
appelle dessinerSudoku(ma_toile, grille_sudoku)

affiche ma_toile
Pas encore noté
Génération d’une surface 3D Peaks : fonctionnement détaillé du programme
Article

Génération d’une surface 3D Peaks

Génération d’une surface 3D Peaks : fonctionnement détaillé du programme

Lire l'articleReplier l'article

Ce programme construit et affiche une surface 3D statique de type peaks, rendue en projection isométrique, avec facettes pleines, tri de profondeur et trois grilles fixes formant un repère spatial. Voici une analyse complète de son fonctionnement.

Paramétrage de la scène

La toile est définie avec une largeur de 560 px et une hauteur de 520 px. Le centre visuel est légèrement abaissé pour compenser la hauteur du relief :

  • OX = LARG / 2
  • OY = HAUT / 2 + 40

L’angle de vue est une projection iso inclinée, définie par :

  • sinA = 0,34
  • cosA = 0,62

Deux échelles sont utilisées :

  • sXY = 62 pour les axes X et Y
  • sZ = 13 pour la hauteur

La résolution de la grille est fixée à N = 22, avec un domaine allant de xmin = -3 à xmax = 3.

Calcul du relief : la fonction « peaks »

Le programme pré-calcule les hauteurs z pour chaque point de la grille. Pour chaque couple (i, j), il calcule :

  • xx et yy : coordonnées normalisées

Trois composantes du relief :

  • La hauteur finale est : z = g1 + g2 + g3

Toutes les valeurs sont stockées dans un tableau linéaire.

Colormap « jet »

Une fonction dédiée convertit une hauteur normalisée en couleur RGB. Elle reproduit le célèbre colormap jet :

  • Bleu ? Vert ? Jaune ? Rouge selon la valeur de hn (0 à 1).

Cette palette renforce la lisibilité du relief.

Construction du repère 3D

Avant de dessiner la surface, le programme trace trois grilles fixes :

  • Plan XY (sol)
  • Plan XZ (mur arrière gauche)
  • Plan YZ (mur arrière droit)

Chaque grille est dessinée en lignes fines (#dddddd), ce qui crée un repère spatial clair et stable.

Projection 3D ? 2D avec rotation

La procédure dessinerVue(rot) applique une rotation horizontale :

  • cr = cos(rot)
  • sr = sin(rot)

Chaque point (x, y, z) est transformé en coordonnées tournées (xr, yr) puis projeté en 2D via l’angle iso :

  • pX = OX + (xr·sXY – yr·sXY)·cosA pY = OY – z·sZ – (xr·sXY + yr·sXY)·sinA

Cette projection donne l’effet 3D sans utiliser de moteur graphique.

Tri de profondeur : rendu correct des facettes

Pour éviter que les facettes s’affichent dans le mauvais ordre, le programme utilise un tri par bandes de profondeur :

Chaque facette reçoit une valeur de profondeur.
Le programme parcourt les bandes de la plus éloignée à la plus proche.
Il dessine uniquement les facettes appartenant à la bande courante.

Ce tri garantit un rendu propre, sans chevauchements incohérents.

Dessin des facettes

Chaque cellule de la grille est rendue comme un polygone à 4 sommets :

  • Les quatre hauteurs za, zb, zc, zd sont récupérées.
  • Les quatre coins sont projetés en 2D.
  • La couleur est déterminée par la hauteur moyenne.
  • Le polygone est dessiné avec un contour sombre (#0a0a0f).

Le résultat est une surface 3D colorée, continue et lisible.

Résultat final

La toile affiche :

  • Le repère 3D
  • La surface « peaks » entièrement colorée
  • Un titre discret : Surface 3D « peaks »

L’appel final : dessinerVue(0) produit une vue alignée, sans rotation.

Conclusion

Ce programme combine plusieurs techniques graphiques :

  • Calcul de relief paramétrique
  • Projection isométrique
  • Colormap dynamique
  • Tri de profondeur manuel
  • Rendu polygonal optimisé

Il démontre comment produire une surface 3D complète sans moteur 3D, uniquement via calculs géométriques et dessin 2D. Une base solide pour des visualisations scientifiques, des rendus mathématiques ou des démonstrations pédagogiques.

Programme :

// ============================================================
//   SURFACE 3D "peaks" — centrée, statique (sans rotation)
//   Facettes pleines, colormap jet, fond noir, tri de profondeur.
//   INCLUS : 3 Grilles fixes (XY, XZ, YZ) formant le repère.
// ============================================================

LARG est un nombre
LARG vaut 560
HAUT est un nombre
HAUT vaut 520

// ---- Centre de la toile (surface centrée) ----
OX est un nombre
OY est un nombre
OX vaut LARG / 2
OY vaut HAUT / 2 + 40 // légèrement bas pour compenser la hauteur du relief

// ---- Angle de vue iso (inclinaison verticale) ----
sinA est un nombre
cosA est un nombre
sinA vaut 0,34
cosA vaut 0,62

// ---- Échelles ----
sXY est un nombre
sZ est un nombre
sXY vaut 62
sZ vaut 13

// ---- Résolution ----
N est un nombre
N vaut 22

xmin est un nombre
xmax est un nombre
xmin vaut - 3
xmax vaut 3

// ---- Pré-calcul des hauteurs z : index(i,j) = j*(N+1) + i ----
hauteurs est un tableau
j est un nombre
pour j de 0 à N
    i est un nombre
    pour i de 0 à N
        xx est un nombre
        yy est un nombre
        xx vaut xmin + (xmax - xmin) * i / N
        yy vaut xmin + (xmax - xmin) * j / N
        g1 est un nombre
        g2 est un nombre
        g3 est un nombre
        g1 vaut 3 * (1 - xx) * (1 - xx) * puissance(2.71828, -(xx * xx) - (yy + 1) * (yy + 1))
        g2 vaut - 10 * (xx / 5 - puissance(xx, 3) - puissance(yy, 5)) * puissance(2.71828, -(xx * xx) - (yy * yy))
        g3 vaut - 1 / 3 * puissance(2.71828, -((xx + 1) * (xx + 1)) - (yy * yy))
        hauteurs ajoute g1 + g2 + g3
    fin pour
fin pour

// ---- Couleur "jet" selon hauteur normalisée hn (0..1) ----
fonction couleur_jet(hn)
    hnc est un nombre
    hnc vaut limiter(hn, 0, 1)
    rC est un nombre
    gC est un nombre
    bC est un nombre
    rC vaut arrondi(255 * limiter(1.5 - absolue(4 * hnc - 3), 0, 1))
    gC vaut arrondi(255 * limiter(1.5 - absolue(4 * hnc - 2), 0, 1))
    bC vaut arrondi(255 * limiter(1.5 - absolue(4 * hnc - 1), 0, 1))
    retourne rgb(rC, gC, bC)
fin fonction

// ============================================================
//  DESSINE L'IMAGE pour un angle de rotation "rot" (radians)
// ============================================================
procédure dessinerVue(rot)
    Fig est une toile
    dimension(Fig, LARG, HAUT)
    remplir(Fig, #ffffff)

    cr est un nombre
    sr est un nombre
    cr vaut cosinus(rot)
    sr vaut sinus(rot)

    // --- DESSIN DES 3 GRILLES FIXES (XY, XZ, YZ) ---
    zBase est un nombre
    zBase vaut - 6
    zMax est un nombre
    zMax vaut 8

    k est un nombre
    pour k de 0 à 10
        yL est un nombre
        yL vaut xmin + (xmax - xmin) * k / 10
        xL est un nombre
        xL vaut xmin + (xmax - xmin) * k / 10
        zL est un nombre
        zL vaut zBase + (zMax - zBase) * k / 10

        // 1. GRILLE SOL (Plan XY à Z = zBase)
        px1, py1, px2, py2 est un nombre
        px1 vaut OX + (xmin * sXY - yL * sXY) * cosA
        py1 vaut OY - zBase * sZ - (xmin * sXY + yL * sXY) * sinA
        px2 vaut OX + (xmax * sXY - yL * sXY) * cosA
        py2 vaut OY - zBase * sZ - (xmax * sXY + yL * sXY) * sinA
        ligne(Fig, px1, py1, px2, py2, #dddddd, 1)

        px3, py3, px4, py4 est un nombre
        px3 vaut OX + (xL * sXY - xmin * sXY) * cosA
        py3 vaut OY - zBase * sZ - (xL * sXY + xmin * sXY) * sinA
        px4 vaut OX + (xL * sXY - xmax * sXY) * cosA
        py4 vaut OY - zBase * sZ - (xL * sXY + xmax * sXY) * sinA
        ligne(Fig, px3, py3, px4, py4, #dddddd, 1)

        // 2. GRILLE MUR XZ (Arrière-gauche, à Y = xmax)
        px5, py5, px6, py6 est un nombre
        px5 vaut OX + (xL * sXY - xmax * sXY) * cosA
        py5 vaut OY - zBase * sZ - (xL * sXY + xmax * sXY) * sinA
        px6 vaut OX + (xL * sXY - xmax * sXY) * cosA
        py6 vaut OY - zMax * sZ - (xL * sXY + xmax * sXY) * sinA
        ligne(Fig, px5, py5, px6, py6, #dddddd, 1)

        px7, py7, px8, py8 est un nombre
        px7 vaut OX + (xmin * sXY - xmax * sXY) * cosA
        py7 vaut OY - zL * sZ - (xmin * sXY + xmax * sXY) * sinA
        px8 vaut OX + (xmax * sXY - xmax * sXY) * cosA
        py8 vaut OY - zL * sZ - (xmax * sXY + xmax * sXY) * sinA
        ligne(Fig, px7, py7, px8, py8, #dddddd, 1)

        // 3. GRILLE MUR YZ (Arrière-droit, à X = xmax)
        px9, py9, px10, py10 est un nombre
        px9 vaut OX + (xmax * sXY - yL * sXY) * cosA
        py9 vaut OY - zBase * sZ - (xmax * sXY + yL * sXY) * sinA
        px10 vaut OX + (xmax * sXY - yL * sXY) * cosA
        py10 vaut OY - zMax * sZ - (xmax * sXY + yL * sXY) * sinA
        ligne(Fig, px9, py9, px10, py10, #dddddd, 1)

        px11, py11, px12, py12 est un nombre
        px11 vaut OX + (xmax * sXY - xmin * sXY) * cosA
        py11 vaut OY - zL * sZ - (xmax * sXY + xmin * sXY) * sinA
        px12 vaut OX + (xmax * sXY - xmax * sXY) * cosA
        py12 vaut OY - zL * sZ - (xmax * sXY + xmax * sXY) * sinA
        ligne(Fig, px11, py11, px12, py12, #dddddd, 1)
    fin pour

    // --- Passe unique avec tri par profondeur (bandes) pour la surface ---
    bande est un nombre
    pour bande de 40 à 0 pas - 1
        jj est un nombre
        pour jj de 0 à N - 1
            ii est un nombre
            pour ii de 0 à N - 1
                // centre (monde) de la facette
                xc est un nombre
                yc est un nombre
                xc vaut xmin + (xmax - xmin) * (ii + 0.5) / N
                yc vaut xmin + (xmax - xmin) * (jj + 0.5) / N
                // coordonnées tournées pour la surface
                xrc est un nombre
                yrc est un nombre
                xrc vaut xc * cr - yc * sr
                yrc vaut xc * sr + yc * cr
                // profondeur -> bande entière (grand = au fond)
                prof est un nombre
                prof vaut arrondi((xrc + yrc + 6) * 40 / 12)
                si prof = bande alors
                    // Les 4 coins (monde), tournés puis projetés
                    za est un nombre
                    zb est un nombre
                    zc2 est un nombre
                    zd est un nombre
                    za vaut hauteurs[jj * (N + 1) + ii]
                    zb vaut hauteurs[jj * (N + 1) + ii + 1]
                    zc2 vaut hauteurs[(jj + 1) * (N + 1) + ii + 1]
                    zd vaut hauteurs[(jj + 1) * (N + 1) + ii]

                    xa est un nombre
                    ya est un nombre
                    xb est un nombre
                    yb est un nombre
                    xa vaut xmin + (xmax - xmin) * ii / N
                    ya vaut xmin + (xmax - xmin) * jj / N
                    xb vaut xmin + (xmax - xmin) * (ii + 1) / N
                    yb vaut xmin + (xmax - xmin) * (jj + 1) / N

                    // projette un coin (x,y,z) avec rotation
                    xr1 est un nombre
                    yr1 est un nombre
                    xr1 vaut xa * cr - ya * sr
                    yr1 vaut xa * sr + ya * cr
                    pax est un nombre
                    pay est un nombre
                    pax vaut OX + (xr1 * sXY - yr1 * sXY) * cosA
                    pay vaut OY - za * sZ - (xr1 * sXY + yr1 * sXY) * sinA

                    xr2 est un nombre
                    yr2 est un nombre
                    xr2 vaut xb * cr - ya * sr
                    yr2 vaut xb * sr + ya * cr
                    pbx est un nombre
                    pby est un nombre
                    pbx vaut OX + (xr2 * sXY - yr2 * sXY) * cosA
                    pby vaut OY - zb * sZ - (xr2 * sXY + yr2 * sXY) * sinA

                    xr3 est un nombre
                    yr3 est un nombre
                    xr3 vaut xb * cr - yb * sr
                    yr3 vaut xb * sr + yb * cr
                    pcx est un nombre
                    pcy est un nombre
                    pcx vaut OX + (xr3 * sXY - yr3 * sXY) * cosA
                    pcy vaut OY - zc2 * sZ - (xr3 * sXY + yr3 * sXY) * sinA

                    xr4 est un nombre
                    yr4 est un nombre
                    xr4 vaut xa * cr - yb * sr
                    yr4 vaut xa * sr + yb * cr
                    pdx est un nombre
                    pdy est un nombre
                    pdx vaut OX + (xr4 * sXY - yr4 * sXY) * cosA
                    pdy vaut OY - zd * sZ - (xr4 * sXY + yr4 * sXY) * sinA

                    // Couleur selon hauteur moyenne
                    zmoy est un nombre
                    zmoy vaut (za + zb + zc2 + zd) / 4
                    hn est un nombre
                    hn vaut (zmoy + 7) / 14
                    coul est un texte
                    coul vaut couleur_jet(hn)

                    contour(Fig, #0a0a0f, 1)
                    polygone(Fig, pax, pay, pbx, pby, pcx, pcy, pdx, pdy, coul)
                    contour(Fig, #0)
                fin si
            fin pour
        fin pour
    fin pour

    label(Fig, 20, 34, "Surface 3d « peaks »", #1F2937, 18)

    // Affichage direct de la toile finale
    affiche Fig
fin procédure

// ============================================================
//  APPEL : on dessine avec un angle de 0 (aligné)
// ============================================================
appelle dessinerVue(0)
Pas encore noté