aaaaaa123456789 icon

Fitness calculation (gen 1)

aaaaaa123456789 | PRO | 02/13/18 01:40:54 AM UTC | 0 ⭐ | 924 👁️ | Never ⏰ | []
C |

21 KB

|

None

|

0 👍

/

0 👎

typedef struct {
  struct {
    unsigned char id;
    unsigned char national_dex;
    unsigned char typenum1, typenum2;
    struct small_stats {
      unsigned char hp, attack, defense, special_attack, special_defense, speed;
    } base_stats;
  } species;
  unsigned char level;
  unsigned experience;
  struct small_stats ivs;
  struct stats {
    unsigned short hp, attack, defense, special_attack, special_defense, speed;
  } evs;
  struct {
    unsigned char id;
    unsigned char pp;
    unsigned char pp_ups;
  } moves[4];
} Pokemon;
 
struct {
  unsigned char power;
  signed char effect_code;
  unsigned char hit_chance; // out of 256
  unsigned char type;
} move_data[] = {
  {  0,  0,   0,  0},
  { 40,  0, 255,  0},
  { 50, -1, 255,  0},
  { 15, 29, 216,  0},
  { 18, 29, 216,  0},
  { 80,  0, 216,  0},
  { 40, 16, 255,  0},
  { 75,  4, 255, 20},
  { 75,  5, 255, 25},
  { 75,  6, 255, 23},
  { 40,  0, 255,  0},
  { 55,  0, 255,  0},
  {  1, 38,  76,  0},
  { 80, 39, 191,  0},
  {  0, 50, 255,  0},
  { 50,  0, 242,  0},
  { 40,  0, 255,  0},
  { 35,  0, 255,  2},
  {  0, 28, 216,  0},
  { 70, 43, 242,  2},
  { 15, 42, 191,  0},
  { 80,  0, 191,  0},
  { 35,  0, 255, 22},
  { 65, 37, 255,  0},
  { 30, 44, 255,  1},
  {120,  0, 191,  0},
  { 70, 45, 242,  1},
  { 60, 37, 216,  1},
  {  0, 22, 255,  0},
  { 70, 37, 255,  0},
  { 65,  0, 255,  0},
  { 15, 29, 216,  0},
  {  1, 38,  76,  0},
  { 35,  0, 242,  0},
  { 85, 36, 255,  0},
  { 15, 42, 216,  0},
  { 90, 48, 216,  0},
  { 90, 27, 255,  0},
  {100, 48, 255,  0},
  {  0, 19, 255,  0},
  { 15,  2, 255,  3},
  { 25, 75, 255,  7},
  { 14, 29, 216,  7},
  {  0, 19, 255,  0},
  { 60, 31, 255,  0},
  {  0, 18, 255,  0},
  {  0, 28, 255,  0},
  {  0, 32, 140,  0},
  {  0, 49, 140,  0},
  { 20, 41, 229,  0}, // set power to 20 to help calculations
  {  0, 86, 140,  0},
  { 40, 69, 255,  3},
  { 40,  4, 255, 20},
  { 95,  4, 255, 20},
  {  0, 46, 255, 25},
  { 40,  0, 255, 21},
  {120,  0, 204, 21},
  { 95,  0, 255, 21},
  { 95,  5, 255, 25},
  {120,  5, 229, 25},
  { 65, 49, 255, 24},
  { 65, 71, 255, 21},
  { 65, 68, 255, 25},
  {150, 80, 229,  0},
  { 35,  0, 255,  2},
  { 80,  0, 255,  2},
  { 80, 48, 204,  1},
  { 50, 37, 229,  1},
  {  1, -7, 255,  1},
  {  1, -4, 255,  1},
  { 80,  0, 255,  0},
  { 20,  3, 255, 22},
  { 40,  3, 255, 22},
  {  0, 84, 229, 22},
  {  0, 12, 255,  0},
  { 55, -1, 242, 22},
  {120, 39, 255, 22},
  {  0, 66, 191,  3},
  {  0, 67, 191, 22},
  {  0, 32, 191, 22},
  { 70, 27, 255, 22},
  {  0, 20, 242,  7},
  { 40, 41, 255, 26}, // set power to 40 to help calculations
  { 15, 42, 178, 20},
  { 40,  6, 255, 23},
  { 95,  6, 255, 23},
  {  0, 67, 255, 23},
  {120,  6, 178, 23},
  { 50,  0, 165,  5},
  {100,  0, 255,  4},
  {  1, 38,  76,  4},
  {100, 39, 255,  4},
  {  0, -2, 216,  3},
  { 50, 76, 255, 24},
  { 90, 71, 255, 24},
  {  0, 32, 153, 24},
  {  0, 10, 255, 24},
  {  0, 52, 255, 24},
  { 40, -3, 255,  0},
  { 20, 81, 255,  0},
  {  0, 28, 255, 24},
  {  0, -4, 255,  8},
  {  0, 82, 255,  0},
  {  0, 59, 216,  0},
  {  0, 15, 255,  0},
  {  0, 56, 255,  0},
  {  0, 11, 255,  0},
  {  0, 15, 255,  0},
  {  0, 22, 255,  0},
  {  0, 49, 255,  8},
  {  0, 11, 255, 21},
  {  0, 11, 255,  0},
  {  0, 51, 255, 24},
  {  0, 64, 255, 24},
  {  0, 25, 255, 25},
  {  0, 65, 255, 24},
  {  0, 47, 255,  0},
  {  0, 26, 255,  0},
  {  0, 83, 255,  0},
  {  0,  9, 255,  2},
  {130,  7, 255,  0},
  {100,  0, 191,  0},
  { 20, 36, 255,  8},
  { 20, 33, 178,  3},
  { 65, 33, 255,  3},
  { 65, 31, 216,  4},
  {120, 34, 216, 20},
  { 80,  0, 255, 21},
  { 35, 42, 191, 21},
  { 60, 17, 255,  0},
  {100, 39, 255,  0},
  { 20, 29, 255,  0},
  { 10, 70, 255,  0},
  {  0, 53, 255, 24},
  {  0, 22, 204, 24},
  {  0, 56, 255,  0},
  { 85, 45, 229,  1},
  {  0, 67, 191,  0},
  {100,  8, 255, 24},
  {  0, 66, 140,  3},
  { 15, 29, 216,  0},
  { 20,  3, 255,  7},
  {  0, 32, 191,  0},
  {140, 39, 229,  2},
  {  0, 57, 255,  0},
  { 20, 70, 255, 21},
  { 70,  0, 255,  0},
  {  0, 32, 255, 22},
  {  0, 22, 178,  0},
  {  1, -5, 204, 24},
  {  0, 85, 255,  0},
  {  0, 51, 255,  3},
  { 90, -1, 216, 21},
  {170,  7, 255,  0},
  { 18, 29, 204,  0},
  { 50, 44, 229,  4},
  {  0, -6, 255, 24},
  { 75,  0, 229,  5},
  { 80, 31, 229,  0},
  {  0, 10, 255,  0},
  {  0, 24, 255,  0},
  { 80,  0, 255,  0},
  {  1, 40, 229,  0},
  { 70, -1, 255,  0},
  {  0, 79, 255,  0},
  { 50, 48, 255,  0}
};
 
