From 4b93916aab11249f03691a2fd459e59ae352d6bb Mon Sep 17 00:00:00 2001 From: Cyper Date: Tue, 22 Apr 2025 21:01:01 -0400 Subject: [PATCH] Initial commit --- app.ts | 1 + package-lock.json | 48 ++++++++++++++ package.json | 25 +++++++ placeholder-.ts | 163 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 237 insertions(+) create mode 100644 app.ts create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 placeholder-.ts diff --git a/app.ts b/app.ts new file mode 100644 index 0000000..5893f9d --- /dev/null +++ b/app.ts @@ -0,0 +1 @@ +console.log('hello world'); \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..2d55000 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,48 @@ +{ + "name": "comicdicebot", + "version": "0.0.1-beta", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "comicdicebot", + "version": "0.0.1-beta", + "license": "MIT", + "devDependencies": { + "@types/node": "^22.14.1", + "typescript": "^5.8.3" + } + }, + "node_modules/@types/node": { + "version": "22.14.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.14.1.tgz", + "integrity": "sha512-u0HuPQwe/dHrItgHHpmw3N2fYCR6x4ivMNbPHRkBVP4CvN+kiRrKHWk3i8tXiO/joPwXLMYvF9TTF0eqgHIuOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/typescript": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..fa11019 --- /dev/null +++ b/package.json @@ -0,0 +1,25 @@ +{ + "name": "comicdicebot", + "version": "0.0.1-beta", + "description": "Comic's Dice Bot", + "keywords": [ + "discord", + "dice" + ], + "repository": { + "type": "git", + "url": "https://git.cyper.cc/Cyper/ComicDiceBot" + }, + "license": "MIT", + "author": "Cyper", + "type": "module", + "main": "app.ts", + "scripts": { + "build": "npx tsc app.ts --outDir dist", + "start": "npm run build && node dist/app.js" + }, + "devDependencies": { + "@types/node": "^22.14.1", + "typescript": "^5.8.3" + } +} diff --git a/placeholder-.ts b/placeholder-.ts new file mode 100644 index 0000000..29ffbf4 --- /dev/null +++ b/placeholder-.ts @@ -0,0 +1,163 @@ +type DiceType = 'f' | 'r' | 't'; + +interface RollResult { + input: string; + value: number; + rolls: number[]; // Kept dice + discarded: number[]; // Discarded dice + summary: string; // Formatted string of all dice +} + +function rollDie(diceType: DiceType): number { + const roll = { + f: Math.floor(Math.random() * 12) + 1 - 6, + r: Math.floor(Math.random() * 18) + 1 - 9, + t: Math.floor(Math.random() * 24) + 1 - 12, + }; + return roll[diceType]; +} + +function rollDice(count: number, diceType: DiceType): number[] { + return Array.from({ length: count }, () => rollDie(diceType)); +} + +function getDieRange(diceType: DiceType): { min: number; max: number } { + const ranges = { + f: { min: -5, max: 6 }, + r: { min: -8, max: 9 }, + t: { min: -11, max: 12 }, + }; + return ranges[diceType]; +} + +function formatSummary(rolls: number[], discarded: number[], diceType: DiceType): string { + const { min, max } = getDieRange(diceType); + + const all = rolls.map(r => ({ value: r, kept: true })) + .concat(discarded.map(r => ({ value: r, kept: false }))); + + return all + .sort((a, b) => b.value - a.value) + .map(({ value, kept }) => { + const isMinOrMax = value === min || value === max; + let formatted = `${value}`; + if (isMinOrMax) formatted = `**${formatted}**`; + if (!kept) formatted = `~${formatted}~`; + return formatted; + }) + .join(' '); +} + +function parseExpression(input: string): RollResult | RollResult[] { + const batchMatch = input.match(/^(\d+)#(.+)$/); + if (batchMatch) { + const [, batchCountStr, innerExpr] = batchMatch; + const batchCount = parseInt(batchCountStr, 10); + return Array.from({ length: batchCount }, () => parseSingleExpression(innerExpr)); + } else { + return parseSingleExpression(input); + } +} + +function parseSingleExpression(expr: string): RollResult { + const match = expr.match(/^(\d+)?([frt])(?:(kh|kl)(\d+))?(?:([+\-*])(\d+))?$/); + if (!match) throw new Error(`Invalid expression: ${expr}`); + + const [, countStr, diceType, keepType, keepCountStr, op, modStr] = match; + const count = parseInt(countStr || '1', 10); + const dice = diceType as DiceType; + const keepCount = keepCountStr ? parseInt(keepCountStr, 10) : null; + const modifier = modStr ? parseInt(modStr, 10) : 0; + const operator = op || '+'; + + const allRolls = rollDice(count, dice); + let rolls = [...allRolls]; + let discarded: number[] = []; + + if (keepType && keepCount !== null) { + const sorted = [...allRolls].sort((a, b) => keepType === 'kh' ? b - a : a - b); + rolls = sorted.slice(0, keepCount); + discarded = sorted.slice(keepCount); + } + + const baseSum = rolls.reduce((acc, val) => acc + val, 0); + let finalValue: number; + + switch (operator) { + case '+': + finalValue = baseSum + modifier; + break; + case '-': + finalValue = baseSum - modifier; + break; + case '*': + finalValue = baseSum * modifier; + break; + default: + finalValue = baseSum; + } + + return { + input: expr, + value: finalValue, + rolls, + discarded, + summary: formatSummary(rolls, discarded, dice) + }; +} + +function safeParseInput(message: string): RollResult | RollResult[] | null { + const trimmed = message.trim(); + + const isSingleWord = !trimmed.includes(' '); + + // CASE 1: Single word without brackets + if (isSingleWord && !trimmed.startsWith('[') && !trimmed.endsWith(']')) { + try { + return parseExpression(trimmed); + } catch { + return null; + } + } + + // CASE 2: Single word WITH brackets like "[3f]" + if (isSingleWord && trimmed.startsWith('[') && trimmed.endsWith(']')) { + const inside = trimmed.slice(1, -1).trim(); + try { + return parseExpression(inside); + } catch { + return null; + } + } + + // CASE 3: Multi-word input — check each word + const words = trimmed.split(/\s+/); + const expressions = words + .filter(word => word.startsWith('[') && word.endsWith(']')) + .map(word => word.slice(1, -1).trim()); + + if (expressions.length === 0) { + return null; + } + + const results: RollResult[] = []; + + for (const expr of expressions) { + try { + const result = parseExpression(expr); + if (Array.isArray(result)) { + results.push(...result); + } else { + results.push(result); + } + } catch { + // skip invalid + } + } + + return results.length > 0 ? results : null; +} + +console.log(safeParseInput('Hello I roll a 5rkh3+1')); +console.log(safeParseInput('I ROLL asdf[2#3fkl1*2]asdf')); +console.log(safeParseInput('[2#3fkl1*2] [2#3fkl1*2]')); \ No newline at end of file