Skip to content

Commit

Permalink
Lecture 7 code
Browse files Browse the repository at this point in the history
  • Loading branch information
gniziemazity authored Apr 21, 2022
1 parent 39b44c9 commit 37da305
Show file tree
Hide file tree
Showing 10 changed files with 555 additions and 0 deletions.
141 changes: 141 additions & 0 deletions 7. Visualizing neural networks/car.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
class Car{
constructor(x,y,width,height,controlType,maxSpeed=3){
this.x=x;
this.y=y;
this.width=width;
this.height=height;

this.speed=0;
this.acceleration=0.2;
this.maxSpeed=maxSpeed;
this.friction=0.05;
this.angle=0;
this.damaged=false;

this.useBrain=controlType=="AI";

if(controlType!="DUMMY"){
this.sensor=new Sensor(this);
this.brain=new NeuralNetwork(
[this.sensor.rayCount,6,4]
);
}
this.controls=new Controls(controlType);
}

update(roadBorders,traffic){
if(!this.damaged){
this.#move();
this.polygon=this.#createPolygon();
this.damaged=this.#assessDamage(roadBorders,traffic);
}
if(this.sensor){
this.sensor.update(roadBorders,traffic);
const offsets=this.sensor.readings.map(
s=>s==null?0:1-s.offset
);
const outputs=NeuralNetwork.feedForward(offsets,this.brain);

if(this.useBrain){
this.controls.forward=outputs[0];
this.controls.left=outputs[1];
this.controls.right=outputs[2];
this.controls.reverse=outputs[3];
}
}
}

#assessDamage(roadBorders,traffic){
for(let i=0;i<roadBorders.length;i++){
if(polysIntersect(this.polygon,roadBorders[i])){
return true;
}
}
for(let i=0;i<traffic.length;i++){
if(polysIntersect(this.polygon,traffic[i].polygon)){
return true;
}
}
return false;
}

#createPolygon(){
const points=[];
const rad=Math.hypot(this.width,this.height)/2;
const alpha=Math.atan2(this.width,this.height);
points.push({
x:this.x-Math.sin(this.angle-alpha)*rad,
y:this.y-Math.cos(this.angle-alpha)*rad
});
points.push({
x:this.x-Math.sin(this.angle+alpha)*rad,
y:this.y-Math.cos(this.angle+alpha)*rad
});
points.push({
x:this.x-Math.sin(Math.PI+this.angle-alpha)*rad,
y:this.y-Math.cos(Math.PI+this.angle-alpha)*rad
});
points.push({
x:this.x-Math.sin(Math.PI+this.angle+alpha)*rad,
y:this.y-Math.cos(Math.PI+this.angle+alpha)*rad
});
return points;
}

#move(){
if(this.controls.forward){
this.speed+=this.acceleration;
}
if(this.controls.reverse){
this.speed-=this.acceleration;
}

if(this.speed>this.maxSpeed){
this.speed=this.maxSpeed;
}
if(this.speed<-this.maxSpeed/2){
this.speed=-this.maxSpeed/2;
}

if(this.speed>0){
this.speed-=this.friction;
}
if(this.speed<0){
this.speed+=this.friction;
}
if(Math.abs(this.speed)<this.friction){
this.speed=0;
}

if(this.speed!=0){
const flip=this.speed>0?1:-1;
if(this.controls.left){
this.angle+=0.03*flip;
}
if(this.controls.right){
this.angle-=0.03*flip;
}
}

this.x-=Math.sin(this.angle)*this.speed;
this.y-=Math.cos(this.angle)*this.speed;
}

draw(ctx,color){
if(this.damaged){
ctx.fillStyle="gray";
}else{
ctx.fillStyle=color;
}
ctx.beginPath();
ctx.moveTo(this.polygon[0].x,this.polygon[0].y);
for(let i=1;i<this.polygon.length;i++){
ctx.lineTo(this.polygon[i].x,this.polygon[i].y);
}
ctx.fill();

if(this.sensor){
this.sensor.draw(ctx);
}
}
}
52 changes: 52 additions & 0 deletions 7. Visualizing neural networks/controls.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
class Controls{
constructor(type){
this.forward=false;
this.left=false;
this.right=false;
this.reverse=false;

switch(type){
case "KEYS":
this.#addKeyboardListeners();
break;
case "DUMMY":
this.forward=true;
break;
}
}

#addKeyboardListeners(){
document.onkeydown=(event)=>{
switch(event.key){
case "ArrowLeft":
this.left=true;
break;
case "ArrowRight":
this.right=true;
break;
case "ArrowUp":
this.forward=true;
break;
case "ArrowDown":
this.reverse=true;
break;
}
}
document.onkeyup=(event)=>{
switch(event.key){
case "ArrowLeft":
this.left=false;
break;
case "ArrowRight":
this.right=false;
break;
case "ArrowUp":
this.forward=false;
break;
case "ArrowDown":
this.reverse=false;
break;
}
}
}
}
18 changes: 18 additions & 0 deletions 7. Visualizing neural networks/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<!DOCTYPE html>
<head>
<title>Self-driving car - No libraries</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<canvas id="carCanvas"></canvas>
<canvas id="networkCanvas"></canvas>
<script src="visualizer.js"></script>
<script src="network.js"></script>
<script src="sensor.js"></script>
<script src="utils.js"></script>
<script src="road.js"></script>
<script src="controls.js"></script>
<script src="car.js"></script>
<script src="main.js"></script>
</body>
</html>
40 changes: 40 additions & 0 deletions 7. Visualizing neural networks/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
const carCanvas=document.getElementById("carCanvas");
carCanvas.width=200;
const networkCanvas=document.getElementById("networkCanvas");
networkCanvas.width=300;

