From bcab29a47f231880b24ee8347aa473c9011ad6ee Mon Sep 17 00:00:00 2001 From: Ilia Denisov Date: Wed, 1 Oct 2025 00:29:25 +0300 Subject: [PATCH] cmd: delete ship type --- pkg/error/generic.go | 14 +++++++++++-- pkg/error/input.go | 8 +++++-- pkg/error/state.go | 8 +++++++ pkg/game/cmd_ship_type.go | 16 ++++++++++++++ pkg/game/cmd_ship_type_test.go | 13 ++++++++++-- pkg/game/generator.go | 6 +++--- pkg/model/game/production.go | 12 ++++++----- pkg/model/game/ship_type.go | 38 +++++++++++++++++++++++++++++++++- 8 files changed, 100 insertions(+), 15 deletions(-) diff --git a/pkg/error/generic.go b/pkg/error/generic.go index d179436..376acf6 100644 --- a/pkg/error/generic.go +++ b/pkg/error/generic.go @@ -9,13 +9,17 @@ const ( ErrStorageFailure int = 1000 ErrGameStateInvalid int = 2000 + + ErrDeleteShipTypeExistingGroup = 5000 + ErrDeleteShipTypePlanetProduction = 5001 ) const ( ErrInputUnknownHostRace int = 3000 + iota ErrInputUnknownOpponentRace ErrInputEntityTypeNameInvalid - ErrInputEntityTypeNameExists + ErrInputEntityTypeNameDuplicate + ErrInputEntityTypeNameNotExists ErrInputShipTypeDriveValue ErrInputShipTypeWeaponsValue ErrInputShipTypeShieldsValue @@ -39,8 +43,10 @@ func GenericErrorText(code int) string { return "Opponent race name is unknown to this game" case ErrInputEntityTypeNameInvalid: return "Name has invalid length or symbols" - case ErrInputEntityTypeNameExists: + case ErrInputEntityTypeNameDuplicate: return "Name already exists" + case ErrInputEntityTypeNameNotExists: + return "Name not exists" case ErrInputShipTypeDriveValue: return "Invalid Drive value" case ErrInputShipTypeWeaponsValue: @@ -55,6 +61,10 @@ func GenericErrorText(code int) string { return "Invalid Armament or Weapons value" case ErrInputShipTypeZeroValues: return "Ship type values cannot be all zeros" + case ErrDeleteShipTypeExistingGroup: + return "Ship type exists in a Group" + case ErrDeleteShipTypePlanetProduction: + return "Ship type in production on the Planet" default: return fmt.Sprintf("Undescribed error with code %d", code) } diff --git a/pkg/error/input.go b/pkg/error/input.go index 335d4b1..c34e742 100644 --- a/pkg/error/input.go +++ b/pkg/error/input.go @@ -12,8 +12,12 @@ func NewEntityTypeNameValidationError(arg ...any) error { return newGenericError(ErrInputEntityTypeNameInvalid, arg...) } -func NewEntityTypeNameExistsError(arg ...any) error { - return newGenericError(ErrInputEntityTypeNameExists, arg...) +func NewEntityTypeNameDuplicateError(arg ...any) error { + return newGenericError(ErrInputEntityTypeNameDuplicate, arg...) +} + +func NewEntityTypeNameNotExistsError(arg ...any) error { + return newGenericError(ErrInputEntityTypeNameNotExists, arg...) } func NewShipTypeDriveValueError(arg ...any) error { diff --git a/pkg/error/state.go b/pkg/error/state.go index 845a0cb..90715b4 100644 --- a/pkg/error/state.go +++ b/pkg/error/state.go @@ -3,3 +3,11 @@ package error func NewGameStateError(arg ...any) error { return newGenericError(ErrGameStateInvalid, arg...) } + +func NewDeleteShipTypeExistingGroupError(arg ...any) error { + return newGenericError(ErrDeleteShipTypeExistingGroup, arg...) +} + +func NewDeleteShipTypePlanetProductionError(arg ...any) error { + return newGenericError(ErrDeleteShipTypePlanetProduction, arg...) +} diff --git a/pkg/game/cmd_ship_type.go b/pkg/game/cmd_ship_type.go index ecd17b6..6954471 100644 --- a/pkg/game/cmd_ship_type.go +++ b/pkg/game/cmd_ship_type.go @@ -17,3 +17,19 @@ func createShipType(r Repo, g game.Game, race, typeName string, d, w, s, c float } return r.SaveState(g) } + +func DeleteShipType(configure func(*Param), race, typeName string) (err error) { + control(configure, func(c *ctrl) { + c.execute(func(r Repo, g game.Game) { + err = deleteShipType(r, g, race, typeName) + }) + }) + return +} + +func deleteShipType(r Repo, g game.Game, race, typeName string) error { + if err := g.DeleteShipType(race, typeName); err != nil { + return err + } + return r.SaveState(g) +} diff --git a/pkg/game/cmd_ship_type_test.go b/pkg/game/cmd_ship_type_test.go index c562eb1..694109e 100644 --- a/pkg/game/cmd_ship_type_test.go +++ b/pkg/game/cmd_ship_type_test.go @@ -12,8 +12,11 @@ import ( func TestCreateShipType(t *testing.T) { race := "race_01" + typeName := "Drone" g(t, func(p func(*game.Param), g func() mg.Game) { - err := game.CreateShipType(p, race, " Drone ", 1, 0, 0, 0, 0) + err := game.DeleteShipType(p, race, typeName) + assert.ErrorContains(t, err, e.GenericErrorText(e.ErrInputEntityTypeNameNotExists)) + err = game.CreateShipType(p, race, " "+typeName+" ", 1, 0, 0, 0, 0) assert.NoError(t, err) st, err := g().ShipTypes(race) assert.NoError(t, err) @@ -24,6 +27,12 @@ func TestCreateShipType(t *testing.T) { assert.Equal(t, st[0].Shields, 0.) assert.Equal(t, st[0].Cargo, 0.) assert.Equal(t, st[0].Armament, uint(0)) + // TODO: test with existing ship group + err = game.DeleteShipType(p, race, typeName) + assert.NoError(t, err) + st, err = g().ShipTypes(race) + assert.NoError(t, err) + assert.Len(t, st, 0) }) } @@ -70,7 +79,7 @@ func TestCreateShipTypeValidation(t *testing.T) { err := game.CreateShipType(p, race, tc.name+strconv.Itoa(i), tc.d, tc.w, tc.s, tc.c, tc.a) assert.NoError(t, err) err = game.CreateShipType(p, race, tc.name+strconv.Itoa(i), tc.d, tc.w, tc.s, tc.c, tc.a) - assert.ErrorContains(t, err, e.GenericErrorText(e.ErrInputEntityTypeNameExists)) + assert.ErrorContains(t, err, e.GenericErrorText(e.ErrInputEntityTypeNameDuplicate)) } else { err := game.CreateShipType(p, race, tc.name, tc.d, tc.w, tc.s, tc.c, tc.a) assert.ErrorContains(t, err, tc.err) diff --git a/pkg/game/generator.go b/pkg/game/generator.go index 3f4f9f3..b50e14f 100644 --- a/pkg/game/generator.go +++ b/pkg/game/generator.go @@ -75,7 +75,7 @@ func buildGameOnMap(races []string, m generator.Map) (*game.Game, error) { m.HomePlanets[i].HW.Size, // HW's pop & ind = size m.HomePlanets[i].HW.Size, m.HomePlanets[i].HW.Resources, - game.ResearchDrive.AsType(""), + game.ResearchDrive.AsType(uuid.Nil), )) planetCount++ for dw := range m.HomePlanets[i].DW { @@ -89,7 +89,7 @@ func buildGameOnMap(races []string, m generator.Map) (*game.Game, error) { m.HomePlanets[i].DW[dw].Size, // DW's pop & ind = size m.HomePlanets[i].DW[dw].Size, m.HomePlanets[i].DW[dw].Resources, - game.ResearchDrive.AsType(""), + game.ResearchDrive.AsType(uuid.Nil), )) planetCount++ } @@ -111,7 +111,7 @@ func buildGameOnMap(races []string, m generator.Map) (*game.Game, error) { 0, 0, m.FreePlanets[i].Resources, - game.ProductionNone.AsType(""), + game.ProductionNone.AsType(uuid.Nil), )) planetCount++ } diff --git a/pkg/model/game/production.go b/pkg/model/game/production.go index 10bc38c..af4f6d3 100644 --- a/pkg/model/game/production.go +++ b/pkg/model/game/production.go @@ -1,5 +1,7 @@ package game +import "github.com/google/uuid" + type PlanetProduction string const ( @@ -17,15 +19,15 @@ const ( ) type ProductionType struct { - Production PlanetProduction `json:"type"` - SubjectName string `json:"subject"` // TODO: change to UUID + Production PlanetProduction `json:"type"` + SubjectID *uuid.UUID `json:"subjectId"` } -func (p PlanetProduction) AsType(subject string) ProductionType { +func (p PlanetProduction) AsType(subject uuid.UUID) ProductionType { switch p { case ResearchScience, ProductionShip: - return ProductionType{Production: p, SubjectName: subject} + return ProductionType{Production: p, SubjectID: &subject} default: - return ProductionType{Production: p} + return ProductionType{Production: p, SubjectID: nil} } } diff --git a/pkg/model/game/ship_type.go b/pkg/model/game/ship_type.go index 707d2ab..f97abc2 100644 --- a/pkg/model/game/ship_type.go +++ b/pkg/model/game/ship_type.go @@ -22,6 +22,42 @@ func (g Game) shipTypesInternal(race uuid.UUID) ([]ShipType, error) { return nil, e.NewGameStateError("ShipTypes: race %v not found", race) } +func (g Game) DeleteShipType(raceName, typeName string) error { + raceID, err := g.hostRaceID(raceName) + if err != nil { + return err + } + return g.deleteShipTypeInternal(raceID, typeName) +} + +func (g Game) deleteShipTypeInternal(race uuid.UUID, name string) error { + for r := range g.Race { + if g.Race[r].ID == race { + for st := range g.Race[r].ShipTypes { + if g.Race[r].ShipTypes[st].Name == name { + for sg := range g.Race[r].ShipGroups { + if g.Race[r].ShipGroups[sg].TypeID == g.Race[r].ShipTypes[st].ID { + return e.NewDeleteShipTypeExistingGroupError(g.Race[r].ShipGroups[sg].Number) + } + } + for pl := range g.Map.Planet { + if g.Map.Planet[pl].Owner == race && + g.Map.Planet[pl].Production.Production == ProductionShip && + g.Map.Planet[pl].Production.SubjectID != nil && + g.Race[r].ShipTypes[st].ID == *g.Map.Planet[pl].Production.SubjectID { + return e.NewDeleteShipTypePlanetProductionError(g.Map.Planet[pl].Name) + } + } + g.Race[r].ShipTypes = append(g.Race[r].ShipTypes[:st], g.Race[r].ShipTypes[st+1:]...) + return nil + } + } + return e.NewEntityTypeNameNotExistsError("ship type %w", name) + } + } + return e.NewGameStateError("DeleteShipType: race %v not found", race) +} + func (g Game) CreateShipType(raceName, typeName string, d, w, s, c float64, a int) error { if err := checkShipTypeValues(d, w, s, c, a); err != nil { return err @@ -42,7 +78,7 @@ func (g Game) createShipTypeInternal(race uuid.UUID, name string, d, w, s, c flo if g.Race[r].ID == race { for st := range g.Race[r].ShipTypes { if g.Race[r].ShipTypes[st].Name == name { - return e.NewEntityTypeNameExistsError("ship type %w", g.Race[r].ShipTypes[st].Name) + return e.NewEntityTypeNameDuplicateError("ship type %w", g.Race[r].ShipTypes[st].Name) } } id := uuid.New()