/// <summary> /// Gets the surviving units of the army that won the combat /// </summary> /// <param name="combat_result"></param> /// <param name="combat_winner"></param> /// <returns></returns> public static int GetSurvivingUnits(LanchesterCombatResult combat_result, CombatWinner combat_winner) { double surviving_units = 0; try { switch (combat_winner) { case CombatWinner.Owned_Army: surviving_units = Math.Sqrt(Math.Abs(Math.Pow(combat_result.OwnedArmy_Count, 2) - ((combat_result.OwnedArmy_CombatEffectiveness / combat_result.EnemyArmy_CombatEffectiveness) * Math.Pow(combat_result.EnemyArmy_Count, 2)))); break; case CombatWinner.Draw: surviving_units = 0; break; case CombatWinner.Enemy_Army: surviving_units = Math.Sqrt(Math.Abs(Math.Pow(combat_result.EnemyArmy_Count, 2) - ((combat_result.EnemyArmy_CombatEffectiveness / combat_result.OwnedArmy_CombatEffectiveness) * Math.Pow(combat_result.OwnedArmy_Count, 2)))); break; } } catch (Exception ex) { Console.WriteLine($@"GetSurvivingUnits() -> {ex.Message}"); surviving_units = 0; } return(Convert.ToInt32(surviving_units)); }
/// <summary> /// Checks the <see cref="LanchesterCombatResult.OwnedArmy_RelativeCombatEffectiveness"/> /// and returns which army won in the combat /// </summary> /// <param name="combat_result"></param> /// <returns></returns> public static CombatWinner GetCombatWinner(LanchesterCombatResult combat_result) { CombatWinner combat_winner = CombatWinner.Draw; try { var relative_cardinality = (combat_result.OwnedArmy_Count / combat_result.EnemyArmy_Count); //Owned Army Loss if (relative_cardinality < combat_result.OwnedArmy_RelativeCombatEffectiveness) { combat_winner = CombatWinner.Enemy_Army; } //Owned Army Won else if (relative_cardinality > combat_result.OwnedArmy_RelativeCombatEffectiveness) { combat_winner = CombatWinner.Owned_Army; } //Draw else { combat_winner = CombatWinner.Draw; } } catch (Exception ex) { Console.WriteLine($@"GetCombatWinner() -> {ex.Message}"); combat_winner = CombatWinner.Draw; } return(combat_winner); }
/// <summary> /// <para> /// Lanchester-based Prediction Algorithm is the highest form of abstraction. It uses the /// Lanchester formula to compute the combat winner, including the survivor in the winning army. /// As a highest-abstraction prediction algorithm, it exchanges fine detail for faster computing. It /// does not consider the case that Health and Energy decreases over time, and that some skills /// have cooldown and duration. However, it considers that some units cannot target some /// other units such as air units. It also consider skills that deals damage/boost damage but not on the finest /// detail. Lastly, it also consider that the damage is reduced by the armor. /// </para> /// <para> /// This method returns a string of survived units from the winning army, but the cost worth /// of doing battle is always relative to the player. As such, if the opposing army won, the cost worth /// returned is negative since it represents as a loss. /// </para> /// </summary> /// <remarks> /// <para> /// While it is said to be the fastest out of the three algorithm, it is still dependent on the target policy. /// The probably running time is O(4n^2 + 2n). /// </para> /// <para> /// In summary, the Lanchester-based prediction considers and does not consider the following: /// Considers: /// <list type="bullet"> /// <item>True Current Damage (Current Damage applied with opposing Armor)</item> /// <item>Restrictions in targeting unit (Some unit cannot target air unit, and vice versa)</item> /// <item>Current Health</item> /// <item>Skills that deals damage and gives boost to the Current damage</item> /// </list> /// Does not Consider: /// <list type="bullet"> /// <item>Decreasing Health</item> /// <item>Decreasing Energy</item> /// <item>Skills with Cooldown and Duration</item> /// <item>Skills that affects Health/Energy</item> /// <item>AoE Damage / Chaining Damage</item> /// <item>Transforming Units</item> /// <item>Time and its related properties to battle</item> /// <item>Damage with bonus to target type</item> /// </list> /// </para> /// </remarks> /// <param name="target_policy"></param> /// <returns></returns> public Tuple <string, CostWorth> LanchesterBasedPrediction(TargetPolicy target_policy) { Tuple <string, CostWorth> battle_result = null; try { //Create a copy of the units var owned_units = _owned_units.GetDeepCopy(); var enemy_units = _enemy_units.GetDeepCopy(); //Set the targets for each army //This will get the true damage of unit, since Damage with Armor is applied switch (target_policy) { case TargetPolicy.Random: RandomBasedTargetPolicy(ref owned_units, enemy_units); RandomBasedTargetPolicy(ref enemy_units, owned_units); break; case TargetPolicy.Priority: PriorityBasedTargetPolicy(ref owned_units, enemy_units); PriorityBasedTargetPolicy(ref enemy_units, owned_units); break; case TargetPolicy.Resource: ResourceBasedTargetPolicy(ref owned_units, enemy_units); ResourceBasedTargetPolicy(ref enemy_units, owned_units); break; } //Compute the battle output var combat_result = new LanchesterCombatResult(owned_units, enemy_units); var combat_winner = LanchesterCombatResult.GetCombatWinner(combat_result); var combat_survivor = LanchesterCombatResult.GetSurvivingUnits(combat_result, combat_winner); //Get the surviving units of the winner Army survived_units = default(Army); switch (combat_winner) { case LanchesterCombatResult.CombatWinner.Owned_Army: //Based on policy, pick which units will survive switch (target_policy) { case TargetPolicy.Random: survived_units = owned_units.RandomlyTake(combat_survivor); break; case TargetPolicy.Priority: survived_units = owned_units.PriorityTake(combat_survivor); break; case TargetPolicy.Resource: survived_units = owned_units.ResourceTake(combat_survivor); break; } battle_result = new Tuple <string, CostWorth>(survived_units.ToString(), survived_units.GetValueOfArmy()); break; case LanchesterCombatResult.CombatWinner.Draw: battle_result = new Tuple <string, CostWorth>(@"""""", default(CostWorth)); break; case LanchesterCombatResult.CombatWinner.Enemy_Army: //Based on policy, pick which units will survive switch (target_policy) { case TargetPolicy.Random: survived_units = enemy_units.RandomlyTake(combat_survivor); break; case TargetPolicy.Priority: survived_units = enemy_units.PriorityTake(combat_survivor); break; case TargetPolicy.Resource: survived_units = enemy_units.ResourceTake(combat_survivor); break; } battle_result = new Tuple <string, CostWorth>(survived_units.ToString(), !survived_units.GetValueOfArmy()); break; } } catch (ArgumentNullException ex) { Console.WriteLine($@"LanchesterBasedPrediction() [{target_policy.ToString()}] -> {ex.Message}"); throw new Exception(""); } catch (Exception ex) { Console.WriteLine($@"LanchesterBasedPrediction() -> {ex.Message}"); battle_result = null; } return(battle_result); }