feat: Added support for unlimited arithmetic operations
NOTE: These are not true PEMDOS operations - it is simply FIFO operations
This commit is contained in:
@ -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(']'))
|
||||||
|
|||||||
Reference in New Issue
Block a user