diff --git a/README.md b/README.md
index c8dbe97..b41f971 100644
--- a/README.md
+++ b/README.md
@@ -60,6 +60,7 @@ CookieCode.ingredient("farine", 200, "g");
CookieCode.materiel("saladier");
CookieCode.constante("temperature", 180);
CookieCode.preparation("pate");
+CookieCode.defi("Cookie avec boucle", { ingredientsMinimum: 3 });
```
### Methodes d'une recette
@@ -81,6 +82,7 @@ CookieCode.preparation("pate");
.afficher()
.toText()
.toHTML()
+.analyser()
```
## Exemple avec variables
@@ -117,6 +119,62 @@ CookieCode.recette("Pate a cookies")
.servir();
```
+## Feedback ludique
+
+CookieCode peut analyser une recette et verifier si elle respecte un defi.
+
+```js
+const recette = CookieCode.recette("Cookie")
+ .prendre("farine", 200, "g")
+ .prendre("sucre", 100, "g")
+ .prendre("chocolat", 150, "g")
+ .ajouter("farine")
+ .ajouter("sucre")
+ .ajouter("chocolat")
+ .melanger()
+ .repeter(2, function (recette) {
+ recette.melanger();
+ })
+ .cuire(12, "minutes")
+ .servir();
+
+const defi = CookieCode.defi("Cookie avec boucle", {
+ ingredientsMinimum: 3,
+ actionsObligatoires: ["melanger", "cuire", "servir"],
+ boucleObligatoire: true,
+ terminerParServir: true,
+ interdireErreurs: true
+});
+
+const verification = CookieCode.verifier(recette, defi);
+console.log(verification.toText());
+```
+
+Sortie possible :
+
+```txt
+Defi : Cookie avec boucle
+Resultat : reussi
+✅ Tu as utilise 3 ingredient(s).
+✅ Action trouvee : melanger.
+✅ Action trouvee : cuire.
+✅ Action trouvee : servir.
+✅ Tu as utilise une boucle avec repeter.
+✅ Ta recette se termine par servir.
+✅ Aucune erreur logique detectee.
+```
+
+Une recette peut aussi etre analysee directement :
+
+```js
+const rapport = recette.analyser();
+
+console.log(rapport.ingredients);
+console.log(rapport.actions);
+console.log(rapport.utiliseBoucle);
+console.log(rapport.erreurs);
+```
+
## Cartes pedagogiques conseillees
| Type de carte | Mots cles | Notion |
diff --git a/dist/cookiecode.js b/dist/cookiecode.js
index 1f263a6..7f5a5c4 100644
--- a/dist/cookiecode.js
+++ b/dist/cookiecode.js
@@ -381,6 +381,47 @@
return lignes;
};
+ Recette.prototype.analyser = function () {
+ var actions = this.notions.fonctions.slice();
+ var etapesAPlates = aplatirEtapes(this.etapes);
+ var derniereAction =
+ etapesAPlates.length > 0
+ ? actionDeTexte(etapesAPlates[etapesAPlates.length - 1].texte)
+ : "";
+ var erreurs = [];
+
+ if (this.etapes.length === 0) {
+ erreurs.push("Ta recette est vide : ajoute au moins une etape.");
+ }
+
+ if (this.etapes.length > 0 && derniereAction !== "servir") {
+ erreurs.push("Ta recette ne se termine pas par servir.");
+ }
+
+ etapesAPlates.forEach((etape, index) => {
+ if (
+ actionDeTexte(etape.texte) === "servir" &&
+ index !== etapesAPlates.length - 1
+ ) {
+ erreurs.push("Tu as servi avant la fin de la recette.");
+ }
+ });
+
+ return {
+ nom: this.nom,
+ ingredients: this.notions.variables.slice(),
+ variables: this.notions.variables.slice(),
+ materiels: this.notions.constantes.slice(),
+ constantes: this.notions.constantes.slice(),
+ actions: actions,
+ nombreEtapes: etapesAPlates.length,
+ utiliseBoucle: this.notions.boucles,
+ utiliseCondition: this.notions.conditions,
+ termineParServir: derniereAction === "servir",
+ erreurs: erreurs,
+ };
+ };
+
Recette.prototype._lignesNotions = function () {
var lignes = [
"",
@@ -446,6 +487,148 @@
return html;
};
+ function aplatirEtapes(etapes) {
+ var resultat = [];
+ etapes.forEach((etape) => {
+ resultat.push(etape);
+ if (etape.enfants && etape.enfants.length > 0) {
+ resultat = resultat.concat(aplatirEtapes(etape.enfants));
+ }
+ });
+ return resultat;
+ }
+
+ function actionDeTexte(texteEtape) {
+ var index;
+ var valeur = texte(texteEtape).toLowerCase();
+ var actions = [
+ "prendre",
+ "utiliser",
+ "ajouter",
+ "retirer",
+ "couper",
+ "melanger",
+ "verser",
+ "chauffer",
+ "cuire",
+ "attendre",
+ "repeter",
+ "si",
+ "servir",
+ ];
+
+ for (index = 0; index < actions.length; index += 1) {
+ if (valeur.indexOf(actions[index]) === 0) {
+ return actions[index];
+ }
+ }
+
+ return "";
+ }
+
+ function defi(nom, regles) {
+ if (!nom && nom !== 0) {
+ throw new Error("CookieCode : un defi doit avoir un nom.");
+ }
+
+ return {
+ type: "defi",
+ nom: texte(nom),
+ regles: regles || {},
+ };
+ }
+
+ function verifier(recetteAVerifier, defiAVerifier) {
+ var rapport = recetteAVerifier.analyser();
+ var regles = defiAVerifier?.regles ? defiAVerifier.regles : {};
+ var messages = [];
+ var reussi = true;
+
+ function valider(condition, succes, echec) {
+ if (condition) {
+ messages.push("✅ " + succes);
+ } else {
+ reussi = false;
+ messages.push("❌ " + echec);
+ }
+ }
+
+ if (regles.ingredientsMinimum !== undefined) {
+ valider(
+ rapport.ingredients.length >= regles.ingredientsMinimum,
+ "Tu as utilise " + rapport.ingredients.length + " ingredient(s).",
+ "Il faut au moins " + regles.ingredientsMinimum + " ingredient(s).",
+ );
+ }
+
+ if (regles.materielsMinimum !== undefined) {
+ valider(
+ rapport.materiels.length >= regles.materielsMinimum,
+ "Tu as utilise " + rapport.materiels.length + " materiel(s).",
+ "Il faut au moins " + regles.materielsMinimum + " materiel(s).",
+ );
+ }
+
+ (regles.actionsObligatoires || []).forEach((action) => {
+ valider(
+ rapport.actions.indexOf(action) !== -1,
+ "Action trouvee : " + action + ".",
+ "Action manquante : " + action + ".",
+ );
+ });
+
+ if (regles.boucleObligatoire) {
+ valider(
+ rapport.utiliseBoucle,
+ "Tu as utilise une boucle avec repeter.",
+ "Il manque une boucle : essaie avec .repeter().",
+ );
+ }
+
+ if (regles.conditionObligatoire) {
+ valider(
+ rapport.utiliseCondition,
+ "Tu as utilise une condition avec si.",
+ "Il manque une condition : essaie avec .si().",
+ );
+ }
+
+ if (regles.terminerParServir) {
+ valider(
+ rapport.termineParServir,
+ "Ta recette se termine par servir.",
+ "Ta recette doit se terminer par .servir().",
+ );
+ }
+
+ if (regles.interdireErreurs) {
+ valider(
+ rapport.erreurs.length === 0,
+ "Aucune erreur logique detectee.",
+ rapport.erreurs.join(" ") || "Une erreur logique a ete detectee.",
+ );
+ }
+
+ if (messages.length === 0) {
+ messages.push("ℹ️ Aucun critere de defi a verifier.");
+ }
+
+ return {
+ reussi: reussi,
+ nom: defiAVerifier ? defiAVerifier.nom : "Defi",
+ messages: messages,
+ rapport: rapport,
+ toText: function () {
+ return [
+ "Defi : " + this.nom,
+ this.reussi ? "Resultat : reussi" : "Resultat : a ameliorer",
+ ]
+ .concat(this.messages)
+ .join("\n");
+ },
+ };
+ }
+
function etapesHTML(etapes) {
var html = "
";
etapes.forEach((etape) => {
@@ -470,7 +653,9 @@
materiel: materiel,
constante: constante,
preparation: preparation,
- version: "0.1.0",
+ defi: defi,
+ verifier: verifier,
+ version: "0.2.0",
};
return CookieCode;
diff --git a/examples/demo.js b/examples/demo.js
index 01e5cf0..66e23ba 100644
--- a/examples/demo.js
+++ b/examples/demo.js
@@ -3,7 +3,7 @@ CookieCode.configurer({
afficherNotions: true,
});
-CookieCode.recette("Cookie au chocolat")
+const recette = CookieCode.recette("Cookie au chocolat")
.prendre("farine", 200, "g")
.prendre("sucre", 100, "g")
.prendre("chocolat", 150, "g")
@@ -17,3 +17,13 @@ CookieCode.recette("Cookie au chocolat")
})
.cuire(12, "minutes")
.servir();
+
+const defi = CookieCode.defi("Cookie avec boucle", {
+ ingredientsMinimum: 3,
+ actionsObligatoires: ["melanger", "cuire", "servir"],
+ boucleObligatoire: true,
+ terminerParServir: true,
+ interdireErreurs: true
+});
+
+console.log(CookieCode.verifier(recette, defi).toText());
diff --git a/features/cookiecode.feature b/features/cookiecode.feature
index ba653a1..3a3b83d 100644
--- a/features/cookiecode.feature
+++ b/features/cookiecode.feature
@@ -24,3 +24,15 @@ Feature: Apprendre l'algorithmique avec CookieCode
Then la recette affiche l'etape conditionnelle
And les actions du bloc conditionnel sont affichees
And la lecture algorithmique indique que si est une condition
+
+ Scenario: Verifier un defi ludique
+ Given un defi demande au moins trois ingredients, une boucle et une fin avec servir
+ When l'apprenant verifie une recette qui respecte ces criteres
+ Then CookieCode indique que le defi est reussi
+ And CookieCode affiche des messages de validation positifs
+
+ Scenario: Aider a ameliorer une recette incomplete
+ Given un defi demande au moins trois ingredients, une boucle et une fin avec servir
+ When l'apprenant verifie une recette incomplete
+ Then CookieCode indique que le resultat est a ameliorer
+ And CookieCode explique les criteres manquants
diff --git a/package.json b/package.json
index 08030d9..a568e59 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "cookiecode",
- "version": "0.1.0",
+ "version": "0.2.0",
"description": "Microlibrairie educative pour apprendre l'algorithmique avec des recettes de cuisine.",
"main": "dist/cookiecode.js",
"scripts": {
diff --git a/src/cookiecode.js b/src/cookiecode.js
index 1f263a6..7f5a5c4 100644
--- a/src/cookiecode.js
+++ b/src/cookiecode.js
@@ -381,6 +381,47 @@
return lignes;
};
+ Recette.prototype.analyser = function () {
+ var actions = this.notions.fonctions.slice();
+ var etapesAPlates = aplatirEtapes(this.etapes);
+ var derniereAction =
+ etapesAPlates.length > 0
+ ? actionDeTexte(etapesAPlates[etapesAPlates.length - 1].texte)
+ : "";
+ var erreurs = [];
+
+ if (this.etapes.length === 0) {
+ erreurs.push("Ta recette est vide : ajoute au moins une etape.");
+ }
+
+ if (this.etapes.length > 0 && derniereAction !== "servir") {
+ erreurs.push("Ta recette ne se termine pas par servir.");
+ }
+
+ etapesAPlates.forEach((etape, index) => {
+ if (
+ actionDeTexte(etape.texte) === "servir" &&
+ index !== etapesAPlates.length - 1
+ ) {
+ erreurs.push("Tu as servi avant la fin de la recette.");
+ }
+ });
+
+ return {
+ nom: this.nom,
+ ingredients: this.notions.variables.slice(),
+ variables: this.notions.variables.slice(),
+ materiels: this.notions.constantes.slice(),
+ constantes: this.notions.constantes.slice(),
+ actions: actions,
+ nombreEtapes: etapesAPlates.length,
+ utiliseBoucle: this.notions.boucles,
+ utiliseCondition: this.notions.conditions,
+ termineParServir: derniereAction === "servir",
+ erreurs: erreurs,
+ };
+ };
+
Recette.prototype._lignesNotions = function () {
var lignes = [
"",
@@ -446,6 +487,148 @@
return html;
};
+ function aplatirEtapes(etapes) {
+ var resultat = [];
+ etapes.forEach((etape) => {
+ resultat.push(etape);
+ if (etape.enfants && etape.enfants.length > 0) {
+ resultat = resultat.concat(aplatirEtapes(etape.enfants));
+ }
+ });
+ return resultat;
+ }
+
+ function actionDeTexte(texteEtape) {
+ var index;
+ var valeur = texte(texteEtape).toLowerCase();
+ var actions = [
+ "prendre",
+ "utiliser",
+ "ajouter",
+ "retirer",
+ "couper",
+ "melanger",
+ "verser",
+ "chauffer",
+ "cuire",
+ "attendre",
+ "repeter",
+ "si",
+ "servir",
+ ];
+
+ for (index = 0; index < actions.length; index += 1) {
+ if (valeur.indexOf(actions[index]) === 0) {
+ return actions[index];
+ }
+ }
+
+ return "";
+ }
+
+ function defi(nom, regles) {
+ if (!nom && nom !== 0) {
+ throw new Error("CookieCode : un defi doit avoir un nom.");
+ }
+
+ return {
+ type: "defi",
+ nom: texte(nom),
+ regles: regles || {},
+ };
+ }
+
+ function verifier(recetteAVerifier, defiAVerifier) {
+ var rapport = recetteAVerifier.analyser();
+ var regles = defiAVerifier?.regles ? defiAVerifier.regles : {};
+ var messages = [];
+ var reussi = true;
+
+ function valider(condition, succes, echec) {
+ if (condition) {
+ messages.push("✅ " + succes);
+ } else {
+ reussi = false;
+ messages.push("❌ " + echec);
+ }
+ }
+
+ if (regles.ingredientsMinimum !== undefined) {
+ valider(
+ rapport.ingredients.length >= regles.ingredientsMinimum,
+ "Tu as utilise " + rapport.ingredients.length + " ingredient(s).",
+ "Il faut au moins " + regles.ingredientsMinimum + " ingredient(s).",
+ );
+ }
+
+ if (regles.materielsMinimum !== undefined) {
+ valider(
+ rapport.materiels.length >= regles.materielsMinimum,
+ "Tu as utilise " + rapport.materiels.length + " materiel(s).",
+ "Il faut au moins " + regles.materielsMinimum + " materiel(s).",
+ );
+ }
+
+ (regles.actionsObligatoires || []).forEach((action) => {
+ valider(
+ rapport.actions.indexOf(action) !== -1,
+ "Action trouvee : " + action + ".",
+ "Action manquante : " + action + ".",
+ );
+ });
+
+ if (regles.boucleObligatoire) {
+ valider(
+ rapport.utiliseBoucle,
+ "Tu as utilise une boucle avec repeter.",
+ "Il manque une boucle : essaie avec .repeter().",
+ );
+ }
+
+ if (regles.conditionObligatoire) {
+ valider(
+ rapport.utiliseCondition,
+ "Tu as utilise une condition avec si.",
+ "Il manque une condition : essaie avec .si().",
+ );
+ }
+
+ if (regles.terminerParServir) {
+ valider(
+ rapport.termineParServir,
+ "Ta recette se termine par servir.",
+ "Ta recette doit se terminer par .servir().",
+ );
+ }
+
+ if (regles.interdireErreurs) {
+ valider(
+ rapport.erreurs.length === 0,
+ "Aucune erreur logique detectee.",
+ rapport.erreurs.join(" ") || "Une erreur logique a ete detectee.",
+ );
+ }
+
+ if (messages.length === 0) {
+ messages.push("ℹ️ Aucun critere de defi a verifier.");
+ }
+
+ return {
+ reussi: reussi,
+ nom: defiAVerifier ? defiAVerifier.nom : "Defi",
+ messages: messages,
+ rapport: rapport,
+ toText: function () {
+ return [
+ "Defi : " + this.nom,
+ this.reussi ? "Resultat : reussi" : "Resultat : a ameliorer",
+ ]
+ .concat(this.messages)
+ .join("\n");
+ },
+ };
+ }
+
function etapesHTML(etapes) {
var html = "";
etapes.forEach((etape) => {
@@ -470,7 +653,9 @@
materiel: materiel,
constante: constante,
preparation: preparation,
- version: "0.1.0",
+ defi: defi,
+ verifier: verifier,
+ version: "0.2.0",
};
return CookieCode;
diff --git a/test/cookiecode.test.js b/test/cookiecode.test.js
index 33a9888..7ddabda 100644
--- a/test/cookiecode.test.js
+++ b/test/cookiecode.test.js
@@ -1,7 +1,7 @@
const assert = require("assert");
const CookieCode = require("../dist/cookiecode.js");
-CookieCode.configurer({ sortie: null, afficherNotions: true });
+CookieCode.configurer({ sortie: "test", afficherNotions: true });
const texteRecette = CookieCode.recette("Cookie au chocolat")
.prendre("farine", 200, "g")
@@ -48,6 +48,50 @@ assert.ok(!html.includes("
{
+ recette.melanger();
+ })
+ .cuire(12, "minutes")
+ .servir();
+
+const rapport = recetteDefi.analyser();
+assert.deepStrictEqual(rapport.ingredients, ["farine", "sucre", "chocolat"]);
+assert.ok(rapport.actions.includes("repeter"));
+assert.strictEqual(rapport.utiliseBoucle, true);
+assert.strictEqual(rapport.termineParServir, true);
+assert.deepStrictEqual(rapport.erreurs, []);
+
+const defiCookie = CookieCode.defi("Cookie avec boucle", {
+ ingredientsMinimum: 3,
+ actionsObligatoires: ["melanger", "cuire", "servir"],
+ boucleObligatoire: true,
+ terminerParServir: true,
+ interdireErreurs: true,
+});
+
+const verification = CookieCode.verifier(recetteDefi, defiCookie);
+assert.strictEqual(verification.reussi, true);
+assert.match(verification.toText(), /Resultat : reussi/);
+assert.match(verification.toText(), /✅ Tu as utilise 3 ingredient\(s\)\./);
+
+const recetteIncomplete = CookieCode.recette("Cookie incomplet")
+ .prendre("farine", 200, "g")
+ .melanger();
+
+const verificationIncomplete = CookieCode.verifier(recetteIncomplete, defiCookie);
+assert.strictEqual(verificationIncomplete.reussi, false);
+assert.match(verificationIncomplete.toText(), /❌ Il faut au moins 3 ingredient\(s\)\./);
+assert.match(verificationIncomplete.toText(), /❌ Il manque une boucle : essaie avec \.repeter\(\)\./);
+assert.match(verificationIncomplete.toText(), /❌ Ta recette doit se terminer par \.servir\(\)\./);
+
+assert.strictEqual(CookieCode.version, "0.2.0");
console.log("CookieCode tests: OK");