@yipe/dice 0.2.11 → 0.2.13
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/index.cjs +280 -30
- package/dist/builder/index.cjs.map +1 -1
- package/dist/builder/index.d.cts +22 -2
- package/dist/builder/index.d.ts +22 -2
- package/dist/builder/index.js +280 -31
- package/dist/builder/index.js.map +1 -1
- package/package.json +1 -1
package/dist/builder/index.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { P as PMF, i as DiceQuery, L as LRUCache } from '../pmf-D1p2Re-J.cjs';
|
|
2
2
|
|
|
3
|
-
type ExpressionNode = DieNode | ConstantNode | SumNode | AddNode | KeepNode | D20RollNode | HalfNode;
|
|
3
|
+
type ExpressionNode = DieNode | ConstantNode | SumNode | AddNode | KeepNode | D20RollNode | HalfNode | MaxOfNode;
|
|
4
4
|
type DieNode = {
|
|
5
5
|
type: "die";
|
|
6
6
|
sides: number;
|
|
@@ -39,6 +39,11 @@ type HalfNode = {
|
|
|
39
39
|
type: "half";
|
|
40
40
|
child: ExpressionNode;
|
|
41
41
|
};
|
|
42
|
+
type MaxOfNode = {
|
|
43
|
+
type: "maxOf";
|
|
44
|
+
count: number;
|
|
45
|
+
child: ExpressionNode;
|
|
46
|
+
};
|
|
42
47
|
|
|
43
48
|
type RollFactory = {
|
|
44
49
|
(count: number, sides?: number, modifier?: number): RollBuilder;
|
|
@@ -156,6 +161,7 @@ declare class RollBuilder {
|
|
|
156
161
|
get rollType(): RollType;
|
|
157
162
|
get baseReroll(): number;
|
|
158
163
|
half(): HalfRollBuilder;
|
|
164
|
+
maxOf(count: number): MaxOfRollBuilder;
|
|
159
165
|
}
|
|
160
166
|
declare class HalfRollBuilder extends RollBuilder {
|
|
161
167
|
private readonly innerRoll;
|
|
@@ -167,6 +173,20 @@ declare class HalfRollBuilder extends RollBuilder {
|
|
|
167
173
|
toPMF(eps?: number): PMF;
|
|
168
174
|
copy(): HalfRollBuilder;
|
|
169
175
|
}
|
|
176
|
+
declare class MaxOfRollBuilder extends RollBuilder {
|
|
177
|
+
private readonly innerRoll;
|
|
178
|
+
private readonly count;
|
|
179
|
+
private readonly diceCount?;
|
|
180
|
+
private readonly diceSides?;
|
|
181
|
+
constructor(innerRoll: RollBuilder, count: number, diceCount?: number, diceSides?: number);
|
|
182
|
+
get lastConfig(): any;
|
|
183
|
+
getSubRollConfigs(): readonly RollConfig[];
|
|
184
|
+
toExpression(): string;
|
|
185
|
+
private findDieInAST;
|
|
186
|
+
toAST(): ExpressionNode;
|
|
187
|
+
toPMF(eps?: number): PMF;
|
|
188
|
+
copy(): MaxOfRollBuilder;
|
|
189
|
+
}
|
|
170
190
|
|
|
171
191
|
type ActionEffect = RollBuilder;
|
|
172
192
|
declare class AttackBuilder implements CheckBuilder {
|
|
@@ -279,4 +299,4 @@ declare const flat: (n: number) => RollBuilder;
|
|
|
279
299
|
declare const roll: RollFactory;
|
|
280
300
|
declare const builderPMFCache: LRUCache<string, PMF>;
|
|
281
301
|
|
|
282
|
-
export { ACBuilder, type AttackConfig, type AttackResolution, type CheckBuilder, DCBuilder, HalfRollBuilder, type KeepMode, type Resolution, RollBuilder, type RollConfig, type RollFactory, type RollType, SaveBuilder, type SaveOutcome, type SaveResolution, builderPMFCache, d, d10, d100, d12, d20, d4, d6, d8, defaultConfig, flat, hd20, roll };
|
|
302
|
+
export { ACBuilder, type AttackConfig, type AttackResolution, type CheckBuilder, DCBuilder, HalfRollBuilder, type KeepMode, MaxOfRollBuilder, type Resolution, RollBuilder, type RollConfig, type RollFactory, type RollType, SaveBuilder, type SaveOutcome, type SaveResolution, builderPMFCache, d, d10, d100, d12, d20, d4, d6, d8, defaultConfig, flat, hd20, roll };
|
package/dist/builder/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { P as PMF, i as DiceQuery, L as LRUCache } from '../pmf-D1p2Re-J.js';
|
|
2
2
|
|
|
3
|
-
type ExpressionNode = DieNode | ConstantNode | SumNode | AddNode | KeepNode | D20RollNode | HalfNode;
|
|
3
|
+
type ExpressionNode = DieNode | ConstantNode | SumNode | AddNode | KeepNode | D20RollNode | HalfNode | MaxOfNode;
|
|
4
4
|
type DieNode = {
|
|
5
5
|
type: "die";
|
|
6
6
|
sides: number;
|
|
@@ -39,6 +39,11 @@ type HalfNode = {
|
|
|
39
39
|
type: "half";
|
|
40
40
|
child: ExpressionNode;
|
|
41
41
|
};
|
|
42
|
+
type MaxOfNode = {
|
|
43
|
+
type: "maxOf";
|
|
44
|
+
count: number;
|
|
45
|
+
child: ExpressionNode;
|
|
46
|
+
};
|
|
42
47
|
|
|
43
48
|
type RollFactory = {
|
|
44
49
|
(count: number, sides?: number, modifier?: number): RollBuilder;
|
|
@@ -156,6 +161,7 @@ declare class RollBuilder {
|
|
|
156
161
|
get rollType(): RollType;
|
|
157
162
|
get baseReroll(): number;
|
|
158
163
|
half(): HalfRollBuilder;
|
|
164
|
+
maxOf(count: number): MaxOfRollBuilder;
|
|
159
165
|
}
|
|
160
166
|
declare class HalfRollBuilder extends RollBuilder {
|
|
161
167
|
private readonly innerRoll;
|
|
@@ -167,6 +173,20 @@ declare class HalfRollBuilder extends RollBuilder {
|
|
|
167
173
|
toPMF(eps?: number): PMF;
|
|
168
174
|
copy(): HalfRollBuilder;
|
|
169
175
|
}
|
|
176
|
+
declare class MaxOfRollBuilder extends RollBuilder {
|
|
177
|
+
private readonly innerRoll;
|
|
178
|
+
private readonly count;
|
|
179
|
+
private readonly diceCount?;
|
|
180
|
+
private readonly diceSides?;
|
|
181
|
+
constructor(innerRoll: RollBuilder, count: number, diceCount?: number, diceSides?: number);
|
|
182
|
+
get lastConfig(): any;
|
|
183
|
+
getSubRollConfigs(): readonly RollConfig[];
|
|
184
|
+
toExpression(): string;
|
|
185
|
+
private findDieInAST;
|
|
186
|
+
toAST(): ExpressionNode;
|
|
187
|
+
toPMF(eps?: number): PMF;
|
|
188
|
+
copy(): MaxOfRollBuilder;
|
|
189
|
+
}
|
|
170
190
|
|
|
171
191
|
type ActionEffect = RollBuilder;
|
|
172
192
|
declare class AttackBuilder implements CheckBuilder {
|
|
@@ -279,4 +299,4 @@ declare const flat: (n: number) => RollBuilder;
|
|
|
279
299
|
declare const roll: RollFactory;
|
|
280
300
|
declare const builderPMFCache: LRUCache<string, PMF>;
|
|
281
301
|
|
|
282
|
-
export { ACBuilder, type AttackConfig, type AttackResolution, type CheckBuilder, DCBuilder, HalfRollBuilder, type KeepMode, type Resolution, RollBuilder, type RollConfig, type RollFactory, type RollType, SaveBuilder, type SaveOutcome, type SaveResolution, builderPMFCache, d, d10, d100, d12, d20, d4, d6, d8, defaultConfig, flat, hd20, roll };
|
|
302
|
+
export { ACBuilder, type AttackConfig, type AttackResolution, type CheckBuilder, DCBuilder, HalfRollBuilder, type KeepMode, MaxOfRollBuilder, type Resolution, RollBuilder, type RollConfig, type RollFactory, type RollType, SaveBuilder, type SaveOutcome, type SaveResolution, builderPMFCache, d, d10, d100, d12, d20, d4, d6, d8, defaultConfig, flat, hd20, roll };
|
package/dist/builder/index.js
CHANGED
|
@@ -2657,6 +2657,14 @@ var RollBuilder = class _RollBuilder {
|
|
|
2657
2657
|
const scaleInt = Math.floor(scale);
|
|
2658
2658
|
if (scaleInt !== scale) throw new Error("Scale must be an integer");
|
|
2659
2659
|
if (scaleInt <= 0) throw new Error("Scale must be > 0");
|
|
2660
|
+
if (scaleInt > 1) {
|
|
2661
|
+
const configs = this.getSubRollConfigs();
|
|
2662
|
+
if (configs.length === 1 && configs[0].keep && configs[0].count === 1) {
|
|
2663
|
+
const config = configs[0];
|
|
2664
|
+
const newConfig = { ...config, count: scaleInt };
|
|
2665
|
+
return new _RollBuilder([newConfig]);
|
|
2666
|
+
}
|
|
2667
|
+
}
|
|
2660
2668
|
const newConfigs = this.getSubRollConfigs().map((config) => {
|
|
2661
2669
|
if (!config.sides || config.sides <= 0) return config;
|
|
2662
2670
|
return { ...config, count: config.count * scaleInt };
|
|
@@ -2876,6 +2884,10 @@ var RollBuilder = class _RollBuilder {
|
|
|
2876
2884
|
half() {
|
|
2877
2885
|
return new HalfRollBuilder(this);
|
|
2878
2886
|
}
|
|
2887
|
+
// Create a "max of N rolls" version of this roll for crit damage with keep operations
|
|
2888
|
+
maxOf(count) {
|
|
2889
|
+
return new MaxOfRollBuilder(this, count);
|
|
2890
|
+
}
|
|
2879
2891
|
};
|
|
2880
2892
|
var HalfRollBuilder = class _HalfRollBuilder extends RollBuilder {
|
|
2881
2893
|
constructor(innerRoll) {
|
|
@@ -2905,6 +2917,78 @@ var HalfRollBuilder = class _HalfRollBuilder extends RollBuilder {
|
|
|
2905
2917
|
return new _HalfRollBuilder(this.innerRoll.copy());
|
|
2906
2918
|
}
|
|
2907
2919
|
};
|
|
2920
|
+
var MaxOfRollBuilder = class _MaxOfRollBuilder extends RollBuilder {
|
|
2921
|
+
constructor(innerRoll, count, diceCount, diceSides) {
|
|
2922
|
+
super(0);
|
|
2923
|
+
this.innerRoll = innerRoll;
|
|
2924
|
+
this.count = count;
|
|
2925
|
+
this.diceCount = diceCount;
|
|
2926
|
+
this.diceSides = diceSides;
|
|
2927
|
+
}
|
|
2928
|
+
get lastConfig() {
|
|
2929
|
+
return this.innerRoll.lastConfig;
|
|
2930
|
+
}
|
|
2931
|
+
getSubRollConfigs() {
|
|
2932
|
+
return this.innerRoll.getSubRollConfigs();
|
|
2933
|
+
}
|
|
2934
|
+
toExpression() {
|
|
2935
|
+
if (this.diceCount && this.diceSides) {
|
|
2936
|
+
return `max${this.count}(${this.diceCount}d${this.diceSides})`;
|
|
2937
|
+
}
|
|
2938
|
+
return `max${this.count}(?d?)`;
|
|
2939
|
+
}
|
|
2940
|
+
findDieInAST(node) {
|
|
2941
|
+
if (node.type === "die") return node;
|
|
2942
|
+
if (node.child) return this.findDieInAST(node.child);
|
|
2943
|
+
if (node.children) {
|
|
2944
|
+
for (const child of node.children) {
|
|
2945
|
+
const result = this.findDieInAST(child.node || child);
|
|
2946
|
+
if (result) return result;
|
|
2947
|
+
}
|
|
2948
|
+
}
|
|
2949
|
+
return null;
|
|
2950
|
+
}
|
|
2951
|
+
toAST() {
|
|
2952
|
+
if (this.diceCount && this.diceSides) {
|
|
2953
|
+
const sumChild = {
|
|
2954
|
+
type: "sum",
|
|
2955
|
+
count: this.diceCount,
|
|
2956
|
+
child: { type: "die", sides: this.diceSides }
|
|
2957
|
+
};
|
|
2958
|
+
return {
|
|
2959
|
+
type: "maxOf",
|
|
2960
|
+
count: this.count,
|
|
2961
|
+
child: sumChild
|
|
2962
|
+
};
|
|
2963
|
+
}
|
|
2964
|
+
try {
|
|
2965
|
+
const configs = this.innerRoll.getSubRollConfigs();
|
|
2966
|
+
if (configs.length === 1 && configs[0].sides) {
|
|
2967
|
+
const config = configs[0];
|
|
2968
|
+
const sumChild = {
|
|
2969
|
+
type: "sum",
|
|
2970
|
+
count: config.count,
|
|
2971
|
+
child: { type: "die", sides: config.sides }
|
|
2972
|
+
};
|
|
2973
|
+
return {
|
|
2974
|
+
type: "maxOf",
|
|
2975
|
+
count: this.count,
|
|
2976
|
+
child: sumChild
|
|
2977
|
+
};
|
|
2978
|
+
}
|
|
2979
|
+
} catch (e) {
|
|
2980
|
+
}
|
|
2981
|
+
throw new Error(
|
|
2982
|
+
`MaxOfRollBuilder.toAST(): Unsupported innerRoll configuration`
|
|
2983
|
+
);
|
|
2984
|
+
}
|
|
2985
|
+
toPMF(eps = 0) {
|
|
2986
|
+
return pmfFromRollBuilder(this, eps);
|
|
2987
|
+
}
|
|
2988
|
+
copy() {
|
|
2989
|
+
return new _MaxOfRollBuilder(this.innerRoll.copy(), this.count);
|
|
2990
|
+
}
|
|
2991
|
+
};
|
|
2908
2992
|
|
|
2909
2993
|
// src/builder/factory.ts
|
|
2910
2994
|
var rollFn = (count, sidesOrDie, modifier) => {
|
|
@@ -2993,13 +3077,50 @@ function astFromRollConfigs(configs) {
|
|
|
2993
3077
|
appliedRollType = true;
|
|
2994
3078
|
}
|
|
2995
3079
|
if (cfg.rollType === "flat" && cfg.keep && cfg.keep.total > 0) {
|
|
2996
|
-
const
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3080
|
+
const baseCount = Math.max(1, Math.floor(Math.abs(count || 1)));
|
|
3081
|
+
const trials = Math.max(1, Math.floor(cfg.keep.total));
|
|
3082
|
+
const k = Math.max(0, Math.floor(cfg.keep.count));
|
|
3083
|
+
if (k === 1 && cfg.keep.mode === "highest") {
|
|
3084
|
+
const perTrial = {
|
|
3085
|
+
type: "sum",
|
|
3086
|
+
count: baseCount,
|
|
3087
|
+
child: node
|
|
3088
|
+
};
|
|
3089
|
+
if (trials === 1) {
|
|
3090
|
+
node = perTrial;
|
|
3091
|
+
} else {
|
|
3092
|
+
node = { type: "maxOf", count: trials, child: perTrial };
|
|
3093
|
+
}
|
|
3094
|
+
} else if (trials === baseCount) {
|
|
3095
|
+
const base = { type: "sum", count: trials, child: node };
|
|
3096
|
+
node = {
|
|
3097
|
+
type: "keep",
|
|
3098
|
+
mode: cfg.keep.mode,
|
|
3099
|
+
count: k,
|
|
3100
|
+
child: base
|
|
3101
|
+
};
|
|
3102
|
+
} else {
|
|
3103
|
+
const perTrial = {
|
|
3104
|
+
type: "sum",
|
|
3105
|
+
count: baseCount,
|
|
3106
|
+
child: node
|
|
3107
|
+
};
|
|
3108
|
+
if (trials === 1) {
|
|
3109
|
+
node = perTrial;
|
|
3110
|
+
} else {
|
|
3111
|
+
const trialPool = {
|
|
3112
|
+
type: "sum",
|
|
3113
|
+
count: trials,
|
|
3114
|
+
child: perTrial
|
|
3115
|
+
};
|
|
3116
|
+
node = {
|
|
3117
|
+
type: "keep",
|
|
3118
|
+
mode: cfg.keep.mode,
|
|
3119
|
+
count: k,
|
|
3120
|
+
child: trialPool
|
|
3121
|
+
};
|
|
3122
|
+
}
|
|
3123
|
+
}
|
|
3003
3124
|
} else {
|
|
3004
3125
|
const c = appliedRollType ? 1 : Math.max(1, count || 1);
|
|
3005
3126
|
node = { type: "sum", count: c, child: node };
|
|
@@ -3053,14 +3174,14 @@ function resolve(node, eps = defaultEps) {
|
|
|
3053
3174
|
return res;
|
|
3054
3175
|
}
|
|
3055
3176
|
case "keep": {
|
|
3056
|
-
const
|
|
3057
|
-
const
|
|
3058
|
-
|
|
3059
|
-
const
|
|
3060
|
-
|
|
3177
|
+
const totalTrials = getTotalCount(node);
|
|
3178
|
+
const keepCount = Math.max(0, Math.min(node.count, totalTrials));
|
|
3179
|
+
if (keepCount === 0 || totalTrials === 0) return PMF.delta(0, eps);
|
|
3180
|
+
const perTrialNode = node.child.child;
|
|
3181
|
+
const perTrialPMF = resolve(perTrialNode, eps);
|
|
3061
3182
|
return keepSumPMF(
|
|
3062
|
-
|
|
3063
|
-
|
|
3183
|
+
perTrialPMF,
|
|
3184
|
+
totalTrials,
|
|
3064
3185
|
keepCount,
|
|
3065
3186
|
node.mode === "highest",
|
|
3066
3187
|
eps
|
|
@@ -3075,6 +3196,12 @@ function resolve(node, eps = defaultEps) {
|
|
|
3075
3196
|
const childPMF = resolve(node.child, eps);
|
|
3076
3197
|
return childPMF.scaleDamage(0.5, "floor");
|
|
3077
3198
|
}
|
|
3199
|
+
case "maxOf": {
|
|
3200
|
+
const childPMF = resolve(node.child, eps);
|
|
3201
|
+
const count = Math.max(1, Math.floor(node.count));
|
|
3202
|
+
if (count === 1) return childPMF;
|
|
3203
|
+
return computeMaxOfPMF(childPMF, count, eps);
|
|
3204
|
+
}
|
|
3078
3205
|
}
|
|
3079
3206
|
})();
|
|
3080
3207
|
builderPMFCache.set(cacheKey, result);
|
|
@@ -3145,6 +3272,7 @@ function findDie(node) {
|
|
|
3145
3272
|
case "sum":
|
|
3146
3273
|
case "d20Roll":
|
|
3147
3274
|
case "half":
|
|
3275
|
+
case "maxOf":
|
|
3148
3276
|
return findDie(node.child);
|
|
3149
3277
|
case "keep":
|
|
3150
3278
|
return findDie(node.child.child);
|
|
@@ -3161,6 +3289,38 @@ function getTotalCount(node) {
|
|
|
3161
3289
|
while (cur.type === "keep") cur = cur.child;
|
|
3162
3290
|
return cur.type === "sum" ? Math.max(0, Math.floor(cur.count)) : 0;
|
|
3163
3291
|
}
|
|
3292
|
+
function computeMaxOfPMF(pmf, count, eps = defaultEps) {
|
|
3293
|
+
if (count <= 1) return pmf;
|
|
3294
|
+
const support = pmf.support();
|
|
3295
|
+
const out = /* @__PURE__ */ new Map();
|
|
3296
|
+
if (count <= 6 && support.length <= 20) {
|
|
3297
|
+
let dfs = function(rollsLeft, currentMax, probability) {
|
|
3298
|
+
if (rollsLeft === 0) {
|
|
3299
|
+
out.set(currentMax, (out.get(currentMax) || 0) + probability);
|
|
3300
|
+
return;
|
|
3301
|
+
}
|
|
3302
|
+
for (const value of support) {
|
|
3303
|
+
const p = pmf.pAt(value);
|
|
3304
|
+
if (p > 0) {
|
|
3305
|
+
const newMax = Math.max(currentMax, value);
|
|
3306
|
+
dfs(rollsLeft - 1, newMax, probability * p);
|
|
3307
|
+
}
|
|
3308
|
+
}
|
|
3309
|
+
};
|
|
3310
|
+
dfs(count, -Infinity, 1);
|
|
3311
|
+
} else {
|
|
3312
|
+
const sortedSupport = [...support].sort((a, b) => a - b);
|
|
3313
|
+
for (const value of sortedSupport) {
|
|
3314
|
+
const cdfAtValue = pmf.cdfAt(value);
|
|
3315
|
+
const cdfAtValueMinus1 = value > sortedSupport[0] ? pmf.cdfAt(value - 1) : 0;
|
|
3316
|
+
const probMax = Math.pow(cdfAtValue, count) - Math.pow(cdfAtValueMinus1, count);
|
|
3317
|
+
if (probMax > eps) {
|
|
3318
|
+
out.set(value, probMax);
|
|
3319
|
+
}
|
|
3320
|
+
}
|
|
3321
|
+
}
|
|
3322
|
+
return PMF.fromMap(out, eps);
|
|
3323
|
+
}
|
|
3164
3324
|
function keepSumPMF(single, total, keep, highest, eps = defaultEps) {
|
|
3165
3325
|
if (keep >= total) return single.power(total, eps);
|
|
3166
3326
|
if (keep <= 0) return PMF.delta(0, eps);
|
|
@@ -3169,24 +3329,106 @@ function keepSumPMF(single, total, keep, highest, eps = defaultEps) {
|
|
|
3169
3329
|
const cacheKey = `keep|${pmfSig}|t:${total}|k:${keep}|h:${highest ? 1 : 0}|e:${eps}`;
|
|
3170
3330
|
const cached = builderPMFCache.get(cacheKey);
|
|
3171
3331
|
if (cached) return cached;
|
|
3172
|
-
|
|
3173
|
-
|
|
3174
|
-
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
const
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
|
|
3332
|
+
if (keep === 1) {
|
|
3333
|
+
if (highest) {
|
|
3334
|
+
return computeMaxOfPMF(single, total, eps);
|
|
3335
|
+
} else {
|
|
3336
|
+
const neg = single.mapDamage((v) => -v);
|
|
3337
|
+
const minPMF = computeMaxOfPMF(neg, total, eps).mapDamage((v) => -v);
|
|
3338
|
+
builderPMFCache.set(cacheKey, minPMF);
|
|
3339
|
+
return minPMF;
|
|
3340
|
+
}
|
|
3341
|
+
}
|
|
3342
|
+
let state = /* @__PURE__ */ new Map();
|
|
3343
|
+
const keyOf = (used, r) => `${used}|${r}`;
|
|
3344
|
+
state.set(keyOf(0, total), /* @__PURE__ */ new Map([[0, 1]]));
|
|
3345
|
+
const valuesDesc = highest ? [...sortedSupport].sort((a, b) => b - a) : [...sortedSupport].sort((a, b) => a - b);
|
|
3346
|
+
const binomPMF = (r, p) => {
|
|
3347
|
+
if (r <= 0) return [1];
|
|
3348
|
+
if (p <= eps) {
|
|
3349
|
+
const arr2 = new Array(r + 1).fill(0);
|
|
3350
|
+
arr2[0] = 1;
|
|
3351
|
+
return arr2;
|
|
3352
|
+
}
|
|
3353
|
+
if (1 - p <= eps) {
|
|
3354
|
+
const arr2 = new Array(r + 1).fill(0);
|
|
3355
|
+
arr2[r] = 1;
|
|
3356
|
+
return arr2;
|
|
3182
3357
|
}
|
|
3183
|
-
|
|
3184
|
-
|
|
3185
|
-
|
|
3358
|
+
const q = 1 - p;
|
|
3359
|
+
const arr = new Array(r + 1).fill(0);
|
|
3360
|
+
arr[0] = Math.pow(q, r);
|
|
3361
|
+
const ratio = p / q;
|
|
3362
|
+
for (let x = 1; x <= r; x++)
|
|
3363
|
+
arr[x] = arr[x - 1] * (r - x + 1) / x * ratio;
|
|
3364
|
+
let s = 0;
|
|
3365
|
+
for (let x = 0; x <= r; x++) s += arr[x];
|
|
3366
|
+
if (Math.abs(1 - s) > 1e-12) for (let x = 0; x <= r; x++) arr[x] /= s;
|
|
3367
|
+
return arr;
|
|
3368
|
+
};
|
|
3369
|
+
const pruneMap = (m, threshold) => {
|
|
3370
|
+
if (threshold <= 0) return m;
|
|
3371
|
+
const out = /* @__PURE__ */ new Map();
|
|
3372
|
+
for (const [sum, pr] of m) if (pr >= threshold) out.set(sum, pr);
|
|
3373
|
+
return out.size === m.size ? m : out;
|
|
3374
|
+
};
|
|
3375
|
+
const pruneState = (st, threshold) => {
|
|
3376
|
+
if (threshold <= 0) return st;
|
|
3377
|
+
const out = /* @__PURE__ */ new Map();
|
|
3378
|
+
for (const [k, m] of st) {
|
|
3379
|
+
const mm = pruneMap(m, threshold);
|
|
3380
|
+
if (mm.size > 0) out.set(k, mm);
|
|
3186
3381
|
}
|
|
3382
|
+
return out;
|
|
3383
|
+
};
|
|
3384
|
+
let processedMass = 0;
|
|
3385
|
+
for (const v of valuesDesc) {
|
|
3386
|
+
const p = single.pAt(v);
|
|
3387
|
+
if (p <= 0) continue;
|
|
3388
|
+
const q = Math.max(eps, 1 - processedMass);
|
|
3389
|
+
const pCond = Math.min(1, p / q);
|
|
3390
|
+
const next = /* @__PURE__ */ new Map();
|
|
3391
|
+
for (const [k, m] of state) {
|
|
3392
|
+
const [usedStr, rStr] = k.split("|");
|
|
3393
|
+
const used = parseInt(usedStr, 10);
|
|
3394
|
+
const r = parseInt(rStr, 10);
|
|
3395
|
+
if (r === 0) {
|
|
3396
|
+
const destKey = keyOf(used, 0);
|
|
3397
|
+
const dest = next.get(destKey) ?? /* @__PURE__ */ new Map();
|
|
3398
|
+
for (const [sum, pr] of m) dest.set(sum, (dest.get(sum) || 0) + pr);
|
|
3399
|
+
next.set(destKey, dest);
|
|
3400
|
+
continue;
|
|
3401
|
+
}
|
|
3402
|
+
const bin = binomPMF(r, pCond);
|
|
3403
|
+
const remainingCapacity = keep - used;
|
|
3404
|
+
for (let x = 0; x <= r; x++) {
|
|
3405
|
+
const px = bin[x];
|
|
3406
|
+
if (px <= eps) continue;
|
|
3407
|
+
const t = Math.min(x, remainingCapacity);
|
|
3408
|
+
const used2 = used + t;
|
|
3409
|
+
const r2 = r - x;
|
|
3410
|
+
const add = t * v;
|
|
3411
|
+
const destKey = keyOf(used2, r2);
|
|
3412
|
+
const dest = next.get(destKey) ?? /* @__PURE__ */ new Map();
|
|
3413
|
+
for (const [sum, pr] of m) {
|
|
3414
|
+
const s2 = sum + add;
|
|
3415
|
+
const prob = pr * px;
|
|
3416
|
+
const cur = dest.get(s2) || 0;
|
|
3417
|
+
const nv = cur + prob;
|
|
3418
|
+
if (nv >= eps) dest.set(s2, nv);
|
|
3419
|
+
}
|
|
3420
|
+
if (dest.size > 0) next.set(destKey, dest);
|
|
3421
|
+
}
|
|
3422
|
+
}
|
|
3423
|
+
state = pruneState(next, eps * 1e-6);
|
|
3424
|
+
processedMass += p;
|
|
3425
|
+
}
|
|
3426
|
+
const finalKey = keyOf(keep, 0);
|
|
3427
|
+
const dist = state.get(finalKey) ?? /* @__PURE__ */ new Map();
|
|
3428
|
+
if (dist.size === 0) {
|
|
3429
|
+
return PMF.emptyMass();
|
|
3187
3430
|
}
|
|
3188
|
-
|
|
3189
|
-
const result = PMF.fromMap(out, eps);
|
|
3431
|
+
const result = PMF.fromMap(dist, eps);
|
|
3190
3432
|
builderPMFCache.set(cacheKey, result);
|
|
3191
3433
|
return result;
|
|
3192
3434
|
}
|
|
@@ -3212,6 +3454,8 @@ function getASTSignature(node) {
|
|
|
3212
3454
|
)}}`;
|
|
3213
3455
|
case "half":
|
|
3214
3456
|
return `half{ch:${getASTSignature(node.child)}}`;
|
|
3457
|
+
case "maxOf":
|
|
3458
|
+
return `maxOf{c:${node.count},ch:${getASTSignature(node.child)}}`;
|
|
3215
3459
|
case "add": {
|
|
3216
3460
|
let constantValue = 0;
|
|
3217
3461
|
const otherChildrenSigs = [];
|
|
@@ -3271,7 +3515,12 @@ var AttackBuilder = class _AttackBuilder {
|
|
|
3271
3515
|
if (this.hitEffect) {
|
|
3272
3516
|
effectPart = `(${this.hitEffect.toExpression()})`;
|
|
3273
3517
|
if (this.critEffect !== null) {
|
|
3274
|
-
|
|
3518
|
+
let crit;
|
|
3519
|
+
if (this.critEffect) {
|
|
3520
|
+
crit = this.critEffect;
|
|
3521
|
+
} else {
|
|
3522
|
+
crit = this.hitEffect?.copy().doubleDice() ?? RollBuilder.fromArgs(0);
|
|
3523
|
+
}
|
|
3275
3524
|
const critThreshold = this.check.critThreshold;
|
|
3276
3525
|
if (critThreshold < 15 || critThreshold > 20) {
|
|
3277
3526
|
throw new Error(
|
|
@@ -3581,6 +3830,6 @@ RollBuilder.prototype.dc = function(saveDC) {
|
|
|
3581
3830
|
return new DCBuilder(this).dc(saveDC);
|
|
3582
3831
|
};
|
|
3583
3832
|
|
|
3584
|
-
export { ACBuilder, DCBuilder, HalfRollBuilder, RollBuilder, SaveBuilder, builderPMFCache, d, d10, d100, d12, d20, d4, d6, d8, defaultConfig, flat, hd20, roll };
|
|
3833
|
+
export { ACBuilder, DCBuilder, HalfRollBuilder, MaxOfRollBuilder, RollBuilder, SaveBuilder, builderPMFCache, d, d10, d100, d12, d20, d4, d6, d8, defaultConfig, flat, hd20, roll };
|
|
3585
3834
|
//# sourceMappingURL=index.js.map
|
|
3586
3835
|
//# sourceMappingURL=index.js.map
|