1. We have added a Gift Upgrades feature that allows you to gift an account upgrade to another member, just in time for the holiday season. You can see the gift option when going to the Account Upgrades screen, or on any user profile screen.
    Dismiss Notice

Smoothing the combat curve

Discussion in 'Civ4 - Fall from Heaven' started by xanaqui42, Sep 5, 2007.

  1. xanaqui42

    xanaqui42 King

    Joined:
    Sep 5, 2006
    Messages:
    780
    For those who haven't been following this discussion, we've been discussing attempting to smooth out the "jump points" in combat. I'd likely merge these changes into a later version of Smarter Orcs, where they could be tested and useful variables chosen.

    I'm unclear as to what R, A, and D are. I'm pretty certain I follow your point though, since I'm using the above equation to compare it to the below ones.
    I think that we're largely on the same page. Here's my thought for interface; each of these would be accessible via XML:

    COMBAT_SMOOTH_ABSOLUTE_HUNDREDTHS (default value: 0) This would allow a variation of +/-
    COMBAT_SMOOTH_ABSOLUTE_HUNDREDTHS/100 points of damage.

    COMBAT_SMOOTH_RELATIVE_HUNDREDTHS (default value: 0) This would allow a variation of * +/-
    COMBAT_SMOOTH_ABSOLUTE_HUNDREDTHS/100 points of damage. This is applied before COMBAT_SMOOTH_ABSOLUTE_HUNDREDTHS .

    COMBAT_SMOOTH_ABSOLUTE_DICE (default value: 1) If COMBAT_SMOOTH_ABSOLUTE_HUNDREDTHS is set to a non-default values, then this indicates the "number of dice" used to generate the value in the given range. The "dice" would be of largely equal size.

    COMBAT_SMOOTH_RELATIVE_DICE (default value: 1) If COMBAT_SMOOTH_RELATIVE_HUNDREDTHS is set to a non-default values, then this indicates the "number of dice" used to generate the value in the given range. The "dice" would be of largely equal size.

    So, for a few examples (any non-described value would be a default):

    COMBAT_SMOOTH_ABSOLUTE_HUNDREDTHS = 100

    If the base damage were 20, then the actual damage would be 19..21, with an "equal" chance of each result 19.00, 19.01, 19.02,...20.98, 20.99, 21.00

    COMBAT_SMOOTH_ABSOLUTE_DICE = 2
    COMBAT_SMOOTH_ABSOLUTE_HUNDREDTHS = 100


    If the base damage were 20, then the actual damage would be 19..21, with the following probabilities of values:
    1/10302 : 19.00, 21.00
    2/10302 : 19.01, 20.99
    3/10302 : 19.02, 20.98
    .
    .
    .

    COMBAT_SMOOTH_RELATIVE_HUNDREDTHS = 10


    If the base damage were 20, then the actual damage would be 18..22, with an "equal" chance of each result 18.00, 18.01, 18.02, ... , 21.98, 21.99, 22.00

    COMBAT_SMOOTH_RELATIVE_DICE = 2
    COMBAT_SMOOTH_RELATIVE_HUNDREDTHS = 10

    If the base damage were 20, then the actual damage would be 18..22, with the following probabilities of values:
    1/40602 : 18.00, 22.00
    2/40602 : 18.01, 21.99
    3/40602 : 18.02, 21.98
    .
    .
    .

    COMBAT_SMOOTH_RELATIVE_HUNDREDTHS = 10
    COMBAT_SMOOTH_ABSOLUTE_HUNDREDTHS = 10

    If the base damage were 20, then the actual damage would be 17.9..22.1, with the following probabilities of values:
    1/8421 : 17.9, 22.1
    1/8421 : 17.91, 22.09
    1/8421 : 17.92, 22.08
    .
    .
    .
    1/8421 : 18.0, 22.0
    2/8421 : 18.01, 21.99
    3/8421 : 18.02, 21.98
    .
    .
    .

    Technically, floating point isn't the best way to implement it (why? because only fractions that are powers of 2 can be represented in floating point). Unless you can think of a reason why hundredths aren't sufficient, I'll suggest calculating using hundredths (that part is already coded, and mostly tested).

    My main questions are the following:

    1) I don't know if you'd have any interest in using both absolute and relative numbers, but if you are interested, is the interaction suggested above useful?
    2) "DICE" are designed to make the curve less like a line, and more like two halves of a normal curve (kinda like an s-curve); presumably, the more dice, the higher the smoothing. Do you think that this sort of feature may be useful?

    I guess that I should note that modifying combat is pretty easy - I can probably do that in a few hours. The tricky part (both coding and computationally) is calculating odds. I think I have a good approach, but it will take a bit more time.
     
  2. Calavente

    Calavente Richard's voice

    Joined:
    Jun 4, 2006
    Messages:
    2,790
    Location:
    France
    having read the original thread not long ago, IIRC,

    _A= attacker strengh : (attack str * combat promo) (str = BASE+ metal weapon + heroic attack + elemental str + stigmata ...)

    _D = defender's strengh :
    if attakers boni > defender boni (city attack, covered promo, tile defense, enchanted blade...)
    D= (defense str* combat promo)/ (1 + attacker's boni - defenders boni)
    if defender boni> attacker boni :
    D= (defense str* combat promo)*(1 + defenders boni - attacker's boni )

    _R = A/D

    my 0.2
     
  3. Vulcans

    Vulcans Prince

    Joined:
    Aug 9, 2005
    Messages:
    326
    Yes, I was using the same terminology as the article given in war academy, D=damage, R=combat ratio etc.
    actually i copied the origional formula of the current system straight out of that article, and then looked at modifications based on that formula.

    i see you used different variable names, but that doesn’t matter, the concept is the same


    0) yes, i think hundreth should be sufficient.



    1) it's probably god to include both in while writing it, allowing for more flexibility/testing. they both have subtily different effect on the probability curve. I think the COMBAT_SMOOTH_RELATIVE_HUNDREDTHS is probably the way to go, as then the random factor will change proportionate to the base damage, which is inversely proportionate to the number of hits required.

    2) i think two dice wold probably give a better smothing, a S curve as you said. that is if we are having a system as the jagged line in the previous post.

    or using COMBAT_SMOOTH*COMBAT_SMOOTH to get a bell shaped probability distrabution across the smothing zone. Slightly different shape would occur from using multiple dice, still a centrally located bell dammage random factor probability.


    So when doing smaller hits the random factor will be smaller aswell, but then it’ll be applied to each of the hits, and so still have the required effect, while making big hits, the relative random factor will have a larger effect on the resulting modified damage, but that will be applied over fewer hits.

    absolute first came to my mind, but i think reletive is better to change the width of the smothing zone across the spectrum as jump points become closer together. but the ratio doesn't stay constant, so we could actually change the smoothing zone width dynamically

    25-33.33 (4 to 3 hits)=1.333
    20-25 (5 to 4 hits)=1.25
    16.67-20 (6 to 5 hits)=1.2
    14.28-16.67 (7 to 6 hits)=1.167

    Ie Max_Reletive= 1+1/(hits required)=1+1/(roof(100/BASE_DAMAGE))
    Min_Reletive=1/Max_Reletive

    therefore the random values would be int he range:
    Min_Reletive<COMBAT_SMOOTH_RELATIVE_HUNDREDTHS<Max _Reletive

    or similar thing applies with absolute
    COMBAT_SMOOTH_RELATIVE_HUNDREDTHS*D=COMBAT_SMOOTH_ absolute_HUNDREDTHS+D

    ie have a changing random value size,
    where random max=next JP damage amount/previous JP damage amount
    ie 25/20=1.25
    or hits (required+1)/(hits required)

    in the 20-25 damage zone the
    1/1.25<COMBAT_SMOOTH_RELATIVE_HUNDREDTHS<1.25

    and in the 16.67-20 damage zone the
    1/1.2<COMBAT_SMOOTH_RELATIVE_HUNDREDTHS<1.2
    etc

    That way the boundary of the smothing zone from the 20 point will just meet the boundary of the 16.67 jp zone. Hence replacing the above graph with one where the lines just meet like this:



    This is with a linear region.
    using multiple dice/random_squared might possibly degrade the shape into more of a snake shape, being more similar to the original line inbetween the jump points, and being sharper gradients over the jump points.
     
  4. eerr

    eerr Emperor

    Joined:
    May 27, 2006
    Messages:
    1,077
    where can i find the combat calculation in the code?
     
  5. Vulcans

    Vulcans Prince

    Joined:
    Aug 9, 2005
    Messages:
    326
    the current system can be found here:
    http://www.civfanatics.com/civ4/stra..._explained.php

    the modifications above are based on the existing system, We'll post the new formulas with explanations when we've worked out which one is the best one to use.
     
  6. xanaqui42

    xanaqui42 King

    Joined:
    Sep 5, 2006
    Messages:
    780
    CvUnit::updateCombat has the main combat function.

    getCombatOdds (in CvGameCoreUtils.cpp) has the human odds function.

    CvUnitAI::AI_finalOddsThreshold has the AI odds function. The main difference between this and getCombatOdds is that this is fast, whereas getCombatOdds is really accurate.

    Obviously, all of these call plenty of other functions, some of which affect combat calculation. CvUnit::maxCombatStr in particular is a big one (it handles all the bonuses and penalties).
     
  7. Vulcans

    Vulcans Prince

    Joined:
    Aug 9, 2005
    Messages:
    326
    After thought:
    the combat jump point when attacker gets to 25 damage operates independantly from the defender hits change jump points ad D=14.28 and D=16.67.

    I decided to do some hand calculations for fun.
    eg 3Vs 4
    R=1.33

    Base damage of 17.333 and 23.07
    Hits required defender=5.77
    Hits required attacker=4.33

    Defender random value range:
    0.92<random_reletive<1.0865

    Attacker random value
    0.896<random_reletive<1.116

    Base damage* random_reletive=final damage

    Do reach jump point defender needing 7 hits instead of 6 hits.
    17.33* random_reletive=16.67
    random_reletive<0.9619
    Chance of (0.9619-0.92)/(1.0865-0.92)=25% chance.

    Do reach jump point attacker needing 5 hits instead of 4 hits.
    Random relative=25/23.07=1.08366
    Chance of (1.08366-0.89)/ (1.116-0.0.89)=86.2% chance.

    Therefore there is
    64.7% chance of attacker needing over 5 hits and defender needing less than 6 hits
    21.6% chance of attacker needing over 5 hits and defender needing less than 7 hits
    10.3% chance of attacker needing over 4 hits and defender needing less than 6 hits
    3.4% chance of attacker needing over 4 hits and defender needing less than 7 hits

    This can then be fed directly into any existing combat calculator.

    Then just calculate the damage normally.

    this might seem long calculations when doing it by hand as the probability depends on the chances of having a certain amount of hits required, and then the probability of getting that many hits. But this would be easy for the computer as it calculates the number of hits required by the random number generation, and then calculates the battle from that.

    In effect the chance of winning depends on the strength ratio (linear) when calculating chance of winning a round, and then also directly on the damage of the defender and the damage of the attacker. hence as the ratio changes it tepends on fluctuating probabilities of 4 linear regions on the graph, hence making a much smother graph, which although being a sequence of linear lines, is close enough to the asymptotic relation we&#8217;re looking for.

    One thing I like about this is that the hit points left are also a continuous function, instead of descrete jumps, and probability of certain amount of remaining HP directly relates back to the combat odds and probability of certain damage amounts.
     
  8. Vulcans

    Vulcans Prince

    Joined:
    Aug 9, 2005
    Messages:
    326
    Actually this really means that when at 20.02 Vs 19.98, it will be something like
    52% chance Attacker requires=5 hits
    48% chance D=5 hits

    24.96% chance A=5 D=5 hits
    24.96% chance A=6 D=6 hits
    27.04% chance A=5 D=6 hits
    23.04% chance A=6 D=5 hits

    At any point the probability can be calculated by the sum of the 4 combat scenarios. With varying probability of the different combat scenarios, the combination of them creates a curve shape, with most probability of the combat scenario that is relevant to that damage bracket.

    so when calculating by hand, at one specific ratio point you can check the probability for each combat scenario, and multiply the probability of that scenario with the probability of that scenario winning. this is all insignificant to the computer, as all the computer does is works out the random value range, comes up with a random numbeer out of the seed generator, and then plays out only one battle with the modified damages.

    The combining of percentage of the different lines would work like this

     
  9. eerr

    eerr Emperor

    Joined:
    May 27, 2006
    Messages:
    1,077
    Spoiler :

    but is it really that important to change the combat calculations?
     
  10. Vulcans

    Vulcans Prince

    Joined:
    Aug 9, 2005
    Messages:
    326
    good question,

    at te moment knowing about the jump points means you can calculate to be at 1.01 ratio or 1.38 ratio for better odds, but avoid 0.99 or 1.37 ratios as you will get bad probabilities.

    people hve been complaining about jump points, using it as a scape goat whenever their hero dies etc. some people came up with a way of making it smoother but changing the average combat odds you make slightly stronger units invincible. hence i came up with a solution that would smooth it without skewing the combat odds, maintaining approximatly the same odds, justwithout the great advantages of being jusy over the jump points andthe disadvantage of being just over the jump points.

    the odds displayed for estimated odds are a asymptotic calculation, hence by making this change the actual odds wll be closer to the expected odds when you right click+drag the cursor over the opponent.
     
  11. xanaqui42

    xanaqui42 King

    Joined:
    Sep 5, 2006
    Messages:
    780
    I've started to attempt to implement this, and I've come across an issue.

    Basically, the problem is in the AI combat odds calculation. I think it's going to be too slow to result in something playable. Basically, here's the pseudo-code for calculating the probability that the attacker needs a particular number of rounds:

    Code:
    Probability = 0
      AbsoluteResult = 0..AbsoluteDamageRange
        TempProbability = ChanceOfObtainingDieResult(AbsoluteResult, MinimumDamage, MaximumDamage, AbsoluteDamageRange, AbsoluteDamageDice)
        CurrentDamage = MinimumDamageForThisNumberOfRounds - AbsoluteDamageRange/2 .. MaximumDamageForThisNumberOfRounds
          if MinimumDamageForThisNumberOfRounds <= (CurrentDamage + AbsoluteResult) <= MaximumDamageForThisNumberOfRounds
            Probability += ChanceOfObtainingDieResult(CurrentDamage - (MinimumDamage + AbsoluteResult), MinimumDamage + AbsoluteDamageRange/2, MaximumDamage - AbsoluteDamageRange/2, RelativeDamageRange, RelativeDamageDice) * TempProbability
    
    
    Where:
    • AbsoluteDamageDice - the number of absolute damage dice.
    • AbsoluteDamageRange = the damage range due to the absolute component.
    • AbsoluteResult = the current result in the absolute range.
    • MinimumDamage = the minimum possible damage.
    • MinimumDamageForThisNumberOfRounds = the minimum number of points of damage for this number of rounds.
    • MaximumDamage = the maximum possible damage.
    • MaximumDamageForThisNumberOfRounds = the maximum number of points of damage for this number of rounds.
    • Probability = the probability of getting the current number of rounds.
    • RelativeDamageDice = the damage dice for the Relative component.
    • RelativeDamageRange = the damage range due to the Relative component.
    • TempProbability = the probability of getting the current absolute die result.

    So, the complexity of this function (which is called hundreds of times, if not more, by mid-game) changes from O(X) to O(X+AB+C*(C^(E-1)+D*D^(F-1))) (Note that it's possible that C^(E-1) and D^(F-1) can be reduced to something simpler; I haven't worked much on this part yet).
    • A = Range of rounds for Attacker.
    • B = Range of Rounds for Defender.
    • C = Range of Absolute damage.
    • D = Range of relative damage.
    • E = Number of Absolute dice
    • F = number of relative dice.
    • X = Number of units in tile (used for Lifespark healing calculation).
    With significant numbers, this will be dominated by O(C^E+CD^F). While some of the above complexity is needed (and hopefully, the orders will be small), I'm not convinced that all of it is.

    Plan to reduce complexity
    My original proposal was 4 variables:
    • COMBAT_SMOOTH_ABSOLUTE_HUNDREDTHS (default value: 0) This would allow a variation of +/-
      COMBAT_SMOOTH_ABSOLUTE_HUNDREDTHS/100 points of damage.
    • COMBAT_SMOOTH_RELATIVE_HUNDREDTHS (default value: 0) This would allow a variation of * +/-
      COMBAT_SMOOTH_ABSOLUTE_HUNDREDTHS/100 points of damage. This is applied before COMBAT_SMOOTH_ABSOLUTE_HUNDREDTHS .
    • COMBAT_SMOOTH_ABSOLUTE_DICE (default value: 1) If COMBAT_SMOOTH_ABSOLUTE_HUNDREDTHS is set to a non-default values, then this indicates the "number of dice" used to generate the value in the given range. The "dice" would be of largely equal size.
    • COMBAT_SMOOTH_RELATIVE_DICE (default value: 1) If COMBAT_SMOOTH_RELATIVE_HUNDREDTHS is set to a non-default values, then this indicates the "number of dice" used to generate the value in the given range. The "dice" would be of largely equal size.

    The new proposal would be making the assumption that there is value in having the option for multiple dice, but not for keeping the absolute and relative ranges separate:
    • COMBAT_SMOOTH_ABSOLUTE_HUNDREDTHS (default value: 0) This would allow a variation of +/-
      COMBAT_SMOOTH_ABSOLUTE_HUNDREDTHS/100 points of damage.
    • COMBAT_SMOOTH_RELATIVE_HUNDREDTHS (default value: 0) This would allow a variation of * +/-
      COMBAT_SMOOTH_ABSOLUTE_HUNDREDTHS/100 points of damage. This is applied before COMBAT_SMOOTH_ABSOLUTE_HUNDREDTHS .
    • COMBAT_SMOOTH_DICE (default value: 1) This indicates the "number of dice" used to generate the value in the given range (the combination of COMBAT_SMOOTH_ABSOLUTE_HUNDREDTHS and COMBAT_SMOOTH_RELATIVE_HUNDREDTHS). The "dice" would be of largely equal size.

    This would change the complexity to around O(X+AB+F^(G-1))
    • A = Range of rounds for Attacker.
    • B = Range of Rounds for Defender.
    • F = Range of damage.
    • G = Number of dice
    • X = Number of units in tile (used for Lifespark healing calculation).

    Even better, the item I'm calling "F^(G-1)" above can be pre-calculated, so the actual complexity would look like:
    O(X+AB+F*Glg(F))
    Which in realistic cases where G >1, F >1 looks like:
    O(F*G)

    Which looks more reasonable for a commonly-called function.
     
  12. MagisterCultuum

    MagisterCultuum Great Sage

    Joined:
    Feb 14, 2007
    Messages:
    16,109
    Location:
    Kael's head
    Whats wrong with mutual destruction? In real life combat its not that uncommon for the victor to be mortally wounded. Theres nothing wrong with two units killing each other. Of course, if you rounded down things might get a little confusing unless you added something like ", but was mortally wounded in the process" to the "your x has defeated a y" statement.
     
  13. Pandemonis

    Pandemonis Warlord

    Joined:
    Aug 8, 2007
    Messages:
    124
    Location:
    King of the High Castle
    I prefer ", but was mortally wounded in this epic battle."
     
  14. eerr

    eerr Emperor

    Joined:
    May 27, 2006
    Messages:
    1,077
    "two units entered the ring, but neither left"
     
  15. xanaqui42

    xanaqui42 King

    Joined:
    Sep 5, 2006
    Messages:
    780
    Nothing in particular. It would be a change to how combat works, though.
     
  16. psychoak

    psychoak Warlord

    Joined:
    May 15, 2007
    Messages:
    159
    I like the principle of smoothing the combat system up, but it seems secondary to the rng. It wont really help with any of the myriad of problems will it? I'm a little spaced, but from what I read, I'm still screwed when I counter-attack an invasion and lose my best defenders at 90%+ and turn the attackers into high xp opponents.

    What I'd kill for in combat is a maximum attack rounds. If they were capped at the number of rounds it usually takes for one unit to kill another equal strength unit, odds of winning would actually be odds of winning instead of odds of living. There would be no horsehockey victories at 2%, no key units lost regularly because you have to take risks against things half your strength. When the rng decided to screw you, hyborem would just find himself missing half his health, instead of being dead after eight straight hits from the enemy horseman. It seems to me that change alone would vastly improve the gameplay, if doing nothing for damage ratios.
     
  17. Vulcans

    Vulcans Prince

    Joined:
    Aug 9, 2005
    Messages:
    326
    Xanaqui42: I have a few ideas, I&#8217;m at work at the moment(yes i'm addicted to this game and even come here in my coffee breaks), I&#8217;ll look into the pseudo code tonight and get back to you.

    Yes it would change the combat to have mutual destruction. I believe that smarter orcs is aiming at fixing bugs while maintaining roughly the same game dynamics as before but with just smarter AI and all the bugs(like combat jump points) removed, so I don&#8217;t know if that type of change would be in the scope of smarter orcs, or in some other patch.

    Although I do like the idea of mutual destruction.
    You could even add a new unit ability, &#8220;Last strilke&#8221;. After losing combat the unit has a chance of another final round of combat to deal damage to the unit defeating it.

    maybe there could be goblin suicide bombers, or some units that have a lot of stamina that can slip out a dagger at the last moment on their death bed to take their opponent with them

    "After a long battle he was finally beaten, the arquebus was knocked to the ground with mortal wounds, slipping in and out of consciousness; the last traces of life were etching away from him. As he gazed up at the clouds he saw the face of his enemy towering over him, razing his axe to cut off his head and finish off the job. Suddenly a moment of clarity cane, with a last gasp of strength he gently pulled the pin out of a grenade in his pocket, and both him and his victor instantly went up in a cloud of smoke."

    anyway, this is a bit off topic. i think smothing the combat curve shouldent be to hard to implement.
     
  18. Hanny

    Hanny Prince

    Joined:
    Sep 3, 2007
    Messages:
    343
    Location:
    IOW UK
    My 2 farthings.

    The step increase rather than linear is a good thing. Why?, because units representing formations of men that have become war hardened, this hardening creates a quantativly more effiecent unit than the sum of its parts would otherwise suggest. So to my mind the step increase portrays this known effect better than does a linear progression, now you can argue that the AI needs to use an optimal system, and step has the advantage of it knowing and acting more aggressivly because it thinks its going to win, once its units reach the correct steps advatage over lower steped ones, in linear it will be less sure is my thinking and act with less aggresion asa consequence.
     
  19. Vulcans

    Vulcans Prince

    Joined:
    Aug 9, 2005
    Messages:
    326
    yes, the battle experianced units het the hardened strength.
    we are actually not looking for a linear relation, we're looking for a asymptotic relation. the difference we are making is that you don't need to think about jump points any more, i've often thought about jump points,

    "oh no, i'm at 1.36 ratio, not worth the effort, if i could just get to 1.38 ratio then i'm suddenly MUCH stronger. which unit should i select to battle?"
    or
    "hey i have a experianced unit at 1.55 ratio, but i don't really need to risk him as my less promoted unit at 1.38 ratio will do nearly as well in this particular battle."

    smothing it out and turning it into a asymptottic (not linear) relation will make the game simpler with one less thing to think about
     
  20. Gamestation

    Gamestation Introducing Servo

    Joined:
    May 24, 2006
    Messages:
    551
    I believe this exists in the Afterworld scenario for BtS. It's either that or it's a limit to how much damage can be taken in a combat before it ends with no one dead but damaged to varying degrees. Not sure if it is possible to bring this into 0.23c though.
     

Share This Page