feat: store battles and bombings
This commit is contained in:
@@ -14,6 +14,7 @@ type Battle struct {
|
||||
ID uuid.UUID
|
||||
Planet uint
|
||||
observerGroups map[int]bool // True = In_Battle, False = Out_Battle
|
||||
initialNumbers map[int]uint // Initial number of ships in the group
|
||||
Protocol []BattleAction
|
||||
|
||||
shipAmmo map[int]uint
|
||||
@@ -22,7 +23,7 @@ type Battle struct {
|
||||
|
||||
type BattleAction struct {
|
||||
Attacker int
|
||||
Defenter int
|
||||
Defender int
|
||||
Destroyed bool
|
||||
}
|
||||
|
||||
@@ -94,6 +95,11 @@ func ProduceBattles(c *Cache) []*Battle {
|
||||
|
||||
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 {
|
||||
battleGroups := FilterBattleGroups(c, observerGroups)
|
||||
b := &Battle{
|
||||
@@ -102,6 +108,9 @@ func ProduceBattles(c *Cache) []*Battle {
|
||||
attacker: make(map[int]map[int]float64),
|
||||
shipAmmo: make(map[int]uint),
|
||||
}
|
||||
for sgi := range observerGroups {
|
||||
b.initialNumbers[sgi] = c.ShipGroup(sgi).Number
|
||||
}
|
||||
|
||||
for i := range battleGroups {
|
||||
attIdx := battleGroups[i]
|
||||
@@ -159,7 +168,7 @@ func SingleBattle(c *Cache, b *Battle) {
|
||||
|
||||
b.Protocol = append(b.Protocol, BattleAction{
|
||||
Attacker: attIdx,
|
||||
Defenter: defIdx,
|
||||
Defender: defIdx,
|
||||
Destroyed: destroyed,
|
||||
})
|
||||
|
||||
|
||||
@@ -1,46 +1,80 @@
|
||||
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 {
|
||||
r := &game.BattleReport{
|
||||
func TransformBattle(c *Cache, b *Battle) *report.BattleReport {
|
||||
r := &report.BattleReport{
|
||||
ID: b.ID,
|
||||
Planet: b.Planet,
|
||||
PlanetName: c.MustPlanet(b.Planet).Name,
|
||||
Races: make(map[int]string),
|
||||
Ships: make(map[int]string),
|
||||
Protocol: make([]game.BattleActionReport, len(b.Protocol)),
|
||||
Races: make(map[int]uuid.UUID),
|
||||
Ships: make(map[int]report.BattleReportGroup),
|
||||
Protocol: make([]report.BattleActionReport, len(b.Protocol)),
|
||||
}
|
||||
|
||||
cacheShipClass := make(map[string]int)
|
||||
cacheRaceName := make(map[string]int)
|
||||
cacheShipClass := make(map[uuid.UUID]int)
|
||||
cacheRaceName := make(map[uuid.UUID]int)
|
||||
|
||||
cacher := func(shipClass string, cache map[string]int) int {
|
||||
if v, ok := cache[shipClass]; ok {
|
||||
addShipGroup := func(groupId int, inBattle bool) int {
|
||||
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
|
||||
} else {
|
||||
itemNumber := len(r.Ships)
|
||||
r.Ships[itemNumber] = shipClass
|
||||
cache[shipClass] = itemNumber
|
||||
return addShipGroup(groupId, true)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
for i := range b.Protocol {
|
||||
r.Protocol[i] = game.BattleActionReport{
|
||||
Attacker: cacher(c.ShipGroupOwnerRace(b.Protocol[i].Attacker).Name, cacheRaceName),
|
||||
AttackerShipClass: cacher(c.ShipGroupShipClass(b.Protocol[i].Attacker).Name, cacheShipClass),
|
||||
Defender: cacher(c.ShipGroupOwnerRace(b.Protocol[i].Defenter).Name, cacheRaceName),
|
||||
DefenderShipClass: cacher(c.ShipGroupShipClass(b.Protocol[i].Defenter).Name, cacheShipClass),
|
||||
r.Protocol[i] = report.BattleActionReport{
|
||||
Attacker: race(b.Protocol[i].Attacker),
|
||||
AttackerShipClass: ship(b.Protocol[i].Attacker),
|
||||
Defender: race(b.Protocol[i].Defender),
|
||||
DefenderShipClass: ship(b.Protocol[i].Defender),
|
||||
Destroyed: b.Protocol[i].Destroyed,
|
||||
}
|
||||
}
|
||||
|
||||
for name, index := range cacheRaceName {
|
||||
r.Races[index] = name
|
||||
}
|
||||
for name, index := range cacheShipClass {
|
||||
r.Ships[index] = name
|
||||
for sgi, inBattle := range b.observerGroups {
|
||||
if !inBattle {
|
||||
addShipGroup(sgi, false)
|
||||
}
|
||||
}
|
||||
|
||||
return r
|
||||
|
||||
@@ -3,36 +3,17 @@ package controller
|
||||
import (
|
||||
"github.com/google/uuid"
|
||||
"github.com/iliadenisov/galaxy/internal/model/game"
|
||||
"github.com/iliadenisov/galaxy/internal/model/report"
|
||||
)
|
||||
|
||||
type BombingReport struct {
|
||||
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 {
|
||||
func (c *Cache) bombingReport(p *game.Planet, ri int, groups []int) report.BombingPlanetReport {
|
||||
attackPower := 0.
|
||||
for _, i := range groups {
|
||||
sg := c.ShipGroup(i)
|
||||
st := c.ShipGroupShipClass(i)
|
||||
attackPower += sg.BombingPower(st)
|
||||
}
|
||||
r := &BombingPlanetReport{
|
||||
r := &report.BombingPlanetReport{
|
||||
ID: uuid.New(),
|
||||
Planet: p.Name,
|
||||
Number: p.Number,
|
||||
@@ -51,8 +32,8 @@ func (c *Cache) bombingReport(p *game.Planet, ri int, groups []int) BombingPlane
|
||||
return *r
|
||||
}
|
||||
|
||||
func (c *Cache) ProduceBombings() []BombingPlanetReport {
|
||||
report := make([]BombingPlanetReport, 0)
|
||||
func (c *Cache) ProduceBombings() []report.BombingPlanetReport {
|
||||
report := make([]report.BombingPlanetReport, 0)
|
||||
for pn, enemies := range c.collectBombingGroups() {
|
||||
p := c.MustPlanet(pn)
|
||||
for ri, groups := range enemies {
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/iliadenisov/galaxy/internal/model/game"
|
||||
"github.com/iliadenisov/galaxy/internal/model/report"
|
||||
"github.com/iliadenisov/galaxy/internal/repo"
|
||||
)
|
||||
|
||||
@@ -26,8 +27,11 @@ type Repo interface {
|
||||
// LoadStateSafe retrieves game current state without preliminary locking
|
||||
LoadStateSafe() (*game.Game, error)
|
||||
|
||||
// SaveBattle stores
|
||||
SaveBattle(t uint, b *game.BattleReport) error
|
||||
// SaveBattle stores a new battle protocol and battle meta data for turn t
|
||||
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 {
|
||||
|
||||
@@ -41,7 +41,7 @@ func buildGameOnMap(races []string, m generator.Map) (*game.Game, error) {
|
||||
}
|
||||
g := &game.Game{
|
||||
ID: gameID,
|
||||
Age: 0,
|
||||
Turn: 0,
|
||||
Race: make([]game.Race, len(races)),
|
||||
}
|
||||
gameMap := &game.Map{
|
||||
|
||||
@@ -35,7 +35,7 @@ func TestNewGame(t *testing.T) {
|
||||
g, err := r.LoadState()
|
||||
assert.NoError(t, err)
|
||||
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))
|
||||
|
||||
for r := range g.Race {
|
||||
|
||||
@@ -2,12 +2,16 @@ package controller
|
||||
|
||||
import (
|
||||
// "github.com/iliadenisov/galaxy/internal/game/battle"
|
||||
"maps"
|
||||
"slices"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"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
|
||||
g.Age += 1
|
||||
c.Cache.g.Turn += 1
|
||||
|
||||
// 01. Корабли, где это возможно, объединяются в группы.
|
||||
c.Cache.TurnMergeEqualShipGroups()
|
||||
@@ -28,7 +32,7 @@ func MakeTurn(c *Controller, r Repo, g *game.Game) error {
|
||||
battles = append(battles, ProduceBattles(c.Cache)...)
|
||||
|
||||
// 07. Корабли бомбят вражеские планеты.
|
||||
_ = c.Cache.ProduceBombings()
|
||||
bombings := c.Cache.ProduceBombings()
|
||||
|
||||
// 08. На планетах строятся корабли.
|
||||
// 09. Корабли, где это возможно, объединяются в группы.
|
||||
@@ -49,12 +53,33 @@ func MakeTurn(c *Controller, r Repo, g *game.Game) error {
|
||||
|
||||
/*** Last steps ***/
|
||||
|
||||
// Store bombings
|
||||
if len(bombings) > 0 {
|
||||
if err := r.SaveBombings(c.Cache.g.Turn, bombings); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Store battles
|
||||
if len(battles) > 0 {
|
||||
battleMeta := make([]game.BattleMeta, len(battles))
|
||||
for i := range battles {
|
||||
// TODO: add In_Battle / Out_Battle participants?
|
||||
br := TransformBattle(c.Cache, battles[i])
|
||||
if err := r.SaveBattle(g.Age, br); err != nil {
|
||||
b := battles[i]
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -68,5 +93,6 @@ func MakeTurn(c *Controller, r Repo, g *game.Game) error {
|
||||
// TODO: Store individual reports
|
||||
|
||||
_ = winners
|
||||
// [ ] monitor memory consumption at this point?
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -105,6 +105,7 @@ func (c *Cache) ShipGroupOwnerRace(groupIndex int) *game.Race {
|
||||
func (c *Cache) ShipGroupNumber(i int, n uint) {
|
||||
c.validateShipGroupIndex(i)
|
||||
c.g.ShipGroups[i].Number = n
|
||||
// FIXME: cargo load must be decreased proportionally
|
||||
}
|
||||
|
||||
func (c *Cache) DeleteShipGroup(i int) {
|
||||
|
||||
Reference in New Issue
Block a user