@yipe/dice 0.9.0 → 0.10.0
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/dist/builder/ac.d.ts.map +1 -1
- package/dist/builder/ast.d.ts +32 -2
- package/dist/builder/ast.d.ts.map +1 -1
- package/dist/builder/attack.d.ts.map +1 -1
- package/dist/builder/dc.d.ts.map +1 -1
- package/dist/builder/example.d.ts +4 -4
- package/dist/builder/example.d.ts.map +1 -1
- package/dist/builder/index.cjs +221 -84
- package/dist/builder/index.cjs.map +1 -1
- package/dist/builder/index.js +221 -84
- package/dist/builder/index.js.map +1 -1
- package/dist/builder/roll.d.ts +5 -1
- package/dist/builder/roll.d.ts.map +1 -1
- package/dist/builder/save.d.ts.map +1 -1
- package/dist/index.cjs +64 -15
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +64 -15
- package/dist/index.js.map +1 -1
- package/dist/parser/dice.d.ts +14 -0
- package/dist/parser/dice.d.ts.map +1 -1
- package/dist/pmf/pmf.d.ts +10 -4
- package/dist/pmf/pmf.d.ts.map +1 -1
- package/package.json +2 -2
- package/CHANGELOG.md +0 -456
package/dist/index.js
CHANGED
|
@@ -1566,7 +1566,7 @@ var _PMF = class _PMF {
|
|
|
1566
1566
|
const id = this.identifier;
|
|
1567
1567
|
let key = `${id}`;
|
|
1568
1568
|
for (let i = 1; i < n; i++) key += `+${id}`;
|
|
1569
|
-
return `${key}@${eps}`;
|
|
1569
|
+
return `${key}@${eps}|${this.fingerprint()}`;
|
|
1570
1570
|
}
|
|
1571
1571
|
/**
|
|
1572
1572
|
* Efficiently computes this PMF convolved with itself `n` times.
|
|
@@ -1917,16 +1917,27 @@ var _PMF = class _PMF {
|
|
|
1917
1917
|
return `v4:${raw ? "RAW" : "N"}:${id1}+${id2}@${eps}|${p1.fingerprint()}|${p2.fingerprint()}`;
|
|
1918
1918
|
}
|
|
1919
1919
|
/**
|
|
1920
|
-
* A
|
|
1921
|
-
* cache keys change
|
|
1922
|
-
*
|
|
1923
|
-
*
|
|
1920
|
+
* A content fingerprint of every bin (probability, per-label `count`, per-label `attr`) plus
|
|
1921
|
+
* the `normalized` flag, so convolution/power cache keys change whenever the underlying
|
|
1922
|
+
* numbers do. Mass/bin-count/face-sum alone are not content-unique: `mapDamage` variants can
|
|
1923
|
+
* keep the same identifier, support, mass, and face sum while differing in per-bin
|
|
1924
|
+
* probabilities or in the `count`/`attr` channels `convolve()`/`power()` actually propagate --
|
|
1925
|
+
* that previously let `power()` return one PMF's cached result for a different PMF. Memoized
|
|
1926
|
+
* because a PMF is immutable once constructed -- this avoids re-deriving the key on every
|
|
1927
|
+
* convolve()/power() call (including cache hits). Bin order is sorted by damage value (and
|
|
1928
|
+
* label keys sorted within each bin) so two equal-content PMFs built via different code paths
|
|
1929
|
+
* fingerprint identically regardless of Map insertion order.
|
|
1924
1930
|
*/
|
|
1925
1931
|
fingerprint() {
|
|
1926
1932
|
if (this._fingerprint === void 0) {
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1933
|
+
const bins = [...this.map.entries()].sort((a, b) => a[0] - b[0]);
|
|
1934
|
+
const parts = [];
|
|
1935
|
+
for (const [damageValue, bin] of bins) {
|
|
1936
|
+
const countStr = Object.keys(bin.count).sort().map((k) => `${k}:${bin.count[k]}`).join(",");
|
|
1937
|
+
const attrStr = bin.attr ? Object.keys(bin.attr).sort().map((k) => `${k}:${bin.attr[k]}`).join(",") : "";
|
|
1938
|
+
parts.push(`${damageValue}:${bin.p}[${countStr}]{${attrStr}}`);
|
|
1939
|
+
}
|
|
1940
|
+
this._fingerprint = `${this.normalized ? 1 : 0}|${parts.join(";")}`;
|
|
1930
1941
|
}
|
|
1931
1942
|
return this._fingerprint;
|
|
1932
1943
|
}
|
|
@@ -2158,11 +2169,13 @@ var _PMF = class _PMF {
|
|
|
2158
2169
|
/** Quantile / inverse CDF for p in [0,1]. Returns smallest x with CDF ≥ p. */
|
|
2159
2170
|
quantile(p) {
|
|
2160
2171
|
if (this.map.size === 0) return 0;
|
|
2172
|
+
const totalMass = this.mass();
|
|
2173
|
+
if (totalMass <= 0) return 0;
|
|
2161
2174
|
const s = this.support().sort((a, b) => a - b);
|
|
2162
2175
|
let acc = 0;
|
|
2163
2176
|
for (const x of s) {
|
|
2164
2177
|
acc += this.pAt(x);
|
|
2165
|
-
if (acc >= p) return x;
|
|
2178
|
+
if (acc / totalMass >= p) return x;
|
|
2166
2179
|
}
|
|
2167
2180
|
return s[s.length - 1];
|
|
2168
2181
|
}
|
|
@@ -3023,6 +3036,12 @@ function combineDiceWithNormalization(dice, normValue, outcomeType, currentNorm,
|
|
|
3023
3036
|
finalResult = finalResult.combine(dice);
|
|
3024
3037
|
return { newNorm: currentNorm * normValue, updatedResult: finalResult };
|
|
3025
3038
|
}
|
|
3039
|
+
function subtractCounts(a, b) {
|
|
3040
|
+
const result = new Dice();
|
|
3041
|
+
for (const [key, value] of a.getFaceEntries()) result.increment(key, value);
|
|
3042
|
+
for (const [key, value] of b.getFaceEntries()) result.increment(key, -value);
|
|
3043
|
+
return result;
|
|
3044
|
+
}
|
|
3026
3045
|
function parseExpression(arr, n) {
|
|
3027
3046
|
const result = (() => {
|
|
3028
3047
|
const res = parseArgument(arr, n);
|
|
@@ -3030,8 +3049,29 @@ function parseExpression(arr, n) {
|
|
|
3030
3049
|
})();
|
|
3031
3050
|
let op = parseOperation(arr);
|
|
3032
3051
|
let finalResult = result;
|
|
3052
|
+
let baseDieMeta = result.privateData?.checkDie && !result.privateData.checkDie.rerollOne ? result.privateData.checkDie : void 0;
|
|
3053
|
+
let bonusOnly = Dice.scalar(0);
|
|
3033
3054
|
while (op != null) {
|
|
3034
3055
|
const arg = !op.unary ? parseArgument(arr, n) : finalResult;
|
|
3056
|
+
let acAlreadyApplied = false;
|
|
3057
|
+
if (baseDieMeta) {
|
|
3058
|
+
if (op === Dice.prototype.addNonZero) {
|
|
3059
|
+
bonusOnly = bonusOnly.add(arg);
|
|
3060
|
+
} else if (op === Dice.prototype.subtract) {
|
|
3061
|
+
bonusOnly = bonusOnly.subtract(arg);
|
|
3062
|
+
} else if (op === Dice.prototype.ac && typeof arg === "number") {
|
|
3063
|
+
const natMaxSlice = bonusOnly.add(baseDieMeta.sides);
|
|
3064
|
+
const restSlice = subtractCounts(finalResult, natMaxSlice);
|
|
3065
|
+
const gatedNatMaxSlice = natMaxSlice.ac(arg);
|
|
3066
|
+
finalResult = restSlice.ac(arg).combine(gatedNatMaxSlice);
|
|
3067
|
+
finalResult.privateData.checkDie = baseDieMeta;
|
|
3068
|
+
finalResult.privateData.natMaxCritSlice = gatedNatMaxSlice;
|
|
3069
|
+
acAlreadyApplied = true;
|
|
3070
|
+
baseDieMeta = void 0;
|
|
3071
|
+
} else {
|
|
3072
|
+
baseDieMeta = void 0;
|
|
3073
|
+
}
|
|
3074
|
+
}
|
|
3035
3075
|
let crit;
|
|
3036
3076
|
let critNorm = 1;
|
|
3037
3077
|
if (arr[0] === "x" || arr[0] === "c") {
|
|
@@ -3042,11 +3082,17 @@ function parseExpression(arr, n) {
|
|
|
3042
3082
|
assertToken(arr, "i");
|
|
3043
3083
|
assertToken(arr, "t");
|
|
3044
3084
|
const count = isXcrit ? parseNumber(arr, n) : 1;
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3085
|
+
const trackedCritSlice = finalResult.privateData?.natMaxCritSlice;
|
|
3086
|
+
if (count === 1 && trackedCritSlice) {
|
|
3087
|
+
crit = trackedCritSlice;
|
|
3088
|
+
finalResult = subtractCounts(finalResult, trackedCritSlice);
|
|
3089
|
+
} else {
|
|
3090
|
+
crit = new Dice();
|
|
3091
|
+
for (let i = 0; i < count; i++) {
|
|
3092
|
+
const max = finalResult.maxFace();
|
|
3093
|
+
crit.setFace(max, finalResult.get(max));
|
|
3094
|
+
finalResult = finalResult.deleteFace(max);
|
|
3095
|
+
}
|
|
3050
3096
|
}
|
|
3051
3097
|
critNorm = crit.total();
|
|
3052
3098
|
crit = op.call(crit, parseBinaryArgument(arg, arr, n));
|
|
@@ -3097,7 +3143,9 @@ function parseExpression(arr, n) {
|
|
|
3097
3143
|
missNorm = miss && missNorm ? miss.total() / missNorm : 1;
|
|
3098
3144
|
}
|
|
3099
3145
|
let norm = finalResult.total();
|
|
3100
|
-
|
|
3146
|
+
if (!acAlreadyApplied) {
|
|
3147
|
+
finalResult = op.call(finalResult, arg);
|
|
3148
|
+
}
|
|
3101
3149
|
norm = norm ? finalResult.total() / norm : 1;
|
|
3102
3150
|
if (crit) {
|
|
3103
3151
|
const result2 = combineDiceWithNormalization(
|
|
@@ -3288,6 +3336,7 @@ function parseDice(s, n) {
|
|
|
3288
3336
|
if (rerollOne) {
|
|
3289
3337
|
result = result.reroll(1);
|
|
3290
3338
|
}
|
|
3339
|
+
result.privateData.checkDie = { sides, rerollOne };
|
|
3291
3340
|
return result;
|
|
3292
3341
|
}
|
|
3293
3342
|
function peek(arr, expected) {
|