"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var winrates_exports = {}; __export(winrates_exports, { commands: () => commands, handlers: () => handlers, pages: () => pages, saveStats: () => saveStats, stats: () => stats }); module.exports = __toCommonJS(winrates_exports); var import_lib = require("../../../lib"); const STATS_PATH = "logs/randbats/{{MONTH}}-winrates.json"; let stats; try { const path = STATS_PATH.replace("{{MONTH}}", getMonth()); if (!(0, import_lib.FS)("logs/randbats/").existsSync()) { (0, import_lib.FS)("logs/randbats/").mkdirSync(); } stats = JSON.parse((0, import_lib.FS)(path).readSync()); } catch { stats = getDefaultStats(); } function getDefaultStats() { return { elo: 1500, month: getMonth(), formats: { // all of these requested by rands staff. they don't anticipate it being changed much // so i'm not spending the time to add commands to toggle this gen9randombattle: { mons: {} }, gen7randombattle: { mons: {} }, gen6randombattle: { mons: {} }, gen5randombattle: { mons: {} }, gen4randombattle: { mons: {} }, gen3randombattle: { mons: {} }, gen2randombattle: { mons: {} } } }; } function saveStats(month = getMonth()) { const curStats = { ...stats }; (0, import_lib.FS)(STATS_PATH.replace("{{MONTH}}", month)).writeUpdate(() => JSON.stringify(curStats)); } function getMonth() { return Chat.toTimestamp(new Date()).split(" ")[0].slice(0, -3); } function getSpeciesName(set, format) { const species = set.species; const item = Dex.items.get(set.item); const moves = set.moves; const megaRayquazaPossible = ["gen6", "gen7"].includes(format.mod) && !format.ruleset.includes("Mega Rayquaza Clause"); if (species.startsWith("Pikachu-")) { return "Pikachu"; } else if (species.startsWith("Unown-")) { return "Unown"; } else if (species === "Gastrodon-East") { return "Gastrodon"; } else if (species === "Magearna-Original") { return "Magearna"; } else if (species === "Genesect-Douse") { return "Genesect"; } else if (species === "Dudunsparce-Three-Segment") { return "Dudunsparce"; } else if (species === "Maushold-Four") { return "Maushold"; } else if (species === "Squawkabilly-Blue") { return "Squawkabilly"; } else if (species === "Squawkabilly-White") { return "Squawkabilly-Yellow"; } else if (species.startsWith("Basculin-")) { return "Basculin"; } else if (species.startsWith("Sawsbuck-")) { return "Sawsbuck"; } else if (species.startsWith("Vivillon-")) { return "Vivillon"; } else if (species.startsWith("Florges-")) { return "Florges"; } else if (species.startsWith("Furfrou-")) { return "Furfrou"; } else if (species.startsWith("Minior-")) { return "Minior"; } else if (species.startsWith("Toxtricity-")) { return "Toxtricity"; } else if (species.startsWith("Tatsugiri-")) { return "Tatsugiri"; } else if (species === "Zacian" && item.name === "Rusted Sword") { return "Zacian-Crowned"; } else if (species === "Zamazenta" && item.name === "Rusted Shield") { return "Zamazenta-Crowned"; } else if (species === "Kyogre" && item.name === "Blue Orb") { return "Kyogre-Primal"; } else if (species === "Groudon" && item.name === "Red Orb") { return "Groudon-Primal"; } else if (item.megaStone) { return item.megaStone; } else if (species === "Rayquaza" && moves.includes("Dragon Ascent") && megaRayquazaPossible) { return "Rayquaza-Mega"; } else { return species; } } function checkRollover() { if (stats.month !== getMonth()) { saveStats(stats.month); Object.assign(stats, getDefaultStats()); saveStats(); } } const getZScore = (data) => 2 * Math.sqrt(data.timesGenerated) * (data.numWins / data.timesGenerated - 0.5); const handlers = { onBattleEnd(battle, winner, players) { void collectStats(battle, winner, players); } }; async function collectStats(battle, winner, players) { const formatData = stats.formats[battle.format]; let eloFloor = stats.elo; const format = Dex.formats.get(battle.format); if (format.mod !== `gen${Dex.gen}`) { eloFloor = 1300; } if (!formatData || battle.rated < eloFloor) return; checkRollover(); for (const p of players) { const team = await battle.getTeam(p); if (!team) return; const mons = team.map((f) => getSpeciesName(f, format)); for (const mon of mons) { if (!formatData.mons[mon]) formatData.mons[mon] = { timesGenerated: 0, numWins: 0 }; formatData.mons[mon].timesGenerated++; if (toID(winner) === toID(p)) { formatData.mons[mon].numWins++; } } } saveStats(); } const commands = { rwr: "randswinrates", randswinrates(target, room, user) { return this.parse(`/j view-winrates-${toID(target) || `gen${Dex.gen}randombattle`}`); }, randswinrateshelp: [ "/randswinrates OR /rwr [format] - Get a list of the win rates for all Pokemon in the given Random Battles format." ], async removewinrates(target, room, user) { this.checkCan("rangeban"); if (!/^[0-9]{4}-[0-9]{2}$/.test(target) || target === getMonth()) { return this.errorReply(`Invalid month: ${target}`); } const path = STATS_PATH.replace("{{MON}}", target); if (!await (0, import_lib.FS)(path).exists()) { return this.errorReply(`No stats for the month ${target}.`); } await (0, import_lib.FS)(path).unlinkIfExists(); this.globalModlog("REMOVEWINRATES", null, target); this.privateGlobalModAction(`${user.name} removed Random Battle winrates for the month of ${target}`); } }; const pages = { async winrates(query, user) { if (!user.named) return Rooms.RETRY_AFTER_LOGIN; query = query.join("-").split("--"); const format = toID(query.shift()); if (!format) return this.errorReply(`Specify a format to view winrates for.`); if (!stats.formats[format]) { return this.errorReply(`That format does not have winrates tracked.`); } checkRollover(); const sorter = toID(query.shift() || "zscore"); if (!["zscore", "raw"].includes(sorter)) { return this.errorReply(`Invalid sorting method. Must be either 'zscore' or 'raw'.`); } const month = query.shift() || getMonth(); if (!/^[0-9]{4}-[0-9]{2}$/.test(month)) { return this.errorReply(`Invalid month: ${month}`); } const isOldMonth = month !== getMonth(); if (isOldMonth && !await (0, import_lib.FS)(STATS_PATH.replace("{{MONTH}}", month)).exists()) { return this.errorReply(`There are no winrates for that month.`); } const formatTitle = Dex.formats.get(format).name; let buf = `
Pokemon | Win % | Z-Score | `; buf += `Raw wins | Times generated |
---|---|---|---|---|
${Dex.species.get(mon).name} | `; const { timesGenerated, numWins } = data; buf += `${(numWins / timesGenerated * 100).toFixed(2)}% | `; buf += `${getZScore(data).toFixed(3)} | `; buf += `${numWins} | ${timesGenerated} | `; buf += `