A .gitignore => .gitignore +1 -0
@@ 0,0 1,1 @@
+cmd/astro-knights-play-stats/*.json
M cmd/astro-knights-play-stats/main.go => cmd/astro-knights-play-stats/main.go +37 -16
@@ 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)
+ }
+ */
}
M cmd/astro-knights-play-stats/tab_knights.go => cmd/astro-knights-play-stats/tab_knights.go +10 -5
@@ 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),
}
*/
}
A collection.go => collection.go +86 -0
@@ 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
+}
M data.go => data.go +137 -78
@@ 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},
}
+*/
M stats.go => stats.go +94 -2
@@ 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
*/