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)); } } }