@@ -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"`
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package game
|
||||
|
||||
import "github.com/google/uuid"
|
||||
|
||||
type Fleet struct {
|
||||
ID uuid.UUID `json:"id"`
|
||||
OwnerID uuid.UUID `json:"ownerId"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
package game
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"maps"
|
||||
|
||||
"galaxy/util"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type Float float64
|
||||
|
||||
func F(v float64) Float {
|
||||
return Float(v)
|
||||
}
|
||||
|
||||
func (f Float) Add(v float64) Float {
|
||||
return f + F(v)
|
||||
}
|
||||
|
||||
func (f Float) F() float64 {
|
||||
return util.Fixed12(float64(f))
|
||||
}
|
||||
|
||||
type TechSet map[Tech]Float
|
||||
|
||||
func (ts TechSet) Value(t Tech) float64 {
|
||||
if v, ok := ts[t]; ok {
|
||||
return v.F()
|
||||
} else {
|
||||
panic(fmt.Sprintf("TechSet: Value: %s's value not set", t.String()))
|
||||
}
|
||||
}
|
||||
|
||||
func (ts TechSet) Set(t Tech, v float64) TechSet {
|
||||
m := maps.Clone(ts)
|
||||
m[t] = F(v)
|
||||
return m
|
||||
}
|
||||
|
||||
func NewTechSet() TechSet {
|
||||
return TechSet{
|
||||
TechDrive: 1.,
|
||||
TechWeapons: 1.,
|
||||
TechShields: 1.,
|
||||
TechCargo: 1.,
|
||||
}
|
||||
}
|
||||
|
||||
type Game struct {
|
||||
ID uuid.UUID `json:"id"`
|
||||
Turn uint `json:"turn"`
|
||||
Stage uint `json:"stage"`
|
||||
Map Map `json:"map"`
|
||||
Race []Race `json:"races"`
|
||||
Votes Float `json:"votes"`
|
||||
ShipGroups []ShipGroup `json:"shipGroup,omitempty"`
|
||||
Fleets []Fleet `json:"fleet,omitempty"`
|
||||
Winner []uuid.UUID `json:"winner,omitempty"`
|
||||
}
|
||||
|
||||
func (g Game) Finished() bool {
|
||||
return len(g.Winner) > 0
|
||||
}
|
||||
|
||||
type GameMeta struct {
|
||||
Battles []BattleMeta `json:"battles,omitempty"`
|
||||
Bombings []Bombing `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"`
|
||||
}
|
||||
|
||||
func (g Game) MarshalBinary() (data []byte, err error) {
|
||||
return json.Marshal(&g)
|
||||
}
|
||||
|
||||
func (g *Game) UnmarshalBinary(data []byte) error {
|
||||
return json.Unmarshal(data, g)
|
||||
}
|
||||
|
||||
func (gm GameMeta) MarshalBinary() (data []byte, err error) {
|
||||
return json.Marshal(&gm)
|
||||
}
|
||||
|
||||
func (gm *GameMeta) UnmarshalBinary(data []byte) error {
|
||||
return json.Unmarshal(data, gm)
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package game_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/iliadenisov/galaxy/server/internal/model/game"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestTechSet(t *testing.T) {
|
||||
s := ts.Set(game.TechDrive, 10.5)
|
||||
assert.Equal(t, 1.1, ts.Value(game.TechDrive))
|
||||
assert.Equal(t, 1.2, ts.Value(game.TechWeapons))
|
||||
assert.Equal(t, 1.3, ts.Value(game.TechShields))
|
||||
assert.Equal(t, 1.4, ts.Value(game.TechCargo))
|
||||
|
||||
assert.Equal(t, 10.5, s.Value(game.TechDrive))
|
||||
assert.Equal(t, 1.2, s.Value(game.TechWeapons))
|
||||
assert.Equal(t, 1.3, s.Value(game.TechShields))
|
||||
assert.Equal(t, 1.4, s.Value(game.TechCargo))
|
||||
|
||||
s = s.Set(game.TechWeapons, 5.7)
|
||||
assert.Equal(t, 1.1, ts.Value(game.TechDrive))
|
||||
assert.Equal(t, 1.2, ts.Value(game.TechWeapons))
|
||||
assert.Equal(t, 1.3, ts.Value(game.TechShields))
|
||||
assert.Equal(t, 1.4, ts.Value(game.TechCargo))
|
||||
|
||||
assert.Equal(t, 10.5, s.Value(game.TechDrive))
|
||||
assert.Equal(t, 5.7, s.Value(game.TechWeapons))
|
||||
assert.Equal(t, 1.3, s.Value(game.TechShields))
|
||||
assert.Equal(t, 1.4, s.Value(game.TechCargo))
|
||||
|
||||
s = s.Set(game.TechShields, 2.13)
|
||||
assert.Equal(t, 1.1, ts.Value(game.TechDrive))
|
||||
assert.Equal(t, 1.2, ts.Value(game.TechWeapons))
|
||||
assert.Equal(t, 1.3, ts.Value(game.TechShields))
|
||||
assert.Equal(t, 1.4, ts.Value(game.TechCargo))
|
||||
|
||||
assert.Equal(t, 10.5, s.Value(game.TechDrive))
|
||||
assert.Equal(t, 5.7, s.Value(game.TechWeapons))
|
||||
assert.Equal(t, 2.13, s.Value(game.TechShields))
|
||||
assert.Equal(t, 1.4, s.Value(game.TechCargo))
|
||||
|
||||
s = s.Set(game.TechCargo, 3.1415926)
|
||||
assert.Equal(t, 1.1, ts.Value(game.TechDrive))
|
||||
assert.Equal(t, 1.2, ts.Value(game.TechWeapons))
|
||||
assert.Equal(t, 1.3, ts.Value(game.TechShields))
|
||||
assert.Equal(t, 1.4, ts.Value(game.TechCargo))
|
||||
|
||||
assert.Equal(t, 10.5, s.Value(game.TechDrive))
|
||||
assert.Equal(t, 5.7, s.Value(game.TechWeapons))
|
||||
assert.Equal(t, 2.13, s.Value(game.TechShields))
|
||||
assert.Equal(t, 3.1415926, s.Value(game.TechCargo))
|
||||
}
|
||||
@@ -0,0 +1,233 @@
|
||||
package game
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"strings"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type CargoType string
|
||||
|
||||
const (
|
||||
CargoColonist CargoType = "COL" // Колонисты
|
||||
CargoMaterial CargoType = "MAT" // Сырьё
|
||||
CargoCapital CargoType = "CAP" // Промышленность
|
||||
)
|
||||
|
||||
var (
|
||||
CargoTypeSet map[string]CargoType = map[string]CargoType{
|
||||
strings.ToLower(CargoColonist.String()): CargoColonist,
|
||||
strings.ToLower(CargoMaterial.String()): CargoMaterial,
|
||||
strings.ToLower(CargoCapital.String()): CargoCapital,
|
||||
}
|
||||
)
|
||||
|
||||
func (ct CargoType) Ref() *CargoType {
|
||||
return &ct
|
||||
}
|
||||
|
||||
func (ct CargoType) String() string {
|
||||
return string(ct)
|
||||
}
|
||||
|
||||
type ShipGroupState string
|
||||
|
||||
const (
|
||||
StateInOrbit ShipGroupState = "In_Orbit"
|
||||
StateLaunched ShipGroupState = "Launched"
|
||||
StateInSpace ShipGroupState = "In_Space"
|
||||
StateUpgrade ShipGroupState = "Upgrade"
|
||||
StateTransfer ShipGroupState = "Transfer" // [ ] Группы будут передаваться мгновенно в начале производства хода
|
||||
)
|
||||
|
||||
func (sgs ShipGroupState) String() string {
|
||||
return string(sgs)
|
||||
}
|
||||
|
||||
type InSpace struct {
|
||||
// X, Y are nil for Launched state
|
||||
Origin uint `json:"origin"`
|
||||
X *Float `json:"x,omitempty"`
|
||||
Y *Float `json:"y,omitempty"`
|
||||
}
|
||||
|
||||
func (is InSpace) Equal(other InSpace) bool {
|
||||
return is.Origin == other.Origin && is.X == other.X && is.Y == other.Y
|
||||
}
|
||||
|
||||
func (is InSpace) Launched() bool {
|
||||
if (is.X == nil && is.Y != nil) || (is.X != nil && is.Y == nil) {
|
||||
panic("group in space state invalid: one of coordinate is not set")
|
||||
}
|
||||
return is.X == nil && is.Y == is.X
|
||||
}
|
||||
|
||||
type InUpgrade struct {
|
||||
UpgradeTech []UpgradePreference `json:"preference"`
|
||||
}
|
||||
|
||||
func (iu InUpgrade) Cost() float64 {
|
||||
var sum float64
|
||||
for i := range iu.UpgradeTech {
|
||||
sum += iu.UpgradeTech[i].Cost.F()
|
||||
}
|
||||
return sum
|
||||
}
|
||||
|
||||
func (iu InUpgrade) TechCost(t Tech) float64 {
|
||||
for i := range iu.UpgradeTech {
|
||||
if iu.UpgradeTech[i].Tech == t {
|
||||
return iu.UpgradeTech[i].Cost.F()
|
||||
}
|
||||
}
|
||||
return 0.
|
||||
}
|
||||
|
||||
type UpgradePreference struct {
|
||||
Tech Tech `json:"tech"`
|
||||
Level Float `json:"level"`
|
||||
Cost Float `json:"cost"`
|
||||
}
|
||||
|
||||
type Tech string
|
||||
|
||||
const (
|
||||
TechAll Tech = "ALL"
|
||||
TechDrive Tech = "DRIVE"
|
||||
TechWeapons Tech = "WEAPONS"
|
||||
TechShields Tech = "SHIELDS"
|
||||
TechCargo Tech = "CARGO"
|
||||
)
|
||||
|
||||
func (t Tech) String() string {
|
||||
return string(t)
|
||||
}
|
||||
|
||||
type ShipGroup struct {
|
||||
ID uuid.UUID `json:"id"`
|
||||
OwnerID uuid.UUID `json:"ownerId"` // Race reference
|
||||
TypeID uuid.UUID `json:"typeId"` // ShipType reference
|
||||
FleetID *uuid.UUID `json:"fleetId,omitempty"` // Fleet reference
|
||||
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"` //
|
||||
StateInSpace *InSpace `json:"inSpace,omitempty"` //
|
||||
StateUpgrade *InUpgrade `json:"upgrade,omitempty"` //
|
||||
StateTransfer bool `json:"transfer,omitempty"` //
|
||||
}
|
||||
|
||||
func (sg ShipGroup) TechLevel(t Tech) Float {
|
||||
return F(sg.Tech.Value(t))
|
||||
}
|
||||
|
||||
func (sg *ShipGroup) SetTechLevel(t Tech, v float64) {
|
||||
sg.Tech = sg.Tech.Set(t, v)
|
||||
}
|
||||
|
||||
func (sg ShipGroup) State() ShipGroupState {
|
||||
switch {
|
||||
case sg.StateInSpace == nil && sg.StateUpgrade == nil:
|
||||
return StateInOrbit
|
||||
case sg.StateInSpace != nil && sg.StateUpgrade == nil:
|
||||
if !sg.StateInSpace.Launched() {
|
||||
return StateInSpace
|
||||
}
|
||||
if sg.StateTransfer {
|
||||
return StateTransfer
|
||||
}
|
||||
return StateLaunched
|
||||
case sg.StateUpgrade != nil && sg.StateInSpace == nil:
|
||||
return StateUpgrade
|
||||
default:
|
||||
panic(fmt.Sprintf("ambigous group state: in_space=%#v upgrage=%#v", sg.StateInSpace, sg.StateUpgrade))
|
||||
}
|
||||
}
|
||||
|
||||
func (sg ShipGroup) AtPlanet() (uint, bool) {
|
||||
switch sg.State() {
|
||||
case StateInOrbit:
|
||||
return sg.Destination, true
|
||||
case StateLaunched:
|
||||
return sg.StateInSpace.Origin, true
|
||||
default:
|
||||
return 0, false
|
||||
}
|
||||
}
|
||||
|
||||
func (sg ShipGroup) Coord() (float64, float64, bool) {
|
||||
state := sg.State()
|
||||
if state == StateInSpace || state == StateLaunched {
|
||||
return sg.StateInSpace.X.F(), sg.StateInSpace.Y.F(), true
|
||||
}
|
||||
return 0, 0, false
|
||||
}
|
||||
|
||||
func (sg ShipGroup) Equal(other ShipGroup) bool {
|
||||
return sg.OwnerID == other.OwnerID &&
|
||||
sg.TypeID == other.TypeID &&
|
||||
sg.FleetID == other.FleetID &&
|
||||
sg.TechLevel(TechDrive) == other.TechLevel(TechDrive) &&
|
||||
sg.TechLevel(TechWeapons) == other.TechLevel(TechWeapons) &&
|
||||
sg.TechLevel(TechShields) == other.TechLevel(TechShields) &&
|
||||
sg.TechLevel(TechCargo) == other.TechLevel(TechCargo) &&
|
||||
sg.CargoType == other.CargoType &&
|
||||
(sg.Load/F(float64(sg.Number))).F() == (other.Load/F(float64(other.Number))).F() &&
|
||||
sg.State() == other.State()
|
||||
}
|
||||
|
||||
// Грузоподъёмность
|
||||
func (sg ShipGroup) CargoCapacity(st *ShipType) float64 {
|
||||
return sg.TechLevel(TechCargo).F() * (st.Cargo.F() + (st.Cargo.F()*st.Cargo.F())/20) * float64(sg.Number)
|
||||
}
|
||||
|
||||
// Масса перевозимого груза -
|
||||
// общее количество единиц груза, деленное на технологический уровень Грузоперевозок
|
||||
func (sg ShipGroup) CarryingMass() float64 {
|
||||
if sg.Load.F() == 0 {
|
||||
return 0
|
||||
}
|
||||
return sg.Load.F() / sg.TechLevel(TechCargo).F()
|
||||
}
|
||||
|
||||
// Масса группы без учёта груза
|
||||
func (sg ShipGroup) EmptyMass(st *ShipType) float64 {
|
||||
return st.EmptyMass() * float64(sg.Number)
|
||||
}
|
||||
|
||||
// Полная масса -
|
||||
// массу корабля самого по себе плюс масса перевозимого груза
|
||||
func (sg ShipGroup) FullMass(st *ShipType) float64 {
|
||||
return sg.EmptyMass(st) + sg.CarryingMass()
|
||||
}
|
||||
|
||||
// Эффективность двигателя -
|
||||
// равна мощности Двигателей, умноженной на технологический уровень блока Двигателей
|
||||
func (sg ShipGroup) DriveEffective(st *ShipType) float64 {
|
||||
return st.Drive.F() * sg.TechLevel(TechDrive).F()
|
||||
}
|
||||
|
||||
// Корабли перемещаются за один ход на количество световых лет, равное
|
||||
// эффективности двигателя, умноженной на 20 и деленной на "Полную массу" корабля
|
||||
func (sg ShipGroup) Speed(st *ShipType) float64 {
|
||||
return sg.DriveEffective(st) * 20 / sg.FullMass(st)
|
||||
}
|
||||
|
||||
// Мощность бомбардировки
|
||||
func (sg ShipGroup) BombingPower(st *ShipType) float64 {
|
||||
return (math.Sqrt(st.Weapons.F()*sg.TechLevel(TechWeapons).F())/10. + 1.) *
|
||||
st.Weapons.F() *
|
||||
sg.TechLevel(TechWeapons).F() *
|
||||
float64(st.Armament) *
|
||||
float64(sg.Number)
|
||||
}
|
||||
|
||||
func (sg ShipGroup) CargoString() string {
|
||||
if sg.CargoType == nil {
|
||||
return "-"
|
||||
}
|
||||
return sg.CargoType.String()
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
package game_test
|
||||
|
||||
import (
|
||||
"math/rand/v2"
|
||||
"testing"
|
||||
|
||||
"galaxy/util"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/iliadenisov/galaxy/server/internal/model/game"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestCargoCapacity(t *testing.T) {
|
||||
test := func(cargoSize float64, expectCapacity float64) {
|
||||
ship := game.ShipType{
|
||||
Drive: 1,
|
||||
Armament: 1,
|
||||
Weapons: 1,
|
||||
Shields: 1,
|
||||
Cargo: game.F(cargoSize),
|
||||
}
|
||||
sg := game.ShipGroup{
|
||||
Number: 1,
|
||||
Tech: map[game.Tech]game.Float{
|
||||
game.TechDrive: game.F(1.5),
|
||||
game.TechWeapons: game.F(1.1),
|
||||
game.TechShields: game.F(2.0),
|
||||
game.TechCargo: game.F(1.0),
|
||||
},
|
||||
}
|
||||
assert.Equal(t, expectCapacity, sg.CargoCapacity(&ship))
|
||||
}
|
||||
test(1, 1.05)
|
||||
test(5, 6.25)
|
||||
test(10, 15)
|
||||
test(50, 175)
|
||||
test(100, 600)
|
||||
}
|
||||
|
||||
func TestCarryingAndFullMass(t *testing.T) {
|
||||
Freighter := &game.ShipType{
|
||||
Name: "Freighter",
|
||||
Drive: 8,
|
||||
Armament: 0,
|
||||
Weapons: 0,
|
||||
Shields: 2,
|
||||
Cargo: 10,
|
||||
}
|
||||
sg := &game.ShipGroup{
|
||||
Number: 1,
|
||||
Tech: map[game.Tech]game.Float{
|
||||
game.TechDrive: game.F(1.0),
|
||||
game.TechWeapons: game.F(1.0),
|
||||
game.TechShields: game.F(1.0),
|
||||
game.TechCargo: game.F(1.0),
|
||||
},
|
||||
Load: 0.0,
|
||||
}
|
||||
em := Freighter.EmptyMass()
|
||||
assert.Equal(t, 0.0, sg.CarryingMass())
|
||||
assert.Equal(t, em, sg.FullMass(Freighter))
|
||||
|
||||
sg.Load = 10.0
|
||||
assert.Equal(t, 10.0, sg.CarryingMass())
|
||||
assert.Equal(t, em+10.0, sg.FullMass(Freighter))
|
||||
|
||||
sg.SetTechLevel(game.TechCargo, 2.5)
|
||||
assert.Equal(t, 4.0, sg.CarryingMass())
|
||||
assert.Equal(t, em+4.0, sg.FullMass(Freighter))
|
||||
}
|
||||
|
||||
func TestSpeed(t *testing.T) {
|
||||
Freighter := &game.ShipType{
|
||||
Name: "Freighter",
|
||||
Drive: 8,
|
||||
Armament: 0,
|
||||
Weapons: 0,
|
||||
Shields: 2,
|
||||
Cargo: 10,
|
||||
}
|
||||
sg := &game.ShipGroup{
|
||||
Number: 1,
|
||||
Tech: map[game.Tech]game.Float{
|
||||
game.TechDrive: game.F(1.0),
|
||||
game.TechWeapons: game.F(1.0),
|
||||
game.TechShields: game.F(1.0),
|
||||
game.TechCargo: game.F(1.0),
|
||||
},
|
||||
Load: 0.0,
|
||||
}
|
||||
assert.Equal(t, 8.0, sg.Speed(Freighter))
|
||||
sg.Load = 5.0
|
||||
assert.Equal(t, 6.4, sg.Speed(Freighter))
|
||||
sg.SetTechLevel(game.TechDrive, 1.5)
|
||||
assert.Equal(t, 9.6, sg.Speed(Freighter))
|
||||
sg.Load = 10
|
||||
sg.SetTechLevel(game.TechCargo, 1.5)
|
||||
assert.Equal(t, 9.0, sg.Speed(Freighter))
|
||||
}
|
||||
|
||||
func TestBombingPower(t *testing.T) {
|
||||
BattleStation := game.ShipType{
|
||||
Name: "Battle_Station",
|
||||
Drive: 60.0,
|
||||
Armament: 3,
|
||||
Weapons: 30.0,
|
||||
Shields: 100.0,
|
||||
Cargo: 0.0,
|
||||
}
|
||||
sg := game.ShipGroup{
|
||||
Number: 1,
|
||||
Tech: map[game.Tech]game.Float{
|
||||
game.TechDrive: game.F(1.0),
|
||||
game.TechWeapons: game.F(1.0),
|
||||
game.TechShields: game.F(1.0),
|
||||
game.TechCargo: game.F(1.0),
|
||||
},
|
||||
}
|
||||
assert.Equal(t, 139.295, util.Fixed3(sg.BombingPower(&BattleStation)))
|
||||
sg.Number = 2
|
||||
assert.Equal(t, 278.590, util.Fixed3(sg.BombingPower(&BattleStation)))
|
||||
}
|
||||
|
||||
func TestDriveEffective(t *testing.T) {
|
||||
tc := []struct {
|
||||
driveShipType game.Float
|
||||
driveTech game.Float
|
||||
expectDriveEffective game.Float
|
||||
}{
|
||||
{1, 1, 1},
|
||||
{1, 2, 2},
|
||||
{2, 1, 2},
|
||||
{0, 1, 0},
|
||||
{0, 1.5, 0},
|
||||
{0, 10, 0},
|
||||
{1.5, 1.5, 2.25},
|
||||
}
|
||||
for i := range tc {
|
||||
someShip := game.ShipType{
|
||||
Drive: tc[i].driveShipType,
|
||||
Armament: rand.UintN(30) + 1,
|
||||
Weapons: game.F(rand.Float64()*30 + 1),
|
||||
Shields: game.F(rand.Float64()*100 + 1),
|
||||
Cargo: game.F(rand.Float64()*20 + 1),
|
||||
}
|
||||
sg := game.ShipGroup{
|
||||
Number: rand.UintN(4) + 1,
|
||||
Tech: map[game.Tech]game.Float{
|
||||
game.TechDrive: tc[i].driveTech,
|
||||
game.TechWeapons: game.F(rand.Float64()*5 + 1),
|
||||
game.TechShields: game.F(rand.Float64()*5 + 1),
|
||||
game.TechCargo: game.F(rand.Float64()*5 + 1),
|
||||
},
|
||||
}
|
||||
assert.Equal(t, tc[i].expectDriveEffective.F(), sg.DriveEffective(&someShip))
|
||||
}
|
||||
}
|
||||
|
||||
func TestShipGroupEqual(t *testing.T) {
|
||||
fleetId := uuid.New()
|
||||
someUUID := uuid.New()
|
||||
mat := game.CargoMaterial
|
||||
cap := game.CargoCapital
|
||||
left := &game.ShipGroup{
|
||||
ID: uuid.New(),
|
||||
Number: 1,
|
||||
|
||||
OwnerID: uuid.New(),
|
||||
TypeID: uuid.New(),
|
||||
FleetID: &fleetId,
|
||||
CargoType: &mat,
|
||||
Load: 123.45,
|
||||
Tech: map[game.Tech]game.Float{
|
||||
game.TechDrive: 1.0,
|
||||
game.TechWeapons: 1.0,
|
||||
game.TechShields: 1.0,
|
||||
game.TechCargo: 1.0,
|
||||
},
|
||||
}
|
||||
|
||||
// essential properties
|
||||
right := *left
|
||||
assert.True(t, left.Equal(right))
|
||||
|
||||
left.OwnerID = someUUID
|
||||
assert.False(t, left.Equal(right))
|
||||
|
||||
right = *left
|
||||
left.TypeID = someUUID
|
||||
assert.False(t, left.Equal(right))
|
||||
|
||||
right = *left
|
||||
left.FleetID = &someUUID
|
||||
assert.False(t, left.Equal(right))
|
||||
|
||||
right = *left
|
||||
left.FleetID = nil
|
||||
assert.False(t, left.Equal(right))
|
||||
|
||||
right = *left
|
||||
coord := game.Float(1)
|
||||
left.StateInSpace = &game.InSpace{
|
||||
Origin: 1,
|
||||
X: &coord,
|
||||
Y: &coord,
|
||||
}
|
||||
assert.False(t, left.Equal(right))
|
||||
|
||||
right = *left
|
||||
left.CargoType = &cap
|
||||
assert.False(t, left.Equal(right))
|
||||
|
||||
right = *left
|
||||
left.CargoType = nil
|
||||
assert.False(t, left.Equal(right))
|
||||
|
||||
right = *left
|
||||
left.Load = 45.123
|
||||
assert.False(t, left.Equal(right))
|
||||
|
||||
right = *left
|
||||
left.SetTechLevel(game.TechDrive, 1.1)
|
||||
assert.Equal(t, 1.1, left.TechLevel(game.TechDrive).F())
|
||||
assert.False(t, left.Equal(right))
|
||||
|
||||
right = *left
|
||||
left.SetTechLevel(game.TechWeapons, 1.1)
|
||||
assert.Equal(t, 1.1, left.TechLevel(game.TechWeapons).F())
|
||||
assert.False(t, left.Equal(right))
|
||||
|
||||
right = *left
|
||||
left.SetTechLevel(game.TechShields, 1.1)
|
||||
assert.Equal(t, 1.1, left.TechLevel(game.TechShields).F())
|
||||
assert.False(t, left.Equal(right))
|
||||
|
||||
right = *left
|
||||
left.SetTechLevel(game.TechCargo, 1.1)
|
||||
assert.Equal(t, 1.1, left.TechLevel(game.TechCargo).F())
|
||||
assert.False(t, left.Equal(right))
|
||||
|
||||
// non-essential properties
|
||||
right = *left
|
||||
|
||||
left.ID = uuid.New()
|
||||
assert.True(t, left.Equal(right))
|
||||
|
||||
// dirty hack to equalize loads
|
||||
left.Number = 5
|
||||
left.Load = game.F(float64(right.Load) / float64(right.Number) * float64(left.Number))
|
||||
assert.True(t, left.Equal(right))
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package game
|
||||
|
||||
type Map struct {
|
||||
Width uint32 `json:"width"`
|
||||
Height uint32 `json:"height"`
|
||||
Planet []Planet `json:"planets"`
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
package game
|
||||
|
||||
import (
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type Planet struct {
|
||||
X Float `json:"x"`
|
||||
Y Float `json:"y"`
|
||||
Number uint `json:"number"`
|
||||
Size Float `json:"size"`
|
||||
Name string `json:"name"`
|
||||
Owner *uuid.UUID `json:"owner,omitempty"`
|
||||
Resources Float `json:"resources"` // R - Ресурсы
|
||||
Capital Float `json:"capital"` // CAP $ - Запасы промышленности
|
||||
Material Float `json:"material"` // MAT M - Запасы ресурсов / сырьё
|
||||
Industry Float `json:"industry"` // I - Промышленность
|
||||
Population Float `json:"population"` // P - Население
|
||||
Colonists Float `json:"colonists"` // COL C - Количество колонистов
|
||||
Production Production `json:"production"`
|
||||
Route map[RouteType]uint `json:"route"`
|
||||
}
|
||||
|
||||
func (p *Planet) Own(v uuid.UUID) {
|
||||
if v == uuid.Nil {
|
||||
p.Free()
|
||||
return
|
||||
}
|
||||
if p.Owner == nil || *p.Owner != v {
|
||||
p.Production = ProductionCapital.AsType(uuid.Nil)
|
||||
}
|
||||
p.Owner = &v
|
||||
}
|
||||
|
||||
func (p *Planet) Free() {
|
||||
p.Owner = nil
|
||||
p.Production = ProductionNone.AsType(uuid.Nil)
|
||||
p.Colonists = 0.
|
||||
p.Population = 0.
|
||||
clear(p.Route)
|
||||
}
|
||||
|
||||
func (p *Planet) Wipe() {
|
||||
p.Free()
|
||||
p.Industry = 0
|
||||
p.Capital = 0
|
||||
}
|
||||
|
||||
func (p Planet) Owned() bool {
|
||||
return p.Owner != nil && *p.Owner != uuid.Nil
|
||||
}
|
||||
|
||||
func (p Planet) OwnedBy(v uuid.UUID) bool {
|
||||
if !p.Owned() {
|
||||
return false
|
||||
}
|
||||
return *p.Owner == v
|
||||
}
|
||||
|
||||
func (p *Planet) Mat(v float64) {
|
||||
p.Material = F(v)
|
||||
}
|
||||
|
||||
func (p *Planet) Pop(v float64) {
|
||||
p.Population = F(v)
|
||||
}
|
||||
|
||||
func (p *Planet) Col(v float64) {
|
||||
p.Colonists = F(v)
|
||||
}
|
||||
|
||||
func (p *Planet) Ind(v float64) {
|
||||
p.Industry = F(v)
|
||||
}
|
||||
|
||||
func (p *Planet) Cap(v float64) {
|
||||
p.Capital = F(v)
|
||||
}
|
||||
|
||||
func (p Planet) Votes() float64 {
|
||||
return p.Population.F() / 1000.
|
||||
}
|
||||
|
||||
// Производственный потенциал без учёта модернизации кораблей
|
||||
func (p Planet) ProductionCapacity() float64 {
|
||||
return PlanetProduction(p.Industry.F(), p.Population.F())
|
||||
}
|
||||
|
||||
func PlanetProduction(industry, population float64) float64 {
|
||||
return industry*0.75 + population*0.25
|
||||
}
|
||||
|
||||
func (p *Planet) ReleaseMaterial(shipMass float64) {
|
||||
if p.Production.Type != ProductionShip || p.Production.Progress == nil {
|
||||
panic("planet is not producing any ships")
|
||||
}
|
||||
p.Material = p.Material.Add(ProducedMaterial(shipMass, float64(*p.Production.Progress)))
|
||||
p.Production.Progress = new(Float)
|
||||
p.Production.ProdUsed = new(Float)
|
||||
}
|
||||
|
||||
func ProducedMaterial(shipMass, progress float64) float64 {
|
||||
return shipMass * progress
|
||||
}
|
||||
|
||||
// Производство промышленности
|
||||
func (p *Planet) ProduceIndustry(freeProduction float64) {
|
||||
prod := freeProduction / 5
|
||||
if float64(p.Material) < prod {
|
||||
prod = (freeProduction + float64(p.Material/p.Resources)) / (5. + 1./float64(p.Resources))
|
||||
p.Material = 0.
|
||||
} else {
|
||||
p.Material = p.Material.Add(-prod)
|
||||
}
|
||||
p.Industry = p.Industry.Add(prod)
|
||||
if p.Industry > p.Population {
|
||||
p.Capital += p.Industry - p.Population
|
||||
p.Industry = p.Population
|
||||
}
|
||||
}
|
||||
|
||||
// Производство материалов
|
||||
func (p *Planet) ProduceMaterial(freeProduction float64) {
|
||||
p.Material = p.Material.Add(freeProduction * p.Resources.F())
|
||||
}
|
||||
|
||||
// Автоматическое увеличение населения на каждом ходу
|
||||
func (p *Planet) ProducePopulation() {
|
||||
p.Population *= 1.08
|
||||
if p.Population > p.Size {
|
||||
p.Colonists += (p.Population - p.Size) / 8.
|
||||
p.Population = p.Size
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Planet) UnpackCapital() {
|
||||
if p.Capital == 0 {
|
||||
return
|
||||
}
|
||||
deficit := p.Population - p.Industry
|
||||
if deficit > p.Capital {
|
||||
deficit = p.Capital
|
||||
}
|
||||
p.Capital -= deficit
|
||||
p.Industry += deficit
|
||||
}
|
||||
|
||||
func (p *Planet) UnpackColonists() {
|
||||
if p.Colonists == 0 {
|
||||
return
|
||||
}
|
||||
deficit := (p.Size - p.Population) / 8
|
||||
if deficit > p.Colonists {
|
||||
deficit = p.Colonists
|
||||
}
|
||||
p.Colonists -= deficit
|
||||
p.Population += deficit * 8
|
||||
}
|
||||
|
||||
func UnloadColonists(p Planet, v float64) Planet {
|
||||
p.Pop(p.Population.F() + v*8)
|
||||
if p.Population > p.Size {
|
||||
p.Col(p.Colonists.F() + (p.Population.F()-p.Size.F())/8.)
|
||||
p.Pop(p.Size.F())
|
||||
}
|
||||
return p
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
package game_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/iliadenisov/galaxy/server/internal/model/game"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestPlanetProduction(t *testing.T) {
|
||||
assert.Equal(t, 1000., game.PlanetProduction(1000., 1000.))
|
||||
assert.Equal(t, 625., game.PlanetProduction(500., 1000.))
|
||||
assert.Equal(t, 750., game.PlanetProduction(1000., 0.))
|
||||
assert.Equal(t, 250., game.PlanetProduction(0., 1000.))
|
||||
}
|
||||
|
||||
func TestProduceIndustry(t *testing.T) {
|
||||
HW := &game.Planet{
|
||||
Size: 1000,
|
||||
Resources: 10,
|
||||
Population: 1000,
|
||||
Industry: 1000,
|
||||
}
|
||||
DW := &game.Planet{
|
||||
Size: 500,
|
||||
Resources: 10,
|
||||
Population: 500,
|
||||
Industry: 500,
|
||||
}
|
||||
HW.ProduceIndustry(HW.ProductionCapacity())
|
||||
assert.InDelta(t, 196.078, HW.Capital.F(), 0.0005)
|
||||
|
||||
HW.Capital = 0
|
||||
HW.Material = 200
|
||||
|
||||
HW.ProduceIndustry(HW.ProductionCapacity())
|
||||
assert.Equal(t, 200., HW.Capital.F())
|
||||
assert.Equal(t, 0., HW.Material.F())
|
||||
|
||||
DW.ProduceIndustry(DW.ProductionCapacity())
|
||||
assert.InDelta(t, 98.039, DW.Capital.F(), 0.0003)
|
||||
|
||||
DW.Capital = 0
|
||||
DW.Material = 100
|
||||
|
||||
DW.ProduceIndustry(DW.ProductionCapacity())
|
||||
assert.Equal(t, 100., DW.Capital.F())
|
||||
assert.Equal(t, 0., DW.Material.F())
|
||||
}
|
||||
|
||||
func TestProduceMaterial(t *testing.T) {
|
||||
HW := &game.Planet{
|
||||
Size: 1000,
|
||||
Resources: 10,
|
||||
Population: 1000,
|
||||
Industry: 1000,
|
||||
}
|
||||
assert.Equal(t, 0., HW.Material.F())
|
||||
|
||||
HW.ProduceMaterial(HW.ProductionCapacity())
|
||||
assert.Equal(t, 10000., HW.Material.F())
|
||||
|
||||
HW.Industry = 500
|
||||
HW.Population = 500
|
||||
HW.ProduceMaterial(HW.ProductionCapacity())
|
||||
assert.Equal(t, 15000., HW.Material.F())
|
||||
|
||||
HW.Population = 1000
|
||||
HW.ProduceMaterial(HW.ProductionCapacity())
|
||||
assert.Equal(t, 21250., HW.Material.F())
|
||||
}
|
||||
|
||||
func TestUnpackCapital(t *testing.T) {
|
||||
HW := &game.Planet{
|
||||
Size: 1000,
|
||||
Resources: 10,
|
||||
Population: 1000,
|
||||
Industry: 1000,
|
||||
}
|
||||
assert.Equal(t, 0., HW.Capital.F())
|
||||
|
||||
HW.UnpackCapital()
|
||||
assert.Equal(t, 1000., HW.Industry.F())
|
||||
assert.Equal(t, 0., HW.Capital.F())
|
||||
|
||||
HW.Capital = 123.
|
||||
HW.UnpackCapital()
|
||||
assert.Equal(t, 1000., HW.Industry.F())
|
||||
assert.Equal(t, 123., HW.Capital.F())
|
||||
|
||||
HW.Industry = 987.
|
||||
HW.UnpackCapital()
|
||||
assert.Equal(t, 1000., HW.Industry.F())
|
||||
assert.Equal(t, 110., HW.Capital.F())
|
||||
|
||||
HW.Population = 876.
|
||||
HW.Industry = 800.
|
||||
HW.UnpackCapital()
|
||||
assert.Equal(t, 876., HW.Population.F())
|
||||
assert.Equal(t, 876., HW.Industry.F())
|
||||
assert.Equal(t, 34., HW.Capital.F())
|
||||
}
|
||||
|
||||
func TestUnpackColonists(t *testing.T) {
|
||||
HW := &game.Planet{
|
||||
Size: 1000,
|
||||
Resources: 10,
|
||||
Population: 1000,
|
||||
Industry: 1000,
|
||||
}
|
||||
assert.Equal(t, 0., HW.Colonists.F())
|
||||
|
||||
HW.UnpackColonists()
|
||||
assert.Equal(t, 1000., HW.Population.F())
|
||||
assert.Equal(t, 0., HW.Colonists.F())
|
||||
|
||||
HW.Colonists = 1.05
|
||||
HW.UnpackColonists()
|
||||
assert.Equal(t, 1000., HW.Population.F())
|
||||
assert.Equal(t, 1.05, HW.Colonists.F())
|
||||
|
||||
HW.Population = 996.0
|
||||
HW.UnpackColonists()
|
||||
assert.Equal(t, 1000., HW.Population.F())
|
||||
assert.Equal(t, 0.55, HW.Colonists.F())
|
||||
|
||||
HW.Population = 0.0
|
||||
HW.UnpackColonists()
|
||||
assert.Equal(t, 4.4, HW.Population.F())
|
||||
assert.Equal(t, 0., HW.Colonists.F())
|
||||
}
|
||||
|
||||
func TestProducePopulation(t *testing.T) {
|
||||
HW := &game.Planet{
|
||||
Size: 1000,
|
||||
Resources: 10,
|
||||
Population: 500,
|
||||
Industry: 1000,
|
||||
}
|
||||
assert.Equal(t, 500., HW.Population.F())
|
||||
assert.Equal(t, 0., HW.Colonists.F())
|
||||
|
||||
HW.ProducePopulation()
|
||||
assert.Equal(t, 540., HW.Population.F())
|
||||
assert.Equal(t, 0., HW.Colonists.F())
|
||||
|
||||
HW.Population = 1000.
|
||||
HW.ProducePopulation()
|
||||
assert.Equal(t, 1000., HW.Population.F())
|
||||
assert.Equal(t, 10., HW.Colonists.F())
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package game
|
||||
|
||||
import (
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type ProductionType string
|
||||
|
||||
const (
|
||||
ProductionNone ProductionType = "-"
|
||||
ProductionMaterial ProductionType = "MAT" // Сырьё
|
||||
ProductionCapital ProductionType = "CAP" // Промышленность
|
||||
|
||||
ResearchDrive ProductionType = "DRIVE"
|
||||
ResearchWeapons ProductionType = "WEAPONS"
|
||||
ResearchShields ProductionType = "SHIELDS"
|
||||
ResearchCargo ProductionType = "CARGO"
|
||||
|
||||
ResearchScience ProductionType = "SCIENCE"
|
||||
ProductionShip ProductionType = "SHIP"
|
||||
)
|
||||
|
||||
type Production struct {
|
||||
Type ProductionType `json:"type"`
|
||||
SubjectID *uuid.UUID `json:"subjectId,omitempty"`
|
||||
Progress *Float `json:"progress,omitempty"`
|
||||
ProdUsed *Float `json:"prodUsed,omitempty"`
|
||||
}
|
||||
|
||||
func (p ProductionType) AsType(subject uuid.UUID) Production {
|
||||
switch p {
|
||||
case ResearchScience, ProductionShip:
|
||||
return Production{Type: p, SubjectID: &subject}
|
||||
default:
|
||||
return Production{Type: p, SubjectID: nil}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package game
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type Relation string
|
||||
|
||||
const (
|
||||
RelationWar Relation = "WAR"
|
||||
RelationPeace Relation = "PEACE"
|
||||
)
|
||||
|
||||
var (
|
||||
relationSet = map[string]Relation{
|
||||
strings.ToLower(RelationWar.String()): RelationWar,
|
||||
strings.ToLower(RelationPeace.String()): RelationPeace,
|
||||
}
|
||||
)
|
||||
|
||||
type Race struct {
|
||||
ID uuid.UUID `json:"id"`
|
||||
Name string `json:"name"`
|
||||
TTL uint `json:"ttl"`
|
||||
Extinct bool `json:"extinct"`
|
||||
Votes Float `json:"votes"`
|
||||
VoteFor uuid.UUID `json:"voteFor"`
|
||||
Relations []RaceRelation `json:"relations"`
|
||||
Tech TechSet `json:"tech"`
|
||||
Sciences []Science `json:"science,omitempty"`
|
||||
ShipTypes []ShipType `json:"shipType,omitempty"`
|
||||
}
|
||||
|
||||
func ParseRelation(v string) (Relation, bool) {
|
||||
if v, ok := relationSet[strings.ToLower(v)]; ok {
|
||||
return v, ok
|
||||
}
|
||||
return Relation(""), false
|
||||
}
|
||||
|
||||
func (r Relation) String() string {
|
||||
return string(r)
|
||||
}
|
||||
|
||||
type RaceRelation struct {
|
||||
RaceID uuid.UUID `json:"raceId"`
|
||||
Relation Relation `json:"relation"`
|
||||
}
|
||||
|
||||
func (r Race) TechLevel(t Tech) float64 {
|
||||
return r.Tech.Value(t)
|
||||
}
|
||||
|
||||
func (r Race) FlightDistance() float64 {
|
||||
return r.TechLevel(TechDrive) * 40
|
||||
}
|
||||
|
||||
func (r Race) VisibilityDistance() float64 {
|
||||
return r.TechLevel(TechDrive) * 30
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package game_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/iliadenisov/galaxy/server/internal/model/game"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var (
|
||||
ts = game.TechSet{
|
||||
game.TechDrive: 1.1,
|
||||
game.TechWeapons: 1.2,
|
||||
game.TechShields: 1.3,
|
||||
game.TechCargo: 1.4,
|
||||
}
|
||||
r = game.Race{
|
||||
Tech: ts,
|
||||
}
|
||||
)
|
||||
|
||||
func TestTechLevel(t *testing.T) {
|
||||
assert.Equal(t, 1.1, r.TechLevel(game.TechDrive))
|
||||
assert.Equal(t, 1.2, r.TechLevel(game.TechWeapons))
|
||||
assert.Equal(t, 1.3, r.TechLevel(game.TechShields))
|
||||
assert.Equal(t, 1.4, r.TechLevel(game.TechCargo))
|
||||
}
|
||||
|
||||
func TestFlightDistance(t *testing.T) {
|
||||
assert.Equal(t, 44., r.FlightDistance())
|
||||
}
|
||||
|
||||
func TestVisibilityDistance(t *testing.T) {
|
||||
assert.Equal(t, 33., r.VisibilityDistance())
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package game
|
||||
|
||||
import "strings"
|
||||
|
||||
type RouteType string
|
||||
|
||||
const (
|
||||
RouteMaterial RouteType = "MAT" // Сырьё
|
||||
RouteCapital RouteType = "CAP" // Промышленность
|
||||
RouteColonist RouteType = "COL" // Колонисты
|
||||
RouteEmpty RouteType = "EMP" // Пустые корабли
|
||||
)
|
||||
|
||||
var (
|
||||
RouteTypeSet map[string]RouteType = map[string]RouteType{
|
||||
strings.ToLower(RouteMaterial.String()): RouteMaterial,
|
||||
strings.ToLower(RouteCapital.String()): RouteCapital,
|
||||
strings.ToLower(RouteColonist.String()): RouteColonist,
|
||||
strings.ToLower(RouteEmpty.String()): RouteEmpty,
|
||||
}
|
||||
RouteToCargo map[RouteType]CargoType = map[RouteType]CargoType{
|
||||
RouteColonist: CargoColonist,
|
||||
RouteCapital: CargoCapital,
|
||||
RouteMaterial: CargoMaterial,
|
||||
}
|
||||
)
|
||||
|
||||
func (rt RouteType) Ref() *RouteType {
|
||||
return &rt
|
||||
}
|
||||
|
||||
func (rt RouteType) String() string {
|
||||
return string(rt)
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package game
|
||||
|
||||
import (
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type Science struct {
|
||||
ID uuid.UUID `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Drive Float `json:"drive"`
|
||||
Weapons Float `json:"weapons"`
|
||||
Shields Float `json:"shields"`
|
||||
Cargo Float `json:"cargo"`
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package game
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type ShipType struct {
|
||||
ID uuid.UUID `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Drive Float `json:"drive"`
|
||||
Armament uint `json:"armament"`
|
||||
Weapons Float `json:"weapons"`
|
||||
Shields Float `json:"shields"`
|
||||
Cargo Float `json:"cargo"`
|
||||
}
|
||||
|
||||
func (st ShipType) Equal(o ShipType) bool {
|
||||
return st.Drive == o.Drive &&
|
||||
st.Weapons == o.Weapons &&
|
||||
st.Armament == o.Armament &&
|
||||
st.Shields == o.Shields &&
|
||||
st.Cargo == o.Cargo
|
||||
}
|
||||
|
||||
func (st ShipType) BlockMass(t Tech) float64 {
|
||||
switch t {
|
||||
case TechDrive:
|
||||
return st.DriveBlockMass()
|
||||
case TechWeapons:
|
||||
return st.WeaponsBlockMass()
|
||||
case TechShields:
|
||||
return st.ShieldsBlockMass()
|
||||
case TechCargo:
|
||||
return st.CargoBlockMass()
|
||||
default:
|
||||
panic("BlockMass: unexpectec tech: " + t.String())
|
||||
}
|
||||
}
|
||||
|
||||
func (st ShipType) DriveBlockMass() float64 {
|
||||
return st.Drive.F()
|
||||
}
|
||||
|
||||
func (st ShipType) WeaponsBlockMass() float64 {
|
||||
if (st.Armament == 0 && st.Weapons != 0) || (st.Armament != 0 && st.Weapons == 0) {
|
||||
panic(fmt.Sprintf("ship class invalid design: A=%d W=%.03f", st.Armament, st.Weapons))
|
||||
}
|
||||
return float64(st.Armament+1) * (st.Weapons.F() / 2)
|
||||
}
|
||||
|
||||
func (st ShipType) ShieldsBlockMass() float64 {
|
||||
return st.Shields.F()
|
||||
}
|
||||
|
||||
func (st ShipType) CargoBlockMass() float64 {
|
||||
return st.Cargo.F()
|
||||
}
|
||||
|
||||
func (st ShipType) EmptyMass() float64 {
|
||||
shipMass := st.DriveBlockMass() + st.ShieldsBlockMass() + st.CargoBlockMass() + st.WeaponsBlockMass()
|
||||
return shipMass
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package game_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/iliadenisov/galaxy/server/internal/model/game"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestEmptyMass(t *testing.T) {
|
||||
Freighter := game.ShipType{
|
||||
Name: "Freighter",
|
||||
Drive: 8,
|
||||
Armament: 0,
|
||||
Weapons: 0,
|
||||
Shields: 2,
|
||||
Cargo: 10,
|
||||
}
|
||||
assert.Equal(t, 20., Freighter.EmptyMass())
|
||||
|
||||
Gunship := game.ShipType{
|
||||
Name: "Gunship",
|
||||
Drive: 4,
|
||||
Armament: 2,
|
||||
Weapons: 2,
|
||||
Shields: 4,
|
||||
Cargo: 0,
|
||||
}
|
||||
assert.Equal(t, 11., Gunship.EmptyMass())
|
||||
|
||||
Cruiser := game.ShipType{
|
||||
Name: "Cruiser",
|
||||
Drive: 15,
|
||||
Armament: 1,
|
||||
Weapons: 15,
|
||||
Shields: 15,
|
||||
Cargo: 0,
|
||||
}
|
||||
assert.Equal(t, 45., Cruiser.EmptyMass())
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package game
|
||||
|
||||
import "github.com/google/uuid"
|
||||
|
||||
type State struct {
|
||||
ID uuid.UUID
|
||||
Turn uint
|
||||
Stage uint
|
||||
Players []PlayerState
|
||||
}
|
||||
|
||||
type PlayerState struct {
|
||||
ID uuid.UUID
|
||||
Name string
|
||||
Extinct bool
|
||||
}
|
||||
Reference in New Issue
Block a user