package game import ( "fmt" "math" "math/rand/v2" "slices" ) type Battle struct { Planet uint Groups []int // ShipGroup indexes BattleReport BattleReport } type BattleReport struct { BattleAction []BattleAction } type BattleAction struct { Attacker int Defenter int Destroyed bool } type BattleOpponent struct { RaceIndex int ShipGroupIndex int ShipType ShipType } func ProduceBattles(g *Game) error { battleOnPlanet := Battle{} _ = battleOnPlanet return nil } func SingleBattle(g *Game, b Battle) { attacker := SelectAttackShip(g, b.Groups) for shots := range attacker.ShipType.Armament { // groupsCopy := slices.Clone(b.Groups) // groupsWithoutAttacker := append(groupsCopy[:attackerIdx], groupsCopy[attackerIdx+1:]...) _ = SelectDefendShip(g, slices.Clone(b.Groups), attacker) _ = shots } } func SelectAttackShip(g *Game, battleGroups []int) BattleOpponent { sgi := rand.IntN(len(battleGroups)) if sgi > len(g.ShipGroups)-1 { panic("SelectAttackShip: battleGroups is bigger than game's ship groups") } sg := g.ShipGroups[sgi] ri := slices.IndexFunc(g.Race, func(r Race) bool { return r.ID == sg.OwnerID }) if ri < 0 { panic(fmt.Sprintf("SelectAttackShip: ship group #%v owner race not found by ID=%v", sg.Index, sg.OwnerID)) } st, ok := ShipClass(g, ri, sg.TypeID) if !ok { panic(fmt.Sprintf("SelectAttackShip: ship class not found for race=%q group=%v", g.Race[ri].Name, sg.Index)) } if st.Weapons == 0 || st.Armament == 0 { panic(fmt.Sprintf("SelectAttackShip: ship_class=%q of race=%q has no weapons for attack", st.Name, g.Race[ri].Name)) } return BattleOpponent{ RaceIndex: ri, ShipGroupIndex: sgi, ShipType: st, } } func SelectDefendShip(g *Game, battleGroups []int, attacker BattleOpponent) BattleOpponent { enemyGroups := FilterAttackingPretendent(g, attacker.RaceIndex, battleGroups) sgi := rand.IntN(len(enemyGroups)) if sgi > len(g.ShipGroups)-1 { panic("SelectDefendShip: battleGroups is bigger than game's ship groups") } sg := g.ShipGroups[sgi] ri := slices.IndexFunc(g.Race, func(r Race) bool { return r.ID == sg.OwnerID }) if ri < 0 { panic(fmt.Sprintf("SelectDefendShip: ship group #%v owner race not found by ID=%v", sg.Index, sg.OwnerID)) } st, ok := ShipClass(g, ri, sg.TypeID) if !ok { panic(fmt.Sprintf("SelectDefendShip: ship class not found for race=%q group=%v", g.Race[ri].Name, sg.Index)) } return BattleOpponent{ RaceIndex: ri, ShipGroupIndex: sgi, ShipType: st, } } // attackerIdx - attacker race index func FilterAttackingPretendent(g *Game, attackerIdx int, battleGroups []int) []int { result := make([]int, 0) for sgi := range battleGroups { sg := g.ShipGroups[sgi] enemyIdx := slices.IndexFunc(g.Race, func(r Race) bool { return r.ID == sg.OwnerID }) if enemyIdx < 0 { panic(fmt.Sprintf("FilterAttackingPretendent: ship group #%v owner race not found by ID=%v", sg.Index, sg.OwnerID)) } rel, err := g.relationInternal(attackerIdx, enemyIdx) if err != nil { panic(err) } // attacker race will be in peace with itself, so attacker ships will be filtered out as well if rel.Relation == RelationPeace { continue } result = append(result, sgi) } return result } func DestroyProbability(attWeapons, attWeaponsTech, defShields, defShiledsTech, defFullMass float64) float64 { effAttack := attWeapons * attWeaponsTech effDefence := EffectiveDefence(defShields, defShiledsTech, defFullMass) return (math.Log10(effAttack/effDefence)/math.Log10(4) + 1) / 2 } func EffectiveDefence(defShields, defShiledsTech, defFullMass float64) float64 { return defShields * defShiledsTech / math.Pow(defFullMass, 1./3.) * math.Pow(30., 1./3.) }