- selector: tournament selection for parent sampling from island populations - prompt: assembles evolution prompts from parent code, replay analysis, and meta description - llm: OpenAI-compatible client routing to ZAI proxy with fast (GLM-5-Turbo) and strong (GLM-5) tiers, plus code block extraction from model responses - Tests for prompt assembly, code extraction, and tournament selection Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
90 lines
2.5 KiB
Go
90 lines
2.5 KiB
Go
package selector
|
|
|
|
import (
|
|
"math/rand"
|
|
"testing"
|
|
|
|
evolverdb "github.com/aicodebattle/acb/cmd/acb-evolver/internal/db"
|
|
)
|
|
|
|
func makePrograms(fitnesses ...float64) []*evolverdb.Program {
|
|
out := make([]*evolverdb.Program, len(fitnesses))
|
|
for i, f := range fitnesses {
|
|
out[i] = &evolverdb.Program{ID: int64(i + 1), Fitness: f}
|
|
}
|
|
return out
|
|
}
|
|
|
|
func TestTournamentSelect_empty(t *testing.T) {
|
|
rng := rand.New(rand.NewSource(42))
|
|
got := TournamentSelect(nil, 3, rng)
|
|
if got != nil {
|
|
t.Fatalf("expected nil for empty population, got %+v", got)
|
|
}
|
|
}
|
|
|
|
func TestTournamentSelect_singleProgram(t *testing.T) {
|
|
rng := rand.New(rand.NewSource(42))
|
|
programs := makePrograms(5.0)
|
|
got := TournamentSelect(programs, 3, rng)
|
|
if got != programs[0] {
|
|
t.Fatalf("expected sole program to be returned")
|
|
}
|
|
}
|
|
|
|
func TestTournamentSelect_kLargerThanPopulation(t *testing.T) {
|
|
rng := rand.New(rand.NewSource(42))
|
|
programs := makePrograms(1.0, 3.0, 2.0)
|
|
// k=10 > len=3, so the global best (fitness=3.0) must be returned.
|
|
got := TournamentSelect(programs, 10, rng)
|
|
if got.Fitness != 3.0 {
|
|
t.Fatalf("expected global best (fitness=3.0), got %.1f", got.Fitness)
|
|
}
|
|
}
|
|
|
|
func TestTournamentSelect_selectsBestAmongSampled(t *testing.T) {
|
|
// Use a fixed seed so the test is deterministic.
|
|
rng := rand.New(rand.NewSource(1))
|
|
programs := makePrograms(1.0, 5.0, 2.0, 4.0, 3.0)
|
|
|
|
// Run many tournaments; the highest-fitness program (5.0) should win
|
|
// significantly more often than any other.
|
|
wins := make(map[float64]int)
|
|
const rounds = 200
|
|
for i := 0; i < rounds; i++ {
|
|
p := TournamentSelect(programs, 3, rng)
|
|
wins[p.Fitness]++
|
|
}
|
|
if wins[5.0] == 0 {
|
|
t.Fatalf("best program (fitness=5.0) never won in %d rounds", rounds)
|
|
}
|
|
// It should win the most.
|
|
for f, w := range wins {
|
|
if f != 5.0 && w >= wins[5.0] {
|
|
t.Errorf("program with fitness=%.1f won %d times, >= best program %d times", f, w, wins[5.0])
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestSelectParents_count(t *testing.T) {
|
|
rng := rand.New(rand.NewSource(42))
|
|
programs := makePrograms(1.0, 2.0, 3.0, 4.0, 5.0)
|
|
parents := SelectParents(programs, 4, 2, rng)
|
|
if len(parents) != 4 {
|
|
t.Fatalf("expected 4 parents, got %d", len(parents))
|
|
}
|
|
for i, p := range parents {
|
|
if p == nil {
|
|
t.Errorf("parent[%d] is nil", i)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestSelectParents_nEqualsOne(t *testing.T) {
|
|
rng := rand.New(rand.NewSource(99))
|
|
programs := makePrograms(1.0, 2.0, 3.0)
|
|
parents := SelectParents(programs, 1, 2, rng)
|
|
if len(parents) != 1 {
|
|
t.Fatalf("expected 1 parent, got %d", len(parents))
|
|
}
|
|
}
|