package controller import ( "fmt" "slices" e "github.com/iliadenisov/galaxy/internal/error" "github.com/iliadenisov/galaxy/internal/model/game" ) func (c Controller) Relation(from, to string) (game.Relation, error) { r1, err := c.Cache.raceIndex(from) if err != nil { return game.Relation(""), err } r2, err := c.Cache.raceIndex(to) if err != nil { return game.Relation(""), err } return c.Cache.Relation(r1, r2), nil } func (c Controller) UpdateRelation(race, opponent string, rel game.Relation) error { ri, err := c.Cache.raceIndex(race) if err != nil { return err } var other int if race == opponent { other = ri } else if other, err = c.Cache.raceIndex(opponent); err != nil { return err } if err != nil { return err } return c.Cache.UpdateRelation(ri, other, rel) } 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.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)) } 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) GiveVotes(race, recipient string) error { ri, err := c.raceIndex(race) if err != nil { return err } rec, err := c.raceIndex(recipient) if err != nil { return err } c.g.Race[ri].VoteFor = c.g.Race[rec].ID return nil } 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) 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) }