PieceOfMind's Advanced Combat Odds

This looks like an awesome modcomp! :goodjob: I am certainly going to include this in Merged Mods.
 
That looks awesome. :goodjob: Didn't you want to keep the overall E[HP] for both units on a line by itself? Even as an option?

Do any of the lines wrap using the larger BM font? If you don't use it, I can test it here as I do.

For the HP/hit line, how about cutting some of the text down so the numbers stick out?

20,20 HP/hit x 5/5 hits @ 50.0%​

You can get the real multiplication symbol using its Unicode XML entity: "×" without the quotes.

For a moment I tried making the unharmed odds conditioned on the unit surviving, but I found the results a bit more confusing and would prefer to avoid it unless I hear a good reason otherwise.

Do you mean that you multiplied the probability of escaping unharmed by the probability of winning? If so, I agree that it doesn't make sense since it is displayed on a line by itself, separate from the outcome. If not, can you explain what you mean by this?
 
This is really a great mod component. A fabulous job PieceOfMind!:goodjob:

As a mathematician, I can really appreciate all of the values and I know about their significance in the combat algorithm. But I guess that when I'm playing the game, then I won't be using all of them. So if and when the BUG Mod adds this, then I will probably be most interested in enabling:
Victory + XP
Retreat + XP
Witdraw + XP
Attacker Unharmed
Attacker Combat Limit

Maybe the Attacker Combat Limit could use a new name. I know that it's named this way in the XML-files, but most people won't know that. In my opinion it's better to talk about a more intuitive name like Attacker damage limit or Attacker maximum damage.

Do you mean that you multiplied the probability of escaping unharmed by the probability of winning? If so, I agree that it doesn't make sense since it is displayed on a line by itself, separate from the outcome. If not, can you explain what you mean by this?

He's talking about the conditional chance that a unit wins without losing hitpoints assuming the condition that it wins is true. It's similar to looking at the hitpoints of a unit assuming that the condition that it has won the battle is true.
The value he is showing is the non-conditional one in this case. He doesn't assume that the unit wins.
 
So if and when the BUG Mod adds this, then I will probably be most interested in enabling:
Victory + XP
...

Do you mean stripping away all the other stuff? I'm already thinking about how many options this should have. Should I make each piece an option (overkill) or have perhaps a Detail setting: Low, Medium, High. The latter will give you less control over individual elements, but BUG is approaching the point of option overload for the average user (probably past it, actually).

Maybe the Attacker Combat Limit could use a new name.

Won't this simply be the defender's E[XP] shown on the withdraw outcome? Or does ACL mean something other than the minimum HP to which a bombarding unit can damage a defender?

He's talking about the conditional chance that a unit wins without losing hitpoints assuming the condition that it wins is true.

Right, I had that backwards. It's been a looooong time since I took statistics. :D And I always found it to be so intere--:sleep: Actually, it was interesting, it was just presented in such a boring way. Of all the math classes I took, stats were the only ones where I had to memorize formulas rather than understand them intuitively.

Oh boy, think I'll go take a nap. :mischief:
 
Do you mean stripping away all the other stuff? I'm already thinking about how many options this should have. Should I make each piece an option (overkill) or have perhaps a Detail setting: Low, Medium, High. The latter will give you less control over individual elements, but BUG is approaching the point of option overload for the average user (probably past it, actually).

It's great that PieceOfMind enabled all of this information, but in a typical game I won't be needing it all. It would be great if I could enable some of these in an atypical battle where I do want to see all of the details but in a typical battle I would just be interested in the ones that I mentioned in my previous post. So in a typical battle, the other values will just distract me from the ones that I do want to see.

My preference for the BUG MOD so that all users might be satisfied:
One, two or three predefined versions where the mod designers decide what the user will see, one custom setting that can be defined in an xml file where the advanced user can customize exactly what he will see.

Do you and PieceOfMind think that is possible?

About overloading the average BUG user with options: I remember a discussion about a light and advanced version of BUG where just the most used or all of the options would be shown. There are many programs which use a beginner, intermediary and advanced interface. BUG is just the type of program which shows the value of this type of distinction in interfaces. But I guess that might be a lot of work as you'll probably have to design two or three BUG Options screens and each of these has to enable or disable the right parts of the mod.

However, such a discussion is for another thread. This is about PieceOfMinds Advanced Combat Odds.

Won't this simply be the defender's E[XP] shown on the withdraw outcome? Or does ACL mean something other than the minimum HP to which a bombarding unit can damage a defender?

Yes, I guess so, but PieceOfMind mentioned it seperately in his first post. At that point, he used a non-conditional E[HP]. I didn't really think it through that now that he's using a conditional one, this value is not that interesting anymore.

Oh boy, think I'll go take a nap. :mischief:

Nah, lets talk a bit more about conditional chances..:p
 
I'm likely too tired to completely understand these conditional chances right now, but the E[HP] | Victory seems surprisingly high to me. Let's look at PoM's last screenshot, the example on the right which is probably a Cav attacking a War Elephant (Victory Odds 75.11%). E[HP] = 53.0 HP :eek:. So on average a winning Cav will suffer < 3 hits from the defending Elephant. The Cav's odds to win 1 round during combat are merely 55.6% -- my gut feeling would expect something like 3-4 hits on average. Is this felt discrepancy the reason why you don't find it that interesting anymore Roland?

Ok. Time for me to go take a nap ...
 
I'm likely too tired to completely understand these conditional chances right now, but the E[HP] | Victory seems surprisingly high to me. Let's look at PoM's last screenshot, the example on the right which is probably a Cav attacking a War Elephant (Victory Odds 75.11%). E[HP] = 53.0 HP :eek:. So on average a winning Cav will suffer < 3 hits from the defending Elephant. The Cav's odds to win 1 round during combat are merely 55.6% -- my gut feeling would expect something like 3-4 hits on average. Is this felt discrepancy the reason why you don't find it that interesting anymore Roland?

Ok. Time for me to go take a nap ...

Anymore? I didn't put forth a different position earlier, did I?

Anyhow, I just don't think that I'll be using the average hitpoints left very often in gameplay. I do think that I will use the chance to lose no hitpoints as that means that my unit doesn't need to heal. That last information is very useful when I am moving my SOD from enemy city to enemy city. It means faster conquest with less units needing to stop to heal if I pick the ones that have a high chance to lose no hitpoints (likely the drill promoted units).
The average hitpoints left isn't that interesting as it doesn't tell you a lot about the time you need to heal (as many might expect it does). That has to do with the distribution of damage that leads to this average hitpoints left. This distribution is typically so variable (high variation for the mathematicians) that the average isn't very interesting for gameplay. It's hard to estimate by looking at this average if you'll likely be healing for 1, 2, 3 or more turns because of this variability.

But maybe other players will use the average hitpoints left in their combat tactics, so it's still a useful piece of information. Just not for me.

Edit: By the way, I would be interested in something like this:
Victory: 75% (3XP)
0 turns to heal 20%
1 turn to heal 27%
2 turns to heal 37%
3 turns to heal 16%
Defeat 25%
etc.