// assign power-like values to various effects that moves may have
#define POISON_VALUE            15
#define PARALYSIS_VALUE         10
#define SLEEP_VALUE             20
#define BURN_VALUE              15
#define FREEZE_VALUE           100
#define CONFUSION_VALUE         30
#define STAT_UP_VALUE            5
#define STAT_DOWN_VALUE          5
#define SCREEN_VALUE            20
#define FLINCH_VALUE            15
#define FAINT_VALUE            300
#define HEAL_VALUE              80
#define VULNERABLE_TURN_VALUE   15
#define SUBSTITUTE_VALUE        30
#define LEECH_SEED_VALUE        20
#define DISABLE_VALUE           20
#define PRIORITY_VALUE           5
 
#define FLAG_DANGEROUS       (1 << 0)  /* the user will probably hurt itself just by having this move */
#define FLAG_BAD_MOVE_CHOICE (1 << 1)  /* the user will probably use this move incorrectly and waste a turn */
#define FLAG_IGNORE          (1 << 2)  /* this move should not be taken into account when assessing damaging potential */
#define FLAG_NO_STAB         (1 << 3)  /* the user will not get STAB reliably due to this move existing */
#define FLAG_IGNORE_STAB     (1 << 4)  /* this move cannot get STAB */
#define FLAG_EXACT_DAMAGE    (1 << 5)  /* the computed move power is this move's exact damage output */
#define FLAG_TRANSFORM       (1 << 6)  /* this move makes the user adopt its opponent's stats and moves */
#define FLAG_LEVEL_DAMAGE    (1 << 7)  /* this move's damage is equal to the level of the user */
#define FLAG_HIGH_CRIT_RATIO (1 << 8)  /* this move has a high critical hit ratio */
#define FLAG_LOW_CRIT_CHANCE (1 << 9)  /* a mon with this move will have a low crit chance, since it's likely to use the move and lower its own crit ratio */
#define FLAG_NO_CRITICALS    (1 << 10) /* this attack cannot ever result in a critical hit */
 
