-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsketch.js
160 lines (149 loc) · 4.67 KB
/
sketch.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
const MAX_POPULATION = 20;
const MAX_FOOD = 50;
const OBSTACLE_RATIO = 0.12;
const CAR_SIZE = 30;
const OBSTACLE_SIZE = 50;
const FOOD_SIZE = 10;
const FOOD_VALUE = 10;
const WIDTH = 800;
const HEIGHT = 800;
let generation = 0;
let spawnPoint;
/** @type {Car[]} */
let cars = [];
/** @type {Car[]} */
let aliveCars = [];
let obstacles = [];
const limits = [];
let timer = 0;
let foodHasBeenEaten = false;
let bestSpan = null;
let onlyDisplayBest = false;
// Using CPU for optimization
tf.setBackend('cpu');
function setup() {
createCanvas(WIDTH, HEIGHT);
spawnPoint = {
x: WIDTH / 2,
y: HEIGHT / 2,
size: OBSTACLE_SIZE * 2,
};
for (let i = 0; i < MAX_POPULATION; i++) {
const car = new Car(CAR_SIZE, WIDTH / 2, HEIGHT / 2, 0.5, 4, 120);
cars.push(car);
}
const generationButton = createButton('New');
const clearButton = createButton('Clear');
const onlyBestButton = createButton('Watch Only Best');
generationButton.mousePressed(generateGeneration);
clearButton.mousePressed(clearObstacles);
onlyBestButton.mousePressed(() => {
onlyDisplayBest = !onlyDisplayBest;
return false;
});
bestSpan = createSpan();
aliveCars = cars;
// Generate limits
generateLimits();
// Random obstacles
generateRandomObstacles();
// Food
generateFood();
obstacles.push(...limits);
textSize(width / 3);
}
function draw() {
background(100);
// Logic
aliveCars.forEach((car) => car.update(obstacles));
aliveCars.forEach((car) => {
for (const obstacle of obstacles) {
if (car.distance(obstacle.x, obstacle.y) < obstacle.size / 2) {
car.dead = true;
return;
}
}
for (const food of car.foods) {
if (!food.eaten && car.distance(food.x, food.y) < food.size) {
car.score += food.value;
food.eaten = true;
foodHasBeenEaten = true;
timer = 0;
}
}
});
aliveCars = aliveCars.filter((car) => !car.dead);
if (aliveCars.length === 0 || timer > 100) {
generateGeneration();
return;
}
// Display
const bestCar = [...cars].sort((a, b) => b.score - a.score)[0];
aliveCars.forEach((car) => {
if (onlyDisplayBest && !bestCar.dead && car !== bestCar) return;
car.display(car === bestCar);
});
ellipse(spawnPoint.x, spawnPoint.y, spawnPoint.size); // Spawn point
obstacles.forEach((b) => b.display());
bestCar.foods.forEach((f) => f.display());
bestSpan.html('Best: ' + bestCar.score);
if (!foodHasBeenEaten) timer++;
foodHasBeenEaten = false;
}
function clearObstacles() {
obstacles = [...limits];
}
function mousePressed() {
obstacles.push(new Obstacle(mouseX, mouseY, OBSTACLE_SIZE));
// Prevent default
return false;
}
function generateFood() {
for (let i = 0; i < MAX_FOOD; i++) {
let randomX = random(OBSTACLE_SIZE, WIDTH - OBSTACLE_SIZE);
let randomY = random(OBSTACLE_SIZE, HEIGHT - OBSTACLE_SIZE);
while (dist(spawnPoint.x, spawnPoint.y, randomX, randomY) - spawnPoint.size < 0) {
randomX = random(OBSTACLE_SIZE, WIDTH - OBSTACLE_SIZE);
randomY = random(OBSTACLE_SIZE, HEIGHT - OBSTACLE_SIZE);
}
cars.forEach((car) => {
car.foods.push(new Food(randomX, randomY, FOOD_VALUE, FOOD_SIZE));
});
}
}
function generateGeneration() {
aliveCars = GeneticAlgorithm.nextGeneration(cars);
cars.forEach((car) => car.dispose());
cars = aliveCars;
timer = 0;
foods = [];
generateFood();
obstacles = [...limits];
generateRandomObstacles();
generation++;
console.log('Generation #' + generation);
}
function generateLimits() {
// Top/Down obstacles
for (let i = 0; i <= WIDTH; i += OBSTACLE_SIZE) {
limits.push(new Obstacle(i, 0, OBSTACLE_SIZE));
limits.push(new Obstacle(i, HEIGHT, OBSTACLE_SIZE));
}
// Left/Right obstacles
for (let i = OBSTACLE_SIZE; i < HEIGHT; i += OBSTACLE_SIZE) {
limits.push(new Obstacle(0, i, OBSTACLE_SIZE));
limits.push(new Obstacle(WIDTH, i, OBSTACLE_SIZE));
}
}
function generateRandomObstacles() {
for (let i = OBSTACLE_SIZE; i < WIDTH; i += OBSTACLE_SIZE) {
for (let j = OBSTACLE_SIZE; j < HEIGHT; j += OBSTACLE_SIZE) {
if (dist(spawnPoint.x, spawnPoint.y, i, j) - spawnPoint.size < 0) continue;
const noiseVal = Math.random();
if (noiseVal < OBSTACLE_RATIO) {
const newObstacle = new Obstacle(i, j, OBSTACLE_SIZE);
obstacles.push(newObstacle);
}
}
}
}