Compare commits

..

No commits in common. "2986d89fc5a622ad300f48268549e79dd17ce2f6" and "ba1adee770c39ff9072331e0a70175b2b477211f" have entirely different histories.

9 changed files with 33 additions and 183 deletions

11
game.js
View File

@ -525,12 +525,6 @@ gameCanvas.addEventListener("wheel", (event) => {
offsetY = mouseY - worldY * scale; offsetY = mouseY - worldY * scale;
}); });
const canvas = document.getElementById("gameCanvas");
window.addEventListener("resize", () => {
// console.log("RESIZE");
// gameWorld.resizeCanvas(canvas)
});
gameCanvas.addEventListener("mousedown", (event) => { gameCanvas.addEventListener("mousedown", (event) => {
isPanning = true; isPanning = true;
@ -580,7 +574,6 @@ function setupCanvas() {
const canvas = document.getElementById("gameCanvas"); const canvas = document.getElementById("gameCanvas");
const context = canvas.getContext("2d"); const context = canvas.getContext("2d");
// Get the device pixel ratio // Get the device pixel ratio
const dpr = window.devicePixelRatio || 1; const dpr = window.devicePixelRatio || 1;
@ -597,7 +590,7 @@ function setupCanvas() {
} }
// Call this function when the page loads // Call this function when the page loads
fetch('./data/levels.json') fetch('/data/levels.json')
.then(response => response.json()) .then(response => response.json())
.then(data => { .then(data => {
gameWorld.levelData = data; gameWorld.levelData = data;
@ -608,7 +601,7 @@ fetch('./data/levels.json')
// Start game loop // Start game loop
gameLoop(); gameLoop();
showLesson(10); showLesson(0);
}); });

View File

@ -19,21 +19,6 @@ export class GameWorld {
this.obstacles = []; this.obstacles = [];
this.robots = []; this.robots = [];
this.lineColor = `rgba(0,0,0,1)`;
this.floorLines = [
{ x: 170, y: 75 },
{ x: 300, y: 75 },
{ x: 500, y: 300 },
{ x: 500, y: 400 },
{ x: 450, y: 450 },
{ x: 400, y: 450 },
{ x: 100, y: 350 },
{ x: 50, y: 250 },
{ x: 80, y: 150 },
{ x: 170, y: 75 },
// add more points as needed
];
this.lineWidth = 3;
@ -203,7 +188,7 @@ export class GameWorld {
cy /= (6 * area); cy /= (6 * area);
return { x: cx, y: cy }; return { x: cx, y: cy };
} }
addObstacle(vertices, strokeColor = "black", fillColor = "gray") { addObstacle(vertices, strokeColor = "black", fillColor = "gray") {
// Sort vertices clockwise (Matter requires this) // Sort vertices clockwise (Matter requires this)
@ -262,24 +247,15 @@ export class GameWorld {
// Return the floor color based on (x, y) coordinates // Return the floor color based on (x, y) coordinates
getFloorColor(x, y) { getFloorColor(x, y) {
const isUnderLine = this.isPointNearLine(x, y, this.floorLines, this.lineWidth); return (x + y) % 50 < 25 ? "black" : "white"; // Example pattern
return isUnderLine;
// if (isUnderLine) {
// console.log("Robot is over a floor line");
// }
} }
// Draw the game world (e.g., obstacles, background) // Draw the game world (e.g., obstacles, background)
draw(ctx) { draw(ctx) {
//this.render(ctx);
this.drawFloorLines(ctx);
// Draw obstacles // Draw obstacles
ctx.strokeStyle = "gray"; // Obstacle outline color ctx.strokeStyle = "gray"; // Obstacle outline color
ctx.lineWidth = 2; // Optional: to make the outline thicker ctx.lineWidth = 2; // Optional: to make the outline thicker
// Draw obstacles // Draw obstacles
this.obstacles.forEach(body => { this.obstacles.forEach(body => {
ctx.beginPath(); ctx.beginPath();
@ -337,45 +313,8 @@ export class GameWorld {
robot.draw(ctx); // Draw the robot's hull and sensors robot.draw(ctx); // Draw the robot's hull and sensors
}); });
} }
drawFloorLines(ctx) {
if (this.floorLines.length < 2) return;
ctx.strokeStyle = this.lineColor;
ctx.lineWidth = this.lineWidth;
ctx.beginPath();
ctx.moveTo(this.floorLines[0].x, this.floorLines[0].y);
for (let i = 1; i < this.floorLines.length; i++) {
ctx.lineTo(this.floorLines[i].x, this.floorLines[i].y);
}
ctx.stroke();
}
resizeCanvasToDisplaySize(ctx) {
let canvas = ctx.canvas;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
if (canvas.width !== width || canvas.height !== height) {
canvas.width = width;
canvas.height = height;
}
}
render(ctx) {
let canvas = ctx.canvas;
this.resizeCanvasToDisplaySize(canvas); // 👈 ensures drawing resolution matches display size
ctx.clearRect(0, 0, canvas.width, canvas.height);
game.draw(ctx);
requestAnimationFrame(render);
}
rayCast(startX, startY, endX, endY, ignoreBodies = []) { rayCast(startX, startY, endX, endY, ignoreBodies = []) {
let closestIntersection = null; let closestIntersection = null;
let startPoint = { x: startX, y: startY }; let startPoint = { x: startX, y: startY };
@ -450,45 +389,8 @@ export class GameWorld {
return null; // No valid intersection return null; // No valid intersection
} }
isPointNearLine(x, y, linePoints, width) {
const threshold = width / 2;
for (let i = 0; i < linePoints.length - 1; i++) {
const p1 = linePoints[i];
const p2 = linePoints[i + 1];
const dist = this.pointToSegmentDistance(x, y, p1.x, p1.y, p2.x, p2.y);
if (dist <= threshold) {
return true;
}
}
return false;
}
pointToSegmentDistance(px, py, x1, y1, x2, y2) {
const dx = x2 - x1;
const dy = y2 - y1;
const lengthSquared = dx * dx + dy * dy;
if (lengthSquared === 0) {
// p1 == p2
const dxp = px - x1;
const dyp = py - y1;
return Math.sqrt(dxp * dxp + dyp * dyp);
}
// Project point onto the segment
let t = ((px - x1) * dx + (py - y1) * dy) / lengthSquared;
t = Math.max(0, Math.min(1, t)); // clamp to segment
const projX = x1 + t * dx;
const projY = y1 + t * dy;
const dxp = px - projX;
const dyp = py - projY;
return Math.sqrt(dxp * dxp + dyp * dyp);
}
} }

View File

@ -17,12 +17,12 @@
<body> <body>
<!-- Top Bar with Heading and Buttons --> <!-- Top Bar with Heading and Buttons -->
<header> <header>
<!-- <div class="header-inner"> <div class="header-inner">
<div> <div>
<h1>Learn CircuitPython</h1> <h1>Learn CircuitPython</h1>
<p>Write code, simulate physics, and see instant output</p> <p>Write code, simulate physics, and see instant output</p>
</div> </div>
</div> --> </div>
</header> </header>

View File

@ -32,10 +32,8 @@ async function initializePyodide() {
"type": sensor.type, // Individual sensor's type "type": sensor.type, // Individual sensor's type
"angle": sensor.angle, // Individual sensor's angle "angle": sensor.angle, // Individual sensor's angle
"distance": Math.round(sensor.distance * 100) / 100, // Individual sensor's distance "distance": Math.round(sensor.distance * 100) / 100, // Individual sensor's distance
"hitpoint": sensor.hitpoint, // Whatever other attributes you need "hitpoint": sensor.hitpoint // Whatever other attributes you need
"canSeeLine": sensor.hasLine
})); }));
//console.log(robot.sensors);
//console.log(sensorData["x"]); //console.log(sensorData["x"]);
sensorData = JSON.stringify(sensorData); sensorData = JSON.stringify(sensorData);
return sensorData; return sensorData;
@ -77,12 +75,6 @@ class RobotModule:
def get_distance_right(self): def get_distance_right(self):
return self.get_sensors()[1]["distance"] return self.get_sensors()[1]["distance"]
def get_line_left(self):
return self.get_sensors()[2]["canSeeLine"]
def get_line_right(self):
return self.get_sensors()[3]["canSeeLine"]
def get_sensors(self): def get_sensors(self):
return json.loads(get_sensor_data("sensors")) # Returns list of sensor dicts return json.loads(get_sensor_data("sensors")) # Returns list of sensor dicts

View File

@ -80,20 +80,3 @@ while True:
robot.move(1) robot.move(1)
robot.turn(0) robot.turn(0)
time.sleep(0.01) time.sleep(0.01)
# FOLLOW LINE
import robot
import time
while True:
robot.move(0.1)
if robot.get_line_left():
robot.turn(-1)
elif robot.get_line_right():
robot.turn(1)
else:
robot.turn(0)
time.sleep(0.05)

View File

@ -24,8 +24,7 @@ export class Robot {
this.addSensor(new RaycastSensor(this, -40, 12, -45, 60)); this.addSensor(new RaycastSensor(this, -40, 12, -45, 60));
this.addSensor(new RaycastSensor(this, 40, 12, 45, 60)); this.addSensor(new RaycastSensor(this, 40, 12, 45, 60));
this.addSensor(new FloorColorSensor(this, -20, 12)); this.addSensor(new FloorColorSensor(this, 0, 0));
this.addSensor(new FloorColorSensor(this, 20, 12));
} }
@ -44,6 +43,10 @@ export class Robot {
this.update_sensors(gameWorld); this.update_sensors(gameWorld);
} }
draw(ctx){
this.draw(ctx);
}
update_sensors(gameWorld) { update_sensors(gameWorld) {
this.sensors.forEach(sensor => sensor.read(this, gameWorld)); this.sensors.forEach(sensor => sensor.read(this, gameWorld));
} }

