robot sensor values now available in simulation, line sensors return distance, await is automatically inserted before sleep commands
parent
39cac21741
commit
6051be2f61
11
game.js
11
game.js
|
|
@ -21,7 +21,7 @@ let startX, startY; // Mouse start positions
|
||||||
|
|
||||||
// ✅ Function to create the Pyodide Worker
|
// ✅ Function to create the Pyodide Worker
|
||||||
function startPyodideWorker() {
|
function startPyodideWorker() {
|
||||||
const worker = new Worker("pyodide-worker.js");
|
const worker = new Worker("pyodide-worker.js?v=" + Date.now());
|
||||||
|
|
||||||
// ✅ Reattach the event listener when a new worker is created
|
// ✅ Reattach the event listener when a new worker is created
|
||||||
worker.onmessage = (event) => {
|
worker.onmessage = (event) => {
|
||||||
|
|
@ -60,7 +60,7 @@ function createInitialRobots() {
|
||||||
|
|
||||||
// ✅ Function to log messages to console
|
// ✅ Function to log messages to console
|
||||||
function logToConsole(text) {
|
function logToConsole(text) {
|
||||||
console.log(text.replace("<b>", "").replace("</b>", ""));
|
//console.log(text.replace("<b>", "").replace("</b>", ""));
|
||||||
consoleElement.innerHTML += text.replace(/\n/g, "<br>") + "<br>";
|
consoleElement.innerHTML += text.replace(/\n/g, "<br>") + "<br>";
|
||||||
consoleElement.scrollTop = consoleElement.scrollHeight;
|
consoleElement.scrollTop = consoleElement.scrollHeight;
|
||||||
}
|
}
|
||||||
|
|
@ -155,13 +155,16 @@ function updateSensorData() {
|
||||||
//logToConsole(`📡 Sensor Update - Distance: ${distance.toFixed(2)}, Speed: ${speed.toFixed(2)}`);
|
//logToConsole(`📡 Sensor Update - Distance: ${distance.toFixed(2)}, Speed: ${speed.toFixed(2)}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
setInterval(updateSensorData, 2000); // Call every 2 seconds
|
//setInterval(updateSensorData, 2000); // Call every 2 seconds
|
||||||
|
|
||||||
|
|
||||||
// ✅ Button Event Listeners
|
// ✅ Button Event Listeners
|
||||||
document.getElementById("compile-button").addEventListener("click", () => {
|
document.getElementById("compile-button").addEventListener("click", () => {
|
||||||
if (paused) return;
|
if (paused) return;
|
||||||
const code = document.getElementById("python-code").value;
|
let code = document.getElementById("python-code").value;
|
||||||
|
console.log(code);
|
||||||
|
code = code.replace(/time\.sleep\(/g, "await time.sleep(");
|
||||||
|
console.log(code);
|
||||||
consoleElement.innerHTML = "";
|
consoleElement.innerHTML = "";
|
||||||
pyodideWorker.postMessage({
|
pyodideWorker.postMessage({
|
||||||
type: "execute",
|
type: "execute",
|
||||||
|
|
|
||||||
22
gameworld.js
22
gameworld.js
|
|
@ -40,7 +40,7 @@ export class GameWorld {
|
||||||
{ x: 420, y: 380 }, // Vertex 2
|
{ x: 420, y: 380 }, // Vertex 2
|
||||||
{ x: 350, y: 550 }, // Vertex 3
|
{ x: 350, y: 550 }, // Vertex 3
|
||||||
{ x: 280, y: 420 } // Vertex 4
|
{ x: 280, y: 420 } // Vertex 4
|
||||||
], { x: 400, y: 0 });
|
], { x: 400, y: -50 });
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -123,27 +123,31 @@ export class GameWorld {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rayCast(startX, startY, endX, endY) {
|
rayCast(startX, startY, endX, endY, ignoreBodies = []) {
|
||||||
let closestIntersection = null;
|
let closestIntersection = null;
|
||||||
let startPoint = { x: startX, y: startY };
|
let startPoint = { x: startX, y: startY };
|
||||||
let endPoint = { x: endX, y: endY };
|
let endPoint = { x: endX, y: endY };
|
||||||
// // Loop through all obstacles
|
|
||||||
// for (let i = 0; i < this.obstacles.length; i++) {
|
|
||||||
// let obstacle = this.obstacles[i];
|
|
||||||
|
|
||||||
// return this.lineIntersectsPolygon(startX, startY, endX, endY, obstacle);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Get all bodies in the world
|
// Get all bodies in the world
|
||||||
let allBodies = Matter.Composite.allBodies(this.engine.world);
|
let allBodies = Matter.Composite.allBodies(this.engine.world);
|
||||||
|
|
||||||
let hit = this.getRayHitPoint(startPoint, endPoint, allBodies);
|
// ✅ Filter out ignored bodies instead of removing them
|
||||||
|
let filteredBodies = allBodies.filter(body => !ignoreBodies.includes(body));
|
||||||
|
|
||||||
|
// Debugging
|
||||||
|
//console.log("Ignoring bodies: ", ignoreBodies.map(b => b.id));
|
||||||
|
//console.log("Filtered bodies: ", filteredBodies.map(b => b.id));
|
||||||
|
|
||||||
|
// Perform raycast only on the filtered bodies
|
||||||
|
let hit = this.getRayHitPoint(startPoint, endPoint, filteredBodies);
|
||||||
if (hit) {
|
if (hit) {
|
||||||
closestIntersection = hit.point;
|
closestIntersection = hit.point;
|
||||||
}
|
}
|
||||||
|
|
||||||
return closestIntersection;
|
return closestIntersection;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
getRayHitPoint(start, end, bodies) {
|
getRayHitPoint(start, end, bodies) {
|
||||||
let closestHit = null;
|
let closestHit = null;
|
||||||
let minDistance = Infinity;
|
let minDistance = Infinity;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
importScripts("https://cdn.jsdelivr.net/pyodide/v0.23.4/full/pyodide.js");
|
importScripts("https://cdn.jsdelivr.net/pyodide/v0.23.4/full/pyodide.js");
|
||||||
|
|
||||||
let sensorData = {}; // ✅ Store sensor values
|
let sensorData = {}; // ✅ Store sensor values
|
||||||
|
let gameWorld = null;
|
||||||
|
|
||||||
async function initializePyodide() {
|
async function initializePyodide() {
|
||||||
self.pyodide = await loadPyodide({
|
self.pyodide = await loadPyodide({
|
||||||
|
|
@ -11,9 +12,44 @@ async function initializePyodide() {
|
||||||
self.postMessage({ type: event, data: data });
|
self.postMessage({ type: event, data: data });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ✅ Expose sensor data to Python
|
// ✅ Expose sensor data to Python
|
||||||
self.pyodide.globals.set("get_sensor_data", (name) => {
|
self.pyodide.globals.set("get_sensor_data", (name) => {
|
||||||
return sensorData[name] ?? null;
|
if (gameWorld == null){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//console.log(gameWorld.robots);
|
||||||
|
let robot = gameWorld.robots[0];
|
||||||
|
//console.log(robot);
|
||||||
|
let sensorArray = robot.sensors || []; // Assuming `sensors` is an array inside the robot object
|
||||||
|
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
|
||||||
|
"hitpoint": sensor.hitpoint // Whatever other attributes you need
|
||||||
|
}));
|
||||||
|
//console.log(sensorData["x"]);
|
||||||
|
sensorData = JSON.stringify(sensorData);
|
||||||
|
return sensorData;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
self.pyodide.globals.set("get_robot_data", () => {
|
||||||
|
if (gameWorld == null){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
let robot = gameWorld.robots[0];
|
||||||
|
|
||||||
|
let robotData = {
|
||||||
|
"position": robot.body.position,
|
||||||
|
"angle": robot.body.angle,
|
||||||
|
"velocity": robot.body.velocity
|
||||||
|
};
|
||||||
|
robotData = JSON.stringify(robotData);
|
||||||
|
return robotData;
|
||||||
});
|
});
|
||||||
|
|
||||||
// ✅ Run Python initialization
|
// ✅ Run Python initialization
|
||||||
|
|
@ -22,10 +58,44 @@ import sys
|
||||||
import pyodide
|
import pyodide
|
||||||
import asyncio
|
import asyncio
|
||||||
import time
|
import time
|
||||||
|
import json
|
||||||
|
import math
|
||||||
|
|
||||||
class RobotModule:
|
class RobotModule:
|
||||||
def get_sensor(self, name):
|
def get_sensor(self, name):
|
||||||
return get_sensor_data(name)
|
sensor_data = json.loads(get_sensor_data(name))
|
||||||
|
return sensor_data
|
||||||
|
|
||||||
|
def get_sensors(self):
|
||||||
|
return json.loads(get_sensor_data("sensors")) # Returns list of sensor dicts
|
||||||
|
|
||||||
|
def get_pos(self):
|
||||||
|
robot_data = json.loads(get_robot_data())
|
||||||
|
position = robot_data["position"]
|
||||||
|
return position
|
||||||
|
|
||||||
|
def get_x(self):
|
||||||
|
robot_data = json.loads(get_robot_data())
|
||||||
|
position = robot_data["position"]["x"]
|
||||||
|
return position
|
||||||
|
|
||||||
|
def get_y(self):
|
||||||
|
robot_data = json.loads(get_robot_data())
|
||||||
|
position = robot_data["position"]["y"]
|
||||||
|
return position
|
||||||
|
|
||||||
|
def get_velocity(self):
|
||||||
|
robot_data = json.loads(get_robot_data())
|
||||||
|
velocity = robot_data["velocity"]
|
||||||
|
return velocity
|
||||||
|
|
||||||
|
def get_velocity_magnitude(self):
|
||||||
|
velXY = self.get_velocity()
|
||||||
|
velX = velXY["x"]
|
||||||
|
velY = velXY["y"]
|
||||||
|
magnitude = math.sqrt(velX**2 + velY**2)
|
||||||
|
|
||||||
|
return magnitude
|
||||||
|
|
||||||
def move(self, speed):
|
def move(self, speed):
|
||||||
send_to_main("move", speed)
|
send_to_main("move", speed)
|
||||||
|
|
@ -65,7 +135,6 @@ time.sleep = async_sleep # ✅ Monkey-patch time.sleep()
|
||||||
}
|
}
|
||||||
|
|
||||||
initializePyodide();
|
initializePyodide();
|
||||||
self.postMessage({ type: "console", message: ":-(" });
|
|
||||||
|
|
||||||
self.onmessage = async (event) => {
|
self.onmessage = async (event) => {
|
||||||
if (!self.pyodide) {
|
if (!self.pyodide) {
|
||||||
|
|
@ -77,7 +146,8 @@ self.onmessage = async (event) => {
|
||||||
// ✅ Update sensor data
|
// ✅ Update sensor data
|
||||||
Object.assign(sensorData, event.data.data);
|
Object.assign(sensorData, event.data.data);
|
||||||
} else if (event.data.type === "game_state") {
|
} else if (event.data.type === "game_state") {
|
||||||
console.log("Game state updated in");
|
gameWorld = event.data.state;
|
||||||
|
//console.log(event.data);
|
||||||
|
|
||||||
} else if (event.data.type === "execute") {
|
} else if (event.data.type === "execute") {
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
10
readme.md
10
readme.md
|
|
@ -1,8 +1,12 @@
|
||||||
import robot
|
import robot
|
||||||
import time
|
import time
|
||||||
|
|
||||||
robot.move(0.5)
|
robot.move(0.00006)
|
||||||
await time.sleep(1)
|
|
||||||
while True:
|
while True:
|
||||||
robot.turn(5)
|
if robot.get_sensors()[0]["hitpoint"]["x"] is not None:
|
||||||
|
robot.turn(0.001)
|
||||||
|
elif robot.get_sensors()[1]["hitpoint"]["x"] is not None:
|
||||||
|
robot.turn(-0.001)
|
||||||
|
else:
|
||||||
|
robot.turn(0)
|
||||||
await time.sleep(0.1)
|
await time.sleep(0.1)
|
||||||
6
robot.js
6
robot.js
|
|
@ -22,8 +22,8 @@ export class Robot {
|
||||||
{ x: -this.width / 2, y: this.height / 2 } // Vertex 4
|
{ x: -this.width / 2, y: this.height / 2 } // Vertex 4
|
||||||
];
|
];
|
||||||
|
|
||||||
this.addSensor(new RaycastSensor(this, -40, 13, -45, 60));
|
this.addSensor(new RaycastSensor(this, -40, 12, -45, 60));
|
||||||
this.addSensor(new RaycastSensor(this, 40, 13, 45, 60));
|
this.addSensor(new RaycastSensor(this, 40, 12, 45, 60));
|
||||||
this.addSensor(new FloorColorSensor(this, 0, 0));
|
this.addSensor(new FloorColorSensor(this, 0, 0));
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -72,7 +72,7 @@ export class Robot {
|
||||||
}
|
}
|
||||||
|
|
||||||
turn(degrees) {
|
turn(degrees) {
|
||||||
this.angularThrust += degrees;
|
this.angularThrust = degrees;
|
||||||
}
|
}
|
||||||
|
|
||||||
draw(ctx) {
|
draw(ctx) {
|
||||||
|
|
|
||||||
22
sensor.js
22
sensor.js
|
|
@ -1,5 +1,6 @@
|
||||||
export class Sensor {
|
export class Sensor {
|
||||||
constructor(robot, offsetAngle, offsetDistance) {
|
constructor(robot, offsetAngle, offsetDistance) {
|
||||||
|
this.type = "base";
|
||||||
this.robot = robot
|
this.robot = robot
|
||||||
this.value = null; // Last recorded value
|
this.value = null; // Last recorded value
|
||||||
this.offsetAngle = offsetAngle;
|
this.offsetAngle = offsetAngle;
|
||||||
|
|
@ -7,6 +8,8 @@ export class Sensor {
|
||||||
this.hitX = null;
|
this.hitX = null;
|
||||||
this.hitY = null;
|
this.hitY = null;
|
||||||
this.hitObject = null;
|
this.hitObject = null;
|
||||||
|
this.angle = 0;
|
||||||
|
this.hitpoint = {x: null, y: null};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -28,11 +31,12 @@ export class Sensor {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class RaycastSensor extends Sensor {
|
export class RaycastSensor extends Sensor {
|
||||||
constructor(robot, offsetAngle, offsetDistance, angle, distance) {
|
constructor(robot, offsetAngle, offsetDistance, angle, range) {
|
||||||
super(robot, offsetAngle, offsetDistance); // Call the parent class constructor
|
super(robot, offsetAngle, offsetDistance); // Call the parent class constructor
|
||||||
|
this.type = "line";
|
||||||
this.angle = angle;
|
this.angle = angle;
|
||||||
this.distance = distance;
|
this.distance = null;
|
||||||
this.range = 100;
|
this.range = range;
|
||||||
}
|
}
|
||||||
|
|
||||||
updatePosition() {
|
updatePosition() {
|
||||||
|
|
@ -51,19 +55,24 @@ export class RaycastSensor extends Sensor {
|
||||||
this.updatePosition();
|
this.updatePosition();
|
||||||
|
|
||||||
const angleInRadians = (this.robot.direction + this.angle) * Math.PI / 180;
|
const angleInRadians = (this.robot.direction + this.angle) * Math.PI / 180;
|
||||||
const x = this.robot.x + Math.cos(angleInRadians) * this.distance;
|
const x = this.robot.x + Math.cos(angleInRadians) * this.range;
|
||||||
const y = this.robot.y + Math.sin(angleInRadians) * this.distance;
|
const y = this.robot.y + Math.sin(angleInRadians) * this.range;
|
||||||
|
|
||||||
// Ensure gameWorld is available and properly passed to the sensor
|
// Ensure gameWorld is available and properly passed to the sensor
|
||||||
let hitPos = gameWorld.rayCast(this.startX, this.startY, this.endX, this.endY);
|
let hitPos = gameWorld.rayCast(this.startX, this.startY, this.endX, this.endY, [this.robot.body]);
|
||||||
if (hitPos != null) {
|
if (hitPos != null) {
|
||||||
this.hitX = hitPos.x;
|
this.hitX = hitPos.x;
|
||||||
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.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.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!");
|
||||||
// } else {
|
// } else {
|
||||||
|
|
@ -106,6 +115,7 @@ 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.type = "color";
|
||||||
}
|
}
|
||||||
|
|
||||||
read(robot, gameWorld) {
|
read(robot, gameWorld) {
|
||||||
|
|
|
||||||
10
todo.md
10
todo.md
|
|
@ -1,12 +1,14 @@
|
||||||
DO
|
DO
|
||||||
|
Create test level of a track with obstacles all around
|
||||||
|
Improve the text editor to be larger, and maybe have some colour coding and allow tabs
|
||||||
|
|
||||||
|
IN PROGRESS
|
||||||
|
|
||||||
|
DONE
|
||||||
Add robot.angle and other state variables
|
Add robot.angle and other state variables
|
||||||
Add robot sensor data connections to detect and change parameters
|
Add robot sensor data connections to detect and change parameters
|
||||||
Add pan and zoom to canvas
|
Add pan and zoom to canvas
|
||||||
|
|
||||||
IN PROGRESS
|
|
||||||
Collision between robots and objects
|
Collision between robots and objects
|
||||||
|
|
||||||
DONE
|
|
||||||
Add line sensor object collisions
|
Add line sensor object collisions
|
||||||
Add robot sensors that detect things in game world, color sense and distance sense for start
|
Add robot sensors that detect things in game world, color sense and distance sense for start
|
||||||
Add Pause Button
|
Add Pause Button
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue