// SPDX-License-Identifier: GPL-3.0-or-later // Author: Sascha Nitsch https://contentnation.net/@grumpydevelop import { RobotStatus } from './robotstatus'; import { RoboBase } from './robobase'; class Rabbit extends RoboBase { carrotX: number = 0; carrotY: number = 0; running = false; mode = 'MUNCH'; munchtime = 0; ready() { console.log('rabbit is ready'); } start() { console.log('simulation started'); this.cmd.powerRight = 0; this.cmd.powerLeft = 0; this.cmd.radarMin = 270; this.cmd.radarMax = 90; this.sendCmd(); this.running = true; } statusReady(status: RobotStatus) { if (!this.running) { return; } switch (this.mode) { case 'RUN': const dx = this.carrotX - status.posX; const dy = this.carrotY - status.posY; const dir = Math.atan2(dx, -dy) * 180 / Math.PI; const dst = Math.sqrt(dx * dx + dy * dy); let rot = dir - status.orientation; if (rot < -180) { rot += 360; } if (rot > 180) { rot -= 360; } rot = Math.min(Math.max(-90, rot), 90); let desiredSpeed = 1; if (dst < 30) { desiredSpeed = dst / 40; } const speedDiff = Math.sin(rot * Math.PI / 180.0); // desired speed of left chain let dsl = desiredSpeed + speedDiff; // desired speed of right chain let dsr = desiredSpeed - speedDiff; // prevent overflows, reduce to limits but keep relative difference if possible if (dsl > 1) { dsr -= Math.max(dsl - 1, -1); dsl = 1; } else if (dsr > 1) { dsl -= Math.max(dsr - 1, -1); dsr = 1; } this.cmd.powerLeft = dsl; this.cmd.powerRight = dsr; // close and slow enough? if (dst < 5 && Math.abs(status.chainSpeedLeft) < .1 && Math.abs(status.chainSpeedRight) < .1) { this.mode = 'MUNCH'; this.cmd.powerLeft = 0; this.cmd.powerRight = 0; this.munchtime = 5 * 20; // 5 sec console.log("munch"); } break; case "MUNCH": if (this.munchtime) { --this.munchtime; } else { this.mode = 'RUN'; this.carrotX = Math.random() * (this.width - 20) + 10; this.carrotY = Math.random() * (this.height - 20) + 10; console.log("target reached, new target", this.carrotX, this.carrotY); } break; } if (status.contactPoints && status.contactPoints.length > 0) { status.contactPoints.forEach((cp) => { // adjust angle by radar rotation const angle = cp.angle + status.radarPos; // global angle from position const globalAngle = angle + status.orientation; const targetX = Math.sin(globalAngle * Math.PI / 180)* cp.dist + status.posX; const targetY = -Math.cos(globalAngle * Math.PI / 180)* cp.dist + status.posY; // run the other way +- 45 degree let newTargetAngle = globalAngle + 180 + Math.random()*90 - 45 if (newTargetAngle >= 360) { newTargetAngle -= 360; } if (newTargetAngle < 0) { newTargetAngle += 360; } // target is 50-100 units away const newTargetDistance = Math.random() * 50 + 50; this.carrotX = status.posX + Math.sin(newTargetAngle * Math.PI / 180) * newTargetDistance; this.carrotY = status.posY - Math.cos(newTargetAngle * Math.PI / 180) * newTargetDistance; if (this.carrotX > this.width - 10) { this.carrotX = this.width - 10; } if (this.carrotY > this.height - 10) { this.carrotY = this.height - 10; } if (this.carrotX < 0) { this.carrotX = 10; } if (this.carrotY < 0) { this.carrotY = 10; } console.log("enemy at", targetX, targetY, "run to", this.carrotX, this.carrotY); }); } this.sendCmd(); } lost() { console.log('we lost'); this.disconnect(); } won() { console.log('we won'); this.disconnect(); } } var rabbit = new Rabbit('rabbit', 'scout'); rabbit.connect();