added dropdown for lessons, and added first robot lesson to second dropdown
parent
4c970c8faf
commit
9b702b6ff1
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"liveServer.settings.port": 5501
|
||||||
|
}
|
||||||
201
data/lessons.js
201
data/lessons.js
|
|
@ -2,7 +2,8 @@ export const lessons = [
|
||||||
{
|
{
|
||||||
id: 'lesson1',
|
id: 'lesson1',
|
||||||
title: '1. Introduction to Python',
|
title: '1. Introduction to Python',
|
||||||
difficulty: 'easy',
|
tabtitle: 'Hello World',
|
||||||
|
level: 'basics',
|
||||||
content: `
|
content: `
|
||||||
<p>Let's learn some Python..</p>
|
<p>Let's learn some Python..</p>
|
||||||
<p>We'll start with what's called a "Hello World" program to make sure everythings working.</p>
|
<p>We'll start with what's called a "Hello World" program to make sure everythings working.</p>
|
||||||
|
|
@ -49,8 +50,9 @@ export const lessons = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'lesson2',
|
id: 'lesson2',
|
||||||
title: 'Data Types and Variables',
|
title: '2. Data Types',
|
||||||
difficulty: 'easy',
|
tabtitle: 'Data Types',
|
||||||
|
level: 'basics',
|
||||||
content: `
|
content: `
|
||||||
<p>Did you try typeing <code>print(Hello World)</code>, without the quotation marks?</p>
|
<p>Did you try typeing <code>print(Hello World)</code>, without the quotation marks?</p>
|
||||||
<p>If you didn't, give it a try now.</p>
|
<p>If you didn't, give it a try now.</p>
|
||||||
|
|
@ -75,21 +77,19 @@ export const lessons = [
|
||||||
"Print a boolean (True/False) to the console"
|
"Print a boolean (True/False) to the console"
|
||||||
],
|
],
|
||||||
doneCondition: (() => {
|
doneCondition: (() => {
|
||||||
|
// Matches print("something") or print('something')
|
||||||
|
const stringPrintRegex = /print\s*\(\s*(['"]).*?\1\s*\)/;
|
||||||
|
|
||||||
|
// Matches print of float literal like 3.14, .5, -2.0, 1e-3
|
||||||
|
const floatPrintRegex = /print\s*\(\s*[-+]?(?:\d+\.\d*|\.\d+|\d+[eE][-+]?\d+)\s*\)/;
|
||||||
|
|
||||||
const stringRegex = /print\s*\(\s*(['"]).*?\1\s*\)/;
|
// Matches print of int literal like 3, -42 (excluding floats)
|
||||||
const intRegex = /(?<![\d.])[-+]?\d+(?![\d.])/;
|
const intPrintRegex = /print\s*\(\s*[-+]?\d+\s*\)/;
|
||||||
const floatRegex = /[-+]?(?:\d+\.\d*|\.\d+)(?:[eE][-+]?\d+)?/;
|
|
||||||
const boolRegex = /\b(True|False|true|false)\b/;
|
|
||||||
|
|
||||||
return ({ code, consoleText, codeRanGood }) => {
|
// Matches print(True) or print(False), case-insensitive
|
||||||
const progress = {
|
const boolPrintRegex = /print\s*\(\s*(True|False)\s*\)/i;
|
||||||
stringDone: false,
|
|
||||||
intDone: false,
|
return ({ code, codeRanGood }) => {
|
||||||
floatDone: false,
|
|
||||||
boolDone: false,
|
|
||||||
// syntaxErrorDone: false, // optional
|
|
||||||
};
|
|
||||||
if (!codeRanGood) {
|
if (!codeRanGood) {
|
||||||
return {
|
return {
|
||||||
done: false,
|
done: false,
|
||||||
|
|
@ -97,17 +97,26 @@ export const lessons = [
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!progress.stringDone && stringRegex.test(code)) {
|
const progress = {
|
||||||
progress.stringDone = true;
|
stringDone: stringPrintRegex.test(code),
|
||||||
|
intDone: intPrintRegex.test(code),
|
||||||
|
floatDone: floatPrintRegex.test(code),
|
||||||
|
boolDone: boolPrintRegex.test(code),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Fix false positives where float also matches int
|
||||||
|
if (progress.floatDone && progress.intDone) {
|
||||||
|
// Check if the float number has a dot or exponent; if not, it was a false int match
|
||||||
|
const matches = code.match(floatPrintRegex);
|
||||||
|
if (matches) {
|
||||||
|
const numbers = matches.map(m => m.match(/[-+]?(?:\d+\.\d*|\.\d+|\d+[eE][-+]?\d+)/)?.[0]);
|
||||||
|
for (const num of numbers) {
|
||||||
|
if (num && !num.includes('.') && !num.toLowerCase().includes('e')) {
|
||||||
|
// It's not a float, remove the float flag
|
||||||
|
progress.floatDone = false;
|
||||||
}
|
}
|
||||||
if (!progress.floatDone && floatRegex.test(consoleText)) {
|
|
||||||
progress.floatDone = true;
|
|
||||||
}
|
}
|
||||||
if (!progress.intDone && intRegex.test(consoleText)) {
|
|
||||||
progress.intDone = true;
|
|
||||||
}
|
}
|
||||||
if (!progress.boolDone && boolRegex.test(consoleText)) {
|
|
||||||
progress.boolDone = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const missing = [];
|
const missing = [];
|
||||||
|
|
@ -116,16 +125,16 @@ export const lessons = [
|
||||||
if (!progress.intDone) missing.push("int");
|
if (!progress.intDone) missing.push("int");
|
||||||
if (!progress.boolDone) missing.push("boolean");
|
if (!progress.boolDone) missing.push("boolean");
|
||||||
|
|
||||||
let hint = "I still need you to print a ";
|
let hint = "";
|
||||||
if (missing.length === 0) {
|
if (missing.length > 0) {
|
||||||
hint = "";
|
hint = "I still need you to use print() with a ";
|
||||||
} else if (missing.length === 1) {
|
hint += missing.length === 1
|
||||||
hint += missing[0];
|
? missing[0]
|
||||||
} else {
|
: missing.slice(0, -1).join(", ") + " and " + missing[missing.length - 1];
|
||||||
hint += missing.slice(0, -1).join(", ") + " and " + missing[missing.length - 1];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const done = progress.stringDone && progress.intDone && progress.floatDone && progress.boolDone;
|
const done = progress.stringDone && progress.intDone && progress.floatDone && progress.boolDone;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
done,
|
done,
|
||||||
progressArray: Object.values(progress),
|
progressArray: Object.values(progress),
|
||||||
|
|
@ -134,11 +143,16 @@ export const lessons = [
|
||||||
};
|
};
|
||||||
})()
|
})()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'lesson3',
|
id: 'lesson3',
|
||||||
title: '3. Arithmetic Operations',
|
title: '3. Arithmetic Operations',
|
||||||
difficulty: 'easy',
|
tabtitle: 'Arithmetic',
|
||||||
|
level: 'basics',
|
||||||
content: `
|
content: `
|
||||||
<p>Let's get python to do our math homework for us.</p>
|
<p>Let's get python to do our math homework for us.</p>
|
||||||
<p>Python can handle basic arithmetic operations like addition, subtraction, multiplication, and division.</p>
|
<p>Python can handle basic arithmetic operations like addition, subtraction, multiplication, and division.</p>
|
||||||
|
|
@ -224,7 +238,8 @@ export const lessons = [
|
||||||
{
|
{
|
||||||
id: 'lesson4',
|
id: 'lesson4',
|
||||||
title: '4. Variables',
|
title: '4. Variables',
|
||||||
difficulty: 'easy',
|
tabtitle: 'Variables',
|
||||||
|
level: 'basics',
|
||||||
content: `
|
content: `
|
||||||
<p>It's common for us to need to keep some data or objects over multiple lines, for that we use <strong>variables</strong>.</p>
|
<p>It's common for us to need to keep some data or objects over multiple lines, for that we use <strong>variables</strong>.</p>
|
||||||
<p>Think of a variable as a container that holds an object for us to use later.</p>
|
<p>Think of a variable as a container that holds an object for us to use later.</p>
|
||||||
|
|
@ -303,7 +318,8 @@ export const lessons = [
|
||||||
{
|
{
|
||||||
id: 'lesson5',
|
id: 'lesson5',
|
||||||
title: '5. More on Variables',
|
title: '5. More on Variables',
|
||||||
difficulty: 'easy',
|
tabtitle: 'Changing Variables',
|
||||||
|
level: 'basics',
|
||||||
content: `
|
content: `
|
||||||
<p>A variable will act like any other object of its data type.</p>
|
<p>A variable will act like any other object of its data type.</p>
|
||||||
<p>For example, if we have a variable called <code>bob</code> with the value <code>32</code>, we can do math with it:</p>
|
<p>For example, if we have a variable called <code>bob</code> with the value <code>32</code>, we can do math with it:</p>
|
||||||
|
|
@ -322,7 +338,7 @@ print(bob)
|
||||||
objectives: [
|
objectives: [
|
||||||
"Initialize a variable with a value",
|
"Initialize a variable with a value",
|
||||||
"Print the variable using print()",
|
"Print the variable using print()",
|
||||||
"Alter the value of the variable",
|
"Alter the value of the variable by adding, subtracting, multiplying, or dividing by itself",
|
||||||
"Print the variable again using print()",
|
"Print the variable again using print()",
|
||||||
],
|
],
|
||||||
doneCondition: (() => {
|
doneCondition: (() => {
|
||||||
|
|
@ -428,7 +444,8 @@ print(bob)
|
||||||
{
|
{
|
||||||
id: 'lesson6',
|
id: 'lesson6',
|
||||||
title: '6. Conditionals',
|
title: '6. Conditionals',
|
||||||
difficulty: 'easy',
|
tabtitle: 'if',
|
||||||
|
level: 'basics',
|
||||||
content: `
|
content: `
|
||||||
<p>Sometimes we want don't want part of our code to run, or we want it to run differently in different situations. That's when we use <strong>conditionals</strong>.</p>
|
<p>Sometimes we want don't want part of our code to run, or we want it to run differently in different situations. That's when we use <strong>conditionals</strong>.</p>
|
||||||
<pre><code>
|
<pre><code>
|
||||||
|
|
@ -512,7 +529,8 @@ if num == 10:
|
||||||
{
|
{
|
||||||
id: 'lesson7',
|
id: 'lesson7',
|
||||||
title: '7. More Conditionals',
|
title: '7. More Conditionals',
|
||||||
difficulty: 'easy',
|
tabtitle: 'if/elif/else',
|
||||||
|
level: 'basics',
|
||||||
content: `
|
content: `
|
||||||
<p>Sometimes we'll want to run one thing OR another, rather than just running something or not. To do this we chain our if statements together with <code>elif</code> and <code>else</code>.</p>
|
<p>Sometimes we'll want to run one thing OR another, rather than just running something or not. To do this we chain our if statements together with <code>elif</code> and <code>else</code>.</p>
|
||||||
<pre><code>
|
<pre><code>
|
||||||
|
|
@ -624,7 +642,8 @@ else:
|
||||||
{
|
{
|
||||||
id: 'lesson8',
|
id: 'lesson8',
|
||||||
title: '8. While Loops',
|
title: '8. While Loops',
|
||||||
difficulty: 'easy',
|
tabtitle: 'While Loops',
|
||||||
|
level: 'basics',
|
||||||
content: `
|
content: `
|
||||||
<p>Most of the time we'll want our code to run multiple times, or even endlessly, in that case we need <strong>loops</strong>.</p>
|
<p>Most of the time we'll want our code to run multiple times, or even endlessly, in that case we need <strong>loops</strong>.</p>
|
||||||
<p>Python has two main types of loops: <code>for</code> loops and <code>while</code> loops.</p>
|
<p>Python has two main types of loops: <code>for</code> loops and <code>while</code> loops.</p>
|
||||||
|
|
@ -632,7 +651,7 @@ else:
|
||||||
<pre><code>
|
<pre><code>
|
||||||
count = 0
|
count = 0
|
||||||
while count < 5:
|
while count < 5:
|
||||||
print("Count is:", count)
|
print(count)
|
||||||
count = count + 1
|
count = count + 1
|
||||||
print("Done!")
|
print("Done!")
|
||||||
</code></pre>
|
</code></pre>
|
||||||
|
|
@ -742,7 +761,8 @@ print("Done!")
|
||||||
{
|
{
|
||||||
id: 'lesson9',
|
id: 'lesson9',
|
||||||
title: '8. For Loops',
|
title: '8. For Loops',
|
||||||
difficulty: 'easy',
|
tabtitle: 'For Loops',
|
||||||
|
level: 'basics',
|
||||||
content: `
|
content: `
|
||||||
<p>A more common type of loop is the <strong>for</strong> loop.</p>
|
<p>A more common type of loop is the <strong>for</strong> loop.</p>
|
||||||
<p>A <code>for</code> loop includes the creation of a variable that will be used to count how many times it should run.</p>
|
<p>A <code>for</code> loop includes the creation of a variable that will be used to count how many times it should run.</p>
|
||||||
|
|
@ -828,11 +848,112 @@ 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: [
|
||||||
|
"Import the time module",
|
||||||
|
"Print something",
|
||||||
|
"Use time.sleep() to pause for an amount of time",
|
||||||
|
"Print something else after the pause"
|
||||||
|
],
|
||||||
|
|
||||||
|
doneCondition: (() => {
|
||||||
|
return ({ code, consoleText, codeRanGood }) => {
|
||||||
|
const progress = {
|
||||||
|
importedTime: false,
|
||||||
|
printedBefore: false,
|
||||||
|
usedSleep: false,
|
||||||
|
printedAfter: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!codeRanGood) {
|
||||||
|
return { done: false, hint: "" };
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. Check for "import time"
|
||||||
|
const importRegex = /^\s*import\s+time\b/m;
|
||||||
|
progress.importedTime = importRegex.test(code);
|
||||||
|
|
||||||
|
// 2. Match all print(...) calls
|
||||||
|
const printRegex = /print\s*\(.*?\)/g;
|
||||||
|
const printMatches = [...code.matchAll(printRegex)];
|
||||||
|
|
||||||
|
// 3. Match time.sleep(...)
|
||||||
|
const sleepRegex = /time\.sleep\s*\(\s*[\d.]+\s*\)/;
|
||||||
|
const sleepMatch = sleepRegex.exec(code);
|
||||||
|
progress.usedSleep = !!sleepMatch;
|
||||||
|
|
||||||
|
// 4. Handle print position logic
|
||||||
|
if (printMatches.length > 0) {
|
||||||
|
// If there's no sleep, we just say "they printed something" — early lesson support
|
||||||
|
if (!sleepMatch) {
|
||||||
|
progress.printedBefore = true; // consider *any* print valid before sleep
|
||||||
|
} else {
|
||||||
|
const sleepIndex = sleepMatch.index;
|
||||||
|
for (const m of printMatches) {
|
||||||
|
if (m.index < sleepIndex) progress.printedBefore = true;
|
||||||
|
if (m.index > sleepIndex) progress.printedAfter = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. Build hint
|
||||||
|
const missing = [];
|
||||||
|
if (!progress.importedTime) missing.push("import the time module");
|
||||||
|
if (!progress.printedBefore) missing.push("print something before sleeping");
|
||||||
|
if (!progress.usedSleep) missing.push("use time.sleep()");
|
||||||
|
if (!progress.printedAfter && progress.usedSleep)
|
||||||
|
missing.push("print something after sleeping");
|
||||||
|
|
||||||
|
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.importedTime &&
|
||||||
|
progress.printedBefore &&
|
||||||
|
progress.usedSleep &&
|
||||||
|
progress.printedAfter,
|
||||||
|
progressArray: Object.values(progress),
|
||||||
|
hint,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
})()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'lesson10',
|
id: 'lesson10',
|
||||||
title: '9. Libraries & Modules (time)',
|
title: '9. Libraries & Modules (time)',
|
||||||
difficulty: 'easy',
|
tabtitle: 'Importing Modules',
|
||||||
|
level: 'basics',
|
||||||
content: `
|
content: `
|
||||||
<p>A lot of the time we need more code than can fit in a single file, or we want to reuse our own, or someone elses code.</p>
|
<p>A lot of the time we need more code than can fit in a single file, or we want to reuse our own, or someone elses code.</p>
|
||||||
<p>For that we use <strong>libraries</strong> and <strong>modules</strong>.</p>
|
<p>For that we use <strong>libraries</strong> and <strong>modules</strong>.</p>
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@
|
||||||
{
|
{
|
||||||
"position": {
|
"position": {
|
||||||
"x": 420,
|
"x": 420,
|
||||||
"y": 200
|
"y": 600
|
||||||
},
|
},
|
||||||
"vertices": [
|
"vertices": [
|
||||||
{
|
{
|
||||||
|
|
@ -230,7 +230,7 @@
|
||||||
],
|
],
|
||||||
"position": {
|
"position": {
|
||||||
"x": 200,
|
"x": 200,
|
||||||
"y": 300
|
"y": 400
|
||||||
},
|
},
|
||||||
"strokeColor": "#999999",
|
"strokeColor": "#999999",
|
||||||
"fillColor": "#CCCCCC"
|
"fillColor": "#CCCCCC"
|
||||||
|
|
|
||||||
40
game.js
40
game.js
|
|
@ -21,6 +21,7 @@ function showLesson(index) {
|
||||||
|
|
||||||
const lesson = lessons[index];
|
const lesson = lessons[index];
|
||||||
loadLessonContent(lesson);
|
loadLessonContent(lesson);
|
||||||
|
updateTabs(lessons, index);
|
||||||
|
|
||||||
document.getElementById('prev-lesson').disabled = index === 0;
|
document.getElementById('prev-lesson').disabled = index === 0;
|
||||||
document.getElementById('next-lesson').disabled = index === lessons.length - 1;
|
document.getElementById('next-lesson').disabled = index === lessons.length - 1;
|
||||||
|
|
@ -35,6 +36,41 @@ function loadLessonContent(lesson) {
|
||||||
document.getElementById('lesson-content').innerHTML = lesson.content;
|
document.getElementById('lesson-content').innerHTML = lesson.content;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateTabs(lessons, currentIndex) {
|
||||||
|
let select = document.getElementById('lesson-select');
|
||||||
|
let select_robot = document.getElementById('robot-select');
|
||||||
|
select.innerHTML = ''; // Clear old options
|
||||||
|
select_robot.innerHTML = ''; // Clear old options
|
||||||
|
|
||||||
|
|
||||||
|
lessons.forEach((lesson, index) => {
|
||||||
|
const option = document.createElement('option');
|
||||||
|
option.value = index;
|
||||||
|
option.textContent = lesson.title || `Lesson ${index + 1}`;
|
||||||
|
if (lesson.level == "basics") {
|
||||||
|
select.appendChild(option);
|
||||||
|
} else {
|
||||||
|
select_robot.appendChild(option);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Set current selected lesson
|
||||||
|
select.value = currentIndex;
|
||||||
|
select_robot.value = currentIndex;
|
||||||
|
// Handle user selection
|
||||||
|
select.addEventListener('change', () => {
|
||||||
|
const selectedIndex = Number(select.value);
|
||||||
|
console.log("Selected lesson index:", selectedIndex);
|
||||||
|
showLesson(selectedIndex);
|
||||||
|
});
|
||||||
|
select_robot.addEventListener('change', () => {
|
||||||
|
const selectedIndex = Number(select_robot.value);
|
||||||
|
console.log("Selected lesson index:", selectedIndex);
|
||||||
|
showLesson(selectedIndex);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
document.getElementById('prev-lesson').addEventListener('click', () => {
|
document.getElementById('prev-lesson').addEventListener('click', () => {
|
||||||
showLesson(currentLesson - 1);
|
showLesson(currentLesson - 1);
|
||||||
});
|
});
|
||||||
|
|
@ -183,8 +219,8 @@ function toggleObjective(index, completed = true) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
clearLessonProgress(); // Clear progress on load for testing
|
//clearLessonProgress(); // Clear progress on load for testing
|
||||||
showLesson(9);
|
showLesson(1);
|
||||||
|
|
||||||
const consoleElement = document.getElementById("console");
|
const consoleElement = document.getElementById("console");
|
||||||
const gameCanvas = document.getElementById("gameCanvas");
|
const gameCanvas = document.getElementById("gameCanvas");
|
||||||
|
|
|
||||||
19
index.html
19
index.html
|
|
@ -25,8 +25,27 @@
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
|
||||||
<!-- New Lesson Box / Instructions Area -->
|
<!-- New Lesson Box / Instructions Area -->
|
||||||
<section id="lesson-box">
|
<section id="lesson-box">
|
||||||
|
|
||||||
|
<div class="dropdown-row content-width">
|
||||||
|
<div class="dropdown-group">
|
||||||
|
<div class="dropdown-title">Basics</div>
|
||||||
|
<select id="lesson-select"></select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="dropdown-group">
|
||||||
|
<div class="dropdown-title">Robot</div>
|
||||||
|
<select id="robot-select"></select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="dropdown-group">
|
||||||
|
<div class="dropdown-title">Challenges</div>
|
||||||
|
<select id="challenge-select"></select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="content-width" style="display: flex; gap: 20px; align-items: flex-start;">
|
<div class="content-width" style="display: flex; gap: 20px; align-items: flex-start;">
|
||||||
|
|
||||||
<!-- 📘 Lesson content area (3/4 width) -->
|
<!-- 📘 Lesson content area (3/4 width) -->
|
||||||
|
|
|
||||||
|
|
@ -98,12 +98,22 @@ class RobotModule:
|
||||||
return magnitude
|
return magnitude
|
||||||
|
|
||||||
def move(self, speed):
|
def move(self, speed):
|
||||||
|
if speed < -1:
|
||||||
|
speed = -1
|
||||||
|
elif speed > 1:
|
||||||
|
speed = 1
|
||||||
|
speed = speed/5000
|
||||||
send_to_main("move", speed)
|
send_to_main("move", speed)
|
||||||
|
|
||||||
def fire(self):
|
def fire(self):
|
||||||
send_to_main("fire", None)
|
send_to_main("fire", None)
|
||||||
|
|
||||||
def turn(self, deg):
|
def turn(self, deg):
|
||||||
|
if deg < -1:
|
||||||
|
deg = -1
|
||||||
|
elif deg > 1:
|
||||||
|
deg = 1
|
||||||
|
deg = deg/250
|
||||||
send_to_main("turn", deg)
|
send_to_main("turn", deg)
|
||||||
|
|
||||||
robot = RobotModule()
|
robot = RobotModule()
|
||||||
|
|
|
||||||
48
style.css
48
style.css
|
|
@ -9,7 +9,7 @@ html {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
font-family: Arial, sans-serif;
|
font-family: Arial, sans-serif;
|
||||||
background-color: #fff;
|
background-color: #ffffff;
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
@ -40,7 +40,7 @@ html {
|
||||||
|
|
||||||
/* ===== Header (top bar) ===== */
|
/* ===== Header (top bar) ===== */
|
||||||
header {
|
header {
|
||||||
background-color: #f3f4f6;
|
background-color: #ffffff;
|
||||||
/* light gray */
|
/* light gray */
|
||||||
padding: 16px 0;
|
padding: 16px 0;
|
||||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||||
|
|
@ -193,6 +193,48 @@ main {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.dropdown-row {
|
||||||
|
display: flex;
|
||||||
|
gap: 20px;
|
||||||
|
padding: 12px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-group {
|
||||||
|
flex: 1; /* Equal horizontal space */
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-title {
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-group select {
|
||||||
|
width: 100%;
|
||||||
|
padding: 4px 8px;
|
||||||
|
font-size: 14px;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid #aaa;
|
||||||
|
background-color: white;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#lesson-box {
|
||||||
|
background: white;
|
||||||
|
border: 1px solid #ffffff;
|
||||||
|
border-top: none;
|
||||||
|
border-radius: 0 0 8px 8px;
|
||||||
|
padding-top: 0; /* prevents double padding below tabs */
|
||||||
|
margin-top: -1px; /* overlap the active tab */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Monaco editor - left half */
|
/* Monaco editor - left half */
|
||||||
#monaco-editor {
|
#monaco-editor {
|
||||||
|
|
@ -289,7 +331,7 @@ main {
|
||||||
}
|
}
|
||||||
|
|
||||||
#lesson-box {
|
#lesson-box {
|
||||||
background: #f9f9f9;
|
background: #ffffff;
|
||||||
padding: 8px 16px;
|
padding: 8px 16px;
|
||||||
border-bottom: 1px solid #ddd;
|
border-bottom: 1px solid #ddd;
|
||||||
font-family: sans-serif;
|
font-family: sans-serif;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue