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 }