refactor: group uuid instead of index

This commit is contained in:
IliaDenisov
2026-02-10 20:54:43 +03:00
parent 6c8384ce7a
commit 56998d4c2d
26 changed files with 333 additions and 363 deletions
+6 -6
View File
@@ -69,14 +69,14 @@ func (c *Cache) cacheShipsAndGroups() {
} else { } else {
c.cacheShipClassByShipGroupIndex = make(map[int]*game.ShipType) c.cacheShipClassByShipGroupIndex = make(map[int]*game.ShipType)
} }
for groupIndex := range c.g.ShipGroups { for sgi := range c.g.ShipGroups {
ri := c.RaceIndex(c.g.ShipGroups[groupIndex].OwnerID) ri := c.RaceIndex(c.g.ShipGroups[sgi].OwnerID)
c.cacheRaceIndexByShipGroupIndex[groupIndex] = ri c.cacheRaceIndexByShipGroupIndex[sgi] = ri
sti, ok := ShipClassIndex(c.g, ri, c.g.ShipGroups[groupIndex].TypeID) sci, ok := ShipClassIndex(c.g, ri, c.g.ShipGroups[sgi].TypeID)
if !ok { 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]
} }
} }
+16 -16
View File
@@ -78,7 +78,7 @@ func (c *Controller) ShipClassRemove(actor, typeName string) error {
return c.Cache.shipClassRemove(ri, typeName) 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) ri, err := c.Cache.validActor(actor)
if err != nil { if err != nil {
return err return err
@@ -87,31 +87,31 @@ func (c *Controller) ShipGroupLoad(actor string, groupIndex uint, cargoType stri
if !ok { if !ok {
return e.NewCargoTypeInvalidError(cargoType) 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) ri, err := c.Cache.validActor(actor)
if err != nil { if err != nil {
return err 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) ri, err := c.Cache.validActor(actor)
if err != nil { if err != nil {
return err 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) ri, err := c.Cache.validActor(actor)
if err != nil { if err != nil {
return err 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 { func (c *Controller) ShipGroupMerge(actor string) error {
@@ -123,23 +123,23 @@ func (c *Controller) ShipGroupMerge(actor string) error {
return nil 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) ri, err := c.Cache.validActor(actor)
if err != nil { if err != nil {
return err 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) ri, err := c.Cache.validActor(actor)
if err != nil { if err != nil {
return err 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) ri, err := c.Cache.validActor(actor)
if err != nil { if err != nil {
return err return err
@@ -148,15 +148,15 @@ func (c *Controller) ShipGroupTransfer(actor, acceptor string, groupIndex, quant
if err != nil { if err != nil {
return err 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) ri, err := c.Cache.validActor(actor)
if err != nil { if err != nil {
return err 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 { func (c *Controller) FleetMerge(actor, fleetSourceName, fleetTargetName string) error {
+8 -8
View File
@@ -51,15 +51,15 @@ type Ctrl interface {
ShipClassCreate(actor, typeName string, drive float64, ammo int, weapons, shileds, cargo float64) error ShipClassCreate(actor, typeName string, drive float64, ammo int, weapons, shileds, cargo float64) error
ShipClassMerge(actor, name, targetName string) error ShipClassMerge(actor, name, targetName string) error
ShipClassRemove(actor, typeName string) error ShipClassRemove(actor, typeName string) error
ShipGroupLoad(actor string, groupIndex uint, cargoType string, ships uint, quantity float64) error ShipGroupLoad(actor string, groupID uuid.UUID, cargoType string, ships uint, quantity float64) error
ShipGroupUnload(actor string, groupIndex uint, ships uint, quantity float64) error ShipGroupUnload(actor string, groupID uuid.UUID, ships uint, quantity float64) error
ShipGroupSend(actor string, groupIndex, planetNumber, quantity uint) error ShipGroupSend(actor string, groupID uuid.UUID, planetNumber, quantity uint) error
ShipGroupUpgrade(actor string, groupIndex uint, techInput string, limitShips uint, limitLevel float64) error ShipGroupUpgrade(actor string, groupID uuid.UUID, techInput string, limitShips uint, limitLevel float64) error
ShipGroupBreak(actor string, groupIndex, quantity uint) error ShipGroupBreak(actor string, groupID uuid.UUID, quantity uint) error
ShipGroupMerge(actor string) error ShipGroupMerge(actor string) error
ShipGroupDismantle(actor string, groupIndex, quantity uint) error ShipGroupDismantle(actor string, groupID uuid.UUID, quantity uint) error
ShipGroupTransfer(actor, acceptor string, groupIndex, quantity uint) error ShipGroupTransfer(actor, acceptor string, groupID uuid.UUID, quantity uint) error
ShipGroupJoinFleet(actor, fleetName string, group, count uint) error ShipGroupJoinFleet(actor, fleetName string, groupID uuid.UUID, count uint) error
FleetMerge(actor, fleetSourceName, fleetTargetName string) error FleetMerge(actor, fleetSourceName, fleetTargetName string) error
FleetSend(actor, fleetName string, planetNumber uint) error FleetSend(actor, fleetName string, planetNumber uint) error
ScienceCreate(actor, typeName string, drive, weapons, shields, cargo float64) error ScienceCreate(actor, typeName string, drive, weapons, shields, cargo float64) error
+20 -10
View File
@@ -1,13 +1,32 @@
package controller package controller
import ( import (
"fmt"
"iter" "iter"
"github.com/google/uuid" "github.com/google/uuid"
e "github.com/iliadenisov/galaxy/internal/error"
"github.com/iliadenisov/galaxy/internal/model/game" "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) { func (c *Cache) AddRace(n string) (int, uuid.UUID) {
id := uuid.New() id := uuid.New()
r := &game.Race{ r := &game.Race{
@@ -57,15 +76,6 @@ func (c *Cache) MustFleetID(ri int, name string) uuid.UUID {
panic("fleet not found") 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 { func (c *Cache) MustShipClass(ri int, name string) *game.ShipType {
st, _, ok := c.ShipClass(ri, name) st, _, ok := c.ShipClass(ri, name)
if !ok { if !ok {
-5
View File
@@ -130,8 +130,6 @@ func newGame() *game.Game {
func newCache() (*controller.Cache, *controller.Controller) { func newCache() (*controller.Cache, *controller.Controller) {
ctl := controller.NewGameController(newGame()) ctl := controller.NewGameController(newGame())
// g := newGame()
// c := controller.NewCache(g)
c := ctl.Cache 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_Gunship, 60, 3, 30, 100, 0))
assertNoError(c.ShipClassCreate(Race_0_idx, Race_0_Freighter, 8, 0, 0, 2, 10)) 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, 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.) 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 return c, ctl
} }
+11 -9
View File
@@ -46,7 +46,8 @@ func (c *Cache) FleetState(fleetID uuid.UUID) FleetState {
InSpace: func() (game.InSpace, bool) { return game.InSpace{}, false }, InSpace: func() (game.InSpace, bool) { return game.InSpace{}, false },
AtPlanet: func() (uint, bool) { return 0, 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 { if fs.State == fleetStateNil {
fs.State = sg.State() fs.State = sg.State()
fs.Destination = sg.Destination fs.Destination = sg.Destination
@@ -100,15 +101,15 @@ func (c *Cache) FleetSpeedAndMass(fi int) (float64, float64) {
return speed, mass 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) c.validateRaceIndex(ri)
name, ok := util.ValidateTypeName(fleetName) name, ok := util.ValidateTypeName(fleetName)
if !ok { if !ok {
return e.NewEntityTypeNameValidationError("%q", name) return e.NewEntityTypeNameValidationError("%q", name)
} }
sgi, ok := c.raceShipGroupIndex(ri, groupIndex) sgi, ok := c.raceShipGroupIndex(ri, groupID)
if !ok { if !ok {
return e.NewEntityNotExistsError("group #%d", groupIndex) return e.NewEntityNotExistsError("group %s", groupID)
} }
if state := c.ShipGroup(sgi).State(); state != game.StateInOrbit { 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 { 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 { if err != nil {
return err 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.validateRaceIndex(ri)
c.validateFleetIndex(fi) c.validateFleetIndex(fi)
return func(yield func(*game.ShipGroup) bool) { return func(yield func(int) bool) {
for sg := range c.listShipGroups(ri) { for sgi := range c.listShipGroupIdx(ri) {
sg := c.ShipGroup(sgi)
if sg.FleetID != nil && *sg.FleetID == c.g.Fleets[fi].ID { if sg.FleetID != nil && *sg.FleetID == c.g.Fleets[fi].ID {
if !yield(sg) { if !yield(sgi) {
break break
} }
} }
+6 -6
View File
@@ -28,8 +28,8 @@ func (c *Cache) FleetSend(ri, fi int, planetNumber uint) error {
return e.NewSendUnreachableDestinationError("range=%.03f", rangeToDestination) return e.NewSendUnreachableDestinationError("range=%.03f", rangeToDestination)
} }
for sg := range c.FleetGroups(ri, fi) { for sgi := range c.FleetGroupIdx(ri, fi) {
st := c.MustShipType(ri, sg.TypeID) st := c.MustShipType(ri, c.ShipGroup(sgi).TypeID)
if st.DriveBlockMass() == 0 { if st.DriveBlockMass() == 0 {
return e.NewSendShipHasNoDrivesError("Class=%s", st.Name) 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) { func (c *Cache) LaunchFleet(ri, fi int, destination uint) {
c.validateRaceIndex(ri) c.validateRaceIndex(ri)
c.validateFleetIndex(fi) c.validateFleetIndex(fi)
for sg := range c.FleetGroups(ri, fi) { for sgi := range c.FleetGroupIdx(ri, fi) {
c.LaunchShips(sg, destination) c.LaunchShips(sgi, destination)
} }
} }
func (c *Cache) UnsendFleet(ri, fi int) { func (c *Cache) UnsendFleet(ri, fi int) {
c.validateRaceIndex(ri) c.validateRaceIndex(ri)
c.validateFleetIndex(fi) c.validateFleetIndex(fi)
for sg := range c.FleetGroups(ri, fi) { for sgi := range c.FleetGroupIdx(ri, fi) {
c.UnsendShips(sg) c.UnsendShips(sgi)
} }
} }
+10 -10
View File
@@ -29,22 +29,22 @@ func TestFleetSend(t *testing.T) {
fleetUnmovable := "R0_Fleet_unmovable" fleetUnmovable := "R0_Fleet_unmovable"
fleetUnmovable2 := "R0_Fleet_unmovable2" 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.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.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.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.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.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) assert.Len(t, slices.Collect(c.ListFleets(Race_0_idx)), 3)
// group #2 - in_space // group #2 - in_space
@@ -75,14 +75,14 @@ func TestFleetSend(t *testing.T) {
assert.NoError(t, g.FleetSend(Race_0.Name, fleetSending, 2)) assert.NoError(t, g.FleetSend(Race_0.Name, fleetSending, 2))
fleetState := c.FleetState(c.MustFleetID(Race_0_idx, fleetSending)) fleetState := c.FleetState(c.MustFleetID(Race_0_idx, fleetSending))
assert.Equal(t, game.StateLaunched, fleetState.State) assert.Equal(t, game.StateLaunched, fleetState.State)
for sg := range c.FleetGroups(Race_0_idx, c.MustFleetIndex(c.MustFleetID(Race_0_idx, fleetSending))) { for sgi := range c.FleetGroupIdx(Race_0_idx, c.MustFleetIndex(c.MustFleetID(Race_0_idx, fleetSending))) {
assert.Equal(t, game.StateLaunched, sg.State()) assert.Equal(t, game.StateLaunched, c.ShipGroup(sgi).State())
} }
assert.NoError(t, g.FleetSend(Race_0.Name, fleetSending, 0)) assert.NoError(t, g.FleetSend(Race_0.Name, fleetSending, 0))
fleetState = c.FleetState(c.MustFleetID(Race_0_idx, fleetSending)) fleetState = c.FleetState(c.MustFleetID(Race_0_idx, fleetSending))
assert.Equal(t, game.StateInOrbit, fleetState.State) assert.Equal(t, game.StateInOrbit, fleetState.State)
for sg := range c.FleetGroups(Race_0_idx, c.MustFleetIndex(c.MustFleetID(Race_0_idx, fleetSending))) { for sgi := range c.FleetGroupIdx(Race_0_idx, c.MustFleetIndex(c.MustFleetID(Race_0_idx, fleetSending))) {
assert.Equal(t, game.StateInOrbit, sg.State()) assert.Equal(t, game.StateInOrbit, c.ShipGroup(sgi).State())
} }
} }
+17 -17
View File
@@ -5,6 +5,7 @@ import (
"slices" "slices"
"testing" "testing"
"github.com/google/uuid"
e "github.com/iliadenisov/galaxy/internal/error" e "github.com/iliadenisov/galaxy/internal/error"
"github.com/iliadenisov/galaxy/internal/model/game" "github.com/iliadenisov/galaxy/internal/model/game"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@@ -12,7 +13,7 @@ import (
func TestShipGroupJoinFleet(t *testing.T) { func TestShipGroupJoinFleet(t *testing.T) {
c, g := newCache() c, g := newCache()
var groupIndex uint = 1 groupIndex := uuid.Nil
fleetOne := "R0_Fleet_one" fleetOne := "R0_Fleet_one"
fleetTwo := "R0_Fleet_two" fleetTwo := "R0_Fleet_two"
@@ -27,6 +28,7 @@ func TestShipGroupJoinFleet(t *testing.T) {
// creating ShipGroup // creating ShipGroup
assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 5)) assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 5))
groupIndex = c.ShipGroup(0).ID
assert.ErrorContains(t, assert.ErrorContains(t,
g.ShipGroupJoinFleet(UnknownRace, fleetOne, groupIndex, 0), g.ShipGroupJoinFleet(UnknownRace, fleetOne, groupIndex, 0),
@@ -57,7 +59,7 @@ func TestShipGroupJoinFleet(t *testing.T) {
// create another ShipGroup // create another ShipGroup
assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Gunship, R0_Planet_0_num, 3)) 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)) assert.NoError(t, g.ShipGroupJoinFleet(Race_0.Name, fleetTwo, groupIndex, 2))
fleets = slices.Collect(c.ListFleets(Race_0_idx)) fleets = slices.Collect(c.ListFleets(Race_0_idx))
groups = slices.Collect(c.RaceShipGroups(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.NotNil(t, groups[gi].FleetID)
assert.Equal(t, fleets[1].ID, *groups[gi].FleetID) assert.Equal(t, fleets[1].ID, *groups[gi].FleetID)
assert.Equal(t, uint(2), groups[gi].Number) assert.Equal(t, uint(2), groups[gi].Number)
assert.Equal(t, uint(3), groups[gi].Index)
gi = 1 gi = 1
assert.Nil(t, groups[gi].FleetID) assert.Nil(t, groups[gi].FleetID)
assert.Equal(t, uint(1), groups[gi].Number) 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)) assert.NoError(t, g.ShipGroupJoinFleet(Race_0.Name, fleetOne, groupIndex, 0))
fleets = slices.Collect(c.ListFleets(Race_0_idx)) fleets = slices.Collect(c.ListFleets(Race_0_idx))
assert.Len(t, fleets, 2) assert.Len(t, fleets, 2)
@@ -94,7 +94,7 @@ func TestShipGroupJoinFleet(t *testing.T) {
gi = 3 gi = 3
c.ShipGroup(gi).StateInSpace = &InSpace c.ShipGroup(gi).StateInSpace = &InSpace
assert.ErrorContains(t, 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)) e.GenericErrorText(e.ErrShipsBusy))
c.ShipGroup(gi).StateInSpace = nil c.ShipGroup(gi).StateInSpace = nil
@@ -102,7 +102,7 @@ func TestShipGroupJoinFleet(t *testing.T) {
c.ShipGroup(0).StateInSpace = &InSpace c.ShipGroup(0).StateInSpace = &InSpace
c.ShipGroup(1).StateInSpace = c.ShipGroup(0).StateInSpace c.ShipGroup(1).StateInSpace = c.ShipGroup(0).StateInSpace
assert.ErrorContains(t, 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)) e.GenericErrorText(e.ErrShipsNotOnSamePlanet))
} }
@@ -133,21 +133,21 @@ func TestFleetMerge(t *testing.T) {
e.GenericErrorText(e.ErrRaceExinct)) e.GenericErrorText(e.ErrRaceExinct))
assert.ErrorContains(t, assert.ErrorContains(t,
g.ShipGroupJoinFleet(UnknownRace, fleetSourceOne, 1, 0), g.ShipGroupJoinFleet(UnknownRace, fleetSourceOne, c.ShipGroup(0).ID, 0),
e.GenericErrorText(e.ErrInputUnknownRace)) e.GenericErrorText(e.ErrInputUnknownRace))
assert.ErrorContains(t, 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)) 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, assert.ErrorContains(t,
g.FleetMerge(Race_0.Name, fleetSourceOne, fleetTargetTwo), g.FleetMerge(Race_0.Name, fleetSourceOne, fleetTargetTwo),
e.GenericErrorText(e.ErrInputEntityNotExists)) 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.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, assert.ErrorContains(t,
g.FleetMerge(Race_0.Name, fleetOnPlanet2, fleetTargetTwo), 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)) 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, 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, 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 fleetIndex := 0
speed, mass = c.FleetSpeedAndMass(fleetIndex) speed, mass = c.FleetSpeedAndMass(fleetIndex)
assert.Equal(t, s, speed) 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))) 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)) 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) speed, mass = c.FleetSpeedAndMass(fleetIndex)
assert.Equal(t, s, speed) assert.Equal(t, s, speed)
assert.Equal(t, m, mass) 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))) 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)) 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) speed, mass = c.FleetSpeedAndMass(fleetIndex)
assert.Equal(t, s, speed) assert.Equal(t, s, speed)
assert.Equal(t, m, mass) assert.Equal(t, m, mass)
+2 -2
View File
@@ -191,8 +191,8 @@ func TestProduceShips(t *testing.T) {
c.MustPlanet(R0_Planet_2_num).Population = 100 c.MustPlanet(R0_Planet_2_num).Population = 100
c.MustPlanet(R0_Planet_2_num).Industry = 100 c.MustPlanet(R0_Planet_2_num).Industry = 100
c.RaceTechLevel(Race_0_idx, game.TechDrive, 1.5) 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, c.ShipGroup(1).ID, "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(0).ID, "Drive", 0, 0))
assert.Equal(t, game.StateUpgrade, c.ShipGroup(0).State()) assert.Equal(t, game.StateUpgrade, c.ShipGroup(0).State())
assert.Equal(t, game.StateUpgrade, c.ShipGroup(1).State()) assert.Equal(t, game.StateUpgrade, c.ShipGroup(1).State())
+1 -1
View File
@@ -610,7 +610,7 @@ func (c *Cache) ReportLocalGroup(ri int, rep *mr.Report) {
sliceIndexValidate(&rep.LocalGroup, i) sliceIndexValidate(&rep.LocalGroup, i)
st := c.MustShipType(ri, sg.TypeID) st := c.MustShipType(ri, sg.TypeID)
c.otherGroup(&rep.LocalGroup[i].OtherGroup, sg, st) 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() rep.LocalGroup[i].State = sg.State().String()
if sg.FleetID != nil { if sg.FleetID != nil {
rep.LocalGroup[i].Fleet = &c.g.Fleets[c.MustFleetIndex(*sg.FleetID)].Name rep.LocalGroup[i].Fleet = &c.g.Fleets[c.MustFleetIndex(*sg.FleetID)].Name
+5 -4
View File
@@ -79,8 +79,8 @@ func (c *Cache) SendRoutedGroups() {
sortGroups := func(g []int) { sortGroups := func(g []int) {
// sort groups by largest CargoCapacity // sort groups by largest CargoCapacity
slices.SortFunc(g, func(l, r int) int { slices.SortFunc(g, func(l, r int) int {
return cmp.Or(cmp.Compare(c.ShipGroup(r).CargoCapacity(c.ShipGroupShipClass(r)), return cmp.Or(
c.ShipGroup(l).CargoCapacity(c.ShipGroupShipClass(l))), cmp.Compare(c.ShipGroup(r).CargoCapacity(c.ShipGroupShipClass(r)), c.ShipGroup(l).CargoCapacity(c.ShipGroupShipClass(l))),
cmp.Compare(l, r)) cmp.Compare(l, r))
}) })
@@ -115,7 +115,7 @@ func (c *Cache) SendRoutedGroups() {
ct = game.CargoMaterial ct = game.CargoMaterial
default: default:
for _, sgi := range groups { for _, sgi := range groups {
c.LaunchShips(c.ShipGroup(sgi), dest) c.LaunchShips(sgi, dest)
} }
groups = reorderGroups(groups) groups = reorderGroups(groups)
continue continue
@@ -131,6 +131,7 @@ func (c *Cache) SendRoutedGroups() {
toLoad = sgCapacity toLoad = sgCapacity
} else if maxShips := uint(math.Ceil(toLoad / (sgCapacity / float64(ships)))); maxShips < ships { } else if maxShips := uint(math.Ceil(toLoad / (sgCapacity / float64(ships)))); maxShips < ships {
newGroupIdx := c.breakGroupUnsafe(c.RaceIndex(sg.OwnerID), sgi, maxShips) newGroupIdx := c.breakGroupUnsafe(c.RaceIndex(sg.OwnerID), sgi, maxShips)
sgi = newGroupIdx
sg = c.ShipGroup(newGroupIdx) sg = c.ShipGroup(newGroupIdx)
} }
// decrease planet resource // decrease planet resource
@@ -138,7 +139,7 @@ func (c *Cache) SendRoutedGroups() {
// load group // load group
sg.Load = sg.Load.Add(toLoad) sg.Load = sg.Load.Add(toLoad)
sg.CargoType = &ct sg.CargoType = &ct
c.LaunchShips(sg, dest) c.LaunchShips(sgi, dest)
groups = reorderGroups(groups) groups = reorderGroups(groups)
} }
} }
+5 -3
View File
@@ -118,11 +118,11 @@ func TestListRoutedSendGroupIds(t *testing.T) {
// Foreign group -> idx 1 // Foreign group -> idx 1
assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 10)) 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 // 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, 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)) planet_0_groups := slices.Collect(c.ListRoutedSendGroupIds(0))
assert.Len(t, planet_0_groups, 1) assert.Len(t, planet_0_groups, 1)
@@ -150,9 +150,11 @@ func TestEnrouteGroups_SplitGroup(t *testing.T) {
c.SendRoutedGroups() c.SendRoutedGroups()
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 2) assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 2)
assert.Equal(t, game.StateInOrbit, c.ShipGroup(0).State()) assert.Equal(t, game.StateInOrbit, c.ShipGroup(0).State())
assert.Equal(t, uint(1), c.ShipGroup(0).Number) assert.Equal(t, uint(1), c.ShipGroup(0).Number)
assert.Equal(t, 0., c.ShipGroup(0).Load.F()) assert.Equal(t, 0., c.ShipGroup(0).Load.F())
assert.Equal(t, game.StateLaunched, c.ShipGroup(1).State()) assert.Equal(t, game.StateLaunched, c.ShipGroup(1).State())
assert.Equal(t, uint(4), c.ShipGroup(1).Number) assert.Equal(t, uint(4), c.ShipGroup(1).Number)
assert.Equal(t, 65., c.ShipGroup(1).Load.F()) 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 // 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, 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)) assert.NoError(t, g.PlanetRouteSet(Race_0.Name, "COL", R0_Planet_0_num, R0_Planet_2_num))
for _, rt := range game.RouteTypeSet { for _, rt := range game.RouteTypeSet {
+1 -1
View File
@@ -101,7 +101,7 @@ func (c *Cache) shipClassRemove(ri int, name string) error {
for sg := range c.listShipGroups(ri) { for sg := range c.listShipGroups(ri) {
if sg.TypeID == st.ID { 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:]...) c.g.Race[ri].ShipTypes = append(c.g.Race[ri].ShipTypes[:i], c.g.Race[ri].ShipTypes[i+1:]...)
+68 -91
View File
@@ -10,43 +10,8 @@ import (
"github.com/google/uuid" "github.com/google/uuid"
e "github.com/iliadenisov/galaxy/internal/error" e "github.com/iliadenisov/galaxy/internal/error"
"github.com/iliadenisov/galaxy/internal/model/game" "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 // ShipGroup is a proxy func, nothing to cache
func (c *Cache) ShipGroup(groupIndex int) *game.ShipGroup { func (c *Cache) ShipGroup(groupIndex int) *game.ShipGroup {
c.validateShipGroupIndex(groupIndex) 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 { func (c *Cache) ShipGroupOwnerRaceIndex(groupIndex int) int {
c.validateShipGroupIndex(groupIndex) c.validateShipGroupIndex(groupIndex)
if len(c.cacheRaceIndexByShipGroupIndex) == 0 { if len(c.cacheRaceIndexByShipGroupIndex) == 0 {
@@ -167,7 +122,7 @@ func (c *Cache) shipGroupMerge(ri int) {
for i := 0; i < len(raceGroups)-1; i++ { for i := 0; i < len(raceGroups)-1; i++ {
for j := len(raceGroups) - 1; j > i; j-- { for j := len(raceGroups) - 1; j > i; j-- {
if raceGroups[i].Equal(raceGroups[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[i].Number += raceGroups[j].Number
raceGroups = append(raceGroups[:j], raceGroups[j+1:]...) 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) sgi, ok := c.raceShipGroupIndex(ri, groupIndex)
if !ok { if !ok {
return e.NewEntityNotExistsError("group #%d", groupIndex) 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 { if ships == 0 && quantity > 0 {
return e.NewCargoQuantityWithoutGroupBreakError() return e.NewCargoQuantityWithoutGroupBreakError()
} }
sgi, ok := c.raceShipGroupIndex(ri, groupIndex) sgi, ok := c.raceShipGroupIndex(ri, groupID)
if !ok { if !ok {
return e.NewEntityNotExistsError("group #%d", groupIndex) return e.NewEntityNotExistsError("group %s", groupID)
} }
if state := c.ShipGroup(sgi).State(); state != game.StateInOrbit { if state := c.ShipGroup(sgi).State(); state != game.StateInOrbit {
return e.NewShipsBusyError("state: %s", state) 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) return e.NewCargoLoadNotEqualError("cargo: %v", *c.ShipGroup(sgi).CargoType)
} }
if ships > 0 && ships < c.ShipGroup(sgi).Number { 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 { if err != nil {
return err 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) c.validateRaceIndex(ri)
if ships == 0 && quantity > 0 { if ships == 0 && quantity > 0 {
return e.NewCargoQuantityWithoutGroupBreakError() return e.NewCargoQuantityWithoutGroupBreakError()
} }
sgi, ok := c.raceShipGroupIndex(ri, groupIndex) sgi, ok := c.raceShipGroupIndex(ri, groupID)
if !ok { if !ok {
return e.NewEntityNotExistsError("group #%d", groupIndex) return e.NewEntityNotExistsError("group %s", groupID)
} }
if state := c.ShipGroup(sgi).State(); state != game.StateInOrbit { if state := c.ShipGroup(sgi).State(); state != game.StateInOrbit {
return e.NewShipsBusyError("state: %s", state) 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) return e.NewEntityNotOwnedError("planet #%d unload %v", p.Number, ct)
} }
if ships > 0 && ships < c.ShipGroup(sgi).Number { 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 { if err != nil {
return err return err
} }
@@ -405,13 +360,13 @@ func (c *Cache) unsafeUnloadCargo(sgi int, q float64) {
p.UnpackCapital() 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 { if ri == riAccept {
return e.NewSameRaceError(c.g.Race[riAccept].Name) return e.NewSameRaceError(c.g.Race[riAccept].Name)
} }
sgi, ok := c.raceShipGroupIndex(ri, groupIndex) sgi, ok := c.raceShipGroupIndex(ri, groupID)
if !ok { if !ok {
return e.NewEntityNotExistsError("group #%d", groupIndex) return e.NewEntityNotExistsError("group %s", groupID)
} }
sg := c.ShipGroup(sgi) sg := c.ShipGroup(sgi)
state := sg.State() state := sg.State()
@@ -444,6 +399,7 @@ func (c *Cache) shipGroupTransfer(ri, riAccept int, groupIndex, quantity uint) (
} }
newGroup := *(sg) newGroup := *(sg)
newGroup.ID = uuid.New()
newGroup.TypeID = c.g.Race[riAccept].ShipTypes[stAcc].ID newGroup.TypeID = c.g.Race[riAccept].ShipTypes[stAcc].ID
newGroup.Tech = maps.Clone(sg.Tech) newGroup.Tech = maps.Clone(sg.Tech)
if state == game.StateLaunched { if state == game.StateLaunched {
@@ -451,6 +407,7 @@ func (c *Cache) shipGroupTransfer(ri, riAccept int, groupIndex, quantity uint) (
} }
if quantity == 0 || quantity == sg.Number { if quantity == 0 || quantity == sg.Number {
// FIXME: remove fleet & invalidate cache?
c.unsafeDeleteShipGroup(sgi) c.unsafeDeleteShipGroup(sgi)
} else { } else {
newGroup.Number = quantity newGroup.Number = quantity
@@ -462,17 +419,11 @@ func (c *Cache) shipGroupTransfer(ri, riAccept int, groupIndex, quantity uint) (
return nil 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) c.validateRaceIndex(ri)
sgi := -1 sgi, ok := c.raceShipGroupIndex(ri, groupID)
for i := range c.ShipGroupsIndex() { if !ok {
if c.ShipGroupOwnerRaceIndex(i) == ri && c.ShipGroup(i).Index == groupIndex { return e.NewEntityNotExistsError("group %s", groupID)
sgi = i
break
}
}
if sgi < 0 {
return e.NewEntityNotExistsError("group #%d", groupIndex)
} }
if state := c.ShipGroup(sgi).State(); state != game.StateInOrbit { 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 { if quantity == 0 || quantity == c.ShipGroup(sgi).Number {
c.internalShipGroupJoinFleet(sgi, nil) c.internalShipGroupJoinFleet(sgi, nil)
} else { } else {
if _, err := c.breakGroupSafe(ri, groupIndex, quantity); err != nil { if _, err := c.breakGroupSafe(ri, groupID, quantity); err != nil {
return err return err
} }
} }
@@ -494,14 +445,14 @@ func (c *Cache) ShipGroupBreak(ri int, groupIndex, quantity uint) error {
return nil 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) c.validateRaceIndex(ri)
sgi, ok := c.raceShipGroupIndex(ri, groupIndex) sgi, ok := c.raceShipGroupIndex(ri, groupID)
if !ok { if !ok {
return -1, e.NewEntityNotExistsError("group #%d", groupIndex) return -1, e.NewEntityNotExistsError("group %s", groupID)
} }
if c.ShipGroup(sgi).Number < newGroupShips { 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 return c.breakGroupUnsafe(ri, sgi, newGroupShips), nil
} }
@@ -519,35 +470,35 @@ func (c *Cache) breakGroupUnsafe(ri, sgi int, newGroupShips uint) int {
// Internal funcs // Internal funcs
func (c *Cache) appendShipGroup(ri int, sg *game.ShipGroup) int { func (c *Cache) raceShipGroupIndex(ri int, id uuid.UUID) (int, bool) {
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) {
c.validateRaceIndex(ri) c.validateRaceIndex(ri)
for i := range c.ShipGroupsIndex() { 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 i, true
} }
} }
return -1, false 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] { func (c *Cache) listShipGroups(ri int) iter.Seq[*game.ShipGroup] {
c.validateRaceIndex(ri) c.validateRaceIndex(ri)
return func(yield func(*game.ShipGroup) bool) { return func(yield func(*game.ShipGroup) bool) {
for i := range c.g.ShipGroups { for sgi := range c.listShipGroupIdx(ri) {
if ri == c.ShipGroupOwnerRaceIndex(i) { if !yield(c.ShipGroup(sgi)) {
if !yield(&c.g.ShipGroups[i]) { return
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))) 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
}
+3 -3
View File
@@ -19,8 +19,8 @@ func TestListMoveableGroupIds(t *testing.T) {
// 3: idx = 2 / [v] In-Fleet group // 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, 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", c.ShipGroup(1).ID, 0))
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))
// 4: idx = 3 / [v] In_Space // 4: idx = 3 / [v] In_Space
assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 7)) 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 // 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, 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()) movableGroups := slices.Collect(c.ListMoveableGroupIds())
assert.Len(t, movableGroups, 5) assert.Len(t, movableGroups, 5)
+25 -34
View File
@@ -1,17 +1,18 @@
package controller package controller
import ( import (
"github.com/google/uuid"
e "github.com/iliadenisov/galaxy/internal/error" e "github.com/iliadenisov/galaxy/internal/error"
"github.com/iliadenisov/galaxy/internal/model/game" "github.com/iliadenisov/galaxy/internal/model/game"
"github.com/iliadenisov/galaxy/internal/util" "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) c.validateRaceIndex(ri)
sgi, ok := c.raceShipGroupIndex(ri, groupIndex) sgi, ok := c.raceShipGroupIndex(ri, groupID)
if !ok { if !ok {
return e.NewEntityNotExistsError("group #%d", groupIndex) return e.NewEntityNotExistsError("group %s", groupID)
} }
st := c.ShipGroupShipClass(sgi) 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 { 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 { if err != nil {
return err return err
} }
@@ -50,48 +51,38 @@ func (c *Cache) shipGroupSend(ri int, groupIndex, planetNumber, quantity uint) e
} }
if p1.Number == p2.Number { if p1.Number == p2.Number {
c.UnsendShips(c.ShipGroup(sgi)) c.UnsendShips(sgi)
c.shipGroupMerge(ri) c.shipGroupMerge(ri)
return nil return nil
} }
c.LaunchShips(c.ShipGroup(sgi), planetNumber) c.LaunchShips(sgi, planetNumber)
return nil return nil
} }
func (c *Cache) LaunchShips(sg *game.ShipGroup, destination uint) *game.ShipGroup { func (c *Cache) LaunchShips(sgi int, destination uint) *game.ShipGroup {
for i := range c.ShipGroupsIndex() { sg := c.ShipGroup(sgi)
if c.ShipGroup(i).OwnerID == sg.OwnerID && c.ShipGroup(i).Index == sg.Index { var p *game.Planet
state := c.ShipGroup(i).State() switch sg.State() {
var p *game.Planet case game.StateInOrbit:
switch state { p = c.MustPlanet(sg.Destination)
case game.StateInOrbit: case game.StateLaunched:
p = c.MustPlanet(sg.Destination) p = c.MustPlanet(sg.StateInSpace.Origin)
case game.StateLaunched: default:
p = c.MustPlanet(sg.StateInSpace.Origin) panic("state invalid")
default:
panic("state invalid")
}
c.g.ShipGroups[i] = LaunchShips(*sg, destination, p.X.F(), p.Y.F())
return &c.g.ShipGroups[i]
}
} }
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 { func (c *Cache) UnsendShips(sgi int) *game.ShipGroup {
for i := range c.ShipGroupsIndex() { sg := c.ShipGroup(sgi)
if c.ShipGroup(i).OwnerID == sg.OwnerID && c.ShipGroup(i).Index == sg.Index { if sg.State() != game.StateLaunched {
state := c.ShipGroup(i).State() panic("state invalid")
if state != game.StateLaunched {
panic("state invalid")
}
c.g.ShipGroups[i] = UnsendShips(*sg)
return &c.g.ShipGroups[i]
}
} }
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 { func LaunchShips(sg game.ShipGroup, destination uint, originX, originY float64) game.ShipGroup {
+27 -24
View File
@@ -4,6 +4,7 @@ import (
"slices" "slices"
"testing" "testing"
"github.com/google/uuid"
e "github.com/iliadenisov/galaxy/internal/error" e "github.com/iliadenisov/galaxy/internal/error"
"github.com/iliadenisov/galaxy/internal/model/game" "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.NoError(t, c.CreateShips(Race_0_idx, "Fortress", R0_Planet_0_num, 1))
assert.ErrorContains(t, assert.ErrorContains(t,
g.ShipGroupSend(UnknownRace, 1, 2, 0), g.ShipGroupSend(UnknownRace, c.ShipGroup(0).ID, 2, 0),
e.GenericErrorText(e.ErrInputUnknownRace)) e.GenericErrorText(e.ErrInputUnknownRace))
assert.ErrorContains(t, 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)) e.GenericErrorText(e.ErrRaceExinct))
assert.ErrorContains(t, assert.ErrorContains(t,
g.ShipGroupSend(Race_0.Name, 555, 2, 0), g.ShipGroupSend(Race_0.Name, uuid.New(), 2, 0),
e.GenericErrorText(e.ErrInputEntityNotExists)) e.GenericErrorText(e.ErrInputEntityNotExists))
assert.ErrorContains(t, 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)) e.GenericErrorText(e.ErrInputEntityNotExists))
assert.ErrorContains(t, 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)) e.GenericErrorText(e.ErrShipsBusy))
assert.ErrorContains(t, 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)) e.GenericErrorText(e.ErrSendShipHasNoDrives))
assert.ErrorContains(t, 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)) e.GenericErrorText(e.ErrBeakGroupNumberNotEnough))
assert.ErrorContains(t, 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)) 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.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 4)
assert.Equal(t, uint(7), c.ShipGroup(0).Number) assert.Equal(t, uint(7), c.ShipGroup(0).Number)
assert.Equal(t, game.StateInOrbit, c.ShipGroup(0).State()) 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.X)
assert.Nil(t, c.ShipGroup(3).StateInSpace.Y) 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.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.Equal(t, uint(9), c.ShipGroup(0).Number)
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 3) assert.Equal(t, game.StateInOrbit, c.ShipGroup(0).State())
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.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.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, uint(10), c.ShipGroup(0).Number)
assert.Equal(t, game.StateLaunched, c.MustShipGroup(Race_0_idx, 1).State()) 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())
} }
+71 -71
View File
@@ -82,16 +82,16 @@ func TestShipGroupMerge(t *testing.T) {
switch { switch {
case sg.TypeID == shipTypeID(Race_0_idx, Race_0_Freighter) && sg.TechLevel(game.TechDrive) == 1.1: 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(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: 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(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: 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.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: 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(13), sg.Number)
assert.Equal(t, uint(3), sg.Index) // assert.Equal(t, uint(3), sg.Index)
default: default:
t.Error("not all ship groups covered") t.Error("not all ship groups covered")
} }
@@ -105,48 +105,48 @@ func TestShipGroupBreak(t *testing.T) {
c.ShipGroup(1).StateInSpace = &InSpace c.ShipGroup(1).StateInSpace = &InSpace
fleet := "R0_Fleet" 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, assert.ErrorContains(t,
g.ShipGroupBreak(UnknownRace, 1, 0), g.ShipGroupBreak(UnknownRace, c.ShipGroup(0).ID, 0),
e.GenericErrorText(e.ErrInputUnknownRace)) e.GenericErrorText(e.ErrInputUnknownRace))
assert.ErrorContains(t, assert.ErrorContains(t,
g.ShipGroupBreak(Race_Extinct.Name, 1, 0), g.ShipGroupBreak(Race_Extinct.Name, c.ShipGroup(0).ID, 0),
e.GenericErrorText(e.ErrRaceExinct)) e.GenericErrorText(e.ErrRaceExinct))
assert.ErrorContains(t, assert.ErrorContains(t,
g.ShipGroupBreak(Race_0.Name, 555, 0), g.ShipGroupBreak(Race_0.Name, uuid.New(), 0),
e.GenericErrorText(e.ErrInputEntityNotExists)) e.GenericErrorText(e.ErrInputEntityNotExists))
assert.ErrorContains(t, assert.ErrorContains(t,
g.ShipGroupBreak(Race_0.Name, 1, 17), g.ShipGroupBreak(Race_0.Name, c.ShipGroup(0).ID, 17),
e.GenericErrorText(e.ErrBeakGroupNumberNotEnough)) e.GenericErrorText(e.ErrBeakGroupNumberNotEnough))
assert.ErrorContains(t, assert.ErrorContains(t,
g.ShipGroupBreak(Race_0.Name, 2, 0), g.ShipGroupBreak(Race_0.Name, c.ShipGroup(1).ID, 0),
e.GenericErrorText(e.ErrShipsBusy)) e.GenericErrorText(e.ErrShipsBusy))
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 2) assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 2)
assert.Len(t, slices.Collect(c.ListFleets(Race_0_idx)), 1) assert.Len(t, slices.Collect(c.ListFleets(Race_0_idx)), 1)
// group #1 -> group #3 (5 new, 8 left) // 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.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 3)
assert.Equal(t, uint(8), c.ShipGroup(0).Number) assert.Equal(t, uint(8), c.ShipGroup(0).Number)
assert.NotNil(t, c.ShipGroup(0).FleetID) assert.NotNil(t, c.ShipGroup(0).FleetID)
assert.Equal(t, uint(5), c.ShipGroup(2).Number) 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).FleetID)
assert.Nil(t, c.ShipGroup(2).CargoType) assert.Nil(t, c.ShipGroup(2).CargoType)
// group #1 -> group #4 (2 new, 6 left) // group #1 -> group #4 (2 new, 6 left)
c.ShipGroup(0).CargoType = game.CargoColonist.Ref() c.ShipGroup(0).CargoType = game.CargoColonist.Ref()
c.ShipGroup(0).Load = 32.8 // 8 ships c.ShipGroup(0).Load = 32.8 // 8 ships
assert.NoError(t, c.ShipGroupBreak(Race_0_idx, 1, 2)) // group #4 (3) 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.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 4)
assert.Equal(t, uint(6), c.ShipGroup(0).Number) assert.Equal(t, uint(6), c.ShipGroup(0).Number)
assert.NotNil(t, c.ShipGroup(0).FleetID) assert.NotNil(t, c.ShipGroup(0).FleetID)
assert.Equal(t, uint(2), c.ShipGroup(3).Number) 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.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.NotNil(t, c.ShipGroup(3).FleetID)
assert.Equal(t, game.CargoColonist.Ref(), c.ShipGroup(0).CargoType) 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())) assert.Equal(t, 8.2, number.Fixed3(c.ShipGroup(3).Load.F()))
// group #1 -> MAX 6 off the fleet // 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.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 4)
assert.Equal(t, uint(6), c.ShipGroup(0).Number) assert.Equal(t, uint(6), c.ShipGroup(0).Number)
assert.Nil(t, c.ShipGroup(0).FleetID) assert.Nil(t, c.ShipGroup(0).FleetID)
// group #4 -> ALL off the fleet // 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.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 4)
assert.Equal(t, uint(2), c.ShipGroup(3).Number) assert.Equal(t, uint(2), c.ShipGroup(3).Number)
assert.Nil(t, c.ShipGroup(3).FleetID) assert.Nil(t, c.ShipGroup(3).FleetID)
@@ -170,10 +170,10 @@ func TestShipGroupBreak(t *testing.T) {
func TestShipGroupTransfer(t *testing.T) { func TestShipGroupTransfer(t *testing.T) {
c, g := newCache() 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_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, 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", 2, 0)) assert.NoError(t, g.ShipGroupJoinFleet(Race_0.Name, "R0_Fleet", c.ShipGroup(2).ID, 0))
assert.NotNil(t, c.ShipGroup(2).FleetID) assert.NotNil(t, c.ShipGroup(2).FleetID)
c.ShipGroup(2).StateInSpace = &InSpace c.ShipGroup(2).StateInSpace = &InSpace
c.ShipGroup(2).CargoType = game.CargoMaterial.Ref() 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.Len(t, slices.Collect(c.RaceShipGroups(Race_1_idx)), 1)
assert.ErrorContains(t, 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)) e.GenericErrorText(e.ErrInputUnknownRace))
assert.ErrorContains(t, 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)) e.GenericErrorText(e.ErrInputUnknownRace))
assert.ErrorContains(t, 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)) e.GenericErrorText(e.ErrRaceExinct))
assert.ErrorContains(t, 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)) e.GenericErrorText(e.ErrRaceExinct))
assert.ErrorContains(t, 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)) e.GenericErrorText(e.ErrInputSameRace))
assert.ErrorContains(t, 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)) e.GenericErrorText(e.ErrInputEntityNotExists))
assert.ErrorContains(t, 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)) e.GenericErrorText(e.ErrBeakGroupNumberNotEnough))
assert.ErrorContains(t, 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)) 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_0_idx)), 2)
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_1_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.Equal(t, c.ShipGroup(3).Number, uint(11))
assert.Nil(t, c.ShipGroup(3).FleetID) 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_0_idx)), 3)
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_1_idx)), 1) 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.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.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 4)
assert.Equal(t, game.StateInOrbit, c.ShipGroup(4).State()) 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, game.StateLaunched, c.ShipGroup(4).State())
assert.Equal(t, c.ShipGroup(4).OwnerID, Race_0_ID) 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_0_idx)), 3)
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_1_idx)), 2) assert.Len(t, slices.Collect(c.RaceShipGroups(Race_1_idx)), 2)
assert.Equal(t, game.StateTransfer, c.ShipGroup(4).State()) assert.Equal(t, game.StateTransfer, c.ShipGroup(4).State())
assert.Equal(t, c.ShipGroup(4).OwnerID, Race_1_ID) assert.Equal(t, c.ShipGroup(4).OwnerID, Race_1_ID)
assert.ErrorContains(t, 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)) e.GenericErrorText(e.ErrShipsBusy))
} }
@@ -280,49 +280,49 @@ func TestShipGroupLoad(t *testing.T) {
// tests // tests
assert.ErrorContains(t, 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)) e.GenericErrorText(e.ErrInputUnknownRace))
assert.ErrorContains(t, 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)) e.GenericErrorText(e.ErrRaceExinct))
assert.ErrorContains(t, 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)) e.GenericErrorText(e.ErrInputCargoTypeInvalid))
assert.ErrorContains(t, 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)) e.GenericErrorText(e.ErrInputEntityNotExists))
assert.ErrorContains(t, 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)) e.GenericErrorText(e.ErrShipsBusy))
assert.ErrorContains(t, 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)) e.GenericErrorText(e.ErrInputEntityNotOwned))
assert.ErrorContains(t, 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)) e.GenericErrorText(e.ErrInputNoCargoBay))
assert.ErrorContains(t, 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)) e.GenericErrorText(e.ErrInputCargoLoadNotEqual))
// initial planet is empty // initial planet is empty
assert.ErrorContains(t, 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)) e.GenericErrorText(e.ErrInputCargoLoadNotEnough))
// add cargo to planet // add cargo to planet
c.PutMaterial(R0_Planet_0_num, 100) c.PutMaterial(R0_Planet_0_num, 100)
// not enough on the planet // not enough on the planet
assert.ErrorContains(t, 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)) e.GenericErrorText(e.ErrInputCargoLoadNotEnough))
// quantity > ships // quantity > ships
assert.ErrorContains(t, 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)) e.GenericErrorText(e.ErrInputCargoQuantityWithoutGroupBreak))
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 5) assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 5)
// break group and load maximum // 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.Equal(t, 58.0, c.MustPlanet(R0_Planet_0_num).Material.F())
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 6) assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 6)
assert.Nil(t, c.ShipGroup(0).CargoType) 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()) assert.Equal(t, 42.0, c.ShipGroup(5).Load.F())
// break group and load limited // 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.Equal(t, 40.0, c.MustPlanet(R0_Planet_0_num).Material.F())
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 7) assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 7)
assert.Nil(t, c.ShipGroup(0).CargoType) assert.Nil(t, c.ShipGroup(0).CargoType)
@@ -346,7 +346,7 @@ func TestShipGroupLoad(t *testing.T) {
// add cargo to planet // add cargo to planet
c.PutMaterial(R0_Planet_0_num, 100) c.PutMaterial(R0_Planet_0_num, 100)
// loading all available cargo // 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.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, 0.0, c.MustPlanet(R0_Planet_0_num).Material.F())
assert.Equal(t, 100.0, c.ShipGroup(0).Load.F()) // free: 131.0 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 // add cargo to planet
c.PutMaterial(R0_Planet_0_num, 200) 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.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, 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, 131.0, c.ShipGroup(0).Load.F()) // free: 100.0
assert.Equal(t, game.CargoMaterial.Ref(), c.ShipGroup(0).CargoType) assert.Equal(t, game.CargoMaterial.Ref(), c.ShipGroup(0).CargoType)
// load to maximum cargo space left // 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.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, 153.0, c.MustPlanet(R0_Planet_0_num).Material.F())
assert.Equal(t, 147.0, c.ShipGroup(0).Load.F()) // free: 0.0 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 // ship group is full
assert.ErrorContains(t, 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)) e.GenericErrorText(e.ErrInputCargoLoadNoSpaceLeft))
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 7) assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 7)
} }
@@ -408,39 +408,39 @@ func TestShipGroupUnload(t *testing.T) {
// tests // tests
assert.ErrorContains(t, assert.ErrorContains(t,
g.ShipGroupUnload(UnknownRace, 1, 0, 0), g.ShipGroupUnload(UnknownRace, c.ShipGroup(0).ID, 0, 0),
e.GenericErrorText(e.ErrInputUnknownRace)) e.GenericErrorText(e.ErrInputUnknownRace))
assert.ErrorContains(t, 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)) e.GenericErrorText(e.ErrRaceExinct))
assert.ErrorContains(t, assert.ErrorContains(t,
g.ShipGroupUnload(Race_0.Name, 555, 0, 0), g.ShipGroupUnload(Race_0.Name, uuid.New(), 0, 0),
e.GenericErrorText(e.ErrInputEntityNotExists)) e.GenericErrorText(e.ErrInputEntityNotExists))
assert.ErrorContains(t, 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)) e.GenericErrorText(e.ErrShipsBusy))
assert.ErrorContains(t, 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)) e.GenericErrorText(e.ErrInputNoCargoBay))
assert.ErrorContains(t, 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)) e.GenericErrorText(e.ErrInputCargoUnloadEmpty))
assert.ErrorContains(t, 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)) e.GenericErrorText(e.ErrInputEntityNotOwned))
c.ShipGroup(0).CargoType = game.CargoColonist.Ref() c.ShipGroup(0).CargoType = game.CargoColonist.Ref()
c.ShipGroup(0).Load = 100 c.ShipGroup(0).Load = 100
assert.ErrorContains(t, 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)) e.GenericErrorText(e.ErrInputCargoUnoadNotEnough))
assert.ErrorContains(t, 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)) e.GenericErrorText(e.ErrInputCargoQuantityWithoutGroupBreak))
assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 6) assert.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 6)
// unload MAT on foreign planet / break group // 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.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.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 7)
assert.Equal(t, uint(3), c.ShipGroup(6).Number) 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())) assert.Equal(t, 72.727, number.Fixed3(c.ShipGroup(5).Load.F()))
// unload MAT on foreign planet / break group / limited MAT // 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.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.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 8)
assert.Equal(t, uint(3), c.ShipGroup(7).Number) 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())) assert.Equal(t, 45.455, number.Fixed3(c.ShipGroup(5).Load.F()))
// unload ALL // 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.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.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 8)
assert.Equal(t, uint(10), c.ShipGroup(0).Number) assert.Equal(t, uint(10), c.ShipGroup(0).Number)
@@ -502,26 +502,26 @@ func TestShipGroupDismantle(t *testing.T) {
// tests // tests
assert.ErrorContains(t, assert.ErrorContains(t,
g.ShipGroupDismantle(UnknownRace, 1, 0), g.ShipGroupDismantle(UnknownRace, c.ShipGroup(0).ID, 0),
e.GenericErrorText(e.ErrInputUnknownRace)) e.GenericErrorText(e.ErrInputUnknownRace))
assert.ErrorContains(t, assert.ErrorContains(t,
g.ShipGroupDismantle(Race_Extinct.Name, 1, 0), g.ShipGroupDismantle(Race_Extinct.Name, c.ShipGroup(0).ID, 0),
e.GenericErrorText(e.ErrRaceExinct)) e.GenericErrorText(e.ErrRaceExinct))
assert.ErrorContains(t, assert.ErrorContains(t,
g.ShipGroupDismantle(Race_0.Name, 555, 0), g.ShipGroupDismantle(Race_0.Name, uuid.New(), 0),
e.GenericErrorText(e.ErrInputEntityNotExists)) e.GenericErrorText(e.ErrInputEntityNotExists))
assert.ErrorContains(t, assert.ErrorContains(t,
g.ShipGroupDismantle(Race_0.Name, 2, 0), g.ShipGroupDismantle(Race_0.Name, c.ShipGroup(1).ID, 0),
e.GenericErrorText(e.ErrShipsBusy)) e.GenericErrorText(e.ErrShipsBusy))
assert.ErrorContains(t, assert.ErrorContains(t,
g.ShipGroupDismantle(Race_0.Name, 3, 12), g.ShipGroupDismantle(Race_0.Name, c.ShipGroup(2).ID, 12),
e.GenericErrorText(e.ErrBeakGroupNumberNotEnough)) e.GenericErrorText(e.ErrBeakGroupNumberNotEnough))
groupEmptyMass := c.ShipGroup(4).EmptyMass(c.MustShipClass(Race_0_idx, Race_0_Freighter)) groupEmptyMass := c.ShipGroup(4).EmptyMass(c.MustShipClass(Race_0_idx, Race_0_Freighter))
planetMAT := c.MustPlanet(R1_Planet_1_num).Material.F() planetMAT := c.MustPlanet(R1_Planet_1_num).Material.F()
planetCOL := c.MustPlanet(R1_Planet_1_num).Colonists.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.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, planetMAT+groupEmptyMass, c.MustPlanet(R1_Planet_1_num).Material.F())
assert.Equal(t, planetCOL, c.MustPlanet(R1_Planet_1_num).Colonists.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)) groupEmptyMass = c.ShipGroup(3).EmptyMass(c.MustShipClass(Race_0_idx, Race_0_Freighter))
groupLoadMAT := c.ShipGroup(3).Load.F() groupLoadMAT := c.ShipGroup(3).Load.F()
planetMAT = c.MustPlanet(R1_Planet_1_num).Material.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.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 3)
assert.Equal(t, planetMAT+groupEmptyMass+groupLoadMAT, c.MustPlanet(R1_Planet_1_num).Material.F()) 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() freePOPLeft := c.MustPlanet(R0_Planet_0_num).Size.F() - c.MustPlanet(R0_Planet_0_num).Population.F()
expectAddedCOL := (expectPOPIncrease - freePOPLeft) / 8 expectAddedCOL := (expectPOPIncrease - freePOPLeft) / 8
expectAddedPOP := freePOPLeft 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.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, planetCOL+expectAddedCOL, c.MustPlanet(R0_Planet_0_num).Colonists.F())
assert.Equal(t, planetPOP+expectAddedPOP, c.MustPlanet(R0_Planet_0_num).Population.F()) assert.Equal(t, planetPOP+expectAddedPOP, c.MustPlanet(R0_Planet_0_num).Population.F())
+6 -5
View File
@@ -5,15 +5,16 @@ import (
"slices" "slices"
"strings" "strings"
"github.com/google/uuid"
e "github.com/iliadenisov/galaxy/internal/error" e "github.com/iliadenisov/galaxy/internal/error"
"github.com/iliadenisov/galaxy/internal/model/game" "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) c.validateRaceIndex(ri)
sgi, ok := c.raceShipGroupIndex(ri, groupIndex) sgi, ok := c.raceShipGroupIndex(ri, groupID)
if !ok { if !ok {
return e.NewEntityNotExistsError("group #%d", groupIndex) return e.NewEntityNotExistsError("group %s", groupID)
} }
st := c.ShipGroupShipClass(sgi) st := c.ShipGroupShipClass(sgi)
sg := c.ShipGroup(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) p := c.MustPlanet(sg.Destination)
if p.Owned() && !p.OwnedBy(c.g.Race[ri].ID) { 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{ upgradeValidTech := map[string]game.Tech{
@@ -117,7 +118,7 @@ func (c *Cache) shipGroupUpgrade(ri int, groupIndex uint, techInput string, limi
// break group if needed // break group if needed
if maxUpgradableShips < sg.Number { if maxUpgradableShips < sg.Number {
nsgi, err := c.breakGroupSafe(ri, groupIndex, maxUpgradableShips) nsgi, err := c.breakGroupSafe(ri, groupID, maxUpgradableShips)
if err != nil { if err != nil {
return err return err
} }
+14 -13
View File
@@ -4,6 +4,7 @@ import (
"slices" "slices"
"testing" "testing"
"github.com/google/uuid"
"github.com/iliadenisov/galaxy/internal/controller" "github.com/iliadenisov/galaxy/internal/controller"
e "github.com/iliadenisov/galaxy/internal/error" e "github.com/iliadenisov/galaxy/internal/error"
"github.com/iliadenisov/galaxy/internal/model/game" "github.com/iliadenisov/galaxy/internal/model/game"
@@ -125,43 +126,43 @@ func TestShipGroupUpgrade(t *testing.T) {
c.ShipGroup(2).Destination = R1_Planet_1_num c.ShipGroup(2).Destination = R1_Planet_1_num
assert.ErrorContains(t, assert.ErrorContains(t,
g.ShipGroupUpgrade(UnknownRace, 1, "DRIVE", 0, 0), g.ShipGroupUpgrade(UnknownRace, c.ShipGroup(0).ID, "DRIVE", 0, 0),
e.GenericErrorText(e.ErrInputUnknownRace)) e.GenericErrorText(e.ErrInputUnknownRace))
assert.ErrorContains(t, 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)) e.GenericErrorText(e.ErrRaceExinct))
assert.ErrorContains(t, 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)) e.GenericErrorText(e.ErrInputEntityNotExists))
assert.ErrorContains(t, 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)) e.GenericErrorText(e.ErrShipsBusy))
assert.ErrorContains(t, 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)) e.GenericErrorText(e.ErrInputEntityNotOwned))
assert.ErrorContains(t, 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)) e.GenericErrorText(e.ErrInputTechUnknown))
assert.ErrorContains(t, 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)) e.GenericErrorText(e.ErrInputUpgradeShipTechNotUsed))
assert.ErrorContains(t, 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)) e.GenericErrorText(e.ErrInputUpgradeParameterNotAllowed))
assert.ErrorContains(t, 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)) e.GenericErrorText(e.ErrInputUpgradeTechLevelInsufficient))
assert.ErrorContains(t, 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)) e.GenericErrorText(e.ErrInputUpgradeShipsAlreadyUpToDate))
c.RaceTechLevel(Race_0_idx, game.TechDrive, 10.0) c.RaceTechLevel(Race_0_idx, game.TechDrive, 10.0)
assert.Equal(t, 10.0, c.Race(Race_0_idx).TechLevel(game.TechDrive)) assert.Equal(t, 10.0, c.Race(Race_0_idx).TechLevel(game.TechDrive))
assert.ErrorContains(t, 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)) 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.Len(t, slices.Collect(c.RaceShipGroups(Race_0_idx)), 4)
assert.Equal(t, uint(8), c.ShipGroup(0).Number) assert.Equal(t, uint(8), c.ShipGroup(0).Number)
assert.Equal(t, uint(2), c.ShipGroup(3).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.Equal(t, game.StateUpgrade, c.ShipGroup(3).State())
assert.ErrorContains(t, 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)) e.GenericErrorText(e.ErrShipsBusy))
} }
+4 -4
View File
@@ -105,10 +105,10 @@ func (t Tech) String() string {
} }
type ShipGroup struct { type ShipGroup struct {
Index uint `json:"index"` // FIXME: use UUID for Group Index (ordered) ID uuid.UUID `json:"id"`
OwnerID uuid.UUID `json:"ownerId"` // Race link OwnerID uuid.UUID `json:"ownerId"` // Race reference
TypeID uuid.UUID `json:"typeId"` // ShipType link TypeID uuid.UUID `json:"typeId"` // ShipType reference
FleetID *uuid.UUID `json:"fleetId,omitempty"` // Fleet link FleetID *uuid.UUID `json:"fleetId,omitempty"` // Fleet reference
Number uint `json:"number"` // Number (quantity) ships of specific ShipType Number uint `json:"number"` // Number (quantity) ships of specific ShipType
CargoType *CargoType `json:"loadType,omitempty"` // CargoType *CargoType `json:"loadType,omitempty"` //
Load Float `json:"load"` // Cargo loaded - "Масса груза" Load Float `json:"load"` // Cargo loaded - "Масса груза"
+2 -2
View File
@@ -162,7 +162,7 @@ func TestShipGroupEqual(t *testing.T) {
mat := game.CargoMaterial mat := game.CargoMaterial
cap := game.CargoCapital cap := game.CargoCapital
left := &game.ShipGroup{ left := &game.ShipGroup{
Index: 1, ID: uuid.New(),
Number: 1, Number: 1,
OwnerID: uuid.New(), OwnerID: uuid.New(),
@@ -241,7 +241,7 @@ func TestShipGroupEqual(t *testing.T) {
// non-essential properties // non-essential properties
right = *left right = *left
left.Index = 2 left.ID = uuid.New()
assert.True(t, left.Equal(right)) assert.True(t, left.Equal(right))
// dirty hack to equalize loads // dirty hack to equalize loads
+5 -3
View File
@@ -1,5 +1,7 @@
package report package report
import "github.com/google/uuid"
type ShipClass struct { type ShipClass struct {
Name string `json:"name"` Name string `json:"name"`
Drive Float `json:"drive"` Drive Float `json:"drive"`
@@ -34,9 +36,9 @@ type IncomingGroup struct {
type LocalGroup struct { type LocalGroup struct {
OtherGroup OtherGroup
Index uint `json:"index"` ID uuid.UUID `json:"id"`
State string `json:"state"` State string `json:"state"`
Fleet *string `json:"fleet"` Fleet *string `json:"fleet"`
} }
type OtherGroup struct { type OtherGroup struct {
-8
View File
@@ -1,7 +1,6 @@
package number package number
import ( import (
"cmp"
"math" "math"
) )
@@ -21,10 +20,3 @@ func fixed(num float64, precision int) float64 {
func round(num float64) int { func round(num float64) int {
return int(num + math.Copysign(0.5, num)) 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
}
-7
View File
@@ -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))
}