From 46b4ab96f0d58fc922f1baa2d38f33a2c938170e Mon Sep 17 00:00:00 2001 From: Ilia Denisov Date: Wed, 4 Feb 2026 20:26:52 +0200 Subject: [PATCH] refactor: FleetState --- internal/controller/battle.go | 2 +- internal/controller/fleet.go | 81 ++++++++++++++++++-------- internal/controller/fleet_send.go | 9 +-- internal/controller/fleet_send_test.go | 8 +-- internal/controller/fleet_test.go | 12 ++-- internal/controller/report.go | 8 +-- 6 files changed, 77 insertions(+), 43 deletions(-) diff --git a/internal/controller/battle.go b/internal/controller/battle.go index 5af77e5..f599a4f 100644 --- a/internal/controller/battle.go +++ b/internal/controller/battle.go @@ -173,7 +173,7 @@ func SingleBattle(c *Cache, b *Battle) { }) if destroyed { - c.ShipGroupNumber(defIdx, c.ShipGroup(defIdx).Number-1) + c.ShipGroupDestroyItem(defIdx) } if c.ShipGroup(defIdx).Number == 0 { delete(b.attacker, defIdx) // Eliminated group cant attack anyone diff --git a/internal/controller/fleet.go b/internal/controller/fleet.go index 03bba02..6bc1b13 100644 --- a/internal/controller/fleet.go +++ b/internal/controller/fleet.go @@ -11,42 +11,74 @@ import ( "github.com/iliadenisov/galaxy/internal/model/game" ) -func (c *Cache) FleetState(fleetID uuid.UUID) (game.ShipGroupState, *uint, *game.InSpace) { +var fleetStateNil = game.ShipGroupState("-") + +type FleetState struct { + State game.ShipGroupState + Destination uint + InSpace func() (game.InSpace, bool) + OnPlanet func() (uint, bool) +} + +func (fs *FleetState) inSpace() bool { + _, ok := fs.InSpace() + return ok +} + +func (fs FleetState) OnSamePlanet(other FleetState) bool { + op1, ok := fs.OnPlanet() + if !ok { + return false + } + op2, ok := other.OnPlanet() + if !ok { + return false + } + return op1 == op2 +} + +func (c *Cache) FleetState(fleetID uuid.UUID) FleetState { fi := c.MustFleetIndex(fleetID) ri := c.RaceIndex(c.g.Fleets[fi].OwnerID) - var state *game.ShipGroupState - var onPlanet *uint - var is *game.InSpace + fs := &FleetState{ + State: fleetStateNil, + InSpace: func() (game.InSpace, bool) { return game.InSpace{}, false }, + OnPlanet: func() (uint, bool) { return 0, false }, + } for sg := range c.FleetGroups(ri, fi) { - if state == nil { - s := sg.State() - state = &s + if fs.State == fleetStateNil { + fs.State = sg.State() + fs.Destination = sg.Destination if planet, ok := sg.OnPlanet(); ok { - onPlanet = &planet + fs.OnPlanet = func() (uint, bool) { return planet, ok } + } + if sg.StateInSpace != nil { + fs.InSpace = func() (game.InSpace, bool) { return *sg.StateInSpace, true } } - is = sg.StateInSpace continue } - if *state != sg.State() { + if fs.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 fs.Destination != sg.Destination { + panic(fmt.Sprintf("FleetState: one or more ships in race's %q fleet %q has different destination", c.g.Race[ri].Name, c.g.Fleets[fi].Name)) } - if (is == nil && sg.StateInSpace != nil) || (is != nil && sg.StateInSpace == nil) { + if planet, ok := sg.OnPlanet(); ok { + if onPlanet, ok := fs.OnPlanet(); ok && onPlanet != planet { + 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 (!fs.inSpace() && sg.StateInSpace != nil) || (fs.inSpace() && 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) { + if is, ok := fs.InSpace(); ok && 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 { + if fs.State == fleetStateNil { 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 + return *fs } // TODO: Hello! Wanna know fleet's speed? Good. Implement & test this func first. @@ -109,8 +141,8 @@ func (c *Cache) JoinShipGroupToFleet(ri int, fleetName string, groupIndex, quant return err } } else { - state, onPlanet, _ := c.FleetState(c.g.Fleets[fi].ID) - if state != game.StateInOrbit || *onPlanet != c.ShipGroup(sgi).Destination { + fleetState := c.FleetState(c.g.Fleets[fi].ID) + if onPlanet, ok := fleetState.OnPlanet(); (ok && onPlanet != c.ShipGroup(sgi).Destination) || fleetState.State != game.StateInOrbit { return e.NewShipsNotOnSamePlanetError("fleet: %s", fleetName) } } @@ -164,11 +196,12 @@ func (c *Cache) JoinFleets(ri int, fleetSourceName, fleetTargetName string) (err 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 { + stateSrc := c.FleetState(c.g.Fleets[fiSource].ID) + stateDst := c.FleetState(c.g.Fleets[fiTarget].ID) + if !stateSrc.OnSamePlanet(stateDst) { 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 diff --git a/internal/controller/fleet_send.go b/internal/controller/fleet_send.go index 7b1d2e0..dc0dc9d 100644 --- a/internal/controller/fleet_send.go +++ b/internal/controller/fleet_send.go @@ -21,12 +21,13 @@ func (c *Controller) SendFleet(raceName, fleetName string, planetNumber uint) er func (c *Cache) SendFleet(ri, fi int, planetNumber uint) error { c.validateRaceIndex(ri) c.validateFleetIndex(fi) - state, sourcePlanet, _ := c.FleetState(c.g.Fleets[fi].ID) - if game.StateInOrbit != state && game.StateLaunched != state { + fleetState := c.FleetState(c.g.Fleets[fi].ID) + sourcePlanet, ok := fleetState.OnPlanet() + if !ok || game.StateInOrbit != fleetState.State && game.StateLaunched != fleetState.State { return e.NewShipsBusyError() } - p1, ok := c.Planet(*sourcePlanet) + p1, ok := c.Planet(sourcePlanet) if !ok { return e.NewGameStateError("source planet #%d does not exists", sourcePlanet) } @@ -46,7 +47,7 @@ func (c *Cache) SendFleet(ri, fi int, planetNumber uint) error { } } - if *sourcePlanet == planetNumber { + if sourcePlanet == planetNumber { c.UnsendFleet(ri, fi) return nil } diff --git a/internal/controller/fleet_send_test.go b/internal/controller/fleet_send_test.go index 276251f..24d2738 100644 --- a/internal/controller/fleet_send_test.go +++ b/internal/controller/fleet_send_test.go @@ -70,15 +70,15 @@ func TestSendFleet(t *testing.T) { e.GenericErrorText(e.ErrSendShipHasNoDrives)) assert.NoError(t, g.SendFleet(Race_0.Name, fleetSending, 2)) - state, _, _ := c.FleetState(c.MustFleetID(Race_0_idx, fleetSending)) - assert.Equal(t, game.StateLaunched, state) + fleetState := c.FleetState(c.MustFleetID(Race_0_idx, fleetSending)) + assert.Equal(t, game.StateLaunched, fleetState.State) for sg := range c.FleetGroups(Race_0_idx, c.MustFleetIndex(c.MustFleetID(Race_0_idx, fleetSending))) { assert.Equal(t, game.StateLaunched, sg.State()) } assert.NoError(t, g.SendFleet(Race_0.Name, fleetSending, 0)) - state, _, _ = c.FleetState(c.MustFleetID(Race_0_idx, fleetSending)) - assert.Equal(t, game.StateInOrbit, state) + fleetState = c.FleetState(c.MustFleetID(Race_0_idx, fleetSending)) + assert.Equal(t, game.StateInOrbit, fleetState.State) for sg := range c.FleetGroups(Race_0_idx, c.MustFleetIndex(c.MustFleetID(Race_0_idx, fleetSending))) { assert.Equal(t, game.StateInOrbit, sg.State()) } diff --git a/internal/controller/fleet_test.go b/internal/controller/fleet_test.go index e92de2d..1420253 100644 --- a/internal/controller/fleet_test.go +++ b/internal/controller/fleet_test.go @@ -41,8 +41,8 @@ func TestJoinShipGroupToFleet(t *testing.T) { gi := 0 assert.Len(t, fleets, 1) assert.Equal(t, fleets[0].Name, fleetOne) - state, _, _ := c.FleetState(fleets[0].ID) - assert.Equal(t, game.StateInOrbit, state) + fleetState := c.FleetState(fleets[0].ID) + assert.Equal(t, game.StateInOrbit, fleetState.State) assert.NotNil(t, groups[gi].FleetID) assert.Equal(t, fleets[0].ID, *groups[gi].FleetID) @@ -56,8 +56,8 @@ func TestJoinShipGroupToFleet(t *testing.T) { assert.Len(t, groups, 3) assert.Len(t, fleets, 2) assert.Equal(t, fleets[1].Name, fleetTwo) - state, _, _ = c.FleetState(fleets[1].ID) - assert.Equal(t, game.StateInOrbit, state) + fleetState = c.FleetState(fleets[1].ID) + assert.Equal(t, game.StateInOrbit, fleetState.State) gi = 2 assert.Len(t, groups, 3) @@ -78,8 +78,8 @@ func TestJoinShipGroupToFleet(t *testing.T) { groups = slices.Collect(c.RaceShipGroups(Race_0_idx)) assert.NotNil(t, groups[gi].FleetID) assert.Equal(t, fleets[0].ID, *groups[gi].FleetID) - state, _, _ = c.FleetState(fleets[0].ID) - assert.Equal(t, game.StateInOrbit, state) + fleetState = c.FleetState(fleets[0].ID) + assert.Equal(t, game.StateInOrbit, fleetState.State) // group not In_Orbit assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Gunship, R0_Planet_0_num, 7)) diff --git a/internal/controller/report.go b/internal/controller/report.go index 54ae0f0..20d5ce2 100644 --- a/internal/controller/report.go +++ b/internal/controller/report.go @@ -585,15 +585,15 @@ func (c *Cache) ReportLocalFleet(ri int, rep *mr.Report) { } speed, _ := c.FleetSpeedAndMass(fi) - state, _, inSpace := c.FleetState(fl.ID) + fleetState := c.FleetState(fl.ID) sliceIndexValidate(&rep.LocalFleet, i) rep.LocalFleet[i].Name = fl.Name rep.LocalFleet[i].Groups = uint(len(gid)) rep.LocalFleet[i].Speed = mr.F(speed) - rep.LocalFleet[i].State = state.String() - rep.LocalFleet[i].Destination = c.ShipGroup(gid[0]).Destination // FIXME: get fleet destination with some nicer way - if inSpace != nil { + rep.LocalFleet[i].State = fleetState.State.String() + rep.LocalFleet[i].Destination = fleetState.Destination + if inSpace, ok := fleetState.InSpace(); ok { rep.LocalFleet[i].Origin = &inSpace.Origin p2 := c.MustPlanet(rep.LocalFleet[i].Destination) rangeToDestination := mr.F(util.ShortDistance(c.g.Map.Width, c.g.Map.Height, inSpace.X.F(), inSpace.Y.F(), p2.X.F(), p2.Y.F()))