diff --git a/internal/controller/battle.go b/internal/controller/battle.go index fe1d551..86b75c6 100644 --- a/internal/controller/battle.go +++ b/internal/controller/battle.go @@ -1,6 +1,7 @@ package controller import ( + "iter" "maps" "math" "math/rand/v2" @@ -84,15 +85,6 @@ func FilterBattleOpponents(c *Cache, attIdx, defIdx int, cacheProbability map[in } -/* -FIXME: Сражение происходит раундами - -Случайным образом из всех участвующих в сражении вооруженных кораблей выбирается один... -...Затем вновь случайным образом выбирается корабль, который будет стрелять... -Так продолжается до тех пор, пока не отстреляются все корабли. Если после -этого еще остались корабли и с той и с другой стороны, все повторяется с -самого начала (происходит еще один цикл сражения). -*/ func ProduceBattles(c *Cache) []*Battle { cacheProbability := make(map[int]map[int]float64) defer func() { clear(cacheProbability) }() @@ -159,45 +151,65 @@ func ProduceBattles(c *Cache) []*Battle { } func SingleBattle(c *Cache, b *Battle) { + roundShooters := make(map[int]bool) for len(b.attacker) > 0 { - attackers := slices.Collect(maps.Keys(b.attacker)) - attIdx := attackers[rand.IntN(len(attackers))] + // список участников раунда + clear(roundShooters) + for sgi := range b.attacker { + roundShooters[sgi] = true + } - for range b.shipAmmo[attIdx] { - defenders := slices.Collect(maps.Keys(b.attacker[attIdx])) - defIdx := defenders[rand.IntN(len(defenders))] - destroyed := false + for len(roundShooters) > 0 { + // attacke group id among round participants + attIdx := randomValue(maps.Keys(roundShooters)) + delete(roundShooters, attIdx) - probability := b.attacker[attIdx][defIdx] - switch { - case probability >= 1: - destroyed = true - case probability > 0: - destroyed = rand.Float64() >= probability - default: - panic("SingleBattle: probability unexpected: value <= 0") - } + for range b.shipAmmo[attIdx] { + // defender group id among all attacker's opponents + defIdx := randomValue(maps.Keys(b.attacker[attIdx])) - b.Protocol = append(b.Protocol, BattleAction{ - Attacker: attIdx, - Defender: defIdx, - Destroyed: destroyed, - }) + destroyed := false - if destroyed { - c.ShipGroupDestroyItem(defIdx) - } - 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) // 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 + probability := b.attacker[attIdx][defIdx] + switch { + case probability >= 1: + destroyed = true + case probability > 0: + destroyed = rand.Float64() >= probability + default: + panic("SingleBattle: probability unexpected: value <= 0") + } + + b.Protocol = append(b.Protocol, BattleAction{ + Attacker: attIdx, + Defender: defIdx, + Destroyed: destroyed, + }) + + if destroyed { + c.ShipGroupDestroyItem(defIdx) + } + if c.ShipGroup(defIdx).Number == 0 { + // Eliminated group cant attack anyone + delete(b.attacker, defIdx) + delete(roundShooters, defIdx) + + for attIdx := range b.attacker { + // Other attackers can't attack eliminated group anymore + delete(b.attacker[attIdx], defIdx) + + if len(b.attacker[attIdx]) == 0 { + // Remove attacker if he lost all opponents + delete(b.attacker, attIdx) + delete(roundShooters, attIdx) + } } } - } - if len(b.attacker[attIdx]) == 0 { - break + + // When attacker has no more targets to shoot - break its ammo cycle + if len(b.attacker[attIdx]) == 0 { + break + } } } } @@ -212,3 +224,8 @@ func DestructionProbability(attWeapons, attWeaponsTech, defShields, defShiledsTe func EffectiveDefence(defShields, defShiledsTech, defFullMass float64) float64 { return defShields * defShiledsTech / math.Pow(defFullMass, 1./3.) * math.Pow(30., 1./3.) } + +func randomValue(v iter.Seq[int]) int { + ids := slices.Collect(v) + return ids[rand.IntN(len(ids))] +} diff --git a/internal/controller/generate_turn.go b/internal/controller/generate_turn.go index 0929f9a..d6b0f73 100644 --- a/internal/controller/generate_turn.go +++ b/internal/controller/generate_turn.go @@ -14,11 +14,11 @@ func (c *Controller) MakeTurn() error { c.Cache.g.Turn += 1 c.Cache.g.Stage = 0 + // TODO: Выполнение приказов + // 01. Вышедшие расы удаляются из списка участвующих рас перед началом просчета очередного хода c.Cache.TurnWipeExtinctRaces() - // TODO: передача кораблей между расами - // 02. Товары загружаются на корабли, находящиеся в начале грузовых маршрутов, и корабли входят в гиперпространство (но ещё не полетели) c.Cache.SendRoutedGroups()