struct move_effect_data {
  unsigned short flags;
  short multiply_offset; // implicit 256 denominator
  int add_offset; // implicit 256 denominator
};
 
struct move_effect_data effect_code_data[] = {
  // note that all omitted fields default to zero
  {0}, // all fields zero
  {0}, // unused
  {.add_offset = POISON_VALUE * 51},
  {.multiply_offset = 128},
  {.add_offset = BURN_VALUE * 25},
  {.add_offset = FREEZE_VALUE * 25},
  {.add_offset = PARALYSIS_VALUE * 25},
  {.multiply_offset = 256, .add_offset = -FAINT_VALUE * 256, .flags = FLAG_DANGEROUS},
  {.multiply_offset = -192, .flags = FLAG_BAD_MOVE_CHOICE},
  {.flags = FLAG_IGNORE},
  {.add_offset = STAT_UP_VALUE * 256},
  {.add_offset = STAT_UP_VALUE * 256},
  {.add_offset = STAT_UP_VALUE * 256},
  {.add_offset = STAT_UP_VALUE * 256},
  {.add_offset = STAT_UP_VALUE * 256},
  {.add_offset = STAT_UP_VALUE * 256},
  {0}, // same as no effect
  {.multiply_offset = 13}, // +5% for perfect accuracy
  {.add_offset = STAT_DOWN_VALUE * 256},
  {.add_offset = STAT_DOWN_VALUE * 256},
  {.add_offset = STAT_DOWN_VALUE * 256},
  {.add_offset = STAT_DOWN_VALUE * 256},
  {.add_offset = STAT_DOWN_VALUE * 256},
  {.add_offset = STAT_DOWN_VALUE * 256},
  {.flags = FLAG_IGNORE | FLAG_NO_STAB},
  {.flags = FLAG_BAD_MOVE_CHOICE},
  {.flags = FLAG_IGNORE | FLAG_BAD_MOVE_CHOICE},
  {.add_offset = -CONFUSION_VALUE * 73}, // will only confuse self after 3.5 turns in average
  {.flags = FLAG_IGNORE | FLAG_BAD_MOVE_CHOICE},
  {.multiply_offset = 2 * 256}, // * 3
  {0}, // unused
  {.add_offset = FLINCH_VALUE * 25},
  {.add_offset = SLEEP_VALUE * 256},
  {.add_offset = POISON_VALUE * 102},
  {.add_offset = BURN_VALUE * 76},
  {0}, // unused
  {.add_offset = PARALYSIS_VALUE * 76},
  {.add_offset = FLINCH_VALUE * 76},
  {.multiply_offset = -256, .add_offset = FAINT_VALUE * 256, .flags = FLAG_BAD_MOVE_CHOICE | FLAG_IGNORE_STAB | FLAG_NO_CRITICALS},
  {.multiply_offset = -128, .add_offset = -VULNERABLE_TURN_VALUE * 256},
  {.multiply_offset = -256, .add_offset = FAINT_VALUE * 64, .flags = FLAG_IGNORE_STAB | FLAG_NO_CRITICALS},
  {.flags = FLAG_EXACT_DAMAGE | FLAG_IGNORE_STAB | FLAG_NO_CRITICALS},
  {.multiply_offset = 2 * 256}, // * 3
  {0}, // wasted turn compensates for two-turn move
  {.multiply_offset = 256},
  {0}, // recoil is 1 HP, which doesn't really matter and we can ignore it
  {.flags = FLAG_BAD_MOVE_CHOICE},
  {.flags = FLAG_BAD_MOVE_CHOICE | FLAG_LOW_CRIT_CHANCE},
  {.multiply_offset = -64},
  {.add_offset = CONFUSION_VALUE * 256},
  {.add_offset = 2 * STAT_UP_VALUE * 256},
  {.add_offset = 2 * STAT_UP_VALUE * 256},
  {.add_offset = 2 * STAT_UP_VALUE * 256},
  {.add_offset = 2 * STAT_UP_VALUE * 256},
  {.add_offset = 2 * STAT_UP_VALUE * 256},
  {.add_offset = 2 * STAT_UP_VALUE * 256},
  {.add_offset = HEAL_VALUE * 256, .flags = FLAG_IGNORE_STAB},
  {.flags = FLAG_TRANSFORM}, // we can't use FLAG_IGNORE on Transform because it will usually be the mon's only move
  {.add_offset = 2 * STAT_DOWN_VALUE * 256},
  {.add_offset = 2 * STAT_DOWN_VALUE * 256},
  {.add_offset = 2 * STAT_DOWN_VALUE * 256},
  {.add_offset = 2 * STAT_DOWN_VALUE * 256},
  {.add_offset = 2 * STAT_DOWN_VALUE * 256},
  {.add_offset = 2 * STAT_DOWN_VALUE * 256},
  {.add_offset = SCREEN_VALUE * 256, .flags = FLAG_IGNORE_STAB},
  {.add_offset = SCREEN_VALUE * 256, .flags = FLAG_IGNORE_STAB},
  {.add_offset = POISON_VALUE * 256},
  {.add_offset = PARALYSIS_VALUE * 256},
  {.add_offset = STAT_DOWN_VALUE * 85},
  {.add_offset = STAT_DOWN_VALUE * 85},
  {.add_offset = STAT_DOWN_VALUE * 85},
  {.add_offset = STAT_DOWN_VALUE * 85},
  {0}, // unused
  {0}, // unused
  {0}, // unused
  {0}, // unused
  {.add_offset = CONFUSION_VALUE * 25},
  {.multiply_offset = 256, .add_offset = POISON_VALUE * 51},
  {0}, // unused
  {.add_offset = SUBSTITUTE_VALUE * 256},
  {.multiply_offset = -128, .add_offset = -VULNERABLE_TURN_VALUE * 256},
  {.multiply_offset = 128, .flags = FLAG_DANGEROUS}, // we can expect the average power gain for Rage to be +50%, but it's a bad idea anyway
  {.flags = FLAG_IGNORE}, // Mimic is just a pain to handle
  {.add_offset = 40 * 256, .flags = FLAG_BAD_MOVE_CHOICE}, // 40 power is a reasonable average
  {.add_offset = LEECH_SEED_VALUE * 256},
  {.multiply_offset = -256, .flags = FLAG_BAD_MOVE_CHOICE | FLAG_IGNORE_STAB},
  {.add_offset = DISABLE_VALUE * 256}
};
 
