TP2e année11 livrablesQuiz corrigé
La Fabrique à Monstres
Tu pars d'un projet fourni et tu codes, en JavaScript, un vrai générateur de monstres en pixel art.
Par Josselyn C., prof de BTS SIO. Publié le , mis à jour le .
- On code
- variables & types · objets · fonctions fléchées · tableaux · forEach/map/filter/reduce · find/some/every · événements · aléatoire · JSON/localStorage · modules
- Le résultat
- compose un monstre, génère au hasard, remplis une galerie, filtre, sauvegarde, exporte
- Outils
- un navigateur + VS Code. Zéro installation. · Durée : la journée (~5 h)
- Pour qui
- SLAM, 2e année
- Durée
- 1 journée
- Prérequis
- Savoir déjà programmer : variables, boucles, fonctions.
- Matériel
- Chrome ou Firefox, VS Code avec l'extension Live Server, et le projet de départ fourni en archive zip.
À télécharger : Fabrique-a-monstres-projet-eleve.zip (le projet de départ)
Compétences : C2.1 Concevoir et développer une solution applicative
2e année : tu as déjà programmé. Ici on muscle le JS moderne — le socle direct de React et d'AppliFrais.
Bienvenue à la Fabrique
Tu es en 2e année : tu sais déjà ce qu'est une variable, une boucle, une fonction. Aujourd'hui on ne réapprend pas à programmer — on muscle le JavaScript moderne (celui de React), sur un projet qui donne envie. On te fournit un projet déjà habillé, avec tous les réglages… mais éteints. À toi de les allumer un par un en écrivant du code. Chaque réglage grisé qui reprend vie = ton code marche.
Comment tu avances (en autonomie)
Ce que tu sauras faire ce soir
| Notion | Dans le générateur, ça sert à… |
|---|---|
const/let, types | Décrire un monstre, un compteur, un nom. |
| Objets | Ranger toutes les caractéristiques d'un monstre. |
| Fonctions fléchées + templates | Écrire dessiner(), fabriquer du texte. |
Tableaux, forEach, map | Créer les pastilles, afficher la galerie. |
| Événements (clic, saisie) | Réagir à l'utilisateur. |
Math.random | Le bouton « Aléatoire », la horde. |
filter/reduce/find/some | Supprimer, compter, statistiques, recherches. |
| JSON + localStorage | Sauvegarder la galerie. |
| Modules (import/export) | Découper le projet, comme dans React. |
Le plan de la journée
| Partie | On construit… |
|---|---|
| 0 · Prise en main | comprendre le projet fourni, le lancer, la boucle de travail. |
| 1 · Réveiller la Fabrique | l'aperçu + tous les réglages (couleur, yeux, cornes, crocs, nom). |
| 2 · Le hasard & la galerie | Aléatoire, galerie, compteur, supprimer, stats, recherches. |
| 3 · On va plus loin | horde, tri/filtre, sauvegarde, export image, lecture du moteur, modules. |
Chapitre 0.1 — Le projet qu'on te fournit
Pré-requis (à vérifier avant de commencer)
| Il te faut… | Détail |
|---|---|
| Un navigateur | Chrome ou Firefox, à jour. C'est lui qui exécute le JavaScript. |
| VS Code | L'éditeur de code. Ouvre le dossier du projet (Fichier → Ouvrir le dossier), pas juste un fichier. |
| L'extension Live Server | Utile en fin de journée (chapitre Modules). Onglet Extensions (⧉) → cherche « Live Server » → Installer. On s'en sert au chapitre 20. |
| Le dossier fourni | Récupère l'archive Fabrique-a-monstres-projet-eleve.zip, décompresse-la, et ouvre le dossier obtenu. |
L'arborescence du projet
monstre-generator/ ├── index.html # la page : aperçu, réglages, galerie ├── styles.css # l'habillage (couleurs, mise en page) ├── sprites.js # le DESSIN des monstres en pixel art ├── main.js # TON code (presque vide au départ) └── README.md # rappel express
Le rôle de chaque fichier
| Fichier | Ce qu'il contient | Tu l'édites ? |
|---|---|---|
index.html | La structure de la page : le canvas d'aperçu, les boutons de réglage, la zone galerie. Il charge les 3 fichiers ci-dessous. Déjà écrit. | Non (sauf 2-3 ajouts guidés en Partie 3) |
styles.css | L'habillage : couleurs, cartes, boutons « game », l'aperçu en damier. Déjà écrit. | Non |
sprites.js | Le moteur de dessin. Il t'offre une fonction toute prête, dessinerMonstre(canvas, options, taille). On lira dedans au chapitre 19, mais tu n'as pas à le comprendre pour t'en servir. | Non (sauf chapitre 19 bonus) |
main.js | TON fichier. Il contient déjà les données (le tableau COULEURS) et des repères par partie. Tout le reste, c'est toi. | OUI, c'est ici que tu bosses. |
Chapitre 0.2 — Lancer & comprendre le projet
Étape 1 — Ouvrir la page
Double-clique sur index.html. La page s'ouvre dans le navigateur : un titre, un aperçu vide, des réglages grisés avec , une galerie vide. C'est l'état de départ : rien n'est codé. Ton objectif de la journée : tout allumer.
Étape 2 — La console (F12), ton copilote
Ouvre les outils de développement : F12 (ou clic droit → Inspecter), onglet Console. Tu dois lire :
Base chargée. Ouvre le playbook et commence la Partie 1 ! 6 couleurs.
Comment tout communique
Le cœur du projet tient en une phrase : un clic modifie l'objet monstre, puis on redessine. Voici le flux que tu vas construire :
Ta boucle de travail, toute la journée
1. J'écris un peu de code dans main.js. 2. J'enregistre. 3. Je recharge la page. 4. Je regarde le résultat + la console. Et je recommence.
1. Variables, types & le monstre-objet
Rappel express (tu connais, on fixe le vocabulaire JS)
const prof = "Marc"; // string (texte)
let score = 0; // number (nombre)
const gagne = false; // boolean (true / false)
Un monstre = un objet
Plein d'infos qui vont ensemble → un objet (paires clé : valeur). Dans main.js, sous « Ton monstre de départ » :
let monstre = {
nom: "Zeb",
couleurIndex: 0, // position dans le tableau COULEURS (0 = Turquoise)
eyes: 2,
horns: false,
fangs: true,
};
console.log(monstre);
▶ {nom: 'Zeb', couleurIndex: 0, eyes: 2, horns: false, fangs: true}
FLAG-A1
l'inventaire
Affiche en console COULEURS.length + monstre.eyes précédé de "FLAG-A1-". Recopie la valeur (ex. FLAG-A1-8).
2. Lire & modifier un objet
console.log(monstre.nom); // "Zeb"
monstre.eyes = 3; // on modifie une propriété
monstre.horns = true;
console.log(monstre.eyes, monstre.horns); // 3 true
Nettoie avant de continuer : remets eyes: 2, horns: false, et retire les console.log de test.
3. Dessiner l'aperçu (ta 1re fonction)
Étape 1 — Attraper le canvas
const preview = document.querySelector("#preview");
Étape 2 — La fonction dessiner()
const dessiner = () => {
const c = COULEURS[monstre.couleurIndex];
dessinerMonstre(preview, {
body: c.body, shade: c.shade, light: c.light, mouth: c.shade,
eyes: monstre.eyes, horns: monstre.horns, fangs: monstre.fangs,
}, 14);
document.querySelector("#nomPreview").textContent = monstre.nom;
};
dessiner();
4. Les couleurs (tableau + forEach + clic)
console.log(COULEURS.length); // 6
console.log(COULEURS[0].nom); // "Turquoise"
Étape 1 — Fabriquer les pastilles
const swatchesEl = document.querySelector("#swatches");
COULEURS.forEach((couleur, i) => {
const pastille = document.createElement("button");
pastille.className = "swatch";
pastille.style.background = couleur.body;
pastille.title = couleur.nom;
pastille.addEventListener("click", () => {
monstre.couleurIndex = i;
dessiner();
});
swatchesEl.appendChild(pastille);
});
FLAG-B1
ta couleur à toi
Ajoute une 7ᵉ couleur dans COULEURS (nom + body/shade/light). Recharge : une 7ᵉ pastille apparaît toute seule (le forEach). Sélectionne-la. FLAG-B1-OK + capture.
5. Le nombre d'yeux
const boutonsYeux = document.querySelectorAll("#segEyes button");
boutonsYeux.forEach((bouton) => {
bouton.addEventListener("click", () => {
monstre.eyes = Number(bouton.dataset.eyes);
dessiner();
});
});
Marquer le bouton actif
À la fin de dessiner() :
boutonsYeux.forEach((b) => {
b.classList.toggle("actif", Number(b.dataset.eyes) === monstre.eyes);
});
FLAG-C1
= vs ===
Remplace un instant === monstre.eyes par = monstre.eyes. Enregistre : erreur en console. Lis-la, remets ===, explique la différence entre =, == et ===. FLAG-C1-<ton explication>.
6. Cornes & crocs (booléens)
document.querySelector("#tHorns").addEventListener("click", () => {
monstre.horns = !monstre.horns; // on inverse
dessiner();
});
document.querySelector("#tFangs").addEventListener("click", () => {
monstre.fangs = !monstre.fangs;
dessiner();
});
État actif
document.querySelector("#tHorns").classList.toggle("actif", monstre.horns);
document.querySelector("#tFangs").classList.toggle("actif", monstre.fangs);
7. Le nom (écouter la saisie)
document.querySelector("#champNom").addEventListener("input", (e) => {
monstre.nom = e.target.value.trim() || "Mon monstre";
document.querySelector("#nomPreview").textContent = monstre.nom;
});
FLAG-B2
ton monstre de démarrage
Change le monstre de départ pour qu'au chargement ce soit ton monstre (couleur, 3 yeux, cornes, nom). FLAG-B2-OK + capture.
8. Le bouton Aléatoire (le hasard)
console.log(Math.floor(Math.random() * 6)); // entier de 0 à 5
Le bouton
document.querySelector("#btnAleatoire").addEventListener("click", () => {
monstre = {
nom: "Zeb " + (1 + Math.floor(Math.random() * 99)),
couleurIndex: Math.floor(Math.random() * COULEURS.length),
eyes: 1 + Math.floor(Math.random() * 3), // 1, 2 ou 3
horns: Math.random() < 0.5,
fangs: Math.random() < 0.6,
};
document.querySelector("#champNom").value = "";
dessiner();
});
FLAG-D1
investigation
Cherche (MDN) la différence entre Math.floor et Math.round, avec un exemple chiffré. FLAG-D1-….
9. La galerie (un tableau qui grandit)
const galerie = []; // vide
galerie.push("x"); // ajoute à la fin
console.log(galerie.length); // 1
Ajouter une COPIE du monstre
const galerie = [];
document.querySelector("#btnAjouter").addEventListener("click", () => {
galerie.push({ ...monstre, id: Date.now() });
afficherGalerie(); // écrite au chapitre 10
});
10. Afficher la galerie
const galerieEl = document.querySelector("#galerie");
const compteurEl = document.querySelector("#compteur");
const afficherGalerie = () => {
compteurEl.textContent = galerie.length + (galerie.length > 1 ? " monstres" : " monstre");
galerieEl.innerHTML = "";
galerie.forEach((m) => {
const carte = document.createElement("div");
carte.className = "mon-card";
const canvas = document.createElement("canvas");
carte.appendChild(canvas);
const nom = document.createElement("div");
nom.className = "n"; nom.textContent = m.nom;
carte.appendChild(nom);
galerieEl.appendChild(carte);
const c = COULEURS[m.couleurIndex];
dessinerMonstre(canvas, {
body: c.body, shade: c.shade, light: c.light, mouth: c.shade,
eyes: m.eyes, horns: m.horns, fangs: m.fangs,
}, 5);
});
};
map : transformer un tableau
const noms = galerie.map((m) => m.nom);
console.log(noms); // ["Zeb 1", "Zeb 2", ...]
11. Supprimer un monstre (filter)
const grands = [4, 9, 2, 7].filter((n) => n > 5); // [9, 7]
Cliquer une carte pour la supprimer
Dans afficherGalerie(), après carte.className = "mon-card" :
carte.title = "Cliquer pour supprimer";
carte.addEventListener("click", () => supprimer(m.id));
Puis, en dehors :
const supprimer = (id) => {
const restants = galerie.filter((m) => m.id !== id);
galerie.length = 0;
restants.forEach((m) => galerie.push(m));
afficherGalerie();
};
12. Statistiques (reduce)
const total = [10, 5, 3].reduce((somme, n) => somme + n, 0); // 18
Compter dans la galerie
const totalYeux = galerie.reduce((somme, m) => somme + m.eyes, 0);
console.log("Yeux au total :", totalYeux);
const avecCornes = galerie.filter((m) => m.horns).length;
console.log(avecCornes, "monstre(s) à cornes");
FLAG-A2
le grand compteur
Ajoute ≥ 4 monstres, calcule avec reduce le total d'yeux de la galerie. FLAG-A2-<total>.
13. Chercher : find, some, every, includes
Quatre méthodes de tableau que tu croiseras tout le temps. Toutes prennent une fonction-test.
galerie.find((m) => m.eyes === 3); // le PREMIER à 3 yeux (ou undefined)
galerie.some((m) => m.horns); // y a-t-il AU MOINS un cornu ? → true/false
galerie.every((m) => m.fangs); // TOUS ont des crocs ? → true/false
["Zeb","Croc"].includes("Croc"); // cette valeur est-elle dedans ? → true
Mise en pratique
if (galerie.some((m) => m.eyes === 3)) {
console.log("Tu as déjà un monstre à 3 yeux !");
}
const premierBleu = galerie.find((m) => m.couleurIndex === 4);
console.log(premierBleu ? premierBleu.nom : "aucun bleu");
FLAG-B3
un badge « collection complète »
Écris un test : si la galerie contient au moins un monstre de chaque nombre d'yeux (1, 2 et 3), affiche « Collection complète ! ». Indice : [1,2,3].every((n) => galerie.some((m) => m.eyes === n)). FLAG-B3-OK.
14. Enchaîner map / filter / reduce
La vraie force : ces méthodes se chaînent, chacune passant son résultat à la suivante. Une seule expression répond à une vraie question.
« Les noms des monstres à cornes, triés »
const cornus = galerie
.filter((m) => m.horns) // on garde les cornus
.map((m) => m.nom) // on ne veut que le nom
.sort(); // ordre alphabétique
console.log(cornus);
« Combien d'yeux au total chez les monstres violets ? »
const yeuxViolets = galerie
.filter((m) => m.couleurIndex === 2)
.reduce((somme, m) => somme + m.eyes, 0);
console.log(yeuxViolets);
« Répartition par couleur » (reduce vers un objet)
const parCouleur = galerie.reduce((compte, m) => {
const nom = COULEURS[m.couleurIndex].nom;
compte[nom] = (compte[nom] || 0) + 1;
return compte;
}, {});
console.log(parCouleur); // { Turquoise: 2, Violet: 1, ... }
FLAG-E1
ta requête à toi
Invente une question sur la galerie et réponds-y en chaînant au moins deux méthodes (ex. « le nom du 1er monstre à 3 yeux et cornes », « la couleur la plus fréquente »). Rends la question + ton code. FLAG-E1-OK.
15. Générer une horde
Étape 1 — Sortir la création dans une fonction
const monstreAleatoire = () => ({
nom: "Zeb " + (1 + Math.floor(Math.random() * 99)),
couleurIndex: Math.floor(Math.random() * COULEURS.length),
eyes: 1 + Math.floor(Math.random() * 3),
horns: Math.random() < 0.5,
fangs: Math.random() < 0.6,
id: Date.now() + Math.floor(Math.random() * 1000),
});
Étape 2 — Le bouton (à ajouter dans index.html)
Dans <div class="actions"> : <button id="btnHorde">Horde de 5</button>. Puis :
document.querySelector("#btnHorde").addEventListener("click", () => {
const cinq = [1, 2, 3, 4, 5].map(() => monstreAleatoire());
cinq.forEach((m) => galerie.push(m));
afficherGalerie();
});
16. Trier & filtrer la galerie
Afficher une liste au choix
Rends afficherGalerie paramétrable : const afficherGalerie = (liste = galerie) => { ... liste.forEach ... } (remplace galerie.forEach par liste.forEach et galerie.length par liste.length).
Deux boutons (dans index.html)
<button id="btnTri">Trier A-Z</button>
<button id="btnTri3">3 yeux</button>
document.querySelector("#btnTri").addEventListener("click", () => {
galerie.sort((a, b) => a.nom.localeCompare(b.nom));
afficherGalerie();
});
document.querySelector("#btnTri3").addEventListener("click", () => {
afficherGalerie(galerie.filter((m) => m.eyes === 3)); // vue filtrée
});
17. Sauvegarder la galerie
localStorage.setItem("test", "coucou");
console.log(localStorage.getItem("test")); // "coucou"
JSON : tableau ⇄ texte
const texte = JSON.stringify(galerie); // tableau → texte
const liste = JSON.parse(texte); // texte → tableau
Sauver & recharger
const sauvegarder = () => localStorage.setItem("galerie", JSON.stringify(galerie));
Appelle sauvegarder() à la fin de afficherGalerie(). Puis, tout en bas de main.js :
const sauvegarde = localStorage.getItem("galerie");
if (sauvegarde) {
JSON.parse(sauvegarde).forEach((m) => galerie.push(m));
}
afficherGalerie();
FLAG-D2
investigation
DevTools → onglet Application → Local Storage → clé galerie. Décris le texte JSON stocké. Bonus : bouton « Vider » (localStorage.removeItem("galerie") + vider la galerie). FLAG-D2-….
18. Exporter ton monstre en image
Un bouton « Télécharger » (dans index.html : <button id="btnExport">Télécharger</button>) :
document.querySelector("#btnExport").addEventListener("click", () => {
const lien = document.createElement("a");
lien.download = monstre.nom + ".png";
lien.href = preview.toDataURL("image/png");
lien.click();
});
19. Sous le capot : lire sprites.js
Tu utilises dessinerMonstre depuis ce matin. Maintenant que tu maîtrises les tableaux, ouvre sprites.js et comprends comment il dessine. (Chapitre bonus, mais très formateur en 2e année.)
Le monstre est une grille de 16×16
const SILHOUETTE = [
"................",
"......oooo......",
"....oobbbboo....",
// ...
];
On parcourt la grille et on peint
for (let r = 0; r < 16; r++)
for (let c = 0; c < 16; c++) {
const ch = SILHOUETTE[r][c];
if (ch === "b") px(c, r, options.body);
else if (ch === "o") px(c, r, options.shade);
}
FLAG-E2
modifie le moteur
Dans SILHOUETTE, change une ligne pour donner une autre forme (ex. élargir le bas, ajouter des pieds). Recharge : tous les monstres changent de silhouette. Décris ta modif. FLAG-E2-OK. (Astuce : garde 16 caractères par ligne.)
20. Découper en modules
Ton main.js est long. Les vrais projets (React, Express) découpent en modules.
Étape 1 — un fichier de données
// donnees.js
export const COULEURS = [ /* ... les 6 couleurs ... */ ];
Étape 2 — importer
// tout en haut de main.js
import { COULEURS } from "./donnees.js";
Et dans index.html : <script type="module" src="main.js"></script>.
Défis bonus (si tu as fini avant les autres)
Grand défi — pour les plus rapides
Tu as tout fini, flags et défis compris ? Voici une vraie modification lourde, à faire en autonomie : peu d'indices, pas de corrigé. C'est un projet de dev — tu cherches, tu testes, tu utilises ce que tu as appris + la doc (MDN). Prévois du temps : c'est ambitieux.
L'Atelier de pixels : dessine TON propre monstre
Jusqu'ici, la forme des monstres est imposée par sprites.js. Le défi : offrir à l'utilisateur une grille où il dessine sa propre créature, case par case, puis l'afficher et l'ajouter à la galerie comme les autres.
Aucun corrigé pour ce défi : si tu le mènes au bout, montre-le au prof — c'est un vrai morceau de développeur. Sinon, garde-le sous le coude : on réutilisera ces idées quand on fera le combat des monstres à la prochaine séance.
On fait le point
Mini-quiz (corrigé dessous)
Exercice Réussi À revoir
Voir le corrigéMasquer le corrigé de l'exercice
=affecte ;==compare en convertissant les types (à éviter) ;===compare valeur et type (le bon). 2. Ranger une copie figée du monstre. 3.findrenvoie le premier élément trouvé ;filterrenvoie tous ceux qui passent, dans un tableau. 4. Ils exigent un serveur (CORS bloquefile://) → Live Server. 5. Transforme un tableau/objet en texte stockable/échangeable.
Tu avais trouvé ?
Glossaire & aide
Glossaire
| Terme | Sens |
|---|---|
| const / let | Étiquette sur une valeur. const ne se réassigne pas. |
| Type | string (texte) · number (nombre) · boolean (true/false). |
| Objet | Paquet de paires clé : valeur (un monstre). |
| Tableau | Liste ordonnée. Position à partir de 0. |
| Fonction fléchée | (x) => ... — bout de code réutilisable. |
| Template literal | Texte entre accents graves avec ${...}. |
| Événement | Action utilisateur (clic, saisie) écoutée par addEventListener. |
| forEach / map | Agir sur / transformer chaque élément. |
| filter | Garder les éléments qui passent un test. |
| reduce | Fondre un tableau en une valeur (ou un objet). |
| find/some/every | Le premier trouvé / au moins un / tous. |
Spread ... | Copier / fusionner objets et tableaux. |
| localStorage | Coffre à texte du navigateur, persistant. |
| JSON | Format texte des données (stringify/parse). |
| Module | Fichier .js qui exporte / importe. |
| DOM | La page vue comme un arbre d'éléments, pilotable en JS. |
Tu es bloqué ?
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, destructuring/spread
• Manipuler des collections avec forEach / map / filter / reduce / find
• Structurer une application en modules (import / export)
TP JavaScript moderne (ES6+) · projet « La Fabrique à Monstres » · le combat des monstres se fera à la séance suivante · prépare le développement React/Express d'AppliFrais.