Skip to content

Commit

Permalink
Take Dice in roll, don't take undefined, use spread instead of manual…
Browse files Browse the repository at this point in the history
… array (#632)
  • Loading branch information
alxjrvs authored Sep 20, 2024
1 parent 7ee536c commit 17f3e0b
Show file tree
Hide file tree
Showing 25 changed files with 310 additions and 279 deletions.
14 changes: 5 additions & 9 deletions GETTING_STARTED.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,14 @@ const { roll } = require('randsum')
```ts
import { roll } from 'randsum'

const result = roll()
const result = roll(20)

console.log(result.total) // a random number between 1 and 20
```

### The Roll Result

`roll()` returns a `RandsumRollResult` object. This has plenty of helpful keys, but the big ones are `total` and `result`.
`roll(arg)` returns a `RandsumRollResult` object. This has plenty of helpful keys, but the big ones are `total` and `result`.

`total` returns the combined total of all your rolls, whereas `result` is an `Array` of `Array`s, each one representing the _set_ of different roll results for each pool of dice you rolled.

Expand Down Expand Up @@ -67,7 +67,7 @@ roll('4d20H+2') // Roll 4 20 sided die, drop highest, add 2

---

You can pass in a `RandsumRollOptions` as the first argument. While rolling standard numerical die, `sides` is the only required value, representing the number of distinct sides of the die.
You can pass in a `RandsumRollOptions` as the first argument. While rolling numerical die, `sides` is the only required value, representing the number of distinct sides of the die.

```ts
roll({ sides: 20 }) // Roll a single 20 sided die
Expand All @@ -94,15 +94,11 @@ roll({
`roll` accepts an array of options in varying different formats!

```ts
roll([
20,
'2d4',
{
roll(20, '2d4', {
sides: 20,
quantity: 4,
modifiers: { drop: { highest: true } }, { plus: 2 }
}
])
})
// Roll 1 d20, 2 d4, and 4 d20 (dropping the highest and adding 2)
```

Expand Down
3 changes: 2 additions & 1 deletion RANDSUM_DICE_NOTATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ In `randsum` :
```js
// Roll 1 twenty-sided die

roll()
roll(20)
roll('1d20')
roll({
Expand Down Expand Up @@ -87,6 +86,8 @@ roll({
})
```

Note: When using custom sides with Randsum Dice Notation, we can only mark sides as single characters. When using full options, you can pass in strings of any length!

#### Custom Sides Caveats and Gotchas

- Whenever _any_ dice pool leverages custom dice, the `total` of the `RandsumRollResult` will be `0`.
Expand Down
12 changes: 6 additions & 6 deletions src/D.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { isCustomSides } from '~guards'
import { isCustomSidesArg } from '~guards'
import { DicePoolType } from '~types'

type Type<T> = T extends string[] ? DicePoolType.custom : DicePoolType.standard
type Type<T> = T extends string[] ? DicePoolType.custom : DicePoolType.numerical
type Faces<T> = T extends string[] ? T : number[]
type Result<F> = F extends number[] ? number : string

Expand All @@ -11,25 +11,25 @@ class D<Sides extends string[] | number> {
type: Type<Sides>

constructor(sides: Sides) {
if (isCustomSides(sides)) {
if (isCustomSidesArg(sides)) {
this.sides = sides.length
this.type = DicePoolType.custom as Type<Sides>
this.faces = sides as Faces<Sides>
return
}
this.sides = sides
this.type = DicePoolType.standard as Type<Sides>
this.type = DicePoolType.numerical as Type<Sides>
this.faces = Array.from(
{ length: Number(sides) },
(_, index) => index + 1
) as Faces<Sides>
}

roll(): Result<Faces<Sides>> {
return this.faces[this.rawRoll()] as Result<Faces<Sides>>
return this.faces[this._rawRoll()] as Result<Faces<Sides>>
}

protected rawRoll(): number {
protected _rawRoll(): number {
return Math.floor(Math.random() * Number(this.sides))
}
}
Expand Down
20 changes: 16 additions & 4 deletions src/guards.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,14 @@ import {
RandsumRollOptions,
RandsumRollParameters,
Modifiers,
RandsumRollArgument
RandsumRollArgument,
DicePoolType
} from '~types'
import { D } from './D'

export function isDiceNotation(argument: unknown): argument is RandsumNotation {
export function isDiceNotationArg(
argument: unknown
): argument is RandsumNotation {
const notAString = typeof argument !== 'string'
const basicTest = !!coreNotationPattern.test(String(argument))
if (!basicTest || notAString) return false
Expand All @@ -17,8 +21,8 @@ export function isDiceNotation(argument: unknown): argument is RandsumNotation {
return cleanArg.replace(completeRollPattern, '').length === 0
}

export function isCustomSides(
argument: RandsumRollArgument | undefined
export function isCustomSidesArg(
argument: RandsumRollArgument
): argument is string[] {
return (
Array.isArray(argument) && argument.every((arg) => typeof arg === 'string')
Expand Down Expand Up @@ -49,3 +53,11 @@ export function isCustomParameters(
export function isFullNumArray(arr: unknown[]): arr is number[] {
return arr.every((item) => typeof item === 'number')
}

export function isD(arg: unknown): arg is D<number | string[]> {
return arg instanceof D
}

export function isCustomSidesD(arg: D<number | string[]>): arg is D<string[]> {
return arg.type === DicePoolType.custom
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { coreNotationPattern } from '~patterns'
import { RandsumNotation, RandsumRollOptions } from '~types'
import { parseCoreNotation, parseModifiers } from './parseModifiers'
import { parseCoreNotation, parseModifiers } from './optionsParsers'

function parseNotation(
function toOptions(
notationString: RandsumNotation
): RandsumRollOptions<number | string> {
const coreNotationMatch = notationString.match(coreNotationPattern)!.at(0)
Expand All @@ -14,4 +14,4 @@ function parseNotation(
}
}

export { parseNotation }
export default { toOptions }
File renamed without changes.
22 changes: 22 additions & 0 deletions src/models/OptionsModel/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { RandsumNotation, RandsumRollOptions } from '~types'
import {
formatCoreNotation,
formatModifierNotation
} from './notationFormatters'
import {
formatCoreDescriptions,
formatModifierDescriptions
} from './stringFormatters'

function toNotation(options: RandsumRollOptions): RandsumNotation {
return `${formatCoreNotation(options)}${formatModifierNotation(options)}` as RandsumNotation
}

function toDescription(options: RandsumRollOptions<number | string>) {
return [
formatCoreDescriptions(options),
...formatModifierDescriptions(options)
]
}

export default { toNotation, toDescription }
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { isValidModifier } from '~guards'
import {
DropOptions,
GreaterLessOptions,
RandsumNotation,
RandsumRollOptions,
ReplaceOptions,
RerollOptions,
UniqueOptions
Expand Down Expand Up @@ -100,3 +103,33 @@ function singleReplaceNotation(replace: ReplaceOptions) {
: formatGreaterLess(replace.from).join(',')
return `${fromValue}=${replace.to}`
}

export function formatModifierNotation({
modifiers
}: RandsumRollOptions): string {
if (!isValidModifier(modifiers)) return ''

const modifierStrings = []

if (modifiers.cap) modifierStrings.push(capNotation(modifiers.cap))
if (modifiers.drop) modifierStrings.push(dropNotation(modifiers.drop))
if (modifiers.replace)
modifierStrings.push(replaceNotation(modifiers.replace))
if (modifiers.reroll) modifierStrings.push(rerollNotation(modifiers.reroll))
if (modifiers.explode) modifierStrings.push(explodeNotation())
if (modifiers.unique) modifierStrings.push(uniqueNotation(modifiers.unique))
if (modifiers.plus) modifierStrings.push(plusNotation(modifiers.plus))
if (modifiers.minus) modifierStrings.push(minusNotation(modifiers.minus))

return modifierStrings.join('')
}

export function formatCoreNotation({
quantity = 1,
sides
}: RandsumRollOptions<string | number>): RandsumNotation {
const formattedSides = Array.isArray(sides)
? `{${sides.map((s) => (s === '' ? ' ' : s)).join('')}}`
: sides
return `${quantity}d${formattedSides}` as RandsumNotation
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { isValidModifier } from '~guards'
import {
GreaterLessOptions,
DropOptions,
ReplaceOptions,
RerollOptions,
UniqueOptions
UniqueOptions,
RandsumRollOptions
} from '~types'

function formatHumanList(list: (string | number)[]) {
Expand Down Expand Up @@ -105,3 +107,42 @@ function extractFromValue(from: number | GreaterLessOptions) {
function singleReplaceString(replace: ReplaceOptions) {
return `Replace ${extractFromValue(replace.from)} with [${replace.to}]`
}

export function formatCoreDescriptions({
sides,
quantity
}: RandsumRollOptions<number | string>) {
const base = `Roll ${quantity}`
const descriptor = (quantity || 1) > 1 ? 'dice' : 'die'
if (Array.isArray(sides)) {
const formattedSides = `${descriptor} with the following sides: (${sides
.map((s) => (s === '' ? ' ' : s))
.join(',')})`
return `${base} ${formattedSides}`
}

return `${base} ${sides}-sided ${descriptor}`
}

export function formatModifierDescriptions({
modifiers
}: RandsumRollOptions<number | string>): string[] {
if (!isValidModifier(modifiers)) return []

const modifierStrings = []

if (modifiers.cap)
capString(modifiers.cap).forEach((str) => modifierStrings.push(str))
if (modifiers.drop)
dropString(modifiers.drop).forEach((str) => modifierStrings.push(str))
if (modifiers.replace)
replaceString(modifiers.replace).forEach((str) => modifierStrings.push(str))
if (modifiers.reroll)
rerollString(modifiers.reroll).forEach((str) => modifierStrings.push(str))
if (modifiers.explode) modifierStrings.push(explodeString())
if (modifiers.unique) modifierStrings.push(uniqueString(modifiers.unique))
if (modifiers.plus) modifierStrings.push(plusString(modifiers.plus))
if (modifiers.minus) modifierStrings.push(minusString(modifiers.minus))

return modifierStrings
}
15 changes: 7 additions & 8 deletions src/parameterizeRollArgument/index.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
import { CoreRollArgument, RandsumRollParameters } from '~types'
import { RandsumRollArgument, RandsumRollParameters } from '~types'
import { D } from '~src/D'
import { formatDescription } from '~utils/formatDescription'
import { formatNotation } from '~utils/formatNotation'
import { parseDiceOptions } from './parseDiceOptions'
import { normalizeArgument } from './normalizeArgument'
import OptionsModel from '~models/OptionsModel'

function parameterizeRollArgument(
argument: CoreRollArgument | undefined
argument: RandsumRollArgument
): RandsumRollParameters {
const options = parseDiceOptions(argument)
const options = normalizeArgument(argument)
const die = new D(options.sides)
return {
options,
argument,
die,
notation: formatNotation(options),
description: formatDescription(options)
notation: OptionsModel.toNotation(options),
description: OptionsModel.toDescription(options)
}
}

Expand Down
39 changes: 39 additions & 0 deletions src/parameterizeRollArgument/normalizeArgument.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import {
isDicePoolOptions,
isDiceNotationArg,
isD,
isCustomSidesD
} from '~guards'
import { RandsumRollArgument, RandsumRollOptions } from '~types'
import NotationModel from '~models/NotationModel'

function normalizeArgument(argument: RandsumRollArgument): RandsumRollOptions {
if (isD(argument)) {
return {
quantity: 1,
sides: isCustomSidesD(argument) ? argument.faces : argument.sides
}
}

if (isDicePoolOptions(argument)) {
return argument
}

if (isDiceNotationArg(argument)) {
return NotationModel.toOptions(argument)
}

if (Array.isArray(argument)) {
return {
quantity: 1,
sides: argument.map(String)
}
}

return {
quantity: 1,
sides: Number(argument || 20)
}
}

export { normalizeArgument }
31 changes: 0 additions & 31 deletions src/parameterizeRollArgument/parseDiceOptions.ts

This file was deleted.

Loading

0 comments on commit 17f3e0b

Please sign in to comment.