But it would of course depend on the healers present on the tile and other factors, so that information should be extracted from the game when such a calculation is made. (I don't know how hard that would be). I guess that with the information of the healing speed, PieceOfMind could easily calculate the turns to heal with the values from his present calculations. His own 'Unit Healing' strategy article would be useful for this part.
 
Ok I have a number of posts to reply to so bear with me for a minute.

That looks awesome. :goodjob: Didn't you want to keep the overall E[HP] for both units on a line by itself? Even as an option?

Well it's still in the code and I can certainly still include it as an option. However, as I said, the E[HP] for both units is a bit less useful and can be reasonably estimated from the conditioned expected hitpoints I've used instead. Until now I've mostly been avoiding the use of options, trying to keep to one standard, but if merging it into BUG/BAT means including some options for it then it would make sense to start designing it with options in mind.

Do any of the lines wrap using the larger BM font? If you don't use it, I can test it here as I do.
I would guess they do.
For the HP/hit line, how about cutting some of the text down so the numbers stick out?

20,20 HP/hit x 5/5 hits @ 50.0%​

You can get the real multiplication symbol using its Unicode XML entity: "&#x0D7;" without the quotes.
That's a good suggestion - I'll see how it looks.
Do you mean that you multiplied the probability of escaping unharmed by the probability of winning? If so, I agree that it doesn't make sense since it is displayed on a line by itself, separate from the outcome. If not, can you explain what you mean by this?
Consider a battle where the only outcomes are victory or defeat and the odds are 50% each way. In that battle, also assume that 25% of the time the attacker will walk away unharmed. I could either display the flat odds the attacker will walk away unharmed as 25%, or I could display the odds that the attacker was unharmed assuming he won the battle, which would be 50% and that is worked out by P(unharmed | survived) = P(unharmed) / P(survived). This involved division - not multiplication.


Do you have a screenshot showing a unit that might withdraw? Just for completeness. ;)
Sure. Here's one.
 

Attachments

  • Civ4ScreenShot0183.JPG
    Civ4ScreenShot0183.JPG
    305 KB · Views: 359
This is really a great mod component. A fabulous job PieceOfMind!:goodjob:

As a mathematician, I can really appreciate all of the values and I know about their significance in the combat algorithm. But I guess that when I'm playing the game, then I won't be using all of them. So if and when the BUG Mod adds this, then I will probably be most interested in enabling:
Victory + XP
Retreat + XP
Witdraw + XP
Attacker Unharmed
Attacker Combat Limit

Maybe the Attacker Combat Limit could use a new name. I know that it's named this way in the XML-files, but most people won't know that. In my opinion it's better to talk about a more intuitive name like Attacker damage limit or Attacker maximum damage.
It looks like you and EmperorFool discussed this to see that it's not as interesting a value anymore, since it can be gained from E[HP | withdraw] displayed beside the withdraw odds. I didn't leave a very intuitive name on it before because honestly it just was a flat-out debug-like output line I'd left in and I felt with version 0.1, it was useful information alongside the rest of it. But yeah, not so useful now...

It's great that PieceOfMind enabled all of this information, but in a typical game I won't be needing it all. It would be great if I could enable some of these in an atypical battle where I do want to see all of the details but in a typical battle I would just be interested in the ones that I mentioned in my previous post. So in a typical battle, the other values will just distract me from the ones that I do want to see.

I understand this. For example, in most battles I don't find the Defender Unharmed odds to be of great use either, unless I am suiciding weak units against strong defenders in which case I don't want there to be much def unharmed odds.

My preference for the BUG MOD so that all users might be satisfied:
One, two or three predefined versions where the mod designers decide what the user will see, one custom setting that can be defined in an xml file where the advanced user can customize exactly what he will see.

Do you and PieceOfMind think that is possible?

It's certainly possible. I think the idea of 3 pre-defined versions is a really good idea. Perhaps you or EmperorFool (or both:D) can make the best suggestions for exactly which things should go in each category. My own viewpoint is probably going to be biased by the things I found most interesting to code and other silly things like that.:lol:

Note there could even be differences in how the information is displayed on top of what information is displayed, so keep that in mind.

By the way, Roland, try setting ACO_debug to true if you can be bothered compiling the DLL yourself, as this will show you much greater detail about the odds of the various HP outcomes. If you can stand to use the odds calculator with all that gory detail you might be able to help spot any errors I have accidentally put in the code.

DanF55771, if you are reading this, I'd like you to also consider putting the ACO_debug on while you try it out.
 
I'm likely too tired to completely understand these conditional chances right now, but the E[HP] | Victory seems surprisingly high to me. Let's look at PoM's last screenshot, the example on the right which is probably a Cav attacking a War Elephant (Victory Odds 75.11%). E[HP] = 53.0 HP :eek:. So on average a winning Cav will suffer < 3 hits from the defending Elephant. The Cav's odds to win 1 round during combat are merely 55.6% -- my gut feeling would expect something like 3-4 hits on average. Is this felt discrepancy the reason why you don't find it that interesting anymore Roland?

Ok. Time for me to go take a nap ...

lol what's with all these naps?:lol:

Good guess but it's a cav vs Combat5 mace :D.

As far as I can tell, your suspicions here can simply be put down to the general unintuitiveness of some statistics.

On average the unit will win with about 40HP (see attached picture) but when we condition it on the unit being victorious the E[HP] is taken up to 53.0. This is not too surprising given the most likely HP is 49 with 16.3% odds. Since the possibility of retreat is not included in the figure, this also boosts the number a bit higher than you might have been expecting. I'm pretty sure if the Cavalry kills the mace 53.0 will indeed be the average HP he will walk away with.



Anymore? I didn't put forth a different position earlier, did I?

Anyhow, I just don't think that I'll be using the average hitpoints left very often in gameplay. I do think that I will use the chance to lose no hitpoints as that means that my unit doesn't need to heal. That last information is very useful when I am moving my SOD from enemy city to enemy city. It means faster conquest with less units needing to stop to heal if I pick the ones that have a high chance to lose no hitpoints (likely the drill promoted units).
The average hitpoints left isn't that interesting as it doesn't tell you a lot about the time you need to heal (as many might expect it does). That has to do with the distribution of damage that leads to this average hitpoints left. This distribution is typically so variable (high variation for the mathematicians) that the average isn't very interesting for gameplay. It's hard to estimate by looking at this average if you'll likely be healing for 1, 2, 3 or more turns because of this variability.

But maybe other players will use the average hitpoints left in their combat tactics, so it's still a useful piece of information. Just not for me.

Edit: By the way, I would be interested in something like this:
Victory: 75% (3XP)
0 turns to heal 20%
1 turn to heal 27%
2 turns to heal 37%
3 turns to heal 16%
Defeat 25%
etc.

But it would of course depend on the healers present on the tile and other factors, so that information should be extracted from the game when such a calculation is made. (I don't know how hard that would be). I guess that with the information of the healing speed, PieceOfMind could easily calculate the turns to heal with the values from his present calculations. His own 'Unit Healing' strategy article would be useful for this part.

In the option that will eventually be the most advanced option, I'm sure we could just include something like "Explicit attacker HP outcome probabilities" or something, and with that option enabled you would see each and every possible attacker HP outcome with its respective probability (conditioned on the victory or survival probability I'd say), just like you do at the moment with the extra debug information.

