#include #include typedef struct { unsigned char HP; unsigned char attack; unsigned char defense; unsigned char strength; } PlayerStats; typedef struct { long double hitChance; unsigned char maxDamage; unsigned char hitPoints; } PlayerInfo; typedef struct { long double win; long double lose; long double draw; } Odds; const Odds error = {0, 0, 0}; static long double getHitChance(unsigned char, unsigned char); static unsigned char getMaxDamage(unsigned char); static Odds calculateOdds(PlayerInfo, PlayerInfo, long double); static void turnsOdds(unsigned char, long double *, long double **, unsigned *, long double); static void hitsOdds(unsigned char, long double, long double *); static void multiplyVector(long double *, long double *, long double); static void addVector(long double *, long double *); static long double totalOdds(long double *); static long double currentError(long double *, unsigned char); static void expand(long double **, unsigned, unsigned); static Odds calculateFor(long double *, long double *, unsigned); long double winningOdds (PlayerStats player1, PlayerStats player2, unsigned char firstMove, long double threshold) { PlayerInfo p1, p2; if (player1.HP > 99) return 0; if (player2.HP > 99) return 0; if (player1.strength > 102) return 0; if (player2.strength > 102) return 0; if (threshold <= 0) return 0; if (threshold >= 1) return 0; if (firstMove > 2) return 0; p1.hitChance = getHitChance(player1.attack, player2.defense); p2.hitChance = getHitChance(player2.attack, player1.defense); p1.maxDamage = getMaxDamage(player1.strength); p2.maxDamage = getMaxDamage(player2.strength); p1.hitPoints = player1.HP; p2.hitPoints = player2.HP; Odds odds = calculateOdds(p1, p2, threshold); switch (firstMove) { case 1: return odds.win + odds.draw; case 2: return odds.win; default: return odds.win + (odds.draw / 2); } } static long double getHitChance (unsigned char attack, unsigned char defense) { long double adjustedAttack = attack + 8; long double adjustedDefense = defense + 8; if (attack <= defense) return (adjustedAttack - 1) / (2 * adjustedDefense); else return 1.0L - ((adjustedDefense + 1) / (2 * adjustedAttack)); } static unsigned char getMaxDamage (unsigned char strength) { return (strength + 13) / 10; } static Odds calculateOdds (PlayerInfo p1, PlayerInfo p2, long double threshold) { unsigned t1, t2; long double * odds1; long double * odds2; long double hitodds1[12]; long double hitodds2[12]; unsigned turns; if ((p1.hitChance < 0) || (p1.hitChance > 1)) return error; if ((p2.hitChance < 0) || (p2.hitChance > 1)) return error; if ((!p1.maxDamage) || (p1.maxDamage > 11)) return error; if ((!p2.maxDamage) || (p2.maxDamage > 11)) return error; if (p1.hitPoints > 99) return error; if (p2.hitPoints > 99) return error; if ((threshold <= 0) || (threshold >= 1)) return error; hitsOdds(p1.maxDamage, p1.hitChance, hitodds1); hitsOdds(p2.maxDamage, p2.hitChance, hitodds2); turnsOdds(p2.hitPoints, hitodds1, &odds1, &t1, threshold); turnsOdds(p1.hitPoints, hitodds2, &odds2, &t2, threshold); if (t1 < t2) { expand(&odds1, t1, t2); turns = t2; } else if (t2 < t1) { expand(&odds2, t2, t1); turns = t1; } else turns = t1; Odds result = calculateFor(odds1, odds2, turns); free(odds1); free(odds2); long double totalError = 1; totalError -= result.win; totalError -= result.lose; totalError -= result.draw; if ((totalError * totalError) > (threshold * threshold * 10)) return calculateOdds(p1, p2, threshold / 10); result.draw += totalError; return result; } static void turnsOdds (unsigned char opponentHP, long double * hitOdds, long double ** result, unsigned * resultSize, long double threshold) { unsigned turns = 0; long double * turnOdds = malloc(sizeof(long double)); *turnOdds = 1; long double damageDealtA[111]; long double damageDealtB[111]; unsigned char current = 0; long double * damageDealt; long double * damageDealtPrev; memset(damageDealtA, 0, 111 * sizeof(long double)); *damageDealtA = 1; unsigned currentTurn; long double curDamage[12]; unsigned char curHP; do { currentTurn = turns; turns += 20; turnOdds = realloc(turnOdds, sizeof(long double) * (turns + 1)); for (currentTurn ++; currentTurn <= turns; currentTurn ++) { damageDealtPrev = current ? damageDealtB : damageDealtA; damageDealt = current ? damageDealtA : damageDealtB; memset(damageDealt, 0, 111 * sizeof(long double)); for (curHP = 0; curHP < opponentHP; curHP ++) { multiplyVector(curDamage, hitOdds, damageDealtPrev[curHP]); addVector(damageDealt + curHP, curDamage); } turnOdds[currentTurn] = totalOdds(damageDealt + opponentHP); current = !current; } damageDealt = current ? damageDealtA : damageDealtB; *turnOdds = currentError(damageDealt, opponentHP); } while (*turnOdds >= threshold); *result = turnOdds; *resultSize = turns; } static void hitsOdds (unsigned char maxDamage, long double hitChance, long double * result) { *result = 1 - hitChance; long double x = hitChance / maxDamage; unsigned char pos; for (pos = 1; pos <= maxDamage; pos ++) result[pos] = x; for (; pos <= 11; pos ++) result[pos] = 0; } static void multiplyVector (long double * result, long double * src, long double value) { unsigned char pos; for (pos = 0; pos < 12; pos ++) result[pos] = src[pos] * value; } static void addVector (long double * dst, long double * src) { unsigned char pos; for (pos = 0; pos < 12; pos ++) dst[pos] += src[pos]; } static long double totalOdds (long double * damageOdds) { long double total = 0; unsigned char pos; for (pos = 0; pos < 12; pos ++) total += damageOdds[pos]; return total; } static long double currentError (long double * damageOdds, unsigned char maxDamage) { long double total = 0; unsigned char pos; for (pos = 0; pos < maxDamage; pos ++) total += damageOdds[pos]; return total; } static void expand (long double ** turnOdds, unsigned from, unsigned to) { *turnOdds = realloc(*turnOdds, sizeof(long double) * (to + 1)); unsigned pos; for (pos = from + 1; pos <= to; pos ++) (*turnOdds)[pos] = 0; } static Odds calculateFor (long double * turnOdds1, long double * turnOdds2, unsigned turns) { long double r0 = 0, r1 = 0, r2 = 0; unsigned turn1, turn2; for (turn1 = 1; turn1 <= turns; turn1 ++) { for (turn2 = 1; turn2 < turn1; turn2 ++) r2 += turnOdds1[turn1] * turnOdds2[turn2]; r0 += turnOdds1[turn1] * turnOdds2[turn1]; for (turn2 = turn1 + 1; turn2 <= turns; turn2 ++) r1 += turnOdds1[turn1] * turnOdds2[turn2]; r1 += turnOdds1[turn1] * (*turnOdds2); } for (turn2 = 1; turn2 <= turns; turn2 ++) r2 += turnOdds2[turn2] * (*turnOdds1); r0 += (*turnOdds1) * (*turnOdds2); return (Odds) {.win = r1, .lose = r2, .draw = r0}; }