chore: refactor structure

This commit is contained in:
Ilia Denisov
2025-11-21 21:40:15 +03:00
parent 126f381b04
commit 269de2184c
72 changed files with 512 additions and 393 deletions
@@ -7,7 +7,7 @@ import (
"strings" "strings"
"testing" "testing"
"github.com/iliadenisov/galaxy/pkg/bitmap" "github.com/iliadenisov/galaxy/internal/bitmap"
) )
func TestBitVectorSize(t *testing.T) { func TestBitVectorSize(t *testing.T) {
+71
View File
@@ -0,0 +1,71 @@
package controller
import (
"fmt"
"github.com/iliadenisov/galaxy/internal/model/game"
"github.com/iliadenisov/galaxy/internal/repo"
)
type Repo interface {
// Lock must be called before any repository operations
Lock() error
// Release must be called after first and only repository operation
Release() error
// SaveTurn stores just generated new turn
SaveTurn(uint, game.Game) error
// SaveState stores current game state updated between turns
SaveState(game.Game) error
// LoadState retrieves game current state
LoadState() (game.Game, error)
}
type Ctrl struct {
param Param
Repo Repo
}
type Param struct {
StoragePath string
}
func NewController(configure func(*Param)) (*Ctrl, error) {
c := &Param{
StoragePath: ".",
}
if configure != nil {
configure(c)
}
r, err := repo.NewFileRepo(c.StoragePath)
if err != nil {
return nil, err
}
return &Ctrl{
param: *c,
Repo: r,
}, nil
}
func (c *Ctrl) ExecuteInit(consumer func(Repo)) error {
if err := c.Repo.Lock(); err != nil {
return fmt.Errorf("execute: lock failed: %s", err)
}
consumer(c.Repo)
return c.Repo.Release()
}
func (c *Ctrl) Execute(consumer func(Repo, game.Game)) error {
if err := c.Repo.Lock(); err != nil {
return fmt.Errorf("execute: lock failed: %s", err)
}
g, err := c.Repo.LoadState()
if err != nil {
return err
}
consumer(c.Repo, g)
return c.Repo.Release()
}
@@ -1,4 +1,4 @@
package game package controller
import ( import (
"fmt" "fmt"
@@ -6,11 +6,11 @@ import (
"slices" "slices"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/iliadenisov/galaxy/pkg/generator" "github.com/iliadenisov/galaxy/internal/generator"
"github.com/iliadenisov/galaxy/pkg/model/game" "github.com/iliadenisov/galaxy/internal/model/game"
) )
func newGame(r Repo, races []string) (uuid.UUID, error) { func NewGame(r Repo, races []string) (uuid.UUID, error) {
m, err := generator.Generate(func(ms *generator.MapSetting) { m, err := generator.Generate(func(ms *generator.MapSetting) {
ms.Players = uint32(len(races)) ms.Players = uint32(len(races))
}) })
@@ -1,4 +1,4 @@
package game package controller_test
import ( import (
"fmt" "fmt"
@@ -6,9 +6,10 @@ import (
"testing" "testing"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/iliadenisov/galaxy/pkg/model/game" "github.com/iliadenisov/galaxy/internal/controller"
"github.com/iliadenisov/galaxy/pkg/repo" "github.com/iliadenisov/galaxy/internal/model/game"
"github.com/iliadenisov/galaxy/pkg/util" "github.com/iliadenisov/galaxy/internal/repo"
"github.com/iliadenisov/galaxy/internal/util"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@@ -25,7 +26,7 @@ func TestNewGame(t *testing.T) {
races[i] = fmt.Sprintf("race_%02d", i) races[i] = fmt.Sprintf("race_%02d", i)
} }
assert.NoError(t, r.Lock()) assert.NoError(t, r.Lock())
gameID, err := newGame(r, races) gameID, err := controller.NewGame(r, races)
assert.NoError(t, err) assert.NoError(t, err)
assert.FileExists(t, filepath.Join(root, "state.json")) assert.FileExists(t, filepath.Join(root, "state.json"))
@@ -14,6 +14,7 @@ const (
ErrDeleteShipTypePlanetProduction = 5001 ErrDeleteShipTypePlanetProduction = 5001
ErrDeleteSciencePlanetProduction = 5002 ErrDeleteSciencePlanetProduction = 5002
ErrMergeShipTypeNotEqual = 5003 ErrMergeShipTypeNotEqual = 5003
ErrJoinFleetGroupNumberNotEnough = 5004
) )
const ( const (
@@ -83,6 +84,8 @@ func GenericErrorText(code int) string {
return "Invalid Production type" return "Invalid Production type"
case ErrMergeShipTypeNotEqual: case ErrMergeShipTypeNotEqual:
return "Source and target ship types are not the same" return "Source and target ship types are not the same"
case ErrJoinFleetGroupNumberNotEnough:
return "Not enough ships in the group to join a fleet"
default: default:
return fmt.Sprintf("Undescribed error with code %d", code) return fmt.Sprintf("Undescribed error with code %d", code)
} }
@@ -67,3 +67,7 @@ func NewProductionInvalidError(arg ...any) error {
func NewMergeShipTypeNotEqualError(arg ...any) error { func NewMergeShipTypeNotEqualError(arg ...any) error {
return newGenericError(ErrMergeShipTypeNotEqual, arg...) return newGenericError(ErrMergeShipTypeNotEqual, arg...)
} }
func NewJoinFleetGroupNumberNotEnoughError(arg ...any) error {
return newGenericError(ErrJoinFleetGroupNumberNotEnough, arg...)
}
+22
View File
@@ -0,0 +1,22 @@
package game
import (
"github.com/iliadenisov/galaxy/internal/controller"
"github.com/iliadenisov/galaxy/internal/model/game"
)
func JoinEqualGroups(configure func(*controller.Param), race string) (err error) {
control(configure, func(c *controller.Ctrl) {
c.Execute(func(r controller.Repo, g game.Game) {
err = joinEqualGroups(r, g, race)
})
})
return
}
func joinEqualGroups(r controller.Repo, g game.Game, race string) error {
if err := g.JoinEqualGroups(race); err != nil {
return err
}
return r.SaveState(g)
}
+17
View File
@@ -0,0 +1,17 @@
package game_test
import (
"testing"
"github.com/iliadenisov/galaxy/internal/controller"
"github.com/iliadenisov/galaxy/internal/game"
mg "github.com/iliadenisov/galaxy/internal/model/game"
"github.com/stretchr/testify/assert"
)
func TestJoinEqualGroups(t *testing.T) {
g(t, func(p func(*controller.Param), g func() mg.Game) {
err := game.JoinEqualGroups(p, "race_01")
assert.NoError(t, err)
})
}
+22
View File
@@ -0,0 +1,22 @@
package game
import (
"github.com/iliadenisov/galaxy/internal/controller"
"github.com/iliadenisov/galaxy/internal/model/game"
)
func RenamePlanet(configure func(*controller.Param), race string, number int, name string) (err error) {
control(configure, func(c *controller.Ctrl) {
c.Execute(func(r controller.Repo, g game.Game) {
err = renamePlanet(r, g, race, number, name)
})
})
return
}
func renamePlanet(r controller.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)
}
@@ -4,16 +4,17 @@ import (
"slices" "slices"
"testing" "testing"
e "github.com/iliadenisov/galaxy/pkg/error" "github.com/iliadenisov/galaxy/internal/controller"
e "github.com/iliadenisov/galaxy/internal/error"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/iliadenisov/galaxy/pkg/game" "github.com/iliadenisov/galaxy/internal/game"
mg "github.com/iliadenisov/galaxy/pkg/model/game" mg "github.com/iliadenisov/galaxy/internal/model/game"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
func TestRenamePlanet(t *testing.T) { func TestRenamePlanet(t *testing.T) {
g(t, func(p func(*game.Param), g func() mg.Game) { g(t, func(p func(*controller.Param), g func() mg.Game) {
cg := g() cg := g()
var number int var number int
var owner uuid.UUID var owner uuid.UUID
+22
View File
@@ -0,0 +1,22 @@
package game
import (
"github.com/iliadenisov/galaxy/internal/controller"
"github.com/iliadenisov/galaxy/internal/model/game"
)
func PlanetProduction(configure func(*controller.Param), race string, planetNumber int, prodType, subject string) (err error) {
control(configure, func(c *controller.Ctrl) {
c.Execute(func(r controller.Repo, g game.Game) {
err = planetProduction(r, g, race, planetNumber, prodType, subject)
})
})
return
}
func planetProduction(r controller.Repo, g game.Game, race string, planetNumber int, prodType, subject string) error {
if err := g.PlanetProduction(race, planetNumber, prodType, subject); err != nil {
return err
}
return r.SaveState(g)
}
+38
View File
@@ -0,0 +1,38 @@
package game
import (
"github.com/iliadenisov/galaxy/internal/controller"
"github.com/iliadenisov/galaxy/internal/model/game"
)
func CreateScience(configure func(*controller.Param), race, typeName string, drive, weapons, shields, cargo float64) (err error) {
control(configure, func(c *controller.Ctrl) {
c.Execute(func(r controller.Repo, g game.Game) {
err = createScience(r, g, race, typeName, drive, weapons, shields, cargo)
})
})
return
}
func createScience(r controller.Repo, g game.Game, race, typeName string, d, w, s, c float64) error {
if err := g.CreateScience(race, typeName, d, w, s, c); err != nil {
return err
}
return r.SaveState(g)
}
func DeleteScience(configure func(*controller.Param), race, typeName string) (err error) {
control(configure, func(c *controller.Ctrl) {
c.Execute(func(r controller.Repo, g game.Game) {
err = deleteScience(r, g, race, typeName)
})
})
return
}
func deleteScience(r controller.Repo, g game.Game, race, typeName string) error {
if err := g.DeleteScience(race, typeName); err != nil {
return err
}
return r.SaveState(g)
}
@@ -4,16 +4,18 @@ import (
"strconv" "strconv"
"testing" "testing"
e "github.com/iliadenisov/galaxy/pkg/error" "github.com/iliadenisov/galaxy/internal/controller"
"github.com/iliadenisov/galaxy/pkg/game"
mg "github.com/iliadenisov/galaxy/pkg/model/game" e "github.com/iliadenisov/galaxy/internal/error"
"github.com/iliadenisov/galaxy/internal/game"
mg "github.com/iliadenisov/galaxy/internal/model/game"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
func TestCreateScience(t *testing.T) { func TestCreateScience(t *testing.T) {
race := "race_01" race := "race_01"
typeName := "First Step" typeName := "First Step"
g(t, func(p func(*game.Param), g func() mg.Game) { g(t, func(p func(*controller.Param), g func() mg.Game) {
err := game.DeleteScience(p, race, typeName) err := game.DeleteScience(p, race, typeName)
assert.ErrorContains(t, err, e.GenericErrorText(e.ErrInputEntityNotExists)) assert.ErrorContains(t, err, e.GenericErrorText(e.ErrInputEntityNotExists))
err = game.CreateScience(p, unknownRaceName, " "+typeName+" ", 1, 0, 0, 0) // TODO: test on dead race err = game.CreateScience(p, unknownRaceName, " "+typeName+" ", 1, 0, 0, 0) // TODO: test on dead race
@@ -72,7 +74,7 @@ func TestCreateScienceValidation(t *testing.T) {
{typeName, 0, 0, 0, -1, e.GenericErrorText(e.ErrInputCargoValue)}, {typeName, 0, 0, 0, -1, e.GenericErrorText(e.ErrInputCargoValue)},
{typeName, 0, 1, 1, -1, e.GenericErrorText(e.ErrInputCargoValue)}, {typeName, 0, 1, 1, -1, e.GenericErrorText(e.ErrInputCargoValue)},
} }
g(t, func(p func(*game.Param), g func() mg.Game) { g(t, func(p func(*controller.Param), g func() mg.Game) {
for i, tc := range table { for i, tc := range table {
if tc.err == "" { if tc.err == "" {
err := game.CreateScience(p, race, tc.name+strconv.Itoa(i), tc.d, tc.w, tc.s, tc.c) err := game.CreateScience(p, race, tc.name+strconv.Itoa(i), tc.d, tc.w, tc.s, tc.c)
+54
View File
@@ -0,0 +1,54 @@
package game
import (
"github.com/iliadenisov/galaxy/internal/controller"
"github.com/iliadenisov/galaxy/internal/model/game"
)
func CreateShipType(configure func(*controller.Param), race, typeName string, drive, weapons, shields, cargo float64, armament int) (err error) {
control(configure, func(c *controller.Ctrl) {
c.Execute(func(r controller.Repo, g game.Game) {
err = createShipType(r, g, race, typeName, drive, weapons, shields, cargo, armament)
})
})
return
}
func createShipType(r controller.Repo, g game.Game, race, typeName string, d, w, s, c float64, a int) error {
if err := g.CreateShipType(race, typeName, d, w, s, c, a); err != nil {
return err
}
return r.SaveState(g)
}
func MergeShipType(configure func(*controller.Param), race, source, target string) (err error) {
control(configure, func(c *controller.Ctrl) {
c.Execute(func(r controller.Repo, g game.Game) {
err = mergeShipType(r, g, race, source, target)
})
})
return
}
func mergeShipType(r controller.Repo, g game.Game, race, source, target string) error {
if err := g.MergeShipType(race, source, target); err != nil {
return err
}
return r.SaveState(g)
}
func DeleteShipType(configure func(*controller.Param), race, typeName string) (err error) {
control(configure, func(c *controller.Ctrl) {
c.Execute(func(r controller.Repo, g game.Game) {
err = deleteShipType(r, g, race, typeName)
})
})
return
}
func deleteShipType(r controller.Repo, g game.Game, race, typeName string) error {
if err := g.DeleteShipType(race, typeName); err != nil {
return err
}
return r.SaveState(g)
}
@@ -4,16 +4,18 @@ import (
"strconv" "strconv"
"testing" "testing"
e "github.com/iliadenisov/galaxy/pkg/error" "github.com/iliadenisov/galaxy/internal/controller"
"github.com/iliadenisov/galaxy/pkg/game"
mg "github.com/iliadenisov/galaxy/pkg/model/game" e "github.com/iliadenisov/galaxy/internal/error"
"github.com/iliadenisov/galaxy/internal/game"
mg "github.com/iliadenisov/galaxy/internal/model/game"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
func TestCreateShipType(t *testing.T) { func TestCreateShipType(t *testing.T) {
race := "race_01" race := "race_01"
typeName := "Drone" typeName := "Drone"
g(t, func(p func(*game.Param), g func() mg.Game) { g(t, func(p func(*controller.Param), g func() mg.Game) {
err := game.DeleteShipType(p, race, typeName) err := game.DeleteShipType(p, race, typeName)
assert.ErrorContains(t, err, e.GenericErrorText(e.ErrInputEntityNotExists)) assert.ErrorContains(t, err, e.GenericErrorText(e.ErrInputEntityNotExists))
err = game.CreateShipType(p, unknownRaceName, " "+typeName+" ", 1, 0, 0, 0, 0) // TODO: test on dead race err = game.CreateShipType(p, unknownRaceName, " "+typeName+" ", 1, 0, 0, 0, 0) // TODO: test on dead race
@@ -77,7 +79,7 @@ func TestCreateShipTypeValidation(t *testing.T) {
{typeName, 0, 1, 0, 0, 0, e.GenericErrorText(e.ErrInputShipTypeWeaponsAndArmamentValue)}, {typeName, 0, 1, 0, 0, 0, e.GenericErrorText(e.ErrInputShipTypeWeaponsAndArmamentValue)},
{typeName, 0, 0, 0, 0, 1, e.GenericErrorText(e.ErrInputShipTypeWeaponsAndArmamentValue)}, {typeName, 0, 0, 0, 0, 1, e.GenericErrorText(e.ErrInputShipTypeWeaponsAndArmamentValue)},
} }
g(t, func(p func(*game.Param), g func() mg.Game) { g(t, func(p func(*controller.Param), g func() mg.Game) {
for i, tc := range table { for i, tc := range table {
if tc.err == "" { if tc.err == "" {
err := game.CreateShipType(p, race, tc.name+strconv.Itoa(i), tc.d, tc.w, tc.s, tc.c, tc.a) err := game.CreateShipType(p, race, tc.name+strconv.Itoa(i), tc.d, tc.w, tc.s, tc.c, tc.a)
@@ -94,7 +96,7 @@ func TestCreateShipTypeValidation(t *testing.T) {
func TestMergeShipType(t *testing.T) { func TestMergeShipType(t *testing.T) {
race := "race_01" race := "race_01"
g(t, func(p func(*game.Param), g func() mg.Game) { g(t, func(p func(*controller.Param), g func() mg.Game) {
err := game.CreateShipType(p, race, "Drone", 1, 0, 0, 0, 0) err := game.CreateShipType(p, race, "Drone", 1, 0, 0, 0, 0)
assert.NoError(t, err) assert.NoError(t, err)
err = game.CreateShipType(p, race, "Spy", 1, 0, 0, 0, 0) err = game.CreateShipType(p, race, "Spy", 1, 0, 0, 0, 0)
+27
View File
@@ -0,0 +1,27 @@
package game
import (
"github.com/iliadenisov/galaxy/internal/controller"
"github.com/iliadenisov/galaxy/internal/model/game"
)
func DeclareWar(configure func(*controller.Param), from, to string) (err error) {
control(configure, func(c *controller.Ctrl) {
c.Execute(func(r controller.Repo, g game.Game) { err = updateRelation(r, g, from, to, game.RelationWar) })
})
return
}
func DeclarePeace(configure func(*controller.Param), from, to string) (err error) {
control(configure, func(c *controller.Ctrl) {
c.Execute(func(r controller.Repo, g game.Game) { err = updateRelation(r, g, from, to, game.RelationPeace) })
})
return
}
func updateRelation(r controller.Repo, g game.Game, hostRace, opponentRace string, rel game.Relation) error {
if err := g.UpdateRelation(hostRace, opponentRace, rel); err != nil {
return err
}
return r.SaveState(g)
}
@@ -3,15 +3,16 @@ package game_test
import ( import (
"testing" "testing"
e "github.com/iliadenisov/galaxy/pkg/error" "github.com/iliadenisov/galaxy/internal/controller"
e "github.com/iliadenisov/galaxy/internal/error"
"github.com/iliadenisov/galaxy/pkg/game" "github.com/iliadenisov/galaxy/internal/game"
mg "github.com/iliadenisov/galaxy/pkg/model/game" mg "github.com/iliadenisov/galaxy/internal/model/game"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
func TestDeclarePeaceAndWarSingle(t *testing.T) { func TestDeclarePeaceAndWarSingle(t *testing.T) {
g(t, func(f func(*game.Param), g func() mg.Game) { g(t, func(f func(*controller.Param), g func() mg.Game) {
hostRace := "race_05" hostRace := "race_05"
opponentRace := "race_01" opponentRace := "race_01"
@@ -37,7 +38,7 @@ func TestDeclarePeaceAndWarSingle(t *testing.T) {
} }
func TestDeclarePeaceAndWarAll(t *testing.T) { func TestDeclarePeaceAndWarAll(t *testing.T) {
g(t, func(f func(*game.Param), g func() mg.Game) { g(t, func(f func(*controller.Param), g func() mg.Game) {
hostRace := "race_07" hostRace := "race_07"
for i := range testRaceCount { for i := range testRaceCount {
+28
View File
@@ -0,0 +1,28 @@
package game
import (
"github.com/google/uuid"
"github.com/iliadenisov/galaxy/internal/controller"
"github.com/iliadenisov/galaxy/internal/model/game"
)
func LoadState(configure func(*controller.Param)) (g game.Game, err error) {
control(configure, func(c *controller.Ctrl) { c.ExecuteInit(func(r controller.Repo) { g, err = c.Repo.LoadState() }) })
return
}
func GenerateGame(configure func(*controller.Param), races []string) (gameID uuid.UUID, err error) {
control(configure, func(c *controller.Ctrl) {
c.ExecuteInit(func(r controller.Repo) { gameID, err = controller.NewGame(r, races) })
})
return
}
func control(configure func(*controller.Param), consumer func(*controller.Ctrl)) error {
c, err := controller.NewController(configure)
if err != nil {
return err
}
consumer(c)
return nil
}
@@ -4,9 +4,11 @@ import (
"fmt" "fmt"
"testing" "testing"
"github.com/iliadenisov/galaxy/pkg/game" "github.com/iliadenisov/galaxy/internal/controller"
mg "github.com/iliadenisov/galaxy/pkg/model/game"
"github.com/iliadenisov/galaxy/pkg/util" "github.com/iliadenisov/galaxy/internal/game"
mg "github.com/iliadenisov/galaxy/internal/model/game"
"github.com/iliadenisov/galaxy/internal/util"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@@ -20,21 +22,21 @@ func raceNum(i int) string {
} }
func TestComposeGame(t *testing.T) { func TestComposeGame(t *testing.T) {
g(t, func(p func(*game.Param), g func() mg.Game) { g(t, func(p func(*controller.Param), g func() mg.Game) {
_, err := game.GenerateGame(p, []string{"r1", "r2"}) _, err := game.GenerateGame(p, []string{"r1", "r2"})
assert.Error(t, err) assert.Error(t, err)
assert.ErrorContains(t, err, "state for turn 0 already saved") assert.ErrorContains(t, err, "state for turn 0 already saved")
}) })
} }
func g(t *testing.T, f func(p func(*game.Param), g func() mg.Game)) { func g(t *testing.T, f func(p func(*controller.Param), g func() mg.Game)) {
root, cleanup := util.CreateWorkDir(t) root, cleanup := util.CreateWorkDir(t)
defer cleanup() defer cleanup()
races := make([]string, testRaceCount) races := make([]string, testRaceCount)
for i := range testRaceCount { for i := range testRaceCount {
races[i] = raceNum(i) races[i] = raceNum(i)
} }
p := func(p *game.Param) { p.StoragePath = root } p := func(p *controller.Param) { p.StoragePath = root }
_, err := game.GenerateGame(p, races) _, err := game.GenerateGame(p, races)
if err != nil { if err != nil {
assert.FailNow(t, "g: ComposeGame", err) assert.FailNow(t, "g: ComposeGame", err)
@@ -4,7 +4,7 @@ import (
"fmt" "fmt"
"testing" "testing"
"github.com/iliadenisov/galaxy/pkg/generator" "github.com/iliadenisov/galaxy/internal/generator"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@@ -5,7 +5,7 @@ import (
"math" "math"
"math/rand" "math/rand"
"github.com/iliadenisov/galaxy/pkg/generator/plotter" "github.com/iliadenisov/galaxy/internal/generator/plotter"
) )
type Map struct { type Map struct {
@@ -4,7 +4,7 @@ import (
"fmt" "fmt"
"testing" "testing"
"github.com/iliadenisov/galaxy/pkg/number" "github.com/iliadenisov/galaxy/internal/number"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@@ -4,7 +4,7 @@ import (
"regexp" "regexp"
"testing" "testing"
g "github.com/iliadenisov/galaxy/pkg/generator" g "github.com/iliadenisov/galaxy/internal/generator"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@@ -6,7 +6,7 @@ import (
"math" "math"
"math/rand" "math/rand"
"github.com/iliadenisov/galaxy/pkg/bitmap" "github.com/iliadenisov/galaxy/internal/bitmap"
) )
type Plotter struct { type Plotter struct {
@@ -3,7 +3,7 @@ package plotter_test
import ( import (
"testing" "testing"
"github.com/iliadenisov/galaxy/pkg/generator/plotter" "github.com/iliadenisov/galaxy/internal/generator/plotter"
) )
func TestNewPlotter(t *testing.T) { func TestNewPlotter(t *testing.T) {
+102
View File
@@ -0,0 +1,102 @@
package game
import (
"math"
"slices"
"github.com/google/uuid"
e "github.com/iliadenisov/galaxy/internal/error"
)
type Fleet struct {
ID uuid.UUID `json:"id"`
OwnerID uuid.UUID `json:"ownerId"`
Name string `json:"name"`
}
// TODO: Hello! Wanna know fleet's speed? Good. Implement & test this func first.
func (g Game) FleetSpeed(fl Fleet) float64 {
result := math.MaxFloat64
for sg := range g.ShipGroups {
if g.ShipGroups[sg].FleetID == nil || *g.ShipGroups[sg].FleetID != fl.ID {
continue
}
st := g.mustShipType(g.ShipGroups[sg].TypeID)
typeSpeed := g.ShipGroups[sg].Speed(st)
if typeSpeed < result {
result = typeSpeed
}
}
return result
}
func (g Game) JoinShipGroupToFleet(raceName, fleetName string, group, count uint) error {
ri, err := g.raceIndex(raceName)
if err != nil {
return err
}
return g.joinShipGroupToFleetInternal(ri, fleetName, group, count)
}
func (g Game) joinShipGroupToFleetInternal(ri int, fleetName string, group, count uint) (err error) {
name, ok := validateTypeName(fleetName)
if !ok {
return e.NewEntityTypeNameValidationError("%q", name)
}
sgi := -1
var maxIndex uint
for i, sg := range g.listShipGroups(ri) {
if sgi < 0 && sg.Index == group {
sgi = i
}
if sg.Index > maxIndex {
maxIndex = sg.Index
}
}
if sgi < 0 {
return e.NewEntityNotExistsError("group #%d", group)
}
if g.ShipGroups[sgi].Number < count {
return e.NewJoinFleetGroupNumberNotEnoughError("%d<%d", g.ShipGroups[sgi].Number, count)
}
fi := g.fleetIndex(ri, name)
if fi < 0 {
fi, err = g.createFleet(ri, name)
if err != nil {
return err
}
}
if g.ShipGroups[sgi].Number != count && count > 0 {
newGroup := g.ShipGroups[sgi]
newGroup.Number -= count
g.ShipGroups[sgi].Number = count
newGroup.Index = maxIndex + 1
g.ShipGroups = append(g.ShipGroups, newGroup)
}
g.ShipGroups[sgi].FleetID = &g.Fleets[fi].ID
return nil
}
func (g Game) fleetIndex(ri int, name string) int {
return slices.IndexFunc(g.Fleets, func(f Fleet) bool { return f.OwnerID == g.Race[ri].ID && f.Name == name })
}
func (g Game) createFleet(ri int, name string) (int, error) {
n, ok := validateTypeName(name)
if !ok {
return 0, e.NewEntityTypeNameValidationError("%q", n)
}
if fl := g.fleetIndex(ri, n); fl >= 0 {
return 0, e.NewEntityTypeNameDuplicateError("fleet %w", g.Fleets[fl].Name)
}
g.Fleets = append(g.Fleets, Fleet{
ID: uuid.New(),
OwnerID: g.Race[ri].ID,
Name: n,
})
return len(g.Fleets) - 1, nil
}
@@ -6,7 +6,7 @@ import (
"strings" "strings"
"github.com/google/uuid" "github.com/google/uuid"
e "github.com/iliadenisov/galaxy/pkg/error" e "github.com/iliadenisov/galaxy/internal/error"
) )
type Game struct { type Game struct {
@@ -6,6 +6,6 @@ func (g *Game) CreateShips(ri int, shipTypeName string, planetNumber int, quanti
return g.createShips(ri, shipTypeName, planetNumber, quantity) return g.createShips(ri, shipTypeName, planetNumber, quantity)
} }
func (g Game) ListShipGroups(ri int) iter.Seq[ShipGroup] { func (g Game) ListShipGroups(ri int) iter.Seq2[int, ShipGroup] {
return g.listShipGroups(ri) return g.listShipGroups(ri)
} }
@@ -2,12 +2,13 @@ package game
import ( import (
"iter" "iter"
"maps"
"math" "math"
"slices" "slices"
"github.com/google/uuid" "github.com/google/uuid"
e "github.com/iliadenisov/galaxy/pkg/error" e "github.com/iliadenisov/galaxy/internal/error"
"github.com/iliadenisov/galaxy/pkg/number" "github.com/iliadenisov/galaxy/internal/number"
) )
type CargoType string type CargoType string
@@ -120,7 +121,7 @@ func (g *Game) JoinEqualGroups(raceName string) error {
} }
func (g *Game) joinEqualGroupsInternal(ri int) { func (g *Game) joinEqualGroupsInternal(ri int) {
shipGroups := slices.Collect(g.listShipGroups(ri)) shipGroups := slices.Collect(maps.Values(maps.Collect(g.listShipGroups(ri))))
origin := len(shipGroups) origin := len(shipGroups)
if origin < 2 { if origin < 2 {
return return
@@ -155,7 +156,7 @@ func (g *Game) createShips(ri int, shipTypeName string, planetNumber int, quanti
} }
var maxIndex uint var maxIndex uint
for sg := range g.listShipGroups(ri) { for _, sg := range g.listShipGroups(ri) {
if sg.Index > maxIndex { if sg.Index > maxIndex {
maxIndex = sg.Index maxIndex = sg.Index
} }
@@ -175,11 +176,11 @@ func (g *Game) createShips(ri int, shipTypeName string, planetNumber int, quanti
return nil return nil
} }
func (g Game) listShipGroups(ri int) iter.Seq[ShipGroup] { func (g Game) listShipGroups(ri int) iter.Seq2[int, ShipGroup] {
return func(yield func(ShipGroup) bool) { return func(yield func(int, ShipGroup) bool) {
for sg := range g.ShipGroups { for sg := range g.ShipGroups {
if g.ShipGroups[sg].OwnerID == g.Race[ri].ID { if g.ShipGroups[sg].OwnerID == g.Race[ri].ID {
if !yield(g.ShipGroups[sg]) { if !yield(sg, g.ShipGroups[sg]) {
return return
} }
} }
@@ -1,14 +1,15 @@
package game_test package game_test
import ( import (
"iter"
"math/rand/v2" "math/rand/v2"
"slices" "slices"
"testing" "testing"
"github.com/google/uuid" "github.com/google/uuid"
e "github.com/iliadenisov/galaxy/pkg/error" "github.com/iliadenisov/galaxy/internal/controller"
gg "github.com/iliadenisov/galaxy/pkg/game" e "github.com/iliadenisov/galaxy/internal/error"
"github.com/iliadenisov/galaxy/pkg/model/game" "github.com/iliadenisov/galaxy/internal/model/game"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@@ -296,9 +297,9 @@ func TestJoinEqualGroups(t *testing.T) {
Height: 10, Height: 10,
Planet: make([]game.Planet, 3), Planet: make([]game.Planet, 3),
} }
g.Map.Planet[0] = gg.NewPlanet(0, "Planet_0", g.Race[0].ID, 0, 0, 100, 0, 0, 0, game.ProductionNone.AsType(uuid.Nil)) g.Map.Planet[0] = controller.NewPlanet(0, "Planet_0", g.Race[0].ID, 0, 0, 100, 0, 0, 0, game.ProductionNone.AsType(uuid.Nil))
g.Map.Planet[1] = gg.NewPlanet(1, "Planet_1", g.Race[1].ID, 1, 1, 100, 0, 0, 0, game.ProductionNone.AsType(uuid.Nil)) g.Map.Planet[1] = controller.NewPlanet(1, "Planet_1", g.Race[1].ID, 1, 1, 100, 0, 0, 0, game.ProductionNone.AsType(uuid.Nil))
g.Map.Planet[2] = gg.NewPlanet(1, "Planet_2", g.Race[0].ID, 2, 2, 100, 0, 0, 0, game.ProductionNone.AsType(uuid.Nil)) g.Map.Planet[2] = controller.NewPlanet(1, "Planet_2", g.Race[0].ID, 2, 2, 100, 0, 0, 0, game.ProductionNone.AsType(uuid.Nil))
err := g.CreateShipType("Race_0", "R0_Gunship", 60, 30, 100, 0, 3) err := g.CreateShipType("Race_0", "R0_Gunship", 60, 30, 100, 0, 3)
assert.NoError(t, err) assert.NoError(t, err)
@@ -317,48 +318,48 @@ func TestJoinEqualGroups(t *testing.T) {
err = g.CreateShips(raceIdx, "R0_Freighter", 0, 1) // 1 -> 2 err = g.CreateShips(raceIdx, "R0_Freighter", 0, 1) // 1 -> 2
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, slices.Collect(g.ListShipGroups(raceIdx)), 1) assert.Len(t, collectGroups(g.ListShipGroups(raceIdx)), 1)
err = g.CreateShips(1, "R1_Freighter", 1, 1) err = g.CreateShips(1, "R1_Freighter", 1, 1)
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, slices.Collect(g.ListShipGroups(1)), 1) assert.Len(t, collectGroups(g.ListShipGroups(1)), 1)
err = g.CreateShips(raceIdx, "R0_Freighter", 0, 6) // (2) err = g.CreateShips(raceIdx, "R0_Freighter", 0, 6) // (2)
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, slices.Collect(g.ListShipGroups(raceIdx)), 2) assert.Len(t, collectGroups(g.ListShipGroups(raceIdx)), 2)
err = g.CreateShips(raceIdx, "R0_Gunship", 0, 2) // (3) err = g.CreateShips(raceIdx, "R0_Gunship", 0, 2) // (3)
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, slices.Collect(g.ListShipGroups(raceIdx)), 3) assert.Len(t, collectGroups(g.ListShipGroups(raceIdx)), 3)
err = g.CreateShips(1, "R1_Gunship", 1, 1) err = g.CreateShips(1, "R1_Gunship", 1, 1)
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, slices.Collect(g.ListShipGroups(1)), 2) assert.Len(t, collectGroups(g.ListShipGroups(1)), 2)
g.Race[raceIdx].Drive = 1.5 g.Race[raceIdx].Drive = 1.5
err = g.CreateShips(raceIdx, "R0_Gunship", 0, 9) // 4 -> 6 err = g.CreateShips(raceIdx, "R0_Gunship", 0, 9) // 4 -> 6
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, slices.Collect(g.ListShipGroups(raceIdx)), 4) assert.Len(t, collectGroups(g.ListShipGroups(raceIdx)), 4)
err = g.CreateShips(raceIdx, "R0_Freighter", 0, 7) // 5 -> 7 err = g.CreateShips(raceIdx, "R0_Freighter", 0, 7) // 5 -> 7
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, slices.Collect(g.ListShipGroups(raceIdx)), 5) assert.Len(t, collectGroups(g.ListShipGroups(raceIdx)), 5)
err = g.CreateShips(raceIdx, "R0_Gunship", 0, 4) // (6) err = g.CreateShips(raceIdx, "R0_Gunship", 0, 4) // (6)
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, slices.Collect(g.ListShipGroups(raceIdx)), 6) assert.Len(t, collectGroups(g.ListShipGroups(raceIdx)), 6)
err = g.CreateShips(raceIdx, "R0_Freighter", 0, 4) // (7) err = g.CreateShips(raceIdx, "R0_Freighter", 0, 4) // (7)
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, slices.Collect(g.ListShipGroups(raceIdx)), 7) assert.Len(t, collectGroups(g.ListShipGroups(raceIdx)), 7)
g.Race[1].Shields = 2.0 g.Race[1].Shields = 2.0
err = g.CreateShips(1, "R1_Freighter", 1, 1) err = g.CreateShips(1, "R1_Freighter", 1, 1)
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, slices.Collect(g.ListShipGroups(1)), 3) assert.Len(t, collectGroups(g.ListShipGroups(1)), 3)
err = g.JoinEqualGroups("Race_0") err = g.JoinEqualGroups("Race_0")
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, slices.Collect(g.ListShipGroups(1)), 3) assert.Len(t, collectGroups(g.ListShipGroups(1)), 3)
assert.Len(t, slices.Collect(g.ListShipGroups(raceIdx)), 4) assert.Len(t, collectGroups(g.ListShipGroups(raceIdx)), 4)
shipTypeID := func(ri int, name string) uuid.UUID { shipTypeID := func(ri int, name string) uuid.UUID {
st := slices.IndexFunc(g.Race[ri].ShipTypes, func(v game.ShipType) bool { return v.Name == name }) st := slices.IndexFunc(g.Race[ri].ShipTypes, func(v game.ShipType) bool { return v.Name == name })
@@ -369,7 +370,7 @@ func TestJoinEqualGroups(t *testing.T) {
return g.Race[ri].ShipTypes[st].ID return g.Race[ri].ShipTypes[st].ID
} }
for sg := range g.ListShipGroups(raceIdx) { for _, sg := range g.ListShipGroups(raceIdx) {
switch { switch {
case sg.TypeID == shipTypeID(raceIdx, "R0_Freighter") && sg.Drive == 1.1: case sg.TypeID == shipTypeID(raceIdx, "R0_Freighter") && sg.Drive == 1.1:
assert.Equal(t, uint(7), sg.Number) assert.Equal(t, uint(7), sg.Number)
@@ -388,3 +389,11 @@ func TestJoinEqualGroups(t *testing.T) {
} }
} }
} }
func collectGroups(i iter.Seq2[int, game.ShipGroup]) []game.ShipGroup {
result := make([]game.ShipGroup, 0)
for _, sg := range i {
result = append(result, sg)
}
return result
}
@@ -5,7 +5,7 @@ import (
"slices" "slices"
"github.com/google/uuid" "github.com/google/uuid"
e "github.com/iliadenisov/galaxy/pkg/error" e "github.com/iliadenisov/galaxy/internal/error"
) )
type UnidentifiedPlanet struct { type UnidentifiedPlanet struct {
@@ -4,7 +4,7 @@ import (
"slices" "slices"
"github.com/google/uuid" "github.com/google/uuid"
e "github.com/iliadenisov/galaxy/pkg/error" e "github.com/iliadenisov/galaxy/internal/error"
) )
type PlanetProduction string type PlanetProduction string
@@ -4,7 +4,7 @@ import (
"slices" "slices"
"github.com/google/uuid" "github.com/google/uuid"
e "github.com/iliadenisov/galaxy/pkg/error" e "github.com/iliadenisov/galaxy/internal/error"
) )
type Science struct { type Science struct {
@@ -74,6 +74,9 @@ func (g Game) createScienceInternal(ri int, name string, d, w, s, c float64) err
if !ok { if !ok {
return e.NewEntityTypeNameValidationError("%q", n) return e.NewEntityTypeNameValidationError("%q", n)
} }
if sc := slices.IndexFunc(g.Race[ri].Sciences, func(s Science) bool { return s.Name == n }); sc >= 0 {
return e.NewEntityTypeNameDuplicateError("science %w", g.Race[ri].Sciences[sc].Name)
}
if d < 0 { if d < 0 {
return e.NewDriveValueError(d) return e.NewDriveValueError(d)
} }
@@ -90,9 +93,6 @@ func (g Game) createScienceInternal(ri int, name string, d, w, s, c float64) err
if sum != 1 { if sum != 1 {
return e.NewScienceSumValuesError("D=%f W=%f S=%f C=%f sum=%f", d, w, s, c, sum) return e.NewScienceSumValuesError("D=%f W=%f S=%f C=%f sum=%f", d, w, s, c, sum)
} }
if sc := slices.IndexFunc(g.Race[ri].Sciences, func(s Science) bool { return s.Name == n }); sc >= 0 {
return e.NewEntityTypeNameDuplicateError("science %w", g.Race[ri].Sciences[sc].Name)
}
g.Race[ri].Sciences = append(g.Race[ri].Sciences, Science{ g.Race[ri].Sciences = append(g.Race[ri].Sciences, Science{
ID: uuid.New(), ID: uuid.New(),
ScienceReport: ScienceReport{ ScienceReport: ScienceReport{
@@ -2,11 +2,10 @@ package game
import ( import (
"fmt" "fmt"
"math"
"slices" "slices"
"github.com/google/uuid" "github.com/google/uuid"
e "github.com/iliadenisov/galaxy/pkg/error" e "github.com/iliadenisov/galaxy/internal/error"
) )
type ShipTypeReport struct { type ShipTypeReport struct {
@@ -28,11 +27,6 @@ type ShipTypeReportForeign struct {
ShipTypeReport ShipTypeReport
} }
type Fleet struct {
OwnerID uuid.UUID `json:"ownerId"`
ShipGroups []ShipGroup `json:"group"`
}
func (st ShipType) Equal(o ShipType) bool { func (st ShipType) Equal(o ShipType) bool {
return st.Drive == o.Drive && return st.Drive == o.Drive &&
st.Weapons == o.Weapons && st.Weapons == o.Weapons &&
@@ -60,18 +54,6 @@ func (st ShipType) ProductionCost() (mat float64, pop float64) {
return return
} }
// TODO: test this
func (g Game) FleetSpeed(fl *Fleet) float64 {
result := math.MaxFloat64
for _, sg := range fl.ShipGroups {
st := g.mustShipType(sg.TypeID)
if sg.Speed(st) < result {
result = sg.Speed(st)
}
}
return result
}
func (g Game) mustShipType(id uuid.UUID) *ShipType { func (g Game) mustShipType(id uuid.UUID) *ShipType {
for ri := range g.Race { for ri := range g.Race {
if st := slices.IndexFunc(g.Race[ri].ShipTypes, func(st ShipType) bool { return st.ID == id }); st >= 0 { if st := slices.IndexFunc(g.Race[ri].ShipTypes, func(st ShipType) bool { return st.ID == id }); st >= 0 {
@@ -3,7 +3,7 @@ package game_test
import ( import (
"testing" "testing"
"github.com/iliadenisov/galaxy/pkg/model/game" "github.com/iliadenisov/galaxy/internal/model/game"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@@ -5,7 +5,7 @@ import (
"path/filepath" "path/filepath"
"testing" "testing"
"github.com/iliadenisov/galaxy/pkg/util" "github.com/iliadenisov/galaxy/internal/util"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@@ -6,7 +6,7 @@ import (
"testing" "testing"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/iliadenisov/galaxy/pkg/util" "github.com/iliadenisov/galaxy/internal/util"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
+1 -1
View File
@@ -11,7 +11,7 @@ package repo
import ( import (
"fmt" "fmt"
"github.com/iliadenisov/galaxy/pkg/model/game" "github.com/iliadenisov/galaxy/internal/model/game"
) )
const ( const (
+2 -2
View File
@@ -4,8 +4,8 @@ import (
"encoding" "encoding"
"errors" "errors"
e "github.com/iliadenisov/galaxy/pkg/error" e "github.com/iliadenisov/galaxy/internal/error"
"github.com/iliadenisov/galaxy/pkg/repo/fs" "github.com/iliadenisov/galaxy/internal/repo/fs"
) )
func NewStorageError(err error) error { func NewStorageError(err error) error {
-19
View File
@@ -1,19 +0,0 @@
package game
import "github.com/iliadenisov/galaxy/pkg/model/game"
func JoinEqualGroups(configure func(*Param), race string) (err error) {
control(configure, func(c *ctrl) {
c.execute(func(r Repo, g game.Game) {
err = joinEqualGroups(r, g, race)
})
})
return
}
func joinEqualGroups(r Repo, g game.Game, race string) error {
if err := g.JoinEqualGroups(race); err != nil {
return err
}
return r.SaveState(g)
}
-16
View File
@@ -1,16 +0,0 @@
package game_test
import (
"testing"
"github.com/iliadenisov/galaxy/pkg/game"
mg "github.com/iliadenisov/galaxy/pkg/model/game"
"github.com/stretchr/testify/assert"
)
func TestJoinEqualGroups(t *testing.T) {
g(t, func(p func(*game.Param), g func() mg.Game) {
err := game.JoinEqualGroups(p, "race_01")
assert.NoError(t, err)
})
}
-19
View File
@@ -1,19 +0,0 @@
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)
}
-19
View File
@@ -1,19 +0,0 @@
package game
import "github.com/iliadenisov/galaxy/pkg/model/game"
func PlanetProduction(configure func(*Param), race string, planetNumber int, prodType, subject string) (err error) {
control(configure, func(c *ctrl) {
c.execute(func(r Repo, g game.Game) {
err = planetProduction(r, g, race, planetNumber, prodType, subject)
})
})
return
}
func planetProduction(r Repo, g game.Game, race string, planetNumber int, prodType, subject string) error {
if err := g.PlanetProduction(race, planetNumber, prodType, subject); err != nil {
return err
}
return r.SaveState(g)
}
-35
View File
@@ -1,35 +0,0 @@
package game
import "github.com/iliadenisov/galaxy/pkg/model/game"
func CreateScience(configure func(*Param), race, typeName string, drive, weapons, shields, cargo float64) (err error) {
control(configure, func(c *ctrl) {
c.execute(func(r Repo, g game.Game) {
err = createScience(r, g, race, typeName, drive, weapons, shields, cargo)
})
})
return
}
func createScience(r Repo, g game.Game, race, typeName string, d, w, s, c float64) error {
if err := g.CreateScience(race, typeName, d, w, s, c); err != nil {
return err
}
return r.SaveState(g)
}
func DeleteScience(configure func(*Param), race, typeName string) (err error) {
control(configure, func(c *ctrl) {
c.execute(func(r Repo, g game.Game) {
err = deleteScience(r, g, race, typeName)
})
})
return
}
func deleteScience(r Repo, g game.Game, race, typeName string) error {
if err := g.DeleteScience(race, typeName); err != nil {
return err
}
return r.SaveState(g)
}
-51
View File
@@ -1,51 +0,0 @@
package game
import "github.com/iliadenisov/galaxy/pkg/model/game"
func CreateShipType(configure func(*Param), race, typeName string, drive, weapons, shields, cargo float64, armament int) (err error) {
control(configure, func(c *ctrl) {
c.execute(func(r Repo, g game.Game) {
err = createShipType(r, g, race, typeName, drive, weapons, shields, cargo, armament)
})
})
return
}
func createShipType(r Repo, g game.Game, race, typeName string, d, w, s, c float64, a int) error {
if err := g.CreateShipType(race, typeName, d, w, s, c, a); err != nil {
return err
}
return r.SaveState(g)
}
func MergeShipType(configure func(*Param), race, source, target string) (err error) {
control(configure, func(c *ctrl) {
c.execute(func(r Repo, g game.Game) {
err = mergeShipType(r, g, race, source, target)
})
})
return
}
func mergeShipType(r Repo, g game.Game, race, source, target string) error {
if err := g.MergeShipType(race, source, target); err != nil {
return err
}
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)
}
-24
View File
@@ -1,24 +0,0 @@
package game
import "github.com/iliadenisov/galaxy/pkg/model/game"
func DeclareWar(configure func(*Param), from, to string) (err error) {
control(configure, func(c *ctrl) {
c.execute(func(r Repo, g game.Game) { err = updateRelation(r, g, from, to, game.RelationWar) })
})
return
}
func DeclarePeace(configure func(*Param), from, to string) (err error) {
control(configure, func(c *ctrl) {
c.execute(func(r Repo, g game.Game) { err = updateRelation(r, g, from, to, game.RelationPeace) })
})
return
}
func updateRelation(r Repo, g game.Game, hostRace, opponentRace string, rel game.Relation) error {
if err := g.UpdateRelation(hostRace, opponentRace, rel); err != nil {
return err
}
return r.SaveState(g)
}
-74
View File
@@ -1,74 +0,0 @@
package game
import (
"fmt"
"github.com/google/uuid"
"github.com/iliadenisov/galaxy/pkg/model/game"
"github.com/iliadenisov/galaxy/pkg/repo"
)
type ctrl struct {
param Param
repo Repo
}
type Param struct {
StoragePath string
}
func LoadState(configure func(*Param)) (g game.Game, err error) {
control(configure, func(c *ctrl) { c.executeInit(func(r Repo) { g, err = c.repo.LoadState() }) })
return
}
func GenerateGame(configure func(*Param), races []string) (gameID uuid.UUID, err error) {
control(configure, func(c *ctrl) { c.executeInit(func(r Repo) { gameID, err = newGame(r, races) }) })
return
}
func newController(configure func(*Param)) (*ctrl, error) {
c := &Param{
StoragePath: ".",
}
if configure != nil {
configure(c)
}
r, err := repo.NewFileRepo(c.StoragePath)
if err != nil {
return nil, err
}
return &ctrl{
param: *c,
repo: r,
}, nil
}
func control(configure func(*Param), consumer func(*ctrl)) error {
c, err := newController(configure)
if err != nil {
return err
}
consumer(c)
return nil
}
func (c *ctrl) executeInit(consumer func(Repo)) error {
if err := c.repo.Lock(); err != nil {
return fmt.Errorf("execute: lock failed: %s", err)
}
consumer(c.repo)
return c.repo.Release()
}
func (c *ctrl) execute(consumer func(Repo, game.Game)) error {
if err := c.repo.Lock(); err != nil {
return fmt.Errorf("execute: lock failed: %s", err)
}
g, err := c.repo.LoadState()
if err != nil {
return err
}
consumer(c.repo, g)
return c.repo.Release()
}
-20
View File
@@ -1,20 +0,0 @@
package game
import "github.com/iliadenisov/galaxy/pkg/model/game"
type Repo interface {
// Lock must be called before any repository operations
Lock() error
// Release must be called after first and only repository operation
Release() error
// SaveTurn stores just generated new turn
SaveTurn(uint, game.Game) error
// SaveState stores current game state updated between turns
SaveState(game.Game) error
// LoadState retrieves game current state
LoadState() (game.Game, error)
}
-15
View File
@@ -1,15 +0,0 @@
package command
type Command struct {
FromRace string
}
type CommandAlly struct {
Command
ToRace string
}
type CommandWar struct {
Command
ToRace string
}