Tab/space alignment consistency from running gofmt on all packages. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
272 lines
7.2 KiB
Go
272 lines
7.2 KiB
Go
package mapelites
|
||
|
||
import "testing"
|
||
|
||
func TestBehaviorToCell(t *testing.T) {
|
||
// 3×3×3×3 grid per §10.2
|
||
g := New(3)
|
||
|
||
cases := []struct {
|
||
agg, eco, expl, form float64
|
||
wantX, wantY, wantZ, wantW int
|
||
}{
|
||
{0.0, 0.0, 0.0, 0.0, 0, 0, 0, 0},
|
||
{1.0, 1.0, 1.0, 1.0, 2, 2, 2, 2},
|
||
{0.5, 0.5, 0.5, 0.5, 1, 1, 1, 1},
|
||
{0.15, 0.85, 0.33, 0.66, 0, 2, 0, 1},
|
||
{0.99, 0.01, 0.99, 0.01, 2, 0, 2, 0},
|
||
{0.09, 0.09, 0.09, 0.09, 0, 0, 0, 0},
|
||
{0.34, 0.67, 0.34, 0.67, 1, 2, 1, 2},
|
||
}
|
||
|
||
for _, tc := range cases {
|
||
x, y, z, w := g.BehaviorToCell(tc.agg, tc.eco, tc.expl, tc.form)
|
||
if x != tc.wantX || y != tc.wantY || z != tc.wantZ || w != tc.wantW {
|
||
t.Errorf("BehaviorToCell(%.2f, %.2f, %.2f, %.2f) = (%d,%d,%d,%d), want (%d,%d,%d,%d)",
|
||
tc.agg, tc.eco, tc.expl, tc.form, x, y, z, w, tc.wantX, tc.wantY, tc.wantZ, tc.wantW)
|
||
}
|
||
}
|
||
}
|
||
|
||
func TestTotalCells(t *testing.T) {
|
||
g := New(3)
|
||
if g.TotalCells() != 81 {
|
||
t.Errorf("3⁴ = 81 cells, got %d", g.TotalCells())
|
||
}
|
||
g10 := New(10)
|
||
if g10.TotalCells() != 10000 {
|
||
t.Errorf("10⁴ = 10000 cells, got %d", g10.TotalCells())
|
||
}
|
||
}
|
||
|
||
func TestTryPlace_EmptyCell(t *testing.T) {
|
||
g := New(3)
|
||
p, placed := g.TryPlace(1, 10.0, 0.1, 0.9, 0.5, 0.5)
|
||
if !placed {
|
||
t.Fatal("expected placement into empty cell")
|
||
}
|
||
if p.X != 0 || p.Y != 2 || p.Z != 1 || p.W != 1 {
|
||
t.Errorf("expected cell (0,2,1,1), got (%d,%d,%d,%d)", p.X, p.Y, p.Z, p.W)
|
||
}
|
||
cell := g.Get(0, 2, 1, 1)
|
||
if !cell.Occupied || cell.ProgramID != 1 || cell.Fitness != 10.0 {
|
||
t.Errorf("unexpected cell state: %+v", cell)
|
||
}
|
||
}
|
||
|
||
func TestTryPlace_LowerFitnessDoesNotReplace(t *testing.T) {
|
||
g := New(3)
|
||
g.TryPlace(1, 10.0, 0.5, 0.5, 0.5, 0.5)
|
||
|
||
_, placed := g.TryPlace(2, 5.0, 0.5, 0.5, 0.5, 0.5)
|
||
if placed {
|
||
t.Fatal("lower fitness should not replace incumbent")
|
||
}
|
||
if g.Get(1, 1, 1, 1).ProgramID != 1 {
|
||
t.Error("incumbent program 1 should still hold the cell")
|
||
}
|
||
}
|
||
|
||
func TestTryPlace_HigherFitnessReplaces(t *testing.T) {
|
||
g := New(3)
|
||
g.TryPlace(1, 10.0, 0.5, 0.5, 0.5, 0.5)
|
||
|
||
_, placed := g.TryPlace(2, 20.0, 0.5, 0.5, 0.5, 0.5)
|
||
if !placed {
|
||
t.Fatal("higher fitness should replace incumbent")
|
||
}
|
||
cell := g.Get(1, 1, 1, 1)
|
||
if cell.ProgramID != 2 || cell.Fitness != 20.0 {
|
||
t.Errorf("expected program 2 with fitness 20, got %+v", cell)
|
||
}
|
||
}
|
||
|
||
func TestTryPlace_EqualFitnessDoesNotReplace(t *testing.T) {
|
||
g := New(3)
|
||
g.TryPlace(1, 10.0, 0.5, 0.5, 0.5, 0.5)
|
||
_, placed := g.TryPlace(2, 10.0, 0.5, 0.5, 0.5, 0.5)
|
||
if placed {
|
||
t.Fatal("equal fitness should not replace incumbent")
|
||
}
|
||
}
|
||
|
||
func TestOccupiedCount(t *testing.T) {
|
||
g := New(3)
|
||
if g.OccupiedCount() != 0 {
|
||
t.Error("new grid should have 0 occupied cells")
|
||
}
|
||
g.TryPlace(1, 1.0, 0.1, 0.1, 0.1, 0.1)
|
||
g.TryPlace(2, 1.0, 0.9, 0.9, 0.9, 0.9)
|
||
g.TryPlace(3, 1.0, 0.5, 0.5, 0.5, 0.5)
|
||
if g.OccupiedCount() != 3 {
|
||
t.Errorf("expected 3 occupied cells, got %d", g.OccupiedCount())
|
||
}
|
||
// Same cell should not increase count
|
||
g.TryPlace(4, 99.0, 0.5, 0.5, 0.5, 0.5)
|
||
if g.OccupiedCount() != 3 {
|
||
t.Errorf("expected still 3 occupied cells after same-cell update, got %d", g.OccupiedCount())
|
||
}
|
||
}
|
||
|
||
func TestElite_EmptyGrid(t *testing.T) {
|
||
g := New(3)
|
||
_, found := g.Elite()
|
||
if found {
|
||
t.Fatal("empty grid should have no elite")
|
||
}
|
||
}
|
||
|
||
func TestElite(t *testing.T) {
|
||
g := New(3)
|
||
g.TryPlace(1, 5.0, 0.1, 0.1, 0.1, 0.1)
|
||
g.TryPlace(2, 15.0, 0.9, 0.9, 0.9, 0.9)
|
||
g.TryPlace(3, 10.0, 0.5, 0.5, 0.5, 0.5)
|
||
|
||
elite, found := g.Elite()
|
||
if !found {
|
||
t.Fatal("expected an elite in non-empty grid")
|
||
}
|
||
if elite.ProgramID != 2 || elite.Fitness != 15.0 {
|
||
t.Errorf("expected elite program 2 (fitness 15), got %+v", elite)
|
||
}
|
||
}
|
||
|
||
func TestAllElites(t *testing.T) {
|
||
g := New(3)
|
||
if len(g.AllElites()) != 0 {
|
||
t.Error("empty grid should return no elites")
|
||
}
|
||
|
||
g.TryPlace(1, 1.0, 0.0, 0.0, 0.0, 0.0)
|
||
g.TryPlace(2, 2.0, 0.5, 0.5, 0.5, 0.5)
|
||
g.TryPlace(3, 3.0, 1.0, 1.0, 1.0, 1.0)
|
||
|
||
elites := g.AllElites()
|
||
if len(elites) != 3 {
|
||
t.Errorf("expected 3 elites, got %d", len(elites))
|
||
}
|
||
}
|
||
|
||
func TestSeedBehaviorVectors(t *testing.T) {
|
||
// Verify that the 6 seed bots land in distinct grid cells on a 3×3×3×3 grid.
|
||
g := New(3)
|
||
|
||
bots := []struct {
|
||
id int64
|
||
name string
|
||
aggression, economy float64
|
||
exploration, formation float64
|
||
}{
|
||
{1, "gatherer", 0.1, 0.9, 0.3, 0.2},
|
||
{2, "guardian", 0.2, 0.6, 0.1, 0.8},
|
||
{3, "rusher", 0.9, 0.2, 0.5, 0.3},
|
||
{4, "swarm", 0.6, 0.5, 0.4, 0.9},
|
||
{5, "hunter", 0.7, 0.3, 0.8, 0.4},
|
||
{6, "random", 0.3, 0.4, 0.5, 0.5},
|
||
}
|
||
|
||
placed := 0
|
||
for _, b := range bots {
|
||
_, ok := g.TryPlace(b.id, 1.0, b.aggression, b.economy, b.exploration, b.formation)
|
||
if ok {
|
||
placed++
|
||
}
|
||
}
|
||
|
||
if placed != 6 {
|
||
t.Errorf("expected all 6 seed bots in distinct cells, but only %d placed", placed)
|
||
}
|
||
if g.OccupiedCount() != 6 {
|
||
t.Errorf("expected 6 occupied cells, got %d", g.OccupiedCount())
|
||
}
|
||
}
|
||
|
||
func TestSlice(t *testing.T) {
|
||
g := New(3)
|
||
|
||
// Place bots at known positions
|
||
g.TryPlace(1, 5.0, 0.1, 0.1, 0.1, 0.1) // (0,0,0,0)
|
||
g.TryPlace(2, 8.0, 0.9, 0.9, 0.1, 0.1) // (2,2,0,0)
|
||
g.TryPlace(3, 3.0, 0.5, 0.1, 0.1, 0.1) // (1,0,0,0)
|
||
|
||
// Slice: aggression×economy at z=0, w=0 (dims 2,3 fixed to 0)
|
||
slice := g.Slice(2, 0, 3, 0)
|
||
|
||
if len(slice) != 3 {
|
||
t.Fatalf("expected 3 rows in slice, got %d", len(slice))
|
||
}
|
||
if len(slice[0]) != 3 {
|
||
t.Fatalf("expected 3 cols in slice, got %d", len(slice[0]))
|
||
}
|
||
|
||
// Check (0,0) = program 1
|
||
c00 := slice[0][0]
|
||
if !c00.Occupied || c00.ProgramID != 1 {
|
||
t.Errorf("slice[0][0]: expected program 1, got %+v", c00)
|
||
}
|
||
// Check (2,2) = program 2
|
||
c22 := slice[2][2]
|
||
if !c22.Occupied || c22.ProgramID != 2 {
|
||
t.Errorf("slice[2][2]: expected program 2, got %+v", c22)
|
||
}
|
||
// Check (1,0) = program 3
|
||
c10 := slice[1][0]
|
||
if !c10.Occupied || c10.ProgramID != 3 {
|
||
t.Errorf("slice[1][0]: expected program 3, got %+v", c10)
|
||
}
|
||
}
|
||
|
||
func TestSnapshot(t *testing.T) {
|
||
g := New(3)
|
||
g.TryPlace(1, 5.0, 0.1, 0.1, 0.1, 0.1)
|
||
g.TryPlace(2, 8.0, 0.9, 0.9, 0.9, 0.9)
|
||
|
||
snap := g.Snapshot()
|
||
if snap.Size != 3 {
|
||
t.Errorf("expected size 3, got %d", snap.Size)
|
||
}
|
||
if len(snap.Cells) != 2 {
|
||
t.Errorf("expected 2 cells in snapshot, got %d", len(snap.Cells))
|
||
}
|
||
if snap.DimNames[0] != "aggression" || snap.DimNames[3] != "formation" {
|
||
t.Errorf("dim names: %v", snap.DimNames)
|
||
}
|
||
}
|
||
|
||
func TestMigration_2Dto4D(t *testing.T) {
|
||
// Simulate migrating a 2-D archive into a 4-D grid.
|
||
// Old programs had only aggression and economy.
|
||
// They should project into the 4-D grid at z=middle, w=middle.
|
||
g := New(3)
|
||
|
||
// Old 2-D program: aggression=0.5, economy=0.5
|
||
// Migrate to 4-D: exploration=0.5 (middle), formation=0.5 (middle)
|
||
middle := 0.5
|
||
g.TryPlace(1, 10.0, 0.5, 0.5, middle, middle)
|
||
|
||
cell := g.Get(1, 1, 1, 1)
|
||
if !cell.Occupied {
|
||
t.Error("migrated program should occupy (1,1,1,1)")
|
||
}
|
||
if cell.ProgramID != 1 {
|
||
t.Errorf("expected program 1, got %d", cell.ProgramID)
|
||
}
|
||
|
||
// A new 4-D program with different exploration/formation should go to a different cell
|
||
_, placed := g.TryPlace(2, 15.0, 0.5, 0.5, 0.9, 0.1)
|
||
if !placed {
|
||
t.Error("different 4-D coords should be a new cell")
|
||
}
|
||
cell2 := g.Get(1, 1, 2, 0)
|
||
if !cell2.Occupied || cell2.ProgramID != 2 {
|
||
t.Error("new program should be at (1,1,2,0)")
|
||
}
|
||
}
|
||
|
||
func TestPlacementKey(t *testing.T) {
|
||
p := Placement{X: 1, Y: 2, Z: 0, W: 2}
|
||
key := p.Key()
|
||
if key != [NumDims]int{1, 2, 0, 2} {
|
||
t.Errorf("expected [1 2 0 2], got %v", key)
|
||
}
|
||
}
|