@randsum/notation
Version:
Dice notation parser and types for the @randsum ecosystem
113 lines (89 loc) • 3.72 kB
text/typescript
import type { DropOptions } from '../types'
import { hasConditions, parseComparisonNotation } from '../comparison'
import { formatHumanList } from '../formatHumanList'
import { type NotationSchema, defineNotationSchema } from '../schema'
const dropHighestPattern = /[Hh](\d+)?/g
const dropLowestPattern = /(?<![Kk])[Ll](\d+)?/g
const dropConstraintsPattern = /[Dd]\{((?:>=|<=|>|<|=)?\d+(?:,(?:>=|<=|>|<|=)?\d+)*)\}/
export const dropSchema: NotationSchema<DropOptions> = defineNotationSchema<DropOptions>({
name: 'drop',
priority: 20,
pattern:
/([Hh](\d+)?|(?<![Kk])[Ll](\d+)?|[Dd]\{((?:>=|<=|>|<|=)?\d+(?:,(?:>=|<=|>|<|=)?\d+)*)\})/,
parse: notation => {
const drop: DropOptions = {}
const highestMatches = Array.from(notation.matchAll(dropHighestPattern))
if (highestMatches.length > 0) {
drop.highest = highestMatches.reduce((sum, match) => {
return sum + (match[1] ? Number(match[1]) : 1)
}, 0)
}
const lowestMatches = Array.from(notation.matchAll(dropLowestPattern))
if (lowestMatches.length > 0) {
drop.lowest = lowestMatches.reduce((sum, match) => {
return sum + (match[1] ? Number(match[1]) : 1)
}, 0)
}
const constraintsMatch = dropConstraintsPattern.exec(notation)
if (constraintsMatch?.[1]) {
const parsed = parseComparisonNotation(constraintsMatch[1])
if (parsed.greaterThan !== undefined) drop.greaterThan = parsed.greaterThan
if (parsed.greaterThanOrEqual !== undefined)
drop.greaterThanOrEqual = parsed.greaterThanOrEqual
if (parsed.lessThan !== undefined) drop.lessThan = parsed.lessThan
if (parsed.lessThanOrEqual !== undefined) drop.lessThanOrEqual = parsed.lessThanOrEqual
if (parsed.exact) drop.exact = parsed.exact
}
return hasConditions(drop) || drop.highest !== undefined || drop.lowest !== undefined
? { drop }
: {}
},
toNotation: options => {
const { highest, lowest, greaterThan, greaterThanOrEqual, lessThan, lessThanOrEqual, exact } =
options
const parts: string[] = []
if (highest) {
parts.push(highest === 1 ? 'H' : `H${highest}`)
}
if (lowest) {
parts.push(lowest === 1 ? 'L' : `L${lowest}`)
}
const dropList: string[] = []
if (greaterThanOrEqual !== undefined) dropList.push(`>=${greaterThanOrEqual}`)
if (greaterThan !== undefined) dropList.push(`>${greaterThan}`)
if (lessThanOrEqual !== undefined) dropList.push(`<=${lessThanOrEqual}`)
if (lessThan !== undefined) dropList.push(`<${lessThan}`)
if (exact) exact.forEach(roll => dropList.push(`${roll}`))
if (dropList.length > 0) {
parts.push(`D{${dropList.join(',')}}`)
}
return parts.length ? parts.join('') : undefined
},
toDescription: options => {
const { highest, lowest, greaterThan, greaterThanOrEqual, lessThan, lessThanOrEqual, exact } =
options
const descriptions: string[] = []
if (highest) {
descriptions.push(highest > 1 ? `Drop highest ${highest}` : 'Drop highest')
}
if (lowest) {
descriptions.push(lowest > 1 ? `Drop lowest ${lowest}` : 'Drop lowest')
}
if (exact) {
descriptions.push(`Drop ${formatHumanList(exact)}`)
}
if (greaterThanOrEqual !== undefined) {
descriptions.push(`Drop greater than or equal to ${greaterThanOrEqual}`)
}
if (greaterThan !== undefined) {
descriptions.push(`Drop greater than ${greaterThan}`)
}
if (lessThanOrEqual !== undefined) {
descriptions.push(`Drop less than or equal to ${lessThanOrEqual}`)
}
if (lessThan !== undefined) {
descriptions.push(`Drop less than ${lessThan}`)
}
return descriptions
}
})