feat: bomb planets

This commit is contained in:
Ilia Denisov
2026-01-19 23:08:57 +02:00
parent bd9db26ef4
commit 40b2cb27f6
13 changed files with 440 additions and 22 deletions
+128
View File
@@ -0,0 +1,128 @@
package controller
import (
"github.com/google/uuid"
"github.com/iliadenisov/galaxy/internal/model/game"
)
type BombingReport struct {
Planets []BombingPlanetReport `json:"planets"`
}
type BombingPlanetReport struct {
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.
for _, i := range groups {
sg := c.ShipGroup(i)
st := c.ShipGroupShipClass(i)
attackPower += sg.BombingPower(st)
}
r := &BombingPlanetReport{
Planet: p.Name,
Number: p.Number,
Owner: c.g.Race[c.RaceIndex(p.Owner)].Name,
Attacker: c.g.Race[ri].Name,
Production: c.PlanetProductionDisplayName(p.Number),
Industry: p.Industry,
Population: p.Population,
Colonists: p.Colonists,
Capital: p.Capital,
Material: p.Material,
AttackPower: attackPower,
}
bombPlanet(p, attackPower)
r.Wiped = p.Population == 0
return *r
}
func (c *Cache) ProduceBombings() []BombingPlanetReport {
report := make([]BombingPlanetReport, 0)
for pn, enemies := range c.collectBombingGroups() {
p := c.MustPlanet(pn)
for ri, groups := range enemies {
br := c.bombingReport(p, ri, groups)
report = append(report, br)
if br.Wiped {
break
}
}
if p.Population == 0 {
// TODO: what aboul colonists left on planet?
p.Owner = uuid.Nil
clear(p.Route)
} else {
// Если на планете остались также и колонисты, то они превращаются в население,
// а накопленная промышленность возмещает потери производства.
p.UnpackCOLtoPOP()
p.UnpackCAPtoIND()
}
}
return report
}
func bombPlanet(p *game.Planet, power float64) {
// Уничтожается население и колонисты в количестве равном [суммарной] мощности бомбардировки
if power > p.Population {
p.Population = 0
} else {
p.Population -= power
}
if power > p.Colonists {
p.Colonists = 0
} else {
p.Colonists -= power
}
// Такое же количество промышленности превращается в сырье
if power > p.Industry {
p.Material += p.Industry
p.Industry = 0
} else {
p.Material += power
p.Industry -= power
}
}
// [planet_num] -> [enemy_race_id] -> []group_id
func (c *Cache) collectBombingGroups() map[uint]map[int][]int {
result := make(map[uint]map[int][]int)
for i := range c.ShipGroupsIndex() {
sg := c.ShipGroup(i)
if sg.State() != game.StateInOrbit {
continue
}
st := c.ShipGroupShipClass(i)
if st.WeaponsBlockMass() == 0 {
continue
}
p := c.MustPlanet(sg.Destination)
if p.Owner == sg.OwnerID || p.Owner == uuid.Nil {
continue
}
r1 := c.RaceIndex(sg.OwnerID)
r2 := c.RaceIndex(p.Owner)
if c.Relation(r1, r2) == game.RelationPeace {
continue
}
// add result
if _, ok := result[p.Number]; !ok {
result[p.Number] = make(map[int][]int)
}
result[p.Number][r1] = append(result[p.Number][r1], i)
}
return result
}
+143
View File
@@ -0,0 +1,143 @@
package controller_test
import (
"testing"
"github.com/google/uuid"
"github.com/iliadenisov/galaxy/internal/controller"
"github.com/iliadenisov/galaxy/internal/model/game"
"github.com/stretchr/testify/assert"
)
func TestBombPlanet(t *testing.T) {
p := controller.NewPlanet(0, "Planet_0", uuid.New(), 1, 1, 1000, 300, 200, 10, game.ResearchDrive.AsType(uuid.Nil))
(&p).Colonists = 100.
assert.Equal(t, 0., p.Material)
controller.BombPlanet(&p, 55.)
assert.Equal(t, 245., p.Population)
assert.Equal(t, 45., p.Colonists)
assert.Equal(t, 145., p.Industry)
assert.Equal(t, 55., p.Material)
controller.BombPlanet(&p, 56.)
assert.Equal(t, 189., p.Population)
assert.Equal(t, 0., p.Colonists)
assert.Equal(t, 89., p.Industry)
assert.Equal(t, 111., p.Material)
controller.BombPlanet(&p, 200.)
assert.Equal(t, 0., p.Population)
assert.Equal(t, 0., p.Colonists)
assert.Equal(t, 0., p.Industry)
assert.Equal(t, 200., p.Material)
}
func TestCollectBombingGroups(t *testing.T) {
c, g := newCache()
assert.NoError(t, g.UpdateRelation(Race_0.Name, Race_1.Name, game.RelationWar))
assert.NoError(t, g.UpdateRelation(Race_1.Name, Race_0.Name, game.RelationWar))
// 1: idx = 0 / Ready to bomb: Race_1/Planet_1
assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Gunship, R0_Planet_0_num, 2)) // bombs
c.ShipGroup(0).Destination = R1_Planet_1_num
// 2: idx = 1 / Ready to bomb: Race_0/Planet_2
assert.NoError(t, c.CreateShips(Race_1_idx, Race_1_Gunship, R1_Planet_1_num, 3)) // bombs
c.ShipGroup(1).Destination = R0_Planet_2_num
// 3: idx = 2 / In_Space
assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Gunship, R0_Planet_0_num, 1))
c.ShipGroup(2).StateInSpace = &game.InSpace{
Origin: 2,
Range: 31.337,
}
// 4: idx = 3 / Has no Ammo
assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Freighter, R0_Planet_0_num, 1))
c.ShipGroup(3).Destination = R1_Planet_1_num
// 5: idx = 4 / On it's own planet
assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Gunship, R0_Planet_0_num, 1))
// 6: idx = 5 / On uninhabited planet
assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Gunship, R0_Planet_0_num, 2))
c.ShipGroup(5).Destination = Uninhabited_Planet_3_num
bg := c.CollectBombingGroups()
assert.Len(t, bg, 2)
assert.Contains(t, bg, R1_Planet_1_num)
assert.Contains(t, bg, R0_Planet_2_num)
assert.Len(t, bg[R1_Planet_1_num], 1)
assert.Contains(t, bg[R1_Planet_1_num], Race_0_idx)
assert.Len(t, bg[R0_Planet_2_num], 1)
assert.Contains(t, bg[R0_Planet_2_num], Race_1_idx)
assert.Len(t, bg[R1_Planet_1_num][Race_0_idx], 1)
assert.Equal(t, 0, bg[R1_Planet_1_num][Race_0_idx][0])
assert.Len(t, bg[R0_Planet_2_num][Race_1_idx], 1)
assert.Equal(t, 1, bg[R0_Planet_2_num][Race_1_idx][0])
// remove bombings from Race_1
assert.NoError(t, g.UpdateRelation(Race_1.Name, Race_0.Name, game.RelationPeace))
bg = c.CollectBombingGroups()
assert.Len(t, bg, 1)
assert.Contains(t, bg, R1_Planet_1_num)
assert.Len(t, bg[R1_Planet_1_num], 1)
assert.Contains(t, bg[R1_Planet_1_num], Race_0_idx)
assert.Len(t, bg[R1_Planet_1_num][Race_0_idx], 1)
assert.Equal(t, 0, bg[R1_Planet_1_num][Race_0_idx][0])
}
func TestProduceBombings(t *testing.T) {
c, g := newCache()
assert.NoError(t, g.UpdateRelation(Race_0.Name, Race_1.Name, game.RelationWar))
assert.NoError(t, g.UpdateRelation(Race_1.Name, Race_0.Name, game.RelationWar))
// 1: idx = 0 / Bombs on: Race_1/Planet_1
assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Gunship, R0_Planet_0_num, 3))
c.ShipGroup(0).Destination = R1_Planet_1_num
// 2: idx = 1 / Bombs on: Race_1/Planet_1
assert.NoError(t, c.CreateShips(Race_0_idx, ShipType_Cruiser, R0_Planet_0_num, 7))
c.ShipGroup(1).Destination = R1_Planet_1_num
// 3: idx = 2 / Bombs on: Race_0/Planet_2
assert.NoError(t, c.CreateShips(Race_1_idx, Race_1_Gunship, R1_Planet_1_num, 1))
c.ShipGroup(2).Destination = R0_Planet_2_num
c.MustPlanet(R0_Planet_2_num).Population = 500
assert.NoError(t, g.SetRoute(Race_0.Name, "CAP", R0_Planet_2_num, R0_Planet_0_num))
assert.NotEmpty(t, c.MustPlanet(R0_Planet_2_num).Route)
assert.NoError(t, g.SetRoute(Race_1.Name, "EMP", R1_Planet_1_num, R0_Planet_2_num))
assert.NotEmpty(t, c.MustPlanet(R1_Planet_1_num).Route)
reports := c.ProduceBombings()
assert.Len(t, reports, 2)
for _, r := range reports {
switch pn := r.Number; pn {
case R1_Planet_1_num:
assert.Equal(t, Race_1.Name, r.Owner)
assert.Equal(t, Race_0.Name, r.Attacker)
assert.InDelta(t, 697.857, r.AttackPower, 0.0003)
assert.True(t, r.Wiped)
assert.Equal(t, uuid.Nil, c.MustPlanet(pn).Owner)
assert.Empty(t, c.MustPlanet(pn).Route)
assert.Equal(t, 0., c.MustPlanet(pn).Population)
case R0_Planet_2_num:
assert.Equal(t, Race_0.Name, r.Owner)
assert.Equal(t, Race_1.Name, r.Attacker)
assert.InDelta(t, 358.85596, r.AttackPower, 0.000001)
assert.False(t, r.Wiped)
assert.Equal(t, Race_0_ID, c.MustPlanet(pn).Owner)
assert.NotEmpty(t, c.MustPlanet(pn).Route)
assert.InDelta(t, 500.-358.85596, c.MustPlanet(pn).Population, 0.000001)
}
}
}
@@ -74,3 +74,11 @@ func (c *Cache) ListRouteEligibleGroupIds(pn uint) iter.Seq[int] {
func (c *Cache) ListMoveableGroupIds() iter.Seq[int] { func (c *Cache) ListMoveableGroupIds() iter.Seq[int] {
return c.listMoveableGroupIds() return c.listMoveableGroupIds()
} }
func (c *Cache) CollectBombingGroups() map[uint]map[int][]int {
return c.collectBombingGroups()
}
func BombPlanet(p *game.Planet, power float64) {
bombPlanet(p, power)
}
+3 -1
View File
@@ -56,6 +56,8 @@ var (
Race_1_Freighter_idx = 1 Race_1_Freighter_idx = 1
Race_1_Cruiser_idx = 2 Race_1_Cruiser_idx = 2
Uninhabited_Planet_3_num uint = 3
ShipType_Cruiser = "Cruiser" ShipType_Cruiser = "Cruiser"
Cruiser = game.ShipType{ Cruiser = game.ShipType{
@@ -97,7 +99,7 @@ func newGame() *game.Game {
controller.NewPlanet(R0_Planet_0_num, "Planet_0", Race_0.ID, 1, 1, 100, 100, 100, 0, game.ProductionNone.AsType(uuid.Nil)), controller.NewPlanet(R0_Planet_0_num, "Planet_0", Race_0.ID, 1, 1, 100, 100, 100, 0, game.ProductionNone.AsType(uuid.Nil)),
controller.NewPlanet(R1_Planet_1_num, "Planet_1", Race_1.ID, 2, 2, 100, 0, 0, 0, game.ProductionNone.AsType(uuid.Nil)), controller.NewPlanet(R1_Planet_1_num, "Planet_1", Race_1.ID, 2, 2, 100, 0, 0, 0, game.ProductionNone.AsType(uuid.Nil)),
controller.NewPlanet(R0_Planet_2_num, "Planet_2", Race_0.ID, 3, 3, 100, 0, 0, 0, game.ProductionNone.AsType(uuid.Nil)), controller.NewPlanet(R0_Planet_2_num, "Planet_2", Race_0.ID, 3, 3, 100, 0, 0, 0, game.ProductionNone.AsType(uuid.Nil)),
controller.NewPlanet(3, "Planet_3", uuid.Nil, 500, 500, 100, 0, 0, 0, game.ProductionNone.AsType(uuid.Nil)), controller.NewPlanet(Uninhabited_Planet_3_num, "Planet_3", uuid.Nil, 500, 500, 100, 0, 0, 0, game.ProductionNone.AsType(uuid.Nil)),
}, },
}, },
} }
+3
View File
@@ -27,6 +27,9 @@ func MakeTurn(c *Controller, r Repo, g *game.Game) error {
// 06. Враждующие корабли снова вступают в схватку (это происходит после выхода из гиперпространства). // 06. Враждующие корабли снова вступают в схватку (это происходит после выхода из гиперпространства).
battles = append(battles, ProduceBattles(c.Cache)...) battles = append(battles, ProduceBattles(c.Cache)...)
// 07. Корабли бомбят вражеские планеты.
_ = c.Cache.ProduceBombings() // TODO: store bombings for reports
/*** Last steps ***/ /*** Last steps ***/
// Store battles // Store battles
+64 -2
View File
@@ -129,6 +129,36 @@ func (c *Cache) PlanetProduction(ri int, number int, prod game.ProductionType, s
return nil return nil
} }
// TODO: test
func (c *Cache) PlanetProductionDisplayName(pn uint) string {
p := c.MustPlanet(pn)
ri := c.RaceIndex(p.Owner)
switch pt := p.Production.Type; pt {
case game.ResearchDrive:
return "Drive"
case game.ResearchWeapons:
return "Weapons"
case game.ResearchShields:
return "Shields"
case game.ResearchCargo:
return "Cargo"
case game.ProductionMaterial:
return "Material"
case game.ProductionCapital:
return "Capital"
case game.ProductionShip:
return c.MustShipType(ri, *p.Production.SubjectID).Name
case game.ResearchScience:
i := slices.IndexFunc(c.g.Race[ri].Sciences, func(sc game.Science) bool { return sc.ID == *p.Production.SubjectID })
if i < 0 {
panic("researching science not found")
}
return c.g.Race[ri].Sciences[i].Name
default:
return string(pt)
}
}
func (c *Cache) Planet(planetNumber uint) (*game.Planet, bool) { func (c *Cache) Planet(planetNumber uint) (*game.Planet, bool) {
if c.cachePlanetByPlanetNumber == nil { if c.cachePlanetByPlanetNumber == nil {
c.cachePlanetByPlanetNumber = make(map[uint]*game.Planet) c.cachePlanetByPlanetNumber = make(map[uint]*game.Planet)
@@ -159,7 +189,7 @@ func (c *Cache) MustPlanetIndex(pn uint) int {
} }
} }
// Свободный производственный потенциал (L) // Свободный роизводственный Потенциал" (L)
// промышленность * 0.75 + население * 0.25 // промышленность * 0.75 + население * 0.25
// за вычетом затрат, расходуемых в течение хода на модернизацию кораблей // за вычетом затрат, расходуемых в течение хода на модернизацию кораблей
func (c *Cache) PlanetProductionCapacity(planetNumber uint) float64 { func (c *Cache) PlanetProductionCapacity(planetNumber uint) float64 {
@@ -168,7 +198,7 @@ func (c *Cache) PlanetProductionCapacity(planetNumber uint) float64 {
for sg := range c.shipGroupsInUpgrade(p.Number) { for sg := range c.shipGroupsInUpgrade(p.Number) {
busyResources += sg.StateUpgrade.Cost() busyResources += sg.StateUpgrade.Cost()
} }
return game.PlanetProduction(p.Industry, p.Population) - busyResources return p.ProductionCapacity() - busyResources
} }
// Internal funcs // Internal funcs
@@ -184,3 +214,35 @@ func (c *Cache) putColonists(pn uint, v float64) {
func (c *Cache) putMaterial(pn uint, v float64) { func (c *Cache) putMaterial(pn uint, v float64) {
c.MustPlanet(pn).Material = v c.MustPlanet(pn).Material = v
} }
func ProduceShip(p *game.Planet, shipMass float64) int {
productionAvailable := p.ProductionCapacity()
if productionAvailable <= 0 {
return 0
}
CAP_perShip := shipMass / p.Resources
productionForMass := shipMass * 10.
ships := 0
flZero := 0.
p.Production.Progress = &flZero
for productionAvailable > 0 {
var productionExtraCAP float64
if CAP_deficit := p.Capital - CAP_perShip; CAP_deficit < 0 {
productionExtraCAP = -CAP_deficit
}
ship_prod := productionExtraCAP + productionForMass
if productionAvailable >= ship_prod {
productionAvailable -= ship_prod
p.Capital = p.Capital - (CAP_perShip - productionExtraCAP)
ships++
} else {
progress := productionAvailable / ship_prod
productionAvailable -= ship_prod * progress
p.Production.Progress = &progress
break
}
}
return ships
}
+50
View File
@@ -3,8 +3,11 @@ package controller_test
import ( import (
"testing" "testing"
"github.com/google/uuid"
"github.com/iliadenisov/galaxy/internal/controller"
e "github.com/iliadenisov/galaxy/internal/error" e "github.com/iliadenisov/galaxy/internal/error"
"github.com/iliadenisov/galaxy/internal/model/game" "github.com/iliadenisov/galaxy/internal/model/game"
"github.com/iliadenisov/galaxy/internal/number"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@@ -118,3 +121,50 @@ func TestPlanetProductionCapacity(t *testing.T) {
c.UpgradeShipGroup(0, game.TechDrive, 1.6) c.UpgradeShipGroup(0, game.TechDrive, 1.6)
assert.Equal(t, 53.125, c.PlanetProductionCapacity(R0_Planet_0_num)) assert.Equal(t, 53.125, c.PlanetProductionCapacity(R0_Planet_0_num))
} }
func TestProduceShip(t *testing.T) {
Drone := game.ShipType{
ShipTypeReport: game.ShipTypeReport{
Name: "Drone",
Drive: 1,
Armament: 0,
Weapons: 0,
Shields: 0,
Cargo: 0,
},
}
BattleShip := game.ShipType{
ShipTypeReport: game.ShipTypeReport{
Name: "BattleShip",
Drive: 25,
Armament: 1,
Weapons: 30,
Shields: 35,
Cargo: 0,
},
}
p := controller.NewPlanet(0, "Planet_0", uuid.New(), 1, 1, 1000, 1000, 1000, 10, game.ProductionShip.AsType(uuid.Nil))
r := controller.ProduceShip(&p, Drone.EmptyMass())
assert.Equal(t, 99, r)
assert.InDelta(t, 0.0099, *p.Production.Progress, 0.000001)
(&p).Production = game.ProductionShip.AsType(uuid.Nil)
(&p).Capital = 10.
r = controller.ProduceShip(&p, Drone.EmptyMass())
assert.Equal(t, 100, r)
assert.Equal(t, 0., *p.Production.Progress)
assert.Equal(t, 0., number.Fixed3(p.Capital)) // TODO: zero CAP is not actual zero after series of decrements
(&p).Production = game.ProductionShip.AsType(uuid.Nil)
(&p).Capital = 0.
r = controller.ProduceShip(&p, BattleShip.EmptyMass())
assert.Equal(t, 1, r)
assert.InDelta(t, 0.1, *p.Production.Progress, 0.001)
(&p).Production = game.ProductionShip.AsType(uuid.Nil)
(&p).Capital = 20.
r = controller.ProduceShip(&p, BattleShip.EmptyMass())
assert.Equal(t, 1, r)
assert.Equal(t, 1./9., *p.Production.Progress)
}
-4
View File
@@ -166,13 +166,9 @@ func (c *Cache) JoinEqualGroups(ri int) {
c.unsafeDeleteShipGroup(idx) c.unsafeDeleteShipGroup(idx)
} }
c.invalidateShipGroupCache()
for i := range raceGroups { for i := range raceGroups {
c.appendShipGroup(ri, &raceGroups[i]) c.appendShipGroup(ri, &raceGroups[i])
} }
c.invalidateShipGroupCache()
} }
func (c *Controller) BreakGroup(raceName string, groupIndex, quantity uint) error { func (c *Controller) BreakGroup(raceName string, groupIndex, quantity uint) error {
+3 -2
View File
@@ -74,8 +74,9 @@ func (c *Cache) UpgradeGroup(ri int, groupIndex uint, techInput string, limitShi
productionCapacity := c.PlanetProductionCapacity(pl.Number) productionCapacity := c.PlanetProductionCapacity(pl.Number)
if c.ShipGroup(sgi).State() == game.StateUpgrade { if c.ShipGroup(sgi).State() == game.StateUpgrade {
// to calculate actual capacity we must substract upgrade cost of selected group, if is upgrade state // to calculate actual capacity we must "compensate" upgrade cost of selected group, if it is in upgrade state
productionCapacity -= c.ShipGroup(sgi).StateUpgrade.Cost() // TODO: this is not tested
productionCapacity += c.ShipGroup(sgi).StateUpgrade.Cost()
} }
uc := game.GroupUpgradeCost(*(c.ShipGroup(sgi)), *st, targetLevel[game.TechDrive], targetLevel[game.TechWeapons], targetLevel[game.TechShields], targetLevel[game.TechCargo]) uc := game.GroupUpgradeCost(*(c.ShipGroup(sgi)), *st, targetLevel[game.TechDrive], targetLevel[game.TechWeapons], targetLevel[game.TechShields], targetLevel[game.TechCargo])
costForShip := uc.UpgradeCost(1) costForShip := uc.UpgradeCost(1)
+1 -5
View File
@@ -5,7 +5,6 @@ import (
"math" "math"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/iliadenisov/galaxy/internal/number"
) )
type CargoType string type CargoType string
@@ -227,13 +226,10 @@ func (sg ShipGroup) UpgradeCargoCost(st *ShipType, cargo float64) float64 {
} }
// Мощность бомбардировки // Мощность бомбардировки
// TODO: maybe rounding must be done only for display?
func (sg ShipGroup) BombingPower(st *ShipType) float64 { func (sg ShipGroup) BombingPower(st *ShipType) float64 {
// return math.Sqrt(sg.Type.Weapons * sg.Weapons) return (math.Sqrt(st.Weapons*sg.TechLevel(TechWeapons))/10. + 1.) *
result := (math.Sqrt(st.Weapons*sg.TechLevel(TechWeapons))/10. + 1.) *
st.Weapons * st.Weapons *
sg.TechLevel(TechWeapons) * sg.TechLevel(TechWeapons) *
float64(st.Armament) * float64(st.Armament) *
float64(sg.Number) float64(sg.Number)
return number.Fixed3(result)
} }
+6 -4
View File
@@ -6,6 +6,7 @@ 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/number"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@@ -104,8 +105,9 @@ func TestSpeed(t *testing.T) {
} }
func TestBombingPower(t *testing.T) { func TestBombingPower(t *testing.T) {
Gunship := game.ShipType{ BattleStation := game.ShipType{
ShipTypeReport: game.ShipTypeReport{ ShipTypeReport: game.ShipTypeReport{
Name: "Battle_Station",
Drive: 60.0, Drive: 60.0,
Armament: 3, Armament: 3,
Weapons: 30.0, Weapons: 30.0,
@@ -122,9 +124,9 @@ func TestBombingPower(t *testing.T) {
game.TechCargo: 1.0, game.TechCargo: 1.0,
}, },
} }
expectedBombingPower := 139.295 assert.Equal(t, 139.295, number.Fixed3(sg.BombingPower(&BattleStation)))
result := sg.BombingPower(&Gunship) sg.Number = 2
assert.Equal(t, expectedBombingPower, result) assert.Equal(t, 278.590, number.Fixed3(sg.BombingPower(&BattleStation)))
} }
func TestDriveEffective(t *testing.T) { func TestDriveEffective(t *testing.T) {
+30 -4
View File
@@ -41,9 +41,9 @@ type PlanetReportForeign struct {
PlanetReport PlanetReport
} }
// TODO: delete func // Производственный потенциал
func (p Planet) ProductionCapacity() float64 { func (p Planet) ProductionCapacity() float64 {
return p.Industry*0.75 + p.Population*0.25 return PlanetProduction(p.Industry, p.Population)
} }
func PlanetProduction(industry, population float64) float64 { func PlanetProduction(industry, population float64) float64 {
@@ -53,7 +53,7 @@ func PlanetProduction(industry, population float64) float64 {
// Производство промышленности // Производство промышленности
// TODO: test on real values // TODO: test on real values
func (p *Planet) IncreaseIndustry() { func (p *Planet) IncreaseIndustry() {
prod := p.ProductionCapacity() / 5 prod := p.ProductionCapacity() / 5.
industryIncrement := math.Min(prod, p.Material) industryIncrement := math.Min(prod, p.Material)
p.Industry += industryIncrement p.Industry += industryIncrement
if p.Industry > p.Population { if p.Industry > p.Population {
@@ -62,6 +62,18 @@ func (p *Planet) IncreaseIndustry() {
} }
} }
func (p *Planet) UnpackCAPtoIND() {
if p.Capital == 0 {
return
}
cap := p.Population - p.Industry
if cap > p.Capital {
cap = p.Capital
}
p.Capital -= cap
p.Industry += cap
}
// Производство материалов // Производство материалов
// TODO: test on real values // TODO: test on real values
func (p *Planet) IncreaseMaterial() { func (p *Planet) IncreaseMaterial() {
@@ -69,14 +81,28 @@ func (p *Planet) IncreaseMaterial() {
} }
// Автоматическое увеличение населения на каждом ходу // Автоматическое увеличение населения на каждом ходу
// TODO: test
func (p *Planet) IncreasePopulation() { func (p *Planet) IncreasePopulation() {
p.Population *= 1.08 p.Population *= 1.08
if p.Population > p.Size { if p.Population > p.Size {
p.Colonists += (p.Population - p.Size) / 8 p.Colonists += (p.Population - p.Size) / 8.
p.Population = p.Size p.Population = p.Size
} }
} }
func (p *Planet) UnpackCOLtoPOP() {
if p.Colonists < 1 {
return
}
maxCOL := uint((p.Size - p.Population) / 8.)
if float64(maxCOL) > p.Colonists {
maxCOL = uint(p.Colonists)
}
maxCOL = uint(float64(maxCOL) - math.Mod(float64(maxCOL), 8.))
p.Colonists -= float64(maxCOL)
p.Population += float64(maxCOL) * 8
}
func UnloadColonists(p Planet, v float64) Planet { func UnloadColonists(p Planet, v float64) Planet {
p.Population += v * 8 p.Population += v * 8
if p.Population > p.Size { if p.Population > p.Size {
+1
View File
@@ -9,6 +9,7 @@ import (
func TestPlanetProduction(t *testing.T) { func TestPlanetProduction(t *testing.T) {
assert.Equal(t, 1000., game.PlanetProduction(1000., 1000.)) 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, 750., game.PlanetProduction(1000., 0.))
assert.Equal(t, 250., game.PlanetProduction(0., 1000.)) assert.Equal(t, 250., game.PlanetProduction(0., 1000.))
} }