feat: bomb planets
This commit is contained in:
@@ -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
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
|
|||||||
@@ -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)),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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,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) {
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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.))
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user