@seroh/roll
v1.0.1
Published
An RPG dice-rolling library with a variety of built-in roll mechanics.
Downloads
55
Maintainers
Readme
Dice Rolling Library
This library provides a flexible and extensible system for rolling dice in tabletop RPGs, simulations, or any other applications requiring randomized results.
Installation
Install the library via npm:
npm install @seroh/roll
Usage
Here’s an example of creating a simple D&D 1d20
attack roll with a -1
modifier:
import {Roll} from "@seroh/roll";
const attack = new Roll(1, 20);
const result = attack.roll(-1);
console.log(`Natural Roll: ${result.natural}`);
console.log(`Modified Roll: ${result.modified}`);
Mechanics
A mechanic defines the rules and behavior of a roll, shaping how the final results are determined. For example, the "Advantage/Disadvantage" mechanic, commonly used in D&D, modifies rolls to favor higher or lower outcomes.
The following mechanics are currently available:
Example
import {Roll, ExplodingMechanic} from "@seroh/roll";
const explodingAttack = new Roll(1, 20, {mechanic: new ExplodingMechanic()});
const result = explodingAttack.roll();
Randomizers
Randomizers determine how randomness is achieved for rolls, enabling customizable strategies such as seeded, weighted, or default randomness.
In addition to the default strategy (which utilizes Math.random
), the following randomization strategies are also supported:
Example
import {Roll, KarmicRandomizer} from "@seroh/roll";
const weightedAttack = new Roll(1, 20, {
randomizer: new KarmicRandomizer({
highRollThreshold: 0.8,
lowRollThreshold: 0.2,
biasFactor: 0.2,
}),
});
const result = weightedAttack.roll();
Extending
The goal of this library is to provide a clear path for other developers to implement their own roll behaviours, therfore the library is seprated into core modules. Each core module is extensible and allows you to implement your own roll mechanics.
Randomizer
Developers can implement their own randomization strategies. By default, all rolls use JavaScript's standard Math.random
method. However, the system is fully extensible, giving you complete control over the randomness.
In order to create your own randomization strategy, you have to extend the Randomizer
class and override its generator
method. This method must return a random number between 0 (inclusive) and 1 (exclusive).
Example of creating your own randomizer
Here’s an example of how seeded randomness can be achieved. It employs the 'Linear Congruential Generator' (LCG) algorithm instead of the default Math.random
function, allowing the generator to produce a deterministic random value based on a provided seed.
import {Randomizer, Roll} from "@seroh/roll";
class SeededRandomizer extends Randomizer {
constructor(private seed: number) {
super();
}
// Generate a number between 0 and 1 (exclusive)
protected generator(): number {
return this.lcg(this.seed);
}
// Linear Congruential Generator (LCG) implementation
private lcg(seed: number): number {
const a = 1664525;
const c = 1013904223;
const m = 2 ** 32;
// Generate the next seed
this.seed = (a * seed + c) % m;
return this.seed / m;
}
}
const attack = new Roll(1, 20, {
randomizer: new SeededRandomizer(12345678),
});
Mechanic
A mechanic defines the rules and behavior of a roll, determining how final results are calculated. For instance, you can use a custom mechanic to implement advanced rolling rules such as exploding roll, advantage mechanics, or other game-specific logic. This allows you to encapsulate and reuse the behavior within your rolls seamlessly.
To create your own roll mechanic, extend the Mechanic class and override its do method.
Example of creating your own roll mechanic
Here's an implementation of a standard "Advantage" mechanic. This mechanic performs two rolls instead of one and returns the highest result. Additionally, it records the history of rolls in the rolls property.
import {Randomizer, Mechanic} from "@seroh/roll";
export class AdvantageMechanic extends Mechanic {
do(min: number, max: number, randomizer: Randomizer) {
const a = randomizer.generate(min, max);
const b = randomizer.generate(min, max);
return {result: Math.max(a, b), rolls: [a, b]};
}
}