But going as far to include the number of turns needed for healing is IMO too much, and with the explicit results I just mentioned it would not be too hard to figure out. There's also the obvious problem of not having the medic in the stack at the moment of you getting the odds, or the figures changing once your units are outside of enemy territory and inside the newly captured city. I can understand you'd appreciate it but I think you could do it reasonably enough in your head when the greater detail is given to you.

On another note, it would be nice if selecting a medic told you explicitly how much HP you can expect to heal each turn in the various types of territory or with a hospital etc.

By the way, an interesting thing I discovered by accident when making the changes in v0.2, which I'd sort of ignored beforehand, was that when the attacker is killed or retreats the expected defender hitpoints are the same. This makes sense because the retreat roll is done after the battle has basically finished and the defender hitpoints are already set in stone.
 

Attachments

  • Civ4ScreenShot0184.JPG
    Civ4ScreenShot0184.JPG
    53.9 KB · Views: 760
Ok I have a number of posts to reply to so bear with me for a minute.

Times up! :p

That's a good suggestion - I'll see how it looks.

Maybe even

20/20 HP x 5/5 hits @ 50.0%

I removed "/hit" as it should be clear by the multiplication.

Looking at the various screenshots, I wonder if the first line should summarize the probabilities of each unit surviving. For the attacker: the sum of victory, retreat, and withdraw. For the defender: the sum of retreat, withdraw and defeat.

Survival: 63.2% / 51.8%

For the details of each outcome, you'd look below. I suggest this only because it seems already you need to look below sometimes, depending on the information you seek. To me, the thing I need to know most often is the chance that each unit survives the combat. But I haven't played with it yet (did you post the new code?) to get a good feel.

On another note, it would be nice if selecting a medic told you explicitly how much HP you can expect to heal each turn in the various types of territory or with a hospital etc.

That's an excellent idea for another modcomp. :D

By the way, an interesting thing I discovered by accident when making the changes in v0.2, which I'd sort of ignored beforehand, was that when the attacker is killed or retreats the expected defender hitpoints are the same.

That makes perfect sense. Would it be too unclear to remove the defender's E[HP] from the retreat outcome? We know the Defeat outcome's E[HP] applies, but will other people figure that out easily?

If you want to make the level of detail configurable now, it's pretty simple. At the top of your changes, add this to grab the value from the XML:

Code:
int iDetail = GC.getDefineINT("COMBAT_ODDS_DETAIL");

You might want to define constants for the different levels in a header file, or just use 0, 1, 2, ...

Code:
// AdvancedCombatOdds.h

#pragma once

#ifndef ADVANCED_COMBAT_ODDS_H
#define ADVANCED_COMBAT_ODDS_H

#define ACO_DETAIL_LOW     0
#define ACO_DETAIL_MEDIUM  1
#define ACO_DETAIL_HIGH    2

#endif

Then it's just a matter of checking the detail before adding some text block.

Code:
// only show E[HP] if at medium or higher
if (iDetail >= ACO_DETAIL_MEDIUM)
{
    ...
}

To merge this with BUG, I'll only need to change the top line that grabs the current setting.
 
Maybe even

20/20 HP x 5/5 hits @ 50.0%

I removed "/hit" as it should be clear by the multiplication.
Hmm, looks better again. The only thing that concerns me a bit is the use of slashes to separate the values. I've prefered using commas to this point because a "/" could cause one to think it's a fraction of some sort. I often think of the phrase "out of" when I see a "/".

Looking at the various screenshots, I wonder if the first line should summarize the probabilities of each unit surviving. For the attacker: the sum of victory, retreat, and withdraw. For the defender: the sum of retreat, withdraw and defeat.

Survival: 63.2% / 51.8%

For the details of each outcome, you'd look below. I suggest this only because it seems already you need to look below sometimes, depending on the information you seek. To me, the thing I need to know most often is the chance that each unit survives the combat. But I haven't played with it yet (did you post the new code?) to get a good feel.
I like that idea. Perhaps if there are no retreat odds or withdraw odds it should be omitted though? Or would omitting it only when it is not needed make the cases where it is needed look inconsistent because you are getting other info?

In any case, I'd prefer it looked like

Survival odds: 63.2% , 51.8%

Actually up until now I'd also been avoiding colouring the actual percentages when possible because white text is a bit easier to read usually. You might notice in my todo list was an idea that I could somehow increase the opacity of the display so the numbers are a bit easier to read when there is a lot of background detail behind them.

The new code is not up yet. However if you want to see what I'm currently working with, it's as follows:

