ai-code-battle/cmd/acb-evolver/internal/db/seed.go
jedarden 5669688984 Add validation pipeline, sandbox, and evolution DB layer (Phase 7)
Three-stage fail-fast validator for LLM-generated bot candidates:
- syntax.go: language-aware parse (go/parser for Go; py_compile, rustfmt,
  tsc, javac, php -l for others; brace-balance fallback)
- schema.go: regex detection of /health + /turn endpoints and "moves" field
- sandbox.go: nsjail-isolated smoke test — builds bot, polls /health, sends
  5 signed /turn requests, verifies JSON moves responses
- validator.go: orchestrates stages with fail-fast short-circuit

DB layer:
- programs table + CRUD (create, get, list, updateFitness, setPromoted)
- validation_log table with RecordValidation, IslandPassRates,
  IslandValidationStats for per-island pass-rate tracking
- seed.go: 6 generation-0 bots across alpha/beta/gamma/delta islands

MAP-Elites grid (mapelites/grid.go): 2-D behavior grid on aggression×economy
axes; TryPlace keeps the fittest occupant per niche.

acb-evolver CLI gains two new subcommands:
  validate <file> -lang <lang> [-island <island>] [-nsjail] [-nolog]
  validation-stats (tabular per-island pass-rate breakdown)

cmd/acb-api/db.go: add programs table to API schema so the API can query
promoted evolved bots.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 22:45:13 -04:00

126 lines
2.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package db
import (
"context"
_ "embed"
"fmt"
)
//go:embed seeds/gatherer_strategy.go.txt
var gathererCode string
//go:embed seeds/rusher_strategy.rs.txt
var rusherCode string
//go:embed seeds/swarm_strategy.ts.txt
var swarmCode string
//go:embed seeds/guardian_strategy.php.txt
var guardianCode string
//go:embed seeds/hunter_strategy.java.txt
var hunterCode string
//go:embed seeds/random_main.py.txt
var randomCode string
// seedProgram describes a built-in strategy bot used to bootstrap the
// programs database.
type seedProgram struct {
name string
language string
island string
aggression float64 // behavior_vector[0]
economy float64 // behavior_vector[1]
code string
}
// seeds is the initial population of 6 built-in strategy bots distributed
// across all 4 islands. Each bot is assigned a behavior vector that captures
// its play-style on the aggression × economy axes.
var seeds = []seedProgram{
// beta island economic strategies
{
name: "gatherer",
language: "go",
island: IslandBeta,
aggression: 0.1,
economy: 0.9,
code: gathererCode,
},
{
name: "guardian",
language: "php",
island: IslandBeta,
aggression: 0.2,
economy: 0.6,
code: guardianCode,
},
// alpha island aggressive strategies
{
name: "rusher",
language: "rust",
island: IslandAlpha,
aggression: 0.9,
economy: 0.2,
code: rusherCode,
},
{
name: "swarm",
language: "typescript",
island: IslandAlpha,
aggression: 0.6,
economy: 0.5,
code: swarmCode,
},
// gamma island adaptive / hunting strategies
{
name: "hunter",
language: "java",
island: IslandGamma,
aggression: 0.7,
economy: 0.3,
code: hunterCode,
},
// delta island baseline / experimental
{
name: "random",
language: "python",
island: IslandDelta,
aggression: 0.3,
economy: 0.4,
code: randomCode,
},
}
// SeedPopulation inserts the 6 built-in strategy bots as generation-0
// programs if the programs table is empty. It is idempotent: a second call
// is a no-op.
func SeedPopulation(ctx context.Context, s *Store) (int, error) {
total, err := s.TotalCount(ctx)
if err != nil {
return 0, fmt.Errorf("check existing programs: %w", err)
}
if total > 0 {
return 0, nil
}
inserted := 0
for _, seed := range seeds {
p := &Program{
Code: seed.code,
Language: seed.language,
Island: seed.island,
Generation: 0,
ParentIDs: []int64{},
BehaviorVector: []float64{seed.aggression, seed.economy},
Fitness: 0.0,
Promoted: false,
}
if _, err := s.Create(ctx, p); err != nil {
return inserted, fmt.Errorf("seed %s: %w", seed.name, err)
}
inserted++
}
return inserted, nil
}