ai-code-battle/cmd/acb-evolver/internal/replay/analyzer_test.go
jedarden ea04f4debb style: apply gofmt alignment fixes across codebase
Tab/space alignment consistency from running gofmt on all packages.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 10:40:33 -04:00

306 lines
6.9 KiB
Go

package replay
import (
"testing"
"time"
"github.com/aicodebattle/acb/engine"
)
func TestAnalyzer_Analyze_NilReplay(t *testing.T) {
a := NewAnalyzer()
got := a.Analyze(nil)
if got != nil {
t.Errorf("expected nil for nil replay, got %+v", got)
}
}
func TestAnalyzer_Analyze_BasicMatch(t *testing.T) {
a := NewAnalyzer()
replay := &engine.Replay{
FormatVersion: "1.0",
MatchID: "test-match-001",
StartTime: time.Now(),
EndTime: time.Now(),
Result: &engine.MatchResult{
Winner: 0,
Reason: "dominance",
Turns: 150,
Scores: []int{120, 45},
Energy: []int{15, 8},
BotsAlive: []int{8, 2},
},
Players: []engine.ReplayPlayer{
{ID: 0, Name: "WinnerBot"},
{ID: 1, Name: "LoserBot"},
},
Map: engine.ReplayMap{
Rows: 60,
Cols: 60,
},
Turns: []engine.ReplayTurn{
{
Turn: 0,
Bots: []engine.ReplayBot{},
Scores: []int{0, 0},
},
{
Turn: 50,
Bots: []engine.ReplayBot{},
Scores: []int{40, 20},
},
{
Turn: 100,
Bots: []engine.ReplayBot{},
Scores: []int{80, 35},
},
{
Turn: 150,
Bots: []engine.ReplayBot{},
Scores: []int{120, 45},
},
},
}
got := a.Analyze(replay)
if got == nil {
t.Fatal("expected non-nil analysis")
}
if got.MatchID != "test-match-001" {
t.Errorf("expected MatchID 'test-match-001', got %q", got.MatchID)
}
if got.WinnerName != "WinnerBot" {
t.Errorf("expected WinnerName 'WinnerBot', got %q", got.WinnerName)
}
if got.LoserName != "LoserBot" {
t.Errorf("expected LoserName 'LoserBot', got %q", got.LoserName)
}
if got.Condition != "dominance" {
t.Errorf("expected Condition 'dominance', got %q", got.Condition)
}
if got.TurnCount != 4 {
t.Errorf("expected TurnCount 4 (number of turn records), got %d", got.TurnCount)
}
}
func TestAnalyzer_Analyze_EliminationMatch(t *testing.T) {
a := NewAnalyzer()
replay := &engine.Replay{
MatchID: "elimination-match",
Result: &engine.MatchResult{
Winner: 1,
Reason: "elimination",
Turns: 75,
Scores: []int{10, 85},
BotsAlive: []int{0, 6},
},
Players: []engine.ReplayPlayer{
{ID: 0, Name: "EliminatedBot"},
{ID: 1, Name: "VictorBot"},
},
Turns: []engine.ReplayTurn{
{Turn: 0, Bots: []engine.ReplayBot{}},
{Turn: 30, Bots: []engine.ReplayBot{
{ID: 1, Owner: 0, Alive: true},
{ID: 2, Owner: 1, Alive: true},
{ID: 3, Owner: 1, Alive: true},
}},
{Turn: 60, Bots: []engine.ReplayBot{
{ID: 2, Owner: 1, Alive: true},
{ID: 3, Owner: 1, Alive: true},
{ID: 4, Owner: 1, Alive: true},
}},
{Turn: 75, Bots: []engine.ReplayBot{
{ID: 2, Owner: 1, Alive: true},
{ID: 3, Owner: 1, Alive: true},
{ID: 4, Owner: 1, Alive: true},
{ID: 5, Owner: 1, Alive: true},
{ID: 6, Owner: 1, Alive: true},
{ID: 7, Owner: 1, Alive: true},
}},
},
}
got := a.Analyze(replay)
if got.WinnerName != "VictorBot" {
t.Errorf("expected WinnerName 'VictorBot', got %q", got.WinnerName)
}
if got.LoserName != "EliminatedBot" {
t.Errorf("expected LoserName 'EliminatedBot', got %q", got.LoserName)
}
if got.Condition != "elimination" {
t.Errorf("expected Condition 'elimination', got %q", got.Condition)
}
// Should detect elimination strategy
foundElimination := false
for _, s := range got.Strategies {
if s == "complete elimination of opponent" {
foundElimination = true
break
}
}
if !foundElimination {
t.Error("expected 'complete elimination of opponent' in strategies")
}
// Should detect vulnerability to early aggression
foundVulnerable := false
for _, w := range got.Weaknesses {
if w == "vulnerable to early aggression" {
foundVulnerable = true
break
}
}
if !foundVulnerable {
t.Errorf("expected 'vulnerable to early aggression' in weaknesses, got %v", got.Weaknesses)
}
}
func TestAnalyzer_Analyze_DrawMatch(t *testing.T) {
a := NewAnalyzer()
replay := &engine.Replay{
MatchID: "draw-match",
Result: &engine.MatchResult{
Winner: -1,
Reason: "draw",
Turns: 500,
Scores: []int{100, 100},
},
Players: []engine.ReplayPlayer{
{ID: 0, Name: "Bot1"},
{ID: 1, Name: "Bot2"},
},
Turns: []engine.ReplayTurn{
{Turn: 0, Bots: []engine.ReplayBot{}},
{Turn: 500, Bots: []engine.ReplayBot{}},
},
}
got := a.Analyze(replay)
if got.WinnerName != "" {
t.Errorf("expected empty WinnerName for draw, got %q", got.WinnerName)
}
if got.LoserName != "" {
t.Errorf("expected empty LoserName for draw, got %q", got.LoserName)
}
if got.Condition != "draw" {
t.Errorf("expected Condition 'draw', got %q", got.Condition)
}
}
func TestAnalyzer_Analyze_WithEvents(t *testing.T) {
a := NewAnalyzer()
replay := &engine.Replay{
MatchID: "eventful-match",
Result: &engine.MatchResult{
Winner: 0,
Reason: "dominance",
Turns: 200,
Scores: []int{200, 50},
},
Players: []engine.ReplayPlayer{
{ID: 0, Name: "Aggressor"},
{ID: 1, Name: "Defender"},
},
Turns: []engine.ReplayTurn{
{Turn: 0, Bots: []engine.ReplayBot{}, Events: nil},
{Turn: 30, Bots: []engine.ReplayBot{
{ID: 1, Owner: 0, Alive: true},
{ID: 2, Owner: 1, Alive: true},
}, Events: []engine.Event{
{Type: "core_captured", Turn: 30, Details: map[string]interface{}{
"attacker_id": float64(0),
"victim_id": float64(1),
}},
}},
{Turn: 60, Bots: []engine.ReplayBot{
{ID: 1, Owner: 0, Alive: true},
{ID: 2, Owner: 0, Alive: true},
{ID: 3, Owner: 0, Alive: true},
{ID: 4, Owner: 0, Alive: true},
}, Events: []engine.Event{
{Type: "energy_collected", Turn: 60, Details: nil},
{Type: "energy_collected", Turn: 60, Details: nil},
}, Scores: []int{80, 30}},
},
}
got := a.Analyze(replay)
// Should have detected key moments
if len(got.KeyMoments) == 0 {
t.Error("expected some key moments from events")
}
}
func TestDedupeMoments(t *testing.T) {
moments := []string{
"First moment",
"Second moment",
"First moment", // duplicate
"Third moment",
"Second moment", // duplicate
}
got := dedupeMoments(moments)
if len(got) != 3 {
t.Errorf("expected 3 unique moments, got %d", len(got))
}
}
func TestDedupeMoments_Limit(t *testing.T) {
moments := make([]string, 10)
for i := range moments {
moments[i] = "moment"
}
moments[0] = "unique1"
moments[1] = "unique2"
moments[2] = "unique3"
moments[3] = "unique4"
moments[4] = "unique5"
moments[5] = "unique6"
got := dedupeMoments(moments)
if len(got) > 5 {
t.Errorf("expected at most 5 moments, got %d", len(got))
}
}
func TestDedupe(t *testing.T) {
items := []string{"a", "b", "a", "c", "b", "d"}
got := dedupe(items)
if len(got) != 4 {
t.Errorf("expected 4 unique items, got %d: %v", len(got), got)
}
// Check all expected items are present
seen := make(map[string]bool)
for _, item := range got {
seen[item] = true
}
for _, expected := range []string{"a", "b", "c", "d"} {
if !seen[expected] {
t.Errorf("expected item %q in result", expected)
}
}
}