CoddyRun
Blog

Articles & Actualités

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

74 publications

Un programme qui transforme un mot en bannière pixel‑art
Article

Bannière pixel‑art

Un programme qui transforme un mot en bannière pixel‑art

Lire l'articleReplier l'article

Ce projet propose une idée aussi simple qu’élégante : demander à l’utilisateur un mot, puis le transformer en une bannière graphique composée de lettres pixelisées.

Chaque caractère est dessiné à partir d’une police bitmap artisanale, et mis en couleur grâce à une palette vive et moderne. Le résultat est une image au style rétro, rappelant les premiers écrans numériques.

Une police bitmap faite à la main

Le cœur du programme repose sur une police 5×7 entièrement codée manuellement.
Chaque lettre, chaque chiffre et même l’espace sont décrits sous forme de motifs composés de 0 et de 1. Ces motifs représentent des pixels : les 1 sont dessinés, les 0 restent vides. Cette approche donne un rendu minimaliste, très proche du pixel‑art classique.

Une palette de couleurs vibrante

Pour éviter la monotonie, le programme utilise une série de couleurs éclatantes. Chaque lettre reçoit une couleur différente, créant un rythme visuel dynamique. Les teintes choisies — rose vif, orange brûlant, jaune lumineux, violet profond, bleu électrique, vert turquoise, rose framboise — apportent une énergie moderne à l’esthétique rétro du pixel‑art.

Une bannière construite lettre par lettre

Une fois le mot saisi par l’utilisateur, le programme parcourt chaque caractère :

  • il identifie son motif dans la police bitmap,
  • il choisit une couleur dans la palette,
  • il dessine les pixels un par un sous forme de petits rectangles,
  • puis il avance pour placer la lettre suivante.

Cette construction progressive donne l’impression que la bannière se tisse sous les yeux, comme un textile numérique.

Un fond sombre pour un contraste parfait

La bannière est posée sur un fond bleu‑noir profond. Ce choix renforce le contraste avec les couleurs vives des lettres et met en valeur le style pixelisé. Le résultat est une image nette, lisible et visuellement percutante.

Un projet simple, créatif et rétro‑moderne

Ce programme est un bel exemple de création graphique générée par code.
Il combine :

  • une police bitmap artisanale,
  • une palette contemporaine,
  • une logique de dessin pixel par pixel,
  • et une mise en forme élégante.

Le tout permet de transformer n’importe quel mot en une bannière unique, colorée et pleine de caractère.

Programme :

@ Demander le mot
saisie, mot est un texte
demande 'Quel mot veux-tu afficher ?' dans saisie
mot vaut majuscules(normaliser(saisie))

@ Alphabet et Police bitmap 5x7
alpha est un texte
alpha vaut 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 '

police est un tableau
// A à Z, 0 à 9, espace (chaque entrée = 35 caractères)
police ajoute '01110100011000111111100011000110001' // A
police ajoute '11110100011000111110100011000111110' // B
police ajoute '01111100001000010000100001000001111' // C
police ajoute '11110100011000110001100011000111110' // D
police ajoute '11111100001000011110100001000011111' // E
police ajoute '11111100001000011110100001000010000' // F
police ajoute '01111100001000010111100011000101111' // G
police ajoute '10001100011000111111100011000110001' // H
police ajoute '11111001000010000100001000010011111' // I
police ajoute '00111000100001000010100101001001100' // J
police ajoute '10001100101010011000101001001010001' // K
police ajoute '10000100001000010000100001000011111' // L
police ajoute '10001110111010110101100011000110001' // M
police ajoute '10001110011010110011100011000110001' // N
police ajoute '01110100011000110001100011000101110' // O
police ajoute '11110100011000111110100001000010000' // P
police ajoute '01110100011000110001101011001001101' // Q
police ajoute '11110100011000111110101001001010001' // R
police ajoute '01111100001000001110000010000111110' // S
police ajoute '11111001000010000100001000010000100' // T
police ajoute '10001100011000110001100011000101110' // U
police ajoute '10001100011000110001100010101000100' // V
police ajoute '10001100011000110101101011101110001' // W
police ajoute '10001100010101000100010101000110001' // X
police ajoute '10001100010101000100001000010000100' // Y
police ajoute '11111000010001000100010001000011111' // Z
police ajoute '01110100111010110101101011100101110' // 0
police ajoute '00100011000010000100001000010001110' // 1
police ajoute '01110100010000100110010001000011111' // 2
police ajoute '11111000100010000010000011000101110' // 3
police ajoute '00010001100101010010111110001000010' // 4
police ajoute '11111100001111000001000011000101110' // 5
police ajoute '00110010001000011110100011000101110' // 6
police ajoute '11111000010001000100010000100001000' // 7
police ajoute '01110100011000101110100011000101110' // 8
police ajoute '01110100011000101111000010001001100' // 9
police ajoute '00000000000000000000000000000000000' // espace

