diff --git a/pkg/error/generic.go b/pkg/error/generic.go index 7abcf42..31cbb6e 100644 --- a/pkg/error/generic.go +++ b/pkg/error/generic.go @@ -20,7 +20,9 @@ const ( ErrInputUnknownOpponentRace ErrInputEntityTypeNameInvalid ErrInputEntityTypeNameDuplicate - ErrInputEntityTypeNameNotExists + ErrInputEntityNotExists + ErrInputEntityNotOwned + ErrInputPlanetNumber ErrInputDriveValue ErrInputWeaponsValue ErrInputShieldsValue @@ -47,8 +49,12 @@ func GenericErrorText(code int) string { return "Name has invalid length or symbols" case ErrInputEntityTypeNameDuplicate: return "Name already exists" - case ErrInputEntityTypeNameNotExists: - return "Name not exists" + case ErrInputEntityNotExists: + return "Entity does not exists" + case ErrInputEntityNotOwned: + return "Entity is not owned" + case ErrInputPlanetNumber: + return "Invalid Planet number" case ErrInputDriveValue: return "Invalid Drive value" case ErrInputWeaponsValue: diff --git a/pkg/error/input.go b/pkg/error/input.go index 6119a8e..7402176 100644 --- a/pkg/error/input.go +++ b/pkg/error/input.go @@ -16,8 +16,16 @@ func NewEntityTypeNameDuplicateError(arg ...any) error { return newGenericError(ErrInputEntityTypeNameDuplicate, arg...) } -func NewEntityTypeNameNotExistsError(arg ...any) error { - return newGenericError(ErrInputEntityTypeNameNotExists, arg...) +func NewEntityNotExistsError(arg ...any) error { + return newGenericError(ErrInputEntityNotExists, arg...) +} + +func NewEntityNotOwnedError(arg ...any) error { + return newGenericError(ErrInputEntityNotOwned, arg...) +} + +func NewPlanetNumberError(arg ...any) error { + return newGenericError(ErrInputPlanetNumber, arg...) } func NewDriveValueError(arg ...any) error { diff --git a/pkg/game/cmd_planet.go b/pkg/game/cmd_planet.go new file mode 100644 index 0000000..3491550 --- /dev/null +++ b/pkg/game/cmd_planet.go @@ -0,0 +1,19 @@ +package game + +import "github.com/iliadenisov/galaxy/pkg/model/game" + +func RenamePlanet(configure func(*Param), race string, number int, name string) (err error) { + control(configure, func(c *ctrl) { + c.execute(func(r Repo, g game.Game) { + err = renamePlanet(r, g, race, number, name) + }) + }) + return +} + +func renamePlanet(r Repo, g game.Game, race string, number int, name string) error { + if err := g.RenamePlanet(race, number, name); err != nil { + return err + } + return r.SaveState(g) +} diff --git a/pkg/game/cmd_planet_test.go b/pkg/game/cmd_planet_test.go new file mode 100644 index 0000000..01a2094 --- /dev/null +++ b/pkg/game/cmd_planet_test.go @@ -0,0 +1,57 @@ +package game_test + +import ( + "slices" + "testing" + + e "github.com/iliadenisov/galaxy/pkg/error" + + "github.com/google/uuid" + "github.com/iliadenisov/galaxy/pkg/game" + mg "github.com/iliadenisov/galaxy/pkg/model/game" + "github.com/stretchr/testify/assert" +) + +func TestRenamePlanet(t *testing.T) { + g(t, func(p func(*game.Param), g func() mg.Game) { + cg := g() + var number int + var owner uuid.UUID + for pl := range cg.Map.Planet { + if cg.Map.Planet[pl].Owner != uuid.Nil { + number = int(cg.Map.Planet[pl].Number) + owner = cg.Map.Planet[pl].Owner + break + } + } + var race string + for r := range cg.Race { + if cg.Race[r].ID == owner { + race = cg.Race[r].Name + break + } + } + newName := "Some-New-Name" + err := game.RenamePlanet(p, race, number, newName) + assert.NoError(t, err) + cg = g() + pi := slices.IndexFunc(cg.Map.Planet, func(pl mg.Planet) bool { return pl.Owner == owner && pl.Number == uint(number) }) + assert.GreaterOrEqual(t, pi, 0) + assert.Equal(t, "Some-New-Name", cg.Map.Planet[pi].Name) + + ri := slices.IndexFunc(cg.Race, func(r mg.Race) bool { return r.Name != race }) + assert.GreaterOrEqual(t, ri, 0) + otherRace := cg.Race[ri].Name + + err = game.RenamePlanet(p, unknownRaceName, number, newName) // TODO: test actual rip race + assert.ErrorContains(t, err, e.GenericErrorText(e.ErrInputUnknownHostRace)) + err = game.RenamePlanet(p, race, number, "") + assert.ErrorContains(t, err, e.GenericErrorText(e.ErrInputEntityTypeNameInvalid)) + err = game.RenamePlanet(p, race, -1, newName) + assert.ErrorContains(t, err, e.GenericErrorText(e.ErrInputPlanetNumber)) + err = game.RenamePlanet(p, race, 100500, newName) + assert.ErrorContains(t, err, e.GenericErrorText(e.ErrInputEntityNotExists)) + err = game.RenamePlanet(p, otherRace, number, newName) + assert.ErrorContains(t, err, e.GenericErrorText(e.ErrInputEntityNotOwned)) + }) +} diff --git a/pkg/game/cmd_science_test.go b/pkg/game/cmd_science_test.go index d88b944..5e0b310 100644 --- a/pkg/game/cmd_science_test.go +++ b/pkg/game/cmd_science_test.go @@ -15,7 +15,9 @@ func TestCreateScience(t *testing.T) { typeName := "First Step" g(t, func(p func(*game.Param), g func() mg.Game) { err := game.DeleteScience(p, race, typeName) - assert.ErrorContains(t, err, e.GenericErrorText(e.ErrInputEntityTypeNameNotExists)) + assert.ErrorContains(t, err, e.GenericErrorText(e.ErrInputEntityNotExists)) + err = game.CreateScience(p, unknownRaceName, " "+typeName+" ", 1, 0, 0, 0) // TODO: test on dead race + assert.ErrorContains(t, err, e.GenericErrorText(e.ErrInputUnknownHostRace)) err = game.CreateScience(p, race, " "+typeName+" ", 1, 0, 0, 0) assert.NoError(t, err) sc, err := g().Sciences(race) @@ -27,6 +29,8 @@ func TestCreateScience(t *testing.T) { assert.Equal(t, sc[0].Shields, 0.) assert.Equal(t, sc[0].Cargo, 0.) // TODO: test with existing ship group + err = game.DeleteScience(p, unknownRaceName, typeName) // TODO: test with actial rip race + assert.ErrorContains(t, err, e.GenericErrorText(e.ErrInputUnknownHostRace)) err = game.DeleteScience(p, race, typeName) assert.NoError(t, err) sc, err = g().Sciences(race) diff --git a/pkg/game/cmd_ship_type_test.go b/pkg/game/cmd_ship_type_test.go index 7c18b51..b6394e1 100644 --- a/pkg/game/cmd_ship_type_test.go +++ b/pkg/game/cmd_ship_type_test.go @@ -15,7 +15,9 @@ func TestCreateShipType(t *testing.T) { typeName := "Drone" g(t, func(p func(*game.Param), g func() mg.Game) { err := game.DeleteShipType(p, race, typeName) - assert.ErrorContains(t, err, e.GenericErrorText(e.ErrInputEntityTypeNameNotExists)) + assert.ErrorContains(t, err, e.GenericErrorText(e.ErrInputEntityNotExists)) + err = game.CreateShipType(p, unknownRaceName, " "+typeName+" ", 1, 0, 0, 0, 0) // TODO: test on dead race + assert.ErrorContains(t, err, e.GenericErrorText(e.ErrInputUnknownHostRace)) err = game.CreateShipType(p, race, " "+typeName+" ", 1, 0, 0, 0, 0) assert.NoError(t, err) st, err := g().ShipTypes(race) @@ -28,6 +30,8 @@ func TestCreateShipType(t *testing.T) { 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, unknownRaceName, typeName) // TODO: test on dead race + assert.ErrorContains(t, err, e.GenericErrorText(e.ErrInputUnknownHostRace)) err = game.DeleteShipType(p, race, typeName) assert.NoError(t, err) st, err = g().ShipTypes(race) diff --git a/pkg/game/command.go b/pkg/game/cmd_war_peace.go similarity index 100% rename from pkg/game/command.go rename to pkg/game/cmd_war_peace.go diff --git a/pkg/game/command_test.go b/pkg/game/cmd_war_peace_test.go similarity index 83% rename from pkg/game/command_test.go rename to pkg/game/cmd_war_peace_test.go index bccfe8d..1dfdecf 100644 --- a/pkg/game/command_test.go +++ b/pkg/game/cmd_war_peace_test.go @@ -3,6 +3,8 @@ package game_test import ( "testing" + e "github.com/iliadenisov/galaxy/pkg/error" + "github.com/iliadenisov/galaxy/pkg/game" mg "github.com/iliadenisov/galaxy/pkg/model/game" "github.com/stretchr/testify/assert" @@ -17,6 +19,11 @@ func TestDeclarePeaceAndWarSingle(t *testing.T) { assert.NoError(t, err) assert.Equal(t, mg.RelationWar, r.Relation) + r, err = g().Relation(unknownRaceName, opponentRace) // TODO: test on dead race + assert.ErrorContains(t, err, e.GenericErrorText(e.ErrInputUnknownHostRace)) + r, err = g().Relation(hostRace, unknownRaceName) // TODO: test on dead race + assert.ErrorContains(t, err, e.GenericErrorText(e.ErrInputUnknownOpponentRace)) + assert.NoError(t, game.DeclarePeace(f, hostRace, opponentRace)) r, err = g().Relation(hostRace, opponentRace) assert.NoError(t, err) diff --git a/pkg/game/controller_test.go b/pkg/game/controller_test.go index 6d22fe1..e76d074 100644 --- a/pkg/game/controller_test.go +++ b/pkg/game/controller_test.go @@ -11,7 +11,8 @@ import ( ) const ( - testRaceCount = 20 + testRaceCount = 20 + unknownRaceName = "Race_RIP" ) func raceNum(i int) string { diff --git a/pkg/model/game/planet.go b/pkg/model/game/planet.go index 9d5e63e..5e35859 100644 --- a/pkg/model/game/planet.go +++ b/pkg/model/game/planet.go @@ -4,6 +4,7 @@ import ( "math" "github.com/google/uuid" + e "github.com/iliadenisov/galaxy/pkg/error" ) type UnidentifiedPlanet struct { @@ -74,3 +75,32 @@ func (p *Planet) IncreasePopulation() { p.Population -= extraPopulation } } + +func (g Game) RenamePlanet(raceName string, planetNumber int, typeName string) error { + raceID, err := g.hostRaceID(raceName) + if err != nil { + return err + } + return g.renamePlanetInternal(raceID, planetNumber, typeName) +} + +func (g Game) renamePlanetInternal(race uuid.UUID, number int, name string) error { + n, ok := validateTypeName(name) + if !ok { + return e.NewEntityTypeNameValidationError("%q", n) + } + if number < 0 { + return e.NewPlanetNumberError(number) + } + num := uint(number) + for pl := range g.Map.Planet { + if g.Map.Planet[pl].Number == num { + if g.Map.Planet[pl].Owner != race { + return e.NewEntityNotOwnedError("planet %#d", num) + } + g.Map.Planet[pl].Name = n + return nil + } + } + return e.NewEntityNotExistsError("planet #%d", number) +} diff --git a/pkg/model/game/science.go b/pkg/model/game/science.go index 5535903..1f3dd27 100644 --- a/pkg/model/game/science.go +++ b/pkg/model/game/science.go @@ -64,7 +64,7 @@ func (g Game) deleteScienceInternal(race uuid.UUID, name string) error { return nil } } - return e.NewEntityTypeNameNotExistsError("science %w", name) + return e.NewEntityNotExistsError("science %w", name) } } return e.NewGameStateError("DeleteScience: race %v not found", race) @@ -78,10 +78,10 @@ func (g Game) CreateScience(raceName, typeName string, d, w, s, c float64) error return g.createScienceInternal(raceID, typeName, d, w, s, c) } -func (g Game) createScienceInternal(race uuid.UUID, n string, d, w, s, c float64) error { - name, ok := validateTypeName(n) +func (g Game) createScienceInternal(race uuid.UUID, name string, d, w, s, c float64) error { + n, ok := validateTypeName(name) if !ok { - return e.NewEntityTypeNameValidationError("%q", name) + return e.NewEntityTypeNameValidationError("%q", n) } if d < 0 { return e.NewDriveValueError(d) @@ -102,7 +102,7 @@ func (g Game) createScienceInternal(race uuid.UUID, n string, d, w, s, c float64 for r := range g.Race { if g.Race[r].ID == race { for sc := range g.Race[r].Sciences { - if g.Race[r].Sciences[sc].Name == name { + if g.Race[r].Sciences[sc].Name == n { return e.NewEntityTypeNameDuplicateError("science %w", g.Race[r].Sciences[sc].Name) } } @@ -110,7 +110,7 @@ func (g Game) createScienceInternal(race uuid.UUID, n string, d, w, s, c float64 g.Race[r].Sciences = append(g.Race[r].Sciences, Science{ ID: id, ScienceReport: ScienceReport{ - Name: name, + Name: n, Drive: d, Weapons: w, Shields: s, diff --git a/pkg/model/game/ship.go b/pkg/model/game/ship.go index 0367b51..7a213be 100644 --- a/pkg/model/game/ship.go +++ b/pkg/model/game/ship.go @@ -177,7 +177,7 @@ func (g Game) deleteShipTypeInternal(race uuid.UUID, name string) error { return nil } } - return e.NewEntityTypeNameNotExistsError("ship type %w", name) + return e.NewEntityNotExistsError("ship type %w", name) } } return e.NewGameStateError("DeleteShipType: race %v not found", race) @@ -191,18 +191,18 @@ func (g Game) CreateShipType(raceName, typeName string, d, w, s, c float64, a in return g.createShipTypeInternal(raceID, typeName, d, w, s, c, a) } -func (g Game) createShipTypeInternal(race uuid.UUID, n string, d, w, s, c float64, a int) error { +func (g Game) createShipTypeInternal(race uuid.UUID, name string, d, w, s, c float64, a int) error { if err := checkShipTypeValues(d, w, s, c, a); err != nil { return err } - name, ok := validateTypeName(n) + n, ok := validateTypeName(name) if !ok { - return e.NewEntityTypeNameValidationError("%q", name) + return e.NewEntityTypeNameValidationError("%q", n) } 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 { + if g.Race[r].ShipTypes[st].Name == n { return e.NewEntityTypeNameDuplicateError("ship type %w", g.Race[r].ShipTypes[st].Name) } } @@ -210,7 +210,7 @@ func (g Game) createShipTypeInternal(race uuid.UUID, n string, d, w, s, c float6 g.Race[r].ShipTypes = append(g.Race[r].ShipTypes, ShipType{ ID: id, ShipTypeReport: ShipTypeReport{ - Name: name, + Name: n, Drive: d, Weapons: w, Shields: s,