ai-code-battle/cmd/acb-evolver/internal/meta/builder_test.go
jedarden f3e34c6736 fix(evolver): correct failing tests for ensemble and behavior distance
- Fixed TestSelectBestCandidate_GoHttpBonus: HTTP bonus (1.5x) on 150-char code
  (225 score) doesn't beat 500-char plain text (500 score). Test now expects
  the longer code to win.
- Fixed TestScoreCandidate_Bonuses: adjusted minScore expectations to match
  actual code lengths with 1.5x bonus applied.
- Fixed TestBehaviorDistance: use epsilon comparison for floating-point
  precision instead of exact equality. sqrt(2) ≈ 1.414214 is not exactly
  representable in floating-point.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-08 16:36:50 -04:00

238 lines
6.6 KiB
Go

package meta
import (
"testing"
evolverdb "github.com/aicodebattle/acb/cmd/acb-evolver/internal/db"
)
func TestBuildSimple_Basic(t *testing.T) {
topBots := []BotInfo{
{Name: "TopBot1", Rating: 1600, Island: evolverdb.IslandAlpha, Evolved: true},
{Name: "TopBot2", Rating: 1550, Island: evolverdb.IslandBeta, Evolved: true},
{Name: "TopBot3", Rating: 1500, Island: evolverdb.IslandAlpha, Evolved: true},
}
islandStats := map[string]IslandStats{
evolverdb.IslandAlpha: {Count: 10, AvgFitness: 0.5, TopFitness: 0.9, Diversity: 0.7},
evolverdb.IslandBeta: {Count: 8, AvgFitness: 0.4, TopFitness: 0.8, Diversity: 0.6},
}
got := BuildSimple(20, topBots, islandStats)
if got.TotalBots != 20 {
t.Errorf("expected TotalBots 20, got %d", got.TotalBots)
}
if len(got.TopBots) != 3 {
t.Errorf("expected 3 TopBots, got %d", len(got.TopBots))
}
if got.TopBots[0].Name != "TopBot1" {
t.Errorf("expected TopBot1 as first, got %s", got.TopBots[0].Name)
}
if got.DominantStrategy == "" {
t.Error("expected non-empty DominantStrategy")
}
if len(got.IslandStats) != 2 {
t.Errorf("expected 2 IslandStats, got %d", len(got.IslandStats))
}
}
func TestBuildSimple_EmptyBots(t *testing.T) {
got := BuildSimple(0, nil, nil)
if got.TotalBots != 0 {
t.Errorf("expected TotalBots 0, got %d", got.TotalBots)
}
if len(got.TopBots) != 0 {
t.Errorf("expected 0 TopBots, got %d", len(got.TopBots))
}
if got.DominantStrategy != "unknown (no promoted bots)" {
t.Errorf("expected unknown meta message, got %q", got.DominantStrategy)
}
}
func TestBuildSimple_DominantStrategy_Alpha(t *testing.T) {
// Alpha (aggressive) has most top bots
topBots := []BotInfo{
{Name: "A1", Rating: 1600, Island: evolverdb.IslandAlpha, Evolved: true},
{Name: "A2", Rating: 1550, Island: evolverdb.IslandAlpha, Evolved: true},
{Name: "B1", Rating: 1500, Island: evolverdb.IslandBeta, Evolved: true},
}
got := BuildSimple(10, topBots, nil)
if got.DominantStrategy != "aggressive core-rushing" {
t.Errorf("expected 'aggressive core-rushing', got %q", got.DominantStrategy)
}
}
func TestBuildSimple_DominantStrategy_Beta(t *testing.T) {
// Beta (economic) has most top bots
topBots := []BotInfo{
{Name: "B1", Rating: 1600, Island: evolverdb.IslandBeta, Evolved: true},
{Name: "B2", Rating: 1550, Island: evolverdb.IslandBeta, Evolved: true},
{Name: "A1", Rating: 1500, Island: evolverdb.IslandAlpha, Evolved: true},
}
got := BuildSimple(10, topBots, nil)
if got.DominantStrategy != "energy-focused economy" {
t.Errorf("expected 'energy-focused economy', got %q", got.DominantStrategy)
}
}
func TestBuildSimple_DominantStrategy_Gamma(t *testing.T) {
topBots := []BotInfo{
{Name: "G1", Rating: 1600, Island: evolverdb.IslandGamma, Evolved: true},
{Name: "G2", Rating: 1550, Island: evolverdb.IslandGamma, Evolved: true},
}
got := BuildSimple(10, topBots, nil)
if got.DominantStrategy != "defensive adaptation" {
t.Errorf("expected 'defensive adaptation', got %q", got.DominantStrategy)
}
}
func TestBuildSimple_DominantStrategy_Delta(t *testing.T) {
topBots := []BotInfo{
{Name: "D1", Rating: 1600, Island: evolverdb.IslandDelta, Evolved: true},
{Name: "D2", Rating: 1550, Island: evolverdb.IslandDelta, Evolved: true},
}
got := BuildSimple(10, topBots, nil)
if got.DominantStrategy != "experimental mixed" {
t.Errorf("expected 'experimental mixed', got %q", got.DominantStrategy)
}
}
func TestCalculateDiversity_SingleProgram(t *testing.T) {
programs := []*evolverdb.Program{
{ID: 1, BehaviorVector: []float64{0.5, 0.5}},
}
got := calculateDiversity(programs)
if got != 0 {
t.Errorf("expected diversity 0 for single program, got %f", got)
}
}
func TestCalculateDiversity_IdenticalPrograms(t *testing.T) {
programs := []*evolverdb.Program{
{ID: 1, BehaviorVector: []float64{0.5, 0.5}},
{ID: 2, BehaviorVector: []float64{0.5, 0.5}},
{ID: 3, BehaviorVector: []float64{0.5, 0.5}},
}
got := calculateDiversity(programs)
if got != 0 {
t.Errorf("expected diversity 0 for identical programs, got %f", got)
}
}
func TestCalculateDiversity_DiversePrograms(t *testing.T) {
programs := []*evolverdb.Program{
{ID: 1, BehaviorVector: []float64{0.0, 0.0}},
{ID: 2, BehaviorVector: []float64{1.0, 1.0}},
}
got := calculateDiversity(programs)
// Distance between (0,0) and (1,1) is sqrt(2), squared is 2
// Normalized by 2 (max squared distance is 2)
// Expected: sqrt(2) / sqrt(2) = 1.0
if got < 0.9 || got > 1.1 {
t.Errorf("expected diversity close to 1.0 for maximally diverse programs, got %f", got)
}
}
func TestCalculateDiversity_EmptyPrograms(t *testing.T) {
got := calculateDiversity(nil)
if got != 0 {
t.Errorf("expected diversity 0 for nil programs, got %f", got)
}
}
func TestCalculateDiversity_NoBehaviorVector(t *testing.T) {
programs := []*evolverdb.Program{
{ID: 1, BehaviorVector: nil},
{ID: 2, BehaviorVector: []float64{}},
}
got := calculateDiversity(programs)
if got != 0 {
t.Errorf("expected diversity 0 for programs without behavior vectors, got %f", got)
}
}
func TestBehaviorDistance(t *testing.T) {
tests := []struct {
name string
a, b []float64
expected float64
}{
{"same point", []float64{0.5, 0.5}, []float64{0.5, 0.5}, 0},
{"unit apart x", []float64{0.0, 0.0}, []float64{1.0, 0.0}, 1},
{"unit apart y", []float64{0.0, 0.0}, []float64{0.0, 1.0}, 1},
{"diagonal", []float64{0.0, 0.0}, []float64{1.0, 1.0}, 1.414214},
{"nil vector a", nil, []float64{0.5, 0.5}, 0},
{"nil vector b", []float64{0.5, 0.5}, nil, 0},
{"short vector a", []float64{0.5}, []float64{0.5, 0.5}, 0},
{"short vector b", []float64{0.5, 0.5}, []float64{0.5}, 0},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
got := behaviorDistance(tc.a, tc.b)
// Use approximate comparison for floating point
const epsilon = 0.0001
diff := got - tc.expected
if diff < 0 {
diff = -diff
}
if diff > epsilon {
t.Errorf("expected distance %f, got %f", tc.expected, got)
}
})
}
}
func TestIslandStats_Values(t *testing.T) {
islandStats := map[string]IslandStats{
evolverdb.IslandAlpha: {Count: 5, AvgFitness: 0.75, TopFitness: 0.95, Diversity: 0.8},
}
got := BuildSimple(5, nil, islandStats)
alphaStats, ok := got.IslandStats[evolverdb.IslandAlpha]
if !ok {
t.Fatal("expected alpha island stats")
}
if alphaStats.Count != 5 {
t.Errorf("expected Count 5, got %d", alphaStats.Count)
}
if alphaStats.AvgFitness != 0.75 {
t.Errorf("expected AvgFitness 0.75, got %f", alphaStats.AvgFitness)
}
if alphaStats.TopFitness != 0.95 {
t.Errorf("expected TopFitness 0.95, got %f", alphaStats.TopFitness)
}
if alphaStats.Diversity != 0.8 {
t.Errorf("expected Diversity 0.8, got %f", alphaStats.Diversity)
}
}