From 08c9a27266df57c72f84bce3766e590c9cdc0aab Mon Sep 17 00:00:00 2001 From: Michael Peters Date: Thu, 29 Aug 2024 15:54:33 -0700 Subject: [PATCH] add tests for computeNextGeneration --- src/site/snake/neat.ts | 8 ++--- src/test/test-neat.ts | 69 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 6 deletions(-) diff --git a/src/site/snake/neat.ts b/src/site/snake/neat.ts index 7a0e9da..65a5061 100644 --- a/src/site/snake/neat.ts +++ b/src/site/snake/neat.ts @@ -228,7 +228,7 @@ * - Effectively pass data from inputs, through hidden nodes, to outputs */ -import { edgesToNodes, NodeID, Network, Node, topoSort, traceParents, RawEdge } from './network'; +import { edgesToNodes, NodeID, traceParents, RawEdge } from './network'; import { keyMax, mapInvert, mapMap, randchoice, randint, randomNegPos, setDifference, setMap } from './util'; export interface GeneData { @@ -589,7 +589,7 @@ export function mutate(genome: Genome, config: MutateConfig): Genome { return newGenome; } -interface NextGenerationConfig { +export interface NextGenerationConfig { fc: FertilityConfig; mcc: MateChoiceConfig; cc: CrossConfig; @@ -624,9 +624,5 @@ export function computeNextGeneration( nextGeneration.set(baby, sid); } - // TODO: consider adding a test for this function - // TODO: consider renaming the top part of this file "neat.ts" and the - // bottom part (activate and below) to be in a new file "brain-neat.ts" - return nextGeneration; } diff --git a/src/test/test-neat.ts b/src/test/test-neat.ts index fb0be10..f61d642 100644 --- a/src/test/test-neat.ts +++ b/src/test/test-neat.ts @@ -13,6 +13,8 @@ import { Population, resetGlobalIDs, assignSpecies, + NextGenerationConfig, + computeNextGeneration, } from '../site/snake/neat'; import { assert, addTest, assertSetEqual, assertDeepEqual } from './tests'; @@ -405,3 +407,70 @@ function testFindAcyclicNewConns() { assertDeepEqual(options, expected); } addTest(testFindAcyclicNewConns); + +function testComputeNextGeneration() { + function makeGenome(weight: number) { + return [{ src_id: 'A', dst_id: 'B', data: { innovation: 1, weight, enabled: true } }]; + } + const genomeA = makeGenome(1); + const genomeB = makeGenome(2); + const genomeC = makeGenome(3); + const genomeD = makeGenome(4); + const genomeE = makeGenome(5); + + const population = new Map(); + population.set(genomeA, 1); + population.set(genomeB, 1); + population.set(genomeC, 1); + population.set(genomeD, 2); + population.set(genomeE, 2); + + const fitness = new Map(); + fitness.set(genomeA, 1); + fitness.set(genomeB, 2); + fitness.set(genomeC, 1); + fitness.set(genomeD, 1); + fitness.set(genomeE, 2); + + const cngc: NextGenerationConfig = { + fc: { + fertile_threshold: 0.6, + champion_min_species_size: 3, + }, + mcc: { + asexual_rate: 0.5, + interspecies_rate: 0.2, + }, + cc: { + reenable_rate: 0.25, + }, + mc: { + mutate_rate: 0.25, + assign_rate: 0.1, + assign_mag: 1.0, + perturb_mag: 0.1, + new_node_rate: 0.2, + new_connection_rate: 0.1, + }, + cdc: { + c1: 1, + c2: 1, + c3: 10, + }, + cdt: 1.0, + }; + + resetGlobalIDs({ node_id: 1, innovation_number: 2, species_id: 3 }); + const ng = computeNextGeneration(population, fitness, cngc); + + // NOTE: these tests are not very detailed as this is difficult + // to test without mocks + assert(ng.size === population.size); + + const sids = new Set(ng.values()); + assert(sids.has(1)); + assert(!sids.has(2)); + // it not guaranteed that sids.has(3) since the new genomes may have the + // same species as the species 1 champ +} +addTest(testComputeNextGeneration);