114 lines
2.4 KiB
TypeScript

import { sum } from "./utils/sum";
export class Dice {
private count!: number;
private sides!: number;
private diceString: string;
toString() {
return this.diceString;
}
constructor(dice: string) {
this.parseDice(dice);
this.diceString = dice;
}
private parseDice(dice: string) {
const [c, s] = dice.split(/[dD]/);
this.count = Number(c);
this.sides = Number(s);
}
public roll() {
let results = [];
for (let i = 0; i < this.count; i++) {
results.push(this.rollSingle());
}
return {
total: sum(...results),
max: Math.max(...results),
min: Math.min(...results),
results,
};
}
public rollMax() {
return this.roll().max;
}
public rollMin() {
return this.roll().min;
}
private rollSingle() {
return Math.ceil(Math.random() * this.sides);
}
public rollAvg() {
return this.roll().total / this.count;
}
public rollTimes(times: number) {
let total = 0;
for (let i = 0; i < times; i++) {
total += this.roll().total;
}
return total;
}
public rollTimesAvg(times: number) {
return this.rollTimes(times) / times;
}
public getNormalizedRollDistribution(): Record<number, number> {
const distribution: Record<number, number> = this.computeDistribution();
// Normalize the distribution
const totalOutcomes = Math.pow(this.sides, this.count);
for (const sum in distribution) {
if (distribution.hasOwnProperty(sum)) {
distribution[sum] /= totalOutcomes;
}
}
return distribution;
}
public getRollDistribution(): Record<number, number> {
return this.computeDistribution();
}
public computeDistribution(): Record<number, number> {
const maxSum = this.count * this.sides;
const dp: number[][] = Array.from({ length: this.count + 1 }, () =>
Array(maxSum + 1).fill(0)
);
dp[0][0] = 1;
for (let dice = 1; dice <= this.count; dice++) {
for (let sum = 0; sum <= maxSum; sum++) {
for (let face = 1; face <= this.sides; face++) {
if (sum >= face) {
dp[dice][sum] += dp[dice - 1][sum - face];
}
}
}
}
const distribution: Record<number, number> = {};
for (let sum = this.count; sum <= maxSum; sum++) {
distribution[sum] = dp[this.count][sum];
}
return distribution;
}
// STATIC
static isDice(d: string) {
return /\d+[dD]\d+/.test(d);
}
}
// globalThis.Dice = Dice;