wip: refactor controller
This commit is contained in:
@@ -0,0 +1,285 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"iter"
|
||||
"math"
|
||||
"slices"
|
||||
|
||||
"github.com/google/uuid"
|
||||
e "github.com/iliadenisov/galaxy/internal/error"
|
||||
"github.com/iliadenisov/galaxy/internal/model/game"
|
||||
)
|
||||
|
||||
func (c *Cache) FleetState(fleetID uuid.UUID) (game.ShipGroupState, *uint, *game.InSpace) {
|
||||
fi := c.MustFleetIndex(fleetID)
|
||||
ri := c.RaceIndex(c.g.Fleets[fi].OwnerID)
|
||||
var state *game.ShipGroupState
|
||||
var onPlanet *uint
|
||||
var is *game.InSpace
|
||||
for sg := range c.FleetGroups(ri, fi) {
|
||||
if state == nil {
|
||||
s := sg.State()
|
||||
state = &s
|
||||
if planet, ok := sg.OnPlanet(); ok {
|
||||
onPlanet = &planet
|
||||
}
|
||||
is = sg.StateInSpace
|
||||
continue
|
||||
}
|
||||
if *state != sg.State() {
|
||||
panic(fmt.Sprintf("FleetState: one or more ships in race's %q fleet %q has different states", c.g.Race[ri].Name, c.g.Fleets[fi].Name))
|
||||
}
|
||||
if planet, ok := sg.OnPlanet(); ok && onPlanet != nil && *onPlanet != planet {
|
||||
for sg := range c.FleetGroups(ri, fi) {
|
||||
fmt.Println("group", sg.Index, "fleet", sg.FleetID, c.g.Fleets[fi].Name, "state", sg.State(), "on", sg.Destination)
|
||||
}
|
||||
panic(fmt.Sprintf("FleetState: one or more ships in race's %q fleet %q are on different planets: %d <> %d", c.g.Race[ri].Name, c.g.Fleets[fi].Name, *onPlanet, planet))
|
||||
}
|
||||
if (is == nil && sg.StateInSpace != nil) || (is != nil && sg.StateInSpace == nil) {
|
||||
panic(fmt.Sprintf("FleetState: one or more ships in race's %q fleet %q on_planet and in_space at the same time", c.g.Race[ri].Name, c.g.Fleets[fi].Name))
|
||||
}
|
||||
if is != nil && sg.StateInSpace != nil && !is.Equal(*sg.StateInSpace) {
|
||||
panic(fmt.Sprintf("FleetState: one or more ships in race's %q fleet %q has different is_space states", c.g.Race[ri].Name, c.g.Fleets[fi].Name))
|
||||
}
|
||||
}
|
||||
if state == nil {
|
||||
panic(fmt.Sprintf("FleetState: race's %q fleet %q has no ships", c.g.Race[ri].Name, c.g.Fleets[fi].Name))
|
||||
}
|
||||
return *state, onPlanet, is
|
||||
}
|
||||
|
||||
// TODO: Hello! Wanna know fleet's speed? Good. Implement & test this func first.
|
||||
func (c *Cache) FleetSpeed(fl game.Fleet) float64 {
|
||||
result := math.MaxFloat64
|
||||
for sg := range c.ShipGroupsIndex() {
|
||||
if c.ShipGroup(sg).FleetID == nil || *c.ShipGroup(sg).FleetID != fl.ID {
|
||||
continue
|
||||
}
|
||||
st := c.ShipGroupShipClass(sg)
|
||||
// st := g.mustShipType(g.ShipGroups[sg].TypeID)
|
||||
typeSpeed := c.ShipGroup(sg).Speed(st)
|
||||
if typeSpeed < result {
|
||||
result = typeSpeed
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (c *Controller) JoinShipGroupToFleet(raceName, fleetName string, group, count uint) error {
|
||||
ri, err := c.Cache.raceIndex(raceName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.Cache.JoinShipGroupToFleet(ri, fleetName, group, count)
|
||||
}
|
||||
|
||||
func (c *Cache) JoinShipGroupToFleet(ri int, fleetName string, groupIndex, quantity uint) (err error) {
|
||||
c.validateRaceIndex(ri)
|
||||
name, ok := validateTypeName(fleetName)
|
||||
if !ok {
|
||||
return e.NewEntityTypeNameValidationError("%q", name)
|
||||
}
|
||||
sgi, ok := c.raceShipGroupIndex(ri, groupIndex)
|
||||
if !ok {
|
||||
return e.NewEntityNotExistsError("group #%d", groupIndex)
|
||||
}
|
||||
|
||||
// sgi := -1
|
||||
// var maxIndex uint
|
||||
// for i, sg := range g.listIndexShipGroups(ri) {
|
||||
// if sgi < 0 && sg.Index == groupIndex {
|
||||
// sgi = i
|
||||
// }
|
||||
// if sg.Index > maxIndex {
|
||||
// maxIndex = sg.Index
|
||||
// }
|
||||
// }
|
||||
// if sgi < 0 {
|
||||
// return e.NewEntityNotExistsError("group #%d", groupIndex)
|
||||
// }
|
||||
|
||||
if c.ShipGroup(sgi).State() != game.StateInOrbit {
|
||||
return e.NewShipsBusyError()
|
||||
}
|
||||
|
||||
if c.ShipGroup(sgi).Number < quantity {
|
||||
return e.NewJoinFleetGroupNumberNotEnoughError("%d<%d", c.ShipGroup(sgi).Number, quantity)
|
||||
}
|
||||
|
||||
fi, ok := c.fleetIndex(ri, name)
|
||||
if !ok {
|
||||
fi, err = c.createFleet(ri, name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
state, onPlanet, _ := c.FleetState(c.g.Fleets[fi].ID)
|
||||
if state != game.StateInOrbit || *onPlanet != c.ShipGroup(sgi).Destination {
|
||||
return e.NewShipsNotOnSamePlanetError("fleet: %s", fleetName)
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: if g.ShipGroups[sgi].FleetID != nil { // delete old fleet if empty, ALSO mind breaking group }
|
||||
if quantity > 0 && quantity < c.ShipGroup(sgi).Number {
|
||||
nsgi, err := c.breakGroupSafe(ri, groupIndex, quantity)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sgi = nsgi
|
||||
// newGroup := g.ShipGroups[sgi]
|
||||
// newGroup.Number -= quantity
|
||||
// g.ShipGroups[sgi].Number = quantity
|
||||
// newGroup.Index = maxIndex + 1
|
||||
// g.ShipGroups = append(g.ShipGroups, newGroup)
|
||||
}
|
||||
|
||||
// g.ShipGroups[sgi].FleetID = &g.Fleets[fi].ID
|
||||
c.ShipGroupFleet(sgi, &c.g.Fleets[fi].ID)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Controller) JoinFleets(raceName, fleetSourceName, fleetTargetName string) error {
|
||||
ri, err := c.Cache.raceIndex(raceName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.Cache.JoinFleets(ri, fleetSourceName, fleetTargetName)
|
||||
}
|
||||
|
||||
func (c *Cache) JoinFleets(ri int, fleetSourceName, fleetTargetName string) (err error) {
|
||||
fiSource, ok := c.fleetIndex(ri, fleetSourceName)
|
||||
if !ok {
|
||||
return e.NewEntityNotExistsError("source fleet %s", fleetSourceName)
|
||||
}
|
||||
fiTarget, ok := c.fleetIndex(ri, fleetTargetName)
|
||||
if !ok {
|
||||
return e.NewEntityNotExistsError("target fleet %s", fleetTargetName)
|
||||
}
|
||||
srcState, planet1, _ := c.FleetState(c.g.Fleets[fiSource].ID)
|
||||
tgtState, planet2, _ := c.FleetState(c.g.Fleets[fiTarget].ID)
|
||||
if srcState != game.StateInOrbit || srcState != tgtState || *planet1 != *planet2 {
|
||||
return e.NewShipsNotOnSamePlanetError()
|
||||
}
|
||||
for sg := range c.listShipGroups(ri) {
|
||||
if sg.FleetID != nil && *sg.FleetID == c.g.Fleets[fiSource].ID {
|
||||
sg.FleetID = &c.g.Fleets[fiTarget].ID
|
||||
}
|
||||
}
|
||||
return c.deleteFleetSafe(ri, fleetSourceName)
|
||||
}
|
||||
|
||||
func (c *Cache) createFleet(ri int, name string) (int, error) {
|
||||
c.validateRaceIndex(ri)
|
||||
n, ok := validateTypeName(name)
|
||||
if !ok {
|
||||
return 0, e.NewEntityTypeNameValidationError("%q", n)
|
||||
}
|
||||
if _, ok := c.fleetIndex(ri, n); ok {
|
||||
return 0, e.NewEntityTypeNameDuplicateError("fleet %w", n)
|
||||
}
|
||||
fleets := slices.Clone(c.g.Fleets)
|
||||
fleets = append(fleets, game.Fleet{
|
||||
ID: uuid.New(),
|
||||
OwnerID: c.g.Race[ri].ID,
|
||||
Name: n,
|
||||
})
|
||||
c.g.Fleets = fleets
|
||||
i := len(c.g.Fleets) - 1
|
||||
if c.cacheFleetIndexByID != nil {
|
||||
c.cacheFleetIndexByID[c.g.Fleets[i].ID] = i
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func (c *Cache) deleteFleetSafe(ri int, name string) error {
|
||||
fi, ok := c.fleetIndex(ri, name)
|
||||
if !ok {
|
||||
return e.NewEntityNotExistsError("fleet %s", name)
|
||||
}
|
||||
for sg := range c.listShipGroups(ri) {
|
||||
if sg.FleetID != nil && *(sg.FleetID) == c.g.Fleets[fi].ID {
|
||||
return e.NewEntityInUseError("fleet %s: race %s, group #%d", name, c.g.Race[ri].Name, sg.Number)
|
||||
}
|
||||
}
|
||||
if c.cacheFleetIndexByID != nil {
|
||||
delete(c.cacheFleetIndexByID, c.g.Fleets[fi].ID)
|
||||
}
|
||||
c.g.Fleets = append(c.g.Fleets[:fi], c.g.Fleets[fi+1:]...)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Internal funcs
|
||||
|
||||
func (c *Cache) FleetIndex(ID uuid.UUID) (int, bool) {
|
||||
if c.cacheFleetIndexByID == nil {
|
||||
c.cacheFleetIndexByID = make(map[uuid.UUID]int)
|
||||
for i := range c.g.Fleets {
|
||||
c.cacheFleetIndexByID[c.g.Fleets[i].ID] = i
|
||||
}
|
||||
}
|
||||
if v, ok := c.cacheFleetIndexByID[ID]; ok {
|
||||
return v, true
|
||||
} else {
|
||||
return -1, false
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: rename / [fleetIndex]
|
||||
func (c *Cache) MustFleetIndex(ID uuid.UUID) int {
|
||||
if v, ok := c.FleetIndex(ID); ok {
|
||||
return v
|
||||
} else {
|
||||
panic(fmt.Sprintf("fleet not found by ID=%v", ID))
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Cache) FleetGroups(ri, fi int) iter.Seq[*game.ShipGroup] {
|
||||
c.validateRaceIndex(ri)
|
||||
c.validateFleetIndex(fi)
|
||||
return func(yield func(*game.ShipGroup) bool) {
|
||||
for sg := range c.listShipGroups(ri) {
|
||||
if sg.FleetID != nil && *sg.FleetID == c.g.Fleets[fi].ID {
|
||||
if !yield(sg) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Cache) listFleets(ri int) iter.Seq[*game.Fleet] {
|
||||
c.validateRaceIndex(ri)
|
||||
return func(yield func(*game.Fleet) bool) {
|
||||
for i := range c.g.Fleets {
|
||||
if c.g.Fleets[i].OwnerID == c.g.Race[ri].ID {
|
||||
if !yield(&c.g.Fleets[i]) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Cache) fleetIndex(ri int, name string) (int, bool) {
|
||||
c.validateRaceIndex(ri)
|
||||
if i := slices.IndexFunc(c.g.Fleets, func(f game.Fleet) bool { return f.OwnerID == c.g.Race[ri].ID && f.Name == name }); i < 0 {
|
||||
return -1, false
|
||||
} else {
|
||||
return i, true
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Cache) mustFleetIndex(ri int, name string) int {
|
||||
if v, ok := c.fleetIndex(ri, name); ok {
|
||||
return v
|
||||
} else {
|
||||
panic(fmt.Sprintf("fleet not found: race_idx=%d name=%q", ri, name))
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Cache) validateFleetIndex(i int) {
|
||||
if i >= len(c.g.Fleets) {
|
||||
panic(fmt.Sprintf("race index out of range: %d >= %d", i, len(c.g.Fleets)))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user