struct move_effect_data custom_effect_data[] = {
  // negative (made up) effect codes go here
  {.flags = FLAG_HIGH_CRIT_RATIO}, // -1: high crit ratio
  {.add_offset = POISON_VALUE * 640}, // -2: toxic (assume poison to be 2.5x as effective)
  {.add_offset = PRIORITY_VALUE * 256}, // -3: quick attack
  {.flags = FLAG_LEVEL_DAMAGE | FLAG_IGNORE_STAB | FLAG_NO_CRITICALS}, // -4: level-based move damage
  {.multiply_offset = -64, .flags = FLAG_LEVEL_DAMAGE | FLAG_IGNORE_STAB | FLAG_NO_CRITICALS}, // -5: psywave (deals 75% of level in average)
  {.add_offset = HEAL_VALUE * 256, .flags = FLAG_BAD_MOVE_CHOICE}, // -6: rest, scored as any heal move but the AI may use it at really bad times
  {.add_offset = 20 * 256, .flags = FLAG_BAD_MOVE_CHOICE | FLAG_NO_CRITICALS} // -7: counter, will deal 20 pow in average, but it's usually a bad choice in gen 1
};
 
unsigned char square_root (unsigned short value) {
  // square roots the gen 1/2 way
  if (value < 3) return value;
  if (value > 64516) return 255;
  unsigned char result;
  for (result = 2; (result * result) < value; result ++);
  return result;
}
 
