package controller_test import ( "testing" "galaxy/game/internal/controller" "galaxy/game/internal/model/game" "github.com/google/uuid" "github.com/stretchr/testify/assert" ) func TestBombPlanet(t *testing.T) { id := uuid.New() p := controller.NewPlanet(0, "Planet_0", &id, 1, 1, 1000, 300, 200, 10, game.ResearchDrive.AsType(uuid.Nil)) (&p).Colonists = 100. assert.Equal(t, 0., p.Material.F()) controller.BombPlanet(&p, 55.) assert.Equal(t, 245., p.Population.F()) assert.Equal(t, 45., p.Colonists.F()) assert.Equal(t, 145., p.Industry.F()) assert.Equal(t, 55., p.Material.F()) controller.BombPlanet(&p, 56.) assert.Equal(t, 189., p.Population.F()) assert.Equal(t, 0., p.Colonists.F()) assert.Equal(t, 89., p.Industry.F()) assert.Equal(t, 111., p.Material.F()) controller.BombPlanet(&p, 200.) assert.Equal(t, 0., p.Population.F()) assert.Equal(t, 0., p.Colonists.F()) assert.Equal(t, 0., p.Industry.F()) assert.Equal(t, 200., p.Material.F()) } func TestCollectBombingGroups(t *testing.T) { c, g := newCache() assert.NoError(t, g.RaceRelation(Race_0.Name, Race_1.Name, game.RelationWar.String())) assert.NoError(t, g.RaceRelation(Race_1.Name, Race_0.Name, game.RelationWar.String())) // 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 = &InSpace // 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.RaceRelation(Race_1.Name, Race_0.Name, game.RelationPeace.String())) 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.RaceRelation(Race_0.Name, Race_1.Name, game.RelationWar.String())) assert.NoError(t, g.RaceRelation(Race_1.Name, Race_0.Name, game.RelationWar.String())) // 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.PlanetRouteSet(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.PlanetRouteSet(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 _, b := range reports { assert.NotEqual(t, uuid.Nil, b.ID) switch pn := b.Number; pn { case R1_Planet_1_num: assert.Equal(t, Race_1.Name, b.Owner) assert.Equal(t, Race_0.Name, b.Attacker) assert.InDelta(t, 697.857, b.AttackPower.F(), 0.0003) assert.True(t, b.Wiped) assert.False(t, c.MustPlanet(pn).Owned()) assert.Empty(t, c.MustPlanet(pn).Route) assert.Equal(t, 0., c.MustPlanet(pn).Population.F()) case R0_Planet_2_num: assert.Equal(t, Race_0.Name, b.Owner) assert.Equal(t, Race_1.Name, b.Attacker) assert.InDelta(t, 358.856, b.AttackPower.F(), 0.0001) assert.False(t, b.Wiped) assert.True(t, c.MustPlanet(pn).OwnedBy(Race_0_ID)) assert.NotEmpty(t, c.MustPlanet(pn).Route) assert.InDelta(t, 500.-358.85596, c.MustPlanet(pn).Population.F(), 0.000001) } } } // TestBombingOrderByPower checks that attacking races are accounted from the // strongest bombing power downwards (the report order), not in the random map // iteration order the engine used before. func TestBombingOrderByPower(t *testing.T) { c, g := newCache() assert.NoError(t, g.RaceRelation(Race_1.Name, Race_0.Name, game.RelationWar.String())) weakIdx, _ := c.AddRace("Weakling") assert.NoError(t, c.UpdateRelation(weakIdx, Race_0_idx, game.RelationWar)) assert.NoError(t, c.ShipClassCreate(weakIdx, "Pebble", 1, 1, 1, 1, 0)) // Planet_0 (Race_0) survives both attacks. c.MustPlanet(R0_Planet_0_num).Population = 1000 // Strong: one Race_1 gunship (~358.9 power); weak: one Pebble (~1.1 power). c.CreateShipsUnsafe_T(Race_1_idx, c.MustShipClass(Race_1_idx, Race_1_Gunship).ID, R0_Planet_0_num, 1) c.CreateShipsUnsafe_T(weakIdx, c.MustShipClass(weakIdx, "Pebble").ID, R0_Planet_0_num, 1) reports := c.ProduceBombings() assert.Len(t, reports, 2) assert.Equal(t, Race_1.Name, reports[0].Attacker, "strongest attacker comes first") assert.Equal(t, "Weakling", reports[1].Attacker) assert.Greater(t, reports[0].AttackPower.F(), reports[1].AttackPower.F()) } // TestBombingWipeZeroesIndustry checks that a planet bombed to extinction loses // its industry but keeps its material and capital stockpiles for the next // colonist (rules "Бомбардировка планет"). func TestBombingWipeZeroesIndustry(t *testing.T) { c, _ := newCache() bomberIdx, _ := c.AddRace("Bomber") assert.NoError(t, c.UpdateRelation(bomberIdx, Race_0_idx, game.RelationWar)) // Bombing power ~106.5 (W=60, A=1, weapons tech 1.0): wipes pop 50 while // only partly converting industry, so the leftover industry is observable. assert.NoError(t, c.ShipClassCreate(bomberIdx, "Reaper", 1, 1, 60, 1, 0)) p := c.MustPlanet(R0_Planet_0_num) p.Population = 50 p.Industry = 200 p.Capital = 30 p.Material = 20 p.Colonists = 0 c.CreateShipsUnsafe_T(bomberIdx, c.MustShipClass(bomberIdx, "Reaper").ID, R0_Planet_0_num, 1) reports := c.ProduceBombings() assert.Len(t, reports, 1) assert.True(t, reports[0].Wiped) pl := c.MustPlanet(R0_Planet_0_num) assert.False(t, pl.Owned()) assert.Equal(t, 0., pl.Population.F()) assert.Equal(t, 0., pl.Industry.F(), "industry collapses on wipe") assert.Equal(t, 30., pl.Capital.F(), "capital stockpile survives") assert.InDelta(t, 126.476, pl.Material.F(), 0.01, "material keeps the converted industry") }