From 7002f3d299aa558c31cc66899b07df873f79ef54 Mon Sep 17 00:00:00 2001 From: Ilia Denisov Date: Mon, 8 Dec 2025 21:33:48 +0300 Subject: [PATCH] cmd: break group --- internal/error/generic.go | 9 +++-- internal/error/input.go | 4 +++ internal/model/game/group.go | 45 ++++++++++++++++++++++++ internal/model/game/group_test.go | 58 +++++++++++++++++++++++++++++++ 4 files changed, 113 insertions(+), 3 deletions(-) diff --git a/internal/error/generic.go b/internal/error/generic.go index eec0a66..6e64a86 100644 --- a/internal/error/generic.go +++ b/internal/error/generic.go @@ -15,9 +15,10 @@ const ( ErrDeleteSciencePlanetProduction = 5002 ErrMergeShipTypeNotEqual = 5003 ErrJoinFleetGroupNumberNotEnough = 5004 - ErrEntityInUse = 5005 - ErrShipsBusy = 5006 - ErrShipsNotOnSamePlanet = 5007 + ErrBeakGroupNumberNotEnough = 5005 + ErrEntityInUse = 5006 + ErrShipsBusy = 5007 + ErrShipsNotOnSamePlanet = 5008 ) const ( @@ -91,6 +92,8 @@ func GenericErrorText(code int) string { return "Source and target ship types are not the same" case ErrJoinFleetGroupNumberNotEnough: return "Not enough ships in the group to join a fleet" + case ErrBeakGroupNumberNotEnough: + return "Not enough ships in the group to make a separate group" case ErrShipsBusy: return "Ships currently not in orbit or free to use" case ErrShipsNotOnSamePlanet: diff --git a/internal/error/input.go b/internal/error/input.go index 7ed7286..a972c2a 100644 --- a/internal/error/input.go +++ b/internal/error/input.go @@ -76,6 +76,10 @@ func NewJoinFleetGroupNumberNotEnoughError(arg ...any) error { return newGenericError(ErrJoinFleetGroupNumberNotEnough, arg...) } +func NewBeakGroupNumberNotEnoughError(arg ...any) error { + return newGenericError(ErrBeakGroupNumberNotEnough, arg...) +} + func NewShipsBusyError(arg ...any) error { return newGenericError(ErrShipsBusy, arg...) } diff --git a/internal/model/game/group.go b/internal/model/game/group.go index f83a60c..befbb2a 100644 --- a/internal/model/game/group.go +++ b/internal/model/game/group.go @@ -122,6 +122,51 @@ func (g *Game) JoinEqualGroups(raceName string) error { return nil } +func (g *Game) BreakGroup(raceName string, groupIndex, quantity uint) error { + ri, err := g.raceIndex(raceName) + if err != nil { + return err + } + return g.breakGroupInternal(ri, groupIndex, quantity) +} + +func (g *Game) breakGroupInternal(ri int, groupIndex, quantity uint) error { + sgi := -1 + var maxIndex uint + for i, sg := range g.listIndexShipGroups(ri) { + if sgi < 0 && sg.Index == groupIndex { + sgi = i + } + if sg.Index > maxIndex { + maxIndex = sg.Index + } + } + if sgi < 0 { + return e.NewEntityNotExistsError("group #%d", groupIndex) + } + + if g.ShipGroups[sgi].State != "In_Orbit" || g.ShipGroups[sgi].Origin != nil || g.ShipGroups[sgi].Range != nil { + return e.NewShipsBusyError() + } + + if g.ShipGroups[sgi].Number < quantity { + return e.NewBeakGroupNumberNotEnoughError("%d<%d", g.ShipGroups[sgi].Number, quantity) + } + + if quantity == 0 || quantity == g.ShipGroups[sgi].Number { + g.ShipGroups[sgi].FleetID = nil + } else { + newGroup := g.ShipGroups[sgi] + newGroup.Number = quantity + g.ShipGroups[sgi].Number -= quantity + newGroup.Index = maxIndex + 1 + newGroup.FleetID = nil + g.ShipGroups = append(g.ShipGroups, newGroup) + } + + return nil +} + func (g *Game) joinEqualGroupsInternal(ri int) { shipGroups := slices.Collect(maps.Values(maps.Collect(g.listIndexShipGroups(ri)))) origin := len(shipGroups) diff --git a/internal/model/game/group_test.go b/internal/model/game/group_test.go index 7e2ba74..0ad97f3 100644 --- a/internal/model/game/group_test.go +++ b/internal/model/game/group_test.go @@ -346,3 +346,61 @@ func TestJoinEqualGroups(t *testing.T) { } } } + +func TestBreakGroup(t *testing.T) { + g := copyGame() + assert.NoError(t, g.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 13)) // group #1 (0) + assert.NoError(t, g.CreateShips(Race_0_idx, Race_0_Gunship, R0_Planet_0_num, 7)) // group #2 (1) - In_Space + g.ShipGroups[1].State = "In_Space" + + fleet := "R0_Fleet" + assert.NoError(t, g.JoinShipGroupToFleet(Race_0.Name, fleet, 1, 0)) + + assert.ErrorContains(t, + g.BreakGroup("UnknownRace", 1, 0), + e.GenericErrorText(e.ErrInputUnknownRace)) + assert.ErrorContains(t, + g.BreakGroup(Race_0.Name, 555, 0), + e.GenericErrorText(e.ErrInputEntityNotExists)) + assert.ErrorContains(t, + g.BreakGroup(Race_0.Name, 1, 17), + e.GenericErrorText(e.ErrBeakGroupNumberNotEnough)) + assert.ErrorContains(t, + g.BreakGroup(Race_0.Name, 2, 0), + e.GenericErrorText(e.ErrShipsBusy)) + + assert.Len(t, slices.Collect(g.ListShipGroups(Race_0_idx)), 2) + assert.Len(t, slices.Collect(g.ListFleets(Race_0_idx)), 1) + + // group #1 -> group #3 (5 new, 8 left) + assert.NoError(t, g.BreakGroup(Race_0.Name, 1, 5)) // group #3 (2) + assert.Len(t, slices.Collect(g.ListShipGroups(Race_0_idx)), 3) + assert.Equal(t, uint(8), g.ShipGroups[0].Number) + assert.NotNil(t, g.ShipGroups[0].FleetID) + assert.Equal(t, uint(5), g.ShipGroups[2].Number) + assert.Equal(t, uint(3), g.ShipGroups[2].Index) + assert.Nil(t, g.ShipGroups[2].FleetID) + + // group #1 -> group #4 (2 new, 6 left) + assert.NoError(t, g.BreakGroup(Race_0.Name, 1, 2)) // group #4 (3) + assert.Len(t, slices.Collect(g.ListShipGroups(Race_0_idx)), 4) + assert.Equal(t, uint(6), g.ShipGroups[0].Number) + assert.NotNil(t, g.ShipGroups[0].FleetID) + assert.Equal(t, uint(2), g.ShipGroups[3].Number) + assert.Equal(t, uint(4), g.ShipGroups[3].Index) + assert.Nil(t, g.ShipGroups[3].FleetID) + assert.NoError(t, g.JoinShipGroupToFleet(Race_0.Name, fleet, 4, 0)) + assert.NotNil(t, g.ShipGroups[3].FleetID) + + // group #1 -> MAX 6 off the fleet + assert.NoError(t, g.BreakGroup(Race_0.Name, 1, 6)) // group #1 (0) + assert.Len(t, slices.Collect(g.ListShipGroups(Race_0_idx)), 4) + assert.Equal(t, uint(6), g.ShipGroups[0].Number) + assert.Nil(t, g.ShipGroups[0].FleetID) + + // group #4 -> ALL off the fleet + assert.NoError(t, g.BreakGroup(Race_0.Name, 4, 0)) // group #1 (0) + assert.Len(t, slices.Collect(g.ListShipGroups(Race_0_idx)), 4) + assert.Equal(t, uint(2), g.ShipGroups[3].Number) + assert.Nil(t, g.ShipGroups[3].FleetID) +}