unsigned short calculate_stat (unsigned char level, unsigned char base, unsigned char DV, unsigned short stat_exp, int is_HP) {
  // remember that shifts truncate! 15 >> 2 is 3, not 3.75
  unsigned growth = 2 * (base + DV) + (square_root(stat_exp) >> 2);
  if (is_HP) growth += 100;
  return (is_HP ? 10 : 5) + growth * level / 100; // also truncates
}
 
struct stats calculate_stats (Pokemon pokemon) {
  return (struct stats) {
    .hp = calculate_stat(pokemon.level, pokemon.species.base_stats.hp, pokemon.ivs.hp, pokemon.evs.hp, 1),
    .attack = calculate_stat(pokemon.level, pokemon.species.base_stats.attack, pokemon.ivs.attack, pokemon.evs.attack, 0),
    .defense = calculate_stat(pokemon.level, pokemon.species.base_stats.defense, pokemon.ivs.defense, pokemon.evs.defense, 0),
    .speed = calculate_stat(pokemon.level, pokemon.species.base_stats.speed, pokemon.ivs.speed, pokemon.evs.speed, 0),
    .special_attack = calculate_stat(pokemon.level, pokemon.species.base_stats.special_attack, pokemon.ivs.special_attack, pokemon.evs.special_attack, 0),
    .special_defense = calculate_stat(pokemon.level, pokemon.species.base_stats.special_defense, pokemon.ivs.special_defense, pokemon.evs.special_defense, 0)
  };
}
 
struct move_effect_data get_move_effect_data (unsigned char move) {
  int effect_code = move_data[move].effect_code;
  if (effect_code >= 0) return effect_code_data[effect_code];
  return custom_effect_data[~effect_code];
}
 
unsigned short get_move_flags (unsigned char move) {
  return get_move_effect_data(move).flags;
}
 
unsigned short get_mon_flags (Pokemon pkmn) {
  return get_move_flags(pkmn.moves -> id) | get_move_flags(pkmn.moves[1].id) | get_move_flags(pkmn.moves[2].id) | get_move_flags(pkmn.moves[3].id);
}
 
