Вход

Просмотр полной версии : Как считается процент победы в бою для юнита



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

Выкладываю функцию для БТС 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]

OT4E
16.01.2008, 15:40
Хммм...

А есть описание структуры CvUnit?

IL2T
16.01.2008, 15:46
<div class='quotetop'>Цитата(OT4E * 16.1.2008, 15:46) 228928</div>

Хммм...

А есть описание структуры CvUnit?
[/b]
По моему оно не критично...

Хотя комментарии программиста лишними бы не были :)

OT4E
16.01.2008, 16:00
<div class='quotetop'>Цитата(IL2T * 16.1.2008, 15:52) 228935</div>

По моему оно не критично...

Хотя комментарии программиста лишними бы не были :)
[/b]

Как это?

<div class='quotetop'>Цитата</div>

currCombatStr(pDefender->plot(), pAttacker)
[/b]
Принципиально, как вычисляется эта функция, что еще надо расшифровывать.

<div class='quotetop'>Цитата</div>
iAttackerFirepower = pAttacker->currFirepower(NULL, NULL);[/b]
NULL означает что атакер не получает никаких бонусов, ни от тайла, ни от способности.
В первом варианте учитывается свойство тайла, не принципиально как, и что-то от атакующего. Так как передается указатель, то сказать, что делается с этим атакующим коэффициэнотом нельзя. Второй вопрос - где учитываются параметры защитника, кроме тайла.

IL2T
16.01.2008, 16:04
Тут можно все-таки логикой обойтись...

Но на всякий случай currCombatStr и currFirepower
[code]
int CvUnit::currCombatStr(const CvPlot* pPlot, const CvUnit* pAttacker, CombatDetails* pCombatDetails) const
{

IL2T
16.01.2008, 16:23
Ну и за компанию тогда maxCombatStr

Комментарии перед функцией
[code]
// maxCombatStr can be called in four different configurations
//

Aleks II
16.01.2008, 16:57
<div class='quotetop'>Цитата(IL2T * 16.1.2008, 17:10) 228944</div>

Тут можно все-таки логикой обойтись...
[/b]

Ага. Хорошо бы выводы, или даже предположения, полученные в результате разбора этих иероглифов, облачить в доступную для понимания непрограммиста форму. При этом желательно, чтобы они имели какую-то практическую пользу для играющего. :shy2:

Вот хочется добавить вопрос в тему (или офтоп?... :whistle: ).
Если в игре рассматривать лог боя, то обнаруживается что бой состоит из нескольких "раундов", в результате которых кто-то у кого-то забирает порцию хитов. Размер порции, видимо, зависит от соотношения сил. Она у каждой стороны своя и расчитывается, наверное, строго по формуле. Бог с ней... А вот как определяется "право удара" вначале каждого раунда? Алгоритм выбора счастливчика где-то присутствует в приведенном IL2T фрагменте?
Ткните меня носом, плз... :shy2:

OT4E
16.01.2008, 17:04
<div class='quotetop'>Цитата</div>

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

Количество урона, наносимое защитником и нападающим, соответственно.

Под GC.GetDefineINT("COMBAT_DAMAGE") скрывается число 20, как я понимаю.

IL2T
16.01.2008, 17:18
<div class='quotetop'>Цитата(Aleks II * 16.1.2008, 17:03) 228958</div>

Ага. Хорошо бы выводы, или даже предположения, полученные в результате разбора этих иероглифов, облачить в доступную для понимания непрограммиста форму. При этом желательно, чтобы они имели какую-то практическую пользу для играющего. :shy2:
[/b]
Будет разбор - будут выводы. :)
Практическая польза зависит от применямой в игре модели военных действий (лично для меня это будет полезно применительно к онлайну).

<div class='quotetop'>Цитата(Aleks II * 16.1.2008, 17:03) 228958</div>

...
А вот как определяется "право удара" вначале каждого раунда?
...
[/b]
Здесь секрета нет. Датчик случайный чисел (Рендом) определяет очередность.

<div class='quotetop'>Цитата(OT4E * 16.1.2008, 17:10) 228961</div>

Под GC.GetDefineINT("COMBAT_DAMAGE") скрывается число 20, как я понимаю.
[/b]
Это параметр из XML скорее всего.

OT4E
16.01.2008, 17:19
<div class='quotetop'>Цитата(Aleks II * 16.1.2008, 17:03) 228958</div>

Если в игре рассматривать лог боя, то обнаруживается что бой состоит из нескольких "раундов", в результате которых кто-то у кого-то забирает порцию хитов. Размер порции, видимо, зависит от соотношения сил. Она у каждой стороны своя и расчитывается, наверное, строго по формуле. Бог с ней... А вот как определяется "право удара" вначале каждого раунда? Алгоритм выбора счастливчика где-то присутствует в приведенном IL2T фрагменте?
Ткните меня носом, плз... :shy2:
[/b]

Бой делится на раунды, в каждом из которых побеждает один из сражающихся юнитов. В каждом из раундов обязательно есть победитель. Все раунды одинаковые между собой, то есть одинаковый наносимый урон и одинаковый шанс его нанести. Между собой раунды не связаны.
Более сильный юнит имеет больший шанс нанести урон, слабый, соответственно, единица минус этот шанс.

Алгоритм выбора счастливчика не рассматривается, по идее это генератор псевдослучайных чисел. Здесь рассматривается алгоритм вычисления вероятности исхода боя, большая часть по поводу "первых ударов".

miklop
27.02.2009, 18:37
А подскажите пожалст, как посмотреть лог боя.
Чё то я не нахожу...

Gromozeka
27.02.2009, 19:42
<div class='quotetop'>Цитата(miklop * 27.2.2009, 21:37) 280525</div>

А подскажите пожалст, как посмотреть лог боя.
Чё то я не нахожу...
[/b]
Ctrl+TAB -> 3 вкладка история сражений

miklop
27.02.2009, 23:26
Спасибо!

<div class='quotetop'>Цитата(Gromozeka * 27.2.2009, 19:42) 280529</div>

<div class='quotetop'>Цитата(miklop * 27.2.2009, 21:37) 280525

А подскажите пожалст, как посмотреть лог боя.
Чё то я не нахожу...
[/b]
Ctrl+TAB -> 3 вкладка история сражений
[/b][/quote]