Spoiler :
Code:
/*************************************************************************************************/
                /** ADVANCED COMBAT ODDS                      13/02/09                          PieceOfMind      */
                /** BEGIN                                                                       v0.2             */
                /*************************************************************************************************/

                //BOOL ACO_debug = false;
                BOOL ACO_debug = false;
                //Change this to true when you need to spot errors, particular in the expected hit points calculations
                ACO_debug = (GC.getDefineINT("ADVANCED_COMBAT_ODDS_DEBUG")==1); // 1 for on, 0 for off

                /** Many thanks to DanF3771 for some of these calculations! **/
                int iAttackerStrength  = pAttacker->currCombatStr(NULL, NULL);
                int iAttackerFirepower = pAttacker->currFirepower(NULL, NULL);
                int iDefenderStrength  = pDefender->currCombatStr(pPlot, pAttacker);
                int iDefenderFirepower = pDefender->currFirepower(pPlot, pAttacker);
                FAssert((iAttackerStrength + iDefenderStrength)*(iAttackerFirepower + iDefenderFirepower) > 0);

                // int iAttackerOdds      = ((GC.getDefineINT("COMBAT_DIE_SIDES") * iAttackerStrength) / (iAttackerStrength + iDefenderStrength));

                int iDefenderOdds = ((GC.getDefineINT("COMBAT_DIE_SIDES") * iDefenderStrength) / (iAttackerStrength + iDefenderStrength));

                int iAttackerOdds = GC.getDefineINT("COMBAT_DIE_SIDES") - iDefenderOdds;



                int iStrengthFactor    = ((iAttackerFirepower + iDefenderFirepower + 1) / 2);
                int iDamageToAttacker  = std::max(1, ((GC.getDefineINT("COMBAT_DAMAGE") * (iDefenderFirepower + iStrengthFactor)) / (iAttackerFirepower + iStrengthFactor)));
                int iDamageToDefender  = std::max(1, ((GC.getDefineINT("COMBAT_DAMAGE") * (iAttackerFirepower + iStrengthFactor)) / (iDefenderFirepower + iStrengthFactor)));

                int iExperience;
                if (pAttacker->combatLimit() < 100)
                {
                    iExperience        = GC.getDefineINT("EXPERIENCE_FROM_WITHDRAWL");
                }
                else
                {
                    iExperience        = (pDefender->attackXPValue() * iDefenderStrength) / iAttackerStrength;
                    iExperience        = range(iExperience, GC.getDefineINT("MIN_EXPERIENCE_PER_COMBAT"), GC.getDefineINT("MAX_EXPERIENCE_PER_COMBAT"));
                }

                int iDefExperienceKill;
                    iDefExperienceKill = (pAttacker->defenseXPValue() * iAttackerStrength) / iDefenderStrength;
                    iDefExperienceKill = range(iDefExperienceKill, GC.getDefineINT("MIN_EXPERIENCE_PER_COMBAT"), GC.getDefineINT("MAX_EXPERIENCE_PER_COMBAT"));


                int iNeededRoundsAttacker = (pDefender->currHitPoints() - pDefender->maxHitPoints() + pAttacker->combatLimit() - (((pAttacker->combatLimit())==GC.getMAX_HIT_POINTS())?1:0))/iDamageToDefender + 1;
                //The extra term introduced here was to account for the incorrect way it treated units that had combatLimits.
                //A catapult that deals 25HP per round, and has a combatLimit of 75HP must deal four successful hits before it kills the warrior -not 3.  This is proved in the way CvUnit::resolvecombat works
                // The old formula (with just a plain -1 instead of a conditional -1 or 0) was incorrectly saying three.

                int iNeededRoundsDefender = (pAttacker->currHitPoints() - 1)/iDamageToAttacker + 1;

                //szTempBuffer.Format(L"iNeededRoundsAttacker = %d\niNeededRoundsDefender = %d",
                //                        iNeededRoundsAttacker, iNeededRoundsDefender);

                //szString.append(NEWLINE);
                //szString.append(szTempBuffer.GetCString());



                //szTempBuffer.Format(L"for %d XP -- " SETCOLR L"%d*%dHP" ENDCOLR L" / " SETCOLR L"%d*%dHP" ENDCOLR L" Odds: %.1f%%  %d",
                //                      iExperience, TEXT_COLOR("COLOR_POSITIVE_TEXT"), iNeededRoundsAttacker, iDamageToDefender, TEXT_COLOR("COLOR_NEGATIVE_TEXT"), iNeededRoundsDefender, iDamageToAttacker, float(iAttackerOdds)/10.0f, iDefenderStrength);
                //szTempBuffer.Format(L"for %d XP -- " SETCOLR L"%d*%dHP" ENDCOLR L" / " SETCOLR L"%d*%dHP" ENDCOLR L" Odds: %.1f%%",
                 //                     iExperience, TEXT_COLOR("COLOR_POSITIVE_TEXT"), iNeededRoundsAttacker, iDamageToDefender, TEXT_COLOR("COLOR_NEGATIVE_TEXT"), iNeededRoundsDefender, iDamageToAttacker, float(iAttackerOdds)*100.0f / float(GC.getDefineINT("COMBAT_DIE_SIDES")));

                //szString.append(NEWLINE);
               // szString.append(szTempBuffer.GetCString());

                //szTempBuffer.Format(L"iAttackerStrength = %d\niDefenderStrength = %d\n\niAttackerFirePower = %d\niDefenderFirePower = %d\n",
                //                        iAttackerStrength, iDefenderStrength, iAttackerFirepower,  iDefenderFirepower);

                //szString.append(NEWLINE);
                //szString.append(szTempBuffer.GetCString());

                //szTempBuffer.Format(L"iStrengthFactor = %d\n = (iAFP+iDFP+1)/2\n\niDamageToAttacker = %d\niDamageToDefender = %d\n",
                //                         iStrengthFactor, iDamageToAttacker, iDamageToDefender);

                //szString.append(NEWLINE);
                //szString.append(szTempBuffer.GetCString());

                //szTempBuffer.Format(L"iDefenderOdds = %d\n = (DIE_SIDES (%d) * %d) / (%d + %d)\niAttackerOdds = %d",
                //                        iDefenderOdds, GC.getDefineINT("COMBAT_DIE_SIDES"),iDefenderStrength, iAttackerStrength, iDefenderStrength, iAttackerOdds);

                //szString.append(NEWLINE);
                //szString.append(szTempBuffer.GetCString());

                //szTempBuffer.Format(L"Attacker combatLimit = %d", // \nAttacker getDamage() = %d",
                //                        pAttacker->combatLimit());//,pAttacker->getDamage());

                //szString.append(NEWLINE);
                //szString.append(szTempBuffer.GetCString());

                int iDefenderHitLimit = pDefender->maxHitPoints() - pAttacker->combatLimit();
                //szTempBuffer.Format(L"iDefenderHitLimit = %d",iDefenderHitLimit);
                //szString.append(NEWLINE);
                //szString.append(szTempBuffer.GetCString());


                //NOW WE CALCULATE SOME INTERESTING STUFF :)


                szTempBuffer.Format(L"-----Advanced Combat Odds v0.2-----");
                szString.append(NEWLINE);
                szString.append(szTempBuffer.GetCString());




                //Alternatively, I could have calculated the defender odds as 1 minus the sum of the earlier three.  I'm more likely to spot errors this way though.


                //szTempBuffer.Format(SETCOLR L"%dHP per hit (%d hits required)" ENDCOLR L"\n" SETCOLR L"%dHP per hit (%d hits required)" ENDCOLR,
                //                                      TEXT_COLOR("COLOR_POSITIVE_TEXT"), iDamageToDefender, iNeededRoundsAttacker, TEXT_COLOR("COLOR_NEGATIVE_TEXT"), iDamageToAttacker, iNeededRoundsDefender);
                //                szString.append(NEWLINE);
                //                szString.append(szTempBuffer.GetCString());




                //const int MAX_SIZE_ARRAY = 16;//This value 16 is hardcoded in for now
                //It is unlikely that

                //Calculate Expected Hitpoints for both attacker and defender and display them side by side.

                //eg.  (100->83:80->5)


                float E_HP_Att = 0.0f;//expected damage dealt to attacker
                float E_HP_Def = 0.0f;
                float E_HP_Att_Withdraw;
                float E_HP_Att_Victory;
                int E_HP_Att_Retreat = (pAttacker->currHitPoints()) - (iNeededRoundsDefender-1)*iDamageToAttacker;//this one is predetermined easily
                float E_HP_Def_Withdraw;
                float E_HP_Def_Defeat; // if attacker dies
                //Note E_HP_Def is the same for if the attacker withdraws or dies

                //float prob_attack[MAX_SIZE_ARRAY];
                //float prob_defend[MAX_SIZE_ARRAY];

                //I was planning to use these two arrays for other calculations, but don't need them for the moment.

                float AttackerUnharmed;
                float DefenderUnharmed;

                AttackerUnharmed = getCombatOddsSpecific(pAttacker,pDefender,0,iNeededRoundsAttacker);
                DefenderUnharmed = getCombatOddsSpecific(pAttacker,pDefender,iNeededRoundsDefender,0);
                DefenderUnharmed += getCombatOddsSpecific(pAttacker,pDefender,iNeededRoundsDefender-1,0);


                            //the following code was incorrect
                /**
                if(iNeededRoundsAttacker-1 == 0) // this is due to the abnormal combatLimit situations
                {
                    for (int n_A = 0; n_A < iNeededRoundsDefender; n_A++)
                        {
                            DefenderUnharmed += getCombatOddsSpecific(pAttacker,pDefender,n_A,iNeededRoundsAttacker);//this is the defender at the combatLimit
                            szTempBuffer.Format(L"DEBUG DefenderUnharmed = %.2f at this point",DefenderUnharmed);
                            szString.append(NEWLINE);szString.append(szTempBuffer.GetCString());
                        }
                }
                **/
                if(ACO_debug) {
                    szTempBuffer.Format(L"E[HP ATTACKER]");
                    szString.append(NEWLINE);szString.append(szTempBuffer.GetCString());
                }
                // already covers both possibility of defender not being killed AND being killed
                for (int n_A = 0; n_A < iNeededRoundsDefender; n_A++)
                {
                    //prob_attack[n_A] = getCombatOddsSpecific(pAttacker,pDefender,n_A,iNeededRoundsAttacker);
                    E_HP_Att += ( (pAttacker->currHitPoints()) - n_A*iDamageToAttacker) * getCombatOddsSpecific(pAttacker,pDefender,n_A,iNeededRoundsAttacker);

                    if(ACO_debug)
                    {
                        szTempBuffer.Format(L"+%d * %0.2f%%  (Def %d) (%d:%d)",
                        ((pAttacker->currHitPoints()) - n_A*iDamageToAttacker),100.0f*getCombatOddsSpecific(pAttacker,pDefender,n_A,iNeededRoundsAttacker),iDefenderHitLimit,n_A,iNeededRoundsAttacker);
                        szString.append(NEWLINE);szString.append(szTempBuffer.GetCString());
                    }
                }

                    E_HP_Att_Victory = E_HP_Att;//NOT YET NORMALISED
                    E_HP_Att_Withdraw = E_HP_Att;//NOT YET NORMALIZED
                if((pAttacker->withdrawalProbability()) > 0)
                { // if withdraw odds involved
                    if(ACO_debug)
                    {
                        szTempBuffer.Format(L"Attacker retreat odds");
                                            szString.append(NEWLINE);szString.append(szTempBuffer.GetCString());
                    }
                    for (int n_D = 0; n_D < iNeededRoundsAttacker; n_D++)
                    {
                        E_HP_Att += ( (pAttacker->currHitPoints()) - (iNeededRoundsDefender-1)*iDamageToAttacker) * getCombatOddsSpecific(pAttacker,pDefender,iNeededRoundsDefender-1,n_D);
                        if(ACO_debug)
                        {
                            szTempBuffer.Format(L"+%d * %0.2f%%  (Def %d) (%d:%d)",
                            ( (pAttacker->currHitPoints()) - (iNeededRoundsDefender-1)*iDamageToAttacker),100.0f*getCombatOddsSpecific(pAttacker,pDefender,iNeededRoundsDefender-1,n_D),100-n_D*iDamageToDefender,iNeededRoundsDefender-1,n_D);
                            szString.append(NEWLINE);szString.append(szTempBuffer.GetCString());
                        }

                    //prob_attack[iNeededRoundsDefender-1] += getCombatOddsSpecific(pAttacker,pDefender,iNeededRoundsDefender-1,n_D);

                    }
                }
                // finished with the attacker HP I think.








                if(ACO_debug)
                {
                    szTempBuffer.Format(L"E[HP DEFENDER]\nOdds that attacker dies or retreats");
                    szString.append(NEWLINE);szString.append(szTempBuffer.GetCString());
                }

                for (int n_D = 0; n_D < iNeededRoundsAttacker; n_D++)
                {
                    //prob_defend[n_D] = getCombatOddsSpecific(pAttacker,pDefender,iNeededRoundsDefender,n_D);//attacker dies
                    //prob_defend[n_D] += getCombatOddsSpecific(pAttacker,pDefender,iNeededRoundsDefender-1,n_D);//attacker retreats
                    E_HP_Def += ( (pDefender->currHitPoints()) - n_D*iDamageToDefender) * (getCombatOddsSpecific(pAttacker,pDefender,iNeededRoundsDefender,n_D)+getCombatOddsSpecific(pAttacker,pDefender,iNeededRoundsDefender-1,n_D));
                    if(ACO_debug)
                    {
                        szTempBuffer.Format(L"+%d * %0.2f%%  (Att 0 or %d) (%d:%d)",
                        ( (pDefender->currHitPoints()) - n_D*iDamageToDefender),100.0f*(getCombatOddsSpecific(pAttacker,pDefender,iNeededRoundsDefender,n_D)+getCombatOddsSpecific(pAttacker,pDefender,iNeededRoundsDefender-1,n_D)),100-(iNeededRoundsDefender-1)*iDamageToAttacker,iNeededRoundsDefender,n_D);
                        szString.append(NEWLINE);szString.append(szTempBuffer.GetCString());
                    }
                }

                E_HP_Def_Defeat = E_HP_Def;
                E_HP_Def_Withdraw = 0.0f;

                if (pAttacker->combatLimit() < (pDefender->maxHitPoints() ))//if attacker has a combatLimit (eg. catapult)
                {

                    if (pAttacker->combatLimit() == iDamageToDefender*(iNeededRoundsAttacker-1) )
                    {//Then we have an odd situation because the last successful hit by an attacker will do 0 damage, and doing either iNeededRoundsAttacker or iNeededRoundsAttacker-1 will cause the same damage
                        if(ACO_debug)
                        {
                            szTempBuffer.Format(L"Odds that attacker withdraws at combatLimit (abnormal)");
                            szString.append(NEWLINE);szString.append(szTempBuffer.GetCString());
                        }
                        for (int n_A = 0; n_A < iNeededRoundsDefender; n_A++)
                        {
                            //prob_defend[iNeededRoundsAttacker-1] += getCombatOddsSpecific(pAttacker,pDefender,n_A,iNeededRoundsAttacker);//this is the defender at the combatLimit
                            E_HP_Def += (float)iDefenderHitLimit * getCombatOddsSpecific(pAttacker,pDefender,n_A,iNeededRoundsAttacker);
                            //should be the same as
                            //E_HP_Def += ( (pDefender->currHitPoints()) - (iNeededRoundsAttacker-1)*iDamageToDefender) * getCombatOddsSpecific(pAttacker,pDefender,n_A,iNeededRoundsAttacker);
                             E_HP_Def_Withdraw += (float)iDefenderHitLimit * getCombatOddsSpecific(pAttacker,pDefender,n_A,iNeededRoundsAttacker);
                            if(ACO_debug)
                            {
                                szTempBuffer.Format(L"+%d * %0.2f%%  (Att %d) (%d:%d)",
                                iDefenderHitLimit,100.0f*getCombatOddsSpecific(pAttacker,pDefender,n_A,iNeededRoundsAttacker),100-n_A*iDamageToAttacker,n_A,iNeededRoundsAttacker);
                                szString.append(NEWLINE);szString.append(szTempBuffer.GetCString());
                            }
                        }

                    }
                    else // normal situation
                    {
                        if(ACO_debug)
                        {
                        szTempBuffer.Format(L"Odds that attacker withdraws at combatLimit (normal)",pAttacker->combatLimit());
                        szString.append(NEWLINE);szString.append(szTempBuffer.GetCString());
                        }
                        //prob_defend[iNeededRoundsAttacker] = 0.0f;
                        for (int n_A = 0; n_A < iNeededRoundsDefender; n_A++)
                        {
                            //prob_defend[iNeededRoundsAttacker] += getCombatOddsSpecific(pAttacker,pDefender,n_A,iNeededRoundsAttacker);//this is the defender at the combatLimit

                            E_HP_Def += (float)iDefenderHitLimit * getCombatOddsSpecific(pAttacker,pDefender,n_A,iNeededRoundsAttacker);
                            E_HP_Def_Withdraw += (float)iDefenderHitLimit * getCombatOddsSpecific(pAttacker,pDefender,n_A,iNeededRoundsAttacker);
                            if(ACO_debug)
                            {
                                szTempBuffer.Format(L"+%d * %0.2f%%  (Att %d) (%d:%d)",
                                iDefenderHitLimit,100.0f*getCombatOddsSpecific(pAttacker,pDefender,n_A,iNeededRoundsAttacker),100-n_A*iDamageToAttacker,n_A,iNeededRoundsAttacker);
                                szString.append(NEWLINE);szString.append(szTempBuffer.GetCString());
                            }
                        }//for
                    }//else


                }








                float AttackerKillOdds = 0.0f;
                float PullOutOdds = 0.0f;
                float RetreatOdds = 0.0f;
                float DefenderKillOdds = 0.0f;

                // General odds with XP values included, and coloured
                if(pAttacker->combatLimit() == (pDefender->maxHitPoints() )) //ie. we can kill the defender... I hope this is the most general form
                {
                    //float AttackerKillOdds = 0.0f;
                    for (int n_A = 0; n_A < iNeededRoundsDefender; n_A++) {
                    AttackerKillOdds += getCombatOddsSpecific(pAttacker,pDefender,n_A,iNeededRoundsAttacker);
                }//for
                szTempBuffer.Format(SETCOLR L"Victory " ENDCOLR L"%0.2f%% (" SETCOLR L"%dXP" ENDCOLR L")  (" SETCOLR L"%0.1fHP" ENDCOLR L")",
                                    TEXT_COLOR("COLOR_POSITIVE_TEXT"),100.0f*AttackerKillOdds,TEXT_COLOR("COLOR_POSITIVE_TEXT"),iExperience,TEXT_COLOR("COLOR_POSITIVE_TEXT"), E_HP_Att_Victory/AttackerKillOdds);
                }
                else
                { // else we cannot kill the defender (eg. catapults attacking)
                    //float PullOutOdds = 0.0f;
                    for (int n_A = 0; n_A < iNeededRoundsDefender; n_A++)
                    {
                    PullOutOdds += getCombatOddsSpecific(pAttacker,pDefender,n_A,iNeededRoundsAttacker);
                    //if (pAttacker->combatLimit() == iDamageToDefender*(iNeededRoundsAttacker-1) ) {
                    //    PullOutOdds += getCombatOddsSpecific(pAttacker,pDefender,n_A,iNeededRoundsAttacker);
                    //    }
                    }//for

                    szTempBuffer.Format(SETCOLR L"Withdraw " ENDCOLR L"%0.2f%% (" SETCOLR L"%dXP" ENDCOLR L")  (" SETCOLR L"%0.1fHP" ENDCOLR L"," SETCOLR L"%dHP" ENDCOLR L")",
                                        TEXT_COLOR("COLOR_POSITIVE_TEXT"),100.0f*PullOutOdds,TEXT_COLOR("COLOR_POSITIVE_TEXT"),GC.getDefineINT("EXPERIENCE_FROM_WITHDRAWL"),
                                        TEXT_COLOR("COLOR_POSITIVE_TEXT"), E_HP_Att_Withdraw/PullOutOdds, TEXT_COLOR("COLOR_NEGATIVE_TEXT"), iDefenderHitLimit);
                }
                    szString.append(NEWLINE);szString.append(szTempBuffer.GetCString());

                if ((pAttacker->withdrawalProbability()) > 0)
                {

                for (int n_D = 0; n_D < iNeededRoundsAttacker; n_D++) {
                    RetreatOdds += getCombatOddsSpecific(pAttacker,pDefender,iNeededRoundsDefender-1,n_D);
                }//for

                }

                for (int n_D = 0; n_D < iNeededRoundsAttacker; n_D++)
                {
                    DefenderKillOdds += getCombatOddsSpecific(pAttacker,pDefender,iNeededRoundsDefender,n_D);
                }//for
                //DefenderKillOdds = 1.0f - (AttackerKillOdds + RetreatOdds + PullOutOdds);//this gives slight negative numbers sometimes, I think


                if ((pAttacker->withdrawalProbability()) > 0)
                {
                    szTempBuffer.Format(SETCOLR L"Retreat " ENDCOLR L"%0.2f%% (" SETCOLR L"%dXP" ENDCOLR L")  (" SETCOLR L"%dHP" ENDCOLR L"," SETCOLR L"%.1fHP" ENDCOLR L")",
                                        TEXT_COLOR("COLOR_POSITIVE_TEXT"),100.0f*RetreatOdds,TEXT_COLOR("COLOR_POSITIVE_TEXT"),GC.getDefineINT("EXPERIENCE_FROM_WITHDRAWL"),
                                        TEXT_COLOR("COLOR_POSITIVE_TEXT"),E_HP_Att_Retreat,TEXT_COLOR("COLOR_NEGATIVE_TEXT"),E_HP_Def_Defeat/(RetreatOdds+DefenderKillOdds));
                    szString.append(NEWLINE);szString.append(szTempBuffer.GetCString());
                }
                szTempBuffer.Format(SETCOLR L"Defeat " ENDCOLR L"%0.2f%% (" SETCOLR L"%dXP" ENDCOLR L")  (" SETCOLR L"%0.1fHP" ENDCOLR L")",
                                    TEXT_COLOR("COLOR_NEGATIVE_TEXT"),100.0f*DefenderKillOdds,TEXT_COLOR("COLOR_NEGATIVE_TEXT"),iDefExperienceKill,TEXT_COLOR("COLOR_NEGATIVE_TEXT"),E_HP_Def_Defeat/(RetreatOdds+DefenderKillOdds));
                szString.append(NEWLINE);szString.append(szTempBuffer.GetCString());








                szTempBuffer.Format(SETCOLR L"%d" ENDCOLR L"," SETCOLR L"%d" ENDCOLR L" HP per hit (" SETCOLR L"%d" ENDCOLR L"," SETCOLR L"%d" ENDCOLR L" hits). Odds: %.1f%%",
                                    TEXT_COLOR("COLOR_POSITIVE_TEXT"), iDamageToDefender, TEXT_COLOR("COLOR_NEGATIVE_TEXT"), iDamageToAttacker,TEXT_COLOR("COLOR_POSITIVE_TEXT"),iNeededRoundsAttacker,TEXT_COLOR("COLOR_NEGATIVE_TEXT"), iNeededRoundsDefender,float(iAttackerOdds)*100.0f / float(GC.getDefineINT("COMBAT_DIE_SIDES")));
                szString.append(NEWLINE);szString.append(szTempBuffer.GetCString());



                //szTempBuffer.Format(L"E[HP] = (" SETCOLR L"%d->%0.2fHP" ENDCOLR L"," SETCOLR L"%d->%0.1fHP" ENDCOLR ,
                //                    TEXT_COLOR("COLOR_POSITIVE_TEXT"),(pAttacker->currHitPoints()),E_HP_Att,TEXT_COLOR("COLOR_NEGATIVE_TEXT"),(pDefender->currHitPoints()),E_HP_Def);
                //szString.append(NEWLINE);szString.append(szTempBuffer.GetCString());




                szTempBuffer.Format(SETCOLR L"Attacker Unharmed " ENDCOLR L"%0.2f%%\n" SETCOLR L"Defender Unharmed " ENDCOLR L"%0.2f%%",
                                    TEXT_COLOR("COLOR_POSITIVE_TEXT"),100.0f*AttackerUnharmed,TEXT_COLOR("COLOR_NEGATIVE_TEXT"),100.0f*DefenderUnharmed);
                szString.append(NEWLINE);szString.append(szTempBuffer.GetCString());


                //if(pAttacker->combatLimit() < GC.getMAX_HIT_POINTS())
                //    {
                //        szTempBuffer.Format(L"Attacker combatLimit = %d",pAttacker->combatLimit());
                //        szString.append(NEWLINE);
                //        szString.append(szTempBuffer.GetCString());
                //    }//if


                szTempBuffer.Format(L"--------------------------------------------");
                szString.append(NEWLINE);
                szString.append(szTempBuffer.GetCString());

                /*************************************************************************************************/
                /** ADVANCED COMBAT ODDS                      13/02/09                          PieceOfMind      */
                /** END                                                                                          */
                /*************************************************************************************************/