unsigned long long calculate_move_power_rating (unsigned char move, Pokemon attacker) {
  // (0.4 * level + 2) * attack stat * power * accuracy, adjusted for crit hit change
  // will contain an implicit shift of 24
  struct stats stats = calculate_stats(attacker);
  struct move_effect_data effect = get_move_effect_data(move);
  int power = move_data[move].power * move_data[move].hit_chance * (256 + effect.multiply_offset);
  unsigned short global_flags = get_mon_flags(attacker);
  unsigned short crit_chance = attacker.species.base_stats.speed >> 1;
  unsigned short attack_stat = (move_data[move].type >= 20) ? stats.special_attack : stats.attack;
  if (effect.flags & FLAG_HIGH_CRIT_RATIO) crit_chance <<= 3;
  if (global_flags & FLAG_LOW_CRIT_CHANCE) crit_chance >>= 2;
  if (crit_chance > 255) crit_chance = 255;
  if (effect.flags & FLAG_LEVEL_DAMAGE) {
    effect.flags |= FLAG_EXACT_DAMAGE;
    power = attacker.level * (256 + effect.multiply_offset) * move_data[move].hit_chance;
  }
  if (effect.flags & FLAG_EXACT_DAMAGE) {
    // at low levels, exact damage is a lot more tempting than at high levels
    // to account for this fact, we take a fixed attack stat value of 100 for damage calculation and pretend they are variable power moves
    effect.flags |= FLAG_IGNORE_STAB | FLAG_NO_CRITICALS;
    attack_stat = 100;
  }
  if (effect.flags & FLAG_NO_CRITICALS) crit_chance = 0;
  if (
      ((move_data[move].type == attacker.species.typenum1) || (move_data[move].type == attacker.species.typenum2)) && 
      !((effect.flags & FLAG_IGNORE_STAB) || (global_flags & FLAG_NO_STAB))
  ) power += power >> 1;
  power += effect.add_offset * move_data[move].hit_chance; // add this AFTER calculating STAB!
  if (power <= 0) return 0;
  unsigned long long regular_damage, critical_damage;
  regular_damage = critical_damage = (unsigned long long) attack_stat * power;
  regular_damage *= 2 * attacker.level / 5 + 5;
  critical_damage *= 4 * attacker.level / 5 + 5;
  return critical_damage * crit_chance + regular_damage * (256 - crit_chance);
}
 
unsigned calculate_transform_rating (Pokemon attacker) {
  // will contain an implicit shift of 24
  // treat it like a generic 80 base power, no effect, 99.6% accuracy STAB attack
  // coming from a mon the same level as the attacker with 85 base all stats, 7 DVs, +32 @L100 from stat exp
  unsigned long long base_power = 0x778800ULL * (5 + attacker.level * 54 / 25); // repeat the calculations above, but hardcode the values
  unsigned long long regular_damage = base_power * (2 * attacker.level / 5 + 5);
  unsigned long long critical_damage = base_power * (4 * attacker.level / 5 + 5);
  return critical_damage * 42 + regular_damage * 214;
}
 
void sort_pair (unsigned long long * lower, unsigned long long * higher) {
  if (*lower <= *higher) return;
  unsigned long long temp = *lower;
  *lower = *higher;
  *higher = temp;
}
 
unsigned char count_moves_with_flag (Pokemon pkmn, unsigned short flag) {
  unsigned char p, result = 0;
  for (p = 0; p < 4; p ++) {
    if (!pkmn.moves[p].id) continue;
    if (get_move_flags(pkmn.moves[p].id) & flag) result ++;
  }
  return result;
}
 
unsigned long long calculate_attacker_rating (Pokemon attacker) {
  // will contain an implicit shift of 32
  unsigned long long move_ratings[4];
  unsigned char p, move_count = 0;
  unsigned short flags;
  for (p = 0; p < 4; p ++) {
    if (!attacker.moves[p].id) continue;
    flags = get_move_flags(attacker.moves[p].id);
    if (flags & FLAG_IGNORE) continue;
    move_ratings[move_count ++] = (flags & FLAG_TRANSFORM) ? calculate_transform_rating(attacker) : calculate_move_power_rating(attacker.moves[p].id, attacker);
  }
  if (!move_count) return 0;
  // simple swap-based sort because there are only four entries anyway
  switch (move_count) {
    case 4:
      sort_pair(move_ratings + 3, move_ratings);
      sort_pair(move_ratings + 3, move_ratings + 1);
      sort_pair(move_ratings + 3, move_ratings + 2);
    case 3:
      sort_pair(move_ratings + 2, move_ratings);
      sort_pair(move_ratings + 2, move_ratings + 1);
    case 2:
      sort_pair(move_ratings + 1, move_ratings);
  }
  // weights: 9, 4, 2, 1. If the mon has less than four moves, the first move's weight is increased to keep the total at 16
  unsigned long long weighted_total = *move_ratings << (4 - move_count);
  for (p = 0; p < move_count; p ++) weighted_total += move_ratings[p] << (p ^ 3); // note that p ^ 3 is the same as 3 - p here
  // take away 1/16 for every bad move the mon has
  return weighted_total * (16 - count_moves_with_flag(attacker, FLAG_BAD_MOVE_CHOICE));
}
 
