@yipe/dice 0.2.1 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -30
- package/dist/builder/index.cjs +588 -552
- package/dist/builder/index.cjs.map +1 -1
- package/dist/builder/index.d.cts +15 -3
- package/dist/builder/index.d.ts +15 -3
- package/dist/builder/index.js +588 -552
- package/dist/builder/index.js.map +1 -1
- package/dist/index.cjs +63 -19
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +56 -20
- package/dist/index.js.map +1 -1
- package/dist/{pmf-DIegvlyA.d.cts → pmf-D1p2Re-J.d.cts} +29 -12
- package/dist/{pmf-DIegvlyA.d.ts → pmf-D1p2Re-J.d.ts} +29 -12
- package/package.json +1 -1
package/dist/builder/index.js
CHANGED
|
@@ -1385,40 +1385,68 @@ var _PMF = class _PMF {
|
|
|
1385
1385
|
* - A PMF representing its outcome (e.g., damage dice).
|
|
1386
1386
|
* - A weight representing its probability of being selected.
|
|
1387
1387
|
*
|
|
1388
|
-
* This is useful for modeling situations like:
|
|
1389
|
-
* - Sneak Attack: 6d6 if first hit is a crit, 3d6 if first hit is non-crit, 0 otherwise.
|
|
1390
|
-
* - Multiple exclusive spells: Fireball vs Cone of Cold vs nothing.
|
|
1391
|
-
*
|
|
1392
1388
|
* Notes:
|
|
1393
|
-
* - If
|
|
1394
|
-
*
|
|
1395
|
-
*
|
|
1396
|
-
*
|
|
1397
|
-
* @param options Array of `{ pmf, weight }` objects, each representing an outcome and its probability.
|
|
1398
|
-
* @param label Optional name for debugging or charting.
|
|
1399
|
-
* @param eps Optional tolerance for floating point rounding
|
|
1400
|
-
* @returns A single PMF representing the exclusive mixture of all options.
|
|
1389
|
+
* - If total weight < 1 (within eps), leftover mass is assumed to be PMF.zero()
|
|
1390
|
+
*
|
|
1391
|
+
* @param options Array of `{ pmf, weight }` or `[PMF, number]`.
|
|
1392
|
+
* @param eps Optional tolerance for floating point rounding.
|
|
1401
1393
|
*/
|
|
1402
1394
|
static exclusive(options, eps = EPS) {
|
|
1403
1395
|
const items = options.map(
|
|
1404
1396
|
(o) => Array.isArray(o) ? { pmf: o[0], weight: o[1] } : o
|
|
1405
1397
|
);
|
|
1406
|
-
|
|
1407
|
-
|
|
1398
|
+
for (const { weight } of items) {
|
|
1399
|
+
if (!Number.isFinite(weight) || weight < -eps) {
|
|
1400
|
+
throw new Error(`PMF.exclusive: invalid weight ${weight}.`);
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1403
|
+
let totalWeight = items.reduce((s, { weight }) => s + weight, 0);
|
|
1404
|
+
if (Math.abs(totalWeight) <= eps) totalWeight = 0;
|
|
1405
|
+
if (Math.abs(1 - totalWeight) <= eps) totalWeight = 1;
|
|
1406
|
+
if (totalWeight > 1 + eps) {
|
|
1408
1407
|
throw new Error(
|
|
1409
1408
|
`PMF.exclusive: total weight ${totalWeight.toFixed(6)} exceeds 1.`
|
|
1410
1409
|
);
|
|
1411
1410
|
}
|
|
1412
|
-
let out =
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
const leftover = 1 - totalWeight;
|
|
1411
|
+
let out = _PMF.empty(eps);
|
|
1412
|
+
for (const { pmf, weight } of items) {
|
|
1413
|
+
if (weight > eps) out = out.addScaled(pmf, weight);
|
|
1414
|
+
}
|
|
1415
|
+
const leftover = Math.max(0, 1 - totalWeight);
|
|
1417
1416
|
if (leftover > eps) {
|
|
1418
1417
|
out = out.addScaled(_PMF.zero(), leftover);
|
|
1419
1418
|
}
|
|
1420
1419
|
return out;
|
|
1421
1420
|
}
|
|
1421
|
+
/**
|
|
1422
|
+
* PMF.mix()
|
|
1423
|
+
*
|
|
1424
|
+
* Builds a PMF as a linear combination of input PMFs with the given weights.
|
|
1425
|
+
* Unlike `exclusive`, this does NOT:
|
|
1426
|
+
* - enforce that weights sum to 1
|
|
1427
|
+
* - add leftover probability to δ0 (PMF.zero())
|
|
1428
|
+
*
|
|
1429
|
+
* Use when outcomes are not mutually exclusive, or for interpolation/blending.
|
|
1430
|
+
*
|
|
1431
|
+
* @param options Array of `{ pmf, weight }` or `[PMF, number]`.
|
|
1432
|
+
* @param eps Optional tolerance for skipping tiny weights.
|
|
1433
|
+
*/
|
|
1434
|
+
static mix(options, eps = EPS) {
|
|
1435
|
+
const items = options.map(
|
|
1436
|
+
(o) => Array.isArray(o) ? { pmf: o[0], weight: o[1] } : o
|
|
1437
|
+
);
|
|
1438
|
+
for (const { weight } of items) {
|
|
1439
|
+
if (!Number.isFinite(weight)) {
|
|
1440
|
+
throw new Error(`PMF.mix: invalid weight ${weight}.`);
|
|
1441
|
+
}
|
|
1442
|
+
}
|
|
1443
|
+
let out = _PMF.empty(eps);
|
|
1444
|
+
for (const { pmf, weight } of items) {
|
|
1445
|
+
if (Math.abs(weight) <= eps) continue;
|
|
1446
|
+
out = out.addScaled(pmf, weight);
|
|
1447
|
+
}
|
|
1448
|
+
return out;
|
|
1449
|
+
}
|
|
1422
1450
|
/**
|
|
1423
1451
|
* General-purpose N-way mixture.
|
|
1424
1452
|
* weights: Array of [weight, PMF].
|
|
@@ -2387,386 +2415,113 @@ function d20PMF(rerollOne) {
|
|
|
2387
2415
|
return result;
|
|
2388
2416
|
}
|
|
2389
2417
|
|
|
2390
|
-
// src/builder/
|
|
2391
|
-
var
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
}
|
|
2418
|
-
|
|
2419
|
-
const n = cfg.rollType === "elven accuracy" ? 3 : 2;
|
|
2420
|
-
const mode = cfg.rollType === "disadvantage" ? "lowest" : "highest";
|
|
2421
|
-
const base = { type: "sum", count: n, child: node };
|
|
2422
|
-
node = { type: "keep", mode, count: 1, child: base };
|
|
2423
|
-
}
|
|
2424
|
-
appliedRollType = true;
|
|
2425
|
-
}
|
|
2426
|
-
if (cfg.rollType === "flat" && cfg.keep && cfg.keep.total > 0) {
|
|
2427
|
-
const base = { type: "sum", count: cfg.keep.total, child: node };
|
|
2428
|
-
node = {
|
|
2429
|
-
type: "keep",
|
|
2430
|
-
mode: cfg.keep.mode,
|
|
2431
|
-
count: cfg.keep.count,
|
|
2432
|
-
child: base
|
|
2433
|
-
};
|
|
2418
|
+
// src/builder/roll.ts
|
|
2419
|
+
var defaultConfig = {
|
|
2420
|
+
count: 1,
|
|
2421
|
+
sides: 0,
|
|
2422
|
+
modifier: 0,
|
|
2423
|
+
reroll: 0,
|
|
2424
|
+
explode: 0,
|
|
2425
|
+
minimum: 0,
|
|
2426
|
+
bestOf: 0,
|
|
2427
|
+
keep: void 0,
|
|
2428
|
+
rollType: "flat"
|
|
2429
|
+
};
|
|
2430
|
+
var RollBuilder = class _RollBuilder {
|
|
2431
|
+
constructor(countOrConfigs = 1) {
|
|
2432
|
+
__publicField(this, "subRollConfigs");
|
|
2433
|
+
// --- Dice Shortcut Methods ---
|
|
2434
|
+
__publicField(this, "d4", () => this.d(4));
|
|
2435
|
+
__publicField(this, "d6", () => this.d(6));
|
|
2436
|
+
__publicField(this, "d8", () => this.d(8));
|
|
2437
|
+
__publicField(this, "d10", () => this.d(10));
|
|
2438
|
+
__publicField(this, "d12", () => this.d(12));
|
|
2439
|
+
__publicField(this, "d20", () => this.d(20));
|
|
2440
|
+
__publicField(this, "d100", () => this.d(100));
|
|
2441
|
+
if (typeof countOrConfigs === "number") {
|
|
2442
|
+
const count = countOrConfigs;
|
|
2443
|
+
if (isNaN(count)) throw new Error("Invalid NaN value for count");
|
|
2444
|
+
this.subRollConfigs = [
|
|
2445
|
+
{ ...defaultConfig, count, isSubtraction: count < 0 }
|
|
2446
|
+
];
|
|
2434
2447
|
} else {
|
|
2435
|
-
|
|
2436
|
-
node = { type: "sum", count: c, child: node };
|
|
2448
|
+
this.subRollConfigs = countOrConfigs.map((c) => ({ ...c }));
|
|
2437
2449
|
}
|
|
2438
|
-
children.push({ node, sign });
|
|
2439
2450
|
}
|
|
2440
|
-
|
|
2441
|
-
return
|
|
2451
|
+
get lastConfig() {
|
|
2452
|
+
return this.subRollConfigs[this.subRollConfigs.length - 1];
|
|
2442
2453
|
}
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
});
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
return resolveSingleDie(node, eps);
|
|
2462
|
-
}
|
|
2463
|
-
case "sum": {
|
|
2464
|
-
const base = resolve(node.child, eps);
|
|
2465
|
-
const n = Math.max(0, Math.floor(node.count));
|
|
2466
|
-
if (n === 0) return PMF.delta(0, eps);
|
|
2467
|
-
if (n === 1) return base;
|
|
2468
|
-
return base.power(n, eps);
|
|
2454
|
+
getSubRollConfigs() {
|
|
2455
|
+
return this.subRollConfigs.map((c) => ({ ...c }));
|
|
2456
|
+
}
|
|
2457
|
+
// for testing
|
|
2458
|
+
static fromConfig(config) {
|
|
2459
|
+
return new _RollBuilder([{ ...defaultConfig, ...config }]);
|
|
2460
|
+
}
|
|
2461
|
+
static fromConfigs(configs) {
|
|
2462
|
+
return new _RollBuilder(
|
|
2463
|
+
configs.map((config) => ({ ...defaultConfig, ...config }))
|
|
2464
|
+
);
|
|
2465
|
+
}
|
|
2466
|
+
static fromArgs(...args) {
|
|
2467
|
+
if (args.length === 1) {
|
|
2468
|
+
const arg = args[0];
|
|
2469
|
+
if (typeof arg === "number") {
|
|
2470
|
+
if (isNaN(arg)) throw new Error("Invalid NaN value for argument");
|
|
2471
|
+
return new _RollBuilder(0).plus(arg);
|
|
2469
2472
|
}
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
const parts = [];
|
|
2473
|
-
for (const c of node.children) {
|
|
2474
|
-
if (c.node.type === "constant") {
|
|
2475
|
-
shift += c.sign * c.node.value;
|
|
2476
|
-
} else {
|
|
2477
|
-
const p = resolve(c.node, eps);
|
|
2478
|
-
parts.push(c.sign === 1 ? p : p.mapDamage((v) => -v));
|
|
2479
|
-
}
|
|
2480
|
-
}
|
|
2481
|
-
if (parts.length === 0) return PMF.delta(shift, eps);
|
|
2482
|
-
let res = parts.length === 1 ? parts[0] : PMF.convolveMany(parts, eps);
|
|
2483
|
-
if (shift !== 0) res = res.mapDamage((v) => v + shift);
|
|
2484
|
-
return res;
|
|
2473
|
+
if (arg instanceof _RollBuilder) {
|
|
2474
|
+
return arg;
|
|
2485
2475
|
}
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
if (keepCount === 0 || total === 0) return PMF.delta(0, eps);
|
|
2492
|
-
return keepSumPMF(
|
|
2493
|
-
perDie,
|
|
2494
|
-
total,
|
|
2495
|
-
keepCount,
|
|
2496
|
-
node.mode === "highest",
|
|
2497
|
-
eps
|
|
2498
|
-
);
|
|
2476
|
+
}
|
|
2477
|
+
if (args.length === 2 || args.length === 3) {
|
|
2478
|
+
const [count, sidesOrDie, modifier] = args;
|
|
2479
|
+
if (typeof count !== "number") {
|
|
2480
|
+
throw new Error("First argument must be a number for multi-arg call");
|
|
2499
2481
|
}
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
const
|
|
2503
|
-
|
|
2482
|
+
if (isNaN(count)) throw new Error("Invalid NaN value for count argument");
|
|
2483
|
+
if (sidesOrDie instanceof _RollBuilder) {
|
|
2484
|
+
const subRollConfigs = sidesOrDie.getSubRollConfigs();
|
|
2485
|
+
if (subRollConfigs.length === 0) {
|
|
2486
|
+
const result = new _RollBuilder(0);
|
|
2487
|
+
return modifier !== void 0 ? result.plus(modifier) : result;
|
|
2488
|
+
}
|
|
2489
|
+
const absCount = Math.abs(count);
|
|
2490
|
+
const newConfigs = subRollConfigs.map((config) => ({
|
|
2491
|
+
...config,
|
|
2492
|
+
count: config.count * absCount,
|
|
2493
|
+
modifier: config.modifier * absCount
|
|
2494
|
+
}));
|
|
2495
|
+
let resultBuilder = new _RollBuilder(newConfigs);
|
|
2496
|
+
if (count < 0) {
|
|
2497
|
+
const negatedConfigs = resultBuilder.getSubRollConfigs().map((c) => ({ ...c, isSubtraction: !c.isSubtraction }));
|
|
2498
|
+
resultBuilder = new _RollBuilder(negatedConfigs);
|
|
2499
|
+
}
|
|
2500
|
+
return modifier !== void 0 ? resultBuilder.plus(modifier) : resultBuilder;
|
|
2501
|
+
} else if (typeof sidesOrDie === "number" || sidesOrDie === void 0) {
|
|
2502
|
+
if (typeof sidesOrDie === "number" && isNaN(sidesOrDie))
|
|
2503
|
+
throw new Error("Invalid NaN value for sides argument");
|
|
2504
|
+
let builder = new _RollBuilder(count);
|
|
2505
|
+
if (sidesOrDie && sidesOrDie > 0) {
|
|
2506
|
+
builder = builder.d(sidesOrDie);
|
|
2507
|
+
}
|
|
2508
|
+
return modifier !== void 0 ? builder.plus(modifier) : builder;
|
|
2504
2509
|
}
|
|
2505
2510
|
}
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
function resolveSingleDie(die, eps = defaultEps) {
|
|
2516
|
-
const signature = getASTSignature(die);
|
|
2517
|
-
const cacheKey = `${signature}_${eps}`;
|
|
2518
|
-
const cached = singleDiePMFCache.get(cacheKey);
|
|
2519
|
-
if (cached) return cached;
|
|
2520
|
-
const s = Math.max(0, Math.floor(die.sides));
|
|
2521
|
-
if (s <= 0) return PMF.delta(0, eps);
|
|
2522
|
-
let probs = /* @__PURE__ */ new Map();
|
|
2523
|
-
for (let v = 1; v <= s; v++) probs.set(v, 1 / s);
|
|
2524
|
-
const r = Math.max(0, Math.floor(die.reroll || 0));
|
|
2525
|
-
if (r > 0) {
|
|
2526
|
-
const k = Math.min(r, s);
|
|
2527
|
-
const rerollMass = k / s;
|
|
2528
|
-
const uniformReroll = rerollMass / s;
|
|
2529
|
-
const next = /* @__PURE__ */ new Map();
|
|
2530
|
-
for (let v = 1; v <= s; v++) {
|
|
2531
|
-
const keep = v <= k ? 0 : 1 / s;
|
|
2532
|
-
next.set(v, keep + uniformReroll);
|
|
2511
|
+
throw new Error(`Invalid arguments passed: ${args.join(", ")}`);
|
|
2512
|
+
}
|
|
2513
|
+
// --- Core Dice Methods ---
|
|
2514
|
+
d(sides) {
|
|
2515
|
+
if (sides !== void 0 && isNaN(sides))
|
|
2516
|
+
throw new Error("Invalid NaN value for sides");
|
|
2517
|
+
if (sides === void 0) return this;
|
|
2518
|
+
if (this.lastConfig.sides && this.lastConfig.sides > 0) {
|
|
2519
|
+
throw new Error("Cannot add a die after adding a die");
|
|
2533
2520
|
}
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
if (minV > 0) pmf = pmf.mapDamage((v) => Math.max(v, minV));
|
|
2539
|
-
const explode = die.explode;
|
|
2540
|
-
if (explode && Number.isFinite(explode) && explode > 0) {
|
|
2541
|
-
const times = Math.floor(explode);
|
|
2542
|
-
const maxFace = s;
|
|
2543
|
-
const nonMax = /* @__PURE__ */ new Map();
|
|
2544
|
-
const pMax = pmf.pAt(maxFace);
|
|
2545
|
-
for (const v of pmf.support()) {
|
|
2546
|
-
if (v !== maxFace) nonMax.set(v, pmf.pAt(v));
|
|
2547
|
-
}
|
|
2548
|
-
let nonMaxPMF = PMF.fromMap(nonMax, eps);
|
|
2549
|
-
if (Math.abs(nonMaxPMF.mass() - (1 - pMax)) > eps) {
|
|
2550
|
-
nonMaxPMF = nonMaxPMF.scaleMass(1 - pMax);
|
|
2551
|
-
}
|
|
2552
|
-
let tail = PMF.delta(0, eps);
|
|
2553
|
-
const addOnce = pmf;
|
|
2554
|
-
for (let t = 1; t <= times; t++) {
|
|
2555
|
-
tail = tail.convolve(addOnce, eps);
|
|
2556
|
-
}
|
|
2557
|
-
const exploded = PMF.branch(
|
|
2558
|
-
tail.mapDamage((v) => v + maxFace),
|
|
2559
|
-
nonMaxPMF,
|
|
2560
|
-
pMax
|
|
2561
|
-
);
|
|
2562
|
-
pmf = exploded;
|
|
2563
|
-
}
|
|
2564
|
-
singleDiePMFCache.set(cacheKey, pmf);
|
|
2565
|
-
return pmf;
|
|
2566
|
-
}
|
|
2567
|
-
function findDie(node) {
|
|
2568
|
-
switch (node.type) {
|
|
2569
|
-
case "die":
|
|
2570
|
-
return node;
|
|
2571
|
-
case "constant":
|
|
2572
|
-
return void 0;
|
|
2573
|
-
case "sum":
|
|
2574
|
-
case "d20Roll":
|
|
2575
|
-
return findDie(node.child);
|
|
2576
|
-
case "keep":
|
|
2577
|
-
return findDie(node.child.child);
|
|
2578
|
-
case "add":
|
|
2579
|
-
for (const c of node.children) {
|
|
2580
|
-
const d2 = findDie(c.node);
|
|
2581
|
-
if (d2) return d2;
|
|
2582
|
-
}
|
|
2583
|
-
return void 0;
|
|
2584
|
-
}
|
|
2585
|
-
}
|
|
2586
|
-
function getTotalCount(node) {
|
|
2587
|
-
let cur = node.child;
|
|
2588
|
-
while (cur.type === "keep") cur = cur.child;
|
|
2589
|
-
return cur.type === "sum" ? Math.max(0, Math.floor(cur.count)) : 0;
|
|
2590
|
-
}
|
|
2591
|
-
function keepSumPMF(single, total, keep, highest, eps = defaultEps) {
|
|
2592
|
-
if (keep >= total) return single.power(total, eps);
|
|
2593
|
-
if (keep <= 0) return PMF.delta(0, eps);
|
|
2594
|
-
const sortedSupport = [...single.support()].sort((a, b) => a - b);
|
|
2595
|
-
const pmfSig = sortedSupport.map((val) => `${val}:${single.pAt(val).toPrecision(6)}`).join(",");
|
|
2596
|
-
const cacheKey = `keep|${pmfSig}|t:${total}|k:${keep}|h:${highest ? 1 : 0}|e:${eps}`;
|
|
2597
|
-
const cached = builderPMFCache.get(cacheKey);
|
|
2598
|
-
if (cached) return cached;
|
|
2599
|
-
const support = single.support();
|
|
2600
|
-
const probs = support.map((v) => ({ v, p: single.pAt(v) }));
|
|
2601
|
-
const out = /* @__PURE__ */ new Map();
|
|
2602
|
-
function dfs(i, accValues, weight) {
|
|
2603
|
-
if (i === total) {
|
|
2604
|
-
const sorted = [...accValues].sort((a, b) => highest ? b - a : a - b);
|
|
2605
|
-
let sum = 0;
|
|
2606
|
-
for (let j = 0; j < keep; j++) sum += sorted[j];
|
|
2607
|
-
out.set(sum, (out.get(sum) || 0) + weight);
|
|
2608
|
-
return;
|
|
2609
|
-
}
|
|
2610
|
-
for (const { v, p } of probs) {
|
|
2611
|
-
if (p <= 0) continue;
|
|
2612
|
-
dfs(i + 1, [...accValues, v], weight * p);
|
|
2613
|
-
}
|
|
2614
|
-
}
|
|
2615
|
-
dfs(0, [], 1);
|
|
2616
|
-
const result = PMF.fromMap(out, eps);
|
|
2617
|
-
builderPMFCache.set(cacheKey, result);
|
|
2618
|
-
return result;
|
|
2619
|
-
}
|
|
2620
|
-
function getASTSignature(node) {
|
|
2621
|
-
switch (node.type) {
|
|
2622
|
-
case "constant":
|
|
2623
|
-
return `c:${node.value}`;
|
|
2624
|
-
case "die": {
|
|
2625
|
-
const parts = [];
|
|
2626
|
-
parts.push(`s:${node.sides}`);
|
|
2627
|
-
if (node.reroll) parts.push(`r:${node.reroll}`);
|
|
2628
|
-
if (node.minimum) parts.push(`m:${node.minimum}`);
|
|
2629
|
-
if (node.explode) parts.push(`e:${node.explode}`);
|
|
2630
|
-
return `d{${parts.join(",")}}`;
|
|
2631
|
-
}
|
|
2632
|
-
case "sum":
|
|
2633
|
-
return `sum{c:${node.count},ch:${getASTSignature(node.child)}}`;
|
|
2634
|
-
case "d20Roll":
|
|
2635
|
-
return `d20{t:${node.rollType},ch:${getASTSignature(node.child)}}`;
|
|
2636
|
-
case "keep":
|
|
2637
|
-
return `keep{c:${node.count},m:${node.mode},ch:${getASTSignature(
|
|
2638
|
-
node.child
|
|
2639
|
-
)}}`;
|
|
2640
|
-
case "add": {
|
|
2641
|
-
let constantValue = 0;
|
|
2642
|
-
const otherChildrenSigs = [];
|
|
2643
|
-
for (const c of node.children) {
|
|
2644
|
-
if (c.node.type === "constant") {
|
|
2645
|
-
constantValue += c.sign * c.node.value;
|
|
2646
|
-
} else {
|
|
2647
|
-
otherChildrenSigs.push(
|
|
2648
|
-
`${c.sign === -1 ? "-" : "+"}${getASTSignature(c.node)}`
|
|
2649
|
-
);
|
|
2650
|
-
}
|
|
2651
|
-
}
|
|
2652
|
-
if (constantValue !== 0) {
|
|
2653
|
-
otherChildrenSigs.push(
|
|
2654
|
-
constantValue > 0 ? `+c:${constantValue}` : `-c:${-constantValue}`
|
|
2655
|
-
);
|
|
2656
|
-
}
|
|
2657
|
-
otherChildrenSigs.sort();
|
|
2658
|
-
return `add[${otherChildrenSigs.join("")}]`;
|
|
2659
|
-
}
|
|
2660
|
-
}
|
|
2661
|
-
}
|
|
2662
|
-
|
|
2663
|
-
// src/builder/roll.ts
|
|
2664
|
-
var defaultConfig = {
|
|
2665
|
-
count: 1,
|
|
2666
|
-
sides: 0,
|
|
2667
|
-
modifier: 0,
|
|
2668
|
-
reroll: 0,
|
|
2669
|
-
explode: 0,
|
|
2670
|
-
minimum: 0,
|
|
2671
|
-
bestOf: 0,
|
|
2672
|
-
keep: void 0,
|
|
2673
|
-
rollType: "flat"
|
|
2674
|
-
};
|
|
2675
|
-
var RollBuilder = class _RollBuilder {
|
|
2676
|
-
constructor(countOrConfigs = 1) {
|
|
2677
|
-
__publicField(this, "subRollConfigs");
|
|
2678
|
-
// --- Dice Shortcut Methods ---
|
|
2679
|
-
__publicField(this, "d4", () => this.d(4));
|
|
2680
|
-
__publicField(this, "d6", () => this.d(6));
|
|
2681
|
-
__publicField(this, "d8", () => this.d(8));
|
|
2682
|
-
__publicField(this, "d10", () => this.d(10));
|
|
2683
|
-
__publicField(this, "d12", () => this.d(12));
|
|
2684
|
-
__publicField(this, "d20", () => this.d(20));
|
|
2685
|
-
__publicField(this, "d100", () => this.d(100));
|
|
2686
|
-
if (typeof countOrConfigs === "number") {
|
|
2687
|
-
const count = countOrConfigs;
|
|
2688
|
-
if (isNaN(count)) throw new Error("Invalid NaN value for count");
|
|
2689
|
-
this.subRollConfigs = [
|
|
2690
|
-
{ ...defaultConfig, count, isSubtraction: count < 0 }
|
|
2691
|
-
];
|
|
2692
|
-
} else {
|
|
2693
|
-
this.subRollConfigs = countOrConfigs.map((c) => ({ ...c }));
|
|
2694
|
-
}
|
|
2695
|
-
}
|
|
2696
|
-
get lastConfig() {
|
|
2697
|
-
return this.subRollConfigs[this.subRollConfigs.length - 1];
|
|
2698
|
-
}
|
|
2699
|
-
getSubRollConfigs() {
|
|
2700
|
-
return this.subRollConfigs.map((c) => ({ ...c }));
|
|
2701
|
-
}
|
|
2702
|
-
// for testing
|
|
2703
|
-
static fromConfig(config) {
|
|
2704
|
-
return new _RollBuilder([{ ...defaultConfig, ...config }]);
|
|
2705
|
-
}
|
|
2706
|
-
static fromConfigs(configs) {
|
|
2707
|
-
return new _RollBuilder(
|
|
2708
|
-
configs.map((config) => ({ ...defaultConfig, ...config }))
|
|
2709
|
-
);
|
|
2710
|
-
}
|
|
2711
|
-
static fromArgs(...args) {
|
|
2712
|
-
if (args.length === 1) {
|
|
2713
|
-
const arg = args[0];
|
|
2714
|
-
if (typeof arg === "number") {
|
|
2715
|
-
if (isNaN(arg)) throw new Error("Invalid NaN value for argument");
|
|
2716
|
-
return new _RollBuilder(0).plus(arg);
|
|
2717
|
-
}
|
|
2718
|
-
if (arg instanceof _RollBuilder) {
|
|
2719
|
-
return arg;
|
|
2720
|
-
}
|
|
2721
|
-
}
|
|
2722
|
-
if (args.length === 2 || args.length === 3) {
|
|
2723
|
-
const [count, sidesOrDie, modifier] = args;
|
|
2724
|
-
if (typeof count !== "number") {
|
|
2725
|
-
throw new Error("First argument must be a number for multi-arg call");
|
|
2726
|
-
}
|
|
2727
|
-
if (isNaN(count)) throw new Error("Invalid NaN value for count argument");
|
|
2728
|
-
if (sidesOrDie instanceof _RollBuilder) {
|
|
2729
|
-
const subRollConfigs = sidesOrDie.getSubRollConfigs();
|
|
2730
|
-
if (subRollConfigs.length === 0) {
|
|
2731
|
-
const result = new _RollBuilder(0);
|
|
2732
|
-
return modifier !== void 0 ? result.plus(modifier) : result;
|
|
2733
|
-
}
|
|
2734
|
-
const absCount = Math.abs(count);
|
|
2735
|
-
const newConfigs = subRollConfigs.map((config) => ({
|
|
2736
|
-
...config,
|
|
2737
|
-
count: config.count * absCount,
|
|
2738
|
-
modifier: config.modifier * absCount
|
|
2739
|
-
}));
|
|
2740
|
-
let resultBuilder = new _RollBuilder(newConfigs);
|
|
2741
|
-
if (count < 0) {
|
|
2742
|
-
const negatedConfigs = resultBuilder.getSubRollConfigs().map((c) => ({ ...c, isSubtraction: !c.isSubtraction }));
|
|
2743
|
-
resultBuilder = new _RollBuilder(negatedConfigs);
|
|
2744
|
-
}
|
|
2745
|
-
return modifier !== void 0 ? resultBuilder.plus(modifier) : resultBuilder;
|
|
2746
|
-
} else if (typeof sidesOrDie === "number" || sidesOrDie === void 0) {
|
|
2747
|
-
if (typeof sidesOrDie === "number" && isNaN(sidesOrDie))
|
|
2748
|
-
throw new Error("Invalid NaN value for sides argument");
|
|
2749
|
-
let builder = new _RollBuilder(count);
|
|
2750
|
-
if (sidesOrDie && sidesOrDie > 0) {
|
|
2751
|
-
builder = builder.d(sidesOrDie);
|
|
2752
|
-
}
|
|
2753
|
-
return modifier !== void 0 ? builder.plus(modifier) : builder;
|
|
2754
|
-
}
|
|
2755
|
-
}
|
|
2756
|
-
throw new Error(`Invalid arguments passed: ${args.join(", ")}`);
|
|
2757
|
-
}
|
|
2758
|
-
// --- Core Dice Methods ---
|
|
2759
|
-
d(sides) {
|
|
2760
|
-
if (sides !== void 0 && isNaN(sides))
|
|
2761
|
-
throw new Error("Invalid NaN value for sides");
|
|
2762
|
-
if (sides === void 0) return this;
|
|
2763
|
-
if (this.lastConfig.sides && this.lastConfig.sides > 0) {
|
|
2764
|
-
throw new Error("Cannot add a die after adding a die");
|
|
2765
|
-
}
|
|
2766
|
-
if (sides === 0) return this;
|
|
2767
|
-
const newConfigs = this.getSubRollConfigs();
|
|
2768
|
-
newConfigs[newConfigs.length - 1].sides = sides;
|
|
2769
|
-
return new this.constructor(newConfigs);
|
|
2521
|
+
if (sides === 0) return this;
|
|
2522
|
+
const newConfigs = this.getSubRollConfigs();
|
|
2523
|
+
newConfigs[newConfigs.length - 1].sides = sides;
|
|
2524
|
+
return new this.constructor(newConfigs);
|
|
2770
2525
|
}
|
|
2771
2526
|
plus(modOrRoll, die) {
|
|
2772
2527
|
if (typeof modOrRoll === "number" && isNaN(modOrRoll))
|
|
@@ -2901,19 +2656,11 @@ var RollBuilder = class _RollBuilder {
|
|
|
2901
2656
|
copy() {
|
|
2902
2657
|
return new this.constructor(this.getSubRollConfigs());
|
|
2903
2658
|
}
|
|
2904
|
-
ac(targetAC) {
|
|
2905
|
-
if (isNaN(targetAC)) throw new Error("Invalid NaN value for targetAC");
|
|
2906
|
-
return new ACBuilder(this, targetAC);
|
|
2907
|
-
}
|
|
2908
2659
|
withElvenAccuracy() {
|
|
2909
2660
|
const newConfigs = this.getSubRollConfigs();
|
|
2910
2661
|
newConfigs[newConfigs.length - 1].rollType = "elven accuracy";
|
|
2911
2662
|
return new this.constructor(newConfigs);
|
|
2912
2663
|
}
|
|
2913
|
-
dc(saveDC) {
|
|
2914
|
-
if (isNaN(saveDC)) throw new Error("Invalid NaN value for saveDC");
|
|
2915
|
-
return new DCBuilder(this).dc(saveDC);
|
|
2916
|
-
}
|
|
2917
2664
|
toExpression() {
|
|
2918
2665
|
const diceConfigs = this.subRollConfigs.filter(
|
|
2919
2666
|
(config) => config.sides && config.sides > 0
|
|
@@ -2941,118 +2688,444 @@ var RollBuilder = class _RollBuilder {
|
|
|
2941
2688
|
result += operator + expression;
|
|
2942
2689
|
}
|
|
2943
2690
|
}
|
|
2944
|
-
if (totalModifier > 0) result += ` + ${totalModifier}`;
|
|
2945
|
-
else if (totalModifier < 0) result += ` - ${Math.abs(totalModifier)}`;
|
|
2946
|
-
return result.replace(/\+ -/g, "-");
|
|
2947
|
-
}
|
|
2948
|
-
toPMF(eps = 0) {
|
|
2949
|
-
return pmfFromRollBuilder(this, eps);
|
|
2950
|
-
}
|
|
2951
|
-
|
|
2952
|
-
return this.toPMF(
|
|
2953
|
-
}
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2691
|
+
if (totalModifier > 0) result += ` + ${totalModifier}`;
|
|
2692
|
+
else if (totalModifier < 0) result += ` - ${Math.abs(totalModifier)}`;
|
|
2693
|
+
return result.replace(/\+ -/g, "-");
|
|
2694
|
+
}
|
|
2695
|
+
toPMF(eps = 0) {
|
|
2696
|
+
return pmfFromRollBuilder(this, eps);
|
|
2697
|
+
}
|
|
2698
|
+
get pmf() {
|
|
2699
|
+
return this.toPMF();
|
|
2700
|
+
}
|
|
2701
|
+
toQuery(eps = 0) {
|
|
2702
|
+
return this.toPMF(eps).query();
|
|
2703
|
+
}
|
|
2704
|
+
toAST() {
|
|
2705
|
+
const configs = this.getSubRollConfigs();
|
|
2706
|
+
return astFromRollConfigs(configs) || { type: "constant", value: 0 };
|
|
2707
|
+
}
|
|
2708
|
+
configToSingleExpressionWithoutModifier(config, isRootDie) {
|
|
2709
|
+
if (!config.sides || config.sides <= 0) return "";
|
|
2710
|
+
let baseDie = `d${config.sides}`;
|
|
2711
|
+
if (config.reroll > 0) {
|
|
2712
|
+
if (config.minimum > 0 && config.explode > 0) ; else if (config.minimum > 0) {
|
|
2713
|
+
for (let i = config.reroll; i >= 1; i--) baseDie += ` reroll ${i}`;
|
|
2714
|
+
} else {
|
|
2715
|
+
for (let i = 1; i <= config.reroll; i++) baseDie += ` reroll ${i}`;
|
|
2716
|
+
}
|
|
2717
|
+
}
|
|
2718
|
+
if (config.minimum > 0) {
|
|
2719
|
+
baseDie = `${config.minimum}>${baseDie}`;
|
|
2720
|
+
if (config.reroll > 0 && config.explode > 0) {
|
|
2721
|
+
for (let i = 1; i <= config.reroll; i++) {
|
|
2722
|
+
baseDie += ` reroll ${i}`;
|
|
2723
|
+
}
|
|
2724
|
+
}
|
|
2725
|
+
}
|
|
2726
|
+
if (baseDie === "d20 reroll 1" && config.minimum <= 1) baseDie = "hd20";
|
|
2727
|
+
let mainExpression = "";
|
|
2728
|
+
switch (config.rollType) {
|
|
2729
|
+
case "advantage":
|
|
2730
|
+
mainExpression = `${baseDie} > ${baseDie}`;
|
|
2731
|
+
break;
|
|
2732
|
+
case "disadvantage":
|
|
2733
|
+
mainExpression = `${baseDie} < ${baseDie}`;
|
|
2734
|
+
break;
|
|
2735
|
+
case "elven accuracy":
|
|
2736
|
+
mainExpression = `${baseDie} > ${baseDie} > ${baseDie}`;
|
|
2737
|
+
break;
|
|
2738
|
+
case "flat":
|
|
2739
|
+
if (config.keep) {
|
|
2740
|
+
const mode = config.keep.mode === "highest" ? "kh" : "kl";
|
|
2741
|
+
mainExpression = `${config.keep.total}${mode}${config.keep.count}(${baseDie})`;
|
|
2742
|
+
} else {
|
|
2743
|
+
const isComplex = baseDie.length > `d${config.sides}`.length;
|
|
2744
|
+
const isHalflingShorthand = baseDie === "hd20";
|
|
2745
|
+
const isD20Shorthand = baseDie === "d20" && isRootDie;
|
|
2746
|
+
const hasMinimum = baseDie.startsWith("(") && baseDie.includes(">");
|
|
2747
|
+
const hasReroll = baseDie.includes("reroll");
|
|
2748
|
+
const effectiveCount = config.isSubtraction ? Math.abs(config.count) : config.count < 0 ? 1 : Math.abs(config.count);
|
|
2749
|
+
if (effectiveCount > 1) {
|
|
2750
|
+
const shouldAddParentheses = isComplex && (// Complex single roll: explode + reroll
|
|
2751
|
+
!hasMinimum && !hasReroll || // Simple complex case
|
|
2752
|
+
hasReroll && !hasMinimum);
|
|
2753
|
+
mainExpression = shouldAddParentheses ? `${effectiveCount}(${baseDie})` : `${effectiveCount}${baseDie}`;
|
|
2754
|
+
} else if (effectiveCount === 1) {
|
|
2755
|
+
if (config.isSubtraction) {
|
|
2756
|
+
mainExpression = `1${baseDie}`;
|
|
2757
|
+
} else if (isComplex || isHalflingShorthand || isD20Shorthand || config.count < 0) {
|
|
2758
|
+
mainExpression = baseDie;
|
|
2759
|
+
} else {
|
|
2760
|
+
mainExpression = `1${baseDie}`;
|
|
2761
|
+
}
|
|
2762
|
+
} else {
|
|
2763
|
+
mainExpression = baseDie;
|
|
2764
|
+
}
|
|
2765
|
+
}
|
|
2766
|
+
if (config.bestOf && config.count && config.bestOf < config.count) {
|
|
2767
|
+
mainExpression += `kh${config.bestOf}`;
|
|
2768
|
+
}
|
|
2769
|
+
break;
|
|
2770
|
+
}
|
|
2771
|
+
return mainExpression;
|
|
2772
|
+
}
|
|
2773
|
+
getRootDieConfig() {
|
|
2774
|
+
const configs = this.subRollConfigs;
|
|
2775
|
+
return configs.find((config) => config.sides > 0) || configs[0];
|
|
2776
|
+
}
|
|
2777
|
+
getAllDieConfigs() {
|
|
2778
|
+
return this.getSubRollConfigs();
|
|
2779
|
+
}
|
|
2780
|
+
getBonusDiceConfigs() {
|
|
2781
|
+
const allConfigs = this.subRollConfigs;
|
|
2782
|
+
const rootConfig = allConfigs.find((config) => config.sides > 0) || allConfigs[0];
|
|
2783
|
+
if (!rootConfig) return [];
|
|
2784
|
+
return allConfigs.filter((config) => config.sides > 0).filter((config) => config !== rootConfig);
|
|
2785
|
+
}
|
|
2786
|
+
getBonusDicePMFs(check, eps = 0) {
|
|
2787
|
+
return check.getBonusDiceConfigs().map(
|
|
2788
|
+
(config) => pmfFromRollBuilder(_RollBuilder.fromConfigs([config]), eps)
|
|
2789
|
+
);
|
|
2790
|
+
}
|
|
2791
|
+
get modifier() {
|
|
2792
|
+
return this.subRollConfigs.reduce(
|
|
2793
|
+
(sum, config) => sum + config.modifier,
|
|
2794
|
+
0
|
|
2795
|
+
);
|
|
2796
|
+
}
|
|
2797
|
+
get rollType() {
|
|
2798
|
+
const rootConfig = this.getRootDieConfig();
|
|
2799
|
+
return rootConfig?.rollType || "flat";
|
|
2800
|
+
}
|
|
2801
|
+
get baseReroll() {
|
|
2802
|
+
const rootConfig = this.getRootDieConfig();
|
|
2803
|
+
return rootConfig?.reroll || 0;
|
|
2804
|
+
}
|
|
2805
|
+
};
|
|
2806
|
+
|
|
2807
|
+
// src/builder/factory.ts
|
|
2808
|
+
var rollFn = (count, sidesOrDie, modifier) => {
|
|
2809
|
+
if (sidesOrDie instanceof RollBuilder) {
|
|
2810
|
+
const subRollConfigs = sidesOrDie.getSubRollConfigs();
|
|
2811
|
+
if (subRollConfigs.length === 0) return new RollBuilder(0).plus(modifier);
|
|
2812
|
+
const absCount = Math.abs(count);
|
|
2813
|
+
const newConfigs = subRollConfigs.map((config) => ({
|
|
2814
|
+
...config,
|
|
2815
|
+
count: config.count * absCount,
|
|
2816
|
+
modifier: config.modifier * absCount
|
|
2817
|
+
}));
|
|
2818
|
+
let resultBuilder = new RollBuilder(newConfigs);
|
|
2819
|
+
if (count < 0) {
|
|
2820
|
+
const negatedConfigs = resultBuilder.getSubRollConfigs().map((c) => ({ ...c, isSubtraction: !c.isSubtraction }));
|
|
2821
|
+
resultBuilder = new RollBuilder(negatedConfigs);
|
|
2822
|
+
}
|
|
2823
|
+
return resultBuilder.plus(modifier);
|
|
2824
|
+
} else {
|
|
2825
|
+
let builder = new RollBuilder(count);
|
|
2826
|
+
if (sidesOrDie && sidesOrDie > 0) {
|
|
2827
|
+
builder = builder.d(sidesOrDie);
|
|
2828
|
+
}
|
|
2829
|
+
return builder.plus(modifier);
|
|
2830
|
+
}
|
|
2831
|
+
};
|
|
2832
|
+
rollFn.d = (sides) => new RollBuilder(1).d(sides);
|
|
2833
|
+
rollFn.hd20 = () => new RollBuilder(1).d20().reroll(1);
|
|
2834
|
+
rollFn.d4 = () => new RollBuilder(1).d4();
|
|
2835
|
+
rollFn.d6 = () => new RollBuilder(1).d6();
|
|
2836
|
+
rollFn.d8 = () => new RollBuilder(1).d8();
|
|
2837
|
+
rollFn.d10 = () => new RollBuilder(1).d10();
|
|
2838
|
+
rollFn.d12 = () => new RollBuilder(1).d12();
|
|
2839
|
+
rollFn.d20 = () => new RollBuilder(1).d20();
|
|
2840
|
+
rollFn.d100 = () => new RollBuilder(1).d100();
|
|
2841
|
+
rollFn.flat = (n) => new RollBuilder(0).plus(n);
|
|
2842
|
+
function d(sides) {
|
|
2843
|
+
return new RollBuilder(1).d(sides);
|
|
2844
|
+
}
|
|
2845
|
+
var d4 = new RollBuilder(1).d4();
|
|
2846
|
+
var d6 = new RollBuilder(1).d6();
|
|
2847
|
+
var d8 = new RollBuilder(1).d8();
|
|
2848
|
+
var d10 = new RollBuilder(1).d10();
|
|
2849
|
+
var d12 = new RollBuilder(1).d12();
|
|
2850
|
+
var d20 = new RollBuilder(1).d20();
|
|
2851
|
+
var hd20 = new RollBuilder(1).d20().reroll(1);
|
|
2852
|
+
var d100 = new RollBuilder(1).d100();
|
|
2853
|
+
var flat = (n) => new RollBuilder(0).plus(n);
|
|
2854
|
+
var roll = rollFn;
|
|
2855
|
+
var builderPMFCache = new LRUCache(1e3);
|
|
2856
|
+
|
|
2857
|
+
// src/builder/ast.ts
|
|
2858
|
+
var defaultEps = 0;
|
|
2859
|
+
var singleDiePMFCache = new LRUCache(1e3);
|
|
2860
|
+
function astFromRollConfigs(configs) {
|
|
2861
|
+
if (!configs || configs.length === 0) return void 0;
|
|
2862
|
+
const children = [];
|
|
2863
|
+
let constantSum = 0;
|
|
2864
|
+
for (const cfg of configs) {
|
|
2865
|
+
const sign = cfg.isSubtraction || cfg.count < 0 ? -1 : 1;
|
|
2866
|
+
const count = Math.abs(cfg.count || 0);
|
|
2867
|
+
constantSum += cfg.modifier || 0;
|
|
2868
|
+
if ((cfg.sides || 0) <= 0) continue;
|
|
2869
|
+
const die = {
|
|
2870
|
+
type: "die",
|
|
2871
|
+
sides: cfg.sides,
|
|
2872
|
+
reroll: cfg.reroll > 0 ? cfg.reroll : void 0,
|
|
2873
|
+
minimum: cfg.minimum > 0 ? cfg.minimum : void 0,
|
|
2874
|
+
explode: cfg.explode && Number.isFinite(cfg.explode) && cfg.explode > 0 ? cfg.explode : void 0
|
|
2875
|
+
};
|
|
2876
|
+
let node = die;
|
|
2877
|
+
let appliedRollType = false;
|
|
2878
|
+
if (cfg.rollType && cfg.rollType !== "flat") {
|
|
2879
|
+
if (cfg.sides === 20) {
|
|
2880
|
+
node = {
|
|
2881
|
+
type: "d20Roll",
|
|
2882
|
+
rollType: cfg.rollType,
|
|
2883
|
+
child: node
|
|
2884
|
+
};
|
|
2885
|
+
} else {
|
|
2886
|
+
const n = cfg.rollType === "elven accuracy" ? 3 : 2;
|
|
2887
|
+
const mode = cfg.rollType === "disadvantage" ? "lowest" : "highest";
|
|
2888
|
+
const base = { type: "sum", count: n, child: node };
|
|
2889
|
+
node = { type: "keep", mode, count: 1, child: base };
|
|
2890
|
+
}
|
|
2891
|
+
appliedRollType = true;
|
|
2892
|
+
}
|
|
2893
|
+
if (cfg.rollType === "flat" && cfg.keep && cfg.keep.total > 0) {
|
|
2894
|
+
const base = { type: "sum", count: cfg.keep.total, child: node };
|
|
2895
|
+
node = {
|
|
2896
|
+
type: "keep",
|
|
2897
|
+
mode: cfg.keep.mode,
|
|
2898
|
+
count: cfg.keep.count,
|
|
2899
|
+
child: base
|
|
2900
|
+
};
|
|
2901
|
+
} else {
|
|
2902
|
+
const c = appliedRollType ? 1 : Math.max(1, count || 1);
|
|
2903
|
+
node = { type: "sum", count: c, child: node };
|
|
2904
|
+
}
|
|
2905
|
+
children.push({ node, sign });
|
|
2906
|
+
}
|
|
2907
|
+
if (children.length === 0) {
|
|
2908
|
+
return { type: "constant", value: constantSum };
|
|
2909
|
+
}
|
|
2910
|
+
const add = { type: "add", children };
|
|
2911
|
+
if (constantSum !== 0)
|
|
2912
|
+
add.children.push({
|
|
2913
|
+
node: { type: "constant", value: constantSum },
|
|
2914
|
+
sign: 1
|
|
2915
|
+
});
|
|
2916
|
+
return add;
|
|
2917
|
+
}
|
|
2918
|
+
function resolve(node, eps = defaultEps) {
|
|
2919
|
+
const signature = getASTSignature(node);
|
|
2920
|
+
const cacheKey = `${signature}_${eps}`;
|
|
2921
|
+
const cached = builderPMFCache.get(cacheKey);
|
|
2922
|
+
if (cached) return cached;
|
|
2923
|
+
const result = (() => {
|
|
2924
|
+
switch (node.type) {
|
|
2925
|
+
case "constant":
|
|
2926
|
+
return PMF.delta(node.value, eps);
|
|
2927
|
+
case "die": {
|
|
2928
|
+
return resolveSingleDie(node, eps);
|
|
2929
|
+
}
|
|
2930
|
+
case "sum": {
|
|
2931
|
+
const base = resolve(node.child, eps);
|
|
2932
|
+
const n = Math.max(0, Math.floor(node.count));
|
|
2933
|
+
if (n === 0) return PMF.delta(0, eps);
|
|
2934
|
+
if (n === 1) return base;
|
|
2935
|
+
return base.power(n, eps);
|
|
2936
|
+
}
|
|
2937
|
+
case "add": {
|
|
2938
|
+
let shift = 0;
|
|
2939
|
+
const parts = [];
|
|
2940
|
+
for (const c of node.children) {
|
|
2941
|
+
if (c.node.type === "constant") {
|
|
2942
|
+
shift += c.sign * c.node.value;
|
|
2943
|
+
} else {
|
|
2944
|
+
const p = resolve(c.node, eps);
|
|
2945
|
+
parts.push(c.sign === 1 ? p : p.mapDamage((v) => -v));
|
|
2946
|
+
}
|
|
2947
|
+
}
|
|
2948
|
+
if (parts.length === 0) return PMF.delta(shift, eps);
|
|
2949
|
+
let res = parts.length === 1 ? parts[0] : PMF.convolveMany(parts, eps);
|
|
2950
|
+
if (shift !== 0) res = res.mapDamage((v) => v + shift);
|
|
2951
|
+
return res;
|
|
2952
|
+
}
|
|
2953
|
+
case "keep": {
|
|
2954
|
+
const total = getTotalCount(node);
|
|
2955
|
+
const die = findDie(node.child.child);
|
|
2956
|
+
const perDie = die ? resolveSingleDie(die, eps) : PMF.delta(0, eps);
|
|
2957
|
+
const keepCount = Math.max(0, Math.min(node.count, total));
|
|
2958
|
+
if (keepCount === 0 || total === 0) return PMF.delta(0, eps);
|
|
2959
|
+
return keepSumPMF(
|
|
2960
|
+
perDie,
|
|
2961
|
+
total,
|
|
2962
|
+
keepCount,
|
|
2963
|
+
node.mode === "highest",
|
|
2964
|
+
eps
|
|
2965
|
+
);
|
|
2966
|
+
}
|
|
2967
|
+
case "d20Roll": {
|
|
2968
|
+
const childDie = findDie(node.child);
|
|
2969
|
+
const rerollOne = !!childDie && (childDie.reroll || 0) >= 1;
|
|
2970
|
+
return d20RollPMF(node.rollType, rerollOne);
|
|
2971
|
+
}
|
|
2972
|
+
}
|
|
2973
|
+
})();
|
|
2974
|
+
builderPMFCache.set(cacheKey, result);
|
|
2975
|
+
return result;
|
|
2976
|
+
}
|
|
2977
|
+
function pmfFromRollBuilder(rb, eps = defaultEps) {
|
|
2978
|
+
const configs = rb.getSubRollConfigs?.() ?? [];
|
|
2979
|
+
const ast = astFromRollConfigs(configs) || { type: "constant", value: 0 };
|
|
2980
|
+
return resolve(ast, eps);
|
|
2981
|
+
}
|
|
2982
|
+
function resolveSingleDie(die, eps = defaultEps) {
|
|
2983
|
+
const signature = getASTSignature(die);
|
|
2984
|
+
const cacheKey = `${signature}_${eps}`;
|
|
2985
|
+
const cached = singleDiePMFCache.get(cacheKey);
|
|
2986
|
+
if (cached) return cached;
|
|
2987
|
+
const s = Math.max(0, Math.floor(die.sides));
|
|
2988
|
+
if (s <= 0) return PMF.delta(0, eps);
|
|
2989
|
+
let probs = /* @__PURE__ */ new Map();
|
|
2990
|
+
for (let v = 1; v <= s; v++) probs.set(v, 1 / s);
|
|
2991
|
+
const r = Math.max(0, Math.floor(die.reroll || 0));
|
|
2992
|
+
if (r > 0) {
|
|
2993
|
+
const k = Math.min(r, s);
|
|
2994
|
+
const rerollMass = k / s;
|
|
2995
|
+
const uniformReroll = rerollMass / s;
|
|
2996
|
+
const next = /* @__PURE__ */ new Map();
|
|
2997
|
+
for (let v = 1; v <= s; v++) {
|
|
2998
|
+
const keep = v <= k ? 0 : 1 / s;
|
|
2999
|
+
next.set(v, keep + uniformReroll);
|
|
3000
|
+
}
|
|
3001
|
+
probs = next;
|
|
3002
|
+
}
|
|
3003
|
+
let pmf = PMF.fromMap(new Map(probs), eps);
|
|
3004
|
+
const minV = Math.max(0, Math.floor(die.minimum || 0));
|
|
3005
|
+
if (minV > 0) pmf = pmf.mapDamage((v) => Math.max(v, minV));
|
|
3006
|
+
const explode = die.explode;
|
|
3007
|
+
if (explode && Number.isFinite(explode) && explode > 0) {
|
|
3008
|
+
const times = Math.floor(explode);
|
|
3009
|
+
const maxFace = s;
|
|
3010
|
+
const nonMax = /* @__PURE__ */ new Map();
|
|
3011
|
+
const pMax = pmf.pAt(maxFace);
|
|
3012
|
+
for (const v of pmf.support()) {
|
|
3013
|
+
if (v !== maxFace) nonMax.set(v, pmf.pAt(v));
|
|
3014
|
+
}
|
|
3015
|
+
let nonMaxPMF = PMF.fromMap(nonMax, eps);
|
|
3016
|
+
if (Math.abs(nonMaxPMF.mass() - (1 - pMax)) > eps) {
|
|
3017
|
+
nonMaxPMF = nonMaxPMF.scaleMass(1 - pMax);
|
|
3018
|
+
}
|
|
3019
|
+
let tail = PMF.delta(0, eps);
|
|
3020
|
+
const addOnce = pmf;
|
|
3021
|
+
for (let t = 1; t <= times; t++) {
|
|
3022
|
+
tail = tail.convolve(addOnce, eps);
|
|
3023
|
+
}
|
|
3024
|
+
const exploded = PMF.branch(
|
|
3025
|
+
tail.mapDamage((v) => v + maxFace),
|
|
3026
|
+
nonMaxPMF,
|
|
3027
|
+
pMax
|
|
3028
|
+
);
|
|
3029
|
+
pmf = exploded;
|
|
2957
3030
|
}
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
3031
|
+
singleDiePMFCache.set(cacheKey, pmf);
|
|
3032
|
+
return pmf;
|
|
3033
|
+
}
|
|
3034
|
+
function findDie(node) {
|
|
3035
|
+
switch (node.type) {
|
|
3036
|
+
case "die":
|
|
3037
|
+
return node;
|
|
3038
|
+
case "constant":
|
|
3039
|
+
return void 0;
|
|
3040
|
+
case "sum":
|
|
3041
|
+
case "d20Roll":
|
|
3042
|
+
return findDie(node.child);
|
|
3043
|
+
case "keep":
|
|
3044
|
+
return findDie(node.child.child);
|
|
3045
|
+
case "add":
|
|
3046
|
+
for (const c of node.children) {
|
|
3047
|
+
const d2 = findDie(c.node);
|
|
3048
|
+
if (d2) return d2;
|
|
2966
3049
|
}
|
|
3050
|
+
return void 0;
|
|
3051
|
+
}
|
|
3052
|
+
}
|
|
3053
|
+
function getTotalCount(node) {
|
|
3054
|
+
let cur = node.child;
|
|
3055
|
+
while (cur.type === "keep") cur = cur.child;
|
|
3056
|
+
return cur.type === "sum" ? Math.max(0, Math.floor(cur.count)) : 0;
|
|
3057
|
+
}
|
|
3058
|
+
function keepSumPMF(single, total, keep, highest, eps = defaultEps) {
|
|
3059
|
+
if (keep >= total) return single.power(total, eps);
|
|
3060
|
+
if (keep <= 0) return PMF.delta(0, eps);
|
|
3061
|
+
const sortedSupport = [...single.support()].sort((a, b) => a - b);
|
|
3062
|
+
const pmfSig = sortedSupport.map((val) => `${val}:${single.pAt(val).toPrecision(6)}`).join(",");
|
|
3063
|
+
const cacheKey = `keep|${pmfSig}|t:${total}|k:${keep}|h:${highest ? 1 : 0}|e:${eps}`;
|
|
3064
|
+
const cached = builderPMFCache.get(cacheKey);
|
|
3065
|
+
if (cached) return cached;
|
|
3066
|
+
const support = single.support();
|
|
3067
|
+
const probs = support.map((v) => ({ v, p: single.pAt(v) }));
|
|
3068
|
+
const out = /* @__PURE__ */ new Map();
|
|
3069
|
+
function dfs(i, accValues, weight) {
|
|
3070
|
+
if (i === total) {
|
|
3071
|
+
const sorted = [...accValues].sort((a, b) => highest ? b - a : a - b);
|
|
3072
|
+
let sum = 0;
|
|
3073
|
+
for (let j = 0; j < keep; j++) sum += sorted[j];
|
|
3074
|
+
out.set(sum, (out.get(sum) || 0) + weight);
|
|
3075
|
+
return;
|
|
2967
3076
|
}
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
for (let i = 1; i <= config.reroll; i++) {
|
|
2972
|
-
baseDie += ` reroll ${i}`;
|
|
2973
|
-
}
|
|
2974
|
-
}
|
|
3077
|
+
for (const { v, p } of probs) {
|
|
3078
|
+
if (p <= 0) continue;
|
|
3079
|
+
dfs(i + 1, [...accValues, v], weight * p);
|
|
2975
3080
|
}
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
2990
|
-
|
|
2991
|
-
|
|
3081
|
+
}
|
|
3082
|
+
dfs(0, [], 1);
|
|
3083
|
+
const result = PMF.fromMap(out, eps);
|
|
3084
|
+
builderPMFCache.set(cacheKey, result);
|
|
3085
|
+
return result;
|
|
3086
|
+
}
|
|
3087
|
+
function getASTSignature(node) {
|
|
3088
|
+
switch (node.type) {
|
|
3089
|
+
case "constant":
|
|
3090
|
+
return `c:${node.value}`;
|
|
3091
|
+
case "die": {
|
|
3092
|
+
const parts = [];
|
|
3093
|
+
parts.push(`s:${node.sides}`);
|
|
3094
|
+
if (node.reroll) parts.push(`r:${node.reroll}`);
|
|
3095
|
+
if (node.minimum) parts.push(`m:${node.minimum}`);
|
|
3096
|
+
if (node.explode) parts.push(`e:${node.explode}`);
|
|
3097
|
+
return `d{${parts.join(",")}}`;
|
|
3098
|
+
}
|
|
3099
|
+
case "sum":
|
|
3100
|
+
return `sum{c:${node.count},ch:${getASTSignature(node.child)}}`;
|
|
3101
|
+
case "d20Roll":
|
|
3102
|
+
return `d20{t:${node.rollType},ch:${getASTSignature(node.child)}}`;
|
|
3103
|
+
case "keep":
|
|
3104
|
+
return `keep{c:${node.count},m:${node.mode},ch:${getASTSignature(
|
|
3105
|
+
node.child
|
|
3106
|
+
)}}`;
|
|
3107
|
+
case "add": {
|
|
3108
|
+
let constantValue = 0;
|
|
3109
|
+
const otherChildrenSigs = [];
|
|
3110
|
+
for (const c of node.children) {
|
|
3111
|
+
if (c.node.type === "constant") {
|
|
3112
|
+
constantValue += c.sign * c.node.value;
|
|
2992
3113
|
} else {
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
const hasMinimum = baseDie.startsWith("(") && baseDie.includes(">");
|
|
2997
|
-
const hasReroll = baseDie.includes("reroll");
|
|
2998
|
-
const effectiveCount = config.isSubtraction ? Math.abs(config.count) : config.count < 0 ? 1 : Math.abs(config.count);
|
|
2999
|
-
if (effectiveCount > 1) {
|
|
3000
|
-
const shouldAddParentheses = isComplex && (// Complex single roll: explode + reroll
|
|
3001
|
-
!hasMinimum && !hasReroll || // Simple complex case
|
|
3002
|
-
hasReroll && !hasMinimum);
|
|
3003
|
-
mainExpression = shouldAddParentheses ? `${effectiveCount}(${baseDie})` : `${effectiveCount}${baseDie}`;
|
|
3004
|
-
} else if (effectiveCount === 1) {
|
|
3005
|
-
if (config.isSubtraction) {
|
|
3006
|
-
mainExpression = `1${baseDie}`;
|
|
3007
|
-
} else if (isComplex || isHalflingShorthand || isD20Shorthand || config.count < 0) {
|
|
3008
|
-
mainExpression = baseDie;
|
|
3009
|
-
} else {
|
|
3010
|
-
mainExpression = `1${baseDie}`;
|
|
3011
|
-
}
|
|
3012
|
-
} else {
|
|
3013
|
-
mainExpression = baseDie;
|
|
3014
|
-
}
|
|
3015
|
-
}
|
|
3016
|
-
if (config.bestOf && config.count && config.bestOf < config.count) {
|
|
3017
|
-
mainExpression += `kh${config.bestOf}`;
|
|
3114
|
+
otherChildrenSigs.push(
|
|
3115
|
+
`${c.sign === -1 ? "-" : "+"}${getASTSignature(c.node)}`
|
|
3116
|
+
);
|
|
3018
3117
|
}
|
|
3019
|
-
|
|
3118
|
+
}
|
|
3119
|
+
if (constantValue !== 0) {
|
|
3120
|
+
otherChildrenSigs.push(
|
|
3121
|
+
constantValue > 0 ? `+c:${constantValue}` : `-c:${-constantValue}`
|
|
3122
|
+
);
|
|
3123
|
+
}
|
|
3124
|
+
otherChildrenSigs.sort();
|
|
3125
|
+
return `add[${otherChildrenSigs.join("")}]`;
|
|
3020
3126
|
}
|
|
3021
|
-
return mainExpression;
|
|
3022
|
-
}
|
|
3023
|
-
getRootDieConfig() {
|
|
3024
|
-
const configs = this.subRollConfigs;
|
|
3025
|
-
return configs.find((config) => config.sides > 0) || configs[0];
|
|
3026
|
-
}
|
|
3027
|
-
getAllDieConfigs() {
|
|
3028
|
-
return this.getSubRollConfigs();
|
|
3029
|
-
}
|
|
3030
|
-
getBonusDiceConfigs() {
|
|
3031
|
-
const allConfigs = this.subRollConfigs;
|
|
3032
|
-
const rootConfig = allConfigs.find((config) => config.sides > 0) || allConfigs[0];
|
|
3033
|
-
if (!rootConfig) return [];
|
|
3034
|
-
return allConfigs.filter((config) => config.sides > 0).filter((config) => config !== rootConfig);
|
|
3035
|
-
}
|
|
3036
|
-
getBonusDicePMFs(check, eps = 0) {
|
|
3037
|
-
return check.getBonusDiceConfigs().map(
|
|
3038
|
-
(config) => pmfFromRollBuilder(_RollBuilder.fromConfigs([config]), eps)
|
|
3039
|
-
);
|
|
3040
|
-
}
|
|
3041
|
-
get modifier() {
|
|
3042
|
-
return this.subRollConfigs.reduce(
|
|
3043
|
-
(sum, config) => sum + config.modifier,
|
|
3044
|
-
0
|
|
3045
|
-
);
|
|
3046
|
-
}
|
|
3047
|
-
get rollType() {
|
|
3048
|
-
const rootConfig = this.getRootDieConfig();
|
|
3049
|
-
return rootConfig?.rollType || "flat";
|
|
3050
|
-
}
|
|
3051
|
-
get baseReroll() {
|
|
3052
|
-
const rootConfig = this.getRootDieConfig();
|
|
3053
|
-
return rootConfig?.reroll || 0;
|
|
3054
3127
|
}
|
|
3055
|
-
}
|
|
3128
|
+
}
|
|
3056
3129
|
|
|
3057
3130
|
// src/builder/attack.ts
|
|
3058
3131
|
var AttackBuilder = class _AttackBuilder {
|
|
@@ -3089,9 +3162,8 @@ var AttackBuilder = class _AttackBuilder {
|
|
|
3089
3162
|
let effectPart = "";
|
|
3090
3163
|
if (this.hitEffect) {
|
|
3091
3164
|
effectPart = `(${this.hitEffect.toExpression()})`;
|
|
3092
|
-
let crit = void 0;
|
|
3093
3165
|
if (this.critEffect !== null) {
|
|
3094
|
-
crit = this.critEffect ?? this.hitEffect.copy().doubleDice();
|
|
3166
|
+
const crit = this.critEffect ?? this.hitEffect.copy().doubleDice();
|
|
3095
3167
|
const critThreshold = this.check.critThreshold;
|
|
3096
3168
|
if (critThreshold < 15 || critThreshold > 20) {
|
|
3097
3169
|
throw new Error(
|
|
@@ -3179,6 +3251,9 @@ var AttackBuilder = class _AttackBuilder {
|
|
|
3179
3251
|
toPMF(eps = 0) {
|
|
3180
3252
|
return this.resolve(eps).pmf;
|
|
3181
3253
|
}
|
|
3254
|
+
get pmf() {
|
|
3255
|
+
return this.toPMF();
|
|
3256
|
+
}
|
|
3182
3257
|
// By default, create query on PMF with no pruning
|
|
3183
3258
|
toQuery(eps = 0) {
|
|
3184
3259
|
return this.toPMF(eps).query();
|
|
@@ -3252,6 +3327,10 @@ var ACBuilder = class _ACBuilder extends RollBuilder {
|
|
|
3252
3327
|
return new _ACBuilder(baseCopy, newConfig.ac, newConfig);
|
|
3253
3328
|
}
|
|
3254
3329
|
};
|
|
3330
|
+
RollBuilder.prototype.ac = function(targetAC) {
|
|
3331
|
+
if (isNaN(targetAC)) throw new Error("Invalid NaN value for targetAC");
|
|
3332
|
+
return new ACBuilder(this, targetAC);
|
|
3333
|
+
};
|
|
3255
3334
|
|
|
3256
3335
|
// src/builder/save.ts
|
|
3257
3336
|
var SaveBuilder = class _SaveBuilder {
|
|
@@ -3294,6 +3373,9 @@ var SaveBuilder = class _SaveBuilder {
|
|
|
3294
3373
|
toPMF(eps = 0) {
|
|
3295
3374
|
return this.resolve(eps).pmf;
|
|
3296
3375
|
}
|
|
3376
|
+
get pmf() {
|
|
3377
|
+
return this.toPMF();
|
|
3378
|
+
}
|
|
3297
3379
|
// By default, create query on PMF with no pruning
|
|
3298
3380
|
toQuery(eps = 0) {
|
|
3299
3381
|
return this.toPMF(eps).query();
|
|
@@ -3391,56 +3473,10 @@ var DCBuilder = class _DCBuilder extends RollBuilder {
|
|
|
3391
3473
|
return PMF.fromMap(m, eps);
|
|
3392
3474
|
}
|
|
3393
3475
|
};
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
if (sidesOrDie instanceof RollBuilder) {
|
|
3398
|
-
const subRollConfigs = sidesOrDie.getSubRollConfigs();
|
|
3399
|
-
if (subRollConfigs.length === 0) return new RollBuilder(0).plus(modifier);
|
|
3400
|
-
const absCount = Math.abs(count);
|
|
3401
|
-
const newConfigs = subRollConfigs.map((config) => ({
|
|
3402
|
-
...config,
|
|
3403
|
-
count: config.count * absCount,
|
|
3404
|
-
modifier: config.modifier * absCount
|
|
3405
|
-
}));
|
|
3406
|
-
let resultBuilder = new RollBuilder(newConfigs);
|
|
3407
|
-
if (count < 0) {
|
|
3408
|
-
const negatedConfigs = resultBuilder.getSubRollConfigs().map((c) => ({ ...c, isSubtraction: !c.isSubtraction }));
|
|
3409
|
-
resultBuilder = new RollBuilder(negatedConfigs);
|
|
3410
|
-
}
|
|
3411
|
-
return resultBuilder.plus(modifier);
|
|
3412
|
-
} else {
|
|
3413
|
-
let builder = new RollBuilder(count);
|
|
3414
|
-
if (sidesOrDie && sidesOrDie > 0) {
|
|
3415
|
-
builder = builder.d(sidesOrDie);
|
|
3416
|
-
}
|
|
3417
|
-
return builder.plus(modifier);
|
|
3418
|
-
}
|
|
3476
|
+
RollBuilder.prototype.dc = function(saveDC) {
|
|
3477
|
+
if (isNaN(saveDC)) throw new Error("Invalid NaN value for saveDC");
|
|
3478
|
+
return new DCBuilder(this).dc(saveDC);
|
|
3419
3479
|
};
|
|
3420
|
-
rollFn.d = (sides) => new RollBuilder(1).d(sides);
|
|
3421
|
-
rollFn.hd20 = () => new RollBuilder(1).d20().reroll(1);
|
|
3422
|
-
rollFn.d4 = () => new RollBuilder(1).d4();
|
|
3423
|
-
rollFn.d6 = () => new RollBuilder(1).d6();
|
|
3424
|
-
rollFn.d8 = () => new RollBuilder(1).d8();
|
|
3425
|
-
rollFn.d10 = () => new RollBuilder(1).d10();
|
|
3426
|
-
rollFn.d12 = () => new RollBuilder(1).d12();
|
|
3427
|
-
rollFn.d20 = () => new RollBuilder(1).d20();
|
|
3428
|
-
rollFn.d100 = () => new RollBuilder(1).d100();
|
|
3429
|
-
rollFn.flat = (n) => new RollBuilder(0).plus(n);
|
|
3430
|
-
function d(sides) {
|
|
3431
|
-
return new RollBuilder(1).d(sides);
|
|
3432
|
-
}
|
|
3433
|
-
var d4 = new RollBuilder(1).d4();
|
|
3434
|
-
var d6 = new RollBuilder(1).d6();
|
|
3435
|
-
var d8 = new RollBuilder(1).d8();
|
|
3436
|
-
var d10 = new RollBuilder(1).d10();
|
|
3437
|
-
var d12 = new RollBuilder(1).d12();
|
|
3438
|
-
var d20 = new RollBuilder(1).d20();
|
|
3439
|
-
var hd20 = new RollBuilder(1).d20().reroll(1);
|
|
3440
|
-
var d100 = new RollBuilder(1).d100();
|
|
3441
|
-
var flat = (n) => new RollBuilder(0).plus(n);
|
|
3442
|
-
var roll = rollFn;
|
|
3443
|
-
var builderPMFCache = new LRUCache(1e3);
|
|
3444
3480
|
|
|
3445
3481
|
export { ACBuilder, DCBuilder, RollBuilder, SaveBuilder, builderPMFCache, d, d10, d100, d12, d20, d4, d6, d8, defaultConfig, flat, hd20, roll };
|
|
3446
3482
|
//# sourceMappingURL=index.js.map
|