+ Ответить в теме
Показано с 1 по 13 из 13

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

Комбинированный просмотр

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

    Выкладываю функцию для БТС 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]
    продвинь это сообщение в соцсеть:  

  2. #2
    Хммм...

    А есть описание структуры CvUnit?
    продвинь это сообщение в соцсеть:  
    Капитан команды RUS.

  3. #3
    <div class='quotetop'>Цитата(OT4E * 16.1.2008, 15:46) [snapback]228928[/snapback]</div>
    Хммм...

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

    Хотя комментарии программиста лишними бы не были
    продвинь это сообщение в соцсеть:  

  4. #4
    <div class='quotetop'>Цитата(IL2T * 16.1.2008, 15:52) [snapback]228935[/snapback]</div>
    По моему оно не критично...

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

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

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

  5. #5
    Тут можно все-таки логикой обойтись...

    Но на всякий случай currCombatStr и currFirepower
    [code]
    int CvUnit::currCombatStr(const CvPlot* pPlot, const CvUnit* pAttacker, CombatDetails* pCombatDetails) const
    {
    продвинь это сообщение в соцсеть:  

  6. #6
    <div class='quotetop'>Цитата(IL2T * 16.1.2008, 17:10) [snapback]228944[/snapback]</div>
    Тут можно все-таки логикой обойтись...
    [/b]
    Ага. Хорошо бы выводы, или даже предположения, полученные в результате разбора этих иероглифов, облачить в доступную для понимания непрограммиста форму. При этом желательно, чтобы они имели какую-то практическую пользу для играющего.

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

  7. #7
    <div class='quotetop'>Цитата(Aleks II * 16.1.2008, 17:03) [snapback]228958[/snapback]</div>
    Ага. Хорошо бы выводы, или даже предположения, полученные в результате разбора этих иероглифов, облачить в доступную для понимания непрограммиста форму. При этом желательно, чтобы они имели какую-то практическую пользу для играющего.
    [/b]
    Будет разбор - будут выводы.
    Практическая польза зависит от применямой в игре модели военных действий (лично для меня это будет полезно применительно к онлайну).

    <div class='quotetop'>Цитата(Aleks II * 16.1.2008, 17:03) [snapback]228958[/snapback]</div>
    ...
    А вот как определяется "право удара" вначале каждого раунда?
    ...
    [/b]
    Здесь секрета нет. Датчик случайный чисел (Рендом) определяет очередность.

    <div class='quotetop'>Цитата(OT4E * 16.1.2008, 17:10) [snapback]228961[/snapback]</div>
    Под GC.GetDefineINT("COMBAT_DAMAGE") скрывается число 20, как я понимаю.
    [/b]
    Это параметр из XML скорее всего.
    продвинь это сообщение в соцсеть:  

  8. #8
    <div class='quotetop'>Цитата(Aleks II * 16.1.2008, 17:03) [snapback]228958[/snapback]</div>
    Если в игре рассматривать лог боя, то обнаруживается что бой состоит из нескольких "раундов", в результате которых кто-то у кого-то забирает порцию хитов. Размер порции, видимо, зависит от соотношения сил. Она у каждой стороны своя и расчитывается, наверное, строго по формуле. Бог с ней... А вот как определяется "право удара" вначале каждого раунда? Алгоритм выбора счастливчика где-то присутствует в приведенном IL2T фрагменте?
    Ткните меня носом, плз...
    [/b]
    Бой делится на раунды, в каждом из которых побеждает один из сражающихся юнитов. В каждом из раундов обязательно есть победитель. Все раунды одинаковые между собой, то есть одинаковый наносимый урон и одинаковый шанс его нанести. Между собой раунды не связаны.
    Более сильный юнит имеет больший шанс нанести урон, слабый, соответственно, единица минус этот шанс.

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

  9. #9
    Ну и за компанию тогда maxCombatStr

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

  10. #10
    <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, как я понимаю.
    продвинь это сообщение в соцсеть:  
    Капитан команды RUS.

  11. #11
    А подскажите пожалст, как посмотреть лог боя.
    Чё то я не нахожу...
    продвинь это сообщение в соцсеть:  

  12. #12
    Super Moderator

    Аватар для Gromozeka


    Регистрация
    13.10.2005
    Адрес
    Бердск Новосибирской обл.
    Сообщений
    4,187
    <div class='quotetop'>Цитата(miklop * 27.2.2009, 21:37) [snapback]280525[/snapback]</div>
    А подскажите пожалст, как посмотреть лог боя.
    Чё то я не нахожу...
    [/b]
    Ctrl+TAB -> 3 вкладка история сражений
    продвинь это сообщение в соцсеть:  
    Любая сложная проблема имеет простое, доступное для понимания неправильное решение (Закон Мерфи)

  13. #13
    Спасибо!

    <div class='quotetop'>Цитата(Gromozeka * 27.2.2009, 19:42) [snapback]280529[/snapback]</div>
    <div class='quotetop'>Цитата(miklop * 27.2.2009, 21:37) [snapback]280525[/snapback]
    А подскажите пожалст, как посмотреть лог боя.
    Чё то я не нахожу...
    [/b]
    Ctrl+TAB -> 3 вкладка история сражений
    [/b][/quote]
    продвинь это сообщение в соцсеть:  

+ Ответить в теме

Ваши права

  • Вы не можете создавать новые темы
  • Вы не можете отвечать в темах
  • Вы не можете прикреплять вложения
  • Вы не можете редактировать свои сообщения
Рейтинг@Mail.ru

free counters