const carCtx = carCanvas.getContext("2d");
const networkCtx = networkCanvas.getContext("2d");

const road=new Road(carCanvas.width/2,carCanvas.width*0.9);
const car=new Car(road.getLaneCenter(1),100,30,50,"AI");
const traffic=[
new Car(road.getLaneCenter(1),-100,30,50,"DUMMY",2)
];

animate();

function animate(time){
for(let i=0;i<traffic.length;i++){
traffic[i].update(road.borders,[]);
}
car.update(road.borders,traffic);

carCanvas.height=window.innerHeight;
networkCanvas.height=window.innerHeight;

carCtx.save();
carCtx.translate(0,-car.y+carCanvas.height*0.7);

road.draw(carCtx);
for(let i=0;i<traffic.length;i++){
traffic[i].draw(carCtx,"red");
}
car.draw(carCtx,"blue");

carCtx.restore();

networkCtx.lineDashOffset=-time/50;
Visualizer.drawNetwork(networkCtx,car.brain);
requestAnimationFrame(animate);
}
68 changes: 68 additions & 0 deletions 7. Visualizing neural networks/network.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
class NeuralNetwork{
constructor(neuronCounts){
this.levels=[];
for(let i=0;i<neuronCounts.length-1;i++){
this.levels.push(new Level(
neuronCounts[i],neuronCounts[i+1]
));
}
}

static feedForward(givenInputs,network){
let outputs=Level.feedForward(
givenInputs,network.levels[0]);
for(let i=1;i<network.levels.length;i++){
outputs=Level.feedForward(
outputs,network.levels[i]);
}
return outputs;
}
}

class Level{
constructor(inputCount,outputCount){
this.inputs=new Array(inputCount);
this.outputs=new Array(outputCount);
this.biases=new Array(outputCount);

this.weights=[];
for(let i=0;i<inputCount;i++){
this.weights[i]=new Array(outputCount);
}

Level.#randomize(this);
}

static #randomize(level){
for(let i=0;i<level.inputs.length;i++){
for(let j=0;j<level.outputs.length;j++){
level.weights[i][j]=Math.random()*2-1;
}
}

for(let i=0;i<level.biases.length;i++){
level.biases[i]=Math.random()*2-1;
}
}

static feedForward(givenInputs,level){
for(let i=0;i<level.inputs.length;i++){
level.inputs[i]=givenInputs[i];
}

for(let i=0;i<level.outputs.length;i++){
let sum=0
for(let j=0;j<level.inputs.length;j++){
sum+=level.inputs[j]*level.weights[j][i];
}

if(sum>level.biases[i]){
level.outputs[i]=1;
}else{
level.outputs[i]=0;
}
}

return level.outputs;
}
}
56 changes: 56 additions & 0 deletions 7. Visualizing neural networks/road.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
class Road{
constructor(x,width,laneCount=3){
this.x=x;
this.width=width;
this.laneCount=laneCount;

this.left=x-width/2;
this.right=x+width/2;

const infinity=1000000;
this.top=-infinity;
this.bottom=infinity;

const topLeft={x:this.left,y:this.top};
const topRight={x:this.right,y:this.top};
const bottomLeft={x:this.left,y:this.bottom};
const bottomRight={x:this.right,y:this.bottom};
this.borders=[
[topLeft,bottomLeft],
[topRight,bottomRight]
];
}

getLaneCenter(laneIndex){
const laneWidth=this.width/this.laneCount;
return this.left+laneWidth/2+
Math.min(laneIndex,this.laneCount-1)*laneWidth;
}

draw(ctx){
ctx.lineWidth=5;
ctx.strokeStyle="white";

for(let i=1;i<=this.laneCount-1;i++){
const x=lerp(
this.left,
this.right,
i/this.laneCount
);

ctx.setLineDash([20,20]);
ctx.beginPath();
ctx.moveTo(x,this.top);
ctx.lineTo(x,this.bottom);
ctx.stroke();
}

ctx.setLineDash([]);
this.borders.forEach(border=>{
ctx.beginPath();
ctx.moveTo(border[0].x,border[0].y);
ctx.lineTo(border[1].x,border[1].y);
ctx.stroke();
});
}
}
Loading

0 comments on commit 37da305

Please sign in to comment.