cmd: add/remove route
This commit is contained in:
@@ -13,3 +13,11 @@ func (g Game) ListShipGroups(ri int) iter.Seq[ShipGroup] {
|
|||||||
func (g Game) ListFleets(ri int) iter.Seq[Fleet] {
|
func (g Game) ListFleets(ri int) iter.Seq[Fleet] {
|
||||||
return g.listFleets(ri)
|
return g.listFleets(ri)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (g Game) MustPlanetByNumber(num uint) Planet {
|
||||||
|
p, err := g.PlanetByNumber(num)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ func (g *Game) sendGroupInternal(ri int, groupIndex, planetNumber, quantity uint
|
|||||||
|
|
||||||
p1, ok := PlanetByNum(g, sourcePlanet)
|
p1, ok := PlanetByNum(g, sourcePlanet)
|
||||||
if !ok {
|
if !ok {
|
||||||
return e.NewGameStateError("source planet #%d does not exists", g.ShipGroups[sgi].Destination)
|
return e.NewGameStateError("source planet #%d does not exists", sourcePlanet)
|
||||||
}
|
}
|
||||||
p2, ok := PlanetByNum(g, planetNumber)
|
p2, ok := PlanetByNum(g, planetNumber)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
|||||||
@@ -33,7 +33,8 @@ type PlanetReport struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Planet struct {
|
type Planet struct {
|
||||||
Owner uuid.UUID `json:"owner"` // FIXME: nil value when no owner
|
Owner uuid.UUID `json:"owner"` // FIXME: nil value when no owner
|
||||||
|
Route map[RouteType]uint `json:"route"`
|
||||||
PlanetReport
|
PlanetReport
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,116 @@
|
|||||||
|
package game
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"slices"
|
||||||
|
|
||||||
|
e "github.com/iliadenisov/galaxy/internal/error"
|
||||||
|
"github.com/iliadenisov/galaxy/internal/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RouteType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
RouteMaterial RouteType = "MAT" // Сырьё
|
||||||
|
RouteCapital RouteType = "CAP" // Промышленность
|
||||||
|
RouteColonist RouteType = "COL" // Колонисты
|
||||||
|
RouteEmpty RouteType = "EMP" // Пустые корабли
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
routeTypeSet map[string]RouteType = map[string]RouteType{
|
||||||
|
RouteMaterial.String(): RouteMaterial,
|
||||||
|
RouteCapital.String(): RouteCapital,
|
||||||
|
RouteColonist.String(): RouteColonist,
|
||||||
|
RouteEmpty.String(): RouteEmpty,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func (rt RouteType) Ref() *RouteType {
|
||||||
|
return &rt
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rt RouteType) String() string {
|
||||||
|
return string(rt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Game) SetRoute(raceName, loadType string, origin, destination uint) error {
|
||||||
|
ri, err := g.raceIndex(raceName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
rt, ok := routeTypeSet[loadType]
|
||||||
|
if !ok {
|
||||||
|
return e.NewCargoTypeInvalidError(loadType)
|
||||||
|
}
|
||||||
|
return g.setRouteInternal(ri, rt, origin, destination)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Game) RemoveRoute(raceName, loadType string, origin uint) error {
|
||||||
|
ri, err := g.raceIndex(raceName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
rt, ok := routeTypeSet[loadType]
|
||||||
|
if !ok {
|
||||||
|
return e.NewCargoTypeInvalidError(loadType)
|
||||||
|
}
|
||||||
|
return g.removeRouteInternal(ri, rt, origin)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Game) setRouteInternal(ri int, rt RouteType, origin, destination uint) error {
|
||||||
|
p1, ok := PlanetByNum(g, origin)
|
||||||
|
if !ok {
|
||||||
|
return e.NewEntityNotExistsError("origin planet #%d", origin)
|
||||||
|
}
|
||||||
|
if p1.Owner != g.Race[ri].ID {
|
||||||
|
return e.NewEntityNotOwnedError("planet #%d", origin)
|
||||||
|
}
|
||||||
|
p2, ok := PlanetByNum(g, destination)
|
||||||
|
if !ok {
|
||||||
|
return e.NewEntityNotExistsError("destination planet #%d", destination)
|
||||||
|
}
|
||||||
|
rangeToDestination := util.ShortDistance(g.Map.Width, g.Map.Height, p1.X, p1.Y, p2.X, p2.Y)
|
||||||
|
if rangeToDestination > g.Race[ri].FlightDistance() {
|
||||||
|
return e.NewSendUnreachableDestinationError("range=%.03f", rangeToDestination)
|
||||||
|
}
|
||||||
|
|
||||||
|
SetPlanetRoute(g, rt, origin, destination)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Game) removeRouteInternal(ri int, rt RouteType, origin uint) error {
|
||||||
|
p1, ok := PlanetByNum(g, origin)
|
||||||
|
if !ok {
|
||||||
|
return e.NewEntityNotExistsError("origin planet #%d", origin)
|
||||||
|
}
|
||||||
|
if p1.Owner != g.Race[ri].ID {
|
||||||
|
return e.NewEntityNotOwnedError("planet #%d", origin)
|
||||||
|
}
|
||||||
|
|
||||||
|
RemovePlanetRoute(g, rt, origin)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetPlanetRoute(g *Game, rt RouteType, origin, destination uint) {
|
||||||
|
pi := slices.IndexFunc(g.Map.Planet, func(p Planet) bool { return p.Number == origin })
|
||||||
|
if pi < 0 {
|
||||||
|
panic(fmt.Sprintf("SetPlanetRoute: origin planet #%d not found", origin))
|
||||||
|
}
|
||||||
|
if g.Map.Planet[pi].Route == nil {
|
||||||
|
g.Map.Planet[pi].Route = make(map[RouteType]uint)
|
||||||
|
}
|
||||||
|
g.Map.Planet[pi].Route[rt] = destination
|
||||||
|
}
|
||||||
|
|
||||||
|
func RemovePlanetRoute(g *Game, rt RouteType, origin uint) {
|
||||||
|
pi := slices.IndexFunc(g.Map.Planet, func(p Planet) bool { return p.Number == origin })
|
||||||
|
if pi < 0 {
|
||||||
|
panic(fmt.Sprintf("RemovePlanetRoute: origin planet #%d not found", origin))
|
||||||
|
}
|
||||||
|
if g.Map.Planet[pi].Route != nil {
|
||||||
|
delete(g.Map.Planet[pi].Route, rt)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
package game_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
e "github.com/iliadenisov/galaxy/internal/error"
|
||||||
|
"github.com/iliadenisov/galaxy/internal/model/game"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSetRoute(t *testing.T) {
|
||||||
|
g := newGame()
|
||||||
|
|
||||||
|
assert.NotContains(t, g.MustPlanetByNumber(0).Route, game.RouteMaterial)
|
||||||
|
assert.NotContains(t, g.MustPlanetByNumber(0).Route, game.RouteCapital)
|
||||||
|
assert.NotContains(t, g.MustPlanetByNumber(0).Route, game.RouteColonist)
|
||||||
|
assert.NotContains(t, g.MustPlanetByNumber(0).Route, game.RouteEmpty)
|
||||||
|
|
||||||
|
assert.NoError(t, g.SetRoute(Race_0.Name, "COL", 0, 2))
|
||||||
|
assert.NotContains(t, g.MustPlanetByNumber(0).Route, game.RouteMaterial)
|
||||||
|
assert.NotContains(t, g.MustPlanetByNumber(0).Route, game.RouteCapital)
|
||||||
|
assert.Contains(t, g.MustPlanetByNumber(0).Route, game.RouteColonist)
|
||||||
|
assert.NotContains(t, g.MustPlanetByNumber(0).Route, game.RouteEmpty)
|
||||||
|
|
||||||
|
assert.NoError(t, g.SetRoute(Race_0.Name, "MAT", 0, 2))
|
||||||
|
assert.Contains(t, g.MustPlanetByNumber(0).Route, game.RouteMaterial)
|
||||||
|
assert.NotContains(t, g.MustPlanetByNumber(0).Route, game.RouteCapital)
|
||||||
|
assert.Contains(t, g.MustPlanetByNumber(0).Route, game.RouteColonist)
|
||||||
|
assert.NotContains(t, g.MustPlanetByNumber(0).Route, game.RouteEmpty)
|
||||||
|
|
||||||
|
assert.NoError(t, g.SetRoute(Race_0.Name, "CAP", 0, 2))
|
||||||
|
assert.Contains(t, g.MustPlanetByNumber(0).Route, game.RouteMaterial)
|
||||||
|
assert.Contains(t, g.MustPlanetByNumber(0).Route, game.RouteCapital)
|
||||||
|
assert.Contains(t, g.MustPlanetByNumber(0).Route, game.RouteColonist)
|
||||||
|
assert.NotContains(t, g.MustPlanetByNumber(0).Route, game.RouteEmpty)
|
||||||
|
|
||||||
|
assert.NoError(t, g.SetRoute(Race_0.Name, "EMP", 0, 2))
|
||||||
|
assert.Contains(t, g.MustPlanetByNumber(0).Route, game.RouteMaterial)
|
||||||
|
assert.Contains(t, g.MustPlanetByNumber(0).Route, game.RouteCapital)
|
||||||
|
assert.Contains(t, g.MustPlanetByNumber(0).Route, game.RouteColonist)
|
||||||
|
assert.Contains(t, g.MustPlanetByNumber(0).Route, game.RouteEmpty)
|
||||||
|
|
||||||
|
assert.ErrorContains(t,
|
||||||
|
g.SetRoute("UnknownRace", "COL", 0, 2),
|
||||||
|
e.GenericErrorText(e.ErrInputUnknownRace))
|
||||||
|
assert.ErrorContains(t,
|
||||||
|
g.SetRoute(Race_0.Name, "IND", 0, 2),
|
||||||
|
e.GenericErrorText(e.ErrInputCargoTypeInvalid))
|
||||||
|
assert.ErrorContains(t,
|
||||||
|
g.SetRoute(Race_0.Name, "COL", 500, 2),
|
||||||
|
e.GenericErrorText(e.ErrInputEntityNotExists))
|
||||||
|
assert.ErrorContains(t,
|
||||||
|
g.SetRoute(Race_0.Name, "COL", 1, 2),
|
||||||
|
e.GenericErrorText(e.ErrInputEntityNotOwned))
|
||||||
|
assert.ErrorContains(t,
|
||||||
|
g.SetRoute(Race_0.Name, "COL", 0, 3),
|
||||||
|
e.GenericErrorText(e.ErrSendUnreachableDestination))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRemoveRoute(t *testing.T) {
|
||||||
|
g := newGame()
|
||||||
|
|
||||||
|
assert.NoError(t, g.SetRoute(Race_0.Name, "COL", 0, 2))
|
||||||
|
assert.NoError(t, g.SetRoute(Race_0.Name, "CAP", 0, 2))
|
||||||
|
assert.NoError(t, g.SetRoute(Race_0.Name, "EMP", 2, 0))
|
||||||
|
|
||||||
|
assert.Contains(t, g.MustPlanetByNumber(0).Route, game.RouteColonist)
|
||||||
|
assert.Contains(t, g.MustPlanetByNumber(0).Route, game.RouteCapital)
|
||||||
|
assert.Contains(t, g.MustPlanetByNumber(2).Route, game.RouteEmpty)
|
||||||
|
|
||||||
|
assert.NoError(t, g.RemoveRoute(Race_0.Name, "COL", 0))
|
||||||
|
assert.NotContains(t, g.MustPlanetByNumber(0).Route, game.RouteColonist)
|
||||||
|
assert.Contains(t, g.MustPlanetByNumber(0).Route, game.RouteCapital)
|
||||||
|
|
||||||
|
assert.NoError(t, g.RemoveRoute(Race_0.Name, "EMP", 2))
|
||||||
|
assert.NotContains(t, g.MustPlanetByNumber(2).Route, game.RouteEmpty)
|
||||||
|
|
||||||
|
assert.ErrorContains(t,
|
||||||
|
g.RemoveRoute("UnknownRace", "COL", 0),
|
||||||
|
e.GenericErrorText(e.ErrInputUnknownRace))
|
||||||
|
assert.ErrorContains(t,
|
||||||
|
g.RemoveRoute(Race_0.Name, "IND", 0),
|
||||||
|
e.GenericErrorText(e.ErrInputCargoTypeInvalid))
|
||||||
|
assert.ErrorContains(t,
|
||||||
|
g.RemoveRoute(Race_0.Name, "COL", 500),
|
||||||
|
e.GenericErrorText(e.ErrInputEntityNotExists))
|
||||||
|
assert.ErrorContains(t,
|
||||||
|
g.RemoveRoute(Race_0.Name, "COL", 1),
|
||||||
|
e.GenericErrorText(e.ErrInputEntityNotOwned))
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user