Tab/space alignment consistency from running gofmt on all packages. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
306 lines
6.9 KiB
Go
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)
|
|
}
|
|
}
|
|
}
|