saving new turn

This commit is contained in:
Ilia Denisov
2025-09-26 01:38:49 +03:00
parent 6d87ea6086
commit 282150a253
13 changed files with 170 additions and 57 deletions
+3 -3
View File
@@ -10,7 +10,7 @@ import (
) )
type Repo interface { type Repo interface {
Persist(game.Game) error SaveTurn(uint, game.Game) error
} }
func NewGame(r Repo, races []string) (uuid.UUID, error) { func NewGame(r Repo, races []string) (uuid.UUID, error) {
@@ -46,7 +46,7 @@ func NewGame(r Repo, races []string) (uuid.UUID, error) {
g.Race[i] = game.Race{ g.Race[i] = game.Race{
ID: raceID, ID: raceID,
Name: races[i], Name: races[i],
Ally: raceID, Vote: raceID,
Drive: 1, Drive: 1,
Weapons: 1, Weapons: 1,
Shields: 1, Shields: 1,
@@ -104,7 +104,7 @@ func NewGame(r Repo, races []string) (uuid.UUID, error) {
g.Map = *gameMap g.Map = *gameMap
if err := r.Persist(*g); err != nil { if err := r.SaveTurn(0, *g); err != nil {
return uuid.Nil, fmt.Errorf("persist: %s", err) return uuid.Nil, fmt.Errorf("persist: %s", err)
} }
return g.ID, nil return g.ID, nil
+13 -5
View File
@@ -1,12 +1,16 @@
package game package game
import "github.com/google/uuid" import (
"encoding/json"
"github.com/google/uuid"
)
type Game struct { type Game struct {
ID uuid.UUID ID uuid.UUID `json:"id"`
Age uint // Game's turn number Age uint `json:"turn"` // Game's turn number
Map Map Map Map `json:"map"`
Race []Race Race []Race `json:"races"`
} }
func (g Game) Votes(raceID uuid.UUID) float64 { func (g Game) Votes(raceID uuid.UUID) float64 {
@@ -19,3 +23,7 @@ func (g Game) Votes(raceID uuid.UUID) float64 {
} }
return pop / 1000. return pop / 1000.
} }
func (g Game) MarshalBinary() (data []byte, err error) {
return json.Marshal(&g)
}
+3 -3
View File
@@ -1,7 +1,7 @@
package game package game
type Map struct { type Map struct {
Width uint32 Width uint32 `json:"width"`
Height uint32 Height uint32 `json:"height"`
Planet []Planet Planet []Planet `json:"planets"`
} }
+13 -12
View File
@@ -7,21 +7,22 @@ import (
) )
type Planet struct { type Planet struct {
X, Y float64 X float64 `json:"x"`
Size float64 Y float64 `json:"y"`
Size float64 `json:"size"`
Name string Name string `json:"name"`
Number uint Number uint `json:"number"`
Owner uuid.UUID Owner uuid.UUID `json:"owner"`
Production ProductionType Production ProductionType `json:"production"`
Population float64 // P - Население Population float64 `json:"population"` // P - Население
Industry float64 // I - Промышленность Industry float64 `json:"industry"` // I - Промышленность
Resources float64 // R - Ресурсы / сырьё Resources float64 `json:"resources"` // R - Ресурсы / сырьё
Capital float64 // CAP $ - Запасы промышленности Capital float64 `json:"capital"` // CAP $ - Запасы промышленности
Material float64 // MAT M - Запасы ресурсов / сырья Material float64 `json:"material"` // MAT M - Запасы ресурсов / сырья
Colonists float64 // COL C - Количество колонистов Colonists float64 `json:"colonists"` // COL C - Количество колонистов
// Параметр "L" - Свободный производственный потенциал // Параметр "L" - Свободный производственный потенциал
} }
+2 -2
View File
@@ -17,8 +17,8 @@ const (
) )
type ProductionType struct { type ProductionType struct {
Production PlanetProduction Production PlanetProduction `json:"type"`
SubjectName string // TODO: change to UUID SubjectName string `json:"subject"` // TODO: change to UUID
} }
func (p PlanetProduction) AsType(subject string) ProductionType { func (p PlanetProduction) AsType(subject string) ProductionType {
+20 -9
View File
@@ -3,17 +3,28 @@ package game
import "github.com/google/uuid" import "github.com/google/uuid"
type Race struct { type Race struct {
ID uuid.UUID ID uuid.UUID `json:"id"`
Name string Name string `json:"name"`
Killed bool Extinct bool `json:"extinct"`
// Votes float64 Vote uuid.UUID `json:"vote"`
Ally uuid.UUID // Race's Votes receiver Relations []RaceRelation `json:"relations"`
Drive float64 Drive float64 `json:"drive"`
Weapons float64 Weapons float64 `json:"weapons"`
Shields float64 Shields float64 `json:"shields"`
Cargo float64 Cargo float64 `json:"cargo"`
Sciences []Science `json:"science,omitempty"`
ShipTypes []ShipType `json:"shipType,omitempty"`
ShipGroups []ShipGroup `json:"shipGroup,omitempty"`
Fleets []Fleet `json:"fleet,omitempty"`
}
type RaceRelation struct {
RaceID uuid.UUID `json:"raceId"`
Peace bool `json:"peace"`
} }
func (r Race) FlightDistance() float64 { func (r Race) FlightDistance() float64 {
+8 -5
View File
@@ -1,9 +1,12 @@
package game package game
import "github.com/google/uuid"
type Science struct { type Science struct {
Name string ID uuid.UUID `json:"id"`
Drive float64 Name string `json:"name"`
Weapons float64 Drive float64 `json:"drive"`
Shields float64 Weapons float64 `json:"weapons"`
Cargo float64 Shields float64 `json:"shields"`
Cargo float64 `json:"cargo"`
} }
+18 -15
View File
@@ -3,31 +3,34 @@ package game
import ( import (
"math" "math"
"github.com/google/uuid"
"github.com/iliadenisov/galaxy/pkg/number" "github.com/iliadenisov/galaxy/pkg/number"
) )
type ShipType struct { type ShipType struct {
Name string ID uuid.UUID `json:"id"`
Drive float64 // [0], [1...] Name string `json:"name"`
Armament uint Drive float64 `json:"drive"` // [0], [1...]
Weapons float64 // [0], [1...] Armament uint `json:"armament"`
Shields float64 // [0], [1...] Weapons float64 `json:"weapons"` // [0], [1...]
Cargo float64 // [0], [1...] Shields float64 `json:"shields"` // [0], [1...]
Cargo float64 `json:"cargo"` // [0], [1...]
} }
type ShipGroup struct { type ShipGroup struct {
Type ShipType TypeID uuid.UUID `json:"id"`
Number uint Type ShipType `json:"-"` // TODO: fill upon load from store
State string // TODO: kinda enum: In_Orbit, In_Space, Transfer_State, Upgrade Number uint `json:"number"`
Load float64 // Cargo loaded - "Масса груза" State string `json:"state"` // TODO: kinda enum: In_Orbit, In_Space, Transfer_State, Upgrade
Drive float64 Load float64 `json:"load"` // Cargo loaded - "Масса груза"
Weapons float64 Drive float64 `json:"drive"`
Shields float64 Weapons float64 `json:"weapons"`
Cargo float64 Shields float64 `json:"shields"`
Cargo float64 `json:"cargo"`
} }
type Fleet struct { type Fleet struct {
ShipGroups []ShipGroup ShipGroups []ShipGroup `json:"group"`
} }
// TODO: test on real values // TODO: test on real values
@@ -7,7 +7,7 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
func Test_ShipType(t *testing.T) { func TestShipType(t *testing.T) {
Gunship := game.ShipType{ Gunship := game.ShipType{
Drive: 4, Drive: 4,
Armament: 2, Armament: 2,
@@ -42,7 +42,7 @@ func Test_ShipType(t *testing.T) {
assert.Equal(t, upgradeCost, 225.) assert.Equal(t, upgradeCost, 225.)
} }
func Test_CargoCapacity(t *testing.T) { func TestCargoCapacity(t *testing.T) {
test := func(cargoSize float64, expectCapacity float64) { test := func(cargoSize float64, expectCapacity float64) {
ship := game.ShipType{ ship := game.ShipType{
Drive: 1, Drive: 1,
@@ -69,7 +69,7 @@ func Test_CargoCapacity(t *testing.T) {
test(100, 600) test(100, 600)
} }
func Test_BombingPower(t *testing.T) { func TestBombingPower(t *testing.T) {
Gunship := game.ShipType{ Gunship := game.ShipType{
Drive: 60.0, Drive: 60.0,
Armament: 3, Armament: 3,
+4
View File
@@ -75,6 +75,10 @@ func (f *fs) Lock() (func() error, error) {
return unlock, nil return unlock, nil
} }
func (f *fs) Exist(path string) (bool, error) {
return fileExists(filepath.Join(f.root, path))
}
func (f *fs) Write(path string, v encoding.BinaryMarshaler) error { func (f *fs) Write(path string, v encoding.BinaryMarshaler) error {
if v == nil { if v == nil {
return errors.New("cant't marshal from nil object") return errors.New("cant't marshal from nil object")
+21
View File
@@ -33,6 +33,27 @@ func TestLock(t *testing.T) {
assert.False(t, exists, "lock file must be removed") assert.False(t, exists, "lock file must be removed")
} }
func TestExist(t *testing.T) {
root, cleanup := createWorkDir(t)
defer cleanup()
fileName := "some-file.ext"
if err := os.WriteFile(filepath.Join(root, fileName), []byte{1, 2, 3, 4}, os.ModePerm); err != nil {
t.Fatal(err)
}
fs, err := NewFileStorage(root)
assert.NoError(t, err, "create file storage")
exist, err := fs.Exist(fileName)
assert.NoError(t, err)
assert.True(t, exist)
exist, err = fs.Exist("random/path")
assert.NoError(t, err)
assert.False(t, exist)
}
func TestWrite(t *testing.T) { func TestWrite(t *testing.T) {
root, cleanup := createWorkDir(t) root, cleanup := createWorkDir(t)
defer cleanup() defer cleanup()
+48
View File
@@ -0,0 +1,48 @@
package repo
/*
/state.json
/000/state.json
/000/race/{UUID}/order/001.json
/000/race/{UUID}/report.json
/000/battle/{planet_UUID}
*/
import (
"fmt"
"github.com/iliadenisov/galaxy/pkg/model/game"
)
func (r *repo) SaveTurn(t uint, g game.Game) error {
return saveTurn(r.s, t, g)
}
func saveTurn(s Storage, t uint, g game.Game) error {
path := fmt.Sprintf("%03d/state.json", t)
exist, err := s.Exist(path)
if err != nil {
return NewStorageError(err)
}
if exist {
return NewStateError(fmt.Sprintf("state for turn %d already saved", t))
}
if err := s.Write(path, g); err != nil {
return NewStorageError(err)
}
// TODO: save reports
// TODO: save battles
return saveState(s, g)
}
func (r *repo) SaveState(g game.Game) error {
return saveState(r.s, g)
}
func saveState(s Storage, g game.Game) error {
path := "state.json"
if err := s.Write(path, g); err != nil {
return NewStorageError(err)
}
return nil
}
+14
View File
@@ -2,12 +2,26 @@ package repo
import ( import (
"encoding" "encoding"
"errors"
"github.com/iliadenisov/galaxy/pkg/repo/fs" "github.com/iliadenisov/galaxy/pkg/repo/fs"
) )
type StorageError error
func NewStorageError(err error) error {
return StorageError(err)
}
type StateError error
func NewStateError(msg string) error {
return StateError(errors.New(msg))
}
type Storage interface { type Storage interface {
Lock() (func() error, error) Lock() (func() error, error)
Exist(string) (bool, error)
Write(string, encoding.BinaryMarshaler) error Write(string, encoding.BinaryMarshaler) error
Read(string, encoding.BinaryUnmarshaler) error Read(string, encoding.BinaryUnmarshaler) error
} }