feat: store battles and bombings
This commit is contained in:
@@ -14,6 +14,7 @@ type Battle struct {
|
|||||||
ID uuid.UUID
|
ID uuid.UUID
|
||||||
Planet uint
|
Planet uint
|
||||||
observerGroups map[int]bool // True = In_Battle, False = Out_Battle
|
observerGroups map[int]bool // True = In_Battle, False = Out_Battle
|
||||||
|
initialNumbers map[int]uint // Initial number of ships in the group
|
||||||
Protocol []BattleAction
|
Protocol []BattleAction
|
||||||
|
|
||||||
shipAmmo map[int]uint
|
shipAmmo map[int]uint
|
||||||
@@ -22,7 +23,7 @@ type Battle struct {
|
|||||||
|
|
||||||
type BattleAction struct {
|
type BattleAction struct {
|
||||||
Attacker int
|
Attacker int
|
||||||
Defenter int
|
Defender int
|
||||||
Destroyed bool
|
Destroyed bool
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,6 +95,11 @@ func ProduceBattles(c *Cache) []*Battle {
|
|||||||
|
|
||||||
result := make([]*Battle, 0)
|
result := make([]*Battle, 0)
|
||||||
|
|
||||||
|
// TODO: check this behavior:
|
||||||
|
// Multiple battles on single planet shoul be produced as single battle too:
|
||||||
|
// A <--> B
|
||||||
|
// C <--> D
|
||||||
|
// where: A in peace with [C, D], B in peace with [C, D], and so on.
|
||||||
for pl, observerGroups := range planetGroups {
|
for pl, observerGroups := range planetGroups {
|
||||||
battleGroups := FilterBattleGroups(c, observerGroups)
|
battleGroups := FilterBattleGroups(c, observerGroups)
|
||||||
b := &Battle{
|
b := &Battle{
|
||||||
@@ -102,6 +108,9 @@ func ProduceBattles(c *Cache) []*Battle {
|
|||||||
attacker: make(map[int]map[int]float64),
|
attacker: make(map[int]map[int]float64),
|
||||||
shipAmmo: make(map[int]uint),
|
shipAmmo: make(map[int]uint),
|
||||||
}
|
}
|
||||||
|
for sgi := range observerGroups {
|
||||||
|
b.initialNumbers[sgi] = c.ShipGroup(sgi).Number
|
||||||
|
}
|
||||||
|
|
||||||
for i := range battleGroups {
|
for i := range battleGroups {
|
||||||
attIdx := battleGroups[i]
|
attIdx := battleGroups[i]
|
||||||
@@ -159,7 +168,7 @@ func SingleBattle(c *Cache, b *Battle) {
|
|||||||
|
|
||||||
b.Protocol = append(b.Protocol, BattleAction{
|
b.Protocol = append(b.Protocol, BattleAction{
|
||||||
Attacker: attIdx,
|
Attacker: attIdx,
|
||||||
Defenter: defIdx,
|
Defender: defIdx,
|
||||||
Destroyed: destroyed,
|
Destroyed: destroyed,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -1,46 +1,80 @@
|
|||||||
package controller
|
package controller
|
||||||
|
|
||||||
import "github.com/iliadenisov/galaxy/internal/model/game"
|
import (
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/iliadenisov/galaxy/internal/model/game"
|
||||||
|
"github.com/iliadenisov/galaxy/internal/model/report"
|
||||||
|
)
|
||||||
|
|
||||||
func TransformBattle(c *Cache, b *Battle) *game.BattleReport {
|
func TransformBattle(c *Cache, b *Battle) *report.BattleReport {
|
||||||
r := &game.BattleReport{
|
r := &report.BattleReport{
|
||||||
ID: b.ID,
|
ID: b.ID,
|
||||||
Planet: b.Planet,
|
Planet: b.Planet,
|
||||||
PlanetName: c.MustPlanet(b.Planet).Name,
|
PlanetName: c.MustPlanet(b.Planet).Name,
|
||||||
Races: make(map[int]string),
|
Races: make(map[int]uuid.UUID),
|
||||||
Ships: make(map[int]string),
|
Ships: make(map[int]report.BattleReportGroup),
|
||||||
Protocol: make([]game.BattleActionReport, len(b.Protocol)),
|
Protocol: make([]report.BattleActionReport, len(b.Protocol)),
|
||||||
}
|
}
|
||||||
|
|
||||||
cacheShipClass := make(map[string]int)
|
cacheShipClass := make(map[uuid.UUID]int)
|
||||||
cacheRaceName := make(map[string]int)
|
cacheRaceName := make(map[uuid.UUID]int)
|
||||||
|
|
||||||
cacher := func(shipClass string, cache map[string]int) int {
|
addShipGroup := func(groupId int, inBattle bool) int {
|
||||||
if v, ok := cache[shipClass]; ok {
|
shipClass := c.ShipGroupShipClass(groupId)
|
||||||
|
sg := c.ShipGroup(groupId)
|
||||||
|
itemNumber := len(r.Ships)
|
||||||
|
r.Ships[itemNumber] = report.BattleReportGroup{
|
||||||
|
OwnerID: sg.OwnerID,
|
||||||
|
InBattle: inBattle,
|
||||||
|
Number: b.initialNumbers[groupId],
|
||||||
|
NumberLeft: sg.Number,
|
||||||
|
ClassName: shipClass.Name,
|
||||||
|
LoadType: sg.CargoString(),
|
||||||
|
LoadQuantity: sg.Load,
|
||||||
|
Drive: sg.TechLevel(game.TechDrive),
|
||||||
|
Weapons: sg.TechLevel(game.TechWeapons),
|
||||||
|
Shields: sg.TechLevel(game.TechShields),
|
||||||
|
Cargo: sg.TechLevel(game.TechCargo),
|
||||||
|
}
|
||||||
|
cacheShipClass[shipClass.ID] = itemNumber
|
||||||
|
return itemNumber
|
||||||
|
}
|
||||||
|
|
||||||
|
ship := func(groupId int) int {
|
||||||
|
shipClass := c.ShipGroupShipClass(groupId)
|
||||||
|
if v, ok := cacheShipClass[shipClass.ID]; ok {
|
||||||
return v
|
return v
|
||||||
} else {
|
} else {
|
||||||
itemNumber := len(r.Ships)
|
return addShipGroup(groupId, true)
|
||||||
r.Ships[itemNumber] = shipClass
|
}
|
||||||
cache[shipClass] = itemNumber
|
}
|
||||||
|
|
||||||
|
race := func(groupId int) int {
|
||||||
|
race := c.ShipGroupOwnerRace(groupId)
|
||||||
|
if v, ok := cacheRaceName[race.ID]; ok {
|
||||||
|
return v
|
||||||
|
} else {
|
||||||
|
itemNumber := len(r.Races)
|
||||||
|
r.Races[itemNumber] = race.ID
|
||||||
|
cacheRaceName[race.ID] = itemNumber
|
||||||
return itemNumber
|
return itemNumber
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := range b.Protocol {
|
for i := range b.Protocol {
|
||||||
r.Protocol[i] = game.BattleActionReport{
|
r.Protocol[i] = report.BattleActionReport{
|
||||||
Attacker: cacher(c.ShipGroupOwnerRace(b.Protocol[i].Attacker).Name, cacheRaceName),
|
Attacker: race(b.Protocol[i].Attacker),
|
||||||
AttackerShipClass: cacher(c.ShipGroupShipClass(b.Protocol[i].Attacker).Name, cacheShipClass),
|
AttackerShipClass: ship(b.Protocol[i].Attacker),
|
||||||
Defender: cacher(c.ShipGroupOwnerRace(b.Protocol[i].Defenter).Name, cacheRaceName),
|
Defender: race(b.Protocol[i].Defender),
|
||||||
DefenderShipClass: cacher(c.ShipGroupShipClass(b.Protocol[i].Defenter).Name, cacheShipClass),
|
DefenderShipClass: ship(b.Protocol[i].Defender),
|
||||||
Destroyed: b.Protocol[i].Destroyed,
|
Destroyed: b.Protocol[i].Destroyed,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for name, index := range cacheRaceName {
|
for sgi, inBattle := range b.observerGroups {
|
||||||
r.Races[index] = name
|
if !inBattle {
|
||||||
}
|
addShipGroup(sgi, false)
|
||||||
for name, index := range cacheShipClass {
|
}
|
||||||
r.Ships[index] = name
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return r
|
return r
|
||||||
|
|||||||
@@ -3,36 +3,17 @@ package controller
|
|||||||
import (
|
import (
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/iliadenisov/galaxy/internal/model/game"
|
"github.com/iliadenisov/galaxy/internal/model/game"
|
||||||
|
"github.com/iliadenisov/galaxy/internal/model/report"
|
||||||
)
|
)
|
||||||
|
|
||||||
type BombingReport struct {
|
func (c *Cache) bombingReport(p *game.Planet, ri int, groups []int) report.BombingPlanetReport {
|
||||||
Planets []BombingPlanetReport `json:"planets"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type BombingPlanetReport struct {
|
|
||||||
ID uuid.UUID `json:"id"`
|
|
||||||
Planet string `json:"name"`
|
|
||||||
Number uint `json:"number"`
|
|
||||||
Owner string `json:"owner"`
|
|
||||||
Attacker string `json:"attacker"`
|
|
||||||
Production string `json:"production"`
|
|
||||||
Industry float64 `json:"industry"` // I - Промышленность
|
|
||||||
Population float64 `json:"population"` // P - Население
|
|
||||||
Colonists float64 `json:"colonists"` // COL C - Количество колонистов
|
|
||||||
Capital float64 `json:"capital"` // CAP $ - Запасы промышленности
|
|
||||||
Material float64 `json:"material"` // MAT M - Запасы ресурсов / сырья
|
|
||||||
AttackPower float64 `json:"attack"`
|
|
||||||
Wiped bool `json:"wiped"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Cache) bombingReport(p *game.Planet, ri int, groups []int) BombingPlanetReport {
|
|
||||||
attackPower := 0.
|
attackPower := 0.
|
||||||
for _, i := range groups {
|
for _, i := range groups {
|
||||||
sg := c.ShipGroup(i)
|
sg := c.ShipGroup(i)
|
||||||
st := c.ShipGroupShipClass(i)
|
st := c.ShipGroupShipClass(i)
|
||||||
attackPower += sg.BombingPower(st)
|
attackPower += sg.BombingPower(st)
|
||||||
}
|
}
|
||||||
r := &BombingPlanetReport{
|
r := &report.BombingPlanetReport{
|
||||||
ID: uuid.New(),
|
ID: uuid.New(),
|
||||||
Planet: p.Name,
|
Planet: p.Name,
|
||||||
Number: p.Number,
|
Number: p.Number,
|
||||||
@@ -51,8 +32,8 @@ func (c *Cache) bombingReport(p *game.Planet, ri int, groups []int) BombingPlane
|
|||||||
return *r
|
return *r
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cache) ProduceBombings() []BombingPlanetReport {
|
func (c *Cache) ProduceBombings() []report.BombingPlanetReport {
|
||||||
report := make([]BombingPlanetReport, 0)
|
report := make([]report.BombingPlanetReport, 0)
|
||||||
for pn, enemies := range c.collectBombingGroups() {
|
for pn, enemies := range c.collectBombingGroups() {
|
||||||
p := c.MustPlanet(pn)
|
p := c.MustPlanet(pn)
|
||||||
for ri, groups := range enemies {
|
for ri, groups := range enemies {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/iliadenisov/galaxy/internal/model/game"
|
"github.com/iliadenisov/galaxy/internal/model/game"
|
||||||
|
"github.com/iliadenisov/galaxy/internal/model/report"
|
||||||
"github.com/iliadenisov/galaxy/internal/repo"
|
"github.com/iliadenisov/galaxy/internal/repo"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -26,8 +27,11 @@ type Repo interface {
|
|||||||
// LoadStateSafe retrieves game current state without preliminary locking
|
// LoadStateSafe retrieves game current state without preliminary locking
|
||||||
LoadStateSafe() (*game.Game, error)
|
LoadStateSafe() (*game.Game, error)
|
||||||
|
|
||||||
// SaveBattle stores
|
// SaveBattle stores a new battle protocol and battle meta data for turn t
|
||||||
SaveBattle(t uint, b *game.BattleReport) error
|
SaveBattle(t uint, b *report.BattleReport, m *game.BattleMeta) error
|
||||||
|
|
||||||
|
// SaveBombing stores all prodused bombings for turn t
|
||||||
|
SaveBombings(t uint, b []report.BombingPlanetReport) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type Controller struct {
|
type Controller struct {
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ func buildGameOnMap(races []string, m generator.Map) (*game.Game, error) {
|
|||||||
}
|
}
|
||||||
g := &game.Game{
|
g := &game.Game{
|
||||||
ID: gameID,
|
ID: gameID,
|
||||||
Age: 0,
|
Turn: 0,
|
||||||
Race: make([]game.Race, len(races)),
|
Race: make([]game.Race, len(races)),
|
||||||
}
|
}
|
||||||
gameMap := &game.Map{
|
gameMap := &game.Map{
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ func TestNewGame(t *testing.T) {
|
|||||||
g, err := r.LoadState()
|
g, err := r.LoadState()
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, gameID, g.ID)
|
assert.Equal(t, gameID, g.ID)
|
||||||
assert.Equal(t, uint(0), g.Age)
|
assert.Equal(t, uint(0), g.Turn)
|
||||||
assert.Equal(t, players, len(g.Race))
|
assert.Equal(t, players, len(g.Race))
|
||||||
|
|
||||||
for r := range g.Race {
|
for r := range g.Race {
|
||||||
|
|||||||
@@ -2,12 +2,16 @@ package controller
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
// "github.com/iliadenisov/galaxy/internal/game/battle"
|
// "github.com/iliadenisov/galaxy/internal/game/battle"
|
||||||
|
"maps"
|
||||||
|
"slices"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
"github.com/iliadenisov/galaxy/internal/model/game"
|
"github.com/iliadenisov/galaxy/internal/model/game"
|
||||||
)
|
)
|
||||||
|
|
||||||
func MakeTurn(c *Controller, r Repo, g *game.Game) error {
|
func MakeTurn(c *Controller, r Repo) error {
|
||||||
// Next turn
|
// Next turn
|
||||||
g.Age += 1
|
c.Cache.g.Turn += 1
|
||||||
|
|
||||||
// 01. Корабли, где это возможно, объединяются в группы.
|
// 01. Корабли, где это возможно, объединяются в группы.
|
||||||
c.Cache.TurnMergeEqualShipGroups()
|
c.Cache.TurnMergeEqualShipGroups()
|
||||||
@@ -28,7 +32,7 @@ func MakeTurn(c *Controller, r Repo, g *game.Game) error {
|
|||||||
battles = append(battles, ProduceBattles(c.Cache)...)
|
battles = append(battles, ProduceBattles(c.Cache)...)
|
||||||
|
|
||||||
// 07. Корабли бомбят вражеские планеты.
|
// 07. Корабли бомбят вражеские планеты.
|
||||||
_ = c.Cache.ProduceBombings()
|
bombings := c.Cache.ProduceBombings()
|
||||||
|
|
||||||
// 08. На планетах строятся корабли.
|
// 08. На планетах строятся корабли.
|
||||||
// 09. Корабли, где это возможно, объединяются в группы.
|
// 09. Корабли, где это возможно, объединяются в группы.
|
||||||
@@ -49,12 +53,33 @@ func MakeTurn(c *Controller, r Repo, g *game.Game) error {
|
|||||||
|
|
||||||
/*** Last steps ***/
|
/*** Last steps ***/
|
||||||
|
|
||||||
|
// Store bombings
|
||||||
|
if len(bombings) > 0 {
|
||||||
|
if err := r.SaveBombings(c.Cache.g.Turn, bombings); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Store battles
|
// Store battles
|
||||||
if len(battles) > 0 {
|
if len(battles) > 0 {
|
||||||
|
battleMeta := make([]game.BattleMeta, len(battles))
|
||||||
for i := range battles {
|
for i := range battles {
|
||||||
// TODO: add In_Battle / Out_Battle participants?
|
b := battles[i]
|
||||||
br := TransformBattle(c.Cache, battles[i])
|
|
||||||
if err := r.SaveBattle(g.Age, br); err != nil {
|
observers := make(map[uuid.UUID]bool)
|
||||||
|
for sgi := range b.observerGroups {
|
||||||
|
observers[c.Cache.ShipGroup(sgi).OwnerID] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
battleMeta[i] = game.BattleMeta{
|
||||||
|
Turn: c.Cache.g.Turn,
|
||||||
|
Planet: b.Planet,
|
||||||
|
BattleID: b.ID,
|
||||||
|
ObserverIDs: slices.Collect(maps.Keys(observers)),
|
||||||
|
}
|
||||||
|
|
||||||
|
report := TransformBattle(c.Cache, b)
|
||||||
|
if err := r.SaveBattle(c.Cache.g.Turn, report, &battleMeta[i]); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -68,5 +93,6 @@ func MakeTurn(c *Controller, r Repo, g *game.Game) error {
|
|||||||
// TODO: Store individual reports
|
// TODO: Store individual reports
|
||||||
|
|
||||||
_ = winners
|
_ = winners
|
||||||
|
// [ ] monitor memory consumption at this point?
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -105,6 +105,7 @@ func (c *Cache) ShipGroupOwnerRace(groupIndex int) *game.Race {
|
|||||||
func (c *Cache) ShipGroupNumber(i int, n uint) {
|
func (c *Cache) ShipGroupNumber(i int, n uint) {
|
||||||
c.validateShipGroupIndex(i)
|
c.validateShipGroupIndex(i)
|
||||||
c.g.ShipGroups[i].Number = n
|
c.g.ShipGroups[i].Number = n
|
||||||
|
// FIXME: cargo load must be decreased proportionally
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cache) DeleteShipGroup(i int) {
|
func (c *Cache) DeleteShipGroup(i int) {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import (
|
|||||||
|
|
||||||
func MakeTurn(configure func(*controller.Param), race string, number int, name string) (err error) {
|
func MakeTurn(configure func(*controller.Param), race string, number int, name string) (err error) {
|
||||||
control(configure, func(c *controller.Controller) {
|
control(configure, func(c *controller.Controller) {
|
||||||
c.ExecuteGame(func(r controller.Repo, g *game.Game) { controller.MakeTurn(c, r, g) })
|
c.ExecuteGame(func(r controller.Repo, g *game.Game) { controller.MakeTurn(c, r) })
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,32 +0,0 @@
|
|||||||
package game
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
|
|
||||||
"github.com/google/uuid"
|
|
||||||
)
|
|
||||||
|
|
||||||
type BattleReport struct {
|
|
||||||
ID uuid.UUID `json:"id"`
|
|
||||||
Planet uint `json:"planet"`
|
|
||||||
PlanetName string `json:"planet_name"`
|
|
||||||
Races map[int]string `json:"races"`
|
|
||||||
Ships map[int]string `json:"ships"`
|
|
||||||
Protocol []BattleActionReport `json:"protocol"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type BattleActionReport struct {
|
|
||||||
Attacker int `json:"r1"`
|
|
||||||
AttackerShipClass int `json:"s1"`
|
|
||||||
Defender int `json:"r2"`
|
|
||||||
DefenderShipClass int `json:"s2"`
|
|
||||||
Destroyed bool `json:"d"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b BattleReport) MarshalBinary() (data []byte, err error) {
|
|
||||||
return json.Marshal(&b)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *BattleReport) UnmarshalBinary(data []byte) error {
|
|
||||||
return json.Unmarshal(data, b)
|
|
||||||
}
|
|
||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"maps"
|
"maps"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
"github.com/iliadenisov/galaxy/internal/model/report"
|
||||||
)
|
)
|
||||||
|
|
||||||
type TechSet map[Tech]float64
|
type TechSet map[Tech]float64
|
||||||
@@ -26,7 +27,7 @@ func (ts TechSet) Set(t Tech, v float64) TechSet {
|
|||||||
|
|
||||||
type Game struct {
|
type Game struct {
|
||||||
ID uuid.UUID `json:"id"`
|
ID uuid.UUID `json:"id"`
|
||||||
Age uint `json:"turn"` // Game's turn number
|
Turn uint `json:"turn"`
|
||||||
Map Map `json:"map"`
|
Map Map `json:"map"`
|
||||||
Race []Race `json:"races"`
|
Race []Race `json:"races"`
|
||||||
Votes float64 `json:"votes"`
|
Votes float64 `json:"votes"`
|
||||||
@@ -34,6 +35,18 @@ type Game struct {
|
|||||||
Fleets []Fleet `json:"fleet,omitempty"`
|
Fleets []Fleet `json:"fleet,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type GameMeta struct {
|
||||||
|
Battles []BattleMeta `json:"battles,omitempty"`
|
||||||
|
Bombings []report.BombingPlanetReport `json:"bombings,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type BattleMeta struct {
|
||||||
|
Turn uint `json:"turn"`
|
||||||
|
Planet uint `json:"planet"`
|
||||||
|
BattleID uuid.UUID `json:"battle_id"`
|
||||||
|
ObserverIDs []uuid.UUID `json:"observer_ids"`
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: remove if not needed
|
// TODO: remove if not needed
|
||||||
func (g Game) RaceVotes(raceID uuid.UUID) float64 {
|
func (g Game) RaceVotes(raceID uuid.UUID) float64 {
|
||||||
var result float64
|
var result float64
|
||||||
@@ -52,3 +65,11 @@ func (g Game) MarshalBinary() (data []byte, err error) {
|
|||||||
func (g *Game) UnmarshalBinary(data []byte) error {
|
func (g *Game) UnmarshalBinary(data []byte) error {
|
||||||
return json.Unmarshal(data, g)
|
return json.Unmarshal(data, g)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b GameMeta) MarshalBinary() (data []byte, err error) {
|
||||||
|
return json.Marshal(&b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *GameMeta) UnmarshalBinary(data []byte) error {
|
||||||
|
return json.Unmarshal(data, b)
|
||||||
|
}
|
||||||
|
|||||||
@@ -233,3 +233,10 @@ func (sg ShipGroup) BombingPower(st *ShipType) float64 {
|
|||||||
float64(st.Armament) *
|
float64(st.Armament) *
|
||||||
float64(sg.Number)
|
float64(sg.Number)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (sg ShipGroup) CargoString() string {
|
||||||
|
if sg.CargoType == nil {
|
||||||
|
return "-"
|
||||||
|
}
|
||||||
|
return sg.CargoType.String()
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package report
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
type BattleReport struct {
|
||||||
|
ID uuid.UUID `json:"id"`
|
||||||
|
Turn uint `json:"turn"`
|
||||||
|
Planet uint `json:"planet"`
|
||||||
|
PlanetName string `json:"planet_name"`
|
||||||
|
Races map[int]uuid.UUID `json:"races"`
|
||||||
|
Ships map[int]BattleReportGroup `json:"ships"`
|
||||||
|
Protocol []BattleActionReport `json:"protocol"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type BattleReportGroup struct {
|
||||||
|
OwnerID uuid.UUID `json:"ownerId"`
|
||||||
|
InBattle bool `json:"inBattle"`
|
||||||
|
Number uint `json:"num"`
|
||||||
|
NumberLeft uint `json:"numLeft"`
|
||||||
|
ClassName string `json:"className"`
|
||||||
|
LoadType string `json:"loadType"`
|
||||||
|
LoadQuantity float64 `json:"loadQuantity"`
|
||||||
|
Drive float64 `json:"drive"`
|
||||||
|
Weapons float64 `json:"wwapons"`
|
||||||
|
Shields float64 `json:"shields"`
|
||||||
|
Cargo float64 `json:"cargo"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type BattleActionReport struct {
|
||||||
|
Attacker int `json:"a"`
|
||||||
|
AttackerShipClass int `json:"sa"`
|
||||||
|
Defender int `json:"d"`
|
||||||
|
DefenderShipClass int `json:"sd"`
|
||||||
|
Destroyed bool `json:"x"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b BattleReport) MarshalBinary() (data []byte, err error) {
|
||||||
|
return json.Marshal(&b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BattleReport) UnmarshalBinary(data []byte) error {
|
||||||
|
return json.Unmarshal(data, b)
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package report
|
||||||
|
|
||||||
|
import "github.com/google/uuid"
|
||||||
|
|
||||||
|
type BombingPlanetReport struct {
|
||||||
|
ID uuid.UUID `json:"id"`
|
||||||
|
Planet string `json:"name"`
|
||||||
|
Number uint `json:"number"`
|
||||||
|
Owner string `json:"owner"`
|
||||||
|
Attacker string `json:"attacker"`
|
||||||
|
Production string `json:"production"`
|
||||||
|
Industry float64 `json:"industry"` // I - Промышленность
|
||||||
|
Population float64 `json:"population"` // P - Население
|
||||||
|
Colonists float64 `json:"colonists"` // COL C - Количество колонистов
|
||||||
|
Capital float64 `json:"capital"` // CAP $ - Запасы промышленности
|
||||||
|
Material float64 `json:"material"` // MAT M - Запасы ресурсов / сырья
|
||||||
|
AttackPower float64 `json:"attack"`
|
||||||
|
Wiped bool `json:"wiped"`
|
||||||
|
}
|
||||||
+63
-9
@@ -1,20 +1,25 @@
|
|||||||
package repo
|
package repo
|
||||||
|
|
||||||
/*
|
/*
|
||||||
/state.json
|
TODO: only state will be saved once (current, turn); meta and bombings are saved at turn generation and saved twice
|
||||||
|
/state.json
|
||||||
/0001/state.json
|
/0001/state.json
|
||||||
/0001/race/{UUID}/report.json
|
/0001/meta.json
|
||||||
|
/0001/bombing.json
|
||||||
/0001/battle/{UUID}.json
|
/0001/battle/{UUID}.json
|
||||||
|
/0001/report/{UUID}.json
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/iliadenisov/galaxy/internal/model/game"
|
"github.com/iliadenisov/galaxy/internal/model/game"
|
||||||
|
"github.com/iliadenisov/galaxy/internal/model/report"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
statePath = "state.json"
|
statePath = "state.json"
|
||||||
|
metaPath = "meta.json"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (r *repo) SaveTurn(t uint, g *game.Game) error {
|
func (r *repo) SaveTurn(t uint, g *game.Game) error {
|
||||||
@@ -65,7 +70,7 @@ func (r *repo) LoadStateSafe() (*game.Game, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func loadState(s Storage, locked bool) (*game.Game, error) {
|
func loadState(s Storage, locked bool) (*game.Game, error) {
|
||||||
var g *game.Game = new(game.Game)
|
var result *game.Game = new(game.Game)
|
||||||
path := statePath
|
path := statePath
|
||||||
exist, err := s.Exists(path)
|
exist, err := s.Exists(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -75,22 +80,62 @@ func loadState(s Storage, locked bool) (*game.Game, error) {
|
|||||||
return nil, NewGameNotInitializedError()
|
return nil, NewGameNotInitializedError()
|
||||||
}
|
}
|
||||||
if locked {
|
if locked {
|
||||||
if err := s.Read(path, g); err != nil {
|
if err := s.Read(path, result); err != nil {
|
||||||
return nil, NewStorageError(err)
|
return nil, NewStorageError(err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if err := s.ReadSafe(path, g); err != nil {
|
if err := s.ReadSafe(path, result); err != nil {
|
||||||
return nil, NewStorageError(err)
|
return nil, NewStorageError(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return g, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *repo) SaveBattle(t uint, b *game.BattleReport) error {
|
func loadMeta(s Storage) (*game.GameMeta, error) {
|
||||||
return saveBattle(r.s, t, b)
|
var result *game.GameMeta = new(game.GameMeta)
|
||||||
|
path := metaPath
|
||||||
|
exist, err := s.Exists(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, NewStorageError(err)
|
||||||
|
}
|
||||||
|
if !exist {
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
// TODO: create separate Read func for meta ops
|
||||||
|
if err := s.ReadSafe(path, result); err != nil {
|
||||||
|
return nil, NewStorageError(err)
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func saveBattle(s Storage, t uint, b *game.BattleReport) error {
|
func saveMeta(s Storage, t uint, gm *game.GameMeta) error {
|
||||||
|
// save turn's meta
|
||||||
|
path := fmt.Sprintf("%s/%s", turnDir(t), metaPath)
|
||||||
|
if err := s.Write(path, gm); err != nil {
|
||||||
|
return NewStorageError(err)
|
||||||
|
}
|
||||||
|
// also save as latest meta
|
||||||
|
path = metaPath
|
||||||
|
if err := s.Write(path, gm); err != nil {
|
||||||
|
return NewStorageError(err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *repo) SaveBattle(t uint, b *report.BattleReport, m *game.BattleMeta) error {
|
||||||
|
meta, err := loadMeta(r.s)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = saveBattle(r.s, t, b)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
meta.Battles = append(meta.Battles, *m)
|
||||||
|
return saveMeta(r.s, t, meta)
|
||||||
|
}
|
||||||
|
|
||||||
|
func saveBattle(s Storage, t uint, b *report.BattleReport) error {
|
||||||
path := fmt.Sprintf("%s/battle/%s.json", turnDir(t), b.ID)
|
path := fmt.Sprintf("%s/battle/%s.json", turnDir(t), b.ID)
|
||||||
exist, err := s.Exists(path)
|
exist, err := s.Exists(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -105,6 +150,15 @@ func saveBattle(s Storage, t uint, b *game.BattleReport) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *repo) SaveBombings(t uint, b []report.BombingPlanetReport) error {
|
||||||
|
meta, err := loadMeta(r.s)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
meta.Bombings = b
|
||||||
|
return saveMeta(r.s, t, meta)
|
||||||
|
}
|
||||||
|
|
||||||
func turnDir(t uint) string {
|
func turnDir(t uint) string {
|
||||||
return fmt.Sprintf("%04d", t)
|
return fmt.Sprintf("%04d", t)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ func StatusHandler(c *gin.Context, config controller.Config) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
c.JSON(http.StatusOK, rest.Status{
|
c.JSON(http.StatusOK, rest.Status{
|
||||||
Turn: g.Age,
|
Turn: g.Turn,
|
||||||
Players: len(g.Race),
|
Players: len(g.Race),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user