let {calculate, Pokemon, Move} = require("@smogon/calc"); let karateChop = new Move(6, "Karate Chop"); let wingAttack = new Move(6, "Wing Attack"); let karateChopCrit = new Move(6, "Karate Chop", {isCrit: true}); let wingAttackCrit = new Move(6, "Wing Attack", {isCrit: true}); function randomInt(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; } function d(x) { return Math.floor(x * 4096) / 4096; } function r(x) { return Math.round(x * 4096) / 4096; } function tryHawluchaCatch(hp, maxHp) { const catchRate = 100; let x = Math.min(255, d(r(d(r(r(3 * maxHp - 2 * hp) * catchRate) / (3 * maxHp))))); let y; if (x === 0) { y = 0; } else { y = Math.floor(r(65536 / r(Math.pow(r(255 / x), 3 / 16)))); } let chance = Math.pow(y / 65536, 4); return Math.random() <= chance; } function generateHawlucha(level) { let hpIv = randomInt(0, 31); let atkIv = randomInt(0, 31); let defIv = randomInt(0, 31); let nature = randomInt(0, 24); let natureName = "Serious"; switch (nature) { case 1: natureName = "Lonely"; break; case 2: natureName = "Brave"; break; case 3: natureName = "Adamant"; break; case 4: natureName = "Naughty"; break; case 5: natureName = "Bold"; break; case 7: natureName = "Relaxed"; break; case 8: natureName = "Impish"; break; case 9: natureName = "Lax"; break; case 10: natureName = "Timid"; break; case 11: natureName = "Hasty"; break; case 15: natureName = "Modest"; break; case 16: natureName = "Mild"; break; case 20: natureName = "Calm"; break; case 21: natureName = "Gentle"; break; } let pokemon = new Pokemon(6, "Hawlucha", { ivs: {hp: hpIv, atk: atkIv, def: defIv, spa: 0, spd: 0, spe: 0}, nature: natureName, level: level, }); return { maxHp: pokemon.rawStats.hp, hp: pokemon.rawStats.hp, pokemon: pokemon, hasKingsRock: randomInt(0, 19) === 0, }; } function simulateEncounter(useAerialAce, hp, atk, def, level) { let ballsLeft = 3; let hawlucha = generateHawlucha(level); let _hawlucha = hawlucha.pokemon; // if (hawlucha.hasKingsRock) { // console.log("Hawlucha has King's Rock."); // } let _farfetchd = new Pokemon(6, "Farfetch'd", { ivs: {hp: hp, atk: atk, def: def, spa: 0, spd: 0, spe: 0}, evs: {hp: 2, atk: 4, def: 3, spa: 3, spd: 1, spe: 9}, nature: "Jolly", level: 18, }); let farfetchd = { maxHp: _farfetchd.rawStats.hp, hp: _farfetchd.rawStats.hp, pokemon: _farfetchd, } for (let turn = 1;; ++turn) { // console.log(""); // console.log("Turn " + turn); let hawluchaMove = randomInt(0, 3); let damageRolls = null; let tryBallThrow = !(useAerialAce && hawlucha.maxHp * 0.8 <= hawlucha.hp && farfetchd.maxHp === farfetchd.hp); if (tryBallThrow) { let catchSuccess = tryHawluchaCatch(hawlucha.hp, hawlucha.maxHp); --ballsLeft; if (!catchSuccess) { // console.log("Failed catch: " + ballsLeft + " balls left."); if (ballsLeft === 0) { return { success: false, turn: turn, result: "out of balls", }; } } else { // console.log("Succeeded catch: " + ballsLeft + " balls left."); return { success: true, turn: turn, }; } } if (level === 19) { switch (hawluchaMove) { case 0: { // Karate Chop let crit = randomInt(0, 7) === 0; damageRolls = calculate(6, _hawlucha, _farfetchd, crit ? karateChopCrit : karateChop).damage; break; } case 1: case 3: { // Aerial Ace, Wing Attack let crit = randomInt(0, 15) === 0; damageRolls = calculate(6, _hawlucha, _farfetchd, crit ? wingAttackCrit : wingAttack).damage; break; } case 2: { // Roost // console.log("Hawlucha used Roost."); hawlucha.hp = Math.min(hawlucha.maxHp, hawlucha.hp + Math.floor(hawlucha.maxHp / 2)); break; } } } else { switch (hawluchaMove) { case 0: case 2: { // Aerial Ace, Wing Attack let crit = randomInt(0, 15) === 0; damageRolls = calculate(6, _hawlucha, _farfetchd, crit ? wingAttackCrit : wingAttack).damage; break; } case 1: { // Roost // console.log("Hawlucha used Roost."); hawlucha.hp = Math.min(hawlucha.maxHp, hawlucha.hp + Math.floor(hawlucha.maxHp / 2)); break; } case 3: // Encore, do nothing // console.log("Hawlucha used Encore."); break; } } if (damageRolls !== null) { let damage = damageRolls[randomInt(0, 15)]; // console.log("Farfetch'd took " + damage + " damage."); farfetchd.hp -= damage; // console.log("Farfetch'd has " + farfetchd.hp + " HP."); if (farfetchd.hp <= 0) { return { success: false, turn: turn, result: "farfetchd fainted", }; } } if (!tryBallThrow) { // console.log("Farfetch'd used Aerial Ace"); if (hawlucha.hasKingsRock && damageRolls !== null && randomInt(0, 9) === 0) { // console.log("Farfetch'd flinched"); continue; } let crit = randomInt(0, 15) === 0; damageRolls = calculate(6, _farfetchd, _hawlucha, crit ? wingAttackCrit : wingAttack).damage; let damage = damageRolls[randomInt(0, 15)]; hawlucha.hp -= damage; // console.log("Hawlucha has " + hawlucha.hp + " left (damage taken: " + damage + ")."); if (hawlucha.hp <= 0) { // Should almost never happen, can happen with lowest Defense & HP & crit and is at best a 5 / 16 range. return { success: false, turn: turn, result: "hawlucha fainted", }; } } } } for (let level = 19; level <= 20; ++level) { for (let iv = 0; iv <=1; ++iv) { for (let ae = 0; ae <= 1; ++ae) { let successCounter = 0; let successTurns = 0; let ivs = iv === 0 ? 0 : 31; for (let i = 0; i < 1000000; ++i) { let result = simulateEncounter(!!ae, ivs, ivs, ivs, level); if (result.success) { ++successCounter; successTurns += result.turn; } } console.log(""); console.log("Level: " + level); console.log("IVs: " + ivs); console.log("Aerial Ace: " + ae.toString()); console.log((successCounter / 10000).toFixed(2)); console.log("Turns: " + (successTurns / successCounter).toFixed(2)); } } }