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