@ Réglages et Palette
taille_case, marge_x, marge_y, espacement est un nombre
taille_case vaut 12
marge_x vaut 20
marge_y vaut 25
espacement vaut 1

couleurs est un tableau
// Couleurs en hexadécimal sans guillemets
couleurs vaut [#FF006E, #FB5607, #FFBE0B, #8338EC, #3A86FF, #06D6A0, #EF476F]

@ Toile dimensionnée selon la longueur du mot
nb, larg, haut est un nombre
nb vaut longueur(mot)
larg vaut marge_x * 2 + nb * (5 + espacement) * taille_case
haut vaut marge_y * 2 + 7 * taille_case

// Borne la largeur de la toile entre 1 et 1000 pixels maximum
larg vaut limiter(larg, 1, 1000)

Banniere est une toile
dimension(Banniere, larg, haut)
remplir(Banniere, #161B26)

@ Dessin lettre par lettre
curseur_x est un nombre
curseur_x vaut marge_x

pour n de 1 à nb
  lettre est un texte
  lettre vaut copier(mot, n, 1)
  
  // position() renvoie 1 pour 'A', donc on retire 1 pour avoir l'index du tableau (0)
  idx est un nombre
  idx vaut position(lettre, alpha) - 1
  
  // Si la lettre est inconnue (idx < 0), on utilise l'espace (dernier élément : index 36)
  si idx < 0 alors
    idx vaut 36
  fin si
  
  g est un texte
  g vaut police[idx]
  
  ic, pos, px, py est un nombre
  bit est un texte
  coul est un texte
  
  ic vaut (n - 1) mod longueur(couleurs)
  coul vaut couleurs[ic]
  
  pour li de 0 à 6
    pour co de 0 à 4
      pos vaut li * 5 + co + 1
      bit vaut copier(g, pos, 1)
      si bit = '1' alors
        px vaut curseur_x + co * taille_case
        py vaut marge_y + li * taille_case
        rectangle(Banniere, px, py, taille_case - 2, taille_case - 2, coul)
      fin si
    fin pour
  fin pour
  
  // Utilisation de ajoute pour incrémenter le curseur
  curseur_x ajoute (5 + espacement) * taille_case
fin pour

affiche Banniere
Pas encore noté
Version 3.4 en approche : Une mise à jour majeure de l’interpréteur
Article

Version 3.4 en approche

Une mise à jour majeure de l’interpréteur

Lire l'articleReplier l'article

La version 3.4 de l’interpréteur marque une avancée significative, tant sur le plan des fonctionnalités que de l’expérience utilisateur.

Cette mise à jour introduit de nouveaux mots-clés, enrichit les capacités graphiques et améliore l’interface pour offrir un environnement de développement plus fluide, plus puissant et plus moderne.

Prise en charge des couleurs hexadécimales avec transparence

L’interpréteur supporte désormais les couleurs hexadécimales au format #AARRGGBB, soit 8 chiffres, permettant de définir non seulement la couleur mais aussi son niveau de transparence (Alpha).

Exemple : #80FF0000 correspond à un rouge semi-transparent.

Cette amélioration ouvre la voie à des interfaces plus raffinées, des effets visuels avancés et une meilleure gestion des superpositions.

Nouveaux mots-clés

La version 3.4 introduit quatre mots-clés essentiels qui enrichissent la logique d’exécution et permettent de structurer des programmes plus dynamiques.

processus
Définit un bloc de code qui s’exécute en parallèle du programme principal.
Idéal pour gérer des tâches simultanées, des animations ou des opérations de fond.

pause
Suspend l’exécution pendant un nombre de millisecondes.

pause 1000 équivaut à une seconde d’attente.
Parfait pour temporiser des actions ou synchroniser des événements.

suspendre
Met un processus en pause.
Permet de contrôler finement l’exécution parallèle, notamment pour stopper temporairement une tâche.

reprendre
Relance un processus précédemment suspendu.
Complète le duo suspendre/reprendre pour une gestion précise des processus.

Nouvelle fonction intégrée

paramètre
Cette fonction permet d’enregistrer et de relire des paramètres persistants.
Elle est particulièrement utile pour :

  • mémoriser des préférences utilisateur,
  • conserver des états entre deux exécutions,
  • gérer des configurations internes.

Interface améliorée

La version 3.4 ne se contente pas d’ajouter des fonctionnalités : elle améliore aussi l’expérience visuelle et ergonomique.

Palette de couleurs optimisée
La palette a été revue pour offrir une sélection plus intuitive, plus cohérente et plus agréable à utiliser.

Suppression du mini-menu d’Edge

Le mini-menu qui apparaissait lors d’un double-clic sur la toile a été retiré.
Résultat : une interaction plus fluide, sans éléments parasites.

Conclusion

La mise à jour 3.4 apporte des améliorations majeures, tant sur le plan technique que visuel. Avec la gestion avancée des couleurs, les nouveaux mots-clés orientés vers l’exécution parallèle, la fonction paramètre et une interface plus propre, cette version renforce la puissance et la modernité de l’interpréteur.

Pas encore noté
Count War : l'art de créer un runner stratégique moderne
Article

Count War

L'art de créer un runner stratégique moderne

Lire l'articleReplier l'article

Créer un jeu comme Count War ne consiste pas seulement à faire avancer des personnages sur une route. C’est un mélange subtil de stratégie, rythme, anticipation, et mise en scène.

Voici comment on conçoit un Count War de A à Z, en se concentrant sur les idées, les mécaniques et la logique de conception.

Définir l’expérience de jeu

Un Count War repose sur une idée simple :
le joueur dirige une foule qui doit survivre à une série d’épreuves pour atteindre un boss final.

L’expérience doit être :

  • rapide
  • lisible
  • addictive
  • basée sur des choix instantanés

Le joueur ne contrôle pas tout : il influence la trajectoire, mais la foule avance seule.
C’est ce mélange entre automatisme et décision immédiate qui crée la tension.

Concevoir la progression du niveau

Un Count War est structuré comme un parcours d’obstacles.
Pour le construire, on imagine une succession d’éléments :

Les portails
Ils modifient la taille de la foule.
Ce sont les moments stratégiques : le joueur doit choisir le bon côté.

Les obstacles
Ils punissent les mauvais choix ou les erreurs de trajectoire.

Les ennemis
Ils déclenchent des combats automatiques qui réduisent la foule.

Le boss final
Il représente l’objectif ultime : survivre jusqu’à lui avec assez de soldats.

La clé est de rythmer le niveau :
un portail ? un obstacle ? un ennemi ? un portail ? un obstacle…
Le joueur doit sentir une montée en tension.

Ajouter de la variété grâce au hasard

Pour éviter que chaque partie soit identique, on introduit :

  • des valeurs différentes sur les portails
  • des positions aléatoires des obstacles
  • des ennemis plus ou moins résistants
  • des mini-boss occasionnels

Le hasard doit être contrôlé :
il surprend le joueur, mais ne doit jamais rendre la partie injuste.

Imaginer les mécaniques de combat

Dans un Count War, le combat est automatique.
Le joueur ne frappe pas : il observe la confrontation.

Combat contre un ennemi simple
La foule perd un certain nombre d’unités par seconde.
L’ennemi aussi.
Le combat s’arrête quand l’un des deux tombe à zéro.

Combat contre un boss
Même principe, mais avec :

  • une barre de vie visible
  • un rythme de combat plus lent
  • une mise en scène plus forte

Le joueur doit sentir que c’est le moment décisif.

Penser les interactions avec les portails

Les portails sont le cœur du gameplay.
Ils doivent être :

  • lisibles
  • rapides à comprendre
  • visuellement distincts

Chaque portail représente une opération :

  • ajouter
  • retirer
  • multiplier
  • diviser

Le joueur doit pouvoir anticiper l’effet en un coup d’œil.
C’est là que se joue la stratégie : choisir le bon côté pour maximiser la survie.

Créer une illusion de profondeur

Même sans code, on peut décrire le principe :

  • les objets proches sont grands
  • les objets éloignés sont petits
  • leur position verticale dépend de leur distance
  • leur largeur diminue avec la profondeur

Cette perspective donne l’impression que la foule avance dans un tunnel ou une route en 3D.

Le décor doit être simple mais dynamique :

  • lignes de route
  • ombres
  • silhouettes
  • éléments répétitifs pour donner du mouvement

Définir les contrôles du joueur

Un Count War doit rester accessible :

  • un mouvement à gauche
  • un mouvement à droite
  • éventuellement une touche pour abandonner

Le joueur doit pouvoir réagir vite, sans réfléchir.
La simplicité des contrôles renforce la tension :
tout se joue en une fraction de seconde.

Construire une interface claire

L’interface doit afficher :

  • le nombre de soldats
  • la progression dans le niveau
  • le niveau actuel
  • le record du joueur
  • les barres de vie en combat

Elle doit être lisible même en plein mouvement.

Soigner la fin de partie

Un Count War doit offrir une conclusion nette :

Victoire
Le boss est vaincu ? célébration, couleurs chaudes, message clair.

Défaite
La foule tombe à zéro ? ton sombre, message fataliste.

Abandon
Le joueur quitte volontairement ? message neutre.

On peut aussi ajouter :

  • un record
  • un classement
  • une récompense visuelle

Conclusion : développer un Count War, c’est créer un parcours stratégique rythmé

Pour concevoir un Count War, il faut penser :

  • comme un designer (rythme du niveau)
  • comme un stratège (portails et choix rapides)
  • comme un metteur en scène (boss, combats, perspective)

Ce type de jeu fonctionne parce qu’il combine :

  • simplicité des contrôles
  • profondeur des choix
  • satisfaction visuelle
  • progression rapide
5,0 / 5 (1 avis)
Les couleurs hexadécimales gèrent désormais la transparence
Nouveauté

Les couleurs hexadécimales gèrent désormais la transparence

Disponible dans la prochaine version de CoddyRun v3.4

Lire l'articleReplier l'article

CoddyRun accepte maintenant la notation hexadécimale à 8 chiffres : #RRGGBBAA.

Les deux derniers caractères représentent le canal alpha, exactement comme en CSS :

  • 00 : totalement transparent
  • FF : totalement opaque

La couleur peut provenir d’un littéral, d’une variable ou d’un calcul, sans aucune modification de syntaxe.

Pourquoi c’est utile

L’alpha permet de hiérarchiser visuellement un rendu sans multiplier les teintes.
C’est aussi un excellent moyen de manipuler concrètement la base 16 :
quatre informations encodées dans une seule chaîne, deux chiffres par canal.

Cette notation est déjà utilisée en CSS et dans la majorité des outils graphiques.
L’intégrer dans CoddyRun rend l'apprentissage plus cohérent et plus proche des standards professionnels.

Compatibilité

Cette évolution est entièrement additive :

  • Les scripts en #RRGGBB fonctionnent exactement comme avant.
  • #0 rétablit toujours la valeur par défaut.
  • Les codes ASCII courts (#65) restent inchangés.
  • Les longueurs invalides sont toujours rejetées, avec un message d’erreur unifié.

Aucun script existant n’a besoin d’être modifié.

Pas encore noté
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)