TP2e année8 livrablesQuiz corrigé
Le Générateur d'équipe
Un design tiré au hasard qui déclenche des stats, puis un export JSON réutilisable ailleurs.
Par Josselyn C., prof de BTS SIO. Publié le , mis à jour le .
- On code
- objets · fonctions fléchées · Math.random · le design qui calcule les stats · tableaux · map/forEach/reduce · JSON
- Le résultat
- on génère une équipe de monstres animés (couleur, yeux, cornes, crocs + PV/attaque), puis on l'exporte en JSON
- Outils
- navigateur + VS Code, zéro installation · Durée : la journée (~5 h)
- Pour qui
- SLAM, 2e année
- Durée
- environ 3 h
- Prérequis
- Les bases de JavaScript ; idéalement après le TP Fabrique à monstres.
- Matériel
- Un navigateur (Chrome ou Firefox), VS Code et le projet de départ fourni (index.html, styles.css, sprites.js).
À télécharger : Generateur-equipe-projet-eleve.zip (le projet de départ)
Compétences : C2.1 Concevoir et développer une solution applicative, C2.3 Gérer les données
Ce JSON servira à alimenter l'app de combat (séance suivante). C'est le vrai rôle de JSON : faire dialoguer deux programmes.
Où on va aujourd'hui
Hier, tu as fabriqué un monstre à la main. Aujourd'hui, on industrialise : on génère des monstres au hasard, mais pas n'importe comment — le design d'un monstre (sa couleur, ses yeux, ses cornes, ses crocs) va déterminer ses statistiques (PV et attaque). Un monstre à 3 yeux avec cornes et crocs sera plus fort qu'un petit à un œil. Puis on exporte l'équipe en JSON pour pouvoir, plus tard, la charger dans une autre application : l'arène de combat.
Ce que tu sauras faire ce soir
| Notion | Dans le générateur… |
|---|---|
| Objets, fonctions fléchées | décrire un monstre, écrire les fonctions du générateur. |
Math.random | tirer un design au hasard. |
| Une fonction qui calcule | faire dépendre les stats du design (la règle du jeu). |
Spread ... | fusionner le design et ses stats en un seul monstre. |
Tableaux, map/forEach/reduce | constituer une équipe, l'afficher, la résumer. |
JSON (stringify/parse) | exporter l'équipe pour une autre application. |
| Sprites animés (fournis) | afficher des monstres qui bougent. |
Chapitre 0.1 — Le projet fourni
Pré-requis
Un navigateur (Chrome/Firefox) et VS Code (ouvre le dossier : Fichier → Ouvrir le dossier). Récupère l'archive Generateur-equipe-projet-eleve.zip, décompresse-la, ouvre le dossier.
L'arborescence
generateur-equipe/ ├── index.html # la page (boutons, équipe, export) ├── styles.css # l'habillage ├── sprites.js # dessin ET animation des monstres ├── main.js # TON code (presque vide) └── README.md
Le rôle de chaque fichier
| Fichier | Contenu | Tu l'édites ? |
|---|---|---|
index.html | La structure : boutons « Générer » / « Ajouter », la zone équipe, la zone d'export JSON. Déjà écrite. | Non |
styles.css | L'habillage clair (cartes, barres de stats, boutons). Déjà écrit. | Non |
sprites.js | Le moteur : dessinerMonstre(...) (pose fixe) et animerMonstre(...) (le monstre bouge). | Non |
main.js | TON fichier. Contient déjà COULEURS et des repères par étape. | OUI |
Chapitre 0.2 — Lancer & la console
Ouvrir
Double-clique sur index.html. La page s'ouvre : des boutons, une zone équipe vide, une zone d'export vide. Normal : rien n'est codé.
La console
F12, onglet Console. Tu dois lire :
Base chargée. 6 couleurs. Ouvre le playbook !
Ta boucle de travail
1. j'écris dans main.js · 2. Ctrl+S · 3. je recharge (Ctrl+R) · 4. je regarde la page + la console.
1. Un monstre = un objet (le design)
On appelle design l'apparence d'un monstre : sa couleur, son nombre d'yeux, s'il a des cornes, des crocs. On range ça dans un objet.
Étape 1 — Un monstre d'essai
const design = {
nom: "Zeb 1",
couleurIndex: 0, // position dans COULEURS (0 = la 1re)
eyes: 2,
horns: false,
fangs: true,
};
console.log(design);
Étape 2 — L'afficher, animé
On va poser un canvas et demander à animerMonstre de dessiner le design. Ajoute temporairement dans index.html, sous les boutons : <canvas id="essai"></canvas>. Puis dans main.js :
const c = COULEURS[design.couleurIndex];
animerMonstre(document.querySelector("#essai"),
{ body: c.body, shade: c.shade, light: c.light, mouth: c.shade,
eyes: design.eyes, horns: design.horns, fangs: design.fangs }, 6);
FLAG-A1
reconnais ton monstre
Règle le design d'essai pour obtenir : couleur n°4, 3 yeux, cornes, pas de crocs. Recharge, vérifie visuellement. Puis affiche console.log("FLAG-A1-" + (design.couleurIndex + design.eyes)) et recopie la valeur.
2. Un design AU HASARD
On veut que l'ordinateur invente le design. Outil : Math.random().
console.log(Math.floor(Math.random() * 6)); // un entier de 0 à 5
Étape 1 — Un compteur pour les noms
let compteur = 0; // en haut de main.js
Étape 2 — La fonction qui invente un design
const designAleatoire = () => ({
nom: "Zeb " + (++compteur),
couleurIndex: Math.floor(Math.random() * COULEURS.length),
eyes: 1 + Math.floor(Math.random() * 3), // 1, 2 ou 3
horns: Math.random() < 0.5, // vrai 1 fois sur 2
fangs: Math.random() < 0.6,
});
console.log(designAleatoire());
FLAG-D1
investigation
Pourquoi Math.floor et pas Math.round pour tirer un index de tableau ? Teste Math.round(Math.random()*3) : quelles valeurs peut-il sortir, et pourquoi est-ce un problème (indice : un index 3 dans un tableau de 3 cases) ? Réponds : FLAG-D1-…
3. Le design DÉCLENCHE les stats
Le cœur de la journée : on ne tire pas les stats au hasard séparément — c'est le design qui les calcule. On invente une règle du jeu : plus d'yeux, des cornes, des crocs = plus fort.
La règle
La fonction
const statsDuDesign = (m) => {
const attaque = 2 + m.eyes + (m.fangs ? 3 : 0) + (m.horns ? 2 : 0);
const pv = 24 + m.eyes * 4 + (m.horns ? 10 : 0);
return { attaque, pv, pvMax: pv };
};
console.log(statsDuDesign({ eyes: 3, horns: true, fangs: true }));
▶ {attaque: 10, pv: 46, pvMax: 46}
FLAG-B1
équilibre le jeu
Un monstre minimal (1 œil, ni cornes ni crocs) a quelle attaque ? Un maximal (3 yeux, cornes, crocs) ? Calcule les deux à la main, vérifie avec la fonction, et propose une modification du barème que tu trouves plus juste. Rends tes chiffres + ta modif. FLAG-B1-OK
4. Assembler : un monstre complet
Un monstre = un design + ses stats. On les fusionne avec le spread.
const monstreAleatoire = () => {
const design = designAleatoire();
return { ...design, ...statsDuDesign(design) };
};
console.log(monstreAleatoire());
▶ {nom:"Zeb 1", couleurIndex:2, eyes:3, horns:true, fangs:false,
attaque:7, pv:46, pvMax:46}
5. Afficher une carte (animée)
Une carte par monstre : le sprite animé, le nom, deux barres (PV, attaque), et les traits.
Les repères de la page
const equipeEl = document.querySelector("#equipe");
const PV_MAX = 50, ATT_MAX = 12; // pour calibrer la largeur des barres
La fonction carte
const carte = (m) => {
const c = COULEURS[m.couleurIndex];
const traits = [m.eyes + (m.eyes > 1 ? " yeux" : " oeil"),
m.horns ? "cornes" : null,
m.fangs ? "crocs" : null].filter(Boolean).join(" · ");
const div = document.createElement("div");
div.className = "unit";
div.innerHTML =
'<canvas></canvas>' +
'<div class="nom">' + m.nom + '</div>' +
'<div class="stat"><span class="lab">PV</span><div class="track"><div class="fill pv" style="width:' + (m.pv/PV_MAX*100) + '%"></div></div><span class="val">' + m.pv + '</span></div>' +
'<div class="stat"><span class="lab">ATT</span><div class="track"><div class="fill att" style="width:' + (m.attaque/ATT_MAX*100) + '%"></div></div><span class="val">' + m.attaque + '</span></div>' +
'<div class="traits">' + traits + '</div>';
equipeEl.appendChild(div);
animerMonstre(div.querySelector("canvas"),
{ body:c.body, shade:c.shade, light:c.light, mouth:c.shade,
eyes:m.eyes, horns:m.horns, fangs:m.fangs }, 5);
};
6. Une équipe (tableau + boutons)
Le tableau et l'affichage
let equipe = [];
const afficher = () => {
equipeEl.innerHTML = ""; // on vide
equipe.forEach(carte); // une carte par monstre
};
Les boutons
document.querySelector("#btnGen").addEventListener("click", () => {
compteur = 0;
equipe = [1, 2, 3, 4].map(() => monstreAleatoire()); // 4 monstres
afficher();
});
document.querySelector("#btnAdd").addEventListener("click", () => {
equipe.push(monstreAleatoire());
afficher();
});
FLAG-C1
le bug du forEach
Remplace un instant equipeEl.innerHTML = "" par rien (supprime la ligne). Génère plusieurs fois : que se passe-t-il, et pourquoi ? Remets la ligne, explique en une phrase à quoi elle sert. FLAG-C1-…
7. Résumer l'équipe (reduce)
Sous l'équipe, on affiche un bilan : nombre de monstres, PV total, attaque moyenne. Outil : reduce.
const resumeEl = document.querySelector("#resume");
// à la fin de afficher() :
const totalPv = equipe.reduce((s, m) => s + m.pv, 0);
const totalAtt = equipe.reduce((s, m) => s + m.attaque, 0);
const moyAtt = equipe.length ? (totalAtt / equipe.length).toFixed(1) : 0;
resumeEl.innerHTML = "Équipe de <b>" + equipe.length + "</b> monstres · " +
"<b>" + totalPv + "</b> PV au total · attaque moyenne <b>" + moyAtt + "</b>";
FLAG-A2
la puissance de l'équipe
Ajoute une ligne au résumé : la puissance = PV total + (attaque totale × 5), calculée avec reduce. Génère une équipe et recopie : FLAG-A2-<la puissance>.
8. Analyser l'équipe (find, filter)
Deux méthodes très utiles pour interroger une collection.
const plusFort = equipe.reduce((max, m) => m.attaque > max.attaque ? m : max);
console.log("Le plus fort :", plusFort.nom, plusFort.attaque);
const cornus = equipe.filter((m) => m.horns);
console.log(cornus.length, "monstre(s) à cornes");
const premier3yeux = equipe.find((m) => m.eyes === 3);
console.log(premier3yeux ? premier3yeux.nom : "aucun à 3 yeux");
9. Exporter l'équipe en JSON
On veut sortir l'équipe du programme sous une forme réutilisable ailleurs. Le format universel : le JSON.
JSON, c'est quoi
const texte = JSON.stringify(equipe, null, 2); // tableau d'objets -> texte
console.log(texte);
L'afficher dans la zone d'export
const jsonEl = document.querySelector("#json");
// à la fin de afficher() :
jsonEl.value = JSON.stringify(equipe, null, 2);
Le bouton Copier
document.querySelector("#btnCopy").addEventListener("click", (e) => {
jsonEl.select();
navigator.clipboard.writeText(jsonEl.value);
e.target.textContent = "Copié !";
setTimeout(() => { e.target.textContent = "Copier le JSON"; }, 1200);
});
10. Le pont : réimporter (parse)
Pour comprendre l'aller-retour : l'opération inverse de stringify est parse. C'est ce que fera l'app de combat.
const texte = jsonEl.value; // le JSON (texte)
const equipeChargee = JSON.parse(texte); // -> un vrai tableau d'objets
console.log(equipeChargee[0].nom, equipeChargee[0].attaque);
FLAG-D2
l'aller-retour
Copie le JSON de ton équipe, colle-le dans une variable const test = `…` (entre accents graves), fais JSON.parse(test) et affiche le nom du 2ᵉ monstre. Ça marche ? Note ce que tu observes. FLAG-D2-OK
11. Sous le capot : l'animation
Tu utilises animerMonstre depuis ce matin. Voyons comment il fait bouger un dessin (chapitre bonus, formateur en 2e année). Ouvre sprites.js.
Une image dépend du temps
const dy = Math.round(Math.sin(t / 380)); // -1, 0 ou 1 : le flottement
const clin = (t % 2600) < 130; // vrai brièvement : le clignement
Une boucle qui redessine
function _tick(t) {
// pour chaque monstre animé : redessine-le à l'instant t
requestAnimationFrame(_tick); // ... et recommence à la prochaine image
}
FLAG-E1
règle l'animation
Dans sprites.js, change le 380 (vitesse du flottement) et le 2600 (fréquence du clignement) et observe. Trouve un réglage que tu aimes, note tes valeurs. FLAG-E1-OK
Grand défi — pour les plus rapides
Tout fini ? Voici une modification lourde, en autonomie, sans corrigé. Prends ton temps.
Deux idées ambitieuses (choisis-en une)
Aucun corrigé : c'est un vrai morceau de dev. Si tu le mènes au bout, montre-le. On réutilisera ces équipes JSON quand on codera l'arène de combat.
On fait le point
Mini-quiz (corrigé dessous)
Exercice Réussi À revoir
Voir le corrigéMasquer le corrigé de l'exercice
- Les stats (PV, attaque) sont calculées à partir du design (yeux, cornes, crocs), pas tirées au hasard à part. 2. Fusionner design et stats en un seul monstre. 3.
stringify: objet → texte (envoyer/stocker) ;parse: texte → objet (exploiter). 4. Sinon les anciennes cartes s'accumulent à chaque affichage. 5. Rappeler une fonction ~60 fois/s pour animer (redessiner à chaque image).
Tu avais trouvé ?
Ton générateur est complet si…
Compétences mobilisées (référentiel BTS SIO SLAM)
Livrable
Bloc 2 SLAM · Concevoir et développer une solution applicative
→ Développer des applications en respectant les standards de programmation
• Écrire du JavaScript moderne (ES6+) : objets, fonctions fléchées, spread
• Manipuler des collections avec map / forEach / filter / reduce
→ Gérer les données : sérialiser/désérialiser en JSON pour l'échange entre applications
TP JavaScript moderne (ES6+) · projet « Générateur d'équipe » · l'équipe exportée en JSON alimentera l'arène de combat (séance suivante).