export class Sensor { constructor(robot, offsetAngle, offsetDistance) { this.robot = robot this.value = null; // Last recorded value this.offsetAngle = offsetAngle; this.offsetDistance = offsetDistance; } updatePosition() { const robotAngle = this.robot.angle * (Math.PI / 180); const offsetAngleRad = this.offsetAngle * (Math.PI / 180); this.startX = this.robot.x + this.offsetDistance * Math.cos(robotAngle + offsetAngleRad); this.startY = this.robot.y + this.offsetDistance * Math.sin(robotAngle + offsetAngleRad); } read(robot, gameWorld) { throw new Error("read() must be implemented in subclasses"); } draw(ctx) { // Default empty draw, can be overridden } } export class RaycastSensor extends Sensor { constructor(robot, offsetAngle, offsetDistance, angle, distance) { super(robot, offsetAngle, offsetDistance); // Call the parent class constructor this.angle = angle; this.distance = distance; this.range = 100; } updatePosition() { const robotAngle = this.robot.angle * (Math.PI / 180); const offsetAngleRad = this.offsetAngle * (Math.PI / 180); this.startX = this.robot.x + this.offsetDistance * Math.cos(robotAngle + offsetAngleRad); this.startY = this.robot.y + this.offsetDistance * Math.sin(robotAngle + offsetAngleRad); const sensorAngle = robotAngle + (this.angle * (Math.PI / 180)); this.endX = this.startX + Math.cos(sensorAngle) * this.range; this.endY = this.startY + Math.sin(sensorAngle) * this.range; } read(robot, gameWorld) { //console.log(robot); //console.log(gameWorld); const angleInRadians = (this.robot.direction + this.angle) * Math.PI / 180; const x = this.robot.x + Math.cos(angleInRadians) * this.distance; const y = this.robot.y + Math.sin(angleInRadians) * this.distance; // Ensure gameWorld is available and properly passed to the sensor if (gameWorld.isObstacle(x, y)) { //console.log("Obstacle detected!"); } else { //console.log("Clear path!"); } } draw(ctx) { this.updatePosition(); ctx.strokeStyle = "lime"; ctx.fillStyle = "green" ctx.beginPath(); ctx.arc(this.startX, this.startY, 2, 0, 2 * Math.PI); ctx.fillStyle = "red"; ctx.fill(); ctx.stroke(); ctx.strokeStyle = "yellow"; ctx.beginPath(); ctx.lineWidth = 2; ctx.moveTo(this.startX, this.startY); ctx.lineTo(this.endX, this.endY); ctx.stroke(); } } export class FloorColorSensor extends Sensor { constructor(robot, offsetAngle, offsetDistance) { super(robot, offsetAngle, offsetDistance); // No angle offset, directly below robot } read(robot, gameWorld) { this.value = gameWorld.getFloorColor(robot.x, robot.y); return this.value; } draw(ctx) { this.updatePosition(); ctx.strokeStyle = "purple"; ctx.fillStyle = "green" ctx.beginPath(); ctx.arc(this.startX, this.startY, 2, 0, 2 * Math.PI); ctx.fillStyle = "red"; ctx.fill(); ctx.stroke(); } }