116 lines
3.9 KiB
TypeScript
116 lines
3.9 KiB
TypeScript
import { vec2 } from 'gl-matrix';
|
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
|
|
import { appConfig } from '../config';
|
|
import { AgentGenerationPipeline } from '../pipelines/agents/agent-generation/agent-generation-pipeline';
|
|
import { settings } from '../settings';
|
|
import { AgentPopulation } from './agent-population';
|
|
|
|
vi.hoisted(() => {
|
|
Object.defineProperty(globalThis, 'localStorage', {
|
|
configurable: true,
|
|
value: {
|
|
getItem: vi.fn(() => null),
|
|
setItem: vi.fn(),
|
|
},
|
|
});
|
|
});
|
|
|
|
const originalBrushSize = settings.brushSize;
|
|
const originalSelectedColorIndex = settings.selectedColorIndex;
|
|
const originalSpawnPerPixel = settings.spawnPerPixel;
|
|
|
|
const createPopulation = () => {
|
|
const pipeline = {
|
|
maxAgentCount: 10_000_000,
|
|
writeAgents: vi.fn(),
|
|
resizeAgents: vi.fn(),
|
|
compactAgents: vi.fn(),
|
|
} as unknown as AgentGenerationPipeline;
|
|
|
|
return new AgentPopulation(pipeline);
|
|
};
|
|
|
|
const setPopulationActiveCount = (population: AgentPopulation, activeCount: number) => {
|
|
Object.assign(population as unknown as Record<string, number>, {
|
|
activeCount,
|
|
});
|
|
};
|
|
|
|
const setPopulationAdaptiveCap = (population: AgentPopulation, adaptiveCap: number) => {
|
|
Object.assign(population as unknown as Record<string, number>, {
|
|
adaptiveCap,
|
|
});
|
|
};
|
|
|
|
const getPopulationAdaptiveCap = (population: AgentPopulation): number =>
|
|
(population as unknown as { adaptiveCap: number }).adaptiveCap;
|
|
|
|
describe('AgentPopulation adaptive budget', () => {
|
|
beforeEach(() => {
|
|
settings.brushSize = 1;
|
|
settings.selectedColorIndex = 0;
|
|
settings.spawnPerPixel = 1;
|
|
});
|
|
|
|
afterEach(() => {
|
|
settings.brushSize = originalBrushSize;
|
|
settings.selectedColorIndex = originalSelectedColorIndex;
|
|
settings.spawnPerPixel = originalSpawnPerPixel;
|
|
});
|
|
|
|
it('expands beyond the 1M start cap only when new agents arrive under healthy FPS', () => {
|
|
const population = createPopulation();
|
|
setPopulationActiveCount(population, 1_000_000);
|
|
|
|
population.growBudget(1 / 60, 60);
|
|
population.spawnStrokeAgents(vec2.fromValues(0, 0), vec2.fromValues(16, 0));
|
|
|
|
expect(getPopulationAdaptiveCap(population)).toBeGreaterThan(
|
|
appConfig.simulation.budget.adaptiveCapInitial
|
|
);
|
|
expect(population.activeAgentCount).toBeGreaterThan(
|
|
appConfig.simulation.budget.adaptiveCapInitial
|
|
);
|
|
expect(getPopulationAdaptiveCap(population)).toBeLessThanOrEqual(
|
|
appConfig.simulation.budget.adaptiveCapMax
|
|
);
|
|
});
|
|
|
|
it('does not grow the cap above the adaptive max agent count', () => {
|
|
const population = createPopulation();
|
|
const maxAgentCount = appConfig.simulation.budget.adaptiveCapMax;
|
|
setPopulationAdaptiveCap(population, maxAgentCount - 1);
|
|
setPopulationActiveCount(population, maxAgentCount - 1);
|
|
|
|
population.growBudget(1 / 60, 60);
|
|
population.spawnStrokeAgents(vec2.fromValues(0, 0), vec2.fromValues(16, 0));
|
|
|
|
expect(getPopulationAdaptiveCap(population)).toBe(maxAgentCount);
|
|
expect(population.activeAgentCount).toBe(maxAgentCount);
|
|
});
|
|
|
|
it('clamps a stale cap before adding agents', () => {
|
|
const population = createPopulation();
|
|
const maxAgentCount = appConfig.simulation.budget.adaptiveCapMax;
|
|
setPopulationAdaptiveCap(population, maxAgentCount + 1_000);
|
|
setPopulationActiveCount(population, maxAgentCount);
|
|
|
|
population.spawnStrokeAgents(vec2.fromValues(0, 0), vec2.fromValues(16, 0));
|
|
|
|
expect(getPopulationAdaptiveCap(population)).toBe(maxAgentCount);
|
|
expect(population.activeAgentCount).toBe(maxAgentCount);
|
|
});
|
|
|
|
it('decreases the cap and active count slowly when FPS falls below the threshold', () => {
|
|
const population = createPopulation();
|
|
setPopulationActiveCount(population, 1_000_000);
|
|
|
|
population.growBudget(10, 50);
|
|
|
|
expect(getPopulationAdaptiveCap(population)).toBe(
|
|
appConfig.simulation.budget.adaptiveCapMin
|
|
);
|
|
expect(population.activeAgentCount).toBe(appConfig.simulation.budget.adaptiveCapMin);
|
|
});
|
|
});
|