package game import ( "io" "log/slog" "testing" "time" "github.com/denisovdennis/autohero/internal/model" ) func TestResolveCombat_MatchesEngineOutcome(t *testing.T) { baseHero := &model.Hero{ ID: 1, Level: 5, MaxHP: 320, HP: 320, Attack: 25, Defense: 8, Speed: 1.0, Strength: 10, Constitution: 12, Agility: 8, Luck: 5, Potions: 0, State: model.StateWalking, } tmpl := model.EnemyTemplates[model.EnemyWolf] enemy := ScaleEnemyTemplate(tmpl, baseHero.Level) logger := slog.New(slog.NewTextHandler(io.Discard, nil)) eventCh := make(chan model.CombatEvent, 8) engine := NewEngine(100*time.Millisecond, eventCh, logger) heroEngine := *baseHero enemyEngine := enemy engine.StartCombat(&heroEngine, &enemyEngine) now := time.Now() for i := 0; i < 20000; i++ { now = now.Add(100 * time.Millisecond) engine.processCombatTick(now) if _, ok := engine.GetCombat(heroEngine.ID); !ok { break } } heroResolve := *baseHero enemyResolve := enemy survived := ResolveCombatToEnd(&heroResolve, &enemyResolve, time.Now(), CombatSimOptions{ TickRate: 100 * time.Millisecond, }) engineSurvived := heroEngine.HP > 0 if survived != engineSurvived { t.Fatalf("survival mismatch: resolve=%v engine=%v", survived, engineSurvived) } // Final HP can differ: engine applies debuff ticks for all combats before the attack queue each tick, // while ResolveCombatToEnd interleaves tick vs attacks on one timeline. Survival is the parity signal. }