Skip to content

Instantly share code, notes, and snippets.

@LiorB-D
Created July 15, 2022 11:39
Show Gist options
  • Save LiorB-D/6cc921b53d584a801dd9d634c08fd135 to your computer and use it in GitHub Desktop.
Save LiorB-D/6cc921b53d584a801dd9d634c08fd135 to your computer and use it in GitHub Desktop.
class Car {
constructor(outerDi, innerDi, screenHt, screenWd, carDi, obsts) {
this.x =
0.5 * (screenWd - (innerDi + outerDi) / 2) + round(random() * 80) - 40; // Slight variation in starting x
this.y = screenHt / 2 - 5; // Right above the starting line
this.angle = -PI / 2; // Facing upward
//Dimensions for later usage
this.carDi = carDi;
this.outerDi = outerDi;
this.innerDi = innerDi;
this.screenHt = screenHt;
this.obsts = obsts;
this.screenWd = screenWd;
// The location of the 'eye' of the car which shows the direction it is facing
this.eyeX = this.x + (cos(this.angle) * this.carDi) / 2;
this.eyeY = this.y + (sin(this.angle) * this.carDi) / 2;
this.v = 0; // Velocity of the car
this.fitness = 0;
this.currQuad = 1 // Current quadrant of the car(Used to detect if it moves backwards)
this.dead = false;
this.compLaps = 0 // Completed Laps
this.setupModel()
}
copyWeights() {
// Turn weights from Tensorformat to array format
const weights = this.model.getWeights(); // Pull weights
const weightCopies = [];
weights.forEach((wLayer) => {
let values = wLayer.dataSync(); // turn that layer from Tensor into array
weightCopies.push(values);
});
return weightCopies;
}
setupModel() {
// Create a sequential neural network
this.model = tf.sequential();
let hidden1 = tf.layers.dense({
units: 8,
activation: "relu",
inputDim: 5,
});
let outputLayer = tf.layers.dense({
units: 4,
activation: "sigmoid",
});
this.model.add(hidden1);
this.model.add(outputLayer);
//Compile
this.model.compile({
optimizer: "adam",
loss: "binaryCrossentropy"
});
}
updatePos() { // Called at every loop
// Move based on velocity
this.x += cos(this.angle) * this.v;
this.y += sin(this.angle) * this.v;
// Update location of eye based on angle
this.eyeX = this.x + (cos(this.angle) * this.carDi) / 3;
this.eyeY = this.y + (sin(this.angle) * this.carDi) / 3;
this.v *= 0.98; // Deccelerate from Friction
this.fitness = this.updateFitness();
// Don't let the car move backwards
if (this.v < 0) {
this.v = 0;
}
}
updateFitness() {
// Geometry to calculate how far along in the track the car is(Using arctangent)
// Relative cartesian location to racetrack
let oX = this.x - this.screenWd / 2;
let oY = this.y - this.screenHt / 2;
let arctan = atan(oY / oX);
// Determine quadrant and lap progress from arctangent and relative location
let lapProg = 0;
let quad = 0;
if (oX < 0) {
if (oY < 0) {
quad = 1;
lapProg = round((50 * arctan) / PI);
} else {
quad = 4;
lapProg = round(100 + (50 * arctan) / PI);
}
} else {
lapProg = 50 + round((50 * arctan) / PI);
if (oY < 0) {
quad = 2;
} else {
quad = 3;
}
}
if (this.currQuad == 1 && quad == 4) { // If it moves backwards past the starting line
this.dead = true;
return this.fitness;
} else if (this.currQuad == 4 && quad == 1) { // If it passes the finish line
this.compLaps += 1;
}
this.currQuad = quad;
return this.compLaps * 100 + lapProg;
}
getCollisDist() {
// Calculate how close an obstacle is in each of the 5 directions
let detDist = [20, 20, 20, 20, 20];
for (let i = 0; i < 20; i++) {
//Front
let xPos = this.x + (i * 5 + this.carDi / 2) * cos(this.angle);
let yPos = this.y + (i * 5 + this.carDi / 2) * sin(this.angle);
if (this.ptInObst(xPos, yPos) && detDist[0] > i) {
detDist[0] = i;
}
//Peripheral Right
xPos = this.x + (i * 5 + this.carDi / 2) * cos(this.angle + PI / 4);
yPos = this.y + (i * 5 + this.carDi / 2) * sin(this.angle + PI / 4);
if (this.ptInObst(xPos, yPos) && detDist[1] > i) {
detDist[1] = i;
}
//Peripheral Left
xPos = this.x + (i * 5 + this.carDi / 2) * cos(this.angle - PI / 4);
yPos = this.y + (i * 5 + this.carDi / 2) * sin(this.angle - PI / 4);
if (this.ptInObst(xPos, yPos) && detDist[2] > i) {
detDist[2] = i;
}
//Side Right
xPos = this.x + (i * 5 + this.carDi / 2) * cos(this.angle + PI / 2);
yPos = this.y + (i * 5 + this.carDi / 2) * sin(this.angle + PI / 2);
if (this.ptInObst(xPos, yPos) && detDist[3] > i) {
detDist[3] = i;
}
//Side Left
xPos = this.x + (i * 5 + this.carDi / 2) * cos(this.angle - PI / 2);
yPos = this.y + (i * 5 + this.carDi / 2) * sin(this.angle - PI / 2);
if (this.ptInObst(xPos, yPos) && detDist[4] > i) {
detDist[4] = i;
}
}
return detDist;
}
ptInObst(x, y) {
// Check if a point is in an obstacle. Used for both the car and obstacle sensing
let outerXRad = this.outerDi / 2 - this.carDi / 2;
let outerYRad = this.outerDi / 3 - this.carDi / 2;
let innerXRad = this.innerDi / 2 + this.carDi / 2;
let innerYRad = this.innerDi / 4 + this.carDi / 2;
let xOffset = (x - this.screenWd / 2) ** 2;
let yOffset = (y - this.screenHt / 2) ** 2;
if (xOffset / outerXRad ** 2 + yOffset / outerYRad ** 2 > 1) {
return true;
}
if (xOffset / innerXRad ** 2 + yOffset / innerYRad ** 2 < 1) {
return true;
}
let hitObst = false;
//Obstacles
this.obsts.forEach((obst) => {
let xObstOffset = (x - obst.x) ** 2;
let yObstOffset = (y - obst.y) ** 2;
let obstRad = obst.di / 2 + this.carDi / 2;
if (xObstOffset + yObstOffset < obstRad ** 2) {
hitObst = true;
}
});
return hitObst;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment