diff options
| author | ewy <ewy0@protonmail.com> | 2026-05-22 16:54:49 +0200 |
|---|---|---|
| committer | ewy <ewy0@protonmail.com> | 2026-05-22 16:54:49 +0200 |
| commit | 007e2de369f9fc26da3237646de14f2af5052ee8 (patch) | |
| tree | f81557385628fc93f1ef9616b8bc75a304f9d740 /stats | |
initial commit
Diffstat (limited to 'stats')
| -rw-r--r-- | stats/acts.go | 33 | ||||
| -rw-r--r-- | stats/ancients.go | 50 | ||||
| -rw-r--r-- | stats/cards.go | 37 | ||||
| -rw-r--r-- | stats/count.go | 33 | ||||
| -rw-r--r-- | stats/db.go | 44 | ||||
| -rw-r--r-- | stats/enrich.go | 71 | ||||
| -rw-r--r-- | stats/option.go | 11 | ||||
| -rw-r--r-- | stats/versions_enrich.go | 23 |
8 files changed, 302 insertions, 0 deletions
diff --git a/stats/acts.go b/stats/acts.go new file mode 100644 index 0000000..19c4219 --- /dev/null +++ b/stats/acts.go @@ -0,0 +1,33 @@ +package stats + +import ( + "slices" + "strings" + "sts2stats/model" +) + +type Act struct { + Index int + Label string + Key string `db:"Key,primarykey"` +} + +var actKeys []string + +func EnrichActs(run model.Run, stat RunStat) (result []any, err error) { + for i, a := range run.Acts { + if slices.Contains(actKeys, a) { + continue + } + actKeys = append(actKeys, a) + + act := Act{ + Index: i, + Key: a, + Label: strings.SplitN(a, ".", 2)[0], + } + + result = append(result, &act) + } + return result, nil +} diff --git a/stats/ancients.go b/stats/ancients.go new file mode 100644 index 0000000..16ea207 --- /dev/null +++ b/stats/ancients.go @@ -0,0 +1,50 @@ +package stats + +import ( + "sts2stats/model" +) + +const ( + AncientKey = "ancient" +) + +type AncientChoice struct { + RunStat + AncientOption + ActIndex int + ActName string + Chosen bool +} + +type AncientOption struct { + Key string + Type string +} + +func EnrichAncients(run model.Run, st RunStat) (opts []any, err error) { + for actIndex, act := range run.MapPointHistory { + for _, floor := range act { + if floor.MapPointType != AncientKey { + continue + } + + for _, stat := range floor.PlayerStats { + for _, choice := range stat.AncientChoice { + + opts = append(opts, &AncientChoice{ + AncientOption: AncientOption{ + Key: choice.TextKey, + Type: choice.Title.Table, + }, + RunStat: st, + ActIndex: actIndex, + ActName: run.Acts[actIndex], + Chosen: choice.WasChosen, + }) + } + } + + } + } + return opts, nil +} diff --git a/stats/cards.go b/stats/cards.go new file mode 100644 index 0000000..d4049f4 --- /dev/null +++ b/stats/cards.go @@ -0,0 +1,37 @@ +package stats + +import ( + "sts2stats/model" +) + +type CardChoice struct { + RunStat + model.PlayerStat + RoomStat + Card string + Upgrade int + Picked bool +} + +func EnrichCardChoice(run model.Run, st RunStat) (result []any, err error) { + for ia, act := range run.MapPointHistory { + for i, floor := range act { + for _, stat := range floor.PlayerStats { + for _, choice := range stat.CardChoices { + result = append(result, &CardChoice{ + RunStat: st, + Card: choice.Card.Id, + Upgrade: choice.Card.CurrentUpgradeLevel, + PlayerStat: stat.PlayerStat, + RoomStat: RoomStat{ + Floor: i, + Act: ia + 1, + }, + Picked: false, + }) + } + } + } + } + return +} diff --git a/stats/count.go b/stats/count.go new file mode 100644 index 0000000..e9c87f5 --- /dev/null +++ b/stats/count.go @@ -0,0 +1,33 @@ +package stats + +import "sts2stats/model" + +type Counter interface { + Count(run model.Run) error +} + +var Counters = []Counter{} + +type CountFunc func(run model.Run) error + +type CountFuncWrapper struct { + f CountFunc +} + +func (c CountFuncWrapper) Count(run model.Run) error { + return c.f(run) +} + +func Func(f CountFunc) Counter { + return CountFuncWrapper{f: f} +} + +func Count(run model.Run) error { + for _, c := range Counters { + err := c.Count(run) + if err != nil { + return err + } + } + return nil +} diff --git a/stats/db.go b/stats/db.go new file mode 100644 index 0000000..327373f --- /dev/null +++ b/stats/db.go @@ -0,0 +1,44 @@ +package stats + +import ( + "sts2stats/model" + "time" +) + +type RunStat struct { + RunId string + StartTime time.Time + Ascension int + Version string + Win bool + FloorsClimbed int + Abandoned bool + InProgress bool +} + +type RoomStat struct { + Act int + Floor int +} + +func NewRunStat(run model.Run) RunStat { + st := RunStat{ + Version: run.BuildID, + StartTime: time.Unix(int64(run.StartTime), 0), + Ascension: run.Ascension, + Win: run.Win, + RunId: run.RunId, + Abandoned: run.WasAbandoned, + FloorsClimbed: runLen(run), + InProgress: run.KilledByEncounter != "" || run.KilledByEvent != "" || run.Win != true, + } + return st +} + +func runLen(run model.Run) int { + var res int + for _, a := range run.MapPointHistory { + res += len(a) + } + return res +} diff --git a/stats/enrich.go b/stats/enrich.go new file mode 100644 index 0000000..40e91ec --- /dev/null +++ b/stats/enrich.go @@ -0,0 +1,71 @@ +package stats + +import ( + "sts2stats/model" + "sts2stats/spool" + "sts2stats/storage" + "sync" + "time" +) + +type LoadFunc = func() error + +// Enricher reads data from the RunSave and fills dictionaries and data structures +type Enricher interface { + Enrich(run model.Run, stat RunStat) ([]any, error) +} + +type EnrichFunc func(run model.Run, stat RunStat) ([]any, error) + +type EnrichFuncWrapper struct { + f EnrichFunc +} + +func EnrichWrap(f EnrichFunc) Enricher { + return EnrichFuncWrapper{f: f} +} + +func (e EnrichFuncWrapper) Enrich(run model.Run, stat RunStat) ([]any, error) { + return e.f(run, stat) +} + +var Enrichers = map[string]Enricher{ + "act": EnrichWrap(EnrichActs), + "version": EnrichWrap(EnrichGameVersion), + "ancient choice": EnrichWrap(EnrichAncients), + "card choice": EnrichWrap(EnrichCardChoice), +} + +func Enrich(run model.Run) error { + startTime := time.Now() + id := run.RunId[:4] + wg := sync.WaitGroup{} + st := NewRunStat(run) + for k, e := range Enrichers { + wg.Go(func() { + spool.Debug("[%v] Starting %v enrichment\n", id, k) + res, err := e.Enrich(run, st) + if err != nil { + spool.Panic("%v\n", err) + } + + if len(res) == 0 { + spool.Debug("[%v] Finished %v enrichment\n", id, k) + return + } + + spool.Debug("[%v] Collected %v entities (%v)\n", id, k, len(res)) + err = storage.SaveNow(res...) + if err != nil { + spool.Panic("during %v: %v\n", k, err) + } + spool.Debug("[%v] Saved %v entities (%v)\n", id, k, len(res)) + }) + } + wg.Wait() + + endTime := time.Now() + spool.Info("[%v] digested run\n", id) + spool.Debug("[%v] took %.2fs", endTime.Sub(startTime).Seconds()) + return nil +} diff --git a/stats/option.go b/stats/option.go new file mode 100644 index 0000000..e8d729d --- /dev/null +++ b/stats/option.go @@ -0,0 +1,11 @@ +package stats + +type ChoiceStat struct { + Amount int + Wins int +} + +type Choice struct { + Taken *ChoiceStat + Ignored *ChoiceStat +} diff --git a/stats/versions_enrich.go b/stats/versions_enrich.go new file mode 100644 index 0000000..416c469 --- /dev/null +++ b/stats/versions_enrich.go @@ -0,0 +1,23 @@ +package stats + +import ( + "slices" + "sts2stats/model" +) + +type GameVersion struct { + Version string `db:"Version,primarykey"` +} + +var versions []string + +func EnrichGameVersion(run model.Run, stat RunStat) (result []any, err error) { + if !slices.Contains(versions, run.BuildID) { + versions = append(versions, run.BuildID) + v := GameVersion{ + Version: run.BuildID, + } + result = append(result, &v) + } + return result, nil +} |
