Aller au contenu
Réussir mon BTS SIO

Tape une notion, une commande ou le nom d'une épreuve.

Par exemple

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.

Schéma : L'écran que tu vas faire fonctionner · Zeb · COULEUR · YEUX · 1 · 2 · 3 · Aléatoire · Ajouter

Comment tu avances (en autonomie)

Ce que tu sauras faire ce soir

NotionDans le générateur, ça sert à…
const/let, typesDécrire un monstre, un compteur, un nom.
ObjetsRanger toutes les caractéristiques d'un monstre.
Fonctions fléchées + templatesÉcrire dessiner(), fabriquer du texte.
Tableaux, forEach, mapCréer les pastilles, afficher la galerie.
Événements (clic, saisie)Réagir à l'utilisateur.
Math.randomLe bouton « Aléatoire », la horde.
filter/reduce/find/someSupprimer, compter, statistiques, recherches.
JSON + localStorageSauvegarder la galerie.
Modules (import/export)Découper le projet, comme dans React.

Le plan de la journée

PartieOn construit…
0 · Prise en maincomprendre le projet fourni, le lancer, la boucle de travail.
1 · Réveiller la Fabriquel'aperçu + tous les réglages (couleur, yeux, cornes, crocs, nom).
2 · Le hasard & la galerieAléatoire, galerie, compteur, supprimer, stats, recherches.
3 · On va plus loinhorde, 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 navigateurChrome ou Firefox, à jour. C'est lui qui exécute le JavaScript.
VS CodeL'éditeur de code. Ouvre le dossier du projet (Fichier → Ouvrir le dossier), pas juste un fichier.
L'extension Live ServerUtile en fin de journée (chapitre Modules). Onglet Extensions (⧉) → cherche « Live Server » → Installer. On s'en sert au chapitre 20.
Le dossier fourniRé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

FichierCe qu'il contientTu l'édites ?
index.htmlLa 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.cssL'habillage : couleurs, cartes, boutons « game », l'aperçu en damier. Déjà écrit.Non
sprites.jsLe 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.jsTON 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 :

Ce que tu dois voir
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 :

Schéma : CLIC / SAISIE · (un réglage) · on modifie · l'objet monstre · dessiner() · (ta fonction) · dessinerMonstre · → pixels · Le flux d'un réglage, du clic aux pixels

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);
Ce que tu dois voir
▶ {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).

0 sur 11 dans cette étape

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.

0 sur 11 dans cette étape

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>.

0 sur 11 dans cette étape

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.

0 sur 11 dans cette étape

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-….

0 sur 11 dans cette étape

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>.

0 sur 11 dans cette étape

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.

0 sur 11 dans cette étape

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.

0 sur 11 dans cette étape

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)

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-….

0 sur 11 dans cette étape

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.)

0 sur 11 dans cette étape

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

bash
// donnees.js
export const COULEURS = [ /* ... les 6 couleurs ... */ ];

Étape 2 — importer

python
// 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
  1. = affecte ; == compare en convertissant les types (à éviter) ; === compare valeur et type (le bon). 2. Ranger une copie figée du monstre. 3. find renvoie le premier élément trouvé ; filter renvoie tous ceux qui passent, dans un tableau. 4. Ils exigent un serveur (CORS bloque file://) → Live Server. 5. Transforme un tableau/objet en texte stockable/échangeable.

Tu avais trouvé ?

Glossaire & aide

Glossaire

TermeSens
const / letÉtiquette sur une valeur. const ne se réassigne pas.
Typestring (texte) · number (nombre) · boolean (true/false).
ObjetPaquet de paires clé : valeur (un monstre).
TableauListe ordonnée. Position à partir de 0.
Fonction fléchée(x) => ... — bout de code réutilisable.
Template literalTexte entre accents graves avec ${...}.
ÉvénementAction utilisateur (clic, saisie) écoutée par addEventListener.
forEach / mapAgir sur / transformer chaque élément.
filterGarder les éléments qui passent un test.
reduceFondre un tableau en une valeur (ou un objet).
find/some/everyLe premier trouvé / au moins un / tous.
Spread ...Copier / fusionner objets et tableaux.
localStorageCoffre à texte du navigateur, persistant.
JSONFormat texte des données (stringify/parse).
ModuleFichier .js qui exporte / importe.
DOMLa 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)

0 sur 11 dans cette étape

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.

TP terminé ?

Coche chaque livrable quand tu l'as : c'est la preuve que l'étape est acquise.

livrables
–
quiz réussi
–
de série
–
Voir tous les TP