From d883bd1f6b9570d1fdc7153ecc66b1da8007fb45 Mon Sep 17 00:00:00 2001 From: HokieGeek Date: Tue, 2 Jan 2024 19:28:17 -0500 Subject: [PATCH] fleshing out a way to display knight combinations --- .gitignore | 1 + cmd/astro-knights-play-stats/main.go | 53 +++-- cmd/astro-knights-play-stats/tab_knights.go | 15 +- collection.go | 86 ++++++++ data.go | 215 +++++++++++++------- stats.go | 96 ++++++++- 6 files changed, 365 insertions(+), 101 deletions(-) create mode 100644 .gitignore create mode 100644 collection.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6141a6a --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +cmd/astro-knights-play-stats/*.json diff --git a/cmd/astro-knights-play-stats/main.go b/cmd/astro-knights-play-stats/main.go index 0780111..e5f77a5 100644 --- a/cmd/astro-knights-play-stats/main.go +++ b/cmd/astro-knights-play-stats/main.go @@ -1,7 +1,9 @@ package main import ( + "encoding/json" "fmt" + "log" "os" "strings" @@ -112,27 +114,46 @@ func (m model) View() string { } func main() { - username := "HokieGeek" + var err error + jsonf, err := os.ReadFile(os.Args[1]) + if err != nil { + log.Fatal(err) + } - // retrieve the plays - fmt.Printf("retrieving plays for %s ...\n", username) - plays, err := astro_knights.GetPlaysByUsername(username) + var collection astro_knights.UserCollection + err = json.Unmarshal(jsonf, &collection) if err != nil { - panic(err) + log.Fatal("error during Unmarshal(): ", err) } - m := model{ - tabs: []string{"plays", "knights", "bosses", "homeworlds"}, - tabContent: []tea.Model{ - newTabPlays(plays), - newTabKnights(plays), - newTabBosses(plays), - newTabHomeworlds(plays), - }, + // retrieve the plays + fmt.Printf("retrieving plays for %s ...\n", collection.BggUsername) + collection.Plays, err = astro_knights.GetPlaysByUsername(collection.BggUsername) + if err != nil { + log.Panic(err) } - if _, err := tea.NewProgram(m, tea.WithAltScreen()).Run(); err != nil { - fmt.Println("WTF?:", err) - os.Exit(1) + stats, err := astro_knights.GetKnightUseStats(collection) + if err != nil { + log.Panic(err) } + + fmt.Println(stats) + + /* + m := model{ + tabs: []string{"plays", "knights", "bosses", "homeworlds"}, + tabContent: []tea.Model{ + newTabPlays(plays), + newTabKnights(plays), + newTabBosses(plays), + newTabHomeworlds(plays), + }, + } + + if _, err := tea.NewProgram(m, tea.WithAltScreen()).Run(); err != nil { + fmt.Println("WTF?:", err) + os.Exit(1) + } + */ } diff --git a/cmd/astro-knights-play-stats/tab_knights.go b/cmd/astro-knights-play-stats/tab_knights.go index 1302aee..223e210 100644 --- a/cmd/astro-knights-play-stats/tab_knights.go +++ b/cmd/astro-knights-play-stats/tab_knights.go @@ -8,7 +8,7 @@ import ( ) type tabKnightsModel struct { - // playsTable table.Model + // matrixTable table.Model width, height int } @@ -40,7 +40,7 @@ func (m tabKnightsModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } } - // m.playsTable, cmd = m.playsTable.Update(msg) + // m.matrixTable, cmd = m.matrixTable.Update(msg) return m, cmd } @@ -48,18 +48,23 @@ func (m tabKnightsModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { func (m tabKnightsModel) View() string { /* if m.height > 0 { - m.playsTable.SetHeight(m.height - 12) // TODO: magic number + m.matrixTable.SetHeight(m.height - 12) // TODO: magic number } - return basicTableStyle.Render(m.playsTable.View()) + return basicTableStyle.Render(m.matrixTable.View()) */ return "TODO" } func newTabKnights(plays []astro_knights.Play) tea.Model { + // stats, err := astro_knights.GetKnightUseStats(plays) + // if err != nil { + // panic(err) + // } + return tabKnightsModel{} /* return tabKnightsModel{ - playsTable: createPlaysTable(plays), + matrixTable: createPlaysTable(plays), } */ } diff --git a/collection.go b/collection.go new file mode 100644 index 0000000..e616a59 --- /dev/null +++ b/collection.go @@ -0,0 +1,86 @@ +package astro_knights + +import "fmt" + +type UserCollection struct { + BggUsername string `json:"bgg_username"` + Editions []string `json:"editions"` + + editionIds []EditionID + knights []KnightID + bosses []BossID + homeworlds []HomeworldID + teamAttacks []TeamAttackID + + Plays []Play `json:"-"` +} + +func (c *UserCollection) populateIDs() error { + if c.editionIds != nil { + return nil + } + + c.editionIds = make([]EditionID, len(c.Editions)) + + for i, ed := range c.Editions { + if eid, ok := EditionNameToID(ed); ok { + c.editionIds[i] = eid + } else { + return fmt.Errorf("could not determine ID of edition named '%s'", ed) + } + } + + c.knights = make([]KnightID, 0) + for _, eid := range c.editionIds { + c.knights = append(c.knights, Editions[eid].Knights...) + } + + c.bosses = make([]BossID, 0) + for _, eid := range c.editionIds { + c.bosses = append(c.bosses, Editions[eid].Bosses...) + } + + c.homeworlds = make([]HomeworldID, 0) + for _, eid := range c.editionIds { + c.homeworlds = append(c.homeworlds, Editions[eid].Homeworlds...) + } + + c.teamAttacks = make([]TeamAttackID, 0) + for _, eid := range c.editionIds { + c.teamAttacks = append(c.teamAttacks, Editions[eid].TeamAttacks...) + } + + return nil +} + +func (c *UserCollection) Knights() ([]KnightID, error) { + if err := c.populateIDs(); c.knights == nil && err != nil { + return nil, err + } + + return c.knights, nil +} + +func (c *UserCollection) Bosses() ([]BossID, error) { + if err := c.populateIDs(); c.bosses == nil && err != nil { + return nil, err + } + + return c.bosses, nil +} + +func (c *UserCollection) Homeworlds() ([]HomeworldID, error) { + if err := c.populateIDs(); c.homeworlds == nil && err != nil { + return nil, err + } + + return c.homeworlds, nil +} + +func (c *UserCollection) TeamAttacks() ([]TeamAttackID, error) { + if err := c.populateIDs(); c.teamAttacks == nil && err != nil { + return nil, err + } + + return c.teamAttacks, nil +} diff --git a/data.go b/data.go index 4555357..8c6aaf3 100644 --- a/data.go +++ b/data.go @@ -1,64 +1,33 @@ package astro_knights -type EditionID int - -const ( - CoreGameID EditionID = 352179 - OrionSystemExpansionID EditionID = 382126 - CoreGamePromoPack1ID EditionID = 406201 - - /* - EternityGameID EditionID = 386538 - EternityFlySavageSkiesID EditionID = 406204 - EternityMysterySolarusID EditionID = 406203 - EternityPromoPack1ID EditionID = 406202 - */ -) - -var EditionsIDs = [...]EditionID{ - CoreGameID, - OrionSystemExpansionID, - CoreGamePromoPack1ID, - /* - EternityGameID, - EternityFlySavageSkiesID, - EternityMysterySolarusID, - EternityPromoPack1ID, - */ -} - -type HomeworldID string - -const ( - Nisarin HomeworldID = "Nisarin" - Ylong HomeworldID = "Ylong" - Qaris HomeworldID = "Qaris" - NewAtlantis HomeworldID = "New Atlantis" - - OrionIII HomeworldID = "Orion III" - - Silarix HomeworldID = "Silarix" - - // Centuron HomeworldID = "Centuron " -) - -var Homeworlds = [...]HomeworldID{Nisarin, Ylong, Qaris, NewAtlantis, OrionIII, Silarix} +import "strings" type KnightID string const ( - ZAK KnightID = "Z.A.K" + ZAK KnightID = "Z.A.K." Christina KnightID = "Christina" Silas KnightID = "Silas" Gavril KnightID = "Gavril" - Namas KnightID = "Namas" + Nasma KnightID = "Nasma" Toli KnightID = "Toli" Deleth KnightID = "Deleth" Alexios KnightID = "Alexios" + + Caleb KnightID = "Caleb" + Reshi KnightID = "Reshi" + ZAK_Eternity KnightID = "Z.A.K. (Eternity)" + Tsana KnightID = "Tsana" + + Naoko KnightID = "Naoko" + Sunshine KnightID = "Sunshine" + + CaptainCadiz KnightID = "Captain Cadiz" + Scuttlebutt KnightID = "Scuttlebutt" ) -var Knights = [...]KnightID{ZAK, Christina, Silas, Gavril, Namas, Toli, Deleth, Alexios} +var Knights = [...]KnightID{ZAK, Christina, Silas, Gavril, Nasma, Toli, Deleth, Alexios} type BossID string @@ -69,6 +38,12 @@ const ( Continnua BossID = "Continnua" FissionParasite BossID = "Fission Parasite" + + DirathianBehemoth BossID = "Dirathian Behemoth" + + ShadeSculptor BossID = "Shade Sculptor" + + TheBlackHoleGalleon BossID = "The Black Hole Galleon" ) var Bosses = [...]BossID{Furion, ArchitectO815, Lunaris, Continnua, FissionParasite} @@ -83,59 +58,143 @@ const ( var BossDifficulties = [...]BossDifficulty{BossNormal, BossExpert, BossNightmare} +type HomeworldID string + +const ( + Nisarin HomeworldID = "Nisarin" + Ylong HomeworldID = "Ylong" + Qaris HomeworldID = "Qaris" + NewAtlantis HomeworldID = "New Atlantis" + + OrionIII HomeworldID = "Orion III" + + Silarix HomeworldID = "Silarix" + + Dirath HomeworldID = "Dirath" + + Eos HomeworldID = "Eos" + + NassaiIV HomeworldID = "Nassai IV" + + Centuron HomeworldID = "Centuron" +) + +var Homeworlds = [...]HomeworldID{Nisarin, Ylong, Qaris, NewAtlantis, OrionIII, Silarix} + +type TeamAttackID string + +const ( + NoKnightLeftBehind TeamAttackID = "No Knight Left Behind!" + + StarburstFinale TeamAttackID = "Starburst Finale!" + + HitThemWhereItHurts TeamAttackID = "Hit Them Where It Hurts!" +) + +type EditionID int + +const ( + CoreGameID EditionID = 352179 + OrionSystemExpansionID EditionID = 382126 + CoreGamePromoPack1ID EditionID = 406201 + + EternityGameID EditionID = 386538 + EternitySavageSkiesID EditionID = 406204 + EternityMysterySolarusID EditionID = 406203 + EternityPromoPack1ID EditionID = 406202 +) + +var EditionsIDs = [...]EditionID{ + CoreGameID, + OrionSystemExpansionID, + CoreGamePromoPack1ID, + EternityGameID, + EternitySavageSkiesID, + EternityMysterySolarusID, + EternityPromoPack1ID, +} + +var editionNameToID = map[string]EditionID{ + "astro knights": CoreGameID, + "core": CoreGameID, + "the orion system": OrionSystemExpansionID, + "orion system": OrionSystemExpansionID, + "astro knights: promo pack": CoreGamePromoPack1ID, + "core promo pack": CoreGamePromoPack1ID, + "astro knights: eternity": EternityGameID, + "eternity": EternityGameID, + "savage skies": EternitySavageSkiesID, + "fly the savage skies": EternitySavageSkiesID, + "mystery of solarus": EternityMysterySolarusID, + "eternity promo pack": EternityPromoPack1ID, +} + +func EditionNameToID(name string) (EditionID, bool) { + normalized := strings.ToLower(strings.TrimSpace(name)) + if id, ok := editionNameToID[normalized]; ok { + return id, true + } + return -1, false +} + type Edition struct { - ID EditionID - Homeworlds []HomeworldID - Knights []KnightID - Bosses []BossID + ID EditionID + Knights []KnightID + Bosses []BossID + Homeworlds []HomeworldID + TeamAttacks []TeamAttackID } var Editions = map[EditionID]Edition{ // Core CoreGameID: Edition{ ID: CoreGameID, - Homeworlds: []HomeworldID{Nisarin, Ylong, Qaris, NewAtlantis}, - Knights: []KnightID{ZAK, Christina, Silas, Gavril, Namas, Toli}, + Knights: []KnightID{ZAK, Christina, Silas, Gavril, Nasma, Toli}, Bosses: []BossID{Furion, ArchitectO815, Lunaris, Continnua}, + Homeworlds: []HomeworldID{Nisarin, Ylong, Qaris, NewAtlantis}, }, OrionSystemExpansionID: Edition{ ID: OrionSystemExpansionID, - Homeworlds: []HomeworldID{OrionIII}, Knights: []KnightID{Deleth, Alexios}, Bosses: []BossID{FissionParasite}, + Homeworlds: []HomeworldID{OrionIII}, }, CoreGamePromoPack1ID: Edition{ ID: CoreGamePromoPack1ID, Homeworlds: []HomeworldID{Silarix}, }, // Eternity - /* - EternityGameID: Edition{ - ID: EternityGameID, - Homeworlds: []HomeworldID{}, - Knights: []KnightID{}, - Bosses: []BossID{}, - }, - EternityFlySavageSkiesID: Edition{ - ID: EternityFlySavageSkiesID, - Homeworlds: []HomeworldID{}, - Knights: []KnightID{}, - Bosses: []BossID{}, - }, - EternityMysterySolarusID: Edition{ - ID: EternityMysterySolarusID, - Homeworlds: []HomeworldID{}, - Knights: []KnightID{}, - Bosses: []BossID{}, - }, - EternityPromoPack1ID: Edition{ - ID: EternityPromoPack1ID, - Homeworlds: []HomeworldID{Centuron}, - }, - */ + EternityGameID: Edition{ + ID: EternityGameID, + Knights: []KnightID{Caleb, Reshi, ZAK_Eternity, Tsana}, + Bosses: []BossID{DirathianBehemoth}, + Homeworlds: []HomeworldID{Dirath}, + TeamAttacks: []TeamAttackID{}, + }, + EternitySavageSkiesID: Edition{ + ID: EternitySavageSkiesID, + Knights: []KnightID{Naoko, Sunshine}, + Bosses: []BossID{ShadeSculptor}, + Homeworlds: []HomeworldID{Eos}, + TeamAttacks: []TeamAttackID{StarburstFinale}, + }, + EternityMysterySolarusID: Edition{ + ID: EternityMysterySolarusID, + Knights: []KnightID{CaptainCadiz, Scuttlebutt}, + Bosses: []BossID{TheBlackHoleGalleon}, + Homeworlds: []HomeworldID{NassaiIV}, + TeamAttacks: []TeamAttackID{NoKnightLeftBehind}, + }, + EternityPromoPack1ID: Edition{ + ID: EternityPromoPack1ID, + Homeworlds: []HomeworldID{Centuron}, + TeamAttacks: []TeamAttackID{HitThemWhereItHurts}, + }, } +/* var EditionsCompatability = [][]EditionID{ {CoreGameID, OrionSystemExpansionID, CoreGamePromoPack1ID}, - // {EternityGameID, EternityFlySavageSkiesID, EternityMysterySolarusID, EternityPromoPack1ID}, + {EternityGameID, EternitySavageSkiesID, EternityMysterySolarusID, EternityPromoPack1ID}, } +*/ diff --git a/stats.go b/stats.go index 0cb9afb..009d48a 100644 --- a/stats.go +++ b/stats.go @@ -93,6 +93,7 @@ func GetPlaysByUsername(username string) ([]Play, error) { } func GetBossUseStats(plays []Play) (map[BossID]map[BossDifficulty]int, error) { + // TODO: boss win count stats := make(map[BossID]map[BossDifficulty]int) for _, b := range Bosses { @@ -123,11 +124,102 @@ func GetHomeworldUseStats(plays []Play) (map[HomeworldID]int, error) { return stats, nil } -func GetKnightUseStats(plays []Play) { //(map[KnightID][] +// type knightCombinationStats [][]KnightID +// type KnightUseStats map[KnightID]knightCombinationStats +/* +type knightCombinationStats struct { + Knights []KnightID + Count int +} +*/ + +type KnightUseStats struct { + // Combinations []knightCombinationStats + // Matrix map[KnightID][]knightCombinationStats + Combinations map[[4]KnightID]int + Matrix map[KnightID]map[KnightID]int +} + +func permsExample() { + nextPerm := func(p []int) { + for i := len(p) - 1; i >= 0; i-- { + if i == 0 || p[i] < len(p)-i-1 { + p[i]++ + return + } + p[i] = 0 + } + } + + orig := []int{11, 22, 33} + for p := make([]int, len(orig)); p[0] < len(p); nextPerm(p) { + perm := append([]int{}, orig...) + for i, v := range p { + perm[i], perm[i+v] = perm[i+v], perm[i] + } + fmt.Println(perm) + } +} + +func GetKnightUseStats(collection UserCollection) (KnightUseStats, error) { + stats := KnightUseStats{ + // Combinations: make([]knightCombinationStats, 0), + // Matrix: make(map[KnightID][]knightCombinationStats), + Combinations: make(map[[4]KnightID]int), + Matrix: make(map[KnightID]map[KnightID]int), + } + + // initialize the matrix + knights, err := collection.Knights() + if err != nil { + return stats, err + } + for _, k1 := range knights { + stats.Matrix[k1] = make(map[KnightID]int) + // stats.Matrix[k1] = make([]knightCombinationStats, 0, len(Knights)) + + for _, k2 := range knights { + // stats.Matrix[k1] = append(stats.Matrix[k1], knightCombinationStats{[]KnightID{k2}, 0}) + stats.Matrix[k1][k2] = 0 + } + } + + // initialize the combinations + /* + combos := make([][4]KnightID, 0) + for _, k1 := range Knights { + // TODO + for _, k2 := range Knights { + } + } + for _, c := range combos { + stats.Combinations[c] = 0 + } + */ + + // populate matrix with plays + for _, p := range collection.Plays { + // fmt.Println(p.Knights) + for _, k1 := range p.Knights { + for _, k2 := range p.Knights { + if k2 != k1 { + /* + fmt.Println(" ", k1, k2) + if _, ok := stats.Matrix[k1]; !ok { + fmt.Println("did not find", k1) + fmt.Printf("%+v\n", stats.Matrix) + } + */ + stats.Matrix[k1][k2] += 1 + } + } + } + } + + return stats, nil } /* - knight combinations -- boss win counts - homeworld, boss+str, knights combinations */ -- 2.45.2