let {calculate, Pokemon, Move} = require("@smogon/calc"); let scratch = new Move(7, "Scratch"); let tackle = new Move(7, "Tackle"); let qa = new Move(7, "Quick Attack"); let scratchCrit = new Move(7, "Scratch", {isCrit: true}); let qaCrit = new Move(7, "Quick Attack", {isCrit: true}); function randomInt(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; } let chimchar; function simulateBattle(strategy) { chimchar.hp = chimchar.pokemon.rawStats.hp; chimchar.maxHp = chimchar.pokemon.rawStats.hp; chimchar.pokemon.boosts.atk = 0; starly.hp = starly.pokemon.rawStats.hp; starly.maxHp = starly.pokemon.rawStats.hp; starly.pokemon.boosts.def = 0; let chimcharFaster = chimchar.pokemon.rawStats.spe > starly.pokemon.rawStats.spe; let chimcharTied = chimchar.pokemon.rawStats.spe === starly.pokemon.rawStats.spe; let healRoll = calculate(7, starly.pokemon, chimchar.pokemon, qa).damage[14]; let potionCount = 10; switch (strategy) { case 1: { // Spam Scratch for (let turn = 1;; ++turn) { let selectedMove = randomInt(0, 2); let crit, chimcharRolls, starlyRolls; let needPotion = chimchar.hp <= healRoll && potionCount > 0; let chimcharGoesFirst = (chimcharFaster || (chimcharTied && randomInt(0, 1) === 0)) && selectedMove !== 0; if (selectedMove < 2) { crit = randomInt(0, 23) === 0; starlyRolls = calculate(7, starly.pokemon, chimchar.pokemon, crit ? qaCrit : qa).damage; } if (!needPotion) { crit = randomInt(0, 23) === 0; // Attack with Scratch chimcharRolls = calculate(7, chimchar.pokemon, starly.pokemon, crit ? scratchCrit : scratch).damage; } if (chimcharGoesFirst || needPotion) { if (needPotion) { --potionCount; if (potionCount === 0) { console.log("Ran out of potions!"); } chimchar.hp = chimchar.maxHp; } else { starly.hp -= chimcharRolls[randomInt(0, 15)]; if (starly.hp <= 0) { return {win: true, turn: turn}; } } if (selectedMove === 2) { chimchar.pokemon.boosts.atk = Math.max(-6, chimchar.pokemon.boosts.atk - 1); } else { chimchar.hp -= starlyRolls[randomInt(0, 15)]; if (chimchar.hp <= 0) { return {win: false, turn: turn}; } } } else { if (selectedMove === 2) { chimchar.pokemon.boosts.atk = Math.max(-6, chimchar.pokemon.boosts.atk - 1); } else { chimchar.hp -= starlyRolls[randomInt(0, 15)]; if (chimchar.hp <= 0) { return {win: false, turn: turn}; } } starly.hp -= chimcharRolls[randomInt(0, 15)]; if (starly.hp <= 0) { return {win: true, turn: turn}; } } } } case 2: { // Leer, then spam Scratch for (let turn = 1;; ++turn) { let selectedMove = randomInt(0, 2); let crit, chimcharRolls, starlyRolls; let needPotion = chimchar.hp <= healRoll; let chimcharGoesFirst = (chimcharFaster || (chimcharTied && randomInt(0, 1) === 0)) && selectedMove !== 0; if (selectedMove < 2) { crit = randomInt(0, 23) === 0; starlyRolls = calculate(7, starly.pokemon, chimchar.pokemon, crit ? qaCrit : qa).damage; } if (!needPotion) { crit = randomInt(0, 23) === 0; // Attack with Scratch chimcharRolls = calculate(7, chimchar.pokemon, starly.pokemon, crit ? scratchCrit : scratch).damage; } if (chimcharGoesFirst || needPotion) { if (needPotion) { --potionCount; if (potionCount === 0) { console.log("Ran out of potions!"); } chimchar.hp = chimchar.maxHp; } else { if (turn === 1) { starly.pokemon.boosts.def = -1; } else { starly.hp -= chimcharRolls[randomInt(0, 15)]; if (starly.hp <= 0) { return {win: true, turn: turn}; } } } if (selectedMove === 2) { chimchar.pokemon.boosts.atk = Math.max(-6, chimchar.pokemon.boosts.atk - 1); } else { chimchar.hp -= starlyRolls[randomInt(0, 15)]; if (chimchar.hp <= 0) { return {win: false, turn: turn}; } } } else { if (selectedMove === 2) { chimchar.pokemon.boosts.atk = Math.max(-6, chimchar.pokemon.boosts.atk - 1); } else { chimchar.hp -= starlyRolls[randomInt(0, 15)]; if (chimchar.hp <= 0) { return {win: false, turn: turn}; } } if (turn === 1) { starly.pokemon.boosts.def = -1; } else { starly.hp -= chimcharRolls[randomInt(0, 15)]; if (starly.hp <= 0) { return {win: true, turn: turn}; } } if (starly.hp <= 0) { return {win: true, turn: turn}; } } } } case 3: { // Scratch, Leer only if Growl, Scratch spam let growlUsedTurn1 = false; for (let turn = 1;; ++turn) { let selectedMove = randomInt(0, 2); let crit, chimcharRolls, starlyRolls; let needPotion = chimchar.hp <= healRoll; let chimcharGoesFirst = (chimcharFaster || (chimcharTied && randomInt(0, 1) === 0)) && selectedMove !== 0; if (selectedMove < 2) { crit = randomInt(0, 23) === 0; starlyRolls = calculate(7, starly.pokemon, chimchar.pokemon, crit ? qaCrit : qa).damage; } if (!needPotion) { crit = randomInt(0, 23) === 0; // Attack with Scratch chimcharRolls = calculate(7, chimchar.pokemon, starly.pokemon, crit ? scratchCrit : scratch).damage; } if (chimcharGoesFirst || needPotion) { if (needPotion) { --potionCount; if (potionCount === 0) { console.log("Ran out of potions!"); } chimchar.hp = chimchar.maxHp; } else { if (turn === 2 && growlUsedTurn1) { starly.pokemon.boosts.def = -1; } else { starly.hp -= chimcharRolls[randomInt(0, 15)]; if (starly.hp <= 0) { return {win: true, turn: turn}; } } } if (selectedMove === 2) { if (turn === 1) { growlUsedTurn1 = true; } chimchar.pokemon.boosts.atk = Math.max(-6, chimchar.pokemon.boosts.atk - 1); } else { chimchar.hp -= starlyRolls[randomInt(0, 15)]; if (chimchar.hp <= 0) { return {win: false, turn: turn}; } } } else { if (selectedMove === 2) { if (turn === 1) { growlUsedTurn1 = true; } chimchar.pokemon.boosts.atk = Math.max(-6, chimchar.pokemon.boosts.atk - 1); } else { chimchar.hp -= starlyRolls[randomInt(0, 15)]; if (chimchar.hp <= 0) { return {win: false, turn: turn}; } } if (turn === 2 && growlUsedTurn1) { starly.pokemon.boosts.def = -1; } else { starly.hp -= chimcharRolls[randomInt(0, 15)]; if (starly.hp <= 0) { return {win: true, turn: turn}; } } if (starly.hp <= 0) { return {win: true, turn: turn}; } } } } } } let starly = { pokemon: new Pokemon(7, "Starly", { ivs: {hp: 0, atk: 0, def: 0, spa: 0, spd: 0, spe: 0}, evs: {hp: 0, atk: 0, def: 0, spa: 0, spd: 0, spe: 0}, nature: "Jolly", level: 5, }), hp: 0, maxHp: 0, }; let it = 0; for (let atk = 10; atk <= 13; ++atk) { let atkIv; let atkEvs; switch (atk) { case 10: atkIv = 0; atkEvs = 0; break; case 11: atkIv = 4; atkEvs = 0; break; case 12: atkIv = 31; atkEvs = 0; break; case 13: atkIv = 31; atkEvs = 52; break; } for (let def = 8; def <= 11; ++def) { if (def === 10) { continue; } let defIv; let defEvs; switch (def) { case 8: defIv = 0; defEvs = 0; break; case 9: defIv = 31; defEvs = 0; break; case 11: defIv = 31; defEvs = 4; break; } let nature = def === 8 ? "Mild" : "Serious"; for (let spd = 11; spd <= 13; ++spd) { let spdIv; let spdEvs; switch (spd) { case 11: spdIv = 0; spdEvs = 0; break; case 12: spdIv = 31; spdEvs = 0; break; case 13: spdIv = 31; spdEvs = 36; break; } ++it; if (it <= 4) { continue; } chimchar = { pokemon: new Pokemon(7, "Chimchar", { ivs: {hp: 31, atk: atkIv, def: defIv, spa: 0, spd: 0, spe: spdIv}, evs: {hp: 0, atk: atkEvs, def: defEvs, spa: 0, spd: 0, spe: spdEvs}, // 52 Attack 4 Def 36 Spd nature: nature, level: 5, }), hp: 0, maxHp: 0, }; console.log(chimchar.pokemon.rawStats.hp + " " + chimchar.pokemon.rawStats.atk + " " + chimchar.pokemon.rawStats.def + " " + chimchar.pokemon.rawStats.spe); let wins = 0; let totalTurns = 0; for (let i = 0; i < 100000; ++i) { let outcome = simulateBattle(1); if (outcome.win) { wins++; totalTurns += outcome.turn; } } console.log(`Strategy 1: Win Rate: ${(wins / 1000).toFixed(2)}; Averge turns: ${totalTurns / wins}`); wins = 0; totalTurns = 0; for (let i = 0; i < 100000; ++i) { let outcome = simulateBattle(2); if (outcome.win) { wins++; totalTurns += outcome.turn; } } console.log(`Strategy 2: Win Rate: ${(wins / 1000).toFixed(2)}; Averge turns: ${totalTurns / wins}`); wins = 0; totalTurns = 0; for (let i = 0; i < 100000; ++i) { let outcome = simulateBattle(3); if (outcome.win) { wins++; totalTurns += outcome.turn; } } console.log(`Strategy 3: Win Rate: ${(wins / 1000).toFixed(2)}; Averge turns: ${totalTurns / wins}`); } } }