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