wip: generate report

This commit is contained in:
Ilia Denisov
2026-02-03 23:41:18 +02:00
parent a567229f8a
commit adbe605783
36 changed files with 1037 additions and 391 deletions
+20
View File
@@ -0,0 +1,20 @@
package game
import "github.com/google/uuid"
type Bombing struct {
ID uuid.UUID `json:"-"`
PlanetOwnedID uuid.UUID `json:"-"` // for the report filtering
Planet string `json:"name"`
Number uint `json:"number"`
Owner string `json:"owner"`
Attacker string `json:"attacker"`
Production string `json:"production"`
Industry Float `json:"industry"` // I - Промышленность
Population Float `json:"population"` // P - Население
Colonists Float `json:"colonists"` // COL C - Количество колонистов
Capital Float `json:"capital"` // CAP $ - Запасы промышленности
Material Float `json:"material"` // MAT M - Запасы ресурсов / сырья
AttackPower Float `json:"attack"`
Wiped bool `json:"wiped"`
}
+21 -7
View File
@@ -6,9 +6,22 @@ import (
"maps"
"github.com/google/uuid"
"github.com/iliadenisov/galaxy/internal/model/report"
)
type Float float64
func F(v float64) Float {
return Float(v)
}
func (f Float) Add(v float64) Float {
return F(f.F() + v)
}
func (f Float) F() float64 {
return float64(f)
}
type TechSet map[Tech]float64
func (ts TechSet) Value(t Tech) float64 {
@@ -25,6 +38,7 @@ func (ts TechSet) Set(t Tech, v float64) TechSet {
return m
}
// TODO: turn's incremental Version
type Game struct {
ID uuid.UUID `json:"id"`
Turn uint `json:"turn"`
@@ -36,8 +50,8 @@ type Game struct {
}
type GameMeta struct {
Battles []BattleMeta `json:"battles,omitempty"`
Bombings []report.BombingPlanetReport `json:"bombings,omitempty"`
Battles []BattleMeta `json:"battles,omitempty"`
Bombings []Bombing `json:"bombings,omitempty"`
}
type BattleMeta struct {
@@ -66,10 +80,10 @@ func (g *Game) UnmarshalBinary(data []byte) error {
return json.Unmarshal(data, g)
}
func (b GameMeta) MarshalBinary() (data []byte, err error) {
return json.Marshal(&b)
func (gm GameMeta) MarshalBinary() (data []byte, err error) {
return json.Marshal(&gm)
}
func (b *GameMeta) UnmarshalBinary(data []byte) error {
return json.Unmarshal(data, b)
func (gm *GameMeta) UnmarshalBinary(data []byte) error {
return json.Unmarshal(data, gm)
}
+11 -15
View File
@@ -100,21 +100,17 @@ func (t Tech) String() string {
}
type ShipGroup struct {
Index uint `json:"index"` // FIXME: use UUID for Group Index (ordered)
OwnerID uuid.UUID `json:"ownerId"` // Race link
TypeID uuid.UUID `json:"typeId"` // ShipType link
FleetID *uuid.UUID `json:"fleetId,omitempty"` // Fleet link
Number uint `json:"number"` // Number (quantity) ships of specific ShipType
CargoType *CargoType `json:"loadType,omitempty"`
Load Float `json:"load"` // Cargo loaded - "Масса груза"
Tech TechSet `json:"tech"`
// TODO: TEST: Destination, Origin, Range
Destination uint `json:"destination"`
StateInSpace *InSpace `json:"stateInSpace,omitempty"`
StateUpgrade *InUpgrade `json:"stateUpgrade,omitempty"`
Index uint `json:"index"` // FIXME: use UUID for Group Index (ordered)
OwnerID uuid.UUID `json:"ownerId"` // Race link
TypeID uuid.UUID `json:"typeId"` // ShipType link
FleetID *uuid.UUID `json:"fleetId,omitempty"` // Fleet link
Number uint `json:"number"` // Number (quantity) ships of specific ShipType
CargoType *CargoType `json:"loadType,omitempty"` //
Load Float `json:"load"` // Cargo loaded - "Масса груза"
Tech TechSet `json:"tech"` //
Destination uint `json:"destination"` // TODO: TEST: Destination, Origin, Range
StateInSpace *InSpace `json:"stateInSpace,omitempty"` //
StateUpgrade *InUpgrade `json:"stateUpgrade,omitempty"` //
}
func (sg ShipGroup) TechLevel(t Tech) Float {
+28 -38
View File
@@ -13,13 +13,11 @@ import (
func TestCargoCapacity(t *testing.T) {
test := func(cargoSize float64, expectCapacity float64) {
ship := game.ShipType{
ShipTypeReport: game.ShipTypeReport{
Drive: 1,
Armament: 1,
Weapons: 1,
Shields: 1,
Cargo: cargoSize,
},
Drive: 1,
Armament: 1,
Weapons: 1,
Shields: 1,
Cargo: cargoSize,
}
sg := game.ShipGroup{
Number: 1,
@@ -41,14 +39,12 @@ func TestCargoCapacity(t *testing.T) {
func TestCarryingAndFullMass(t *testing.T) {
Freighter := &game.ShipType{
ShipTypeReport: game.ShipTypeReport{
Name: "Freighter",
Drive: 8,
Armament: 0,
Weapons: 0,
Shields: 2,
Cargo: 10,
},
Name: "Freighter",
Drive: 8,
Armament: 0,
Weapons: 0,
Shields: 2,
Cargo: 10,
}
sg := &game.ShipGroup{
Number: 1,
@@ -75,14 +71,12 @@ func TestCarryingAndFullMass(t *testing.T) {
func TestSpeed(t *testing.T) {
Freighter := &game.ShipType{
ShipTypeReport: game.ShipTypeReport{
Name: "Freighter",
Drive: 8,
Armament: 0,
Weapons: 0,
Shields: 2,
Cargo: 10,
},
Name: "Freighter",
Drive: 8,
Armament: 0,
Weapons: 0,
Shields: 2,
Cargo: 10,
}
sg := &game.ShipGroup{
Number: 1,
@@ -106,14 +100,12 @@ func TestSpeed(t *testing.T) {
func TestBombingPower(t *testing.T) {
BattleStation := game.ShipType{
ShipTypeReport: game.ShipTypeReport{
Name: "Battle_Station",
Drive: 60.0,
Armament: 3,
Weapons: 30.0,
Shields: 100.0,
Cargo: 0.0,
},
Name: "Battle_Station",
Drive: 60.0,
Armament: 3,
Weapons: 30.0,
Shields: 100.0,
Cargo: 0.0,
}
sg := game.ShipGroup{
Number: 1,
@@ -145,13 +137,11 @@ func TestDriveEffective(t *testing.T) {
}
for i := range tc {
someShip := game.ShipType{
ShipTypeReport: game.ShipTypeReport{
Drive: tc[i].driveShipType,
Armament: rand.UintN(30) + 1,
Weapons: rand.Float64()*30 + 1,
Shields: rand.Float64()*100 + 1,
Cargo: rand.Float64()*20 + 1,
},
Drive: tc[i].driveShipType,
Armament: rand.UintN(30) + 1,
Weapons: rand.Float64()*30 + 1,
Shields: rand.Float64()*100 + 1,
Cargo: rand.Float64()*20 + 1,
}
sg := game.ShipGroup{
Number: rand.UintN(4) + 1,
+3 -4
View File
@@ -1,7 +1,6 @@
package game
import (
"maps"
"math"
"slices"
)
@@ -12,7 +11,7 @@ type UpgradeCalc struct {
func (uc UpgradeCalc) UpgradeCost(ships uint) float64 {
var sum float64
for v := range maps.Values(uc.Cost) {
for _, v := range uc.Cost {
sum += v
}
return sum * float64(ships)
@@ -29,7 +28,7 @@ func BlockUpgradeCost(blockMass, currentBlockTech, targetBlockTech float64) floa
return (1 - currentBlockTech/targetBlockTech) * 10 * blockMass
}
func GroupUpgradeCost(sg ShipGroup, st ShipType, drive, weapons, shields, cargo float64) UpgradeCalc {
func GroupUpgradeCost(sg *ShipGroup, st ShipType, drive, weapons, shields, cargo float64) UpgradeCalc {
uc := &UpgradeCalc{Cost: make(map[Tech]float64)}
if drive > 0 {
uc.Cost[TechDrive] = BlockUpgradeCost(st.DriveBlockMass(), sg.TechLevel(TechDrive).F(), drive)
@@ -46,7 +45,7 @@ func GroupUpgradeCost(sg ShipGroup, st ShipType, drive, weapons, shields, cargo
return *uc
}
func CurrentUpgradingLevel(sg ShipGroup, tech Tech) float64 {
func CurrentUpgradingLevel(sg *ShipGroup, tech Tech) float64 {
if sg.StateUpgrade == nil {
return 0
}
+20 -22
View File
@@ -9,14 +9,12 @@ import (
var (
Cruiser = game.ShipType{
ShipTypeReport: game.ShipTypeReport{
Name: "Cruiser",
Drive: 15,
Armament: 1,
Weapons: 15,
Shields: 15,
Cargo: 0,
},
Name: "Cruiser",
Drive: 15,
Armament: 1,
Weapons: 15,
Shields: 15,
Cargo: 0,
}
)
@@ -27,7 +25,7 @@ func TestBlockUpgradeCost(t *testing.T) {
}
func TestGroupUpgradeCost(t *testing.T) {
sg := game.ShipGroup{
sg := &game.ShipGroup{
Tech: map[game.Tech]float64{
game.TechDrive: 1.0,
game.TechWeapons: 1.0,
@@ -40,7 +38,7 @@ func TestGroupUpgradeCost(t *testing.T) {
}
func TestUpgradeMaxShips(t *testing.T) {
sg := game.ShipGroup{
sg := &game.ShipGroup{
Tech: map[game.Tech]float64{
game.TechDrive: 1.0,
game.TechWeapons: 1.0,
@@ -57,26 +55,26 @@ func TestCurrentUpgradingLevel(t *testing.T) {
sg := &game.ShipGroup{
StateUpgrade: nil,
}
assert.Equal(t, 0.0, game.CurrentUpgradingLevel(*sg, game.TechDrive))
assert.Equal(t, 0.0, game.CurrentUpgradingLevel(*sg, game.TechWeapons))
assert.Equal(t, 0.0, game.CurrentUpgradingLevel(*sg, game.TechShields))
assert.Equal(t, 0.0, game.CurrentUpgradingLevel(*sg, game.TechCargo))
assert.Equal(t, 0.0, game.CurrentUpgradingLevel(sg, game.TechDrive))
assert.Equal(t, 0.0, game.CurrentUpgradingLevel(sg, game.TechWeapons))
assert.Equal(t, 0.0, game.CurrentUpgradingLevel(sg, game.TechShields))
assert.Equal(t, 0.0, game.CurrentUpgradingLevel(sg, game.TechCargo))
sg.StateUpgrade = &game.InUpgrade{
UpgradeTech: []game.UpgradePreference{
{Tech: game.TechDrive, Level: 1.5, Cost: 100.1},
},
}
assert.Equal(t, 1.5, game.CurrentUpgradingLevel(*sg, game.TechDrive))
assert.Equal(t, 0.0, game.CurrentUpgradingLevel(*sg, game.TechWeapons))
assert.Equal(t, 0.0, game.CurrentUpgradingLevel(*sg, game.TechShields))
assert.Equal(t, 0.0, game.CurrentUpgradingLevel(*sg, game.TechCargo))
assert.Equal(t, 1.5, game.CurrentUpgradingLevel(sg, game.TechDrive))
assert.Equal(t, 0.0, game.CurrentUpgradingLevel(sg, game.TechWeapons))
assert.Equal(t, 0.0, game.CurrentUpgradingLevel(sg, game.TechShields))
assert.Equal(t, 0.0, game.CurrentUpgradingLevel(sg, game.TechCargo))
sg.StateUpgrade.UpgradeTech = append(sg.StateUpgrade.UpgradeTech, game.UpgradePreference{Tech: game.TechCargo, Level: 2.2, Cost: 200.2})
assert.Equal(t, 1.5, game.CurrentUpgradingLevel(*sg, game.TechDrive))
assert.Equal(t, 0.0, game.CurrentUpgradingLevel(*sg, game.TechWeapons))
assert.Equal(t, 0.0, game.CurrentUpgradingLevel(*sg, game.TechShields))
assert.Equal(t, 2.2, game.CurrentUpgradingLevel(*sg, game.TechCargo))
assert.Equal(t, 1.5, game.CurrentUpgradingLevel(sg, game.TechDrive))
assert.Equal(t, 0.0, game.CurrentUpgradingLevel(sg, game.TechWeapons))
assert.Equal(t, 0.0, game.CurrentUpgradingLevel(sg, game.TechShields))
assert.Equal(t, 2.2, game.CurrentUpgradingLevel(sg, game.TechCargo))
}
func TestFutureUpgradeLevel(t *testing.T) {
+1 -1
View File
@@ -27,9 +27,9 @@ type PlanetReport struct {
Population Float `json:"population"` // P - Население
Colonists Float `json:"colonists"` // COL C - Количество колонистов
Production Production `json:"production"` // TODO: internal/report format
// Параметр "L" - Свободный производственный потенциал
}
// TODO: unwrap in one struct
type Planet struct {
Owner uuid.UUID `json:"owner"` // FIXME: nil value when no owner
Route map[RouteType]uint `json:"route"`
+4
View File
@@ -20,6 +20,10 @@ type Race struct {
type Relation string
func (r Relation) String() string {
return string(r)
}
const (
RelationWar Relation = "War"
RelationPeace Relation = "Peace"
-67
View File
@@ -1,67 +0,0 @@
package game
type Report struct {
Width, Height uint32
PlanetCount uint32 // do we need that?
PlayersLeft uint32 // do we need that?
Votes float64
VoteFor string
Statuses []PlayerStatus
Sciences []ScienceReport
ForeignSciences []ScienceReportForeign
ShipTypes []ShipTypeReport
ForeignShipTypes []ShipTypeReportForeign
Battles []any // TODO: tbd
Bombings []any // TODO: tbd
IncomingGroups []IncomingGroup
Planets []PlanetReport
ForeignPlanets []PlanetReportForeign
UninhabitedPlanets []UninhabitedPlanet
UnidentifiedPlanets []UnidentifiedPlanet
ShipsInProduction []any // TODO: tbd
Routes []any // TODO: tbd
Fleets []any // TODO: tbd
ShipGroups []any // TODO: tbd
ForeignShipGroups []any // TODO: tbd
UnidentifiedGroups []any // TODO: tbd
}
type IncomingGroup struct {
SourcePlanetNumber uint
TargetPlanetNumber uint
Distance float64
Speed float64
Mass float64
}
type ReportRelation struct {
RaceName string
Relation string
}
type PlayerStatus struct {
Name string
Drive float64 `json:"drive"`
Weapons float64 `json:"weapons"`
Shields float64 `json:"shields"`
Cargo float64 `json:"cargo"`
Population float64
Industry float64
Planets uint16
Relation ReportRelation
Votes float64
}
+6 -15
View File
@@ -5,19 +5,10 @@ import (
)
type Science struct {
ID uuid.UUID `json:"id"`
ScienceReport
}
type ScienceReportForeign struct {
RaceName string
ScienceReport
}
type ScienceReport struct {
Name string `json:"name"`
Drive float64 `json:"drive"`
Weapons float64 `json:"weapons"`
Shields float64 `json:"shields"`
Cargo float64 `json:"cargo"`
ID uuid.UUID `json:"id"`
Name string `json:"name"`
Drive float64 `json:"drive"`
Weapons float64 `json:"weapons"`
Shields float64 `json:"shields"`
Cargo float64 `json:"cargo"`
}
+7 -16
View File
@@ -4,23 +4,14 @@ import (
"github.com/google/uuid"
)
type ShipTypeReport struct {
Name string `json:"name"`
Drive float64 `json:"drive"`
Armament uint `json:"armament"`
Weapons float64 `json:"weapons"`
Shields float64 `json:"shields"`
Cargo float64 `json:"cargo"`
}
type ShipType struct {
ID uuid.UUID `json:"id"`
ShipTypeReport
}
type ShipTypeReportForeign struct {
RaceName string
ShipTypeReport
ID uuid.UUID `json:"id"`
Name string `json:"name"`
Drive float64 `json:"drive"`
Armament uint `json:"armament"`
Weapons float64 `json:"weapons"`
Shields float64 `json:"shields"`
Cargo float64 `json:"cargo"`
}
func (st ShipType) Equal(o ShipType) bool {
+18 -24
View File
@@ -9,38 +9,32 @@ import (
func TestEmptyMass(t *testing.T) {
Freighter := game.ShipType{
ShipTypeReport: game.ShipTypeReport{
Name: "Freighter",
Drive: 8,
Armament: 0,
Weapons: 0,
Shields: 2,
Cargo: 10,
},
Name: "Freighter",
Drive: 8,
Armament: 0,
Weapons: 0,
Shields: 2,
Cargo: 10,
}
assert.Equal(t, 20., Freighter.EmptyMass())
Gunship := game.ShipType{
ShipTypeReport: game.ShipTypeReport{
Name: "Gunship",
Drive: 4,
Armament: 2,
Weapons: 2,
Shields: 4,
Cargo: 0,
},
Name: "Gunship",
Drive: 4,
Armament: 2,
Weapons: 2,
Shields: 4,
Cargo: 0,
}
assert.Equal(t, 11., Gunship.EmptyMass())
Cruiser := game.ShipType{
ShipTypeReport: game.ShipTypeReport{
Name: "Cruiser",
Drive: 15,
Armament: 1,
Weapons: 15,
Shields: 15,
Cargo: 0,
},
Name: "Cruiser",
Drive: 15,
Armament: 1,
Weapons: 15,
Shields: 15,
Cargo: 0,
}
assert.Equal(t, 45., Cruiser.EmptyMass())
}