feat: Added support for unlimited arithmetic operations

NOTE: These are not true PEMDOS operations - it is simply FIFO operations
This commit is contained in:
2025-04-23 23:53:02 -04:00
parent 4b93916aab
commit 5cec4c8122

View File

@ -6,6 +6,7 @@ interface RollResult {
rolls: number[]; // Kept dice rolls: number[]; // Kept dice
discarded: number[]; // Discarded dice discarded: number[]; // Discarded dice
summary: string; // Formatted string of all dice summary: string; // Formatted string of all dice
steps?: string[]; // Arithmetic operations performed
} }
function rollDie(diceType: DiceType): number { function rollDie(diceType: DiceType): number {
@ -32,7 +33,6 @@ function getDieRange(diceType: DiceType): { min: number; max: number } {
function formatSummary(rolls: number[], discarded: number[], diceType: DiceType): string { function formatSummary(rolls: number[], discarded: number[], diceType: DiceType): string {
const { min, max } = getDieRange(diceType); const { min, max } = getDieRange(diceType);
const all = rolls.map(r => ({ value: r, kept: true })) const all = rolls.map(r => ({ value: r, kept: true }))
.concat(discarded.map(r => ({ value: r, kept: false }))); .concat(discarded.map(r => ({ value: r, kept: false })));
@ -60,15 +60,16 @@ function parseExpression(input: string): RollResult | RollResult[] {
} }
function parseSingleExpression(expr: string): RollResult { function parseSingleExpression(expr: string): RollResult {
const match = expr.match(/^(\d+)?([frt])(?:(kh|kl)(\d+))?(?:([+\-*])(\d+))?$/); const baseMatch = expr.match(/^(\d+)?([frt])(?:(kh|kl)(\d+))?/);
if (!match) throw new Error(`Invalid expression: ${expr}`); if (!baseMatch) throw new Error(`Invalid expression: ${expr}`);
const [, countStr, diceType, keepType, keepCountStr] = baseMatch;
const diceExprLength = baseMatch[0].length;
const arithmeticExpr = expr.slice(diceExprLength);
const [, countStr, diceType, keepType, keepCountStr, op, modStr] = match;
const count = parseInt(countStr || '1', 10); const count = parseInt(countStr || '1', 10);
const dice = diceType as DiceType; const dice = diceType as DiceType;
const keepCount = keepCountStr ? parseInt(keepCountStr, 10) : null; const keepCount = keepCountStr ? parseInt(keepCountStr, 10) : null;
const modifier = modStr ? parseInt(modStr, 10) : 0;
const operator = op || '+';
const allRolls = rollDice(count, dice); const allRolls = rollDice(count, dice);
let rolls = [...allRolls]; let rolls = [...allRolls];
@ -80,38 +81,39 @@ function parseSingleExpression(expr: string): RollResult {
discarded = sorted.slice(keepCount); discarded = sorted.slice(keepCount);
} }
const baseSum = rolls.reduce((acc, val) => acc + val, 0); const steps: string[] = [];
let finalValue: number; let result = rolls.reduce((acc, val) => acc + val, 0);
steps.push(`Initial sum: ${result}`);
switch (operator) { const opRegex = /([+\-*/])\s*(-?\d+(?:\.\d+)?)/g;
case '+': let match;
finalValue = baseSum + modifier; while ((match = opRegex.exec(arithmeticExpr)) !== null) {
break; const [, op, valStr] = match;
case '-': const val = parseFloat(valStr);
finalValue = baseSum - modifier; const prev = result;
break; switch (op) {
case '*': case '+': result += val; break;
finalValue = baseSum * modifier; case '-': result -= val; break;
break; case '*': result *= val; break;
default: case '/': result /= val; break;
finalValue = baseSum; }
steps.push(`${prev} ${op} ${val} = ${result}`);
} }
return { return {
input: expr, input: expr,
value: finalValue, value: result,
rolls, rolls,
discarded, discarded,
summary: formatSummary(rolls, discarded, dice) summary: formatSummary(rolls, discarded, dice),
steps
}; };
} }
function safeParseInput(message: string): RollResult | RollResult[] | null { function safeParseInput(message: string): RollResult | RollResult[] | null {
const trimmed = message.trim(); const trimmed = message.trim();
const isSingleWord = !trimmed.includes(' '); const isSingleWord = !trimmed.includes(' ');
// CASE 1: Single word without brackets
if (isSingleWord && !trimmed.startsWith('[') && !trimmed.endsWith(']')) { if (isSingleWord && !trimmed.startsWith('[') && !trimmed.endsWith(']')) {
try { try {
return parseExpression(trimmed); return parseExpression(trimmed);
@ -120,7 +122,6 @@ function safeParseInput(message: string): RollResult | RollResult[] | null {
} }
} }
// CASE 2: Single word WITH brackets like "[3f]"
if (isSingleWord && trimmed.startsWith('[') && trimmed.endsWith(']')) { if (isSingleWord && trimmed.startsWith('[') && trimmed.endsWith(']')) {
const inside = trimmed.slice(1, -1).trim(); const inside = trimmed.slice(1, -1).trim();
try { try {
@ -130,7 +131,6 @@ function safeParseInput(message: string): RollResult | RollResult[] | null {
} }
} }
// CASE 3: Multi-word input — check each word
const words = trimmed.split(/\s+/); const words = trimmed.split(/\s+/);
const expressions = words const expressions = words
.filter(word => word.startsWith('[') && word.endsWith(']')) .filter(word => word.startsWith('[') && word.endsWith(']'))