The other files haven't changed.

That makes perfect sense. Would it be too unclear to remove the defender's E[HP] from the retreat outcome? We know the Defeat outcome's E[HP] applies, but will other people figure that out easily?
My preference would be to leave it there for the moment.

If you want to make the level of detail configurable now, it's pretty simple. At the top of your changes, add this to grab the value from the XML:

Code:
int iDetail = GC.getDefineINT("COMBAT_ODDS_DETAIL");

You might want to define constants for the different levels in a header file, or just use 0, 1, 2, ...

Code:
// AdvancedCombatOdds.h

#pragma once

#ifndef ADVANCED_COMBAT_ODDS_H
#define ADVANCED_COMBAT_ODDS_H

#define ACO_DETAIL_LOW     0
#define ACO_DETAIL_MEDIUM  1
#define ACO_DETAIL_HIGH    2

#endif

Then it's just a matter of checking the detail before adding some text block.

Code:
// only show E[HP] if at medium or higher
if (iDetail >= ACO_DETAIL_MEDIUM)
{
    ...
}

To merge this with BUG, I'll only need to change the top line that grabs the current setting.

I'll do my best to implement this sort of thing. Just one question though... That header file you suggested is a new file right? I'll have to create AdvancedCombatOdds.h and just put those few lines in?

With regard to the options, did we want to allow greater customisability with the highest detail setting? If so, what would be the best way to go about it?

Perhaps the high detail should be renamed to custom, or an additional setting of custom should be added. Or is this too many settings already?
 
I've prefered using commas to this point because a "/" could cause one to think it's a fraction of some sort.

Oops, I meant to switch the / to a comma, not the other way around. I had mixed them in my initial suggestion. I agree with commas instead of / since you're displaying math-related info.

Perhaps if there are no retreat odds or withdraw odds it should be omitted though?

I'm usually a fan of consistency. I like having that info on a single line up top, even if it is redundant to the info right below it. If the display differs based on the possible outcomes, it has a higher chance of causing confusion.

Actually up until now I'd also been avoiding colouring the actual percentages when possible because white text is a bit easier to read usually.

Then how to make it clear that one is attacker and the other defender?

Survival: 63.2% vs. 51.8%​

You might notice in my todo list was an idea that I could somehow increase the opacity of the display so the numbers are a bit easier to read when there is a lot of background detail behind them.

I have had zero success changing anything but the location of the hover text area. I think it is handled entirely by the EXE graphics engine. For example, whereas I can change the location of it, there's a final "minimum width" parameter to the function call. It seems to ignore whatever I pass in for that value. :(

That header file you suggested is a new file right? I'll have to create AdvancedCombatOdds.h and just put those few lines in?

Exactly right.

With regard to the options, did we want to allow greater customisability with the highest detail setting? If so, what would be the best way to go about it?

I think first is to lay out the full set of display values. Then we can see if a fully-customizable view is even necessary. I was thinking there is really only 4 pieces of data:

  1. XP per outcome
  2. Probability of no damage
  3. E[HP] per outcome
  4. Probability of each HP combination
These seem to logically progress from simple to complex data, but I can see 2 and 3 being interchangeable. Best to nail down the full set of information, then figure out how to group them.
 
Here are my current suggestions...
In each of the medium and high lists, I emphasised the ones not included in the lower detail levels.

Note in the following lists, by "unconditional" I mean not conditioned on any event, except technically the "sure event". In other words, conditioned against all possible events - the set of events with probability 1.

Master List:
  • Survival odds
  • Main battle outcome probabilities (Victory, Defeat, Retreat, Withdraw)
  • XP for each battle outcome (beside main battle outcomes)
  • Expected hitpoints conditioned on battle outcome (beside battle outcomes)
  • Unconditional Attacker Unharmed Odds
  • Unconditional Defender Unharmed Odds
  • Unconditional expected Attacker,Defender HP
  • Att,Def damage per hit and number of hits and odds of hits
  • Probability of every possible Attacker HP outcome
  • Probability of every possible Defender HP outcome

Low detail:
  • Survival odds
  • Main battle outcome probabilities (Victory, Defeat, Retreat, Withdraw)
  • XP for each battle outcome (beside main battle outcomes)

Medium detail:
  • Survival odds
  • Main battle outcome probabilities (Victory, Defeat, Retreat, Withdraw)
  • XP for each battle outcome (beside main battle outcomes)
  • Expected hitpoints conditioned on battle outcome (beside battle outcomes)
  • Att,Def damage per hit and number of hits and odds of hits
  • Unconditional Attacker Unharmed Odds
  • Unconditional Defender Unharmed Odds

High Detail:
  • Survival odds
  • Main battle outcome probabilities (Victory, Defeat, Retreat, Withdraw)
  • XP for each battle outcome (beside main battle outcomes)
  • Expected hitpoints conditioned on battle outcome (beside battle outcomes)
  • Att,Def damage per hit and number of hits and odds of hits
  • Probability of every possible Attacker HP outcome
  • Probability of every possible Defender HP outcome
  • Unconditional Attacker Unharmed Odds
  • Unconditional Defender Unharmed Odds
  • Unconditional expected Attacker,Defender HP

In particular, the "Probability of every possible Defender HP outcome" I'm very unsure about including even at the high detail level. I would be inclined to include this only when it is enabled via a customise option. Another thing I am unsure about is whether to make the "Probability of every possible Combatant HP outcome" odds conditioned on unit surviving or not. Roland do you have any input on this, since you seem to be the one in favour of something like this?

What do people think of these lists?

In my view, XP is important enough to include at every level of detail, especially because it does not take much room and is not too distracting. Note the low detail setting still has the added advantage over the game's odds that it does not suffer from the Withdraw Odds bug which I mentioned in the first post. Also, it is able to separate Withdraw and Retreat odds.
 
The only thing I would change in the lists would be to move the unharmed probabilities to low level. This is something that I think people will want to see when choosing, say, which unit to use after suiciding their siege units. But it's not an obvious decision either way. :confused:

Hmm, there are so many options that I'm sure no matter what we choose, someone will disagree.

For the full details being a separate option, perhaps just add another level: "Everything". ;)
 
And since the inclusion of various statistics is included in every higher level of detail, I am thinking it would be better to use an integer to set the detail, so I can just write something like:

if(detail>2) {blah;}

etc. each time I need to print something. Low level could be 1, everything could be 4.

After suiciding siege units, I tend to pick units that earn the best XP or have the highest expected hitpoints. Usually the units with the highest expected hitpoints are the same as the units with the highest unharmed odds, but then the highest hitpoints are only revealed at medium as well.

Personally I'd say the Unharmed odds for Attatacker and Defender in most battles are so low that it's not worth including at the lowest level of detail.


I was thinking low detail really should be low detail - for users who want the bare minimum over the ordinary game's display. I would hope for medium detail to be the option most people would find the most useful, and this would be the default setting. High detail should really only be for the people who really indulge in the detail and aren't intimidated by a bit of math or a few extra numbers.

Lol the Everything level will be a bit silly but it will give enough detail in those rare situations where you need everything.

So the new lists:

Spoiler :
Low detail:
  • Survival odds
  • Main battle outcome probabilities (Victory, Defeat, Retreat, Withdraw)
  • XP for each battle outcome (beside main battle outcomes)

Medium detail:
  • Survival odds
  • Main battle outcome probabilities (Victory, Defeat, Retreat, Withdraw)
  • XP for each battle outcome (beside main battle outcomes)
  • Expected hitpoints conditioned on battle outcome (beside battle outcomes)
  • Att,Def damage per hit and number of hits and odds of hits
  • Unconditional Attacker Unharmed Odds
  • Unconditional Defender Unharmed Odds

High Detail:
  • Survival odds
  • Main battle outcome probabilities (Victory, Defeat, Retreat, Withdraw)
  • XP for each battle outcome (beside main battle outcomes)
  • Expected hitpoints conditioned on battle outcome (beside battle outcomes)
  • Att,Def damage per hit and number of hits and odds of hits
  • Probability of every possible Attacker HP outcome
  • Unconditional Attacker Unharmed Odds
  • Unconditional Defender Unharmed Odds

Everything!:
  • Survival odds
  • Main battle outcome probabilities (Victory, Defeat, Retreat, Withdraw)
  • XP for each battle outcome (beside main battle outcomes)
  • Expected hitpoints conditioned on battle outcome (beside battle outcomes)
  • Unconditional Attacker Unharmed Odds
  • Unconditional Defender Unharmed Odds
  • Unconditional expected Attacker,Defender HP
  • Att,Def damage per hit and number of hits and odds of hits
  • Probability of every possible Attacker HP outcome
  • Probability of every possible Defender HP outcome
  • Unconditional expected Attacker,Defender HP


Note I moved "Unconditional expected Attacker,Defender HP" up to the everything level.
 
And since the inclusion of various statistics is included in every higher level of detail, I am thinking it would be better to use an integer to set the detail . . .

That's exactly what those #define statements do, only it makes the code more readable (words vs. numbers) and allows you to change the numbers without changing all the code--just the #defines in the header. The compiler only sees the numbers in the end, and it lines up with how BUG handles text dropdown options: their value is an integer from 0 to N-1, the index of the item selected.

You've convinced me about the odds of being unharmed. The only odd thing I see is splitting the odds of each HP for attacker and defender across two levels. All the other values that apply to both attacker and defender are shown in the same level, but I'm not particular about this one. I can see the value of the attacker HP odds details over the defender's.

If you want to get nuts, I did recently add sliders to BUG's options screen. We could have a slider for level of detail from 0 to 100! :lol: Okay, 10 to keep it reasonable. :rolleyes:
 
That's exactly what those #define statements do, only it makes the code more readable (words vs. numbers) and allows you to change the numbers without changing all the code--just the #defines in the header. The compiler only sees the numbers in the end, and it lines up with how BUG handles text dropdown options: their value is an integer from 0 to N-1, the index of the item selected.

Ahh of course. I had barely even looked at the actual details of the code you had provided me. What you said makes sense to me now.

You've convinced me about the odds of being unharmed. The only odd thing I see is splitting the odds of each HP for attacker and defender across two levels. All the other values that apply to both attacker and defender are shown in the same level, but I'm not particular about this one. I can see the value of the attacker HP odds details over the defender's.

I think I'd opt to go without the Defender detailed HP because usually people attack at good odds (whether because of superior units or injured defenders) and also that even at the highest detail setting I don't want people to be overwhelmed with numbers. But I agree it's hard to decide. I could really could swing either way I guess.

By the way, the format for displaying these detailed HP results would be something like:

(using the example pic from a few posts above)

Victory 75.11% (3XP) (53.0HP)
Retreat 7.47% (1XP) (15HP, 36.8HP)
Withdraw 0.00%
100HP: 5.31%
83HP: 11.80%
66HP: 15.71%
49HP: 16.28%
32HP: 14.45%
15HP: 19.02%
Defeat 17.43% (2XP) (36.8HP)
100HP: 0.77%
78HP: 2.56%
56HP: 4.97%
34HP: 7.37%
12HP: 9.23%

I just noticed... with this added detail there's no longer any point displaying the separate Att/Def Unharmed odds - it's already covered here.

If you want to get nuts, I did recently add sliders to BUG's options screen. We could have a slider for level of detail from 0 to 100! :lol: Okay, 10 to keep it reasonable. :rolleyes:
:lol:

EDIT By the way EmperorFool I couldn't get the x character to work. It came up with a square and the rest of the stuff without the # there. This is how I tried to use it:

szTempBuffer.Format(L"blah blah blah &#x0D7; blah blah blah");

Do you know why it might not have worked?
 
What, no histogram?! :mischief:

I think we're pretty much agreed; neither of us has a strong opinion of which item belongs to which level. That means we'll never guess what the various users will want. I think we're as close as possible, however. :goodjob:

To be clear, having the display of each piece be controlled by a separate option would be just as easy as the single level of detail setting. I just think that 99% of the users will be happy to pick a level that shows at least what they want and ignore the rest. You need to ask yourself if you want to try to cover the other 1% with an additional Custom level.

For stand-alone, you can store the custom settings as individual boolean XML entries. When included in BUG, they would just point to individual checkbox options. I can still leave them off the screen and the user can change them directly in the INI file.

This could be abstracted into a function:

Code:
bool isAcoOption(int iDetail, int iMinDetail, const char* szOption)
{
    if (iDetail == ACO_DETAIL_CUSTOM)
    {
        return GC.getDefineBOOL(szOption);
        // and for BUG
        // return getBugOptionBOOL(szOption);
    }
    return iDetail >= iMinDetail;
}

...

// display odds that attacker survives unharmed
if (isAcoOption(iDetail, ACO_DETAIL_MEDIUM, "ACO_ODDS_UNHARMED_ATT"))
{
    ...
}
 
Top Bottom