diff --git a/data/lessons.js b/data/lessons.js
index 9838526..a9d77fb 100644
--- a/data/lessons.js
+++ b/data/lessons.js
@@ -13,12 +13,16 @@ export const lessons = [
You should see "Hello World" printed in the output area
`,
+ objectives: [
+ "Print \"Hello World\" to the console"
+ ],
doneCondition: (() => {
- const progress = {
- stringDone: false
- };
- return ({ code, consoleText, codeRanGood }) => {
+
+ return ({ lesson, code, consoleText, codeRanGood }) => {
+ const progress = {
+ stringDone: false
+ };
if (codeRanGood && !progress.stringDone && consoleText.includes("Hello World")) {
progress.stringDone = true;
}
@@ -37,6 +41,7 @@ export const lessons = [
return {
done: progress.stringDone,
+ progressArray: Object.values(progress),
hint
};
};
@@ -63,21 +68,28 @@ export const lessons = [
Click the "Run" button to execute your code
`,
+ objectives: [
+ "Print a string to the console",
+ "Print an int to the console",
+ "Print a float to the console",
+ "Print a boolean (True/False) to the console"
+ ],
doneCondition: (() => {
- const progress = {
- stringDone: false,
- intDone: false,
- floatDone: false,
- boolDone: false,
- // syntaxErrorDone: false, // optional
- };
- const stringRegex = /(["'])(?:(?=(\\?))\2.)*?\1/;
+
+ const stringRegex = /print\s*\(\s*(['"]).*?\1\s*\)/;
const intRegex = /(? {
+ const progress = {
+ stringDone: false,
+ intDone: false,
+ floatDone: false,
+ boolDone: false,
+ // syntaxErrorDone: false, // optional
+ };
if (!codeRanGood) {
return {
done: false,
@@ -85,7 +97,7 @@ export const lessons = [
};
}
- if (!progress.stringDone && stringRegex.test(consoleText)) {
+ if (!progress.stringDone && stringRegex.test(code)) {
progress.stringDone = true;
}
if (!progress.floatDone && floatRegex.test(consoleText)) {
@@ -99,6 +111,7 @@ export const lessons = [
}
const missing = [];
+ if (!progress.stringDone) missing.push("string");
if (!progress.floatDone) missing.push("float");
if (!progress.intDone) missing.push("int");
if (!progress.boolDone) missing.push("boolean");
@@ -112,8 +125,12 @@ export const lessons = [
hint += missing.slice(0, -1).join(", ") + " and " + missing[missing.length - 1];
}
- const done = progress.intDone && progress.floatDone && progress.boolDone;
- return { done, hint };
+ const done = progress.stringDone && progress.intDone && progress.floatDone && progress.boolDone;
+ return {
+ done,
+ progressArray: Object.values(progress),
+ hint
+ };
};
})()
@@ -133,19 +150,25 @@ export const lessons = [
Division: print(20 / 5)
`,
+ objectives: [
+ "Do some addition",
+ "Do some subtraction",
+ "Do some multiplication",
+ "Do some division",
+ ],
doneCondition: (() => {
// Persistent tracking object inside closure
- const progress = {
- addDone: false,
- subDone: false,
- mulDone: false,
- divDone: false,
-
- };
-
return ({ code, consoleText, codeRanGood }) => {
+ const progress = {
+ addDone: false,
+ subDone: false,
+ mulDone: false,
+ divDone: false,
+
+ };
+
if (!codeRanGood) {
return {
done: false,
@@ -190,7 +213,11 @@ export const lessons = [
const done = progress.addDone && progress.subDone && progress.mulDone && progress.divDone;
- return { done, hint };
+ return {
+ done,
+ progressArray: Object.values(progress),
+ hint
+ };
};
})()
},
@@ -211,28 +238,19 @@ export const lessons = [
Print the value of bob: print(bob)
`,
- steps: [
- {
- content: `First, try printing an addition: print(2 + 3)
`,
- doneCondition: (text) => text.includes("5"),
- },
- {
- content: `Nice! Now try subtraction: print(5 - 2)
`,
- doneCondition: (text) => text.includes("3"),
- },
- {
- content: `Now try multiplication: print(4 * 2)
`,
- doneCondition: (text) => text.includes("8"),
- }
+ objectives: [
+ "Initialize a variable with a value",
+ "Print the variable using print()"
],
doneCondition: (() => {
- const progress = {
- varCreated: false,
- varPrinted: false,
- varName: null,
- };
+
return ({ code, consoleText, codeRanGood }) => {
+ const progress = {
+ varCreated: false,
+ varPrinted: false,
+ varName: null,
+ };
if (!codeRanGood) {
return {
done: false,
@@ -274,7 +292,11 @@ export const lessons = [
}
const done = progress.varCreated && progress.varPrinted;
- return { done, hint };
+ return {
+ done,
+ progressArray: Object.values(progress),
+ hint
+ };
};
})()
},
@@ -297,72 +319,111 @@ print(bob)
In this example, the first time you print bob it will give his initial value of 32, but the second time we have added 10 to bob.
Give it a try.
`,
- steps: [
- {
- content: `First, try printing an addition: print(2 + 3)
`,
- doneCondition: (text) => text.includes("5"),
- },
- {
- content: `Nice! Now try subtraction: print(5 - 2)
`,
- doneCondition: (text) => text.includes("3"),
- },
- {
- content: `Now try multiplication: print(4 * 2)
`,
- doneCondition: (text) => text.includes("8"),
- }
+ objectives: [
+ "Initialize a variable with a value",
+ "Print the variable using print()",
+ "Alter the value of the variable",
+ "Print the variable again using print()",
],
doneCondition: (() => {
- const progress = {
- varCreated: false,
- varPrinted: false,
- varArithmeticDone: false,
- varPrintedTwice: false,
- };
+
return ({ code, consoleText, codeRanGood }) => {
+ const progress = {
+ varCreated: false,
+ varPrinted: false,
+ varArithmeticDone: false,
+ varPrintedTwice: false,
+ };
if (!codeRanGood) {
return {
done: false,
+ progressArray: Object.values(progress),
hint: ""
};
}
- const assignRegex = /\b(\w+)\s*=\s*[\d'"]/; // variable assignment
- const printRegex1 = /print\(\s*\1\s*\)/; // first print of the variable
- const mathRegex = /\1\s*=\s*\1\s*[\+\-\*/]\s*[\d'"]/; // arithmetic using itself
- const printRegex2 = /print\(\s*\1\s*\)/; // second print of the variable
- const match = code.match(assignRegex);
- if (!match) return false;
-
- const varName = match[1];
- const dynamicPrint1 = new RegExp(`print\\(\\s*${varName}\\s*\\)`);
- const dynamicMath = new RegExp(`${varName}\\s*=\\s*${varName}\\s*[+\\-*/]\\s*[\\d'"]`);
- const dynamicPrint2 = new RegExp(`print\\(\\s*${varName}\\s*\\)`, 'g');
-
- progress.varCreated = match;
- progress.dynamicPrint1 = dynamicPrint1.test(code);
- progress.varArithmeticDone = dynamicMath.test(code);
- progress.varPrintedTwice = (code.match(dynamicPrint2) || []).length >= 2
-
- let missing = [];
- if (!progress.varCreated) missing.push("create a variable");
- if (!progress.dynamicPrint1) missing.push("print the variable");
- if (!progress.varArithmeticDone) missing.push("alter the variable");
- if (!progress.varPrintedTwice) missing.push("print the variable a second time");
-
- let hint = "I still need you to ";
- if (missing.length === 0) {
- hint = "";
- } else if (missing.length === 1) {
- hint += missing[0];
- } else {
- hint += missing.slice(0, -1).join(", ") + " and " + missing[missing.length - 1];
+ const assignRegex = /^(\w+)\s*=\s*.+$/m;
+ const assignMatch = assignRegex.exec(code);
+ if (!assignMatch) {
+ return {
+ done: false,
+ progressArray: Object.values(progress),
+ hint: "I still need you to create a variable"
+ };
}
- const done = progress.varCreated && progress.dynamicPrint1 && progress.varArithmeticDone && progress.varPrintedTwice;
- return { done, hint };
+ const varName = assignMatch[1];
+
+ const printRegex = new RegExp(`print\\s*\\(\\s*${varName}\\s*\\)`, "g");
+ const assignAllRegex = new RegExp(`^${varName}\\s*=.+$`, "gm");
+ const arithmeticRegex = new RegExp(
+ `^${varName}\\s*=\\s*${varName}\\s*[+\\-*/]\\s*.+$|^${varName}\\s*[+\\-*/]=\\s*.+$`,
+ "gm"
+ );
+
+ progress.varCreated = true;
+
+ const printMatches = [...code.matchAll(printRegex)];
+ progress.varPrinted = printMatches.length > 0;
+ if (!progress.varPrinted) {
+ return {
+ done: false,
+ progressArray: Object.values(progress),
+ hint: "I still need you to print the variable"
+ };
+ }
+
+ progress.varArithmeticDone = arithmeticRegex.test(code);
+ if (!progress.varArithmeticDone) {
+ return {
+ done: false,
+ progressArray: Object.values(progress),
+
+ hint: "I still need you to alter the variable (e.g. with arithmetic)"
+ };
+ }
+
+ const assignMatches = [...code.matchAll(assignAllRegex)];
+ if (assignMatches.length < 2) {
+ return {
+ done: false,
+ progressArray: Object.values(progress),
+ hint: "I still need you to alter the variable before printing it again"
+ };
+ }
+
+ // Debug logs
+ console.log("Variable:", varName);
+ console.log("Assignments found:", assignMatches.length);
+ assignMatches.forEach((m, i) =>
+ console.log(`Assign #${i}: index=${m.index}, text=${m[0].trim()}`)
+ );
+ console.log("Prints found:", printMatches.length);
+ printMatches.forEach((m, i) =>
+ console.log(`Print #${i}: index=${m.index}, text=${code.slice(m.index, m.index + 20)}`)
+ );
+ console.log("Arithmetic detected:", progress.varArithmeticDone);
+
+ const secondAssignIndex = assignMatches[1].index;
+ progress.varPrintedTwice = printMatches.some((m) => m.index > secondAssignIndex);
+
+ if (!progress.varPrintedTwice) {
+ return {
+ done: false,
+ progressArray: Object.values(progress),
+ hint: "I still need you to print the variable a second time after altering it",
+ };
+ }
+
+ return {
+ done: true,
+ progressArray: Object.values(progress),
+ hint: "",
+ };
};
})()
+
},
{
id: 'lesson6',
@@ -394,15 +455,20 @@ if num == 10:
print("num is equal to 10")
`,
+ objectives: [
+ "Initialize a variable with a value",
+ "Create an if statement to check a condition", ,
+ "Print a message based on the condition",
+ ],
doneCondition: (() => {
- const progress = {
- varCreated: false,
- ifStatement: false,
- printedSomething: false,
- };
return ({ code, consoleText, codeRanGood }) => {
+ const progress = {
+ varCreated: false,
+ ifStatement: false,
+ printedSomething: false,
+ };
if (!codeRanGood) {
return {
done: false,
@@ -434,7 +500,11 @@ if num == 10:
}
const done = progress.varCreated && progress.ifStatement && progress.printedSomething;
- return { done, hint };
+ return {
+ done,
+ progressArray: Object.values(progress),
+ hint
+ };
};
})()
@@ -459,17 +529,24 @@ else:
You can chain as many elif statements as you like, but only one else at the end.
Try it for yourself:
`,
-
+ objectives: [
+ "Initialize a variable with a value",
+ "Create an if statement to check a condition", ,
+ "Create an elif statement to check another condition",
+ "Create an else statement to catch all other conditions",
+ "Print a message based on each of the conditions",
+ ],
doneCondition: (() => {
- const progress = {
- varCreated: false,
- ifStatement: false,
- elifStatement: false,
- elseStatement: false,
- threeDistinctPrints: false,
- };
return ({ code, consoleText, codeRanGood }) => {
+
+ const progress = {
+ varCreated: false,
+ ifStatement: false,
+ elifStatement: false,
+ elseStatement: false,
+ threeDistinctPrints: false,
+ };
if (!codeRanGood) {
return { done: false, hint: "" };
}
@@ -536,7 +613,11 @@ else:
const done = progress.varCreated && progress.ifStatement && progress.elifStatement &&
progress.elseStatement && progress.threeDistinctPrints;
- return { done, hint };
+ return {
+ done,
+ progressArray: Object.values(progress),
+ hint
+ };
};
})()
},
diff --git a/game.js b/game.js
index 2046943..40c967b 100644
--- a/game.js
+++ b/game.js
@@ -27,6 +27,7 @@ function showLesson(index) {
//console.log(isLessonDone(lesson.id));
updateLessonStatus();
+ PopulateObjectives(lesson.objectives); // Populate objectives for the lesson
}
function loadLessonContent(lesson) {
@@ -60,6 +61,17 @@ function checkLessonDone() {
if (result.done) {
markLessonDone(lesson.id);
}
+ if (result.progressArray){
+ console.log("Progress: ", result.progressArray);
+ for (let i = 0; i < result.progressArray.length; i++) {
+ const objective = result.progressArray[i];
+ if (objective) {
+ toggleObjective(i, true); // Mark as completed
+ } else {
+ toggleObjective(i, false); // Mark as not completed
+ }
+ }
+ }
if (result.hint) {
logToConsole("Hint: " + result.hint, false);
//console.log("Hint:", result.hint); // Or show it in your console UI
@@ -132,8 +144,47 @@ function clearLessonProgress() {
}
+let objectiveElements = []; // store checkbox + item div
+
+const listContainer = document.getElementById("objectives-list");
+
+function PopulateObjectives(objectives) {
+
+ listContainer.innerHTML = ""; // Clear the DOM
+ objectiveElements.length = 0; // Clear the stored references
+ objectives.forEach((text, index) => {
+ const item = document.createElement("div");
+ item.className = "objective";
+
+ const checkbox = document.createElement("input");
+ checkbox.type = "checkbox";
+ checkbox.id = "obj-" + index;
+ checkbox.disabled = true; // make it non-interactive
+
+ const label = document.createElement("label");
+ label.htmlFor = checkbox.id;
+ label.textContent = text;
+
+ item.appendChild(checkbox);
+ item.appendChild(label);
+ listContainer.appendChild(item);
+
+ objectiveElements.push({ checkbox, item }); // Store reference
+ });
+}
+
+
+
+function toggleObjective(index, completed = true) {
+ const obj = objectiveElements[index];
+ if (obj) {
+ obj.checkbox.checked = completed;
+ obj.item.classList.toggle("completed", completed);
+ }
+}
+
clearLessonProgress(); // Clear progress on load for testing
-showLesson(7);
+showLesson(6);
const consoleElement = document.getElementById("console");
const gameCanvas = document.getElementById("gameCanvas");
@@ -166,7 +217,7 @@ function startPyodideWorker() {
switch (event.data.type) {
case "console":
logToConsole(event.data.data);
-
+
break;
case "error":
logToConsole(`${event.data.message}`);
diff --git a/index.html b/index.html
index 7dc403a..4eca3c0 100644
--- a/index.html
+++ b/index.html
@@ -27,22 +27,34 @@
-
-
Lesson Title
-
Lesson Content
+
-
-
-
-
+
+
+
Lesson Title
+
Lesson Content
+
+
-
+
+
+
+
-
diff --git a/style.css b/style.css
index 05248e5..b2c03b1 100644
--- a/style.css
+++ b/style.css
@@ -16,6 +16,28 @@ html {
}
+
+ .objective {
+ margin: 10px 0;
+ display: flex;
+ align-items: center;
+ }
+
+ .objective label {
+ margin-left: 8px;
+ transition: color 0.3s;
+ }
+
+ .completed label {
+ color: green;
+ font-weight: bold;
+ text-decoration: line-through;
+ }
+
+ input[type="checkbox"] {
+ pointer-events: none; /* make it non-clickable */
+ }
+
/* ===== Header (top bar) ===== */
header {
background-color: #f3f4f6;