package controller import ( "fmt" "iter" "slices" e "github.com/iliadenisov/galaxy/internal/error" "github.com/iliadenisov/galaxy/internal/model/game" ) func (c *Cache) Relation(r1, r2 int) game.Relation { if c.cacheRelation == nil { c.cacheRelation = make(map[int]map[int]game.Relation) for r1 := range c.listRaceActingIdx() { for r2 := range c.listRaceActingIdx() { 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)) } c.updateRelationCache(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) updateRelationCache(r1, r2 int, rel game.Relation) { if r1 == r2 { return } if c.cacheRelation == nil { c.cacheRelation = make(map[int]map[int]game.Relation) } if _, ok := c.cacheRelation[r1]; !ok { c.cacheRelation[r1] = make(map[int]game.Relation) } c.cacheRelation[r1][r2] = rel } func (c *Cache) Voted(ri int) int { c.validateRaceIndex(ri) return c.RaceIndex(c.g.Race[ri].VoteFor) } func (c *Cache) UpdateRelation(ri, other int, rel game.Relation) (err error) { defer func() { if err == nil && c.cacheRelation != nil { c.updateRelationCache(ri, other, rel) } }() for o := range c.g.Race[ri].Relations { switch { case ri == other: c.g.Race[ri].Relations[o].Relation = rel case c.g.Race[ri].Relations[o].RaceID == c.g.Race[other].ID: c.g.Race[ri].Relations[o].Relation = rel return nil } } if ri != other { err = e.NewGameStateError("UpdateRelation: opponent not found") } return } func (c *Cache) validateRaceIndex(i int) { if i >= len(c.g.Race) { panic(fmt.Sprintf("race index out of range: %d >= %d", i, len(c.g.Race))) } } func (c *Cache) validActor(name string) (int, error) { i, err := c.validRace(name) if err != nil { return -1, err } c.g.Race[i].TTL = 10 return i, nil } func (c *Cache) validRace(name string) (int, error) { i, err := c.raceIndex(name) if err != nil { return -1, err } if c.g.Race[i].Extinct { return -1, e.NewRaceExinctError(name) } return i, nil } func (c *Cache) raceIndex(name string) (int, error) { i := slices.IndexFunc(c.g.Race, func(r game.Race) bool { return r.Name == name }) if i < 0 { return i, e.NewRaceUnknownError(name) } return i, nil } func (c *Cache) raceTechLevel(ri int, t game.Tech, v float64) { c.validateRaceIndex(ri) c.g.Race[ri].Tech = c.g.Race[ri].Tech.Set(t, v) } func (c *Cache) TurnWipeExtinctRaces() { for i := range c.listRaceActingIdx() { if c.g.Race[i].TTL == 0 { c.wipeRace(i) } } } func (c *Cache) wipeRace(ri int) { c.validateRaceIndex(ri) r := &c.g.Race[ri] c.g.ShipGroups = slices.DeleteFunc(c.g.ShipGroups, func(v game.ShipGroup) bool { return v.OwnerID == r.ID }) c.g.Fleets = slices.DeleteFunc(c.g.Fleets, func(v game.Fleet) bool { return v.OwnerID == r.ID }) clear(r.ShipTypes) clear(r.Sciences) for i := range c.g.Map.Planet { p := &c.g.Map.Planet[i] if p.Owner != nil && *p.Owner != r.ID { continue } p.Wipe() } for i := range c.listRaceActingIdx() { if i == ri { continue } if c.g.Race[i].VoteFor == r.ID { c.g.Race[i].VoteFor = c.g.Race[i].ID } } r.Votes = 0 r.VoteFor = r.ID r.Extinct = true r.TTL = 0 c.invalidateFleetCache() c.invalidateShipGroupCache() } func (c *Cache) listRaceActingIdx() iter.Seq[int] { return func(yield func(int) bool) { for i := range c.listRaceIdx() { if c.g.Race[i].Extinct { continue } if !yield(i) { return } } } } func (c *Cache) listRaceIdx() iter.Seq[int] { return func(yield func(int) bool) { for i := range c.g.Race { if !yield(i) { return } } } }