unsigned long long calculate_defender_rating (Pokemon defender) {
  // must contain an implicit shift of 32 to be compatible with calculate_attacker_rating
  struct stats stats = calculate_stats(defender);
  unsigned long long rating = stats.defense + stats.special_defense; // start with the good old stat average. Accumulated shift: 1
  unsigned short low_stat = (stats.defense > stats.special_defense) ? stats.special_defense : stats.defense;
  unsigned short high_stat = rating - low_stat; // simple trick here
  rating *= stats.hp; // in two steps so there is no overflow
  rating *= defender.species.base_stats.speed + 452; // offset of 1/512 for every base speed point away from 70. Accumulated shift: 10
  rating *= 20; // adjustment factor: 50 for the damage formula, times 40% to make it equivalent to level
  unsigned short stat_ratio = ((unsigned) low_stat << 16) / high_stat;
  // factor in the stat spread, so mons like Chansey get their defensive rating lowered (because there is a weak point). Accumulated shift: 18
  if ((stat_ratio > 65025) || !stat_ratio) // stat_ratio = 0 indicates low_stat = high_stat and some problematic overflow
    rating <<= 8;
  else
    rating *= square_root(stat_ratio);
  unsigned char total_moves = 0, dangerous_moves = 0, self_damaging_moves = 0;
  struct move_effect_data effect;
  unsigned char p;
  for (p = 0; p < 4; p ++) {
    if (!defender.moves[p].id) continue;
    total_moves ++;
    effect = get_move_effect_data(defender.moves[p].id);
    if (effect.flags & FLAG_DANGEROUS) dangerous_moves ++;
    if (effect.add_offset < 0) self_damaging_moves ++; // negative add mod means that the move hurts the user somehow
  }
  if ((total_moves == dangerous_moves) && (total_moves == self_damaging_moves)) return 0; // this mon is suicidal and should not be used at all
  total_moves --; // easier indexing
  rating *= 128 - dangerous_moves * total_moves[(unsigned char []) {128, 64, 43, 32}]; // dangerous move chance out of 128. Accumulated shift: 25
  rating *= 128 - self_damaging_moves * total_moves[(unsigned char []) {60, 30, 20, 15}]; // 1 - (15/32 * self damage chance). Accumulated shift: 32
  // nothing interesting to do here now
  return rating;
}
 
unsigned calculate_overall_rating (Pokemon pkmn) {
  struct stats stats = calculate_stats(pkmn);
  unsigned long long attack_rating = calculate_attacker_rating(pkmn), defense_rating = calculate_defender_rating(pkmn);
  // faster mons attack first, hence the weights here. Note that they add a shift of 10
  unsigned short attack_weight = (stats.speed * 100 / pkmn.level) + 320;
  unsigned short defense_weight = 1024 - attack_weight;
  // at this point we have to be very careful with overflows, so we split the values like this
  unsigned long long attack_upper = attack_rating >> 32, attack_lower = attack_rating & 0xffffffffULL;
  unsigned long long defense_upper = defense_rating >> 32, defense_lower = defense_rating & 0xffffffffULL;
  attack_upper *= attack_weight;
  attack_lower *= attack_weight;
  defense_upper *= defense_weight;
  defense_lower *= defense_weight;
  unsigned long long rating = attack_upper + defense_upper, rating_lower = attack_lower + defense_lower;
  rating += (rating_lower >> 32) + 512; // don't lose the carry from the lower bits before we discard them, and add 0x200 to round to nearest in the next step
  return rating >> 10; // do away with all the shifts and return a rounded rating value
}

Comments