From dc668c67a0743688967bee73cdcf8986ae5c6db3 Mon Sep 17 00:00:00 2001 From: xeals Date: Wed, 26 Apr 2023 17:01:02 +1000 Subject: [PATCH] add Showdown calculator --- .gitignore | 145 ++++++++++++++++ bot.js | 2 + bot.py | 21 ++- calc.js | 299 ++++++++++++++++++++++++++++++++ calc_main.js | 8 + flake.nix | 6 +- package-lock.json | 431 ++++++++++++++++++++++++++++++++++++++++++++++ package.json | 18 ++ 8 files changed, 928 insertions(+), 2 deletions(-) create mode 100644 bot.js create mode 100644 calc.js create mode 100644 calc_main.js create mode 100644 package-lock.json create mode 100644 package.json diff --git a/.gitignore b/.gitignore index ac63138..46305fe 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,148 @@ htmlcov *.db cache/ token + +# Created by https://www.toptal.com/developers/gitignore/api/node +# Edit at https://www.toptal.com/developers/gitignore?templates=node + +### Node ### +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# vuepress v2.x temp and cache directory +.temp + +# Docusaurus cache and generated files +.docusaurus + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* + +### Node Patch ### +# Serverless Webpack directories +.webpack/ + +# Optional stylelint cache + +# SvelteKit build / generate output +.svelte-kit + +# End of https://www.toptal.com/developers/gitignore/api/node diff --git a/bot.js b/bot.js new file mode 100644 index 0000000..894c505 --- /dev/null +++ b/bot.js @@ -0,0 +1,2 @@ +import calc from "./calc"; + diff --git a/bot.py b/bot.py index 6c262b9..08e11d8 100755 --- a/bot.py +++ b/bot.py @@ -61,11 +61,30 @@ class BotClient(discord.Client): async def on_message(self, message: discord.Message): content = message.content - if self.is_replay(message): + if self.is_command(message): + await self.on_command(message) + elif self.is_replay(message): await self.on_replay(message) elif self.is_leaguefact(message): await self.on_leaguefact(message) + def is_command(self, message: discord.Message) -> bool: + return message.content.startswith("%") + + async def on_command(self, message: discord.Message): + command = discord.message.split(" ")[0] + match command: + case "%calc": + reply = await _calculate(command[1:]) + await message.reply(content=reply) + case _: + _log.info(f"Unrecognised command {command}") + + async def _calculate(args: list[str]) -> str: + proc = sp.run(["node", "calc_main.js", "--"] + args) + msg = proc.stdout + return msg.strip() + def is_replay(self, message: discord.Message) -> bool: if re.match("https://replay.pokemonshowdown.com/dl-.*", message.content): return True diff --git a/calc.js b/calc.js new file mode 100644 index 0000000..c481fc5 --- /dev/null +++ b/calc.js @@ -0,0 +1,299 @@ +import calc, { + calculate, + Generations, + Field, + Move, + Pokemon, +} from "@smogon/calc"; +import assert from "assert"; +import chev, { Lexer } from "chevrotain"; + +/** + * Creates a lexer. + * + * @returns {Lexer} + */ +function buildLexer() { + const Boost = chev.createToken({ name: "Boost", pattern: /[+-]\d+/ }); + const EV = chev.createToken({ name: "EV", pattern: /\d+[+-]?/ }); + const Stat = chev.createToken({ + name: "Stat", + pattern: /HP|Atk|Def|SpA|SpD|Spe/, + }); + + const Item = chev.createToken({ + name: "Item", + pattern: new RegExp(calc.ITEMS.flatMap((gen) => gen).join("|")), + }); + const Ability = chev.createToken({ + name: "Ability", + pattern: new RegExp(calc.ABILITIES.flatMap((gen) => gen).join("|")), + }); + + const Pokemon = chev.createToken({ + name: "Pokemon", + pattern: new RegExp( + calc.SPECIES.flatMap((gen) => Object.keys(gen)).join("|") + ), + }); + const Move = chev.createToken({ + name: "Move", + pattern: new RegExp( + calc.MOVES.flatMap((gen) => Object.keys(gen)).join("|") + ), + }); + + const whitespace = chev.createToken({ + name: "Whitespace", + pattern: /\s+/, + group: Lexer.SKIPPED, + }); + const div = chev.createToken({ + name: "div", + pattern: "/", + group: Lexer.SKIPPED, + }); + const vs = chev.createToken({ + name: "vs", + pattern: "vs.", + }); + + const terrainEnter = chev.createToken({ + name: "terrainEnter", + pattern: "in", + push_mode: "terrain_mode", + }); + const Weather = chev.createToken({ + name: "Weather", + pattern: /Sun|Rain|Sand|Snow/, + }); + const Terrain = chev.createToken({ + name: "Terrain", + pattern: /(Electric|Grassy|Misty|Psychic) Terrain/, + }); + + const screenEnter = chev.createToken({ + name: "screenEnter", + pattern: "through", + push_mode: "screen_mode", + }); + + return new Lexer({ + modes: { + default_mode: [ + whitespace, + div, + vs, + Boost, + EV, + Stat, + Item, + Ability, + Pokemon, + Move, + terrainEnter, + screenEnter, + ], + terrain_mode: [whitespace, screenEnter, Weather, Terrain], + screen_mode: [whitespace, Move], + }, + defaultMode: "default_mode", + }); +} + +/** + * @generator + * @template T item type + * @param {T[]} arr + * @yields {T} item + */ +function* iterate(arr) { + for (const a of arr) { + yield a; + } +} + +/** + * @param {string} type token type + * @param {chev.IToken} token token + * @return {string} matched token + */ +function unwrapToken(type, token) { + assert( + token.tokenType.name == type, + "expected token %s, got %s", + type, + token.tokenType.name + ); + return token.image; +} + +const POS_NATURES = { + atk: "Adamant", + def: "Bold", + spa: "Modest", + spd: "Calm", + spe: "Jolly", +}; + +const NEG_NATURES = { + atk: "Modest", + def: "Lonely", + spa: "Adamant", + spd: "Rash", + spe: "Brave", +}; + +/** + * Parses a Smogon calculator output: + * + * -2 8 SpA Choice Specs Torkoal vs. + * 252 HP / 4+ SpD Assault Vest Abomasnow + * in Sun + * through Light Screen + * + * @param {string} line textual line + * @return {calc.Result} calculation result + */ +function parseAndCalculate(line) { + const lexer = buildLexer(); + const result = lexer.tokenize(line); + if (result.errors && result.errors.length > 0) { + console.error("Unparsed tokens: %o", result.errors); + } + + /** @type {string} */ + var attacker; + /** @type {string} */ + var defender; + + /** @type {calc.State.Pokemon} */ + var attackerOpts = {}; + /** @type {calc.State.Pokemon} */ + var defenderOpts = {}; + + /** @type {string} */ + var move; + /** @type {calc.State.Field} */ + var field = {}; + + // Tokenising state. + var isAttacker = true; + var it = iterate(result.tokens); + + const opts = () => (isAttacker ? attackerOpts : defenderOpts); + + while (true) { + let item = it.next().value; + if (!item) break; + switch (item.tokenType.name) { + case "Boost": + { + let boost = unwrapToken("Boost", item); + let ev = unwrapToken("EV", it.next().value); + let stat = unwrapToken("Stat", it.next().value).toLowerCase(); + opts().boosts = { [stat]: parseInt(boost), ...opts().boosts }; + opts().evs = { [stat]: parseInt(ev), ...opts().evs }; + if (ev.endsWith("+")) { + opts().nature = POS_NATURES[stat]; + } else if (ev.endsWith("-")) { + opts().nature = NEG_NATURES[stat]; + } + } + break; + case "EV": + { + let ev = unwrapToken("EV", item); + let stat = unwrapToken("Stat", it.next().value).toLowerCase(); + opts().evs = { [stat]: parseInt(ev), ...opts().evs }; + if (ev.endsWith("+")) { + opts().nature = POS_NATURES[stat]; + } else if (ev.endsWith("-")) { + opts().nature = NEG_NATURES[stat]; + } + } + break; + case "Stat": + throw Error("Impossibel state: bare Stat"); + case "Item": + opts().item = unwrapToken("Item", item); + break; + case "Pokemon": + { + let name = unwrapToken("Pokemon", item); + if (isAttacker) attacker = name; + else defender = name; + } + break; + case "Move": + move = unwrapToken("Move", item); + break; + case "terrainEnter": + { + let next = it.next().value; + switch (next.tokenType.name) { + case "Weather": + field.weather = next.image; + break; + case "Terrain": + field.terrain = next.image; + break; + default: + throw Error( + "Unhandled terrain %s: %s", + next.tokenType.name, + next.image + ); + } + } + break; + case "screenEnter": + { + let screen = unwrapToken("Move", it.next().value); + field.defenderSide = field.defenderSide || {}; + field.defenderSide.isLightScreen = screen === "Light Screen"; + field.defenderSide.isReflect = screen === "Reflect"; + field.defenderSide.isAuroraVeil = screen === "Aurora Veil"; + field.defenderSide.is; + } + break; + case "vs": + isAttacker = false; + break; + default: + console.error("unmatched token type: %s", item.tokenType.name); + break; + } + } + + const gen = Generations.get(8); + return calculate( + gen, + new Pokemon(gen, attacker, attackerOpts), + new Pokemon(gen, defender, defenderOpts), + new Move(gen, move), + new Field(field) + ); +} + +function test() { + const text = + "-2 8 SpA Choice Specs Torkoal Overheat vs. 252 HP / 4+ SpD Assault Vest Abomasnow in Sun through Light Screen"; + const res = parseAndCalculate(text); + + assert(res.attacker.boosts.spa === -2, "should have -2 SpA"); + assert(res.attacker.evs.spa === 8, "should have 8 SpA EVs"); + assert(res.attacker.item === "Choice Specs", "should have Choice Specs"); + assert(res.attacker.name === "Torkoal", "should be Torkoal"); + assert(res.move.name === "Overheat", "should be Overheat"); + assert(res.defender.evs.hp === 252, "should have 252 HP EVs"); + assert(res.defender.evs.spd === 4, "should have 4 SpD EVs"); + assert(res.field.weather === "Sun", "should be in sun"); + + assert( + res.desc().replace(/:.*/, "") === text, + "non-damage text should be equivalent to input" + ); +} + +export { parseAndCalculate, test }; diff --git a/calc_main.js b/calc_main.js new file mode 100644 index 0000000..998c673 --- /dev/null +++ b/calc_main.js @@ -0,0 +1,8 @@ +import { parseAndCalculate } from "./calc.js"; + +// FIXME: issue with using execArgv. +let args = process.argv.slice(2); +if (args[0] === "--") args = args.slice(1); +const line = args.join(" "); +const res = parseAndCalculate(line); +console.log(res.fullDesc()); diff --git a/flake.nix b/flake.nix index eb08739..6da012e 100644 --- a/flake.nix +++ b/flake.nix @@ -17,7 +17,11 @@ ps.requests ]); in - [ python pkgs.sqlite ]; + [ + pkgs.nodejs + python + pkgs.sqlite + ]; }; }); } diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..a59752f --- /dev/null +++ b/package-lock.json @@ -0,0 +1,431 @@ +{ + "name": "hhirls", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "hhirls", + "version": "1.0.0", + "license": "WTFPL", + "dependencies": { + "@smogon/calc": "^0.7.0", + "chevrotain": "^10.5.0", + "discord.js": "^14.9.0" + } + }, + "node_modules/@chevrotain/cst-dts-gen": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/@chevrotain/cst-dts-gen/-/cst-dts-gen-10.5.0.tgz", + "integrity": "sha512-lhmC/FyqQ2o7pGK4Om+hzuDrm9rhFYIJ/AXoQBeongmn870Xeb0L6oGEiuR8nohFNL5sMaQEJWCxr1oIVIVXrw==", + "dependencies": { + "@chevrotain/gast": "10.5.0", + "@chevrotain/types": "10.5.0", + "lodash": "4.17.21" + } + }, + "node_modules/@chevrotain/gast": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/@chevrotain/gast/-/gast-10.5.0.tgz", + "integrity": "sha512-pXdMJ9XeDAbgOWKuD1Fldz4ieCs6+nLNmyVhe2gZVqoO7v8HXuHYs5OV2EzUtbuai37TlOAQHrTDvxMnvMJz3A==", + "dependencies": { + "@chevrotain/types": "10.5.0", + "lodash": "4.17.21" + } + }, + "node_modules/@chevrotain/types": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-10.5.0.tgz", + "integrity": "sha512-f1MAia0x/pAVPWH/T73BJVyO2XU5tI4/iE7cnxb7tqdNTNhQI3Uq3XkqcoteTmD4t1aM0LbHCJOhgIDn07kl2A==" + }, + "node_modules/@chevrotain/utils": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/@chevrotain/utils/-/utils-10.5.0.tgz", + "integrity": "sha512-hBzuU5+JjB2cqNZyszkDHZgOSrUUT8V3dhgRl8Q9Gp6dAj/H5+KILGjbhDpc3Iy9qmqlm/akuOI2ut9VUtzJxQ==" + }, + "node_modules/@discordjs/builders": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-1.6.1.tgz", + "integrity": "sha512-CCcLwn/8ANhlAbhlE18fcaN0hfXTen53/JiwZs1t9oE/Cqa9maA8ZRarkCIsXF4J7J/MYnd0J6IsxeKsq+f6mw==", + "dependencies": { + "@discordjs/formatters": "^0.3.0", + "@discordjs/util": "^0.2.0", + "@sapphire/shapeshift": "^3.8.1", + "discord-api-types": "^0.37.37", + "fast-deep-equal": "^3.1.3", + "ts-mixer": "^6.0.3", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=16.9.0" + } + }, + "node_modules/@discordjs/collection": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-1.5.0.tgz", + "integrity": "sha512-suyVndkEAAWrGxyw/CPGdtXoRRU6AUNkibtnbJevQzpelkJh3Q1gQqWDpqf5i39CnAn5+LrN0YS+cULeEjq2Yw==", + "engines": { + "node": ">=16.9.0" + } + }, + "node_modules/@discordjs/formatters": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@discordjs/formatters/-/formatters-0.3.0.tgz", + "integrity": "sha512-Fc4MomalbP8HMKEMor3qUiboAKDtR7PSBoPjwm7WYghVRwgJlj5WYvUsriLsxeKk8+Qq2oy+HJlGTUkGvX0YnA==", + "dependencies": { + "discord-api-types": "^0.37.37" + }, + "engines": { + "node": ">=16.9.0" + } + }, + "node_modules/@discordjs/rest": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-1.7.0.tgz", + "integrity": "sha512-r2HzmznRIo8IDGYBWqQfkEaGN1LrFfWQd3dSyC4tOpMU8nuVvFUEw6V/lwnG44jyOq+vgyDny2fxeUDMt9I4aQ==", + "dependencies": { + "@discordjs/collection": "^1.5.0", + "@discordjs/util": "^0.2.0", + "@sapphire/async-queue": "^1.5.0", + "@sapphire/snowflake": "^3.4.0", + "discord-api-types": "^0.37.37", + "file-type": "^18.2.1", + "tslib": "^2.5.0", + "undici": "^5.21.0" + }, + "engines": { + "node": ">=16.9.0" + } + }, + "node_modules/@discordjs/util": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@discordjs/util/-/util-0.2.0.tgz", + "integrity": "sha512-/8qNbebFzLWKOOg+UV+RB8itp4SmU5jw0tBUD3ifElW6rYNOj1Ku5JaSW7lLl/WgjjxF01l/1uQPCzkwr110vg==", + "engines": { + "node": ">=16.9.0" + } + }, + "node_modules/@sapphire/async-queue": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.5.0.tgz", + "integrity": "sha512-JkLdIsP8fPAdh9ZZjrbHWR/+mZj0wvKS5ICibcLrRI1j84UmLMshx5n9QmL8b95d4onJ2xxiyugTgSAX7AalmA==", + "engines": { + "node": ">=v14.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/@sapphire/shapeshift": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/@sapphire/shapeshift/-/shapeshift-3.8.2.tgz", + "integrity": "sha512-NXpnJAsxN3/h9TqQPntOeVWZrpIuucqXI3IWF6tj2fWCoRLCuVK5wx7Dtg7pRrtkYfsMUbDqgKoX26vrC5iYfA==", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "lodash": "^4.17.21" + }, + "engines": { + "node": ">=v14.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/@sapphire/snowflake": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/@sapphire/snowflake/-/snowflake-3.4.2.tgz", + "integrity": "sha512-KJwlv5gkGjs1uFV7/xx81n3tqgBwBJvH94n1xDyH3q+JSmtsMeSleJffarEBfG2yAFeJiFA4BnGOK6FFPHc19g==", + "engines": { + "node": ">=v14.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/@smogon/calc": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@smogon/calc/-/calc-0.7.0.tgz", + "integrity": "sha512-2+AGILYtqwRBceIIYkxH1dvtLwzSdyEwAAFWDfjYVwwNohmsNKjU5RVsDal8matpmNJptHF2FekSbHONNZTlUg==", + "dependencies": { + "@types/node": "^18.11.9" + } + }, + "node_modules/@tokenizer/token": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", + "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==" + }, + "node_modules/@types/node": { + "version": "18.16.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.16.1.tgz", + "integrity": "sha512-DZxSZWXxFfOlx7k7Rv4LAyiMroaxa3Ly/7OOzZO8cBNho0YzAi4qlbrx8W27JGqG57IgR/6J7r+nOJWw6kcvZA==" + }, + "node_modules/@types/ws": { + "version": "8.5.4", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.4.tgz", + "integrity": "sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, + "node_modules/chevrotain": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-10.5.0.tgz", + "integrity": "sha512-Pkv5rBY3+CsHOYfV5g/Vs5JY9WTHHDEKOlohI2XeygaZhUeqhAlldZ8Hz9cRmxu709bvS08YzxHdTPHhffc13A==", + "dependencies": { + "@chevrotain/cst-dts-gen": "10.5.0", + "@chevrotain/gast": "10.5.0", + "@chevrotain/types": "10.5.0", + "@chevrotain/utils": "10.5.0", + "lodash": "4.17.21", + "regexp-to-ast": "0.5.0" + } + }, + "node_modules/discord-api-types": { + "version": "0.37.40", + "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.40.tgz", + "integrity": "sha512-LMALvtO+p6ERK8rwWoaI490NfIE/egbqjR4/rfLL1z9gQE1gqLiTpIUUDIunfAtKYzeH6ucyXhaXXWpfZh/Q6g==" + }, + "node_modules/discord.js": { + "version": "14.9.0", + "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-14.9.0.tgz", + "integrity": "sha512-ygGms5xP4hG+QrrY9k7d/OYCzMltSMtdl/2Snzq/nLCiZo+Sna91Ulv9l0+B5Jd/Czcq37B7wJAnmja7GOa+bg==", + "dependencies": { + "@discordjs/builders": "^1.6.0", + "@discordjs/collection": "^1.5.0", + "@discordjs/formatters": "^0.3.0", + "@discordjs/rest": "^1.7.0", + "@discordjs/util": "^0.2.0", + "@sapphire/snowflake": "^3.4.0", + "@types/ws": "^8.5.4", + "discord-api-types": "^0.37.37", + "fast-deep-equal": "^3.1.3", + "lodash.snakecase": "^4.1.1", + "tslib": "^2.5.0", + "undici": "^5.21.0", + "ws": "^8.13.0" + }, + "engines": { + "node": ">=16.9.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "node_modules/file-type": { + "version": "18.3.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-18.3.0.tgz", + "integrity": "sha512-pkPZ5OGIq0TYb37b8bHDLNeQSe1H2KlaQ2ySGpJkkr2KZdaWsO4QhPzHA0mQcsUW2cSqJk+4gM/UyLz/UFbXdQ==", + "dependencies": { + "readable-web-to-node-stream": "^3.0.2", + "strtok3": "^7.0.0", + "token-types": "^5.0.1" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sindresorhus/file-type?sponsor=1" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/lodash.snakecase": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", + "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==" + }, + "node_modules/peek-readable": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-5.0.0.tgz", + "integrity": "sha512-YtCKvLUOvwtMGmrniQPdO7MwPjgkFBtFIrmfSbYmYuq3tKDV/mcfAhBth1+C3ru7uXIZasc/pHnb+YDYNkkj4A==", + "engines": { + "node": ">=14.16" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readable-web-to-node-stream": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz", + "integrity": "sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==", + "dependencies": { + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/regexp-to-ast": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/regexp-to-ast/-/regexp-to-ast-0.5.0.tgz", + "integrity": "sha512-tlbJqcMHnPKI9zSrystikWKwHkBqu2a/Sgw01h3zFjvYrMxEDYHzzoMZnUrbIfpTFEsoRnnviOXNCzFiSc54Qw==" + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/strtok3": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-7.0.0.tgz", + "integrity": "sha512-pQ+V+nYQdC5H3Q7qBZAz/MO6lwGhoC2gOAjuouGf/VO0m7vQRh8QNMl2Uf6SwAtzZ9bOw3UIeBukEGNJl5dtXQ==", + "dependencies": { + "@tokenizer/token": "^0.3.0", + "peek-readable": "^5.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/token-types": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/token-types/-/token-types-5.0.1.tgz", + "integrity": "sha512-Y2fmSnZjQdDb9W4w4r1tswlMHylzWIeOKpx0aZH9BgGtACHhrk3OkT52AzwcuqTRBZtvvnTjDBh8eynMulu8Vg==", + "dependencies": { + "@tokenizer/token": "^0.3.0", + "ieee754": "^1.2.1" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, + "node_modules/ts-mixer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.3.tgz", + "integrity": "sha512-k43M7uCG1AkTyxgnmI5MPwKoUvS/bRvLvUb7+Pgpdlmok8AoqmUaZxUUw8zKM5B1lqZrt41GjYgnvAi0fppqgQ==" + }, + "node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" + }, + "node_modules/undici": { + "version": "5.22.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.22.0.tgz", + "integrity": "sha512-fR9RXCc+6Dxav4P9VV/sp5w3eFiSdOjJYsbtWfd4s5L5C4ogyuVpdKIVHeW0vV1MloM65/f7W45nR9ZxwVdyiA==", + "dependencies": { + "busboy": "^1.6.0" + }, + "engines": { + "node": ">=14.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "node_modules/ws": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", + "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..46e7486 --- /dev/null +++ b/package.json @@ -0,0 +1,18 @@ +{ + "name": "hhirls", + "version": "1.0.0", + "description": "Pokemon Showdown data processing, mostly for HHIRLLL's Pokemon league. Ugly as fuck.", + "main": "calc.js", + "type": "module", + "scripts": { + "calc": "node calc_main.js", + "test": "node -e 'import(\"./calc.js\").then(mod => mod.test())'" + }, + "author": "", + "license": "WTFPL", + "dependencies": { + "@smogon/calc": "^0.7.0", + "chevrotain": "^10.5.0", + "discord.js": "^14.9.0" + } +}