Spaces:
Running
Running
; | |
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, | |
getSpeciesName: () => getSpeciesName, | |
handlers: () => handlers, | |
pages: () => pages, | |
saveStats: () => saveStats, | |
stats: () => stats | |
}); | |
module.exports = __toCommonJS(winrates_exports); | |
var import_lib = require("../../../lib"); | |
const STATS_PATH = Monitor.logPath("randbats/{{MONTH}}-winrates.json").path; | |
const stats = getDefaultStats(); | |
try { | |
const path = STATS_PATH.replace("{{MONTH}}", getMonth()); | |
if (!Monitor.logPath("randbats/").existsSync()) { | |
Monitor.logPath("randbats/").mkdirSync(); | |
} | |
const savedStats = JSON.parse((0, import_lib.FS)(path).readSync()); | |
stats.elo = savedStats.elo; | |
stats.month = savedStats.month; | |
for (const k in stats.formats) { | |
stats.formats[k] = savedStats.formats[k] || stats.formats[k]; | |
} | |
} catch { | |
} | |
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: {} }, | |
gen9randomdoublesbattle: { mons: {} }, | |
gen9babyrandombattle: { mons: {} }, | |
gen9superstaffbrosultimate: { mons: {} }, | |
gen8randombattle: { mons: {} }, | |
gen7randombattle: { mons: {} }, | |
gen6randombattle: { mons: {} }, | |
gen5randombattle: { mons: {} }, | |
gen4randombattle: { mons: {} }, | |
gen3randombattle: { mons: {} }, | |
gen2randombattle: { mons: {} }, | |
gen1randombattle: { 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 === "Greninja-Bond") { | |
return "Greninja"; | |
} else if (species === "Keldeo-Resolute") { | |
return "Keldeo"; | |
} else if (species === "Zarude-Dada") { | |
return "Zarude"; | |
} else if (species === "Polteageist-Antique") { | |
return "Polteageist"; | |
} else if (species === "Sinistcha-Masterpiece") { | |
return "Sinistcha"; | |
} 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.startsWith("Alcremie-")) { | |
return "Alcremie"; | |
} 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") && !item.zMove && megaRayquazaPossible) { | |
return "Rayquaza-Mega"; | |
} else if (species === "Poltchageist-Artisan") { | |
return "Poltchageist"; | |
} else if (species === "Shellos-East") { | |
return "Shellos"; | |
} else if (species === "Sinistea-Antique") { | |
return "Sinistea"; | |
} else if (species.startsWith("Deerling-")) { | |
return "Deerling"; | |
} else if (species.startsWith("Flabe\u0301be\u0301-")) { | |
return "Flabe\u0301be\u0301"; | |
} 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 === "gen2" || format.team === "randomBaby") { | |
eloFloor = 1150; | |
} else if (format.mod !== `gen${Dex.gen}`) { | |
eloFloor = 1300; | |
} else if (format.gameType === "doubles") { | |
eloFloor = 1400; | |
} | |
if (!formatData || format.mod !== "gen9ssb" && battle.rated < eloFloor || !winner) | |
return; | |
checkRollover(); | |
for (const p of battle.players) { | |
const team = await battle.getPlayerTeam(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.name)) { | |
formatData.mons[mon].numWins++; | |
} | |
} | |
} | |
saveStats(); | |
} | |
const commands = { | |
rwr: "randswinrates", | |
randswinrates(target, room, user) { | |
target = toID(target); | |
if (/^(gen|)[0-9]+$/.test(target)) { | |
if (target.startsWith("gen")) | |
target = target.slice(3); | |
target = `gen${target}randombattle`; | |
} | |
return this.parse(`/j view-winrates-${target ? Dex.formats.get(target).id : `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 = `<div class="pad"><h2>Winrates for ${formatTitle} (${month})</h2>`; | |
const prevMonth = new Date(new Date(`${month}-15`).getTime() - 30 * 24 * 60 * 60 * 1e3).toISOString().slice(0, 7); | |
let hasButton = false; | |
if (await (0, import_lib.FS)(STATS_PATH.replace("{{MONTH}}", prevMonth)).exists()) { | |
buf += `<a class="button" href="/view-winrates-${format}--${sorter}--${prevMonth}">Previous month</a>`; | |
hasButton = true; | |
} | |
const nextMonth = new Date(new Date(`${month}-15`).getTime() + 30 * 24 * 60 * 60 * 1e3).toISOString().slice(0, 7); | |
if (await (0, import_lib.FS)(STATS_PATH.replace("{{MONTH}}", nextMonth)).exists()) { | |
if (hasButton) | |
buf += ` | `; | |
buf += `<a class="button" href="/view-winrates-${format}--${sorter}--${nextMonth}">Next month</a>`; | |
hasButton = true; | |
} | |
buf += hasButton ? ` | ` : ""; | |
const otherSort = sorter === "zscore" ? "Raw" : "Z-Score"; | |
buf += `<a class="button" target="replace" href="/view-winrates-${format}--${toID(otherSort)}--${month}">`; | |
buf += `Sort by ${otherSort} descending</a>`; | |
buf += `<hr />`; | |
const statData = month === stats.month ? stats : JSON.parse(await (0, import_lib.FS)(STATS_PATH.replace("{{MONTH}}", month)).read()); | |
const formatData = statData.formats[format]; | |
if (!formatData) { | |
buf += `<div class="message-error">No stats for that format found on that month.</div>`; | |
return buf; | |
} | |
this.title = `[Winrates] [${format}] ${month}`; | |
let sortFn; | |
if (sorter === "zscore") { | |
sortFn = ([_, data]) => [-getZScore(data), -data.timesGenerated]; | |
} else { | |
sortFn = ([_, data]) => [ | |
-(data.numWins / data.timesGenerated), | |
-data.numWins, | |
-data.timesGenerated | |
]; | |
} | |
const mons = import_lib.Utils.sortBy(Object.entries(formatData.mons), sortFn); | |
buf += `<div class="ladder pad"><table><tr><th>Pokemon</th><th>Win %</th><th>Z-Score</th>`; | |
buf += `<th>Raw wins</th><th>Times generated</th></tr>`; | |
for (const [mon, data] of mons) { | |
buf += `<tr><td>${Dex.species.get(mon).name}</td>`; | |
const { timesGenerated, numWins } = data; | |
buf += `<td>${(numWins / timesGenerated * 100).toFixed(2)}%</td>`; | |
buf += `<td>${getZScore(data).toFixed(3)}</td>`; | |
buf += `<td>${numWins}</td><td>${timesGenerated}</td>`; | |
buf += `</tr>`; | |
} | |
buf += `</table></div></div>`; | |
return buf; | |
} | |
}; | |
//# sourceMappingURL=winrates.js.map | |