Хотел давно разобрать код, но времени не хватает...
Давайте попробуем сделать это совместными усилиями!

Выкладываю функцию для БТС 3.13 и Варлордс 2.13 (различия минимальные и все помечены):

<div class='quotetop'>Цитата</div>
// FUNCTION: getCombatOdds
// Calculates combat odds, given two units
// Returns value from 0-1000
// Written by DeepO
int getCombatOdds(CvUnit* pAttacker, CvUnit* pDefender)
{
float fOddsEvent;
float fOddsAfterEvent;
int iAttackerStrength;
int iAttackerFirepower;
int iDefenderStrength;
int iDefenderFirepower;
int iDefenderOdds;
int iAttackerOdds;
int iStrengthFactor;
int iDamageToAttacker;
int iDamageToDefender;
int iNeededRoundsAttacker;
int iNeededRoundsDefender;
int iMaxRounds;
int iAttackerLowFS;
int iAttackerHighFS;
int iDefenderLowFS;
int iDefenderHighFS;
int iFirstStrikes;

int iDefenderHitLimit; //добавлено в BTS 3.13

int iI;
int iJ;
int iI3;
int iI4;
int iOdds = 0;

// setup battle, calculate strengths and odds
//////

//Added ST
iAttackerStrength = pAttacker->currCombatStr(NULL, NULL);
iAttackerFirepower = pAttacker->currFirepower(NULL, NULL);

iDefenderStrength = pDefender->currCombatStr(pDefender->plot(), pAttacker);
iDefenderFirepower = pDefender->currFirepower(pDefender->plot(), pAttacker);

FAssert((iAttackerStrength + iDefenderStrength) > 0);
FAssert((iAttackerFirepower + iDefenderFirepower) > 0);

iDefenderOdds = ((GC.getDefineINT("COMBAT_DIE_SIDES") * iDefenderStrength) / (iAttackerStrength + iDefenderStrength));
if (iDefenderOdds == 0)
{
return 1000;
}
iAttackerOdds = GC.getDefineINT("COMBAT_DIE_SIDES") - iDefenderOdds;
if (iAttackerOdds == 0)
{
return 0;
}

iStrengthFactor = ((iAttackerFirepower + iDefenderFirepower + 1) / 2);

// calculate damage done in one round
//////

БтС 3.13
iDamageToAttacker = std::max(1,((GC.getDefineINT("COMBAT_DAMAGE") * (iDefenderFirepower + iStrengthFactor)) / (iAttackerFirepower + iStrengthFactor)));
iDamageToDefender = std::max(1,((GC.getDefineINT("COMBAT_DAMAGE") * (iAttackerFirepower + iStrengthFactor)) / (iDefenderFirepower + iStrengthFactor)));


Warlords 2.13
iDamageToAttacker = max(1,((GC.getDefineINT("COMBAT_DAMAGE") * (iDefenderFirepower + iStrengthFactor)) / (iAttackerFirepower + iStrengthFactor)));
iDamageToDefender = max(1,((GC.getDefineINT("COMBAT_DAMAGE") * (iAttackerFirepower + iStrengthFactor)) / (iDefenderFirepower + iStrengthFactor)));


// calculate needed rounds.
// Needed rounds = round_up(health/damage)
//////

БтС 3.13
iDefenderHitLimit = pDefender->maxHitPoints() - pAttacker->combatLimit();

iNeededRoundsAttacker = (std::max(0, pDefender->currHitPoints() - iDefenderHitLimit) + iDamageToDefender - 1 ) / iDamageToDefender;


Warlords 2.13
iNeededRoundsAttacker = (pDefender->currHitPoints() + iDamageToDefender - 1 ) / iDamageToDefender;



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

// calculate possible first strikes distribution.
// We can&#39;t use the getCombatFirstStrikes() function (only one result,
// no distribution), so we need to mimic it.
//////

iAttackerLowFS = (pDefender->immuneToFirstStrikes()) ? 0 : pAttacker->firstStrikes();
iAttackerHighFS = (pDefender->immuneToFirstStrikes()) ? 0 : (pAttacker->firstStrikes() + pAttacker->chanceFirstStrikes());

iDefenderLowFS = (pAttacker->immuneToFirstStrikes()) ? 0 : pDefender->firstStrikes();
iDefenderHighFS = (pAttacker->immuneToFirstStrikes()) ? 0 : (pDefender->firstStrikes() + pDefender->chanceFirstStrikes());

// For every possible first strike event, calculate the odds of combat.
// Then, add these to the total, weighted to the chance of that first
// strike event occurring
//////

for (iI = iAttackerLowFS; iI < iAttackerHighFS + 1; iI++)
{
for (iJ = iDefenderLowFS; iJ < iDefenderHighFS + 1; iJ++)
{
// for every possible combination of fs results, calculate the chance

if (iI >= iJ)
{
// Attacker gets more or equal first strikes than defender

iFirstStrikes = iI - iJ;

// For every possible first strike getting hit, calculate both
// the chance of that event happening, as well as the rest of
// the chance assuming the event has happened. Multiply these
// together to get the total chance (Bayes rule).
// iI3 counts the number of successful first strikes
//////

for (iI3 = 0; iI3 < (iFirstStrikes + 1); iI3++)
{
// event: iI3 first strikes hit the defender

// calculate chance of iI3 first strikes hitting: fOddsEvent
// f(k;n,p)=C(n,k)*(p^k)*((1-p)^(n-k))
// this needs to be in floating point math
//////

fOddsEvent = ((float)getBinomialCoefficient(iFirstStrikes, iI3)) * pow((((float)iAttackerOdds) / GC.getDefineINT("COMBAT_DIE_SIDES")), iI3) * pow((1.0f - (((float)iAttackerOdds) / GC.getDefineINT("COMBAT_DIE_SIDES"))), (iFirstStrikes - iI3));

// calculate chance assuming iI3 first strike hits: fOddsAfterEvent
//////

if (iI3 >= iNeededRoundsAttacker)
{
fOddsAfterEvent = 1;
}
else
{
fOddsAfterEvent = 0;

// odds for _at_least_ (iNeededRoundsAttacker - iI3) (the remaining hits
// the attacker needs to make) out of (iMaxRounds - iI3) (the left over
// rounds) is the sum of each _exact_ draw
//////

for (iI4 = (iNeededRoundsAttacker - iI3); iI4 < (iMaxRounds - iI3 + 1); iI4++)
{
// odds of exactly iI4 out of (iMaxRounds - iI3) draws.
// f(k;n,p)=C(n,k)*(p^k)*((1-p)^(n-k))
// this needs to be in floating point math
//////

fOddsAfterEvent += ((float)getBinomialCoefficient((iMaxRounds - iI3), iI4)) * pow((((float)iAttackerOdds) / GC.getDefineINT("COMBAT_DIE_SIDES")), iI4) * pow((1.0f - (((float)iAttackerOdds) / GC.getDefineINT("COMBAT_DIE_SIDES"))), ((iMaxRounds - iI3) - iI4));
}
}

// Multiply these together, round them properly, and add
// the result to the total iOdds
//////

iOdds += ((int)(1000.0 * (fOddsEvent*fOddsAfterEvent + 0.0005)));
}
}
else // (iI < iJ)
{
// Attacker gets less first strikes than defender

iFirstStrikes = iJ - iI;

// For every possible first strike getting hit, calculate both
// the chance of that event happening, as well as the rest of
// the chance assuming the event has happened. Multiply these
// together to get the total chance (Bayes rule).
// iI3 counts the number of successful first strikes
//////

for (iI3 = 0; iI3 < (iFirstStrikes + 1); iI3++)
{
// event: iI3 first strikes hit the defender

// First of all, check if the attacker is still alive.
// Otherwise, no further calculations need to occur
/////

if (iI3 < iNeededRoundsDefender)
{
// calculate chance of iI3 first strikes hitting: fOddsEvent
// f(k;n,p)=C(n,k)*(p^k)*((1-p)^(n-k))
// this needs to be in floating point math
//////

fOddsEvent = ((float)getBinomialCoefficient(iFirstStrikes, iI3)) * pow((((float)iDefenderOdds) / GC.getDefineINT("COMBAT_DIE_SIDES")), iI3) * pow((1.0f - (((float)iDefenderOdds) / GC.getDefineINT("COMBAT_DIE_SIDES"))), (iFirstStrikes - iI3));

// calculate chance assuming iI3 first strike hits: fOddsAfterEvent
//////

fOddsAfterEvent = 0;

// odds for _at_least_ iNeededRoundsAttacker (the remaining hits
// the attacker needs to make) out of (iMaxRounds - iI3) (the left over
// rounds) is the sum of each _exact_ draw
//////

for (iI4 = iNeededRoundsAttacker; iI4 < (iMaxRounds - iI3 + 1); iI4++)
{

// odds of exactly iI4 out of (iMaxRounds - iI3) draws.
// f(k;n,p)=C(n,k)*(p^k)*((1-p)^(n-k))
// this needs to be in floating point math
//////

fOddsAfterEvent += ((float)getBinomialCoefficient((iMaxRounds - iI3), iI4)) * pow((((float)iAttackerOdds) / GC.getDefineINT("COMBAT_DIE_SIDES")), iI4) * pow((1.0f - (((float)iAttackerOdds) / GC.getDefineINT("COMBAT_DIE_SIDES"))), ((iMaxRounds - iI3) - iI4));
}

// Multiply these together, round them properly, and add
// the result to the total iOdds
//////

iOdds += ((int)(1000.0 * (fOddsEvent*fOddsAfterEvent + 0.0005)));
}
}
}
}
}

// Weigh the total to the number of possible combinations of first strikes events
// note: the integer math breaks down when #FS > 656 (with a die size of 1000)
//////

iOdds /= (((pDefender->immuneToFirstStrikes()) ? 0 : pAttacker->chanceFirstStrikes()) + 1) * (((pAttacker->immuneToFirstStrikes()) ? 0 : pDefender->chanceFirstStrikes()) + 1);

// finished!
//////

return iOdds;
}
[/b]