package controller import ( "fmt" "iter" "slices" "github.com/google/uuid" "github.com/iliadenisov/galaxy/internal/model/game" ) type Cache struct { g *game.Game cacheRaceIndexByID map[uuid.UUID]int raceIndexByShipGroupIndex map[int]int shipClassByShipGroupIndex map[int]*game.ShipType planetByPlanetNumber map[uint]*game.Planet cacheRelation map[int]map[int]game.Relation } func NewCache(g *game.Game) *Cache { if g == nil { panic("NewCache: nil Game passed") } c := &Cache{ g: g, } return c } func (c *Cache) Relation(r1, r2 int) game.Relation { if c.cacheRelation == nil { for r1 := range c.g.Race { for r2 := range c.g.Race { if r1 == r2 { continue } rel := slices.IndexFunc(c.g.Race[r1].Relations, func(r game.RaceRelation) bool { return r.RaceID == c.g.Race[r2].ID }) if rel < 0 { panic(fmt.Sprintf("Relation: opponent not found idx=%d", r2)) } if _, ok := c.cacheRelation[r1]; !ok { c.cacheRelation[r1] = make(map[int]game.Relation) } c.cacheRelation[r1][r2] = c.g.Race[r1].Relations[rel].Relation } } } if _, ok := c.cacheRelation[r1]; !ok { panic(fmt.Sprintf("Relation: no left race idx=%d", r1)) } if v, ok := c.cacheRelation[r1][r2]; !ok { panic(fmt.Sprintf("Relation: no right race idx=%d", r2)) } else { return v } } func (c *Cache) Planet(planetNumber uint) *game.Planet { if c.planetByPlanetNumber == nil { c.planetByPlanetNumber = make(map[uint]*game.Planet) for p := range c.g.Map.Planet { c.planetByPlanetNumber[c.g.Map.Planet[p].Number] = &c.g.Map.Planet[p] } } if v, ok := c.planetByPlanetNumber[planetNumber]; ok { return v } else { panic(fmt.Sprintf("Planet: not found by number=%d", planetNumber)) } } func (c *Cache) ShipGroupShipClass(groupIndex int) *game.ShipType { if c.shipClassByShipGroupIndex == nil { c.fillShipsAndGroups() } c.validateShipGroupIndex(groupIndex) if v, ok := c.shipClassByShipGroupIndex[groupIndex]; ok { return v } else { panic(fmt.Sprintf("ShipClassByShipGroupIndex: group not found by index=%v", groupIndex)) } } func (c *Cache) RaceIndex(ID uuid.UUID) int { if c.cacheRaceIndexByID == nil { c.cacheRaceIndexByID = make(map[uuid.UUID]int) for i := range c.g.Race { c.cacheRaceIndexByID[c.g.Race[i].ID] = i } } if v, ok := c.cacheRaceIndexByID[ID]; ok { return v } else { panic(fmt.Sprintf("RaceIndex: race not found by ID=%v", ID)) } } // ShipGroup is a proxy func, nothing to cache func (c *Cache) ShipGroup(groupIndex int) *game.ShipGroup { c.validateShipGroupIndex(groupIndex) return &c.g.ShipGroups[groupIndex] } func (c *Cache) ShipGroupsIndex() iter.Seq[int] { return func(yield func(int) bool) { for i := range c.g.ShipGroups { if !yield(i) { return } } } } func (c *Cache) ShipGroupOwnerRaceIndex(groupIndex int) int { if c.raceIndexByShipGroupIndex == nil { c.fillShipsAndGroups() } c.validateShipGroupIndex(groupIndex) if v, ok := c.raceIndexByShipGroupIndex[groupIndex]; ok { return v } else { panic(fmt.Sprintf("ShipGroupRace: group not found by index=%v", groupIndex)) } } func (c *Cache) ShipGroupOwnerRace(groupIndex int) *game.Race { return &c.g.Race[c.ShipGroupOwnerRaceIndex(groupIndex)] } func (c *Cache) ShipGroupNumber(i int, n uint) { c.validateShipGroupIndex(i) c.g.ShipGroups[i].Number = n } func (c *Cache) DeleteShipGroup(i int) { c.validateShipGroupIndex(i) c.unsafeDeleteShipGroup(i) } func (c *Cache) DeleteKilledShipGroups() { for i := len(c.g.ShipGroups) - 1; i >= 0; i-- { if c.g.ShipGroups[i].Number == 0 { c.unsafeDeleteShipGroup(i) } } } func (c *Cache) unsafeDeleteShipGroup(i int) { c.g.ShipGroups = append(c.g.ShipGroups[:i], c.g.ShipGroups[i+1:]...) delete(c.raceIndexByShipGroupIndex, i) delete(c.shipClassByShipGroupIndex, i) } // Internal func (c *Cache) validateShipGroupIndex(i int) { if i >= len(c.g.ShipGroups) { panic(fmt.Sprintf("group index out of groups len: %d >= %d", i, len(c.g.ShipGroups))) } } func (c *Cache) fillShipsAndGroups() { if c.raceIndexByShipGroupIndex != nil { clear(c.raceIndexByShipGroupIndex) } else { c.raceIndexByShipGroupIndex = make(map[int]int) } if c.shipClassByShipGroupIndex != nil { clear(c.shipClassByShipGroupIndex) } else { c.shipClassByShipGroupIndex = make(map[int]*game.ShipType) } for groupIndex := range c.g.ShipGroups { ri := c.RaceIndex(c.g.ShipGroups[groupIndex].OwnerID) c.raceIndexByShipGroupIndex[groupIndex] = ri sti, ok := ShipClassIndex(c.g, ri, c.g.ShipGroups[groupIndex].TypeID) if !ok { panic(fmt.Sprintf("CollectPlanetGroups: ship class not found for race=%q group=%v", c.g.Race[ri].Name, c.g.ShipGroups[groupIndex].Index)) } c.shipClassByShipGroupIndex[groupIndex] = &c.g.Race[ri].ShipTypes[sti] } } // Helpers func ShipClassIndex(g *game.Game, ri int, classID uuid.UUID) (int, bool) { if len(g.Race) < ri+1 { panic(fmt.Sprintf("ShipClass: game race index %d invalid: len=%d", ri, len(g.Race))) } sti := slices.IndexFunc(g.Race[ri].ShipTypes, func(st game.ShipType) bool { return st.ID == classID }) return sti, sti >= 0 }