added second robot lesson and changing levels. Need to check win conditions on all checkpoints reached, current error leaves level incomplete if checkpoint is reached after code finishes
parent
8a0219500d
commit
3551ec747d
213
data/lessons.js
213
data/lessons.js
|
|
@ -848,77 +848,6 @@ print("Done!")
|
|||
})()
|
||||
|
||||
|
||||
},
|
||||
{
|
||||
id: 'robot1',
|
||||
title: '1. Moving the Robot',
|
||||
tabtitle: 'Importing Modules',
|
||||
level: 'robot',
|
||||
content: `
|
||||
<p>This robot simulation is a simplified version of a real robot.</p>
|
||||
<p>It has a single library which you can use to access all its controls and sensors.</p>
|
||||
<p>In a real robot you would have many different libraries for different parts, sensors, and even microcontroller functions.</p>
|
||||
</br>
|
||||
<p>We'll start by importing the robot library, and using it to move.</p>
|
||||
<pre><code>
|
||||
import robot # Import the robot library
|
||||
import time # Import the time module
|
||||
|
||||
robot.move(1) # Move forward at max speed
|
||||
time.sleep(2) # Wait for 2 seconds
|
||||
robot.move(-1) # Move backward at max speed
|
||||
time.sleep(2) # Wait for 2 seconds
|
||||
robot.move(0) # Stop the robot
|
||||
</code></pre>
|
||||
`,
|
||||
objectives: [
|
||||
"Reach the first checkpoint",
|
||||
"Reach the second checkpoint",
|
||||
|
||||
"Code should complete without errors"
|
||||
],
|
||||
|
||||
doneCondition: (() => {
|
||||
return ({ code, consoleText, codeRanGood, gameWorld }) => {
|
||||
const progress = {
|
||||
firstCheckpoint: gameWorld.waypointsReached[0],
|
||||
secondCheckpoint: gameWorld.waypointsReached[1],
|
||||
codeRanGood: codeRanGood,
|
||||
|
||||
};
|
||||
|
||||
if (!codeRanGood) {
|
||||
return { done: false, hint: "" };
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 5. Build hint
|
||||
const missing = [];
|
||||
if (!progress.firstCheckpoint) missing.push("reach the first checkpoint");
|
||||
if (!progress.secondCheckpoint) missing.push("reach the second checkpoint");
|
||||
|
||||
let hint = "";
|
||||
if (missing.length === 1) {
|
||||
hint = `I still need you to ${missing[0]}`;
|
||||
} else if (missing.length > 1) {
|
||||
hint = `I still need you to ${missing.slice(0, -1).join(", ")} and ${missing.at(-1)}`;
|
||||
}
|
||||
|
||||
return {
|
||||
done:
|
||||
progress.firstCheckpoint &&
|
||||
progress.secondCheckpoint &&
|
||||
progress.codeRanGood,
|
||||
progressArray: Object.values(progress),
|
||||
hint,
|
||||
};
|
||||
};
|
||||
})()
|
||||
|
||||
|
||||
|
||||
|
||||
},
|
||||
{
|
||||
id: 'lesson10',
|
||||
|
|
@ -1022,5 +951,147 @@ print("Done!")
|
|||
|
||||
|
||||
|
||||
},
|
||||
|
||||
{
|
||||
id: 'robot1',
|
||||
title: '1. Moving the Robot',
|
||||
tabtitle: 'Importing Modules',
|
||||
level: 'robot',
|
||||
map: 'Level 1',
|
||||
content: `
|
||||
<p>This robot simulation is a simplified version of a real robot.</p>
|
||||
<p>It has a single library which you can use to access all its controls and sensors.</p>
|
||||
<p>In a real robot you would have many different libraries for different parts, sensors, and even microcontroller functions.</p>
|
||||
</br>
|
||||
<p>We'll start by importing the robot library, and using it to move.</p>
|
||||
<pre><code>
|
||||
import robot # Import the robot library
|
||||
import time # Import the time module
|
||||
|
||||
robot.move(1) # Move forward at max speed
|
||||
time.sleep(2) # Wait for 2 seconds
|
||||
robot.move(-1) # Move backward at max speed
|
||||
time.sleep(2) # Wait for 2 seconds
|
||||
robot.move(0) # Stop the robot
|
||||
</code></pre>
|
||||
`,
|
||||
objectives: [
|
||||
"Reach the first checkpoint",
|
||||
"Reach the second checkpoint",
|
||||
|
||||
"Code should complete without errors"
|
||||
],
|
||||
|
||||
doneCondition: (() => {
|
||||
return ({ code, consoleText, codeRanGood, gameWorld }) => {
|
||||
const progress = {
|
||||
firstCheckpoint: gameWorld.waypointsReached[0],
|
||||
secondCheckpoint: gameWorld.waypointsReached[1],
|
||||
codeRanGood: codeRanGood,
|
||||
|
||||
};
|
||||
|
||||
if (!codeRanGood) {
|
||||
return { done: false, hint: "" };
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 5. Build hint
|
||||
const missing = [];
|
||||
if (!progress.firstCheckpoint) missing.push("reach the first checkpoint");
|
||||
if (!progress.secondCheckpoint) missing.push("reach the second checkpoint");
|
||||
|
||||
let hint = "";
|
||||
if (missing.length === 1) {
|
||||
hint = `I still need you to ${missing[0]}`;
|
||||
} else if (missing.length > 1) {
|
||||
hint = `I still need you to ${missing.slice(0, -1).join(", ")} and ${missing.at(-1)}`;
|
||||
}
|
||||
|
||||
return {
|
||||
done:
|
||||
progress.firstCheckpoint &&
|
||||
progress.secondCheckpoint &&
|
||||
progress.codeRanGood,
|
||||
progressArray: Object.values(progress),
|
||||
hint,
|
||||
};
|
||||
};
|
||||
})()
|
||||
},
|
||||
{
|
||||
id: 'robot1',
|
||||
title: '2. Steering the Robot',
|
||||
tabtitle: 'Importing Modules',
|
||||
level: 'robot',
|
||||
map: 'Level 2',
|
||||
content: `
|
||||
<p>Turning is very similar to moving, we use the <code>robot.turn(amount)</code> function.</p>
|
||||
<p>The <code>amount</code> parameter is a number between -1 and 1, where -1 is full left, 0 is no turn, and 1 is full right.</p>
|
||||
|
||||
<pre><code>
|
||||
import robot
|
||||
import time
|
||||
|
||||
robot.turn(1)
|
||||
time.sleep(2)
|
||||
robot.turn(0)
|
||||
</code></pre>
|
||||
</br>
|
||||
<p>This code causes the robot to turn right at max speed for 2 seconds, then stop.</p>
|
||||
|
||||
<p>You'll need to combine moving, turning, and waiting to reach all the checkpoints.</p>
|
||||
<p><strong>Note:</strong> The values for move, turn, and sleep can all be decimal numbers (floats). ie <code>time.sleep(0.5)</code> or <code>robot.move(0.8)</code></p>
|
||||
`,
|
||||
objectives: [
|
||||
"Reach the first checkpoint",
|
||||
"Reach the second checkpoint",
|
||||
|
||||
"Code should complete without errors"
|
||||
],
|
||||
|
||||
doneCondition: (() => {
|
||||
return ({ code, consoleText, codeRanGood, gameWorld }) => {
|
||||
const progress = {
|
||||
firstCheckpoint: gameWorld.waypointsReached[0],
|
||||
secondCheckpoint: gameWorld.waypointsReached[1],
|
||||
codeRanGood: codeRanGood,
|
||||
|
||||
};
|
||||
|
||||
if (!codeRanGood) {
|
||||
return { done: false, hint: "" };
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 5. Build hint
|
||||
const missing = [];
|
||||
if (!progress.firstCheckpoint) missing.push("reach the first checkpoint");
|
||||
if (!progress.secondCheckpoint) missing.push("reach the second checkpoint");
|
||||
|
||||
let hint = "";
|
||||
if (missing.length === 1) {
|
||||
hint = `I still need you to ${missing[0]}`;
|
||||
} else if (missing.length > 1) {
|
||||
hint = `I still need you to ${missing.slice(0, -1).join(", ")} and ${missing.at(-1)}`;
|
||||
}
|
||||
|
||||
return {
|
||||
done:
|
||||
progress.firstCheckpoint &&
|
||||
progress.secondCheckpoint &&
|
||||
progress.codeRanGood,
|
||||
progressArray: Object.values(progress),
|
||||
hint,
|
||||
};
|
||||
};
|
||||
})()
|
||||
|
||||
|
||||
|
||||
|
||||
},
|
||||
];
|
||||
|
|
|
|||
|
|
@ -209,7 +209,33 @@
|
|||
"waypoints": [
|
||||
{
|
||||
"position": {
|
||||
"x": 220,
|
||||
"x": 420,
|
||||
"y": 200
|
||||
},
|
||||
"vertices": [
|
||||
{
|
||||
"x": -50,
|
||||
"y": -50
|
||||
},
|
||||
{
|
||||
"x": 50,
|
||||
"y": -50
|
||||
},
|
||||
{
|
||||
"x": 50,
|
||||
"y": 50
|
||||
},
|
||||
{
|
||||
"x": -50,
|
||||
"y": 50
|
||||
}
|
||||
],
|
||||
"strokeColor": "#0000FF",
|
||||
"fillColor": "#0000CC"
|
||||
},
|
||||
{
|
||||
"position": {
|
||||
"x": 420,
|
||||
"y": 500
|
||||
},
|
||||
"vertices": [
|
||||
|
|
@ -230,7 +256,7 @@
|
|||
"y": 50
|
||||
}
|
||||
],
|
||||
"strokeColor": "#000099",
|
||||
"strokeColor": "#0000FF",
|
||||
"fillColor": "#0000CC"
|
||||
}
|
||||
],
|
||||
|
|
@ -255,8 +281,8 @@
|
|||
}
|
||||
],
|
||||
"position": {
|
||||
"x": 200,
|
||||
"y": 400
|
||||
"x": 800,
|
||||
"y": 300
|
||||
},
|
||||
"strokeColor": "#999999",
|
||||
"fillColor": "#CCCCCC"
|
||||
|
|
|
|||
26
game.js
26
game.js
|
|
@ -23,6 +23,21 @@ function showLesson(index) {
|
|||
loadLessonContent(lesson);
|
||||
updateTabs(lessons, index);
|
||||
|
||||
if (lesson.map) {
|
||||
console.log("Loading map for lesson:", lesson.map);
|
||||
document.getElementById('gameCanvas').style.display = 'block';
|
||||
for (let i = 0; i < gameWorld.levelData.length; i++) {
|
||||
if (gameWorld.levelData[i].name === lesson.map) {
|
||||
gameWorld.currentLevel = i;
|
||||
console.log("Setting current level to:", i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
resetGameWorld();
|
||||
} else {
|
||||
document.getElementById('gameCanvas').style.display = 'none';
|
||||
}
|
||||
|
||||
document.getElementById('prev-lesson').disabled = index === 0;
|
||||
document.getElementById('next-lesson').disabled = index === lessons.length - 1;
|
||||
|
||||
|
|
@ -95,6 +110,7 @@ function checkLessonDone() {
|
|||
codeRanGood: codeRanGood,
|
||||
gameWorld: gameWorld
|
||||
});
|
||||
console.log(gameWorld.waypointsReached);
|
||||
if (result.done) {
|
||||
markLessonDone(lesson.id);
|
||||
}
|
||||
|
|
@ -221,7 +237,7 @@ function toggleObjective(index, completed = true) {
|
|||
}
|
||||
|
||||
//clearLessonProgress(); // Clear progress on load for testing
|
||||
showLesson(9);
|
||||
|
||||
|
||||
const consoleElement = document.getElementById("console");
|
||||
const gameCanvas = document.getElementById("gameCanvas");
|
||||
|
|
@ -385,6 +401,8 @@ function resetGame() {
|
|||
document.getElementById("pause-button").innerText = "Pause";
|
||||
|
||||
logToConsole("Welcome to the game! Type your Python code in the editor and click 'Compile' to execute it.");
|
||||
|
||||
document.getElementById('compile-button').disabled = false;
|
||||
}
|
||||
|
||||
const targetFPS = 30;
|
||||
|
|
@ -408,7 +426,7 @@ function gameLoop(timestamp) {
|
|||
|
||||
gameWorld.update();
|
||||
gameWorld.draw(ctx);
|
||||
|
||||
console.log(gameWorld.waypointsReached);
|
||||
// if (gameWorld.checkPlayerCompletedTask()) {
|
||||
// logToConsole("✅ Task Completed! ✅");
|
||||
// togglePause();
|
||||
|
|
@ -557,16 +575,18 @@ function setupCanvas() {
|
|||
}
|
||||
|
||||
// Call this function when the page loads
|
||||
|
||||
fetch('/data/levels.json')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
gameWorld.levelData = data;
|
||||
|
||||
setupCanvas();
|
||||
|
||||
resetGame(); // Initialize the game and robots
|
||||
// Start game loop
|
||||
gameLoop();
|
||||
|
||||
showLesson(11);
|
||||
});
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ async function initializePyodide() {
|
|||
sensorData = sensorArray.map(sensor => ({
|
||||
"type": sensor.type, // Individual sensor's type
|
||||
"angle": sensor.angle, // Individual sensor's angle
|
||||
"distance": sensor.distance, // Individual sensor's distance
|
||||
"distance": Math.round(sensor.distance * 100) / 100, // Individual sensor's distance
|
||||
"hitpoint": sensor.hitpoint // Whatever other attributes you need
|
||||
}));
|
||||
//console.log(sensorData["x"]);
|
||||
|
|
@ -69,6 +69,12 @@ class RobotModule:
|
|||
sensor_data = json.loads(get_sensor_data(name))
|
||||
return sensor_data
|
||||
|
||||
def get_distance_left(self):
|
||||
return self.get_sensors()[0]["distance"]
|
||||
|
||||
def get_distance_right(self):
|
||||
return self.get_sensors()[1]["distance"]
|
||||
|
||||
def get_sensors(self):
|
||||
return json.loads(get_sensor_data("sensors")) # Returns list of sensor dicts
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue