PieceOfMind's Advanced Combat Odds

phungus,
I'm not sure why you want first strikes in the display since they're shown at the bottom anyway, but using them in the code is not hard. The thing is, you have to be careful whether the opponent is immune to first strikes. Here's what I used in CvGameCoreUtils:

Code:
AttFSnet = ( (pDefender->immuneToFirstStrikes()) ? 0 : pAttacker->firstStrikes() ) - ((pAttacker->immuneToFirstStrikes()) ? 0 : pDefender->firstStrikes());
    AttFSC = (pDefender->immuneToFirstStrikes()) ? 0 : (pAttacker->chanceFirstStrikes());
    DefFSC = (pAttacker->immuneToFirstStrikes()) ? 0 : (pDefender->chanceFirstStrikes());
 
I, too, would like to see something about first strikes added, only because this is the Advanced Combat Odds calculator. ;) And PoM is allllll about those yummy Drill promotions, right? :lol:

From reading your article, PieceOfMind, I believe this is the info we need: who gets them, how many, what probability for each "chance" strike.

This battle is between a unit with 3-5 first strikes and a unit with 1 first strike. 1 FS cancels out on each side, leaving the attacker with 2-4 FS. Oh, I assume that 0 chances is one of the options, so with 2 chances there are 3 possibilities, each at 33.33%, right?

First Strikes: 2+2 at 33.33%

Now when both units have FS chances, IIRC the dice are rolled for each units' chances once at the start of the battle, the one that totals to fewer cancels out against the one with more, and then the one with more gets their remaining chances, right? However, we need to show the chances for both units and cancel out only the guaranteed FS.

This battle is the same units as above, but the defender also has 1 FS chance (1-2 FS).

First Strikes: 1+2 at 33.33% vs. 1 at 50.00%

As useful as I think first strikes, at this point I can only disagree with any need to add them to the combat odds. The average number of first strikes that get converted is always the average of the two numbers x-y. If you have 4-7 first strikes it's (4+7)/2=5.5.

The display is already very full of information and first strikes are already printed at the bottom. The fact that first strikes can be cancelled out on occasion is not a very important fact to include in the combat odds.

There's also the risk that any first strike details with odds of each occuring is only going to be ambiguous and make people confused.

EF, you are correct about the odds you found but I don't think the example displays you printed are very intuitive.
 
I don't think the example displays you printed are very intuitive.

Yes, I felt the same way after posting. ;) And I didn't find any better way that would remain on one line.

What is really needed is some Civilopedia strategy text for each of the promotions, or combat as a whole, that describes how FS work along with other combat details.
 
Any ideas on how to call the # of First Strikes for pUnit? I've got a thread on it here: http://forums.civfanatics.com/showthread.php?t=311653

Edit: thanks for the help there POM, I have no idea what I did wrong... Anyway, this is basically what I had in mind:

Code, should be self explanitory. Basically it finds the average expected first strike rounds and displays them:
Spoiler :
Code:
				if (tmpDetail > 2)
				{
				int DefFS = ((pAttacker->immuneToFirstStrikes()) ? 0 : pDefender->firstStrikes());
				int AttFS = ((pDefender->immuneToFirstStrikes()) ? 0 : pAttacker->firstStrikes());
				int AttFSC = ((pDefender->immuneToFirstStrikes()) ? 0 : (pAttacker->chanceFirstStrikes()));
				int DefFSC = ((pAttacker->immuneToFirstStrikes()) ? 0 : (pDefender->chanceFirstStrikes()));
				float NetFS = (AttFS + AttFSC / 2.0f - DefFS - DefFSC / 2.0f);
					if (NetFS > 0)
					{
						szTempBuffer.Format(L"\nFirst " SETCOLR L"%.1f" ENDCOLR L" Rounds Invulnerable to Defender",
											TEXT_COLOR("COLOR_POSITIVE_TEXT"),NetFS);
											;szString.append(szTempBuffer.GetCString());
					}
					else
					{
					if (NetFS < 0 )
					{
						NetFS = (0 - NetFS);
						szTempBuffer.Format(L"\nFirst " SETCOLR L"%.1f" ENDCOLR L" Rounds Invulnerable to Attacker",
											TEXT_COLOR("COLOR_NEGATIVE_TEXT"),NetFS);
											;szString.append(szTempBuffer.GetCString());
					}
				szTempBuffer.Format(L" ");
                //szString.append(NEWLINE);
                szString.append(szTempBuffer.GetCString());
				}
This is what it looks like:
 
This is mainly for you phungus...

Here is a copy of the code I'm working with at the moment. Ideally I'd still like to add Flanking details to it before I release another version though. There are a number of changes so far. I think there may have been a bugfix too. I'm not sure if in v0.4 it was always showing the correct detailed HP results for defenders when attacking with cannons, catapults etc. I'm pretty sure it's working now in any case.

I just read your edit. At this stage I'm struggling to see much use in the line you've added. It has the potential to mislead at the moment. If a samurai (2FS) attacks a Drill IV skirmisher (4-8 first strikes), then your code would report

First 4.0 rounds invulnerable to attacker.

But in reality it could be the first 2 rounds invulnerable to the attacker or up to 6 rounds invulnerable to the attacker.

If 2 Drill IV skirmisher's fight, nothing would be displayed, yet the results are different than they are for 2 unpromoted skirmishers.

I know they are the averages but how important is knowing the averages? I'm more focused on outcomes of battle and their probabilities. Since knowing the av. first strike rounds does not influence the outcome of battle any more than what is already presented in the earlier information, I'm struggling to see the point. Really the only thing I've included so far that isn't outcome focused is the damager per hit etc. line.

But part of the reason I'm being more critical now is that at low resolutions Everything detail is already pushing the limits of how much can be fit in. At 1920x1200, which I run, I only use up about half of the available room lol.
 
I just read your edit. At this stage I'm struggling to see much use in the line you've added. It has the potential to mislead at the moment...
Yeah, what I posted shouldn't be included. I'm still thinking about a good, and useful way to note the effects of First Strikes in ACO. I'll post it here when I have something.

Quick Bugfix
Spoiler :
In the Beggining of the code with all the Experience calculations add in:
Code:
				int iWithdrawXP;
					iWithdrawXP = GC.getDefineINT("EXPERIENCE_FROM_WITHDRAWL");
Then replace all instances of GC.getDefineINT("EXPERIENCE_FROM_WITHDRAWL") with iWithdrawXP

Fixes Negative XP Bug from Barb and Animal Normalization
Code:
                if(pDefender->isBarbarian())
                {
                    if(pDefender->isAnimal())
                    {//animal
                        iExperience = range(iExperience,0,GC.getDefineINT("ANIMAL_MAX_XP_VALUE")-(pAttacker->getExperience()));
						if (iExperience < 0 ) {
						iExperience = 0; }
						iWithdrawXP = range(iWithdrawXP,0,GC.getDefineINT("ANIMAL_MAX_XP_VALUE")-(pAttacker->getExperience()));
						if (iWithdrawXP < 0 ) {
						iWithdrawXP = 0; }
                        iBonusAttackerXP = range(iBonusAttackerXP,0,GC.getDefineINT("ANIMAL_MAX_XP_VALUE")-(pAttacker->getExperience() + iExperience));
						if (iBonusAttackerXP < 0 ) {
						iBonusAttackerXP = 0; }
                        iBonusWithdrawXP = range(iBonusWithdrawXP,0,GC.getDefineINT("ANIMAL_MAX_XP_VALUE")-(pAttacker->getExperience() + iWithdrawXP));
						if (iBonusWithdrawXP < 0 ) {
						iBonusWithdrawXP = 0; }
                    }
                    else
                    {//normal barbarian
                        iExperience = range(iExperience,0,GC.getDefineINT("BARBARIAN_MAX_XP_VALUE")-pAttacker->getExperience());
						if (iExperience < 0 ) {
						iExperience = 0; }
						iWithdrawXP = range(iWithdrawXP,0,GC.getDefineINT("BARBARIAN_MAX_XP_VALUE")-(pAttacker->getExperience()));
						if (iWithdrawXP < 0 ) {
						iWithdrawXP = 0; }
                        iBonusAttackerXP = range(iBonusAttackerXP,0,GC.getDefineINT("BARBARIAN_MAX_XP_VALUE")-(pAttacker->getExperience() + iExperience));
						if (iBonusAttackerXP < 0 ) {
						iBonusAttackerXP = 0; }
                        iBonusWithdrawXP = range(iBonusWithdrawXP,0,GC.getDefineINT("BARBARIAN_MAX_XP_VALUE")-(pAttacker->getExperience() + iWithdrawXP));
						if (iBonusWithdrawXP < 0 ) {
						iBonusWithdrawXP = 0; }
                    }
                }
Edit: Added in withdraw fix.
 
I just thought of a very handy alternate view for this, though slightly unrelated and possibly anti-UG: hold control to see the odds as if the battle were reversed.

My injured Warrior (0.9/2.0) with Combat I (+10%) and Woodsman I (+20%) has been healing on a jungle grassland hill (+75%) for two turns (+10%). Along comes a healthy barbarian Warrior (2.0). Should I move my Warrior to avoid being attacked, possibly moving next to another barbarian and losing his fortify bonus, or should I hold fast? I'd love ACO to let me see the barbarian's odds against my Warrior.

What do you think of this idea? It would be pretty trivial to implement by simply flipping pAttacker and pDefender if the control key was held down.
 
I just thought of a very handy alternate view for this, though slightly unrelated and possibly anti-UG: hold control to see the odds as if the battle were reversed.

My injured Warrior (0.9/2.0) with Combat I (+10%) and Woodsman I (+20%) has been healing on a jungle grassland hill (+75%) for two turns (+10%). Along comes a healthy barbarian Warrior (2.0). Should I move my Warrior to avoid being attacked, possibly moving next to another barbarian and losing his fortify bonus, or should I hold fast? I'd love ACO to let me see the barbarian's odds against my Warrior.

What do you think of this idea? It would be pretty trivial to implement by simply flipping pAttacker and pDefender if the control key was held down.

Yeah I thought of that a few days ago. lol. I agree it would be pretty handy. I'll have to have a think about how to do it.
 
Code:
if (DLL->ctrlKey())
{
    CvUnit* swap = pAttacker;
    pAttacker = pDefender;
    pDefender = swap;
}
 
Fixes Negative XP Bug from Barb and Animal Normalization

Wait... Before I fix it. I'm not seeing what the bug is exactly. What's a situation where the xp goes negative?
 
Code:
if (DLL->ctrlKey())
{
    CvUnit* swap = pAttacker;
    pAttacker = pDefender;
    pDefender = swap;
}

Thanks for the code. Just wondering though... Should this be allowed? Also, what happens when there are more than 2 defenders on the tile? We'd only be looking at combat with their best defender - not their best attacker, right?
 
Wait... Before I fix it. I'm not seeing what the bug is exactly. What's a situation where the xp goes negative?
If you're over 10XP, it corrects by showing negative XP being earned. Also for the iWithdraw, it doesn't properly show 0XP for barbs, and just replacing withdraw XP with a stored variable normalizes things, the same way it's done in the rest of the code.


For the switcharoo thing, I had to change EF's code slightly to this
Code:
				bool bctrl; bctrl = gDLL->ctrlKey(); if (bctrl)
				{
					CvUnit* swap = pAttacker;
					pAttacker = pDefender;
					pDefender = swap;
				}
Very cool.
Just wondering though... Should this be allowed? Also, what happens when there are more than 2 defenders on the tile? We'd only be looking at combat with their best defender - not their best attacker, right?
I say include it. It might be good to add in an XML value to disable it though, but that's pretty easy to do. Unfortunately it does pick the best defender, instead of the best attacker, but it's still very cool, and most of the time the two are interchangeable, from an individual unit's perspective.
 
Should this be allowed?

I'm still undecided. The player can calculate all this by hand, so why not?

Also, what happens when there are more than 2 defenders on the tile? We'd only be looking at combat with their best defender - not their best attacker, right?

Correct. I don't know how easy it is to acquire the best attacker for a plot, but if you can you could do that instead. But at least the best defender would be better than nothing.
 
I just thought of a very handy alternate view for this, though slightly unrelated and possibly anti-UG: hold control to see the odds as if the battle were reversed.

My injured Warrior (0.9/2.0) with Combat I (+10%) and Woodsman I (+20%) has been healing on a jungle grassland hill (+75%) for two turns (+10%). Along comes a healthy barbarian Warrior (2.0). Should I move my Warrior to avoid being attacked, possibly moving next to another barbarian and losing his fortify bonus, or should I hold fast? I'd love ACO to let me see the barbarian's odds against my Warrior.

What do you think of this idea? It would be pretty trivial to implement by simply flipping pAttacker and pDefender if the control key was held down.

This would greatly enhance this mod (although it's already a very good utility).

If possible, it should determine the best attacker from the various enemies when using this. Why would this be very hard? It's just a comparison between the various combat odds when these units would attack. Or am I missing something? By the way, this is not the same as the unit that the AI will use first when attacking. The AI will use the unit that will most cost effectively inflict damage when the odds are very bad. But that's not an issue for this mod. The best attacker would be the most useful piece of information.
 
I just thought of a very handy alternate view for this, though slightly unrelated and possibly anti-UG: hold control to see the odds as if the battle were reversed.

My injured Warrior (0.9/2.0) with Combat I (+10%) and Woodsman I (+20%) has been healing on a jungle grassland hill (+75%) for two turns (+10%). Along comes a healthy barbarian Warrior (2.0). Should I move my Warrior to avoid being attacked, possibly moving next to another barbarian and losing his fortify bonus, or should I hold fast? I'd love ACO to let me see the barbarian's odds against my Warrior.

What do you think of this idea? It would be pretty trivial to implement by simply flipping pAttacker and pDefender if the control key was held down.

I take it that you mean the odds of the defender (I guess you could probably calculate those, so I'm not sure if it would be anti-BUG). But, the one problem I could see is how would you determine the odds for multiple units in an opponent's stack (would it automatically match up with the best attacker?)?
 
There must be some function that the AI uses to choose which unit to use to attack a plot. We could use that.

Ruff Hi and I were discussing it this morning, and he came up with a good suggestion that also addresses the UGness of the feature: average the odds for all units on the plot if there are multiple enemies and then show only a rough estimate of the odds by rounding it to the nearest 10% or 20% or even show it as words. The reasoning is that you cannot see the odds in the normal game, and no one but PieceOfMind would write a program to calculate the actual odds outside of Civ4. Thus, this feature as I proposed it would be somewhat anti-UG.
 
There must be some function that the AI uses to choose which unit to use to attack a plot. We could use that.

Ruff Hi and I were discussing it this morning, and he came up with a good suggestion that also addresses the UGness of the feature: average the odds for all units on the plot if there are multiple enemies and then show only a rough estimate of the odds by rounding it to the nearest 10% or 20% or even show it as words. The reasoning is that you cannot see the odds in the normal game, and no one but PieceOfMind would write a program to calculate the actual odds outside of Civ4. Thus, this feature as I proposed it would be somewhat anti-UG.

I also would sometimes use a program to determine the odds of my defenders. Not often, but when it's critical.
 
But, the one problem I could see is how would you determine the odds for multiple units in an opponent's stack (would it automatically match up with the best attacker?)?

The best way would be to come up with expected HP loss values for each one, and show them all in descending order. The reason you'd want to see them all is just in case they do multiple attacks - no individual unit may be able to hurt mine, but the cumulative effect of them all could still be enough to kill it.

(Once you've determined which one has the greatest expected damage, it becomes the one you get the Everything Report on if you press Shift and Ctrl at the same time.)

I'd include an extra column after the expected HP loss for the cumulative total of all units up to that point. Make the lines where that equals or exceeds the HP (current plus expected healing if it remains stationary this turn) of the defender red, and any lines where it puts the unit below 50% in yellow. (For those color-blind people, maybe put a little skull-n-bones icon to the right of the line where HP is expected to reach fatal.) Then I can see how many units the opponent will have to send after it to take it out.

It gets more complicated when there are multiple potential defenders; you'd have a lot of possible HP losses for each battle; depending on those, the defender for the next would change. This, and collateral damage, would make assessing how much damage your entire stack could handle very difficult. And we haven't even gotten into what happens when you're attacked from multiple tiles, with close air support, etc.
 
Edit: OK found this in CVSelectionGroupAI:

So I tried this:
Spoiler :
Code:
					int CvSelectionGroupAI::AI_attackOdds(const CvPlot* pPlot, bool bPotentialEnemy) const
					{
						CvUnit* pAttacker;
						FAssert(getOwnerINLINE() != NO_PLAYER);
						int iOdds = 0;
						pAttacker = AI_getBestGroupAttacker(pPlot, bPotentialEnemy, iOdds);
					}
But it says "CvSelectionGroupAI::AI_attackOdds' : local function definitions are illegal" when I try to compile.
What's up with that, it's directly copied from the the SelectionGroup file source.

Edit: OK, changed CvSelectionGroup to CvGameTextMgr, but then I get: AI_attackOdds is not a member of CvGameTextMgr
 
Wow, this is really cool. Great effort! :D
 
Top Bottom