/
Mativ
/
solo-master
Обзор
Документация
Войти
/
Mativ
/
solo-master
Код
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
src/shared/DiceFormula.ts
140 строк
5 KB
mativ
доп поля токенов
09 апр 2026, 19:10
09 апр 2026, 19:10
8c700ea
Код
Авторство
О чём код?
const VALID_FACES = new Set([4, 6, 8, 10, 12, 20, 100]); const FORMULA_PATTERN = /^[+-]?(\d+d\d+|\d+)([+-](\d+d\d+|\d+))*$/i; export interface RollResult { result: number; details: string; } export class DiceFormula { static readonly FORMULA_RULES = 'Формат броска: XdY (например: 2d6, 1d20)\n' + 'Допустимые грани: 4, 6, 8, 10, 12, 20, 100\n' + 'Операции: + и -\n' + 'Модификаторы: целые числа (например: +5, -2)\n' + 'Примеры: 2d6+3 · 1d20-1d4 · 3d6 · 10 · 2+4-3'; private _formula: string = ''; constructor(formula: string) { if (!DiceFormula.isvalid(formula)) throw new Error("Формула не валидна: " + formula); this.formula = formula } get formula(): string { return this._formula; } set formula(value: string) { this._formula = DiceFormula.normalize(value); } get isvalid(): boolean { return DiceFormula.isvalid(this._formula); } roll(): RollResult { return DiceFormula.roll(this._formula); } // ── Static ──────────────────────────────────────────────────────────────── static normalize(input: string): string { if (!input) return ""; let s = input.replace(/к/gi, 'd').replace(/\s+/g, ''); if (!s) return s; if (s[0] !== '+' && s[0] !== '-') s = '+' + s; const diceTokens: string[] = []; const numTokens: string[] = []; const re = /([+-])(\d+d\d+|\d+)/gi; let m: RegExpExecArray | null; while ((m = re.exec(s)) !== null) { if (/d/i.test(m[2])) diceTokens.push(m[0]); else numTokens.push(m[0]); } const result = [...diceTokens, ...numTokens].join(''); return result.startsWith('+') ? result.slice(1) : result; } static isvalid(formula: string): boolean { if (!formula) return false; if (!FORMULA_PATTERN.test(formula)) return false; const re = /\d+d(\d+)/gi; let m: RegExpExecArray | null; while ((m = re.exec(formula)) !== null) { if (!VALID_FACES.has(Number(m[1]))) return false; } return true; } static min(formula: string): number { if (!DiceFormula.isvalid(formula)) throw new Error(`Invalid dice formula: "${formula}"`); const s = formula[0] === '+' || formula[0] === '-' ? formula : '+' + formula; let total = 0; const re = /([+-])(\d+)d(\d+)|([+-])(\d+)/gi; let m: RegExpExecArray | null; while ((m = re.exec(s)) !== null) { if (m[1] !== undefined) { const sign = m[1] === '+' ? 1 : -1; total += sign * Number(m[2]) * 1; } else { total += (m[4] === '+' ? 1 : -1) * Number(m[5]); } } return total; } static max(formula: string): number { if (!DiceFormula.isvalid(formula)) throw new Error(`Invalid dice formula: "${formula}"`); const s = formula[0] === '+' || formula[0] === '-' ? formula : '+' + formula; let total = 0; const re = /([+-])(\d+)d(\d+)|([+-])(\d+)/gi; let m: RegExpExecArray | null; while ((m = re.exec(s)) !== null) { if (m[1] !== undefined) { const sign = m[1] === '+' ? 1 : -1; total += sign * Number(m[2]) * Number(m[3]); } else { total += (m[4] === '+' ? 1 : -1) * Number(m[5]); } } return total; } static roll(formula: string): RollResult { if (!DiceFormula.isvalid(formula)) { throw new Error(`Invalid dice formula: "${formula}"`); } const s = formula[0] === '+' || formula[0] === '-' ? formula : '+' + formula; let total = 0; const parts: string[] = []; const re = /([+-])(\d+)d(\d+)|([+-])(\d+)/gi; let m: RegExpExecArray | null; while ((m = re.exec(s)) !== null) { if (m[1] !== undefined) { const sign = m[1] === '+' ? 1 : -1; const count = Number(m[2]); const face = Number(m[3]); const rolls = Array.from({ length: count }, () => Math.floor(Math.random() * face) + 1); total += sign * rolls.reduce((a, b) => a + b, 0); parts.push(`${sign === -1 ? '-' : ''}d${face}[${rolls.join(',')}]`); } else { const sign = m[4] === '+' ? 1 : -1; const num = Number(m[5]); total += sign * num; parts.push(`${m[4]}${num}`); } } let details = parts.join(''); if (details.startsWith('+')) details = details.slice(1); return { result: total, details }; } }