@randsum/notation
Version:
Dice notation parser and types for the @randsum ecosystem
81 lines (64 loc) • 2.87 kB
text/typescript
import type { RerollOptions } from '../types'
import { formatComparisonNotation, hasConditions, parseComparisonNotation } from '../comparison'
import { formatHumanList } from '../formatHumanList'
import { type NotationSchema, defineNotationSchema } from '../schema'
const rerollPattern = /[Rr]\{((?:>=|<=|>|<|=)?\d+(?:,(?:>=|<=|>|<|=)?\d+)*)\}(\d+)?/g
export const rerollSchema: NotationSchema<RerollOptions> = defineNotationSchema<RerollOptions>({
name: 'reroll',
priority: 40,
pattern: /[Rr]\{((?:>=|<=|>|<|=)?\d+(?:,(?:>=|<=|>|<|=)?\d+)*)\}(\d+)?/,
parse: notation => {
const matches = Array.from(notation.matchAll(rerollPattern))
if (matches.length === 0) return {}
const reroll: RerollOptions = {}
for (const match of matches) {
const conditions = match[1]
const maxCount = match[2] ? Number(match[2]) : undefined
if (maxCount) {
reroll.max = maxCount
}
if (conditions) {
const parsed = parseComparisonNotation(conditions)
if (parsed.greaterThan !== undefined) reroll.greaterThan = parsed.greaterThan
if (parsed.greaterThanOrEqual !== undefined)
reroll.greaterThanOrEqual = parsed.greaterThanOrEqual
if (parsed.lessThan !== undefined) reroll.lessThan = parsed.lessThan
if (parsed.lessThanOrEqual !== undefined) reroll.lessThanOrEqual = parsed.lessThanOrEqual
if (parsed.exact) reroll.exact = [...(reroll.exact ?? []), ...parsed.exact]
}
}
return hasConditions(reroll) || reroll.max !== undefined ? { reroll } : {}
},
toNotation: options => {
const parts = formatComparisonNotation(options)
if (!parts.length) return undefined
const maxSuffix = options.max ? `${options.max}` : ''
return `R{${parts.join(',')}}${maxSuffix}`
},
toDescription: options => {
const { exact, greaterThan, greaterThanOrEqual, lessThan, lessThanOrEqual, max } = options
const rerollList: string[] = []
if (exact) {
exact.forEach(roll => rerollList.push(`${roll}`))
}
const greaterLessList: string[] = []
if (greaterThanOrEqual !== undefined) {
greaterLessList.push(`greater than or equal to ${greaterThanOrEqual}`)
}
if (greaterThan !== undefined) {
greaterLessList.push(`greater than ${greaterThan}`)
}
if (lessThanOrEqual !== undefined) {
greaterLessList.push(`less than or equal to ${lessThanOrEqual}`)
}
if (lessThan !== undefined) {
greaterLessList.push(`less than ${lessThan}`)
}
const exactList = formatHumanList(rerollList.map(Number))
const greaterLess = greaterLessList.join(' and ')
const conditions = [exactList, greaterLess].filter(Boolean).join(', ')
if (!conditions) return []
const maxText = max !== undefined ? ` (up to ${max} times)` : ''
return [`Reroll ${conditions}${maxText}`]
}
})