80 lines
3.2 KiB
TypeScript
80 lines
3.2 KiB
TypeScript
// E2E Test for Bridge Builder
|
|
import { describe, it, expect } from 'bun:test';
|
|
import { BridgeSimulation } from './BridgeSimulation';
|
|
import { GeneticAlgorithm } from './GeneticAlgo';
|
|
import { DEFAULT_BRIDGE_CONFIG, DEFAULT_SIM_CONFIG, DEFAULT_GA_CONFIG } from './types';
|
|
import type { BridgeGenome } from './types';
|
|
|
|
describe('Bridge Builder E2E', () => {
|
|
it('should evolve bridges that survive longer than 12 steps', () => {
|
|
const ga = new GeneticAlgorithm(DEFAULT_GA_CONFIG);
|
|
|
|
// Create initial population
|
|
const populationSize = 10;
|
|
let population: BridgeGenome[] = [];
|
|
for (let i = 0; i < populationSize; i++) {
|
|
population.push(ga.createRandomGenome(3, 8));
|
|
}
|
|
|
|
console.log('\n=== Bridge Builder E2E Test ===\n');
|
|
|
|
// Check first genome structure
|
|
console.log('First genome sample:');
|
|
console.log(` Nodes: ${population[0].nodes.length}`, population[0].nodes.slice(0, 3));
|
|
console.log(` Beams: ${population[0].beams.length}`, population[0].beams.slice(0, 5));
|
|
|
|
// Evolve for 50 generations
|
|
for (let gen = 1; gen <= 50; gen++) {
|
|
const fitnesses: number[] = [];
|
|
const steps: number[] = [];
|
|
|
|
// Evaluate each genome
|
|
for (const genome of population) {
|
|
const sim = new BridgeSimulation(genome, DEFAULT_BRIDGE_CONFIG, DEFAULT_SIM_CONFIG);
|
|
sim.run(DEFAULT_SIM_CONFIG.maxSteps);
|
|
|
|
const result = sim.getResult();
|
|
fitnesses.push(result.fitness);
|
|
steps.push(result.stepsSupported);
|
|
}
|
|
|
|
const maxFitness = Math.max(...fitnesses);
|
|
const avgFitness = fitnesses.reduce((a, b) => a + b, 0) / fitnesses.length;
|
|
const bestIdx = fitnesses.indexOf(maxFitness);
|
|
const maxSteps = Math.max(...steps);
|
|
const avgSteps = steps.reduce((a, b) => a + b, 0) / steps.length;
|
|
|
|
if (gen % 10 === 0 || gen === 1) {
|
|
console.log(`Gen ${gen}:`);
|
|
console.log(` Fitness: ${avgFitness.toFixed(1)} (best: ${maxFitness.toFixed(1)})`);
|
|
console.log(` Steps: ${avgSteps.toFixed(1)} (best: ${maxSteps})`);
|
|
console.log(` Best genome: ${population[bestIdx].nodes.length} nodes, ${population[bestIdx].beams.length} beams`);
|
|
}
|
|
|
|
// Evolve population
|
|
population = ga.evolve(population, fitnesses);
|
|
}
|
|
|
|
// Final check
|
|
const finalFitnesses = population.map(genome => {
|
|
const sim = new BridgeSimulation(genome, DEFAULT_BRIDGE_CONFIG, DEFAULT_SIM_CONFIG);
|
|
sim.run(DEFAULT_SIM_CONFIG.maxSteps);
|
|
return sim.getResult();
|
|
});
|
|
|
|
const bestFinalResult = finalFitnesses.reduce((best, curr) =>
|
|
curr.fitness > best.fitness ? curr : best
|
|
);
|
|
|
|
console.log('\n=== Final Results ===');
|
|
console.log(`Best fitness: ${bestFinalResult.fitness.toFixed(1)}`);
|
|
console.log(`Steps survived: ${bestFinalResult.stepsSupported} / ${DEFAULT_SIM_CONFIG.maxSteps}`);
|
|
console.log(`Collapsed: ${bestFinalResult.collapsed}`);
|
|
console.log(`Beams: ${bestFinalResult.beamCount}`);
|
|
|
|
// Test assertions
|
|
expect(bestFinalResult.stepsSupported).toBeGreaterThan(12);
|
|
expect(bestFinalResult.fitness).toBeGreaterThan(1200);
|
|
});
|
|
});
|