Create car.js
Browse files
car.js
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
class Car{
|
| 2 |
+
constructor(x,y,width,height,controlType,maxSpeed=3){
|
| 3 |
+
this.x=x;
|
| 4 |
+
this.y=y;
|
| 5 |
+
this.width=width;
|
| 6 |
+
this.height=height;
|
| 7 |
+
|
| 8 |
+
this.speed=0;
|
| 9 |
+
this.acceleration=0.2;
|
| 10 |
+
this.maxSpeed=maxSpeed;
|
| 11 |
+
this.friction=0.05;
|
| 12 |
+
this.angle=0;
|
| 13 |
+
|
| 14 |
+
this.damaged=false;
|
| 15 |
+
|
| 16 |
+
this.useBrain=controlType=="AI";
|
| 17 |
+
|
| 18 |
+
if(controlType!="DUMMY"){
|
| 19 |
+
this.sensor=new Sensor();
|
| 20 |
+
this.brain=new NeuralNetwork(
|
| 21 |
+
[this.sensor.rayCount,6,4]
|
| 22 |
+
);
|
| 23 |
+
}
|
| 24 |
+
this.controls=new Controls(controlType);
|
| 25 |
+
}
|
| 26 |
+
|
| 27 |
+
update(roadBorders,traffic){
|
| 28 |
+
if(!this.damaged){
|
| 29 |
+
this.#move();
|
| 30 |
+
this.polygon=this.#createPolygon();
|
| 31 |
+
this.damaged=this.#assessDamage(roadBorders,traffic);
|
| 32 |
+
}
|
| 33 |
+
if(this.sensor){
|
| 34 |
+
this.sensor.update(this.x,this.y,this.angle,roadBorders,traffic);
|
| 35 |
+
const offsets=this.sensor.readings.map(
|
| 36 |
+
s=>s==null?0:1-s.offset
|
| 37 |
+
);
|
| 38 |
+
const outputs=NeuralNetwork.feedForward(offsets,this.brain);
|
| 39 |
+
if(this.useBrain){
|
| 40 |
+
this.controls.forward=outputs[0];
|
| 41 |
+
this.controls.left=outputs[1];
|
| 42 |
+
this.controls.right=outputs[2];
|
| 43 |
+
this.controls.reverse=outputs[3];
|
| 44 |
+
}
|
| 45 |
+
}
|
| 46 |
+
}
|
| 47 |
+
|
| 48 |
+
#assessDamage(roadBorders,traffic){
|
| 49 |
+
for(let i=0;i<roadBorders.length;i++){
|
| 50 |
+
if(polysIntersect(
|
| 51 |
+
[...this.polygon,this.polygon[0]],
|
| 52 |
+
roadBorders[i])
|
| 53 |
+
){
|
| 54 |
+
return true;
|
| 55 |
+
}
|
| 56 |
+
}
|
| 57 |
+
for(let i=0;i<traffic.length;i++){
|
| 58 |
+
const poly=traffic[i].polygon;
|
| 59 |
+
if(polysIntersect(
|
| 60 |
+
[...this.polygon,this.polygon[0]],
|
| 61 |
+
[...poly,poly[0]])
|
| 62 |
+
){
|
| 63 |
+
return true;
|
| 64 |
+
}
|
| 65 |
+
}
|
| 66 |
+
return false;
|
| 67 |
+
}
|
| 68 |
+
|
| 69 |
+
#createPolygon(){
|
| 70 |
+
const points=[];
|
| 71 |
+
const rad=Math.hypot(this.width,this.height)/2;
|
| 72 |
+
const alpha=Math.atan2(this.width,this.height);
|
| 73 |
+
points.push({
|
| 74 |
+
x:this.x-Math.sin(this.angle-alpha)*rad,
|
| 75 |
+
y:this.y-Math.cos(this.angle-alpha)*rad
|
| 76 |
+
});
|
| 77 |
+
points.push({
|
| 78 |
+
x:this.x-Math.sin(this.angle+alpha)*rad,
|
| 79 |
+
y:this.y-Math.cos(this.angle+alpha)*rad
|
| 80 |
+
});
|
| 81 |
+
points.push({
|
| 82 |
+
x:this.x-Math.sin(Math.PI+this.angle-alpha)*rad,
|
| 83 |
+
y:this.y-Math.cos(Math.PI+this.angle-alpha)*rad
|
| 84 |
+
});
|
| 85 |
+
points.push({
|
| 86 |
+
x:this.x-Math.sin(Math.PI+this.angle+alpha)*rad,
|
| 87 |
+
y:this.y-Math.cos(Math.PI+this.angle+alpha)*rad
|
| 88 |
+
});
|
| 89 |
+
return points;
|
| 90 |
+
}
|
| 91 |
+
|
| 92 |
+
#move(){
|
| 93 |
+
if(this.controls.forward){
|
| 94 |
+
this.speed+=this.acceleration;
|
| 95 |
+
}
|
| 96 |
+
if(this.controls.reverse){
|
| 97 |
+
this.speed-=this.acceleration;
|
| 98 |
+
}
|
| 99 |
+
|
| 100 |
+
if(this.speed!=0){
|
| 101 |
+
const flip=this.speed>0?1:-1;
|
| 102 |
+
if(this.controls.left){
|
| 103 |
+
this.angle+=0.03*flip;
|
| 104 |
+
}
|
| 105 |
+
if(this.controls.right){
|
| 106 |
+
this.angle-=0.03*flip;
|
| 107 |
+
}
|
| 108 |
+
}
|
| 109 |
+
|
| 110 |
+
if(this.speed>this.maxSpeed){
|
| 111 |
+
this.speed=this.maxSpeed;
|
| 112 |
+
}
|
| 113 |
+
if(this.speed<-this.maxSpeed/2){
|
| 114 |
+
this.speed=-this.maxSpeed/2;
|
| 115 |
+
}
|
| 116 |
+
|
| 117 |
+
if(this.speed>0){
|
| 118 |
+
this.speed-=this.friction;
|
| 119 |
+
}
|
| 120 |
+
if(this.speed<0){
|
| 121 |
+
this.speed+=this.friction;
|
| 122 |
+
}
|
| 123 |
+
if(Math.abs(this.speed)<this.friction){
|
| 124 |
+
this.speed=0;
|
| 125 |
+
}
|
| 126 |
+
this.x-=Math.sin(this.angle)*this.speed;
|
| 127 |
+
this.y-=Math.cos(this.angle)*this.speed;
|
| 128 |
+
}
|
| 129 |
+
|
| 130 |
+
draw(ctx,drawSensor=false){
|
| 131 |
+
if(this.damaged){
|
| 132 |
+
ctx.fillStyle="gray";
|
| 133 |
+
}else{
|
| 134 |
+
ctx.fillStyle="black";
|
| 135 |
+
}
|
| 136 |
+
ctx.beginPath();
|
| 137 |
+
ctx.moveTo(this.polygon[0].x,this.polygon[0].y);
|
| 138 |
+
for(let i=1;i<this.polygon.length;i++){
|
| 139 |
+
ctx.lineTo(this.polygon[i].x,this.polygon[i].y);
|
| 140 |
+
}
|
| 141 |
+
ctx.fill();
|
| 142 |
+
if(this.sensor && drawSensor){
|
| 143 |
+
this.sensor.draw(ctx);
|
| 144 |
+
}
|
| 145 |
+
}
|
| 146 |
+
}
|