ai-code-battle/cmd/acb-evolver/internal/selector/tournament.go
jedarden bd4b0d3244 Add LLM prompt builder and ensemble integration (Phase 7)
- 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>
2026-03-26 22:26:09 -04:00

55 lines
1.6 KiB
Go

// Package selector implements parent sampling strategies for the evolution
// pipeline. The primary strategy is tournament selection, which balances
// selection pressure (favoring fit individuals) with diversity (random
// sampling prevents premature convergence).
package selector
import (
"math/rand"
evolverdb "github.com/aicodebattle/acb/cmd/acb-evolver/internal/db"
)
// TournamentSelect picks the highest-fitness program from k randomly sampled
// candidates drawn without replacement from programs.
//
// When k ≥ len(programs) the function returns the globally best program.
// Returns nil when programs is empty.
func TournamentSelect(programs []*evolverdb.Program, k int, rng *rand.Rand) *evolverdb.Program {
if len(programs) == 0 {
return nil
}
// If k covers the whole population, just return the global best.
if k >= len(programs) {
best := programs[0]
for _, p := range programs[1:] {
if p.Fitness > best.Fitness {
best = p
}
}
return best
}
// Sample k distinct indices using a Fisher-Yates partial shuffle.
indices := rng.Perm(len(programs))[:k]
best := programs[indices[0]]
for _, idx := range indices[1:] {
if programs[idx].Fitness > best.Fitness {
best = programs[idx]
}
}
return best
}
// SelectParents draws n parents via tournament selection with tournament size k.
// The same program may be selected more than once (sampling with replacement
// across tournaments).
func SelectParents(programs []*evolverdb.Program, n, k int, rng *rand.Rand) []*evolverdb.Program {
parents := make([]*evolverdb.Program, n)
for i := range parents {
parents[i] = TournamentSelect(programs, k, rng)
}
return parents
}