From 56998d4c2df0bdc36000298ae9818daeded30356 Mon Sep 17 00:00:00 2001 From: IliaDenisov Date: Tue, 10 Feb 2026 20:54:43 +0300 Subject: [PATCH] refactor: group uuid instead of index --- internal/controller/cache.go | 12 +- internal/controller/command.go | 32 ++-- internal/controller/controller.go | 16 +- internal/controller/controller_export_test.go | 30 ++-- internal/controller/controller_test.go | 5 - internal/controller/fleet.go | 20 ++- internal/controller/fleet_send.go | 12 +- internal/controller/fleet_send_test.go | 20 +-- internal/controller/fleet_test.go | 34 ++-- internal/controller/planet_test.go | 4 +- internal/controller/report.go | 2 +- internal/controller/route.go | 9 +- internal/controller/route_test.go | 8 +- internal/controller/ship_class.go | 2 +- internal/controller/ship_group.go | 159 ++++++++---------- internal/controller/ship_group_move_test.go | 6 +- internal/controller/ship_group_send.go | 59 +++---- internal/controller/ship_group_send_test.go | 51 +++--- internal/controller/ship_group_test.go | 142 ++++++++-------- internal/controller/ship_group_upgrade.go | 11 +- .../controller/ship_group_upgrade_test.go | 27 +-- internal/model/game/group.go | 8 +- internal/model/game/group_test.go | 4 +- internal/model/report/ship.go | 8 +- internal/number/number.go | 8 - internal/number/number_test.go | 7 - 26 files changed, 333 insertions(+), 363 deletions(-) diff --git a/internal/controller/cache.go b/internal/controller/cache.go index 00cb3d8..1f7defb 100644 --- a/internal/controller/cache.go +++ b/internal/controller/cache.go @@ -69,14 +69,14 @@ func (c *Cache) cacheShipsAndGroups() { } else { c.cacheShipClassByShipGroupIndex = make(map[int]*game.ShipType) } - for groupIndex := range c.g.ShipGroups { - ri := c.RaceIndex(c.g.ShipGroups[groupIndex].OwnerID) - c.cacheRaceIndexByShipGroupIndex[groupIndex] = ri - sti, ok := ShipClassIndex(c.g, ri, c.g.ShipGroups[groupIndex].TypeID) + for sgi := range c.g.ShipGroups { + ri := c.RaceIndex(c.g.ShipGroups[sgi].OwnerID) + c.cacheRaceIndexByShipGroupIndex[sgi] = ri + sci, ok := ShipClassIndex(c.g, ri, c.g.ShipGroups[sgi].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)) + panic(fmt.Sprintf("CollectPlanetGroups: ship class not found for race=%q group=%v", c.g.Race[ri].Name, c.g.ShipGroups[sgi].ID)) } - c.cacheShipClassByShipGroupIndex[groupIndex] = &c.g.Race[ri].ShipTypes[sti] + c.cacheShipClassByShipGroupIndex[sgi] = &c.g.Race[ri].ShipTypes[sci] } } diff --git a/internal/controller/command.go b/internal/controller/command.go index 89451d3..53468b0 100644 --- a/internal/controller/command.go +++ b/internal/controller/command.go @@ -78,7 +78,7 @@ func (c *Controller) ShipClassRemove(actor, typeName string) error { return c.Cache.shipClassRemove(ri, typeName) } -func (c *Controller) ShipGroupLoad(actor string, groupIndex uint, cargoType string, ships uint, quantity float64) error { +func (c *Controller) ShipGroupLoad(actor string, groupID uuid.UUID, cargoType string, ships uint, quantity float64) error { ri, err := c.Cache.validActor(actor) if err != nil { return err @@ -87,31 +87,31 @@ func (c *Controller) ShipGroupLoad(actor string, groupIndex uint, cargoType stri if !ok { return e.NewCargoTypeInvalidError(cargoType) } - return c.Cache.shipGroupLoad(ri, groupIndex, ct, ships, quantity) + return c.Cache.shipGroupLoad(ri, groupID, ct, ships, quantity) } -func (c *Controller) ShipGroupUnload(actor string, groupIndex uint, ships uint, quantity float64) error { +func (c *Controller) ShipGroupUnload(actor string, groupID uuid.UUID, ships uint, quantity float64) error { ri, err := c.Cache.validActor(actor) if err != nil { return err } - return c.Cache.shipGroupUnload(ri, groupIndex, ships, quantity) + return c.Cache.shipGroupUnload(ri, groupID, ships, quantity) } -func (c *Controller) ShipGroupSend(actor string, groupIndex, planetNumber, quantity uint) error { +func (c *Controller) ShipGroupSend(actor string, groupID uuid.UUID, planetNumber, quantity uint) error { ri, err := c.Cache.validActor(actor) if err != nil { return err } - return c.Cache.shipGroupSend(ri, groupIndex, planetNumber, quantity) + return c.Cache.shipGroupSend(ri, groupID, planetNumber, quantity) } -func (c *Controller) ShipGroupUpgrade(actor string, groupIndex uint, techInput string, limitShips uint, limitLevel float64) error { +func (c *Controller) ShipGroupUpgrade(actor string, groupID uuid.UUID, techInput string, limitShips uint, limitLevel float64) error { ri, err := c.Cache.validActor(actor) if err != nil { return err } - return c.Cache.shipGroupUpgrade(ri, groupIndex, techInput, limitShips, limitLevel) + return c.Cache.shipGroupUpgrade(ri, groupID, techInput, limitShips, limitLevel) } func (c *Controller) ShipGroupMerge(actor string) error { @@ -123,23 +123,23 @@ func (c *Controller) ShipGroupMerge(actor string) error { return nil } -func (c *Controller) ShipGroupBreak(actor string, groupIndex, quantity uint) error { +func (c *Controller) ShipGroupBreak(actor string, groupID uuid.UUID, quantity uint) error { ri, err := c.Cache.validActor(actor) if err != nil { return err } - return c.Cache.ShipGroupBreak(ri, groupIndex, quantity) + return c.Cache.ShipGroupBreak(ri, groupID, quantity) } -func (c *Controller) ShipGroupDismantle(actor string, groupIndex, quantity uint) error { +func (c *Controller) ShipGroupDismantle(actor string, groupID uuid.UUID, quantity uint) error { ri, err := c.Cache.validActor(actor) if err != nil { return err } - return c.Cache.shipGroupDismantle(ri, groupIndex, quantity) + return c.Cache.shipGroupDismantle(ri, groupID, quantity) } -func (c *Controller) ShipGroupTransfer(actor, acceptor string, groupIndex, quantity uint) error { +func (c *Controller) ShipGroupTransfer(actor, acceptor string, groupID uuid.UUID, quantity uint) error { ri, err := c.Cache.validActor(actor) if err != nil { return err @@ -148,15 +148,15 @@ func (c *Controller) ShipGroupTransfer(actor, acceptor string, groupIndex, quant if err != nil { return err } - return c.Cache.shipGroupTransfer(ri, riAccept, groupIndex, quantity) + return c.Cache.shipGroupTransfer(ri, riAccept, groupID, quantity) } -func (c *Controller) ShipGroupJoinFleet(actor, fleetName string, group, count uint) error { +func (c *Controller) ShipGroupJoinFleet(actor, fleetName string, groupID uuid.UUID, count uint) error { ri, err := c.Cache.validActor(actor) if err != nil { return err } - return c.Cache.ShipGroupJoinFleet(ri, fleetName, group, count) + return c.Cache.ShipGroupJoinFleet(ri, fleetName, groupID, count) } func (c *Controller) FleetMerge(actor, fleetSourceName, fleetTargetName string) error { diff --git a/internal/controller/controller.go b/internal/controller/controller.go index d568f13..e8d0f7a 100644 --- a/internal/controller/controller.go +++ b/internal/controller/controller.go @@ -51,15 +51,15 @@ type Ctrl interface { ShipClassCreate(actor, typeName string, drive float64, ammo int, weapons, shileds, cargo float64) error ShipClassMerge(actor, name, targetName string) error ShipClassRemove(actor, typeName string) error - ShipGroupLoad(actor string, groupIndex uint, cargoType string, ships uint, quantity float64) error - ShipGroupUnload(actor string, groupIndex uint, ships uint, quantity float64) error - ShipGroupSend(actor string, groupIndex, planetNumber, quantity uint) error - ShipGroupUpgrade(actor string, groupIndex uint, techInput string, limitShips uint, limitLevel float64) error - ShipGroupBreak(actor string, groupIndex, quantity uint) error + ShipGroupLoad(actor string, groupID uuid.UUID, cargoType string, ships uint, quantity float64) error + ShipGroupUnload(actor string, groupID uuid.UUID, ships uint, quantity float64) error + ShipGroupSend(actor string, groupID uuid.UUID, planetNumber, quantity uint) error + ShipGroupUpgrade(actor string, groupID uuid.UUID, techInput string, limitShips uint, limitLevel float64) error + ShipGroupBreak(actor string, groupID uuid.UUID, quantity uint) error ShipGroupMerge(actor string) error - ShipGroupDismantle(actor string, groupIndex, quantity uint) error - ShipGroupTransfer(actor, acceptor string, groupIndex, quantity uint) error - ShipGroupJoinFleet(actor, fleetName string, group, count uint) error + ShipGroupDismantle(actor string, groupID uuid.UUID, quantity uint) error + ShipGroupTransfer(actor, acceptor string, groupID uuid.UUID, quantity uint) error + ShipGroupJoinFleet(actor, fleetName string, groupID uuid.UUID, count uint) error FleetMerge(actor, fleetSourceName, fleetTargetName string) error FleetSend(actor, fleetName string, planetNumber uint) error ScienceCreate(actor, typeName string, drive, weapons, shields, cargo float64) error diff --git a/internal/controller/controller_export_test.go b/internal/controller/controller_export_test.go index 6191e58..f50aca0 100644 --- a/internal/controller/controller_export_test.go +++ b/internal/controller/controller_export_test.go @@ -1,13 +1,32 @@ package controller import ( - "fmt" "iter" "github.com/google/uuid" + e "github.com/iliadenisov/galaxy/internal/error" "github.com/iliadenisov/galaxy/internal/model/game" ) +func (c *Cache) CreateShips(ri int, shipTypeName string, planetNumber uint, quantity int) error { + class, _, ok := c.ShipClass(ri, shipTypeName) + if !ok { + return e.NewEntityNotExistsError("ship class q", shipTypeName) + } + + p, ok := c.Planet(planetNumber) + if !ok { + return e.NewEntityNotExistsError("planet #%d", planetNumber) + } + if !p.OwnedBy(c.g.Race[ri].ID) { + return e.NewEntityNotOwnedError("planet #%d", planetNumber) + } + + c.createShipsUnsafe(ri, class.ID, p.Number, uint(quantity)) + + return nil +} + func (c *Cache) AddRace(n string) (int, uuid.UUID) { id := uuid.New() r := &game.Race{ @@ -57,15 +76,6 @@ func (c *Cache) MustFleetID(ri int, name string) uuid.UUID { panic("fleet not found") } -func (c *Cache) MustShipGroup(ri int, index uint) *game.ShipGroup { - for sg := range c.listShipGroups(ri) { - if sg.Index == index { - return sg - } - } - panic(fmt.Sprintf("race i=%d have no group i=%d", ri, index)) -} - func (c *Cache) MustShipClass(ri int, name string) *game.ShipType { st, _, ok := c.ShipClass(ri, name) if !ok { diff --git a/internal/controller/controller_test.go b/internal/controller/controller_test.go index 1a812fd..53de96d 100644 --- a/internal/controller/controller_test.go +++ b/internal/controller/controller_test.go @@ -130,8 +130,6 @@ func newGame() *game.Game { func newCache() (*controller.Cache, *controller.Controller) { ctl := controller.NewGameController(newGame()) - // g := newGame() - // c := controller.NewCache(g) c := ctl.Cache assertNoError(c.ShipClassCreate(Race_0_idx, Race_0_Gunship, 60, 3, 30, 100, 0)) assertNoError(c.ShipClassCreate(Race_0_idx, Race_0_Freighter, 8, 0, 0, 2, 10)) @@ -141,9 +139,6 @@ func newCache() (*controller.Cache, *controller.Controller) { assertNoError(c.ShipClassCreate(Race_1_idx, Race_1_Freighter, 8, 0, 0, 2, 10)) assertNoError(c.ShipClassCreate(Race_1_idx, ShipType_Cruiser, 15, 2, 15, 15, 0)) // same name - different type (why.) - // ctl := controller.NewRepoController(nil) - // ctl.Cache = c - return c, ctl } diff --git a/internal/controller/fleet.go b/internal/controller/fleet.go index 66b95b0..c21ebff 100644 --- a/internal/controller/fleet.go +++ b/internal/controller/fleet.go @@ -46,7 +46,8 @@ func (c *Cache) FleetState(fleetID uuid.UUID) FleetState { InSpace: func() (game.InSpace, bool) { return game.InSpace{}, false }, AtPlanet: func() (uint, bool) { return 0, false }, } - for sg := range c.FleetGroups(ri, fi) { + for sgi := range c.FleetGroupIdx(ri, fi) { + sg := c.ShipGroup(sgi) if fs.State == fleetStateNil { fs.State = sg.State() fs.Destination = sg.Destination @@ -100,15 +101,15 @@ func (c *Cache) FleetSpeedAndMass(fi int) (float64, float64) { return speed, mass } -func (c *Cache) ShipGroupJoinFleet(ri int, fleetName string, groupIndex, quantity uint) (err error) { +func (c *Cache) ShipGroupJoinFleet(ri int, fleetName string, groupID uuid.UUID, quantity uint) (err error) { c.validateRaceIndex(ri) name, ok := util.ValidateTypeName(fleetName) if !ok { return e.NewEntityTypeNameValidationError("%q", name) } - sgi, ok := c.raceShipGroupIndex(ri, groupIndex) + sgi, ok := c.raceShipGroupIndex(ri, groupID) if !ok { - return e.NewEntityNotExistsError("group #%d", groupIndex) + return e.NewEntityNotExistsError("group %s", groupID) } if state := c.ShipGroup(sgi).State(); state != game.StateInOrbit { @@ -139,7 +140,7 @@ func (c *Cache) ShipGroupJoinFleet(ri int, fleetName string, groupIndex, quantit } if quantity > 0 && quantity < c.ShipGroup(sgi).Number { - nsgi, err := c.breakGroupSafe(ri, groupIndex, quantity) + nsgi, err := c.breakGroupSafe(ri, groupID, quantity) if err != nil { return err } @@ -268,13 +269,14 @@ func (c *Cache) MustFleetIndex(ID uuid.UUID) int { } } -func (c *Cache) FleetGroups(ri, fi int) iter.Seq[*game.ShipGroup] { +func (c *Cache) FleetGroupIdx(ri, fi int) iter.Seq[int] { c.validateRaceIndex(ri) c.validateFleetIndex(fi) - return func(yield func(*game.ShipGroup) bool) { - for sg := range c.listShipGroups(ri) { + return func(yield func(int) bool) { + for sgi := range c.listShipGroupIdx(ri) { + sg := c.ShipGroup(sgi) if sg.FleetID != nil && *sg.FleetID == c.g.Fleets[fi].ID { - if !yield(sg) { + if !yield(sgi) { break } } diff --git a/internal/controller/fleet_send.go b/internal/controller/fleet_send.go index 9136a1f..719ae0d 100644 --- a/internal/controller/fleet_send.go +++ b/internal/controller/fleet_send.go @@ -28,8 +28,8 @@ func (c *Cache) FleetSend(ri, fi int, planetNumber uint) error { return e.NewSendUnreachableDestinationError("range=%.03f", rangeToDestination) } - for sg := range c.FleetGroups(ri, fi) { - st := c.MustShipType(ri, sg.TypeID) + for sgi := range c.FleetGroupIdx(ri, fi) { + st := c.MustShipType(ri, c.ShipGroup(sgi).TypeID) if st.DriveBlockMass() == 0 { return e.NewSendShipHasNoDrivesError("Class=%s", st.Name) } @@ -48,15 +48,15 @@ func (c *Cache) FleetSend(ri, fi int, planetNumber uint) error { func (c *Cache) LaunchFleet(ri, fi int, destination uint) { c.validateRaceIndex(ri) c.validateFleetIndex(fi) - for sg := range c.FleetGroups(ri, fi) { - c.LaunchShips(sg, destination) + for sgi := range c.FleetGroupIdx(ri, fi) { + c.LaunchShips(sgi, destination) } } func (c *Cache) UnsendFleet(ri, fi int) { c.validateRaceIndex(ri) c.validateFleetIndex(fi) - for sg := range c.FleetGroups(ri, fi) { - c.UnsendShips(sg) + for sgi := range c.FleetGroupIdx(ri, fi) { + c.UnsendShips(sgi) } } diff --git a/internal/controller/fleet_send_test.go b/internal/controller/fleet_send_test.go index 8a30ec3..409127b 100644 --- a/internal/controller/fleet_send_test.go +++ b/internal/controller/fleet_send_test.go @@ -29,22 +29,22 @@ func TestFleetSend(t *testing.T) { fleetUnmovable := "R0_Fleet_unmovable" fleetUnmovable2 := "R0_Fleet_unmovable2" - assert.NoError(t, g.ShipGroupJoinFleet(Race_0.Name, fleetSending, 1, 0)) + assert.NoError(t, g.ShipGroupJoinFleet(Race_0.Name, fleetSending, c.ShipGroup(0).ID, 0)) assert.Len(t, slices.Collect(c.ListFleets(Race_0_idx)), 1) - assert.NoError(t, g.ShipGroupJoinFleet(Race_0.Name, fleetSending, 3, 0)) + assert.NoError(t, g.ShipGroupJoinFleet(Race_0.Name, fleetSending, c.ShipGroup(2).ID, 0)) assert.Len(t, slices.Collect(c.ListFleets(Race_0_idx)), 1) - assert.NoError(t, g.ShipGroupJoinFleet(Race_0.Name, fleetInSpace, 2, 0)) + assert.NoError(t, g.ShipGroupJoinFleet(Race_0.Name, fleetInSpace, c.ShipGroup(1).ID, 0)) assert.Len(t, slices.Collect(c.ListFleets(Race_0_idx)), 2) - assert.NoError(t, g.ShipGroupJoinFleet(Race_0.Name, fleetUnmovable, 3, 0)) + assert.NoError(t, g.ShipGroupJoinFleet(Race_0.Name, fleetUnmovable, c.ShipGroup(2).ID, 0)) assert.Len(t, slices.Collect(c.ListFleets(Race_0_idx)), 3) - assert.NoError(t, g.ShipGroupJoinFleet(Race_0.Name, fleetUnmovable2, 4, 0)) + assert.NoError(t, g.ShipGroupJoinFleet(Race_0.Name, fleetUnmovable2, c.ShipGroup(3).ID, 0)) assert.Len(t, slices.Collect(c.ListFleets(Race_0_idx)), 4) - assert.NoError(t, g.ShipGroupJoinFleet(Race_0.Name, fleetUnmovable, 4, 0)) + assert.NoError(t, g.ShipGroupJoinFleet(Race_0.Name, fleetUnmovable, c.ShipGroup(3).ID, 0)) assert.Len(t, slices.Collect(c.ListFleets(Race_0_idx)), 3) // group #2 - in_space @@ -75,14 +75,14 @@ func TestFleetSend(t *testing.T) { assert.NoError(t, g.FleetSend(Race_0.Name, fleetSending, 2)) 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()) + for sgi := range c.FleetGroupIdx(Race_0_idx, c.MustFleetIndex(c.MustFleetID(Race_0_idx, fleetSending))) { + assert.Equal(t, game.StateLaunched, c.ShipGroup(sgi).State()) } assert.NoError(t, g.FleetSend(Race_0.Name, fleetSending, 0)) 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()) + for sgi := range c.FleetGroupIdx(Race_0_idx, c.MustFleetIndex(c.MustFleetID(Race_0_idx, fleetSending))) { + assert.Equal(t, game.StateInOrbit, c.ShipGroup(sgi).State()) } } diff --git a/internal/controller/fleet_test.go b/internal/controller/fleet_test.go index b8a747d..f3643dd 100644 --- a/internal/controller/fleet_test.go +++ b/internal/controller/fleet_test.go @@ -5,6 +5,7 @@ import ( "slices" "testing" + "github.com/google/uuid" e "github.com/iliadenisov/galaxy/internal/error" "github.com/iliadenisov/galaxy/internal/model/game" "github.com/stretchr/testify/assert" @@ -12,7 +13,7 @@ import ( func TestShipGroupJoinFleet(t *testing.T) { c, g := newCache() - var groupIndex uint = 1 + groupIndex := uuid.Nil fleetOne := "R0_Fleet_one" fleetTwo := "R0_Fleet_two" @@ -27,6 +28,7 @@ func TestShipGroupJoinFleet(t *testing.T) { // creating ShipGroup assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 5)) + groupIndex = c.ShipGroup(0).ID assert.ErrorContains(t, g.ShipGroupJoinFleet(UnknownRace, fleetOne, groupIndex, 0), @@ -57,7 +59,7 @@ func TestShipGroupJoinFleet(t *testing.T) { // create another ShipGroup assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Gunship, R0_Planet_0_num, 3)) - groupIndex = 2 + groupIndex = c.ShipGroup(1).ID assert.NoError(t, g.ShipGroupJoinFleet(Race_0.Name, fleetTwo, groupIndex, 2)) fleets = slices.Collect(c.ListFleets(Race_0_idx)) groups = slices.Collect(c.RaceShipGroups(Race_0_idx)) @@ -72,14 +74,12 @@ func TestShipGroupJoinFleet(t *testing.T) { assert.NotNil(t, groups[gi].FleetID) assert.Equal(t, fleets[1].ID, *groups[gi].FleetID) assert.Equal(t, uint(2), groups[gi].Number) - assert.Equal(t, uint(3), groups[gi].Index) gi = 1 assert.Nil(t, groups[gi].FleetID) assert.Equal(t, uint(1), groups[gi].Number) - assert.Equal(t, uint(2), groups[gi].Index) - groupIndex = groups[gi].Index + groupIndex = groups[gi].ID assert.NoError(t, g.ShipGroupJoinFleet(Race_0.Name, fleetOne, groupIndex, 0)) fleets = slices.Collect(c.ListFleets(Race_0_idx)) assert.Len(t, fleets, 2) @@ -94,7 +94,7 @@ func TestShipGroupJoinFleet(t *testing.T) { gi = 3 c.ShipGroup(gi).StateInSpace = &InSpace assert.ErrorContains(t, - g.ShipGroupJoinFleet(Race_0.Name, fleetOne, c.ShipGroup(gi).Index, 0), + g.ShipGroupJoinFleet(Race_0.Name, fleetOne, c.ShipGroup(gi).ID, 0), e.GenericErrorText(e.ErrShipsBusy)) c.ShipGroup(gi).StateInSpace = nil @@ -102,7 +102,7 @@ func TestShipGroupJoinFleet(t *testing.T) { c.ShipGroup(0).StateInSpace = &InSpace c.ShipGroup(1).StateInSpace = c.ShipGroup(0).StateInSpace assert.ErrorContains(t, - g.ShipGroupJoinFleet(Race_0.Name, fleetOne, c.ShipGroup(gi).Index, 0), + g.ShipGroupJoinFleet(Race_0.Name, fleetOne, c.ShipGroup(gi).ID, 0), e.GenericErrorText(e.ErrShipsNotOnSamePlanet)) } @@ -133,21 +133,21 @@ func TestFleetMerge(t *testing.T) { e.GenericErrorText(e.ErrRaceExinct)) assert.ErrorContains(t, - g.ShipGroupJoinFleet(UnknownRace, fleetSourceOne, 1, 0), + g.ShipGroupJoinFleet(UnknownRace, fleetSourceOne, c.ShipGroup(0).ID, 0), e.GenericErrorText(e.ErrInputUnknownRace)) assert.ErrorContains(t, - g.ShipGroupJoinFleet(Race_Extinct.Name, fleetSourceOne, 1, 0), + g.ShipGroupJoinFleet(Race_Extinct.Name, fleetSourceOne, c.ShipGroup(0).ID, 0), e.GenericErrorText(e.ErrRaceExinct)) - assert.NoError(t, g.ShipGroupJoinFleet(Race_0.Name, fleetSourceOne, 1, 0)) + assert.NoError(t, g.ShipGroupJoinFleet(Race_0.Name, fleetSourceOne, c.ShipGroup(0).ID, 0)) assert.ErrorContains(t, g.FleetMerge(Race_0.Name, fleetSourceOne, fleetTargetTwo), e.GenericErrorText(e.ErrInputEntityNotExists)) - assert.NoError(t, g.ShipGroupJoinFleet(Race_0.Name, fleetTargetTwo, 3, 0)) + assert.NoError(t, g.ShipGroupJoinFleet(Race_0.Name, fleetTargetTwo, c.ShipGroup(2).ID, 0)) assert.NoError(t, g.FleetMerge(Race_0.Name, fleetSourceOne, fleetTargetTwo)) - assert.NoError(t, g.ShipGroupJoinFleet(Race_0.Name, fleetOnPlanet2, 2, 0)) + assert.NoError(t, g.ShipGroupJoinFleet(Race_0.Name, fleetOnPlanet2, c.ShipGroup(1).ID, 0)) assert.ErrorContains(t, g.FleetMerge(Race_0.Name, fleetOnPlanet2, fleetTargetTwo), @@ -168,12 +168,12 @@ func TestFleetSpeedAndMass(t *testing.T) { m := c.ShipGroup(0).FullMass(c.MustShipClass(Race_0_idx, Race_0_Gunship)) assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 5)) // 2 - assert.NoError(t, g.ShipGroupLoad(Race_0.Name, 2, "MAT", 10., 0)) + assert.NoError(t, g.ShipGroupLoad(Race_0.Name, c.ShipGroup(1).ID, "MAT", 10., 0)) assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 7)) // 3 - assert.NoError(t, g.ShipGroupLoad(Race_0.Name, 3, "CAP", 10., 0)) + assert.NoError(t, g.ShipGroupLoad(Race_0.Name, c.ShipGroup(2).ID, "CAP", 10., 0)) - assert.NoError(t, g.ShipGroupJoinFleet(Race_0.Name, fleet, 1, 0)) + assert.NoError(t, g.ShipGroupJoinFleet(Race_0.Name, fleet, c.ShipGroup(0).ID, 0)) fleetIndex := 0 speed, mass = c.FleetSpeedAndMass(fleetIndex) assert.Equal(t, s, speed) @@ -182,7 +182,7 @@ func TestFleetSpeedAndMass(t *testing.T) { s = math.Min(s, c.ShipGroup(1).Speed(c.MustShipClass(Race_0_idx, Race_0_Freighter))) m += c.ShipGroup(1).FullMass(c.MustShipClass(Race_0_idx, Race_0_Freighter)) - assert.NoError(t, g.ShipGroupJoinFleet(Race_0.Name, fleet, 2, 0)) + assert.NoError(t, g.ShipGroupJoinFleet(Race_0.Name, fleet, c.ShipGroup(1).ID, 0)) speed, mass = c.FleetSpeedAndMass(fleetIndex) assert.Equal(t, s, speed) assert.Equal(t, m, mass) @@ -190,7 +190,7 @@ func TestFleetSpeedAndMass(t *testing.T) { s = math.Min(s, c.ShipGroup(2).Speed(c.MustShipClass(Race_0_idx, Race_0_Freighter))) m += c.ShipGroup(2).FullMass(c.MustShipClass(Race_0_idx, Race_0_Freighter)) - assert.NoError(t, g.ShipGroupJoinFleet(Race_0.Name, fleet, 3, 0)) + assert.NoError(t, g.ShipGroupJoinFleet(Race_0.Name, fleet, c.ShipGroup(2).ID, 0)) speed, mass = c.FleetSpeedAndMass(fleetIndex) assert.Equal(t, s, speed) assert.Equal(t, m, mass) diff --git a/internal/controller/planet_test.go b/internal/controller/planet_test.go index d9cb0d1..ceec7c9 100644 --- a/internal/controller/planet_test.go +++ b/internal/controller/planet_test.go @@ -191,8 +191,8 @@ func TestProduceShips(t *testing.T) { c.MustPlanet(R0_Planet_2_num).Population = 100 c.MustPlanet(R0_Planet_2_num).Industry = 100 c.RaceTechLevel(Race_0_idx, game.TechDrive, 1.5) - assert.NoError(t, g.ShipGroupUpgrade(Race_0.Name, 2, "Drive", 0, 0)) - assert.NoError(t, g.ShipGroupUpgrade(Race_0.Name, 1, "Drive", 0, 0)) + assert.NoError(t, g.ShipGroupUpgrade(Race_0.Name, c.ShipGroup(1).ID, "Drive", 0, 0)) + assert.NoError(t, g.ShipGroupUpgrade(Race_0.Name, c.ShipGroup(0).ID, "Drive", 0, 0)) assert.Equal(t, game.StateUpgrade, c.ShipGroup(0).State()) assert.Equal(t, game.StateUpgrade, c.ShipGroup(1).State()) diff --git a/internal/controller/report.go b/internal/controller/report.go index 7cb00dd..4de5ee4 100644 --- a/internal/controller/report.go +++ b/internal/controller/report.go @@ -610,7 +610,7 @@ func (c *Cache) ReportLocalGroup(ri int, rep *mr.Report) { sliceIndexValidate(&rep.LocalGroup, i) st := c.MustShipType(ri, sg.TypeID) c.otherGroup(&rep.LocalGroup[i].OtherGroup, sg, st) - rep.LocalGroup[i].Index = sg.Index + rep.LocalGroup[i].ID = sg.ID rep.LocalGroup[i].State = sg.State().String() if sg.FleetID != nil { rep.LocalGroup[i].Fleet = &c.g.Fleets[c.MustFleetIndex(*sg.FleetID)].Name diff --git a/internal/controller/route.go b/internal/controller/route.go index 7fcbe43..61dfdad 100644 --- a/internal/controller/route.go +++ b/internal/controller/route.go @@ -79,8 +79,8 @@ func (c *Cache) SendRoutedGroups() { sortGroups := func(g []int) { // sort groups by largest CargoCapacity slices.SortFunc(g, func(l, r int) int { - return cmp.Or(cmp.Compare(c.ShipGroup(r).CargoCapacity(c.ShipGroupShipClass(r)), - c.ShipGroup(l).CargoCapacity(c.ShipGroupShipClass(l))), + return cmp.Or( + cmp.Compare(c.ShipGroup(r).CargoCapacity(c.ShipGroupShipClass(r)), c.ShipGroup(l).CargoCapacity(c.ShipGroupShipClass(l))), cmp.Compare(l, r)) }) @@ -115,7 +115,7 @@ func (c *Cache) SendRoutedGroups() { ct = game.CargoMaterial default: for _, sgi := range groups { - c.LaunchShips(c.ShipGroup(sgi), dest) + c.LaunchShips(sgi, dest) } groups = reorderGroups(groups) continue @@ -131,6 +131,7 @@ func (c *Cache) SendRoutedGroups() { toLoad = sgCapacity } else if maxShips := uint(math.Ceil(toLoad / (sgCapacity / float64(ships)))); maxShips < ships { newGroupIdx := c.breakGroupUnsafe(c.RaceIndex(sg.OwnerID), sgi, maxShips) + sgi = newGroupIdx sg = c.ShipGroup(newGroupIdx) } // decrease planet resource @@ -138,7 +139,7 @@ func (c *Cache) SendRoutedGroups() { // load group sg.Load = sg.Load.Add(toLoad) sg.CargoType = &ct - c.LaunchShips(sg, dest) + c.LaunchShips(sgi, dest) groups = reorderGroups(groups) } } diff --git a/internal/controller/route_test.go b/internal/controller/route_test.go index 5137ce5..7a1ed68 100644 --- a/internal/controller/route_test.go +++ b/internal/controller/route_test.go @@ -118,11 +118,11 @@ func TestListRoutedSendGroupIds(t *testing.T) { // Foreign group -> idx 1 assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 10)) - assert.NoError(t, g.ShipGroupTransfer(Race_0.Name, Race_1.Name, 5, 0)) + assert.NoError(t, g.ShipGroupTransfer(Race_0.Name, Race_1.Name, c.ShipGroup(4).ID, 0)) // 5: idx = 4 / Part of the Fleet assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 10)) - assert.NoError(t, g.ShipGroupJoinFleet(Race_0.Name, "Fleet", 5, 0)) + assert.NoError(t, g.ShipGroupJoinFleet(Race_0.Name, "Fleet", c.ShipGroup(5).ID, 0)) planet_0_groups := slices.Collect(c.ListRoutedSendGroupIds(0)) assert.Len(t, planet_0_groups, 1) @@ -150,9 +150,11 @@ func TestEnrouteGroups_SplitGroup(t *testing.T) { c.SendRoutedGroups() assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 2) + assert.Equal(t, game.StateInOrbit, c.ShipGroup(0).State()) assert.Equal(t, uint(1), c.ShipGroup(0).Number) assert.Equal(t, 0., c.ShipGroup(0).Load.F()) + assert.Equal(t, game.StateLaunched, c.ShipGroup(1).State()) assert.Equal(t, uint(4), c.ShipGroup(1).Number) assert.Equal(t, 65., c.ShipGroup(1).Load.F()) @@ -281,7 +283,7 @@ func TestListRoutedUnloadShipGroupIds(t *testing.T) { // 5: idx = 4 / Part of the Fleet assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 10)) - assert.NoError(t, g.ShipGroupJoinFleet(Race_0.Name, "Fleet", 5, 0)) + assert.NoError(t, g.ShipGroupJoinFleet(Race_0.Name, "Fleet", c.ShipGroup(4).ID, 0)) assert.NoError(t, g.PlanetRouteSet(Race_0.Name, "COL", R0_Planet_0_num, R0_Planet_2_num)) for _, rt := range game.RouteTypeSet { diff --git a/internal/controller/ship_class.go b/internal/controller/ship_class.go index 805890a..0e595e3 100644 --- a/internal/controller/ship_class.go +++ b/internal/controller/ship_class.go @@ -101,7 +101,7 @@ func (c *Cache) shipClassRemove(ri int, name string) error { for sg := range c.listShipGroups(ri) { if sg.TypeID == st.ID { - return e.NewDeleteShipTypeExistingGroupError("group: %v", sg.Index) + return e.NewDeleteShipTypeExistingGroupError("group: %s", sg.ID) } } c.g.Race[ri].ShipTypes = append(c.g.Race[ri].ShipTypes[:i], c.g.Race[ri].ShipTypes[i+1:]...) diff --git a/internal/controller/ship_group.go b/internal/controller/ship_group.go index d428bd6..d38292a 100644 --- a/internal/controller/ship_group.go +++ b/internal/controller/ship_group.go @@ -10,43 +10,8 @@ import ( "github.com/google/uuid" e "github.com/iliadenisov/galaxy/internal/error" "github.com/iliadenisov/galaxy/internal/model/game" - "github.com/iliadenisov/galaxy/internal/number" ) -func (c *Cache) CreateShips(ri int, shipTypeName string, planetNumber uint, quantity int) error { - class, _, ok := c.ShipClass(ri, shipTypeName) - if !ok { - return e.NewEntityNotExistsError("ship class q", shipTypeName) - } - - p, ok := c.Planet(planetNumber) - if !ok { - return e.NewEntityNotExistsError("planet #%d", planetNumber) - } - if !p.OwnedBy(c.g.Race[ri].ID) { - return e.NewEntityNotOwnedError("planet #%d", planetNumber) - } - - c.createShipsUnsafe(ri, class.ID, p.Number, uint(quantity)) - - return nil -} - -func (c *Cache) createShipsUnsafe(ri int, classID uuid.UUID, planet uint, quantity uint) { - c.appendShipGroup(ri, &game.ShipGroup{ - OwnerID: c.g.Race[ri].ID, - TypeID: classID, - Destination: planet, - Number: uint(quantity), - Tech: map[game.Tech]game.Float{ - game.TechDrive: game.F(c.g.Race[ri].TechLevel(game.TechDrive)), - game.TechWeapons: game.F(c.g.Race[ri].TechLevel(game.TechWeapons)), - game.TechShields: game.F(c.g.Race[ri].TechLevel(game.TechShields)), - game.TechCargo: game.F(c.g.Race[ri].TechLevel(game.TechCargo)), - }, - }) -} - // ShipGroup is a proxy func, nothing to cache func (c *Cache) ShipGroup(groupIndex int) *game.ShipGroup { c.validateShipGroupIndex(groupIndex) @@ -77,16 +42,6 @@ func (c *Cache) ShipGroupsIndex() iter.Seq[int] { } } -func (c *Cache) ShipGroupMaxIndex(ri int) uint { - var max uint = 0 - for i := range c.g.ShipGroups { - if r := c.ShipGroupOwnerRaceIndex(i); r == ri && c.ShipGroup(i).Index > max { - max = c.ShipGroup(i).Index - } - } - return max -} - func (c *Cache) ShipGroupOwnerRaceIndex(groupIndex int) int { c.validateShipGroupIndex(groupIndex) if len(c.cacheRaceIndexByShipGroupIndex) == 0 { @@ -167,7 +122,7 @@ func (c *Cache) shipGroupMerge(ri int) { for i := 0; i < len(raceGroups)-1; i++ { for j := len(raceGroups) - 1; j > i; j-- { if raceGroups[i].Equal(raceGroups[j]) { - raceGroups[i].Index = number.Max(raceGroups[i].Index, raceGroups[j].Index) + raceGroups[i].ID = raceGroups[j].ID raceGroups[i].Number += raceGroups[j].Number raceGroups = append(raceGroups[:j], raceGroups[j+1:]...) } @@ -195,7 +150,7 @@ func (c *Cache) shipGroupMerge(ri int) { } } -func (c *Cache) shipGroupDismantle(ri int, groupIndex, quantity uint) error { +func (c *Cache) shipGroupDismantle(ri int, groupIndex uuid.UUID, quantity uint) error { sgi, ok := c.raceShipGroupIndex(ri, groupIndex) if !ok { return e.NewEntityNotExistsError("group #%d", groupIndex) @@ -253,13 +208,13 @@ func (c *Cache) shipGroupDismantle(ri int, groupIndex, quantity uint) error { // Корабль может нести только один тип груза одновременно. // Возможные типы груза - это колонисты, сырье и промышленность. // Груз может быть доставлен на борт корабля с Вашей или не занятой планеты, на которой он имеется. -func (c *Cache) shipGroupLoad(ri int, groupIndex uint, ct game.CargoType, ships uint, quantity float64) error { +func (c *Cache) shipGroupLoad(ri int, groupID uuid.UUID, ct game.CargoType, ships uint, quantity float64) error { if ships == 0 && quantity > 0 { return e.NewCargoQuantityWithoutGroupBreakError() } - sgi, ok := c.raceShipGroupIndex(ri, groupIndex) + sgi, ok := c.raceShipGroupIndex(ri, groupID) if !ok { - return e.NewEntityNotExistsError("group #%d", groupIndex) + return e.NewEntityNotExistsError("group %s", groupID) } if state := c.ShipGroup(sgi).State(); state != game.StateInOrbit { return e.NewShipsBusyError("state: %s", state) @@ -281,7 +236,7 @@ func (c *Cache) shipGroupLoad(ri int, groupIndex uint, ct game.CargoType, ships return e.NewCargoLoadNotEqualError("cargo: %v", *c.ShipGroup(sgi).CargoType) } if ships > 0 && ships < c.ShipGroup(sgi).Number { - nsgi, err := c.breakGroupSafe(ri, groupIndex, ships) + nsgi, err := c.breakGroupSafe(ri, groupID, ships) if err != nil { return err } @@ -323,14 +278,14 @@ func (c *Cache) shipGroupLoad(ri int, groupIndex uint, ct game.CargoType, ships // Промышленность и Сырье могут быть выгружены на любой планете. // Колонисты могут быть высажены только на планеты, принадлежащие Вам или на необитаемые планеты. -func (c *Cache) shipGroupUnload(ri int, groupIndex uint, ships uint, quantity float64) error { +func (c *Cache) shipGroupUnload(ri int, groupID uuid.UUID, ships uint, quantity float64) error { c.validateRaceIndex(ri) if ships == 0 && quantity > 0 { return e.NewCargoQuantityWithoutGroupBreakError() } - sgi, ok := c.raceShipGroupIndex(ri, groupIndex) + sgi, ok := c.raceShipGroupIndex(ri, groupID) if !ok { - return e.NewEntityNotExistsError("group #%d", groupIndex) + return e.NewEntityNotExistsError("group %s", groupID) } if state := c.ShipGroup(sgi).State(); state != game.StateInOrbit { return e.NewShipsBusyError("state: %s", state) @@ -350,7 +305,7 @@ func (c *Cache) shipGroupUnload(ri int, groupIndex uint, ships uint, quantity fl return e.NewEntityNotOwnedError("planet #%d unload %v", p.Number, ct) } if ships > 0 && ships < c.ShipGroup(sgi).Number { - nsgi, err := c.breakGroupSafe(ri, groupIndex, ships) + nsgi, err := c.breakGroupSafe(ri, groupID, ships) if err != nil { return err } @@ -405,13 +360,13 @@ func (c *Cache) unsafeUnloadCargo(sgi int, q float64) { p.UnpackCapital() } -func (c *Cache) shipGroupTransfer(ri, riAccept int, groupIndex, quantity uint) (err error) { +func (c *Cache) shipGroupTransfer(ri, riAccept int, groupID uuid.UUID, quantity uint) (err error) { if ri == riAccept { return e.NewSameRaceError(c.g.Race[riAccept].Name) } - sgi, ok := c.raceShipGroupIndex(ri, groupIndex) + sgi, ok := c.raceShipGroupIndex(ri, groupID) if !ok { - return e.NewEntityNotExistsError("group #%d", groupIndex) + return e.NewEntityNotExistsError("group %s", groupID) } sg := c.ShipGroup(sgi) state := sg.State() @@ -444,6 +399,7 @@ func (c *Cache) shipGroupTransfer(ri, riAccept int, groupIndex, quantity uint) ( } newGroup := *(sg) + newGroup.ID = uuid.New() newGroup.TypeID = c.g.Race[riAccept].ShipTypes[stAcc].ID newGroup.Tech = maps.Clone(sg.Tech) if state == game.StateLaunched { @@ -451,6 +407,7 @@ func (c *Cache) shipGroupTransfer(ri, riAccept int, groupIndex, quantity uint) ( } if quantity == 0 || quantity == sg.Number { + // FIXME: remove fleet & invalidate cache? c.unsafeDeleteShipGroup(sgi) } else { newGroup.Number = quantity @@ -462,17 +419,11 @@ func (c *Cache) shipGroupTransfer(ri, riAccept int, groupIndex, quantity uint) ( return nil } -func (c *Cache) ShipGroupBreak(ri int, groupIndex, quantity uint) error { +func (c *Cache) ShipGroupBreak(ri int, groupID uuid.UUID, quantity uint) error { c.validateRaceIndex(ri) - sgi := -1 - for i := range c.ShipGroupsIndex() { - if c.ShipGroupOwnerRaceIndex(i) == ri && c.ShipGroup(i).Index == groupIndex { - sgi = i - break - } - } - if sgi < 0 { - return e.NewEntityNotExistsError("group #%d", groupIndex) + sgi, ok := c.raceShipGroupIndex(ri, groupID) + if !ok { + return e.NewEntityNotExistsError("group %s", groupID) } if state := c.ShipGroup(sgi).State(); state != game.StateInOrbit { @@ -486,7 +437,7 @@ func (c *Cache) ShipGroupBreak(ri int, groupIndex, quantity uint) error { if quantity == 0 || quantity == c.ShipGroup(sgi).Number { c.internalShipGroupJoinFleet(sgi, nil) } else { - if _, err := c.breakGroupSafe(ri, groupIndex, quantity); err != nil { + if _, err := c.breakGroupSafe(ri, groupID, quantity); err != nil { return err } } @@ -494,14 +445,14 @@ func (c *Cache) ShipGroupBreak(ri int, groupIndex, quantity uint) error { return nil } -func (c *Cache) breakGroupSafe(ri int, groupIndex uint, newGroupShips uint) (int, error) { +func (c *Cache) breakGroupSafe(ri int, groupID uuid.UUID, newGroupShips uint) (int, error) { c.validateRaceIndex(ri) - sgi, ok := c.raceShipGroupIndex(ri, groupIndex) + sgi, ok := c.raceShipGroupIndex(ri, groupID) if !ok { - return -1, e.NewEntityNotExistsError("group #%d", groupIndex) + return -1, e.NewEntityNotExistsError("group %s", groupID) } if c.ShipGroup(sgi).Number < newGroupShips { - return -1, e.NewBreakGroupIllegalNumberError("group #%d ships: %d -> %d", c.ShipGroup(sgi).Index, c.ShipGroup(sgi).Number, newGroupShips) + return -1, e.NewBreakGroupIllegalNumberError("group=%s ships: %d -> %d", c.ShipGroup(sgi).ID, c.ShipGroup(sgi).Number, newGroupShips) } return c.breakGroupUnsafe(ri, sgi, newGroupShips), nil } @@ -519,35 +470,35 @@ func (c *Cache) breakGroupUnsafe(ri, sgi int, newGroupShips uint) int { // Internal funcs -func (c *Cache) appendShipGroup(ri int, sg *game.ShipGroup) int { - c.validateRaceIndex(ri) - sg.Index = c.ShipGroupMaxIndex(ri) + 1 - sg.OwnerID = c.g.Race[ri].ID - sg.FleetID = nil - c.g.ShipGroups = append(c.g.ShipGroups, *sg) - i := len(c.g.ShipGroups) - 1 - c.invalidateShipGroupCache() - return i -} - -func (c *Cache) raceShipGroupIndex(ri int, index uint) (int, bool) { +func (c *Cache) raceShipGroupIndex(ri int, id uuid.UUID) (int, bool) { c.validateRaceIndex(ri) for i := range c.ShipGroupsIndex() { - if c.ShipGroupOwnerRaceIndex(i) == ri && c.ShipGroup(i).Index == index { + if c.ShipGroupOwnerRaceIndex(i) == ri && c.ShipGroup(i).ID == id { return i, true } } return -1, false } +func (c *Cache) listShipGroupIdx(ri int) iter.Seq[int] { + c.validateRaceIndex(ri) + return func(yield func(int) bool) { + for i := range c.g.ShipGroups { + if ri == c.ShipGroupOwnerRaceIndex(i) { + if !yield(i) { + return + } + } + } + } +} + func (c *Cache) listShipGroups(ri int) iter.Seq[*game.ShipGroup] { c.validateRaceIndex(ri) return func(yield func(*game.ShipGroup) bool) { - for i := range c.g.ShipGroups { - if ri == c.ShipGroupOwnerRaceIndex(i) { - if !yield(&c.g.ShipGroups[i]) { - return - } + for sgi := range c.listShipGroupIdx(ri) { + if !yield(c.ShipGroup(sgi)) { + return } } } @@ -584,3 +535,29 @@ func (c *Cache) validateShipGroupIndex(i int) { panic(fmt.Sprintf("group index out of range: %d >= %d", i, len(c.g.ShipGroups))) } } + +func (c *Cache) createShipsUnsafe(ri int, classID uuid.UUID, planet uint, quantity uint) { + c.appendShipGroup(ri, &game.ShipGroup{ + OwnerID: c.g.Race[ri].ID, + TypeID: classID, + Destination: planet, + Number: uint(quantity), + Tech: map[game.Tech]game.Float{ + game.TechDrive: game.F(c.g.Race[ri].TechLevel(game.TechDrive)), + game.TechWeapons: game.F(c.g.Race[ri].TechLevel(game.TechWeapons)), + game.TechShields: game.F(c.g.Race[ri].TechLevel(game.TechShields)), + game.TechCargo: game.F(c.g.Race[ri].TechLevel(game.TechCargo)), + }, + }) +} + +func (c *Cache) appendShipGroup(ri int, sg *game.ShipGroup) int { + c.validateRaceIndex(ri) + sg.ID = uuid.New() + sg.OwnerID = c.g.Race[ri].ID + sg.FleetID = nil + c.g.ShipGroups = append(c.g.ShipGroups, *sg) + i := len(c.g.ShipGroups) - 1 + c.invalidateShipGroupCache() + return i +} diff --git a/internal/controller/ship_group_move_test.go b/internal/controller/ship_group_move_test.go index cdf8976..f2f364c 100644 --- a/internal/controller/ship_group_move_test.go +++ b/internal/controller/ship_group_move_test.go @@ -19,8 +19,8 @@ func TestListMoveableGroupIds(t *testing.T) { // 3: idx = 2 / [v] In-Fleet group assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 10)) - assert.NoError(t, g.ShipGroupJoinFleet(Race_0.Name, "Fleet", 2, 0)) - assert.NoError(t, g.ShipGroupJoinFleet(Race_0.Name, "Fleet", 3, 0)) + assert.NoError(t, g.ShipGroupJoinFleet(Race_0.Name, "Fleet", c.ShipGroup(1).ID, 0)) + assert.NoError(t, g.ShipGroupJoinFleet(Race_0.Name, "Fleet", c.ShipGroup(2).ID, 0)) // 4: idx = 3 / [v] In_Space assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 7)) @@ -34,7 +34,7 @@ func TestListMoveableGroupIds(t *testing.T) { // 6: idx = 5 / [v] Just launched group assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 10)) - assert.NoError(t, g.ShipGroupSend(Race_0.Name, 6, R0_Planet_2_num, 0)) + assert.NoError(t, g.ShipGroupSend(Race_0.Name, c.ShipGroup(5).ID, R0_Planet_2_num, 0)) movableGroups := slices.Collect(c.ListMoveableGroupIds()) assert.Len(t, movableGroups, 5) diff --git a/internal/controller/ship_group_send.go b/internal/controller/ship_group_send.go index 52a2da0..fad43a5 100644 --- a/internal/controller/ship_group_send.go +++ b/internal/controller/ship_group_send.go @@ -1,17 +1,18 @@ package controller import ( + "github.com/google/uuid" e "github.com/iliadenisov/galaxy/internal/error" "github.com/iliadenisov/galaxy/internal/model/game" "github.com/iliadenisov/galaxy/internal/util" ) -func (c *Cache) shipGroupSend(ri int, groupIndex, planetNumber, quantity uint) error { +func (c *Cache) shipGroupSend(ri int, groupID uuid.UUID, planetNumber, quantity uint) error { c.validateRaceIndex(ri) - sgi, ok := c.raceShipGroupIndex(ri, groupIndex) + sgi, ok := c.raceShipGroupIndex(ri, groupID) if !ok { - return e.NewEntityNotExistsError("group #%d", groupIndex) + return e.NewEntityNotExistsError("group %s", groupID) } st := c.ShipGroupShipClass(sgi) @@ -42,7 +43,7 @@ func (c *Cache) shipGroupSend(ri int, groupIndex, planetNumber, quantity uint) e } if quantity > 0 && quantity < c.ShipGroup(sgi).Number { - nsgi, err := c.breakGroupSafe(ri, groupIndex, quantity) + nsgi, err := c.breakGroupSafe(ri, groupID, quantity) if err != nil { return err } @@ -50,48 +51,38 @@ func (c *Cache) shipGroupSend(ri int, groupIndex, planetNumber, quantity uint) e } if p1.Number == p2.Number { - c.UnsendShips(c.ShipGroup(sgi)) + c.UnsendShips(sgi) c.shipGroupMerge(ri) return nil } - c.LaunchShips(c.ShipGroup(sgi), planetNumber) + c.LaunchShips(sgi, planetNumber) return nil } -func (c *Cache) LaunchShips(sg *game.ShipGroup, destination uint) *game.ShipGroup { - for i := range c.ShipGroupsIndex() { - if c.ShipGroup(i).OwnerID == sg.OwnerID && c.ShipGroup(i).Index == sg.Index { - state := c.ShipGroup(i).State() - var p *game.Planet - switch state { - case game.StateInOrbit: - p = c.MustPlanet(sg.Destination) - case game.StateLaunched: - p = c.MustPlanet(sg.StateInSpace.Origin) - default: - panic("state invalid") - } - c.g.ShipGroups[i] = LaunchShips(*sg, destination, p.X.F(), p.Y.F()) - return &c.g.ShipGroups[i] - } +func (c *Cache) LaunchShips(sgi int, destination uint) *game.ShipGroup { + sg := c.ShipGroup(sgi) + var p *game.Planet + switch sg.State() { + case game.StateInOrbit: + p = c.MustPlanet(sg.Destination) + case game.StateLaunched: + p = c.MustPlanet(sg.StateInSpace.Origin) + default: + panic("state invalid") } - panic("ship group not found") + c.g.ShipGroups[sgi] = LaunchShips(*sg, destination, p.X.F(), p.Y.F()) + return &c.g.ShipGroups[sgi] } -func (c *Cache) UnsendShips(sg *game.ShipGroup) *game.ShipGroup { - for i := range c.ShipGroupsIndex() { - if c.ShipGroup(i).OwnerID == sg.OwnerID && c.ShipGroup(i).Index == sg.Index { - state := c.ShipGroup(i).State() - if state != game.StateLaunched { - panic("state invalid") - } - c.g.ShipGroups[i] = UnsendShips(*sg) - return &c.g.ShipGroups[i] - } +func (c *Cache) UnsendShips(sgi int) *game.ShipGroup { + sg := c.ShipGroup(sgi) + if sg.State() != game.StateLaunched { + panic("state invalid") } - panic("ship group not found") + c.g.ShipGroups[sgi] = UnsendShips(*sg) + return &c.g.ShipGroups[sgi] } func LaunchShips(sg game.ShipGroup, destination uint, originX, originY float64) game.ShipGroup { diff --git a/internal/controller/ship_group_send_test.go b/internal/controller/ship_group_send_test.go index 361a58e..b54f247 100644 --- a/internal/controller/ship_group_send_test.go +++ b/internal/controller/ship_group_send_test.go @@ -4,6 +4,7 @@ import ( "slices" "testing" + "github.com/google/uuid" e "github.com/iliadenisov/galaxy/internal/error" "github.com/iliadenisov/galaxy/internal/model/game" @@ -23,31 +24,31 @@ func TestShipGroupSend(t *testing.T) { assert.NoError(t, c.CreateShips(Race_0_idx, "Fortress", R0_Planet_0_num, 1)) assert.ErrorContains(t, - g.ShipGroupSend(UnknownRace, 1, 2, 0), + g.ShipGroupSend(UnknownRace, c.ShipGroup(0).ID, 2, 0), e.GenericErrorText(e.ErrInputUnknownRace)) assert.ErrorContains(t, - g.ShipGroupSend(Race_Extinct.Name, 1, 2, 0), + g.ShipGroupSend(Race_Extinct.Name, c.ShipGroup(0).ID, 2, 0), e.GenericErrorText(e.ErrRaceExinct)) assert.ErrorContains(t, - g.ShipGroupSend(Race_0.Name, 555, 2, 0), + g.ShipGroupSend(Race_0.Name, uuid.New(), 2, 0), e.GenericErrorText(e.ErrInputEntityNotExists)) assert.ErrorContains(t, - g.ShipGroupSend(Race_0.Name, 1, 222, 0), + g.ShipGroupSend(Race_0.Name, c.ShipGroup(0).ID, 222, 0), e.GenericErrorText(e.ErrInputEntityNotExists)) assert.ErrorContains(t, - g.ShipGroupSend(Race_0.Name, 2, 1, 0), + g.ShipGroupSend(Race_0.Name, c.ShipGroup(1).ID, 1, 0), e.GenericErrorText(e.ErrShipsBusy)) assert.ErrorContains(t, - g.ShipGroupSend(Race_0.Name, 3, 2, 0), + g.ShipGroupSend(Race_0.Name, c.ShipGroup(2).ID, 2, 0), e.GenericErrorText(e.ErrSendShipHasNoDrives)) assert.ErrorContains(t, - g.ShipGroupSend(Race_0.Name, 1, 2, 100), + g.ShipGroupSend(Race_0.Name, c.ShipGroup(0).ID, 2, 100), e.GenericErrorText(e.ErrBeakGroupNumberNotEnough)) assert.ErrorContains(t, - g.ShipGroupSend(Race_0.Name, 1, 3, 0), + g.ShipGroupSend(Race_0.Name, c.ShipGroup(0).ID, 3, 0), e.GenericErrorText(e.ErrSendUnreachableDestination)) - assert.NoError(t, g.ShipGroupSend(Race_0.Name, 1, R0_Planet_2_num, 3)) // send 3 of 10 + assert.NoError(t, g.ShipGroupSend(Race_0.Name, c.ShipGroup(0).ID, R0_Planet_2_num, 3)) // send 3 of 10 assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 4) assert.Equal(t, uint(7), c.ShipGroup(0).Number) assert.Equal(t, game.StateInOrbit, c.ShipGroup(0).State()) @@ -57,23 +58,25 @@ func TestShipGroupSend(t *testing.T) { assert.Nil(t, c.ShipGroup(3).StateInSpace.X) assert.Nil(t, c.ShipGroup(3).StateInSpace.Y) - assert.NoError(t, g.ShipGroupSend(Race_0.Name, 4, R0_Planet_0_num, 2)) // un-send 2 of 3 + assert.NoError(t, g.ShipGroupSend(Race_0.Name, c.ShipGroup(3).ID, R0_Planet_0_num, 2)) // un-send 2 of 3 assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 4) - assert.Equal(t, uint(9), c.MustShipGroup(Race_0_idx, 1).Number) - assert.Equal(t, game.StateInOrbit, c.MustShipGroup(Race_0_idx, 1).State()) - assert.Equal(t, uint(1), c.MustShipGroup(Race_0_idx, 4).Number) - assert.Equal(t, game.StateLaunched, c.MustShipGroup(Race_0_idx, 4).State()) - assert.NotNil(t, c.MustShipGroup(Race_0_idx, 4).StateInSpace) - assert.Nil(t, c.MustShipGroup(Race_0_idx, 4).StateInSpace.X) - assert.Nil(t, c.MustShipGroup(Race_0_idx, 4).StateInSpace.Y) - assert.NoError(t, g.ShipGroupSend(Race_0.Name, 4, R0_Planet_0_num, 0)) // un-send the rest 1 - assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 3) - assert.Equal(t, uint(10), c.MustShipGroup(Race_0_idx, 1).Number) - assert.Equal(t, game.StateInOrbit, c.MustShipGroup(Race_0_idx, 1).State()) + assert.Equal(t, uint(9), c.ShipGroup(0).Number) + assert.Equal(t, game.StateInOrbit, c.ShipGroup(0).State()) - assert.NoError(t, g.ShipGroupSend(Race_0.Name, 1, R0_Planet_2_num, 0)) + assert.Equal(t, uint(1), c.ShipGroup(3).Number) + assert.Equal(t, game.StateLaunched, c.ShipGroup(3).State()) + assert.NotNil(t, c.ShipGroup(3).StateInSpace) + assert.Nil(t, c.ShipGroup(3).StateInSpace.X) + assert.Nil(t, c.ShipGroup(3).StateInSpace.Y) + + assert.NoError(t, g.ShipGroupSend(Race_0.Name, c.ShipGroup(3).ID, R0_Planet_0_num, 0)) // un-send the rest 1 assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 3) - assert.Equal(t, uint(10), c.MustShipGroup(Race_0_idx, 1).Number) - assert.Equal(t, game.StateLaunched, c.MustShipGroup(Race_0_idx, 1).State()) + assert.Equal(t, uint(10), c.ShipGroup(0).Number) + assert.Equal(t, game.StateInOrbit, c.ShipGroup(0).State()) + + assert.NoError(t, g.ShipGroupSend(Race_0.Name, c.ShipGroup(0).ID, R0_Planet_2_num, 0)) + assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 3) + assert.Equal(t, uint(10), c.ShipGroup(0).Number) + assert.Equal(t, game.StateLaunched, c.ShipGroup(0).State()) } diff --git a/internal/controller/ship_group_test.go b/internal/controller/ship_group_test.go index 2d0a195..2567b69 100644 --- a/internal/controller/ship_group_test.go +++ b/internal/controller/ship_group_test.go @@ -82,16 +82,16 @@ func TestShipGroupMerge(t *testing.T) { switch { case sg.TypeID == shipTypeID(Race_0_idx, Race_0_Freighter) && sg.TechLevel(game.TechDrive) == 1.1: assert.Equal(t, uint(7), sg.Number) - assert.Equal(t, uint(1), sg.Index) + // assert.Equal(t, uint(1), sg.Index) case sg.TypeID == shipTypeID(Race_0_idx, Race_0_Freighter) && sg.TechLevel(game.TechDrive) == 1.5: assert.Equal(t, uint(11), sg.Number) - assert.Equal(t, uint(4), sg.Index) + // assert.Equal(t, uint(4), sg.Index) case sg.TypeID == shipTypeID(Race_0_idx, Race_0_Gunship) && sg.TechLevel(game.TechDrive) == 1.1: assert.Equal(t, uint(2), sg.Number) - assert.Equal(t, uint(2), sg.Index) + // assert.Equal(t, uint(2), sg.Index) case sg.TypeID == shipTypeID(Race_0_idx, Race_0_Gunship) && sg.TechLevel(game.TechDrive) == 1.5: assert.Equal(t, uint(13), sg.Number) - assert.Equal(t, uint(3), sg.Index) + // assert.Equal(t, uint(3), sg.Index) default: t.Error("not all ship groups covered") } @@ -105,48 +105,48 @@ func TestShipGroupBreak(t *testing.T) { c.ShipGroup(1).StateInSpace = &InSpace fleet := "R0_Fleet" - assert.NoError(t, g.ShipGroupJoinFleet(Race_0.Name, fleet, 1, 0)) + assert.NoError(t, g.ShipGroupJoinFleet(Race_0.Name, fleet, c.ShipGroup(0).ID, 0)) assert.ErrorContains(t, - g.ShipGroupBreak(UnknownRace, 1, 0), + g.ShipGroupBreak(UnknownRace, c.ShipGroup(0).ID, 0), e.GenericErrorText(e.ErrInputUnknownRace)) assert.ErrorContains(t, - g.ShipGroupBreak(Race_Extinct.Name, 1, 0), + g.ShipGroupBreak(Race_Extinct.Name, c.ShipGroup(0).ID, 0), e.GenericErrorText(e.ErrRaceExinct)) assert.ErrorContains(t, - g.ShipGroupBreak(Race_0.Name, 555, 0), + g.ShipGroupBreak(Race_0.Name, uuid.New(), 0), e.GenericErrorText(e.ErrInputEntityNotExists)) assert.ErrorContains(t, - g.ShipGroupBreak(Race_0.Name, 1, 17), + g.ShipGroupBreak(Race_0.Name, c.ShipGroup(0).ID, 17), e.GenericErrorText(e.ErrBeakGroupNumberNotEnough)) assert.ErrorContains(t, - g.ShipGroupBreak(Race_0.Name, 2, 0), + g.ShipGroupBreak(Race_0.Name, c.ShipGroup(1).ID, 0), e.GenericErrorText(e.ErrShipsBusy)) assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 2) assert.Len(t, slices.Collect(c.ListFleets(Race_0_idx)), 1) // group #1 -> group #3 (5 new, 8 left) - assert.NoError(t, c.ShipGroupBreak(Race_0_idx, 1, 5)) // group #3 (2) + assert.NoError(t, c.ShipGroupBreak(Race_0_idx, c.ShipGroup(0).ID, 5)) // group #3 (2) assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 3) assert.Equal(t, uint(8), c.ShipGroup(0).Number) assert.NotNil(t, c.ShipGroup(0).FleetID) assert.Equal(t, uint(5), c.ShipGroup(2).Number) - assert.Equal(t, uint(3), c.ShipGroup(2).Index) + // assert.Equal(t, uint(3), c.ShipGroup(2).Index) assert.Nil(t, c.ShipGroup(2).FleetID) assert.Nil(t, c.ShipGroup(2).CargoType) // group #1 -> group #4 (2 new, 6 left) c.ShipGroup(0).CargoType = game.CargoColonist.Ref() - c.ShipGroup(0).Load = 32.8 // 8 ships - assert.NoError(t, c.ShipGroupBreak(Race_0_idx, 1, 2)) // group #4 (3) + c.ShipGroup(0).Load = 32.8 // 8 ships + assert.NoError(t, c.ShipGroupBreak(Race_0_idx, c.ShipGroup(0).ID, 2)) // group #4 (3) assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 4) assert.Equal(t, uint(6), c.ShipGroup(0).Number) assert.NotNil(t, c.ShipGroup(0).FleetID) assert.Equal(t, uint(2), c.ShipGroup(3).Number) - assert.Equal(t, uint(4), c.ShipGroup(3).Index) + // assert.Equal(t, uint(4), c.ShipGroup(3).Index) assert.Nil(t, c.ShipGroup(3).FleetID) - assert.NoError(t, c.ShipGroupJoinFleet(Race_0_idx, fleet, 4, 0)) + assert.NoError(t, c.ShipGroupJoinFleet(Race_0_idx, fleet, c.ShipGroup(3).ID, 0)) assert.NotNil(t, c.ShipGroup(3).FleetID) assert.Equal(t, game.CargoColonist.Ref(), c.ShipGroup(0).CargoType) @@ -155,13 +155,13 @@ func TestShipGroupBreak(t *testing.T) { assert.Equal(t, 8.2, number.Fixed3(c.ShipGroup(3).Load.F())) // group #1 -> MAX 6 off the fleet - assert.NoError(t, g.ShipGroupBreak(Race_0.Name, 1, 6)) // group #1 (0) + assert.NoError(t, g.ShipGroupBreak(Race_0.Name, c.ShipGroup(0).ID, 6)) // group #1 (0) assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 4) assert.Equal(t, uint(6), c.ShipGroup(0).Number) assert.Nil(t, c.ShipGroup(0).FleetID) // group #4 -> ALL off the fleet - assert.NoError(t, g.ShipGroupBreak(Race_0.Name, 4, 0)) // group #1 (0) + assert.NoError(t, g.ShipGroupBreak(Race_0.Name, c.ShipGroup(3).ID, 0)) // group #1 (0) assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 4) assert.Equal(t, uint(2), c.ShipGroup(3).Number) assert.Nil(t, c.ShipGroup(3).FleetID) @@ -170,10 +170,10 @@ func TestShipGroupBreak(t *testing.T) { func TestShipGroupTransfer(t *testing.T) { c, g := newCache() assert.NoError(t, c.CreateShips(Race_0_idx, ShipType_Cruiser, R0_Planet_0_num, 11)) // group #1 (0) - assert.NoError(t, c.CreateShips(Race_1_idx, ShipType_Cruiser, R1_Planet_1_num, 23)) // group #1 (1) + assert.NoError(t, c.CreateShips(Race_1_idx, ShipType_Cruiser, R1_Planet_1_num, 23)) // group #2 (1) - assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Gunship, R0_Planet_0_num, 17)) // group #2 (2) - In_Space - assert.NoError(t, g.ShipGroupJoinFleet(Race_0.Name, "R0_Fleet", 2, 0)) + assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Gunship, R0_Planet_0_num, 17)) // group #3 (2) - In_Space + assert.NoError(t, g.ShipGroupJoinFleet(Race_0.Name, "R0_Fleet", c.ShipGroup(2).ID, 0)) assert.NotNil(t, c.ShipGroup(2).FleetID) c.ShipGroup(2).StateInSpace = &InSpace c.ShipGroup(2).CargoType = game.CargoMaterial.Ref() @@ -183,31 +183,31 @@ func TestShipGroupTransfer(t *testing.T) { assert.Len(t, slices.Collect(c.RaceShipGroups(Race_1_idx)), 1) assert.ErrorContains(t, - g.ShipGroupTransfer(UnknownRace, Race_1.Name, 2, 0), + g.ShipGroupTransfer(UnknownRace, Race_1.Name, c.ShipGroup(1).ID, 0), e.GenericErrorText(e.ErrInputUnknownRace)) assert.ErrorContains(t, - g.ShipGroupTransfer(Race_0.Name, UnknownRace, 2, 0), + g.ShipGroupTransfer(Race_0.Name, UnknownRace, c.ShipGroup(1).ID, 0), e.GenericErrorText(e.ErrInputUnknownRace)) assert.ErrorContains(t, - g.ShipGroupTransfer(Race_0.Name, Race_Extinct.Name, 2, 0), + g.ShipGroupTransfer(Race_0.Name, Race_Extinct.Name, c.ShipGroup(1).ID, 0), e.GenericErrorText(e.ErrRaceExinct)) assert.ErrorContains(t, - g.ShipGroupTransfer(Race_Extinct.Name, Race_1.Name, 2, 0), + g.ShipGroupTransfer(Race_Extinct.Name, Race_1.Name, c.ShipGroup(1).ID, 0), e.GenericErrorText(e.ErrRaceExinct)) assert.ErrorContains(t, - g.ShipGroupTransfer(Race_0.Name, Race_0.Name, 2, 0), + g.ShipGroupTransfer(Race_0.Name, Race_0.Name, c.ShipGroup(1).ID, 0), e.GenericErrorText(e.ErrInputSameRace)) assert.ErrorContains(t, - g.ShipGroupTransfer(Race_0.Name, Race_1.Name, 555, 0), + g.ShipGroupTransfer(Race_0.Name, Race_1.Name, uuid.New(), 0), e.GenericErrorText(e.ErrInputEntityNotExists)) assert.ErrorContains(t, - g.ShipGroupTransfer(Race_0.Name, Race_1.Name, 2, 18), + g.ShipGroupTransfer(Race_0.Name, Race_1.Name, c.ShipGroup(2).ID, 18), e.GenericErrorText(e.ErrBeakGroupNumberNotEnough)) assert.ErrorContains(t, - g.ShipGroupTransfer(Race_0.Name, Race_1.Name, 1, 0), + g.ShipGroupTransfer(Race_0.Name, Race_1.Name, c.ShipGroup(0).ID, 0), e.GenericErrorText(e.ErrGiveawayGroupShipsTypeNotEqual)) - assert.NoError(t, g.ShipGroupTransfer(Race_0.Name, Race_1.Name, 2, 11)) // group #2 (3) + assert.NoError(t, g.ShipGroupTransfer(Race_0.Name, Race_1.Name, c.ShipGroup(2).ID, 11)) // group #2 (3) assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 2) assert.Len(t, slices.Collect(c.RaceShipGroups(Race_1_idx)), 2) @@ -233,24 +233,24 @@ func TestShipGroupTransfer(t *testing.T) { assert.Equal(t, c.ShipGroup(3).Number, uint(11)) assert.Nil(t, c.ShipGroup(3).FleetID) - assert.NoError(t, g.ShipGroupTransfer(Race_1.Name, Race_0.Name, 2, 11)) + assert.NoError(t, g.ShipGroupTransfer(Race_1.Name, Race_0.Name, c.ShipGroup(3).ID, 11)) assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 3) assert.Len(t, slices.Collect(c.RaceShipGroups(Race_1_idx)), 1) assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Gunship, R0_Planet_0_num, 1)) assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 4) assert.Equal(t, game.StateInOrbit, c.ShipGroup(4).State()) - assert.NoError(t, g.ShipGroupSend(Race_0.Name, c.ShipGroup(4).Index, R0_Planet_2_num, 0)) + assert.NoError(t, g.ShipGroupSend(Race_0.Name, c.ShipGroup(4).ID, R0_Planet_2_num, 0)) assert.Equal(t, game.StateLaunched, c.ShipGroup(4).State()) assert.Equal(t, c.ShipGroup(4).OwnerID, Race_0_ID) - assert.NoError(t, g.ShipGroupTransfer(Race_0.Name, Race_1.Name, c.ShipGroup(4).Index, 0)) + assert.NoError(t, g.ShipGroupTransfer(Race_0.Name, Race_1.Name, c.ShipGroup(4).ID, 0)) assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 3) assert.Len(t, slices.Collect(c.RaceShipGroups(Race_1_idx)), 2) assert.Equal(t, game.StateTransfer, c.ShipGroup(4).State()) assert.Equal(t, c.ShipGroup(4).OwnerID, Race_1_ID) assert.ErrorContains(t, - g.ShipGroupTransfer(Race_1.Name, Race_0.Name, c.ShipGroup(4).Index, 0), + g.ShipGroupTransfer(Race_1.Name, Race_0.Name, c.ShipGroup(4).ID, 0), e.GenericErrorText(e.ErrShipsBusy)) } @@ -280,49 +280,49 @@ func TestShipGroupLoad(t *testing.T) { // tests assert.ErrorContains(t, - g.ShipGroupLoad(UnknownRace, 1, game.CargoMaterial.String(), 0, 0), + g.ShipGroupLoad(UnknownRace, c.ShipGroup(0).ID, game.CargoMaterial.String(), 0, 0), e.GenericErrorText(e.ErrInputUnknownRace)) assert.ErrorContains(t, - g.ShipGroupLoad(Race_Extinct.Name, 1, game.CargoMaterial.String(), 0, 0), + g.ShipGroupLoad(Race_Extinct.Name, c.ShipGroup(0).ID, game.CargoMaterial.String(), 0, 0), e.GenericErrorText(e.ErrRaceExinct)) assert.ErrorContains(t, - g.ShipGroupLoad(Race_0.Name, 1, "GOLD", 0, 0), + g.ShipGroupLoad(Race_0.Name, c.ShipGroup(0).ID, "GOLD", 0, 0), e.GenericErrorText(e.ErrInputCargoTypeInvalid)) assert.ErrorContains(t, - g.ShipGroupLoad(Race_0.Name, 555, game.CargoMaterial.String(), 0, 0), + g.ShipGroupLoad(Race_0.Name, uuid.New(), game.CargoMaterial.String(), 0, 0), e.GenericErrorText(e.ErrInputEntityNotExists)) assert.ErrorContains(t, - g.ShipGroupLoad(Race_0.Name, 3, game.CargoMaterial.String(), 0, 0), + g.ShipGroupLoad(Race_0.Name, c.ShipGroup(2).ID, game.CargoMaterial.String(), 0, 0), e.GenericErrorText(e.ErrShipsBusy)) assert.ErrorContains(t, - g.ShipGroupLoad(Race_0.Name, 5, game.CargoMaterial.String(), 0, 0), + g.ShipGroupLoad(Race_0.Name, c.ShipGroup(4).ID, game.CargoMaterial.String(), 0, 0), e.GenericErrorText(e.ErrInputEntityNotOwned)) assert.ErrorContains(t, - g.ShipGroupLoad(Race_0.Name, 2, game.CargoMaterial.String(), 0, 0), + g.ShipGroupLoad(Race_0.Name, c.ShipGroup(1).ID, game.CargoMaterial.String(), 0, 0), e.GenericErrorText(e.ErrInputNoCargoBay)) assert.ErrorContains(t, - g.ShipGroupLoad(Race_0.Name, 4, game.CargoMaterial.String(), 0, 0), + g.ShipGroupLoad(Race_0.Name, c.ShipGroup(3).ID, game.CargoMaterial.String(), 0, 0), e.GenericErrorText(e.ErrInputCargoLoadNotEqual)) // initial planet is empty assert.ErrorContains(t, - g.ShipGroupLoad(Race_0.Name, 1, game.CargoMaterial.String(), 0, 0), + g.ShipGroupLoad(Race_0.Name, c.ShipGroup(0).ID, game.CargoMaterial.String(), 0, 0), e.GenericErrorText(e.ErrInputCargoLoadNotEnough)) // add cargo to planet c.PutMaterial(R0_Planet_0_num, 100) // not enough on the planet assert.ErrorContains(t, - g.ShipGroupLoad(Race_0.Name, 1, game.CargoMaterial.String(), 11, 101), + g.ShipGroupLoad(Race_0.Name, c.ShipGroup(0).ID, game.CargoMaterial.String(), 11, 101), e.GenericErrorText(e.ErrInputCargoLoadNotEnough)) // quantity > ships assert.ErrorContains(t, - g.ShipGroupLoad(Race_0.Name, 1, game.CargoMaterial.String(), 0, 1), + g.ShipGroupLoad(Race_0.Name, c.ShipGroup(0).ID, game.CargoMaterial.String(), 0, 1), e.GenericErrorText(e.ErrInputCargoQuantityWithoutGroupBreak)) assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 5) // break group and load maximum - assert.NoError(t, g.ShipGroupLoad(Race_0.Name, 1, game.CargoMaterial.String(), 2, 0)) + assert.NoError(t, g.ShipGroupLoad(Race_0.Name, c.ShipGroup(0).ID, game.CargoMaterial.String(), 2, 0)) assert.Equal(t, 58.0, c.MustPlanet(R0_Planet_0_num).Material.F()) assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 6) assert.Nil(t, c.ShipGroup(0).CargoType) @@ -333,7 +333,7 @@ func TestShipGroupLoad(t *testing.T) { assert.Equal(t, 42.0, c.ShipGroup(5).Load.F()) // break group and load limited - assert.NoError(t, g.ShipGroupLoad(Race_0.Name, 1, game.CargoMaterial.String(), 2, 18)) + assert.NoError(t, g.ShipGroupLoad(Race_0.Name, c.ShipGroup(0).ID, game.CargoMaterial.String(), 2, 18)) assert.Equal(t, 40.0, c.MustPlanet(R0_Planet_0_num).Material.F()) assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 7) assert.Nil(t, c.ShipGroup(0).CargoType) @@ -346,7 +346,7 @@ func TestShipGroupLoad(t *testing.T) { // add cargo to planet c.PutMaterial(R0_Planet_0_num, 100) // loading all available cargo - assert.NoError(t, g.ShipGroupLoad(Race_0.Name, 1, game.CargoMaterial.String(), 0, 0)) + assert.NoError(t, g.ShipGroupLoad(Race_0.Name, c.ShipGroup(0).ID, game.CargoMaterial.String(), 0, 0)) assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 7) assert.Equal(t, 0.0, c.MustPlanet(R0_Planet_0_num).Material.F()) assert.Equal(t, 100.0, c.ShipGroup(0).Load.F()) // free: 131.0 @@ -354,14 +354,14 @@ func TestShipGroupLoad(t *testing.T) { // add cargo to planet c.PutMaterial(R0_Planet_0_num, 200) - assert.NoError(t, g.ShipGroupLoad(Race_0.Name, 1, game.CargoMaterial.String(), 11, 31)) + assert.NoError(t, g.ShipGroupLoad(Race_0.Name, c.ShipGroup(0).ID, game.CargoMaterial.String(), 11, 31)) assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 7) assert.Equal(t, 169.0, c.MustPlanet(R0_Planet_0_num).Material.F()) assert.Equal(t, 131.0, c.ShipGroup(0).Load.F()) // free: 100.0 assert.Equal(t, game.CargoMaterial.Ref(), c.ShipGroup(0).CargoType) // load to maximum cargo space left - assert.NoError(t, g.ShipGroupLoad(Race_0.Name, 1, game.CargoMaterial.String(), 11, 0)) + assert.NoError(t, g.ShipGroupLoad(Race_0.Name, c.ShipGroup(0).ID, game.CargoMaterial.String(), 11, 0)) assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 7) assert.Equal(t, 153.0, c.MustPlanet(R0_Planet_0_num).Material.F()) assert.Equal(t, 147.0, c.ShipGroup(0).Load.F()) // free: 0.0 @@ -369,7 +369,7 @@ func TestShipGroupLoad(t *testing.T) { // ship group is full assert.ErrorContains(t, - g.ShipGroupLoad(Race_0.Name, 1, game.CargoMaterial.String(), 0, 0), + g.ShipGroupLoad(Race_0.Name, c.ShipGroup(0).ID, game.CargoMaterial.String(), 0, 0), e.GenericErrorText(e.ErrInputCargoLoadNoSpaceLeft)) assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 7) } @@ -408,39 +408,39 @@ func TestShipGroupUnload(t *testing.T) { // tests assert.ErrorContains(t, - g.ShipGroupUnload(UnknownRace, 1, 0, 0), + g.ShipGroupUnload(UnknownRace, c.ShipGroup(0).ID, 0, 0), e.GenericErrorText(e.ErrInputUnknownRace)) assert.ErrorContains(t, - g.ShipGroupUnload(Race_Extinct.Name, 1, 0, 0), + g.ShipGroupUnload(Race_Extinct.Name, c.ShipGroup(0).ID, 0, 0), e.GenericErrorText(e.ErrRaceExinct)) assert.ErrorContains(t, - g.ShipGroupUnload(Race_0.Name, 555, 0, 0), + g.ShipGroupUnload(Race_0.Name, uuid.New(), 0, 0), e.GenericErrorText(e.ErrInputEntityNotExists)) assert.ErrorContains(t, - g.ShipGroupUnload(Race_0.Name, 3, 0, 0), + g.ShipGroupUnload(Race_0.Name, c.ShipGroup(2).ID, 0, 0), e.GenericErrorText(e.ErrShipsBusy)) assert.ErrorContains(t, - g.ShipGroupUnload(Race_0.Name, 2, 0, 0), + g.ShipGroupUnload(Race_0.Name, c.ShipGroup(1).ID, 0, 0), e.GenericErrorText(e.ErrInputNoCargoBay)) assert.ErrorContains(t, - g.ShipGroupUnload(Race_0.Name, 1, 0, 0), + g.ShipGroupUnload(Race_0.Name, c.ShipGroup(0).ID, 0, 0), e.GenericErrorText(e.ErrInputCargoUnloadEmpty)) assert.ErrorContains(t, - g.ShipGroupUnload(Race_0.Name, 5, 0, 0), + g.ShipGroupUnload(Race_0.Name, c.ShipGroup(4).ID, 0, 0), e.GenericErrorText(e.ErrInputEntityNotOwned)) c.ShipGroup(0).CargoType = game.CargoColonist.Ref() c.ShipGroup(0).Load = 100 assert.ErrorContains(t, - g.ShipGroupUnload(Race_0.Name, 1, 11, 101), + g.ShipGroupUnload(Race_0.Name, c.ShipGroup(0).ID, 11, 101), e.GenericErrorText(e.ErrInputCargoUnoadNotEnough)) assert.ErrorContains(t, - g.ShipGroupUnload(Race_0.Name, 1, 0, 1), + g.ShipGroupUnload(Race_0.Name, c.ShipGroup(0).ID, 0, 1), e.GenericErrorText(e.ErrInputCargoQuantityWithoutGroupBreak)) assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 6) // unload MAT on foreign planet / break group - assert.NoError(t, g.ShipGroupUnload(Race_0.Name, 6, 3, 0)) + assert.NoError(t, g.ShipGroupUnload(Race_0.Name, c.ShipGroup(5).ID, 3, 0)) assert.Equal(t, 27.273, number.Fixed3(c.MustPlanet(R1_Planet_1_num).Material.F())) assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 7) assert.Equal(t, uint(3), c.ShipGroup(6).Number) @@ -452,7 +452,7 @@ func TestShipGroupUnload(t *testing.T) { assert.Equal(t, 72.727, number.Fixed3(c.ShipGroup(5).Load.F())) // unload MAT on foreign planet / break group / limited MAT - assert.NoError(t, g.ShipGroupUnload(Race_0.Name, 6, 3, 20.0)) + assert.NoError(t, g.ShipGroupUnload(Race_0.Name, c.ShipGroup(5).ID, 3, 20.0)) assert.Equal(t, 47.273, number.Fixed3(c.MustPlanet(R1_Planet_1_num).Material.F())) assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 8) assert.Equal(t, uint(3), c.ShipGroup(7).Number) @@ -463,7 +463,7 @@ func TestShipGroupUnload(t *testing.T) { assert.Equal(t, 45.455, number.Fixed3(c.ShipGroup(5).Load.F())) // unload ALL - assert.NoError(t, g.ShipGroupUnload(Race_0.Name, 1, 0, 0)) + assert.NoError(t, g.ShipGroupUnload(Race_0.Name, c.ShipGroup(0).ID, 0, 0)) assert.Equal(t, 100.0, number.Fixed3(c.MustPlanet(R0_Planet_0_num).Colonists.F())) assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 8) assert.Equal(t, uint(10), c.ShipGroup(0).Number) @@ -502,26 +502,26 @@ func TestShipGroupDismantle(t *testing.T) { // tests assert.ErrorContains(t, - g.ShipGroupDismantle(UnknownRace, 1, 0), + g.ShipGroupDismantle(UnknownRace, c.ShipGroup(0).ID, 0), e.GenericErrorText(e.ErrInputUnknownRace)) assert.ErrorContains(t, - g.ShipGroupDismantle(Race_Extinct.Name, 1, 0), + g.ShipGroupDismantle(Race_Extinct.Name, c.ShipGroup(0).ID, 0), e.GenericErrorText(e.ErrRaceExinct)) assert.ErrorContains(t, - g.ShipGroupDismantle(Race_0.Name, 555, 0), + g.ShipGroupDismantle(Race_0.Name, uuid.New(), 0), e.GenericErrorText(e.ErrInputEntityNotExists)) assert.ErrorContains(t, - g.ShipGroupDismantle(Race_0.Name, 2, 0), + g.ShipGroupDismantle(Race_0.Name, c.ShipGroup(1).ID, 0), e.GenericErrorText(e.ErrShipsBusy)) assert.ErrorContains(t, - g.ShipGroupDismantle(Race_0.Name, 3, 12), + g.ShipGroupDismantle(Race_0.Name, c.ShipGroup(2).ID, 12), e.GenericErrorText(e.ErrBeakGroupNumberNotEnough)) groupEmptyMass := c.ShipGroup(4).EmptyMass(c.MustShipClass(Race_0_idx, Race_0_Freighter)) planetMAT := c.MustPlanet(R1_Planet_1_num).Material.F() planetCOL := c.MustPlanet(R1_Planet_1_num).Colonists.F() - assert.NoError(t, g.ShipGroupDismantle(Race_0.Name, 5, 0)) + assert.NoError(t, g.ShipGroupDismantle(Race_0.Name, c.ShipGroup(4).ID, 0)) assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 4) assert.Equal(t, planetMAT+groupEmptyMass, c.MustPlanet(R1_Planet_1_num).Material.F()) assert.Equal(t, planetCOL, c.MustPlanet(R1_Planet_1_num).Colonists.F()) @@ -529,7 +529,7 @@ func TestShipGroupDismantle(t *testing.T) { groupEmptyMass = c.ShipGroup(3).EmptyMass(c.MustShipClass(Race_0_idx, Race_0_Freighter)) groupLoadMAT := c.ShipGroup(3).Load.F() planetMAT = c.MustPlanet(R1_Planet_1_num).Material.F() - assert.NoError(t, g.ShipGroupDismantle(Race_0.Name, 4, 0)) + assert.NoError(t, g.ShipGroupDismantle(Race_0.Name, c.ShipGroup(3).ID, 0)) assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 3) assert.Equal(t, planetMAT+groupEmptyMass+groupLoadMAT, c.MustPlanet(R1_Planet_1_num).Material.F()) @@ -547,7 +547,7 @@ func TestShipGroupDismantle(t *testing.T) { freePOPLeft := c.MustPlanet(R0_Planet_0_num).Size.F() - c.MustPlanet(R0_Planet_0_num).Population.F() expectAddedCOL := (expectPOPIncrease - freePOPLeft) / 8 expectAddedPOP := freePOPLeft - assert.NoError(t, g.ShipGroupDismantle(Race_0.Name, 3, quantity)) + assert.NoError(t, g.ShipGroupDismantle(Race_0.Name, c.ShipGroup(2).ID, quantity)) assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 3) assert.Equal(t, planetCOL+expectAddedCOL, c.MustPlanet(R0_Planet_0_num).Colonists.F()) assert.Equal(t, planetPOP+expectAddedPOP, c.MustPlanet(R0_Planet_0_num).Population.F()) diff --git a/internal/controller/ship_group_upgrade.go b/internal/controller/ship_group_upgrade.go index eabae26..d0e5576 100644 --- a/internal/controller/ship_group_upgrade.go +++ b/internal/controller/ship_group_upgrade.go @@ -5,15 +5,16 @@ import ( "slices" "strings" + "github.com/google/uuid" e "github.com/iliadenisov/galaxy/internal/error" "github.com/iliadenisov/galaxy/internal/model/game" ) -func (c *Cache) shipGroupUpgrade(ri int, groupIndex uint, techInput string, limitShips uint, limitLevel float64) error { +func (c *Cache) shipGroupUpgrade(ri int, groupID uuid.UUID, techInput string, limitShips uint, limitLevel float64) error { c.validateRaceIndex(ri) - sgi, ok := c.raceShipGroupIndex(ri, groupIndex) + sgi, ok := c.raceShipGroupIndex(ri, groupID) if !ok { - return e.NewEntityNotExistsError("group #%d", groupIndex) + return e.NewEntityNotExistsError("group %s", groupID) } st := c.ShipGroupShipClass(sgi) sg := c.ShipGroup(sgi) @@ -24,7 +25,7 @@ func (c *Cache) shipGroupUpgrade(ri int, groupIndex uint, techInput string, limi p := c.MustPlanet(sg.Destination) if p.Owned() && !p.OwnedBy(c.g.Race[ri].ID) { - return e.NewEntityNotOwnedError("planet #%d for upgrade group #%d", p.Number, groupIndex) + return e.NewEntityNotOwnedError("planet #%d for upgrade group %s", p.Number, groupID) } upgradeValidTech := map[string]game.Tech{ @@ -117,7 +118,7 @@ func (c *Cache) shipGroupUpgrade(ri int, groupIndex uint, techInput string, limi // break group if needed if maxUpgradableShips < sg.Number { - nsgi, err := c.breakGroupSafe(ri, groupIndex, maxUpgradableShips) + nsgi, err := c.breakGroupSafe(ri, groupID, maxUpgradableShips) if err != nil { return err } diff --git a/internal/controller/ship_group_upgrade_test.go b/internal/controller/ship_group_upgrade_test.go index a880e9a..0052cc8 100644 --- a/internal/controller/ship_group_upgrade_test.go +++ b/internal/controller/ship_group_upgrade_test.go @@ -4,6 +4,7 @@ import ( "slices" "testing" + "github.com/google/uuid" "github.com/iliadenisov/galaxy/internal/controller" e "github.com/iliadenisov/galaxy/internal/error" "github.com/iliadenisov/galaxy/internal/model/game" @@ -125,43 +126,43 @@ func TestShipGroupUpgrade(t *testing.T) { c.ShipGroup(2).Destination = R1_Planet_1_num assert.ErrorContains(t, - g.ShipGroupUpgrade(UnknownRace, 1, "DRIVE", 0, 0), + g.ShipGroupUpgrade(UnknownRace, c.ShipGroup(0).ID, "DRIVE", 0, 0), e.GenericErrorText(e.ErrInputUnknownRace)) assert.ErrorContains(t, - g.ShipGroupUpgrade(Race_Extinct.Name, 1, "DRIVE", 0, 0), + g.ShipGroupUpgrade(Race_Extinct.Name, c.ShipGroup(0).ID, "DRIVE", 0, 0), e.GenericErrorText(e.ErrRaceExinct)) assert.ErrorContains(t, - g.ShipGroupUpgrade(Race_0.Name, 555, "DRIVE", 0, 0), + g.ShipGroupUpgrade(Race_0.Name, uuid.New(), "DRIVE", 0, 0), e.GenericErrorText(e.ErrInputEntityNotExists)) assert.ErrorContains(t, - g.ShipGroupUpgrade(Race_0.Name, 2, "DRIVE", 0, 0), + g.ShipGroupUpgrade(Race_0.Name, c.ShipGroup(1).ID, "DRIVE", 0, 0), e.GenericErrorText(e.ErrShipsBusy)) assert.ErrorContains(t, - g.ShipGroupUpgrade(Race_0.Name, 3, "DRIVE", 0, 0), + g.ShipGroupUpgrade(Race_0.Name, c.ShipGroup(2).ID, "DRIVE", 0, 0), e.GenericErrorText(e.ErrInputEntityNotOwned)) assert.ErrorContains(t, - g.ShipGroupUpgrade(Race_0.Name, 1, "GUN", 0, 0), + g.ShipGroupUpgrade(Race_0.Name, c.ShipGroup(0).ID, "GUN", 0, 0), e.GenericErrorText(e.ErrInputTechUnknown)) assert.ErrorContains(t, - g.ShipGroupUpgrade(Race_0.Name, 1, "CARGO", 0, 0), + g.ShipGroupUpgrade(Race_0.Name, c.ShipGroup(0).ID, "CARGO", 0, 0), e.GenericErrorText(e.ErrInputUpgradeShipTechNotUsed)) assert.ErrorContains(t, - g.ShipGroupUpgrade(Race_0.Name, 1, "ALL", 0, 2.0), + g.ShipGroupUpgrade(Race_0.Name, c.ShipGroup(0).ID, "ALL", 0, 2.0), e.GenericErrorText(e.ErrInputUpgradeParameterNotAllowed)) assert.ErrorContains(t, - g.ShipGroupUpgrade(Race_0.Name, 1, "DRIVE", 0, 2.0), + g.ShipGroupUpgrade(Race_0.Name, c.ShipGroup(0).ID, "DRIVE", 0, 2.0), e.GenericErrorText(e.ErrInputUpgradeTechLevelInsufficient)) assert.ErrorContains(t, - g.ShipGroupUpgrade(Race_0.Name, 1, "DRIVE", 0, 1.1), + g.ShipGroupUpgrade(Race_0.Name, c.ShipGroup(0).ID, "DRIVE", 0, 1.1), e.GenericErrorText(e.ErrInputUpgradeShipsAlreadyUpToDate)) c.RaceTechLevel(Race_0_idx, game.TechDrive, 10.0) assert.Equal(t, 10.0, c.Race(Race_0_idx).TechLevel(game.TechDrive)) assert.ErrorContains(t, - g.ShipGroupUpgrade(Race_0.Name, 1, "DRIVE", 0, 10.0), + g.ShipGroupUpgrade(Race_0.Name, c.ShipGroup(0).ID, "DRIVE", 0, 10.0), e.GenericErrorText(e.ErrUpgradeInsufficientResources)) - assert.NoError(t, g.ShipGroupUpgrade(Race_0.Name, 1, "DRIVE", 2, 1.2)) + assert.NoError(t, g.ShipGroupUpgrade(Race_0.Name, c.ShipGroup(0).ID, "DRIVE", 2, 1.2)) assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 4) assert.Equal(t, uint(8), c.ShipGroup(0).Number) assert.Equal(t, uint(2), c.ShipGroup(3).Number) @@ -169,6 +170,6 @@ func TestShipGroupUpgrade(t *testing.T) { assert.Equal(t, game.StateUpgrade, c.ShipGroup(3).State()) assert.ErrorContains(t, - g.ShipGroupUpgrade(Race_0.Name, 4, "DRIVE", 1, 1.3), + g.ShipGroupUpgrade(Race_0.Name, c.ShipGroup(3).ID, "DRIVE", 1, 1.3), e.GenericErrorText(e.ErrShipsBusy)) } diff --git a/internal/model/game/group.go b/internal/model/game/group.go index 1ef68c5..226a58a 100644 --- a/internal/model/game/group.go +++ b/internal/model/game/group.go @@ -105,10 +105,10 @@ func (t Tech) String() string { } type ShipGroup struct { - Index uint `json:"index"` // FIXME: use UUID for Group Index (ordered) - OwnerID uuid.UUID `json:"ownerId"` // Race link - TypeID uuid.UUID `json:"typeId"` // ShipType link - FleetID *uuid.UUID `json:"fleetId,omitempty"` // Fleet link + ID uuid.UUID `json:"id"` + OwnerID uuid.UUID `json:"ownerId"` // Race reference + TypeID uuid.UUID `json:"typeId"` // ShipType reference + FleetID *uuid.UUID `json:"fleetId,omitempty"` // Fleet reference Number uint `json:"number"` // Number (quantity) ships of specific ShipType CargoType *CargoType `json:"loadType,omitempty"` // Load Float `json:"load"` // Cargo loaded - "Масса груза" diff --git a/internal/model/game/group_test.go b/internal/model/game/group_test.go index 14b20d9..6f7714f 100644 --- a/internal/model/game/group_test.go +++ b/internal/model/game/group_test.go @@ -162,7 +162,7 @@ func TestShipGroupEqual(t *testing.T) { mat := game.CargoMaterial cap := game.CargoCapital left := &game.ShipGroup{ - Index: 1, + ID: uuid.New(), Number: 1, OwnerID: uuid.New(), @@ -241,7 +241,7 @@ func TestShipGroupEqual(t *testing.T) { // non-essential properties right = *left - left.Index = 2 + left.ID = uuid.New() assert.True(t, left.Equal(right)) // dirty hack to equalize loads diff --git a/internal/model/report/ship.go b/internal/model/report/ship.go index a4404c9..a5bb2c8 100644 --- a/internal/model/report/ship.go +++ b/internal/model/report/ship.go @@ -1,5 +1,7 @@ package report +import "github.com/google/uuid" + type ShipClass struct { Name string `json:"name"` Drive Float `json:"drive"` @@ -34,9 +36,9 @@ type IncomingGroup struct { type LocalGroup struct { OtherGroup - Index uint `json:"index"` - State string `json:"state"` - Fleet *string `json:"fleet"` + ID uuid.UUID `json:"id"` + State string `json:"state"` + Fleet *string `json:"fleet"` } type OtherGroup struct { diff --git a/internal/number/number.go b/internal/number/number.go index 773ae89..831cb3c 100644 --- a/internal/number/number.go +++ b/internal/number/number.go @@ -1,7 +1,6 @@ package number import ( - "cmp" "math" ) @@ -21,10 +20,3 @@ func fixed(num float64, precision int) float64 { func round(num float64) int { return int(num + math.Copysign(0.5, num)) } - -func Max[T cmp.Ordered](x, y T) T { - if cmp.Compare(x, y) == 1 { - return x - } - return y -} diff --git a/internal/number/number_test.go b/internal/number/number_test.go index 40b2fa0..f14a6c2 100644 --- a/internal/number/number_test.go +++ b/internal/number/number_test.go @@ -31,10 +31,3 @@ func TestFixed(t *testing.T) { }) } } - -func TestMax(t *testing.T) { - assert.Equal(t, 10., Max(9., 10.)) - assert.Equal(t, 11., Max(11., 10.)) - assert.Equal(t, 0, Max(-1, 0)) - assert.Equal(t, 1, Max(1, 0)) -}