diff --git a/placeholder-.ts b/placeholder-.ts index 29ffbf4..3efe9e4 100644 --- a/placeholder-.ts +++ b/placeholder-.ts @@ -6,6 +6,7 @@ interface RollResult { rolls: number[]; // Kept dice discarded: number[]; // Discarded dice summary: string; // Formatted string of all dice + steps?: string[]; // Arithmetic operations performed } 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 { const { min, max } = getDieRange(diceType); - const all = rolls.map(r => ({ value: r, kept: true })) .concat(discarded.map(r => ({ value: r, kept: false }))); @@ -60,15 +60,16 @@ function parseExpression(input: string): RollResult | RollResult[] { } function parseSingleExpression(expr: string): RollResult { - const match = expr.match(/^(\d+)?([frt])(?:(kh|kl)(\d+))?(?:([+\-*])(\d+))?$/); - if (!match) throw new Error(`Invalid expression: ${expr}`); + const baseMatch = expr.match(/^(\d+)?([frt])(?:(kh|kl)(\d+))?/); + 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 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]; @@ -80,38 +81,39 @@ function parseSingleExpression(expr: string): RollResult { discarded = sorted.slice(keepCount); } - const baseSum = rolls.reduce((acc, val) => acc + val, 0); - let finalValue: number; + const steps: string[] = []; + let result = rolls.reduce((acc, val) => acc + val, 0); + steps.push(`Initial sum: ${result}`); - switch (operator) { - case '+': - finalValue = baseSum + modifier; - break; - case '-': - finalValue = baseSum - modifier; - break; - case '*': - finalValue = baseSum * modifier; - break; - default: - finalValue = baseSum; + const opRegex = /([+\-*/])\s*(-?\d+(?:\.\d+)?)/g; + let match; + while ((match = opRegex.exec(arithmeticExpr)) !== null) { + const [, op, valStr] = match; + const val = parseFloat(valStr); + const prev = result; + switch (op) { + case '+': result += val; break; + case '-': result -= val; break; + case '*': result *= val; break; + case '/': result /= val; break; + } + steps.push(`${prev} ${op} ${val} = ${result}`); } return { input: expr, - value: finalValue, + value: result, rolls, discarded, - summary: formatSummary(rolls, discarded, dice) + summary: formatSummary(rolls, discarded, dice), + steps }; } 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); @@ -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(']')) { const inside = trimmed.slice(1, -1).trim(); 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 expressions = words .filter(word => word.startsWith('[') && word.endsWith(']'))