feat(go-services): Phase 2 — combat_stats accumulator (cross-language exact)
Ports main.py's _combat_session_delta / _combat_merge_into_lifetime (incl. the documented "offense/defense use latest, additively" quirk) and the combat_stats handler (session delta -> DB-backed lifetime merge -> delete-then-insert of combat_stats + combat_stats_sessions). Read handlers gain the live combat overlay (union live + DB), like Python. Validation: - combat.go `combat-merge` CLI folds snapshots through the accumulator; diffed against the Python functions on identical input -> byte-IDENTICAL. - combat_test.go golden test runs in the build (go test now part of the tracker Dockerfile). - Live: 40 combat lifetime rows + 40 session snapshots + rare_events flowing in dereth_go via the shadow consumer. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
a5d69ba88d
commit
7350b00341
6 changed files with 347 additions and 4 deletions
54
go-services/tracker-go/combat_test.go
Normal file
54
go-services/tracker-go/combat_test.go
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
package main
|
||||
|
||||
import "testing"
|
||||
|
||||
// Golden values cross-checked against the Python _combat_session_delta /
|
||||
// _combat_merge_into_lifetime on identical input (see compare run). Folds two
|
||||
// cumulative snapshots; the first is treated as the whole delta.
|
||||
func TestCombatMerge(t *testing.T) {
|
||||
snap1 := map[string]any{
|
||||
"total_damage_given": 100.0, "total_kills": 2.0,
|
||||
"monsters": map[string]any{
|
||||
"Drudge": map[string]any{
|
||||
"name": "Drudge", "kill_count": 2.0, "damage_given": 100.0,
|
||||
"offense": map[string]any{"melee": map[string]any{"slashing": map[string]any{
|
||||
"total_attacks": 10.0, "max_normal_damage": 15.0,
|
||||
}}},
|
||||
},
|
||||
},
|
||||
}
|
||||
snap2 := map[string]any{
|
||||
"total_damage_given": 250.0, "total_kills": 5.0,
|
||||
"monsters": map[string]any{
|
||||
"Drudge": map[string]any{
|
||||
"name": "Drudge", "kill_count": 4.0, "damage_given": 200.0,
|
||||
"offense": map[string]any{"melee": map[string]any{"slashing": map[string]any{
|
||||
"total_attacks": 20.0, "max_normal_damage": 18.0,
|
||||
}}},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
lifetime := map[string]any{}
|
||||
lifetime = combatMergeIntoLifetime(lifetime, snap1) // first = whole delta
|
||||
lifetime = combatMergeIntoLifetime(lifetime, combatSessionDelta(snap2, snap1))
|
||||
|
||||
if got := num(lifetime["total_kills"]); got != 5 {
|
||||
t.Errorf("total_kills = %v, want 5", got)
|
||||
}
|
||||
if got := num(lifetime["total_damage_given"]); got != 250 {
|
||||
t.Errorf("total_damage_given = %v, want 250", got)
|
||||
}
|
||||
drudge := asMap(asMap(lifetime["monsters"])["Drudge"])
|
||||
if got := num(drudge["kill_count"]); got != 4 {
|
||||
t.Errorf("Drudge.kill_count = %v, want 4", got)
|
||||
}
|
||||
slashing := asMap(asMap(asMap(drudge["offense"])["melee"])["slashing"])
|
||||
// offense uses the latest snapshot additively (the documented quirk): 10 + 20.
|
||||
if got := num(slashing["total_attacks"]); got != 30 {
|
||||
t.Errorf("offense slashing total_attacks = %v, want 30 (latest-additive quirk)", got)
|
||||
}
|
||||
if got := num(slashing["max_normal_damage"]); got != 18 {
|
||||
t.Errorf("offense slashing max_normal_damage = %v, want 18 (max)", got)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue