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