test: battle multiple non-crossing enemies
This commit is contained in:
@@ -13,8 +13,8 @@ import (
|
||||
type Battle struct {
|
||||
ID uuid.UUID
|
||||
Planet uint
|
||||
observerGroups map[int]bool // True = In_Battle, False = Out_Battle
|
||||
initialNumbers map[int]uint // Initial number of ships in the group
|
||||
ObserverGroups map[int]bool // True = In_Battle, False = Out_Battle
|
||||
InitialNumbers map[int]uint // Initial number of ships in the group
|
||||
Protocol []BattleAction
|
||||
|
||||
shipAmmo map[int]uint
|
||||
@@ -95,21 +95,21 @@ func ProduceBattles(c *Cache) []*Battle {
|
||||
|
||||
result := make([]*Battle, 0)
|
||||
|
||||
// TODO: check this behavior:
|
||||
// Multiple battles on single planet shoul be produced as single battle too:
|
||||
// Multiple battles on single planet shoul be produced as single battle:
|
||||
// A <--> B
|
||||
// C <--> D
|
||||
// where: A in peace with [C, D], B in peace with [C, D], and so on.
|
||||
// where: [A] and [B] are mutual enemies, as well [C] and [D]
|
||||
for pl, observerGroups := range planetGroups {
|
||||
battleGroups := FilterBattleGroups(c, observerGroups)
|
||||
b := &Battle{
|
||||
Planet: pl,
|
||||
observerGroups: observerGroups,
|
||||
ObserverGroups: observerGroups,
|
||||
InitialNumbers: make(map[int]uint),
|
||||
attacker: make(map[int]map[int]float64),
|
||||
shipAmmo: make(map[int]uint),
|
||||
}
|
||||
for sgi := range observerGroups {
|
||||
b.initialNumbers[sgi] = c.ShipGroup(sgi).Number
|
||||
b.InitialNumbers[sgi] = c.ShipGroup(sgi).Number
|
||||
}
|
||||
|
||||
for i := range battleGroups {
|
||||
@@ -125,10 +125,13 @@ func ProduceBattles(c *Cache) []*Battle {
|
||||
})
|
||||
if len(opponents) > 0 {
|
||||
b.shipAmmo[attIdx] = c.ShipGroupShipClass(attIdx).Armament
|
||||
b.observerGroups[attIdx] = true
|
||||
b.ObserverGroups[attIdx] = true
|
||||
for _, defIdx := range opponents {
|
||||
if _, ok := b.attacker[attIdx][defIdx]; !ok {
|
||||
b.attacker[attIdx] = make(map[int]float64)
|
||||
}
|
||||
b.attacker[attIdx][defIdx] = cacheProbability[attIdx][defIdx]
|
||||
b.observerGroups[defIdx] = true
|
||||
b.ObserverGroups[defIdx] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -178,13 +181,13 @@ func SingleBattle(c *Cache, b *Battle) {
|
||||
if c.ShipGroup(defIdx).Number == 0 {
|
||||
delete(b.attacker, defIdx) // Eliminated group cant attack anyone
|
||||
for attIdx := range b.attacker {
|
||||
delete(b.attacker[attIdx], defIdx) // Attackers can't attack eliminated group anymore
|
||||
delete(b.attacker[attIdx], defIdx) // Other attackers can't attack eliminated group anymore
|
||||
if len(b.attacker[attIdx]) == 0 {
|
||||
delete(b.attacker, attIdx) // Remove attacker if he lost all opponents
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(b.attacker) == 0 {
|
||||
if len(b.attacker[attIdx]) == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package controller_test
|
||||
|
||||
import (
|
||||
"maps"
|
||||
"slices"
|
||||
"testing"
|
||||
|
||||
"github.com/iliadenisov/galaxy/internal/controller"
|
||||
@@ -124,3 +126,70 @@ func TestFilterBattleOpponents(t *testing.T) {
|
||||
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))
|
||||
|
||||
rel, err := g.Relation(Race_0.Name, race_C_name)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, game.RelationPeace, rel)
|
||||
|
||||
rel, err = g.Relation(Race_1.Name, race_C_name)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, game.RelationPeace, rel)
|
||||
|
||||
rel, err = g.Relation(Race_0.Name, race_D_name)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, game.RelationPeace, rel)
|
||||
|
||||
rel, err = g.Relation(Race_1.Name, race_D_name)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, game.RelationPeace, rel)
|
||||
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,20 +23,13 @@ func TransformBattle(c *Cache, b *Battle) *report.BattleReport {
|
||||
sg := c.ShipGroup(groupId)
|
||||
itemNumber := len(r.Ships)
|
||||
bg := &report.BattleReportGroup{
|
||||
// OwnerID: sg.OwnerID,
|
||||
// ClassArmament: shipClass.Armament,
|
||||
// ClassMass: report.F(shipClass.EmptyMass()),
|
||||
Race: c.g.Race[c.RaceIndex(sg.OwnerID)].Name,
|
||||
InBattle: inBattle,
|
||||
Number: b.initialNumbers[groupId],
|
||||
Number: b.InitialNumbers[groupId],
|
||||
NumberLeft: sg.Number,
|
||||
ClassName: shipClass.Name,
|
||||
LoadType: sg.CargoString(),
|
||||
LoadQuantity: report.F(sg.Load.F()),
|
||||
// DriveTech: report.F(sg.TechLevel(game.TechDrive).F()),
|
||||
// WeaponsTech: report.F(sg.TechLevel(game.TechWeapons).F()),
|
||||
// ShieldsTech: report.F(sg.TechLevel(game.TechShields).F()),
|
||||
// CargoTech: report.F(sg.TechLevel(game.TechCargo).F()),
|
||||
}
|
||||
for t, v := range sg.Tech {
|
||||
bg.Tech[t.String()] = report.F(v.F())
|
||||
@@ -77,7 +70,7 @@ func TransformBattle(c *Cache, b *Battle) *report.BattleReport {
|
||||
}
|
||||
}
|
||||
|
||||
for sgi, inBattle := range b.observerGroups {
|
||||
for sgi, inBattle := range b.ObserverGroups {
|
||||
if !inBattle {
|
||||
addShipGroup(sgi, false)
|
||||
}
|
||||
|
||||
@@ -8,6 +8,29 @@ import (
|
||||
"github.com/iliadenisov/galaxy/internal/model/game"
|
||||
)
|
||||
|
||||
func (c *Cache) AddRace(n string) (int, uuid.UUID) {
|
||||
id := uuid.New()
|
||||
r := &game.Race{
|
||||
ID: id,
|
||||
VoteFor: id,
|
||||
Name: n,
|
||||
Tech: game.NewTechSet(),
|
||||
Relations: make([]game.RaceRelation, len(c.g.Race)),
|
||||
}
|
||||
c.g.Race = append(c.g.Race, *r)
|
||||
for i := range c.g.Race {
|
||||
if c.g.Race[i].ID != id {
|
||||
c.g.Race[i].Relations = append(c.g.Race[i].Relations, game.RaceRelation{RaceID: id, Relation: game.RelationPeace})
|
||||
continue
|
||||
}
|
||||
for j := range c.g.Race[i].Relations {
|
||||
c.g.Race[i].Relations[j].RaceID = c.g.Race[j].ID
|
||||
c.g.Race[i].Relations[j].Relation = game.RelationPeace
|
||||
}
|
||||
}
|
||||
return len(c.g.Race) - 1, id
|
||||
}
|
||||
|
||||
func (c *Cache) Race(i int) game.Race {
|
||||
c.validateRaceIndex(i)
|
||||
return c.g.Race[i]
|
||||
@@ -102,3 +125,7 @@ func (c *Cache) VotesByRace() map[int]float64 {
|
||||
func VotingWinners(calc []*VoteGroup, gameVotes float64) []int {
|
||||
return votingWinners(calc, gameVotes)
|
||||
}
|
||||
|
||||
func (c *Cache) CreateShipsUnsafe_T(ri int, classID uuid.UUID, planet uint, quantity uint) {
|
||||
c.createShipsUnsafe(ri, classID, planet, quantity)
|
||||
}
|
||||
|
||||
@@ -61,12 +61,7 @@ func buildGameOnMap(races []string, m generator.Map) (*game.Game, error) {
|
||||
ID: raceID,
|
||||
Name: races[i],
|
||||
VoteFor: raceID,
|
||||
Tech: map[game.Tech]game.Float{
|
||||
game.TechDrive: 1,
|
||||
game.TechWeapons: 1,
|
||||
game.TechShields: 1,
|
||||
game.TechCargo: 1,
|
||||
},
|
||||
Tech: game.NewTechSet(),
|
||||
}
|
||||
gameMap.Planet = append(gameMap.Planet, NewPlanet(
|
||||
planetCount,
|
||||
|
||||
@@ -85,7 +85,7 @@ func MakeTurn(c *Controller, r Repo) error {
|
||||
b := battles[i]
|
||||
|
||||
observers := make(map[uuid.UUID]bool)
|
||||
for sgi := range b.observerGroups {
|
||||
for sgi := range b.ObserverGroups {
|
||||
observers[c.Cache.ShipGroup(sgi).OwnerID] = true
|
||||
}
|
||||
|
||||
|
||||
@@ -43,7 +43,6 @@ func (c *Cache) createShipsUnsafe(ri int, classID uuid.UUID, planet uint, quanti
|
||||
game.TechCargo: game.F(c.g.Race[ri].TechLevel(game.TechCargo)),
|
||||
},
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
// ShipGroup is a proxy func, nothing to cache
|
||||
|
||||
@@ -39,6 +39,15 @@ func (ts TechSet) Set(t Tech, v float64) TechSet {
|
||||
return m
|
||||
}
|
||||
|
||||
func NewTechSet() TechSet {
|
||||
return TechSet{
|
||||
TechDrive: 1.,
|
||||
TechWeapons: 1.,
|
||||
TechShields: 1.,
|
||||
TechCargo: 1.,
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: turn's incremental Version
|
||||
type Game struct {
|
||||
ID uuid.UUID `json:"id"`
|
||||
|
||||
@@ -16,18 +16,11 @@ type BattleReport struct {
|
||||
}
|
||||
|
||||
type BattleReportGroup struct {
|
||||
// OwnerID uuid.UUID `json:"-"` // make report: visible ship class
|
||||
InBattle bool `json:"inBattle"`
|
||||
Number uint `json:"num"`
|
||||
NumberLeft uint `json:"numLeft"`
|
||||
// ClassArmament uint `json:"-"` // make report: visible ship class
|
||||
// ClassMass Float `json:"-"` // make report: visible ship class
|
||||
LoadQuantity Float `json:"loadQuantity"`
|
||||
Tech map[string]Float `json:"tech"`
|
||||
// DriveTech Float `json:"drive"`
|
||||
// WeaponsTech Float `json:"weapons"`
|
||||
// ShieldsTech Float `json:"shields"`
|
||||
// CargoTech Float `json:"cargo"`
|
||||
Race string `json:"race"`
|
||||
ClassName string `json:"className"`
|
||||
LoadType string `json:"loadType"`
|
||||
|
||||
Reference in New Issue
Block a user