Skip to content

Commit

Permalink
dispatches race file into services
Browse files Browse the repository at this point in the history
  • Loading branch information
Tom Hohler committed Apr 25, 2018
1 parent 196b2a5 commit 8a17699
Show file tree
Hide file tree
Showing 7 changed files with 198 additions and 187 deletions.
30 changes: 16 additions & 14 deletions src/demos/neat/racing/car.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ import { Map } from "./map";
import { Position } from "./position";
import { Vector2 } from "./vector2";

const SENSOR_DISTANCE = 200;
const MAX_SPEED = 1000;
const sensorDelta = 1;
export class Car {

readonly sensorRange = 200;
readonly width = 36;
readonly height = 18;
readonly maxSpeed = 1000;
speed: number = 0;
direction: number = Math.PI / 4;
brain?: Network;
Expand All @@ -18,8 +20,8 @@ export class Car {
lastCheckPoint: number | null = null;
distanceToLastCheckPoint: number = 0;

constructor(pos: Position, brain?: Network) {
this.pos = pos;
constructor(x: number, y: number, brain?: Network) {
this.pos = new Position(x, y);
this.brain = brain;
}

Expand All @@ -40,18 +42,18 @@ export class Car {

checkCollisions = (map: Map, nextPosX: number, nextPosY: number) => {
const sensorsActivated = [false, false, false];
for (let k = 0; k < SENSOR_DISTANCE; k += sensorDelta) {
for (let k = 0; k < this.sensorRange; k += sensorDelta) {
const firstSensorPos = {
x: Math.floor(this.pos.x + 18 + k * Math.cos(this.direction - Math.PI / 4)),
y: Math.floor(this.pos.y + 9 + k * Math.sin(this.direction - Math.PI / 4))
x: Math.floor(this.pos.x + this.width / 2 + k * Math.cos(this.direction - Math.PI / 4)),
y: Math.floor(this.pos.y + this.height / 2 + k * Math.sin(this.direction - Math.PI / 4))
};
const secondSensorPos = {
x: Math.floor(this.pos.x + 18 + k * Math.cos(this.direction)),
y: Math.floor(this.pos.y + 9 + k * Math.sin(this.direction))
x: Math.floor(this.pos.x + this.width / 2 + k * Math.cos(this.direction)),
y: Math.floor(this.pos.y + this.height / 2 + k * Math.sin(this.direction))
};
const thirdSensorPos = {
x: Math.floor(this.pos.x + 18 + k * Math.cos(this.direction + Math.PI / 4)),
y: Math.floor(this.pos.y + 9 + k * Math.sin(this.direction + Math.PI / 4))
x: Math.floor(this.pos.x + this.width / 2 + k * Math.cos(this.direction + Math.PI / 4)),
y: Math.floor(this.pos.y + this.height / 2 + k * Math.sin(this.direction + Math.PI / 4))
};
const sensors = [firstSensorPos, secondSensorPos, thirdSensorPos];

Expand All @@ -70,7 +72,7 @@ export class Car {
if (map.collisionMap[sensors[i].x][sensors[i].y]) {
this.activatedSensors[i] = 0;
} else {
this.activatedSensors[i] = (SENSOR_DISTANCE - k) / SENSOR_DISTANCE;
this.activatedSensors[i] = (this.sensorRange - k) / this.sensorRange;
sensorsActivated[i] = true;
}
}
Expand Down Expand Up @@ -105,8 +107,8 @@ export class Car {
if (this.speed < 9) {
this.speed = 0;
}
if (this.speed > MAX_SPEED) {
this.speed = MAX_SPEED;
if (this.speed > this.maxSpeed) {
this.speed = this.maxSpeed;
}
this.distanceToLastCheckPoint = this.lastCheckPoint == null ? 0 : map.checkPoints[this.lastCheckPoint].distanceTo(this.pos);
this.pos.x = nextPosX;
Expand Down
289 changes: 148 additions & 141 deletions src/demos/neat/racing/race.ts
Original file line number Diff line number Diff line change
@@ -1,153 +1,160 @@
import { Selection } from "../../../genetic/methods";
import { Population } from "../../../genetic/model";
import { Activation } from "../../../neural-network/methods";
import { Network } from "../../../neural-network/model/network";
import { Car } from "./car";
import { CheckPoint } from "./checkPoint";
import * as checkPoints from "./checkPoints.json";
import { Map } from "./map";
import { Position } from "./position";
import { Vector2 } from "./vector2";
import { gameService } from "./services";

const dt = 1000 / 60;
export async function race() {
await gameService.init();
gameService.start();
}

export function race() {
const c = document.getElementById("canvas") as HTMLCanvasElement;
const ctx = c.getContext("2d")!;
// import { Selection } from "../../../genetic/methods";
// import { Population } from "../../../genetic/model";
// import { Activation } from "../../../neural-network/methods";
// import { Network } from "../../../neural-network/model/network";
// import { Car } from "./car";
// import { CheckPoint } from "./checkPoint";
// import * as checkPoints from "./checkPoints.json";
// import { Map } from "./map";
// import { Position } from "./position";
// import { Vector2 } from "./vector2";

const carImage = new Image();
carImage.src = "/assets/car.png";
const mapImage = new Image();
mapImage.src = "/assets/raceMap.png";
// const dt = 1000 / 60;

const loadImage = (image: HTMLImageElement) => new Promise<HTMLImageElement>((resolve, reject) => {
image.onload = () => {
console.log("resolved", image.src);
resolve(image);
};
if (image.complete) {
resolve(image);
}
});
// export function race() {
// const c = document.getElementById("canvas") as HTMLCanvasElement;
// const ctx = c.getContext("2d")!;

const mutate = (genes: number[]) => {
const genesCopy = genes.slice(0);
const randomIndex = Math.floor(Math.random() * genes.length);
genesCopy[randomIndex] = Math.random() * 2 - 1;
return genesCopy;
};
// const carImage = new Image();
// carImage.src = "/assets/car.png";
// const mapImage = new Image();
// mapImage.src = "/assets/raceMap.png";

run();
// const loadImage = (image: HTMLImageElement) => new Promise<HTMLImageElement>((resolve, reject) => {
// image.onload = () => {
// console.log("resolved", image.src);
// resolve(image);
// };
// if (image.complete) {
// resolve(image);
// }
// });

async function run() {
try {
await loadImage(carImage);
await loadImage(mapImage);
} catch {
console.log("oups");
throw Error("failed to load images");
}
let carsPop = createPopulation();
let cars = carsPop.candidates.map(it => new Car(new Position(200, 50), Network.fromWeights(it.genes, [3, 4, 4, 2])));
for (let i = 0; i < cars.length; i++) {
carsPop.candidates[i].fitness = () => {
return cars[i].checkPoints * 1000;
// return cars[i].pos.distanceTo(new Position(200, 50));
// return 1 - (cars[i].pos.x + cars[i].pos.y);
};
}
let raceTime = 0;
ctx.drawImage(mapImage, 0, 0);
const map = loadMap(mapImage, checkPoints);
let generation = 0;
// const mutate = (genes: number[]) => {
// const genesCopy = genes.slice(0);
// const randomIndex = Math.floor(Math.random() * genes.length);
// genesCopy[randomIndex] = Math.random() * 2 - 1;
// return genesCopy;
// };

while (generation < 30) {
if (raceTime > 10000 ) {
console.log(generation);
generation++;
raceTime = 0;
carsPop = carsPop.createNextGeneration();
cars = carsPop.candidates.map(
it => new Car(new Position(200, 50), Network.fromWeights(it.genes, [3, 4, 4, 2]))
);
for (let i = 0; i < cars.length; i++) {
carsPop.candidates[i].fitness = () => {
return cars[i].checkPoints * 1000;
};
}
}
cars.map(it => {
it.update(map, dt / 1000);
});
// cars[0].draw(ctx, carImage);
raceTime = raceTime + dt;
}
requestAnimationFrame(loop);
// run();

let t0 = Date.now();
function loop() {
if (raceTime > 60000) {
generation++;
console.log("generation", generation, Date.now() - t0, raceTime);
t0 = Date.now();
raceTime = 0;
console.table(carsPop.candidates.sort((a, b) => b.fitness([]) - a.fitness([])).slice(0, 10).map(it => it.fitness([])));
carsPop = carsPop.createNextGeneration();
cars = carsPop.candidates.map(
it => new Car(new Position(200, 50), Network.fromWeights(it.genes, [3, 4, 4, 2]))
);
for (let i = 0; i < cars.length; i++) {
carsPop.candidates[i].fitness = () => {
return cars[i].checkPoints * 1000;
};
}
}
ctx.clearRect(0, 0, c.width, c.height);
drawCheckPoints(map.checkPoints);
ctx.drawImage(mapImage, 0, 0);
cars.map(it => {
it.update(map, dt / 1000);
it.draw(ctx, carImage);
});
// cars[0].draw(ctx, carImage);
raceTime = raceTime + dt;
// const waitTime = Math.max(0, (1000 / 60) - dt);
// const waitTime = dt;
requestAnimationFrame(loop);
}
}
// async function run() {
// try {
// await loadImage(carImage);
// await loadImage(mapImage);
// } catch {
// console.log("oups");
// throw Error("failed to load images");
// }
// let carsPop = createPopulation();
// let cars = carsPop.candidates.map(it => new Car(new Position(200, 50), Network.fromWeights(it.genes, [3, 4, 4, 2])));
// for (let i = 0; i < cars.length; i++) {
// carsPop.candidates[i].fitness = () => {
// return cars[i].checkPoints * 1000;
// // return cars[i].pos.distanceTo(new Position(200, 50));
// // return 1 - (cars[i].pos.x + cars[i].pos.y);
// };
// }
// let raceTime = 0;
// ctx.drawImage(mapImage, 0, 0);
// const map = loadMap(mapImage, checkPoints);
// let generation = 0;

function createPopulation() {
return Population.generatePopulation(
100,
() => Network.perceptron([3, 4, 4, 2]).weights,
{
mutate,
mutationProbability: 0.4
}
);
}
// while (generation < 30) {
// if (raceTime > 10000 ) {
// console.log(generation);
// generation++;
// raceTime = 0;
// carsPop = carsPop.createNextGeneration();
// cars = carsPop.candidates.map(
// it => new Car(new Position(200, 50), Network.fromWeights(it.genes, [3, 4, 4, 2]))
// );
// for (let i = 0; i < cars.length; i++) {
// carsPop.candidates[i].fitness = () => {
// return cars[i].checkPoints * 1000;
// };
// }
// }
// cars.map(it => {
// it.update(map, dt / 1000);
// });
// // cars[0].draw(ctx, carImage);
// raceTime = raceTime + dt;
// }
// requestAnimationFrame(loop);

function loadMap(image: HTMLImageElement, checkPointsJson: any) {
const collisionMap: number[][] = [];
for (let x = 0; x < image.width; x ++) {
const row = [];
for (let y = 0; y < image.height; y ++) {
row.push(ctx.getImageData(x, y, 1, 1).data[3] === 0 ? 1 : 0);
}
collisionMap.push(row);
}
const checkPointArray: CheckPoint[] = checkPointsJson.map((it: any) => CheckPoint.fromJson(it));
return new Map(collisionMap, checkPointArray);
}
// let t0 = Date.now();
// function loop() {
// if (raceTime > 60000) {
// generation++;
// console.log("generation", generation, Date.now() - t0, raceTime);
// t0 = Date.now();
// raceTime = 0;
// console.table(carsPop.candidates.sort((a, b) => b.fitness([]) - a.fitness([])).slice(0, 10).map(it => it.fitness([])));
// carsPop = carsPop.createNextGeneration();
// cars = carsPop.candidates.map(
// it => new Car(new Position(200, 50), Network.fromWeights(it.genes, [3, 4, 4, 2]))
// );
// for (let i = 0; i < cars.length; i++) {
// carsPop.candidates[i].fitness = () => {
// return cars[i].checkPoints * 1000;
// };
// }
// }
// ctx.clearRect(0, 0, c.width, c.height);
// drawCheckPoints(map.checkPoints);
// ctx.drawImage(mapImage, 0, 0);
// cars.map(it => {
// it.update(map, dt / 1000);
// it.draw(ctx, carImage);
// });
// // cars[0].draw(ctx, carImage);
// raceTime = raceTime + dt;
// // const waitTime = Math.max(0, (1000 / 60) - dt);
// // const waitTime = dt;
// requestAnimationFrame(loop);
// }
// }

function drawCheckPoints(checkPointArray: CheckPoint[]) {
checkPointArray.map(it => {
ctx.beginPath();
ctx.strokeStyle = "green";
ctx.moveTo(it.pos1.x, it.pos1.y);
ctx.lineTo(it.pos2.x, it.pos2.y);
ctx.stroke();
});
}
}
// function createPopulation() {
// return Population.generatePopulation(
// 100,
// () => Network.perceptron([3, 4, 4, 2]).weights,
// {
// mutate,
// mutationProbability: 0.4
// }
// );
// }

// function loadMap(image: HTMLImageElement, checkPointsJson: any) {
// const collisionMap: number[][] = [];
// for (let x = 0; x < image.width; x ++) {
// const row = [];
// for (let y = 0; y < image.height; y ++) {
// row.push(ctx.getImageData(x, y, 1, 1).data[3] === 0 ? 1 : 0);
// }
// collisionMap.push(row);
// }
// const checkPointArray: CheckPoint[] = checkPointsJson.map((it: any) => CheckPoint.fromJson(it));
// return new Map(collisionMap, checkPointArray);
// }

// function drawCheckPoints(checkPointArray: CheckPoint[]) {
// checkPointArray.map(it => {
// ctx.beginPath();
// ctx.strokeStyle = "green";
// ctx.moveTo(it.pos1.x, it.pos1.y);
// ctx.lineTo(it.pos2.x, it.pos2.y);
// ctx.stroke();
// });
// }
// }
Loading

0 comments on commit 8a17699

Please sign in to comment.