Felladrin's picture
Add the simulator files
17fd0c2 verified
import { SpatialGrid, randomRange } from "./utils.js";
import { ENTITY_TYPES, createGenome, reproduce } from "./genetics.js";
import { Entity } from "./entities.js";
export class EntityManager {
constructor(worldWidth, worldHeight) {
this.entities = [];
this.spatialGrid = new SpatialGrid(worldWidth, worldHeight, 60);
this.worldWidth = worldWidth;
this.worldHeight = worldHeight;
}
add(entity) {
this.entities.push(entity);
}
update(dt, world, particles, stats) {
this.spatialGrid.clear();
for (const e of this.entities) {
if (e.alive) this.spatialGrid.insert(e, e.x, e.y);
}
const newborns = [];
for (const e of this.entities) {
e.update(dt, world, this.entities, this.spatialGrid, particles);
const child = e.tryReproduce(this.spatialGrid, particles);
if (child) {
newborns.push(child);
stats.onBirth();
}
}
for (const child of newborns) {
this.entities.push(child);
}
for (let i = this.entities.length - 1; i >= 0; i--) {
if (!this.entities[i].alive) {
stats.onDeath();
this.entities.splice(i, 1);
}
}
}
render(ctx) {
this.entities.sort((a, b) => a.y - b.y);
for (const e of this.entities) {
e.render(ctx);
}
}
getAlive() {
return this.entities.filter((e) => e.alive);
}
spawnRandom(count, world) {
const types = Object.keys(ENTITY_TYPES);
for (let i = 0; i < count; i++) {
const type = types[Math.floor(Math.random() * types.length)];
const genome = createGenome(type);
const x = randomRange(20, world.width - 20);
const y = randomRange(20, world.height - 20);
this.entities.push(new Entity(x, y, genome, 0));
}
}
spawnFromPopulation(population, count, world) {
if (population.length < 2) return;
const candidates = population.map((e) => ({
genome: e.genome,
fitness: e.fitness,
}));
for (let i = 0; i < count; i++) {
const childGenome = reproduce(candidates);
const x = randomRange(20, world.width - 20);
const y = randomRange(20, world.height - 20);
const gen = Math.max(...population.map((e) => e.generation)) + 1;
this.entities.push(new Entity(x, y, childGenome, gen));
}
}
}