Initial commit
This commit is contained in:
48
package-lock.json
generated
Normal file
48
package-lock.json
generated
Normal file
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
25
package.json
Normal file
25
package.json
Normal file
@ -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"
|
||||
}
|
||||
}
|
||||
163
placeholder-.ts
Normal file
163
placeholder-.ts
Normal file
@ -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]'));
|
||||
Reference in New Issue
Block a user