From 40b2cb27f6d909eea0db6e8c4d651e344c7a15cf Mon Sep 17 00:00:00 2001 From: Ilia Denisov Date: Mon, 19 Jan 2026 23:08:57 +0200 Subject: [PATCH] feat: bomb planets --- internal/controller/bombing.go | 128 ++++++++++++++++ internal/controller/bombing_test.go | 143 ++++++++++++++++++ internal/controller/controller_export_test.go | 8 + internal/controller/controller_test.go | 4 +- internal/controller/generate_turn.go | 3 + internal/controller/planet.go | 66 +++++++- internal/controller/planet_test.go | 50 ++++++ internal/controller/ship_group.go | 4 - internal/controller/ship_group_upgrade.go | 5 +- internal/model/game/group.go | 6 +- internal/model/game/group_test.go | 10 +- internal/model/game/planet.go | 34 ++++- internal/model/game/planet_test.go | 1 + 13 files changed, 440 insertions(+), 22 deletions(-) create mode 100644 internal/controller/bombing.go create mode 100644 internal/controller/bombing_test.go diff --git a/internal/controller/bombing.go b/internal/controller/bombing.go new file mode 100644 index 0000000..d80d037 --- /dev/null +++ b/internal/controller/bombing.go @@ -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 +} diff --git a/internal/controller/bombing_test.go b/internal/controller/bombing_test.go new file mode 100644 index 0000000..6ddbfa9 --- /dev/null +++ b/internal/controller/bombing_test.go @@ -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) + } + } +} diff --git a/internal/controller/controller_export_test.go b/internal/controller/controller_export_test.go index 829c80b..a790e1c 100644 --- a/internal/controller/controller_export_test.go +++ b/internal/controller/controller_export_test.go @@ -74,3 +74,11 @@ func (c *Cache) ListRouteEligibleGroupIds(pn uint) iter.Seq[int] { func (c *Cache) ListMoveableGroupIds() iter.Seq[int] { 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) +} diff --git a/internal/controller/controller_test.go b/internal/controller/controller_test.go index 0db83e2..54cf663 100644 --- a/internal/controller/controller_test.go +++ b/internal/controller/controller_test.go @@ -56,6 +56,8 @@ var ( Race_1_Freighter_idx = 1 Race_1_Cruiser_idx = 2 + Uninhabited_Planet_3_num uint = 3 + ShipType_Cruiser = "Cruiser" 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(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(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)), }, }, } diff --git a/internal/controller/generate_turn.go b/internal/controller/generate_turn.go index b190a8b..4d9a55c 100644 --- a/internal/controller/generate_turn.go +++ b/internal/controller/generate_turn.go @@ -27,6 +27,9 @@ func MakeTurn(c *Controller, r Repo, g *game.Game) error { // 06. Враждующие корабли снова вступают в схватку (это происходит после выхода из гиперпространства). battles = append(battles, ProduceBattles(c.Cache)...) + // 07. Корабли бомбят вражеские планеты. + _ = c.Cache.ProduceBombings() // TODO: store bombings for reports + /*** Last steps ***/ // Store battles diff --git a/internal/controller/planet.go b/internal/controller/planet.go index 86897aa..9272491 100644 --- a/internal/controller/planet.go +++ b/internal/controller/planet.go @@ -129,6 +129,36 @@ func (c *Cache) PlanetProduction(ri int, number int, prod game.ProductionType, s 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) { if c.cachePlanetByPlanetNumber == nil { c.cachePlanetByPlanetNumber = make(map[uint]*game.Planet) @@ -159,7 +189,7 @@ func (c *Cache) MustPlanetIndex(pn uint) int { } } -// Свободный производственный потенциал (L) +// Свободный "Производственный Потенциал" (L) // промышленность * 0.75 + население * 0.25 // за вычетом затрат, расходуемых в течение хода на модернизацию кораблей 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) { busyResources += sg.StateUpgrade.Cost() } - return game.PlanetProduction(p.Industry, p.Population) - busyResources + return p.ProductionCapacity() - busyResources } // Internal funcs @@ -184,3 +214,35 @@ func (c *Cache) putColonists(pn uint, v float64) { func (c *Cache) putMaterial(pn uint, v float64) { 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 +} diff --git a/internal/controller/planet_test.go b/internal/controller/planet_test.go index 63c068d..f497ea6 100644 --- a/internal/controller/planet_test.go +++ b/internal/controller/planet_test.go @@ -3,8 +3,11 @@ package controller_test import ( "testing" + "github.com/google/uuid" + "github.com/iliadenisov/galaxy/internal/controller" e "github.com/iliadenisov/galaxy/internal/error" "github.com/iliadenisov/galaxy/internal/model/game" + "github.com/iliadenisov/galaxy/internal/number" "github.com/stretchr/testify/assert" ) @@ -118,3 +121,50 @@ func TestPlanetProductionCapacity(t *testing.T) { c.UpgradeShipGroup(0, game.TechDrive, 1.6) 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) +} diff --git a/internal/controller/ship_group.go b/internal/controller/ship_group.go index 65ae52f..5411c38 100644 --- a/internal/controller/ship_group.go +++ b/internal/controller/ship_group.go @@ -166,13 +166,9 @@ func (c *Cache) JoinEqualGroups(ri int) { c.unsafeDeleteShipGroup(idx) } - c.invalidateShipGroupCache() - for i := range raceGroups { c.appendShipGroup(ri, &raceGroups[i]) } - - c.invalidateShipGroupCache() } func (c *Controller) BreakGroup(raceName string, groupIndex, quantity uint) error { diff --git a/internal/controller/ship_group_upgrade.go b/internal/controller/ship_group_upgrade.go index 1aaf7c7..bd0714f 100644 --- a/internal/controller/ship_group_upgrade.go +++ b/internal/controller/ship_group_upgrade.go @@ -74,8 +74,9 @@ func (c *Cache) UpgradeGroup(ri int, groupIndex uint, techInput string, limitShi productionCapacity := c.PlanetProductionCapacity(pl.Number) if c.ShipGroup(sgi).State() == game.StateUpgrade { - // to calculate actual capacity we must substract upgrade cost of selected group, if is upgrade state - productionCapacity -= c.ShipGroup(sgi).StateUpgrade.Cost() + // to calculate actual capacity we must "compensate" upgrade cost of selected group, if it is in upgrade state + // 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]) costForShip := uc.UpgradeCost(1) diff --git a/internal/model/game/group.go b/internal/model/game/group.go index f7f2754..12d9e14 100644 --- a/internal/model/game/group.go +++ b/internal/model/game/group.go @@ -5,7 +5,6 @@ import ( "math" "github.com/google/uuid" - "github.com/iliadenisov/galaxy/internal/number" ) 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 { - // return math.Sqrt(sg.Type.Weapons * sg.Weapons) - result := (math.Sqrt(st.Weapons*sg.TechLevel(TechWeapons))/10. + 1.) * + return (math.Sqrt(st.Weapons*sg.TechLevel(TechWeapons))/10. + 1.) * st.Weapons * sg.TechLevel(TechWeapons) * float64(st.Armament) * float64(sg.Number) - return number.Fixed3(result) } diff --git a/internal/model/game/group_test.go b/internal/model/game/group_test.go index b07d69b..d91f6d8 100644 --- a/internal/model/game/group_test.go +++ b/internal/model/game/group_test.go @@ -6,6 +6,7 @@ import ( "github.com/google/uuid" "github.com/iliadenisov/galaxy/internal/model/game" + "github.com/iliadenisov/galaxy/internal/number" "github.com/stretchr/testify/assert" ) @@ -104,8 +105,9 @@ func TestSpeed(t *testing.T) { } func TestBombingPower(t *testing.T) { - Gunship := game.ShipType{ + BattleStation := game.ShipType{ ShipTypeReport: game.ShipTypeReport{ + Name: "Battle_Station", Drive: 60.0, Armament: 3, Weapons: 30.0, @@ -122,9 +124,9 @@ func TestBombingPower(t *testing.T) { game.TechCargo: 1.0, }, } - expectedBombingPower := 139.295 - result := sg.BombingPower(&Gunship) - assert.Equal(t, expectedBombingPower, result) + assert.Equal(t, 139.295, number.Fixed3(sg.BombingPower(&BattleStation))) + sg.Number = 2 + assert.Equal(t, 278.590, number.Fixed3(sg.BombingPower(&BattleStation))) } func TestDriveEffective(t *testing.T) { diff --git a/internal/model/game/planet.go b/internal/model/game/planet.go index c29a6bb..d293cbc 100644 --- a/internal/model/game/planet.go +++ b/internal/model/game/planet.go @@ -41,9 +41,9 @@ type PlanetReportForeign struct { PlanetReport } -// TODO: delete func +// Производственный потенциал 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 { @@ -53,7 +53,7 @@ func PlanetProduction(industry, population float64) float64 { // Производство промышленности // TODO: test on real values func (p *Planet) IncreaseIndustry() { - prod := p.ProductionCapacity() / 5 + prod := p.ProductionCapacity() / 5. industryIncrement := math.Min(prod, p.Material) p.Industry += industryIncrement 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 func (p *Planet) IncreaseMaterial() { @@ -69,14 +81,28 @@ func (p *Planet) IncreaseMaterial() { } // Автоматическое увеличение населения на каждом ходу +// TODO: test func (p *Planet) IncreasePopulation() { p.Population *= 1.08 if p.Population > p.Size { - p.Colonists += (p.Population - p.Size) / 8 + p.Colonists += (p.Population - p.Size) / 8. 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 { p.Population += v * 8 if p.Population > p.Size { diff --git a/internal/model/game/planet_test.go b/internal/model/game/planet_test.go index 048bf8a..eb884e4 100644 --- a/internal/model/game/planet_test.go +++ b/internal/model/game/planet_test.go @@ -9,6 +9,7 @@ import ( 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.)) }