Files
galaxy-game/internal/controller/battle_test.go
T
2026-02-08 10:11:58 +02:00

185 lines
7.2 KiB
Go

package controller_test
import (
"maps"
"slices"
"testing"
"github.com/iliadenisov/galaxy/internal/controller"
"github.com/iliadenisov/galaxy/internal/model/game"
"github.com/stretchr/testify/assert"
)
var (
attacker = game.ShipType{
Name: "Attacker",
Drive: 8,
Armament: 1,
Weapons: 8,
Shields: 8,
Cargo: 0,
}
defender = game.ShipType{
Name: "Defender",
Drive: 1,
Armament: 1,
Weapons: 1,
Shields: 1,
Cargo: 0,
}
ship = game.ShipType{
Name: "Ship",
Drive: 10,
Armament: 1,
Weapons: 10,
Shields: 10,
Cargo: 0,
}
)
func TestDestructionProbability(t *testing.T) {
probability := controller.DestructionProbability(ship.Weapons.F(), 1, ship.Shields.F(), 1, ship.EmptyMass())
assert.Equal(t, .5, probability)
undefeatedShip := ship
undefeatedShip.Shields = 55
probability = controller.DestructionProbability(ship.Weapons.F(), 1, undefeatedShip.Shields.F(), 1, undefeatedShip.EmptyMass())
assert.LessOrEqual(t, probability, 0.)
disruptiveShip := ship
disruptiveShip.Weapons = 40
probability = controller.DestructionProbability(disruptiveShip.Weapons.F(), 1, ship.Shields.F(), 1, ship.EmptyMass())
assert.GreaterOrEqual(t, probability, 1.)
}
func TestEffectiveDefence(t *testing.T) {
assert.Equal(t, 10., controller.EffectiveDefence(ship.Shields.F(), 1, ship.EmptyMass()))
attackerEffectiveDefence := controller.EffectiveDefence(attacker.Shields.F(), 1, attacker.EmptyMass())
defenderEffectiveDefence := controller.EffectiveDefence(defender.Shields.F(), 1, defender.EmptyMass())
// attacker's effective shields must be 'just' 4 times greater than defender's
assert.InDelta(t, defenderEffectiveDefence*4, attackerEffectiveDefence, 0)
}
func TestCollectPlanetGroups(t *testing.T) {
c, _ := newCache()
assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Gunship, R0_Planet_0_num, 10)) // 1 #0
assert.NoError(t, c.CreateShips(Race_0_idx, ShipType_Cruiser, R0_Planet_0_num, 3)) // 2 #1
assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Gunship, R0_Planet_0_num, 3)) // 3 #2
c.ShipGroup(2).StateInSpace = &game.InSpace{Origin: 2, Range: 1.23} // 3 #2 -> In_Space
assert.NoError(t, c.CreateShips(Race_0_idx, ShipType_Cruiser, R0_Planet_0_num, 1)) // 4 #3
c.ShipGroup(3).Destination = R1_Planet_1_num // 4 #3 -> Planet_1
assert.NoError(t, c.CreateShips(Race_1_idx, Race_1_Gunship, R1_Planet_1_num, 15)) // 5 #4
c.ShipGroup(4).Destination = R0_Planet_0_num // 5 #4 -> Planet_0
planetGroups := controller.CollectPlanetGroups(c)
for pl := range planetGroups {
switch pl {
case R0_Planet_0_num:
assert.Equal(t, 3, len(planetGroups[pl]))
assert.Contains(t, planetGroups[pl], 0)
assert.Contains(t, planetGroups[pl], 1)
assert.Contains(t, planetGroups[pl], 4)
default:
assert.Fail(t, "planet #%d should not contain groups for battle", pl)
}
}
}
func TestFilterBattleOpponents(t *testing.T) {
c, _ := newCache()
assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Gunship, R0_Planet_0_num, 1)) // 0
assert.NoError(t, c.CreateShips(Race_0_idx, ShipType_Cruiser, R0_Planet_0_num, 1)) // 1
assert.NoError(t, c.CreateShips(Race_1_idx, Race_1_Gunship, R1_Planet_1_num, 15)) // 2
undefeatedShip := ship
undefeatedShip.Shields = 100
assert.NoError(t, c.CreateShipType(Race_1_idx, undefeatedShip.Name, undefeatedShip.Drive.F(), int(undefeatedShip.Armament), undefeatedShip.Weapons.F(), undefeatedShip.Shields.F(), undefeatedShip.Cargo.F()))
assert.NoError(t, c.CreateShips(Race_1_idx, undefeatedShip.Name, R1_Planet_1_num, 1)) // 3
cacheProbability := make(map[int]map[int]float64)
assert.False(t, controller.FilterBattleOpponents(c, 0, 2, cacheProbability))
assert.Contains(t, cacheProbability, 0)
assert.Contains(t, cacheProbability[0], 2)
assert.InDelta(t, 0.396222, cacheProbability[0][2], 0.0000001)
assert.False(t, controller.FilterBattleOpponents(c, 2, 0, cacheProbability))
assert.Contains(t, cacheProbability, 2)
assert.Contains(t, cacheProbability[2], 0)
assert.InDelta(t, 0.495, cacheProbability[2][0], 0.0001)
// Test: same owner
assert.True(t, controller.FilterBattleOpponents(c, 0, 0, cacheProbability))
assert.True(t, controller.FilterBattleOpponents(c, 0, 1, cacheProbability))
assert.True(t, controller.FilterBattleOpponents(c, 1, 0, cacheProbability))
// Test: reace reations
assert.NoError(t, c.UpdateRelation(Race_0_idx, Race_1_idx, game.RelationPeace))
assert.True(t, controller.FilterBattleOpponents(c, 0, 2, cacheProbability))
assert.True(t, controller.FilterBattleOpponents(c, 2, 0, cacheProbability))
assert.NoError(t, c.UpdateRelation(Race_0_idx, Race_1_idx, game.RelationWar))
assert.LessOrEqual(t, controller.DestructionProbability(Cruiser.Weapons.F(), 1, undefeatedShip.Shields.F(), 1, undefeatedShip.EmptyMass()), 0.)
assert.True(t, controller.FilterBattleOpponents(c, 1, 3, cacheProbability))
assert.NotContains(t, cacheProbability[1], 3)
}
func TestProduceBattles(t *testing.T) {
c, g := newCache()
race_C_name, race_D_name := "Race_C", "Race_D"
race_C_idx, _ := c.AddRace(race_C_name)
race_D_idx, _ := c.AddRace(race_D_name)
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))
assert.NoError(t, g.UpdateRelation(race_C_name, race_D_name, game.RelationWar))
assert.NoError(t, g.UpdateRelation(race_D_name, race_C_name, game.RelationWar))
assert.Equal(t, game.RelationPeace, c.Relation(Race_0_idx, race_C_idx))
assert.Equal(t, game.RelationPeace, c.Relation(Race_1_idx, race_C_idx))
assert.Equal(t, game.RelationPeace, c.Relation(Race_0_idx, race_D_idx))
assert.Equal(t, game.RelationPeace, c.Relation(Race_1_idx, race_D_idx))
// Race_0
assert.NoError(t, c.CreateShips(Race_0_idx, Race_0_Gunship, R0_Planet_0_num, 10))
// Race_1
c.CreateShipsUnsafe_T(Race_1_idx, c.MustShipClass(Race_1_idx, Race_1_Gunship).ID, R0_Planet_0_num, 11)
// Race_C
assert.NoError(t, c.CreateShipType(race_C_idx, Cruiser.Name, Cruiser.Drive.F(), int(Cruiser.Armament), Cruiser.Weapons.F(), Cruiser.Shields.F(), Cruiser.Cargo.F()))
c.CreateShipsUnsafe_T(race_C_idx, c.MustShipClass(race_C_idx, Cruiser.Name).ID, R0_Planet_0_num, 12)
// Race_D
assert.NoError(t, c.CreateShipType(race_D_idx, Cruiser.Name, Cruiser.Drive.F(), int(Cruiser.Armament), Cruiser.Weapons.F(), Cruiser.Shields.F(), Cruiser.Cargo.F()))
c.CreateShipsUnsafe_T(race_D_idx, c.MustShipClass(race_D_idx, Cruiser.Name).ID, R0_Planet_0_num, 13)
battle := controller.ProduceBattles(c)
assert.Len(t, battle, 1)
b := battle[0]
assert.Equal(t, R0_Planet_0_num, b.Planet)
assert.Len(t, b.ObserverGroups, 4)
assert.Len(t, b.InitialNumbers, 4)
assert.ElementsMatch(t, slices.Collect(maps.Keys(b.ObserverGroups)), slices.Collect(maps.Keys(b.InitialNumbers)))
assert.Equal(t, 10, int(b.InitialNumbers[0]))
assert.Equal(t, 11, int(b.InitialNumbers[1]))
assert.Equal(t, 12, int(b.InitialNumbers[2]))
assert.Equal(t, 13, int(b.InitialNumbers[3]))
if c.ShipGroup(0).Number == 0 {
assert.Greater(t, c.ShipGroup(1).Number, uint(0))
} else {
assert.Zero(t, c.ShipGroup(1).Number)
}
if c.ShipGroup(2).Number == 0 {
assert.Greater(t, c.ShipGroup(3).Number, uint(0))
} else {
assert.Zero(t, c.ShipGroup(3).Number)
}
}