View File

@ -9,7 +9,7 @@ export class Sensor {
this.hitY = null; this.hitY = null;
this.hitObject = null; this.hitObject = null;
this.angle = 0; this.angle = 0;
this.hitpoint = { x: null, y: null }; this.hitpoint = {x: null, y: null};
} }
@ -65,13 +65,13 @@ export class RaycastSensor extends Sensor {
this.hitY = hitPos.y; this.hitY = hitPos.y;
this.endX = this.hitX; this.endX = this.hitX;
this.endY = this.hitY; this.endY = this.hitY;
this.hitpoint = { x: this.hitX, y: this.hitY }; this.hitpoint = {x: this.hitX, y: this.hitY};
this.distance = Math.sqrt(Math.pow(this.hitX - this.startX, 2) + Math.pow(this.hitY - this.startY, 2)); this.distance = Math.sqrt(Math.pow(this.hitX - this.startX, 2) + Math.pow(this.hitY - this.startY, 2));
} else { } else {
this.hitX = null; this.hitX = null;
this.hitY = null; this.hitY = null;
this.hitpoint = { x: this.hitX, y: this.hitY }; this.hitpoint = {x: this.hitX, y: this.hitY};
this.distance = Math.sqrt(Math.pow(this.endX - this.startX, 2) + Math.pow(this.endY - this.startY, 2)); this.distance = Math.sqrt(Math.pow(this.endX - this.startX, 2) + Math.pow(this.endY - this.startY, 2));
} }
// console.log("Obstacle detected!"); // console.log("Obstacle detected!");
@ -115,34 +115,22 @@ export class RaycastSensor extends Sensor {
export class FloorColorSensor extends Sensor { export class FloorColorSensor extends Sensor {
constructor(robot, offsetAngle, offsetDistance) { constructor(robot, offsetAngle, offsetDistance) {
super(robot, offsetAngle, offsetDistance); // No angle offset, directly below robot super(robot, offsetAngle, offsetDistance); // No angle offset, directly below robot
this.hasLine = false;
this.type = "color"; this.type = "color";
this.fill = "red";
} }
read(robot, gameWorld) { read(robot, gameWorld) {
this.updatePosition(); this.value = gameWorld.getFloorColor(robot.x, robot.y);
this.value = gameWorld.getFloorColor(this.startX, this.startY);
this.hasLine = this.value;
if (this.value) {
this.fill = "green";
} else {
this.fill = "red";
}
//console.log(this.value);
return this.value; return this.value;
} }
draw(ctx) { draw(ctx) {
this.updatePosition(); this.updatePosition();
ctx.lineWidth = 1; ctx.strokeStyle = "purple";
ctx.strokeStyle = this.fill;
ctx.fillStyle = "green" ctx.fillStyle = "green"
ctx.beginPath(); ctx.beginPath();
ctx.arc(this.startX, this.startY, 2, 0, 2 * Math.PI); ctx.arc(this.startX, this.startY, 2, 0, 2 * Math.PI);
ctx.fillStyle = this.fill; ctx.fillStyle = "red";
ctx.fill(); ctx.fill();
ctx.stroke(); ctx.stroke();

View File

@ -137,7 +137,6 @@ button {
/* ===== Main Content ===== */ /* ===== Main Content ===== */
main { main {
height: 100%;
display: flex; display: flex;
justify-content: center; justify-content: center;
padding: 16px; padding: 16px;
@ -247,9 +246,6 @@ main {
border: 1px solid #d1d5db; border: 1px solid #d1d5db;
/* gray-300 */ /* gray-300 */
height: 100%; height: 100%;
display: flex;
flex-direction: column;
min-width: 200px;
} }
/* Right side: canvas + console */ /* Right side: canvas + console */

View File

@ -1,10 +1,3 @@
BUGS?
Allow different caps in "Hello World" in lesson1?
Disallow copy/paste of objectives?
Require printing of arithmetic in lesson2?
Hide hints behind button? Maybe require a few fails first?
Variables persist past reset
DO DO
Create test level of a track with obstacles all around Create test level of a track with obstacles all around