@yipe/dice 0.5.0 → 0.7.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 +25 -0
- package/dist/builder/ac.d.ts.map +1 -0
- package/dist/builder/ast.d.ts +9 -0
- package/dist/builder/ast.d.ts.map +1 -0
- package/dist/builder/attack.d.ts +41 -0
- package/dist/builder/attack.d.ts.map +1 -0
- package/dist/builder/d20.d.ts +6 -0
- package/dist/builder/d20.d.ts.map +1 -0
- package/dist/builder/dc.d.ts +26 -0
- package/dist/builder/dc.d.ts.map +1 -0
- package/dist/builder/example.d.ts +356 -0
- package/dist/builder/example.d.ts.map +1 -0
- package/dist/builder/factory.d.ts +17 -0
- package/dist/builder/factory.d.ts.map +1 -0
- package/dist/builder/index.cjs +172 -285
- package/dist/builder/index.cjs.map +1 -1
- package/dist/builder/index.d.ts +8 -429
- package/dist/builder/index.d.ts.map +1 -0
- package/dist/builder/index.js +172 -285
- package/dist/builder/index.js.map +1 -1
- package/dist/builder/nodes.d.ts +61 -0
- package/dist/builder/nodes.d.ts.map +1 -0
- package/dist/builder/prob.d.ts +3 -0
- package/dist/builder/prob.d.ts.map +1 -0
- package/dist/builder/roll.d.ts +205 -0
- package/dist/builder/roll.d.ts.map +1 -0
- package/dist/builder/save.d.ts +19 -0
- package/dist/builder/save.d.ts.map +1 -0
- package/dist/builder/types.d.ts +66 -0
- package/dist/builder/types.d.ts.map +1 -0
- package/dist/common/bounce.d.ts +32 -0
- package/dist/common/bounce.d.ts.map +1 -0
- package/dist/common/errors.d.ts +26 -0
- package/dist/common/errors.d.ts.map +1 -0
- package/dist/common/lru-cache.d.ts +17 -0
- package/dist/common/lru-cache.d.ts.map +1 -0
- package/dist/common/types.d.ts +65 -0
- package/dist/common/types.d.ts.map +1 -0
- package/dist/index.cjs +152 -285
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +9 -111
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +152 -285
- package/dist/index.js.map +1 -1
- package/dist/parser/dice.d.ts +70 -0
- package/dist/parser/dice.d.ts.map +1 -0
- package/dist/parser/parser.d.ts +14 -0
- package/dist/parser/parser.d.ts.map +1 -0
- package/dist/pmf/mixture.d.ts +37 -0
- package/dist/pmf/mixture.d.ts.map +1 -0
- package/dist/pmf/pmf.d.ts +449 -0
- package/dist/pmf/pmf.d.ts.map +1 -0
- package/dist/{pmf-D5VRghZI.d.cts → pmf/query.d.ts} +18 -548
- package/dist/pmf/query.d.ts.map +1 -0
- package/package.json +16 -15
- package/.claude/worktrees/amazing-matsumoto-27220c/LICENSE +0 -21
- package/.claude/worktrees/amazing-matsumoto-27220c/README.md +0 -518
- package/.claude/worktrees/vibrant-lovelace-0cc9e7/LICENSE +0 -21
- package/.claude/worktrees/vibrant-lovelace-0cc9e7/README.md +0 -518
- package/.claude/worktrees/wizardly-mclean-e375de/LICENSE +0 -21
- package/.claude/worktrees/wizardly-mclean-e375de/README.md +0 -518
- package/CHANGELOG.md +0 -239
- package/dist/builder/index.d.cts +0 -429
- package/dist/index.d.cts +0 -111
- package/dist/pmf-D5VRghZI.d.ts +0 -1129
package/dist/pmf-D5VRghZI.d.ts
DELETED
|
@@ -1,1129 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Simple LRU cache implementation
|
|
3
|
-
*/
|
|
4
|
-
declare class LRUCache<K, V> {
|
|
5
|
-
private readonly maxSize;
|
|
6
|
-
private cache;
|
|
7
|
-
constructor(maxSize?: number);
|
|
8
|
-
get(key: K): V | undefined;
|
|
9
|
-
delete(key: K): void;
|
|
10
|
-
set(key: K, value: V): this;
|
|
11
|
-
clear(): void;
|
|
12
|
-
get size(): number;
|
|
13
|
-
has(key: K): boolean;
|
|
14
|
-
keys(): IterableIterator<K>;
|
|
15
|
-
values(): IterableIterator<V>;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/** Mapping from outcome label to probability mass or damage attribution. */
|
|
19
|
-
type OutcomeLabelMap = Partial<Record<string, number>>;
|
|
20
|
-
/** Computational epsilon for pruning negligible probabilities. */
|
|
21
|
-
declare const EPS = 1e-12;
|
|
22
|
-
/** A probability bin for a specific damage value. */
|
|
23
|
-
interface Bin {
|
|
24
|
-
/** Total probability mass at this damage value. */
|
|
25
|
-
p: number;
|
|
26
|
-
/** Per-outcome probability mass contributions at this damage. */
|
|
27
|
-
count: OutcomeLabelMap;
|
|
28
|
-
/** Optional per-outcome damage attribution at this damage. */
|
|
29
|
-
attr?: OutcomeLabelMap;
|
|
30
|
-
}
|
|
31
|
-
interface CritConfig {
|
|
32
|
-
critThreshold: number;
|
|
33
|
-
}
|
|
34
|
-
/** Simple mapping from damage value to probability. */
|
|
35
|
-
type DamageDistribution = Record<number, number>;
|
|
36
|
-
/** Canonical outcome labels supported by the query helpers. */
|
|
37
|
-
type OutcomeType = "crit" | "hit" | "missNone" | "missDamage" | "saveHalf" | "saveFail" | "pc";
|
|
38
|
-
type Rounding = "none" | "floor" | "round" | "ceil";
|
|
39
|
-
/** How a d20 attack roll resolves: single die, keep-highest of 2/3, or keep-lowest of 2. */
|
|
40
|
-
type RollType = "flat" | "advantage" | "disadvantage" | "elven accuracy";
|
|
41
|
-
/**
|
|
42
|
-
* P(critical hit) for the given crit window and d20 {@link RollType}.
|
|
43
|
-
*
|
|
44
|
-
* `critRange` is the number of top faces that crit (1 for a natural 20, 2 for
|
|
45
|
-
* 19–20, …), so a single die crits with probability `critRange / 20`. Advantage
|
|
46
|
-
* rolls two d20s / elven accuracy three, keeping the best; disadvantage keeps
|
|
47
|
-
* the worst of two.
|
|
48
|
-
*/
|
|
49
|
-
declare function critProbability(critRange: number, rollType?: RollType): number;
|
|
50
|
-
/**
|
|
51
|
-
* The canonical "clean miss" outcome — a point of zero damage with no rider.
|
|
52
|
-
* This is the {@link OutcomeType} that attribution charts and outcome stats key
|
|
53
|
-
* on, and is distinct from the builder's attack-resolution `miss` weight label.
|
|
54
|
-
*/
|
|
55
|
-
declare const MISS_NONE_OUTCOME: OutcomeType;
|
|
56
|
-
/**
|
|
57
|
-
* All outcome types in canonical severity order — clean miss → crit. This is
|
|
58
|
-
* also the natural stacking order for attribution charts (least- to
|
|
59
|
-
* most-impactful, bottom → top). Enumerates every {@link OutcomeType} exactly
|
|
60
|
-
* once; use it instead of hand-maintained per-consumer outcome tables.
|
|
61
|
-
*/
|
|
62
|
-
declare const ALL_OUTCOME_TYPES: OutcomeType[];
|
|
63
|
-
/**
|
|
64
|
-
* Outcome types in display order for stats / breakdown rows — most prominent
|
|
65
|
-
* first (crit, hit, …) down to the clean miss.
|
|
66
|
-
*/
|
|
67
|
-
declare const OUTCOME_DISPLAY_ORDER: OutcomeType[];
|
|
68
|
-
/**
|
|
69
|
-
* Sort outcome labels by a canonical order (defaults to {@link ALL_OUTCOME_TYPES}).
|
|
70
|
-
* Labels not present in `order` sort after known ones, alphabetically — so
|
|
71
|
-
* ad-hoc/test labels outside the {@link OutcomeType} union stay stable.
|
|
72
|
-
*/
|
|
73
|
-
declare function sortOutcomes<T extends string>(outcomes: Iterable<T>, order?: readonly string[]): T[];
|
|
74
|
-
declare const onAnyHit: OutcomeType[];
|
|
75
|
-
declare const onCritOnly: OutcomeType[];
|
|
76
|
-
declare const onHitOnly: OutcomeType[];
|
|
77
|
-
declare const onMissOnly: OutcomeType[];
|
|
78
|
-
declare const onMissDamageOnly: OutcomeType[];
|
|
79
|
-
declare const onSaveHalfOnly: OutcomeType[];
|
|
80
|
-
declare const onSaveFailOnly: OutcomeType[];
|
|
81
|
-
declare const onPotentCantripOnly: OutcomeType[];
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Query interface for analyzing dice roll probability distributions.
|
|
85
|
-
*
|
|
86
|
-
* Combines multiple attack PMFs and provides statistical analysis methods for:
|
|
87
|
-
* - Basic statistics (mean, variance, min/max, percentiles)
|
|
88
|
-
* - Probability queries (hit chances, success rates, exact counts)
|
|
89
|
-
* - Damage analysis (ranges by outcome type, expected values)
|
|
90
|
-
* - Data export (charts, tables, visualizations)
|
|
91
|
-
*
|
|
92
|
-
*/
|
|
93
|
-
declare class DiceQuery {
|
|
94
|
-
readonly singles: PMF[];
|
|
95
|
-
private readonly _eps;
|
|
96
|
-
private readonly _combinedProvided;
|
|
97
|
-
private _combined?;
|
|
98
|
-
private _combinedWithAttr?;
|
|
99
|
-
constructor(singles: PMF | PMF[], combined?: PMF, eps?: number);
|
|
100
|
-
/**
|
|
101
|
-
* The combined damage distribution of all single PMFs (their convolution),
|
|
102
|
-
* normalized to total probability 1.
|
|
103
|
-
*
|
|
104
|
-
* Computed lazily on first access and cached. Queries that only need
|
|
105
|
-
* additive statistics — {@link DiceQuery.mean}, {@link DiceQuery.variance},
|
|
106
|
-
* {@link DiceQuery.stddev} — never trigger this convolution.
|
|
107
|
-
*/
|
|
108
|
-
get combined(): PMF;
|
|
109
|
-
private static readonly DEFAULT_OUTCOMES;
|
|
110
|
-
/**
|
|
111
|
-
* Returns a new PMF with damage attribution metadata populated.
|
|
112
|
-
*
|
|
113
|
-
* This method computes attribution on-demand for builder-generated PMFs,
|
|
114
|
-
* enabling them to work with damage attribution charts. The `attr` field
|
|
115
|
-
* tracks how much damage each outcome type contributes at each damage value.
|
|
116
|
-
*
|
|
117
|
-
* For each bin at damage D: sum(attr.values()) ≈ D × P(damage = D)
|
|
118
|
-
*
|
|
119
|
-
* Performance: Cached after first call. Adds minimal overhead vs `combined`.
|
|
120
|
-
*
|
|
121
|
-
* @returns PMF with attr field populated for damage attribution charts
|
|
122
|
-
*
|
|
123
|
-
* @example
|
|
124
|
-
* const attack = d20.plus(5).ac(15).onHit(d(2,6).plus(3)).onCrit(d(2,6))
|
|
125
|
-
* const query = attack.toQuery()
|
|
126
|
-
* const pmf = query.combinedWithAttribution()
|
|
127
|
-
* // Now pmf can be used with toDamageAttributionChartSeries()
|
|
128
|
-
*/
|
|
129
|
-
combinedWithAttribution(): PMF;
|
|
130
|
-
/**
|
|
131
|
-
* Per-label `damage value → probability mass` series for the combined,
|
|
132
|
-
* attribution-carrying distribution — the provenance core of the stacked
|
|
133
|
-
* damage-attribution chart. Convenience for
|
|
134
|
-
* `combinedWithAttribution().attributionByValue()`; see
|
|
135
|
-
* {@link PMF.attributionByValue}.
|
|
136
|
-
*/
|
|
137
|
-
attributionByValue(): Map<string, Map<number, number>>;
|
|
138
|
-
/**
|
|
139
|
-
* How many of the independent single PMFs can produce the given outcome
|
|
140
|
-
* label. Useful for "all of them succeeded" style probabilities where the
|
|
141
|
-
* exponent is the number of contributing attacks (see
|
|
142
|
-
* {@link DiceQuery.probExactlyK}).
|
|
143
|
-
*/
|
|
144
|
-
countSinglesWith(label: string): number;
|
|
145
|
-
/**
|
|
146
|
-
* Returns the expected damage across all possible outcomes.
|
|
147
|
-
*
|
|
148
|
-
* Example: `query.mean()` → 12.5
|
|
149
|
-
* Use case: "What's my average damage per round?"
|
|
150
|
-
*/
|
|
151
|
-
mean(): number;
|
|
152
|
-
/**
|
|
153
|
-
* Returns the variance of the damage distribution.
|
|
154
|
-
*
|
|
155
|
-
* Example: `query.variance()` → 45.2
|
|
156
|
-
* Use case: "How much does my damage vary from the average?"
|
|
157
|
-
* High variance means higher risk/reward. Lower variance means more consistent damage.
|
|
158
|
-
*/
|
|
159
|
-
variance(): number;
|
|
160
|
-
/**
|
|
161
|
-
* Returns the standard deviation of the damage distribution.
|
|
162
|
-
*
|
|
163
|
-
* Example: `query.stdev()` → 6.7
|
|
164
|
-
* Use case: "What's the typical spread around my average damage?"
|
|
165
|
-
* Used to determine how consistent the damage is.
|
|
166
|
-
*/
|
|
167
|
-
stddev(): number;
|
|
168
|
-
/** Alias of {@link DiceQuery.stddev}, matching {@link PMF.stdev}. */
|
|
169
|
-
stdev(): number;
|
|
170
|
-
/**
|
|
171
|
-
* Returns the Cumulative Distribution Function.
|
|
172
|
-
*/
|
|
173
|
-
cdf(x: number): number;
|
|
174
|
-
/**
|
|
175
|
-
* Returns the probability of dealing X damage or less.
|
|
176
|
-
* In statistics, this is called the cumulative distribution function (CDF).
|
|
177
|
-
* Example: `query.cdf(20)` → 0.75
|
|
178
|
-
* Use case: "What's the chance I deal 20 damage or less?"
|
|
179
|
-
*/
|
|
180
|
-
probTotalAtMost(x: number): number;
|
|
181
|
-
/**
|
|
182
|
-
* Returns the Complementary Cumulative Distribution Function.
|
|
183
|
-
*/
|
|
184
|
-
ccdf(x: number): number;
|
|
185
|
-
/**
|
|
186
|
-
* Returns the probability of dealing at least X damage.
|
|
187
|
-
*
|
|
188
|
-
* Example: `query.probTotalAtLeast(25)` → 0.35
|
|
189
|
-
* Use case: "What's the chance I deal at least 25 damage to finish the enemy?"
|
|
190
|
-
*/
|
|
191
|
-
probTotalAtLeast(threshold: number): number;
|
|
192
|
-
/**
|
|
193
|
-
* Returns damage values at specific percentiles.
|
|
194
|
-
*
|
|
195
|
-
* Example: `query.percentiles([0.25, 0.5, 0.75])` → [8, 12, 18]
|
|
196
|
-
* Use case: "What are my 25th, 50th, and 75th percentile damage values?"
|
|
197
|
-
*/
|
|
198
|
-
percentiles(percentileValues: number[]): number[];
|
|
199
|
-
/**
|
|
200
|
-
* Returns the minimum possible damage.
|
|
201
|
-
*
|
|
202
|
-
* Example: `query.min()` → 0
|
|
203
|
-
* Use case: "What's the worst-case damage if everything misses?"
|
|
204
|
-
*/
|
|
205
|
-
min(): number;
|
|
206
|
-
/**
|
|
207
|
-
* Returns the maximum possible damage.
|
|
208
|
-
*
|
|
209
|
-
* Example: `query.max()` → 56
|
|
210
|
-
* Use case: "What's the best-case damage if everything crits and rolls max?"
|
|
211
|
-
*/
|
|
212
|
-
max(): number;
|
|
213
|
-
private singleProb;
|
|
214
|
-
/**
|
|
215
|
-
* Full count distribution [P(0), P(1), …, P(n)] for "an attack succeeds if it
|
|
216
|
-
* carries ANY of `labels`", over the n independent singles.
|
|
217
|
-
*
|
|
218
|
-
* Each single's per-event success probability is the Poisson-binomial
|
|
219
|
-
* marginal P(≥1 of labels) from {@link probabilityOf} (i.e. probAtLeastOne),
|
|
220
|
-
* computed exactly once. The binomial DP then runs once to produce the whole
|
|
221
|
-
* distribution, so the array-label paths of probExactlyK / probAtLeastK /
|
|
222
|
-
* probAtMostK can slice or sum from it instead of rebuilding a DiceQuery and
|
|
223
|
-
* re-running the DP per requested k.
|
|
224
|
-
*/
|
|
225
|
-
private countDistribution;
|
|
226
|
-
probAtLeastK(labels: OutcomeType | OutcomeType[], k: number): number;
|
|
227
|
-
/**
|
|
228
|
-
* Returns the probability that at least one attack has the specified outcome(s).
|
|
229
|
-
* - This is the complement of probAtMostK(labels, 0)
|
|
230
|
-
*
|
|
231
|
-
* Examples:
|
|
232
|
-
* - `query.probAtLeastOne('hit')` → 0.88 (88% chance at least one attack hits)
|
|
233
|
-
* - `query.probAtLeastOne(['hit', 'crit'])` → 0.96 (96% chance at least one succeeds)
|
|
234
|
-
*
|
|
235
|
-
* Use cases:
|
|
236
|
-
* - "What's the chance at least one of my attacks connects?"
|
|
237
|
-
*
|
|
238
|
-
* Note:
|
|
239
|
-
*
|
|
240
|
-
* - You have to pass in an array of labels to avoid double-counting if you are
|
|
241
|
-
* using multiple labels. You cannot just add them.
|
|
242
|
-
*/
|
|
243
|
-
probAtLeastOne(labels: OutcomeType | OutcomeType[]): number;
|
|
244
|
-
/**
|
|
245
|
-
* Computes binomial probabilities for exactly 0, 1, 2, ..., maxK occurrences of a label.
|
|
246
|
-
*
|
|
247
|
-
* Uses dynamic programming to efficiently calculate the probability distribution
|
|
248
|
-
* of how many attacks will have the specified outcome, accounting for different
|
|
249
|
-
* success probabilities across individual attacks.
|
|
250
|
-
*
|
|
251
|
-
* Example: For 3 attacks with 50% hit chance each, returns:
|
|
252
|
-
* [0.125, 0.375, 0.375, 0.125] = [P(0 hits), P(1 hit), P(2 hits), P(3 hits)]
|
|
253
|
-
*
|
|
254
|
-
* @param label - The outcome type to count
|
|
255
|
-
* @param maxK - Maximum number of occurrences to calculate (usually number of attacks)
|
|
256
|
-
* @returns Array where index K contains P(exactly K attacks have the label)
|
|
257
|
-
*/
|
|
258
|
-
private computeBinomialProbabilities;
|
|
259
|
-
/**
|
|
260
|
-
* Returns the probability that exactly K attacks result in the specified outcome(s).
|
|
261
|
-
*
|
|
262
|
-
* Single label examples:
|
|
263
|
-
* - probExactlyK('hit', 2) = probability exactly 2 attacks hit
|
|
264
|
-
* - probExactlyK('crit', 1) = probability exactly 1 attack crits
|
|
265
|
-
* - probExactlyK('crit', 0) = probability no attacks crit
|
|
266
|
-
*
|
|
267
|
-
* Array examples:
|
|
268
|
-
* - probExactlyK(['hit', 'crit'], 2) = probability exactly 2 attacks succeed
|
|
269
|
-
* - probExactlyK(['hit', 'crit'], 1) = probability exactly 1 attack succeeds
|
|
270
|
-
* - probExactlyK(['missDamage', 'missNone'], 0) = probability no attacks miss
|
|
271
|
-
*
|
|
272
|
-
* Use cases:
|
|
273
|
-
* - "What's the chance exactly one of my attacks hits?"
|
|
274
|
-
* - "How likely am I to get exactly 2 successes out of 3 attacks?"
|
|
275
|
-
* - "What's the probability that exactly half my attacks succeed?"
|
|
276
|
-
*
|
|
277
|
-
* Note: For arrays, an attack counts as a "success" if it has any of the specified labels.
|
|
278
|
-
* This is different from probAtMostK, which counts an attack as a "success" if it has ALL of the specified labels.
|
|
279
|
-
*/
|
|
280
|
-
probExactlyK(labels: OutcomeType | OutcomeType[], k: number): number;
|
|
281
|
-
/**
|
|
282
|
-
* Returns the probability that AT MOST K attacks result in the specified outcome(s).
|
|
283
|
-
*
|
|
284
|
-
* Single label examples:
|
|
285
|
-
* - probAtMostK('hit', 1) = probability 0 or 1 attacks hit (at most 1)
|
|
286
|
-
* - probAtMostK('crit', 0) = probability no attacks crit
|
|
287
|
-
* - probAtMostK('missDamage', 2) = probability at most 2 attacks miss
|
|
288
|
-
*
|
|
289
|
-
* Array examples:
|
|
290
|
-
* - probAtMostK(['hit', 'crit'], 1) = probability at most 1 attack succeeds
|
|
291
|
-
* - probAtMostK(['hit', 'crit'], 0) = probability no attacks succeed (all miss)
|
|
292
|
-
*
|
|
293
|
-
* Use cases:
|
|
294
|
-
* - "What's the chance that at most one attack hits?" (rest miss)
|
|
295
|
-
* - "How likely am I to have mostly failures?" (at most 1 success)
|
|
296
|
-
* - "What's the probability of a really bad turn?" (at most 0 successes)
|
|
297
|
-
*
|
|
298
|
-
*/
|
|
299
|
-
probAtMostK(labels: OutcomeType | OutcomeType[], k: number): number;
|
|
300
|
-
/**
|
|
301
|
-
* Returns the expected damage attributed to specific outcome types.
|
|
302
|
-
*
|
|
303
|
-
* Single label examples:
|
|
304
|
-
* - expectedDamageFrom('hit') = expected damage from hit components
|
|
305
|
-
* - expectedDamageFrom('crit') = expected damage from crit components
|
|
306
|
-
*
|
|
307
|
-
* Array examples:
|
|
308
|
-
* - expectedDamageFrom(['hit', 'crit']) = expected damage from any success
|
|
309
|
-
* - expectedDamageFrom(['missDamage', 'missNone']) = expected damage from misses
|
|
310
|
-
*
|
|
311
|
-
* Use cases:
|
|
312
|
-
* - "How much damage do I expect from successful attacks?"
|
|
313
|
-
* - "What's the damage contribution from critical hits specifically?"
|
|
314
|
-
* - "How much damage comes from miss effects (like save-for-half spells)?"
|
|
315
|
-
*/
|
|
316
|
-
expectedDamageFrom(labels: OutcomeType | OutcomeType[]): number;
|
|
317
|
-
/**
|
|
318
|
-
* Returns damage statistics for scenarios where AT LEAST ONE attack results in
|
|
319
|
-
* the specified outcome(s).
|
|
320
|
-
*
|
|
321
|
-
* This method answers "What happens when things go reasonably well?" rather than
|
|
322
|
-
* "What's the theoretical maximum?" It includes mixed scenarios which are more
|
|
323
|
-
* common and tactically relevant than pure scenarios.
|
|
324
|
-
*
|
|
325
|
-
* Single label examples:
|
|
326
|
-
* - damageStatsFrom('hit') = damage range when at least one attack hits
|
|
327
|
-
* - damageStatsFrom('crit') = damage range when at least one attack crits
|
|
328
|
-
*
|
|
329
|
-
* Array examples:
|
|
330
|
-
* - damageStatsFrom(['hit', 'crit']) = damage range when at least one attack succeeds
|
|
331
|
-
* - damageStatsFrom(['missDamage', 'missNone']) = damage range when at least one attack misses
|
|
332
|
-
*
|
|
333
|
-
* Tactical Use Cases:
|
|
334
|
-
* - "Given that I don't completely whiff (99% of turns), what damage should I expect?"
|
|
335
|
-
* - "When planning to kill a 60 HP enemy, what's my damage range on successful turns?"
|
|
336
|
-
* - "Should I use this risky spell if it has good damage when it works?"
|
|
337
|
-
* - "What's my damage potential when something goes right?" (vs pure failure)
|
|
338
|
-
*
|
|
339
|
-
* Combat Planning Examples:
|
|
340
|
-
* - 4 attacks with 90% hit chance: "96% of the time you'll do 25-150 damage, avg 52"
|
|
341
|
-
* (Much more useful than "You average 50 damage including complete misses")
|
|
342
|
-
* - Risk assessment: "80% of successful turns do 40-80 damage, but 20% do 80-150"
|
|
343
|
-
* - Resource management: "If I hit anything, I'll likely finish this enemy"
|
|
344
|
-
*
|
|
345
|
-
* Statistical Note:
|
|
346
|
-
* This includes mixed scenarios (2 hits + 1 crit, 3 hits + 1 miss, etc.) which
|
|
347
|
-
* occur far more frequently than pure scenarios. For pure scenarios, use combinedDamageStats.
|
|
348
|
-
*
|
|
349
|
-
* KNOWN LIMITATION (multi-attack, single label): the returned `count` is an
|
|
350
|
-
* EXPECTED COUNT (E[#label], so > 1 for N≥2 attacks, not a probability), and
|
|
351
|
-
* `avg` is the size-biased conditional mean E[dmg·#label]/E[#label] rather than
|
|
352
|
-
* E[dmg | the label occurs]. For a single attack both are the plain
|
|
353
|
-
* conditional figures. Use {@link probAtLeastOne} for the scenario probability.
|
|
354
|
-
*
|
|
355
|
-
* @example
|
|
356
|
-
* // High-level tactical planning
|
|
357
|
-
* const successStats = query.damageStatsFrom('hit')
|
|
358
|
-
* const successChance = query.probAtLeastOne('hit')
|
|
359
|
-
* console.log(`${(successChance*100).toFixed(1)}% chance to do ${successStats.min}-${successStats.max} damage`)
|
|
360
|
-
*/
|
|
361
|
-
damageStatsFrom(labels: OutcomeType | OutcomeType[]): {
|
|
362
|
-
min: number;
|
|
363
|
-
max: number;
|
|
364
|
-
avg: number;
|
|
365
|
-
count: number;
|
|
366
|
-
};
|
|
367
|
-
/**
|
|
368
|
-
* Returns damage statistics for scenarios where ALL attacks result in the specified
|
|
369
|
-
* outcome, calculated by leveraging the pure partition of singles.
|
|
370
|
-
*
|
|
371
|
-
* This method answers "What's the theoretical best/worst case?" and "What are the
|
|
372
|
-
* clean mathematical boundaries?" It provides pure scenarios without mixing outcomes.
|
|
373
|
-
*
|
|
374
|
-
* Examples:
|
|
375
|
-
* - combinedDamageStats('hit') = damage range when all attacks hit (none crit, none miss)
|
|
376
|
-
* - combinedDamageStats('crit') = damage range when all attacks crit (none just hit)
|
|
377
|
-
*
|
|
378
|
-
* UI and Display Use Cases:
|
|
379
|
-
* - Statistics panels showing "MAX Hit Damage" (users expect pure hits, not mixed)
|
|
380
|
-
* - "Best case scenario" vs "worst case scenario" analysis
|
|
381
|
-
* - Mathematical verification: "Does our hit damage calculation match manual math?"
|
|
382
|
-
* - Clean damage type attribution: "How much comes from base hits vs crits?"
|
|
383
|
-
*
|
|
384
|
-
* Design and Balance Use Cases:
|
|
385
|
-
* - Game designers: "What's the damage ceiling if someone gets lucky?"
|
|
386
|
-
* - Character optimization: "What's my absolute maximum potential?"
|
|
387
|
-
* - Ability comparison: "Which build has higher crit ceiling?"
|
|
388
|
-
* - Minimum guaranteed damage: "What's the worst I can do if everything hits?"
|
|
389
|
-
*
|
|
390
|
-
* Mathematical Use Cases:
|
|
391
|
-
* - Validating complex calculations against simple manual math
|
|
392
|
-
* - Understanding damage component contributions in isolation
|
|
393
|
-
* - Separating luck (crit variance) from consistency (hit variance)
|
|
394
|
-
* - Building intuition about damage sources
|
|
395
|
-
*
|
|
396
|
-
* When to Use This vs damageStatsFrom():
|
|
397
|
-
* - Use THIS for: UI max/min displays, theoretical limits, clean comparisons
|
|
398
|
-
* - Use damageStatsFrom() for: tactical planning, realistic expectations, mixed scenarios
|
|
399
|
-
*
|
|
400
|
-
* Statistical Note:
|
|
401
|
-
* Pure scenarios (all hits, all crits) are rare but represent clear mathematical
|
|
402
|
-
* boundaries. These stats help understand the "shape" of your damage potential.
|
|
403
|
-
*
|
|
404
|
-
* @example
|
|
405
|
-
* // UI display logic
|
|
406
|
-
* const pureHitMax = query.combinedDamageStats('hit').max // Clean "MAX Hit Damage: 90"
|
|
407
|
-
* const pureCritMax = query.combinedDamageStats('crit').max // Clean "MAX Crit Damage: 168"
|
|
408
|
-
*
|
|
409
|
-
* // vs tactical planning (use damageStatsFrom instead)
|
|
410
|
-
* const realisticRange = query.damageStatsFrom('hit') // Includes mixed scenarios
|
|
411
|
-
*/
|
|
412
|
-
combinedDamageStats(targetLabel: OutcomeType): {
|
|
413
|
-
min: number;
|
|
414
|
-
max: number;
|
|
415
|
-
avg: number;
|
|
416
|
-
count: number;
|
|
417
|
-
};
|
|
418
|
-
/**
|
|
419
|
-
* Returns the probability that at least one attack carries ANY of the
|
|
420
|
-
* specified labels (the marginal P(≥1) across the independent attacks).
|
|
421
|
-
*
|
|
422
|
-
* Examples:
|
|
423
|
-
* - `query.probabilityOf('hit')` → 0.88 (probability at least one hit occurs)
|
|
424
|
-
* - `query.probabilityOf(['hit', 'crit'])` → 0.96 (probability of any success)
|
|
425
|
-
*
|
|
426
|
-
* Use cases:
|
|
427
|
-
* - "What's the chance my resolution includes a success label?"
|
|
428
|
-
* - "How likely am I to get any hits or crits across all attacks?"
|
|
429
|
-
*
|
|
430
|
-
* Note: this must NOT be computed by summing `combined` bin probabilities. A
|
|
431
|
-
* single combined damage total is reachable by many outcome combinations and
|
|
432
|
-
* a bin can hold several labels at once, so summing `bin.p` over bins that
|
|
433
|
-
* contain a label over-counts. The correct marginal is the Poisson-binomial
|
|
434
|
-
* complement over the per-attack probabilities, i.e. {@link probAtLeastOne}.
|
|
435
|
-
*/
|
|
436
|
-
probabilityOf(labels: OutcomeType | OutcomeType[]): number;
|
|
437
|
-
/**
|
|
438
|
-
* Returns the probability of missing (any type of miss).
|
|
439
|
-
*
|
|
440
|
-
* Example: `query.missChance()` → 0.04
|
|
441
|
-
* Use case: "What's the chance I miss completely this turn?"
|
|
442
|
-
*/
|
|
443
|
-
missChance(): number;
|
|
444
|
-
/**
|
|
445
|
-
* Returns data formatted for plotting damage probability distribution.
|
|
446
|
-
*
|
|
447
|
-
* Example: `query.toChartSeries()` → [{x: 0, y: 0.04}, {x: 6, y: 0.1}, ...]
|
|
448
|
-
* Use case: "I want to visualize my damage distribution in a chart."
|
|
449
|
-
*/
|
|
450
|
-
toChartSeries(): Array<{
|
|
451
|
-
x: number;
|
|
452
|
-
y: number;
|
|
453
|
-
}>;
|
|
454
|
-
/**
|
|
455
|
-
* Returns tabular data showing damage values and their probability breakdowns.
|
|
456
|
-
*
|
|
457
|
-
* Example: `query.toLabeledTable(['hit', 'crit'])` →
|
|
458
|
-
* [{damage: 6, total: 0.01, hit: 0.008, crit: 0}, ...]
|
|
459
|
-
*
|
|
460
|
-
* Use case: "I want to see exactly how hit/crit probabilities contribute to each damage value."
|
|
461
|
-
*/
|
|
462
|
-
toLabeledTable(labels?: OutcomeType[]): Array<{
|
|
463
|
-
damage: number;
|
|
464
|
-
total: number;
|
|
465
|
-
} & Record<string, number>>;
|
|
466
|
-
/**
|
|
467
|
-
* Returns data for stacked charts with unconditional per-label probability mass per damage.
|
|
468
|
-
*
|
|
469
|
-
* - Each dataset value equals the unconditional probability mass for that label at that damage
|
|
470
|
-
* (i.e., `bin.count[label]`).
|
|
471
|
-
* - Column sums may be less than the total probability `bin.p` when you omit labels or when
|
|
472
|
-
* there is unlabeled mass. Include all relevant outcome labels if you need the sum to match.
|
|
473
|
-
* - This behavior matches tests that expect raw per-label mass (not proportional scaling).
|
|
474
|
-
* - NOTE: This implementation may break dprcalc.com chart binning at large n, need to test it more.
|
|
475
|
-
*
|
|
476
|
-
* @example
|
|
477
|
-
* query.toStackedChartData(['hit', 'crit'])
|
|
478
|
-
* // → {labels: [0, 6, 12, ...], datasets: [{label: 'hit', data: [0, 0.03, ...]}, ...]}
|
|
479
|
-
*/
|
|
480
|
-
toStackedChartData(labels?: OutcomeType[], epsilon?: number): {
|
|
481
|
-
labels: number[];
|
|
482
|
-
datasets: Array<{
|
|
483
|
-
label: string;
|
|
484
|
-
data: number[];
|
|
485
|
-
}>;
|
|
486
|
-
};
|
|
487
|
-
/**
|
|
488
|
-
* Returns pure mathematical data for attribution charts showing outcome contributions.
|
|
489
|
-
*
|
|
490
|
-
* Automatically discovers all outcome types present in the PMF, applies filtering rules,
|
|
491
|
-
* and returns proportional data suitable for stacked visualization.
|
|
492
|
-
*
|
|
493
|
-
* @param options Configuration options
|
|
494
|
-
* @param options.stackOrder Preferred order for outcome types (unknowns placed at end)
|
|
495
|
-
* @param options.filterRules Function to determine if outcome should be included for a given damage value
|
|
496
|
-
* @param options.asPercentages Whether to return percentages (0-100) or probabilities (0-1)
|
|
497
|
-
* @returns Pure data structure with support, outcomes, and proportional data
|
|
498
|
-
*
|
|
499
|
-
* @example
|
|
500
|
-
* query.toAttributionChartSeries()
|
|
501
|
-
* // → {support: [0, 6, 12], outcomes: ['hit', 'crit'], data: {hit: [5.2, 8.1, ...], crit: [0, 2.3, ...]}}
|
|
502
|
-
*/
|
|
503
|
-
toAttributionChartSeries(options?: {
|
|
504
|
-
stackOrder?: string[];
|
|
505
|
-
filterRules?: (outcome: string, damage: number) => boolean;
|
|
506
|
-
asPercentages?: boolean;
|
|
507
|
-
}): {
|
|
508
|
-
support: number[];
|
|
509
|
-
outcomes: string[];
|
|
510
|
-
data: {
|
|
511
|
-
[outcome: string]: number[];
|
|
512
|
-
};
|
|
513
|
-
};
|
|
514
|
-
/**
|
|
515
|
-
* Returns pure mathematical data for damage attribution charts showing damage contribution
|
|
516
|
-
* from each outcome type at each damage value.
|
|
517
|
-
*
|
|
518
|
-
* Similar to toAttributionChartSeries() but uses bin.attr (damage attribution) instead of
|
|
519
|
-
* bin.count (probability attribution).
|
|
520
|
-
*
|
|
521
|
-
* @param options Configuration options
|
|
522
|
-
* @param options.stackOrder Preferred order for outcome types (unknowns placed at end)
|
|
523
|
-
* @param options.filterRules Function to determine if outcome should be included for a given damage value
|
|
524
|
-
* @param options.asPercentages Whether to return percentages (0-100) or raw damage values (0+)
|
|
525
|
-
* @returns Pure data structure with support, outcomes, and damage attribution data
|
|
526
|
-
*
|
|
527
|
-
* @example
|
|
528
|
-
* query.toDamageAttributionChartSeries()
|
|
529
|
-
* // → {support: [0, 6, 12], outcomes: ['hit', 'crit'], data: {hit: [3.2, 5.1, ...], crit: [0, 1.8, ...]}}
|
|
530
|
-
*/
|
|
531
|
-
toDamageAttributionChartSeries(options?: {
|
|
532
|
-
stackOrder?: string[];
|
|
533
|
-
filterRules?: (outcome: string, damage: number) => boolean;
|
|
534
|
-
asPercentages?: boolean;
|
|
535
|
-
}): {
|
|
536
|
-
support: number[];
|
|
537
|
-
outcomes: string[];
|
|
538
|
-
data: {
|
|
539
|
-
[outcome: string]: number[];
|
|
540
|
-
};
|
|
541
|
-
};
|
|
542
|
-
/**
|
|
543
|
-
* Returns pure mathematical data for outcome attribution charts showing which
|
|
544
|
-
* attack outcome combinations can produce each damage value.
|
|
545
|
-
*
|
|
546
|
-
* Unlike toDamageAttributionChartSeries() which tracks damage sources, this tracks
|
|
547
|
-
* outcome combinations - answering "what attack outcomes produced this damage?"
|
|
548
|
-
*
|
|
549
|
-
* @param options Configuration options
|
|
550
|
-
* @param options.stackOrder Preferred order for outcome types (unknowns placed at end)
|
|
551
|
-
* @param options.filterRules Function to determine if outcome should be included for a given damage value
|
|
552
|
-
* @param options.asPercentages Whether to return percentages (0-100) or probabilities (0-1)
|
|
553
|
-
* @returns Pure data structure with support, outcomes, and outcome combination probabilities
|
|
554
|
-
*
|
|
555
|
-
* @example
|
|
556
|
-
* query.toOutcomeAttributionChartSeries()
|
|
557
|
-
* // → {support: [0, 6, 12], outcomes: ['all_miss', 'mixed', 'all_hit'], data: {all_miss: [15, 0, 0], mixed: [60, 80, 20], all_hit: [25, 20, 80]}}
|
|
558
|
-
*/
|
|
559
|
-
toOutcomeAttributionChartSeries(options?: {
|
|
560
|
-
stackOrder?: string[];
|
|
561
|
-
filterRules?: (outcome: string, damage: number) => boolean;
|
|
562
|
-
asPercentages?: boolean;
|
|
563
|
-
}): {
|
|
564
|
-
support: number[];
|
|
565
|
-
outcomes: string[];
|
|
566
|
-
data: {
|
|
567
|
-
[outcome: string]: number[];
|
|
568
|
-
};
|
|
569
|
-
};
|
|
570
|
-
/**
|
|
571
|
-
* Returns pure mathematical data for cumulative distribution function (CDF).
|
|
572
|
-
* Shows P(X ≤ x) - the probability of getting at most x damage.
|
|
573
|
-
*
|
|
574
|
-
* @param asPercentages Whether to return percentages (0-100) or probabilities (0-1)
|
|
575
|
-
* @returns Pure data structure with support and cumulative probabilities
|
|
576
|
-
*
|
|
577
|
-
* @example
|
|
578
|
-
* query.toCDFSeries()
|
|
579
|
-
* // → {support: [0, 6, 12], data: [5.2, 18.3, 45.1]}
|
|
580
|
-
*/
|
|
581
|
-
toCDFSeries(asPercentages?: boolean): {
|
|
582
|
-
support: number[];
|
|
583
|
-
data: number[];
|
|
584
|
-
};
|
|
585
|
-
/**
|
|
586
|
-
* Returns pure mathematical data for complementary cumulative distribution function (CCDF).
|
|
587
|
-
* Shows P(X ≥ x) - the probability of getting at least x damage.
|
|
588
|
-
*
|
|
589
|
-
* @param asPercentages Whether to return percentages (0-100) or probabilities (0-1)
|
|
590
|
-
* @returns Pure data structure with support and complementary cumulative probabilities
|
|
591
|
-
*
|
|
592
|
-
* @example
|
|
593
|
-
* query.toCCDFSeries()
|
|
594
|
-
* // → {support: [0, 6, 12], data: [100, 94.8, 81.7]}
|
|
595
|
-
*/
|
|
596
|
-
toCCDFSeries(asPercentages?: boolean): {
|
|
597
|
-
support: number[];
|
|
598
|
-
data: number[];
|
|
599
|
-
};
|
|
600
|
-
/** Probability of doing strictly more than threshold damage (default >0). */
|
|
601
|
-
probDamageGreaterThan(threshold?: number): number;
|
|
602
|
-
/** All outcome keys actually present (typed & ordered if you pass an order). */
|
|
603
|
-
outcomeKeys(order?: OutcomeType[]): OutcomeType[];
|
|
604
|
-
/** Total probability per outcome across the PMF. */
|
|
605
|
-
outcomeTotals(outcomes?: OutcomeType[]): Map<OutcomeType, number>;
|
|
606
|
-
/** Conditional damage range per outcome (min/avg/max of X | outcome). */
|
|
607
|
-
outcomeDamageRanges(outcomes?: OutcomeType[]): Map<OutcomeType, {
|
|
608
|
-
min: number;
|
|
609
|
-
avg: number;
|
|
610
|
-
max: number;
|
|
611
|
-
}>;
|
|
612
|
-
/**
|
|
613
|
-
* Snapshot of the distribution in the exact shape the UI consumes.
|
|
614
|
-
* - outcome probabilities are "at least one" (and equal to "all" for a single PMF)
|
|
615
|
-
* - damageRange is conditional on the outcome occurring
|
|
616
|
-
*
|
|
617
|
-
* The outcome probabilities use the correct Poisson-binomial marginals
|
|
618
|
-
* (`atLeastOneProbability` = P(≥1 attack has it), `allProbability` = P(all do)),
|
|
619
|
-
* so they are always valid probabilities in [0,1].
|
|
620
|
-
*
|
|
621
|
-
* KNOWN LIMITATION (multi-attack): `damageRange.avg` is still aggregated from
|
|
622
|
-
* the combined PMF's `count`, which the convolution accumulates as an EXPECTED
|
|
623
|
-
* COUNT, so for N≥2 attacks it is the size-biased mean E[dmg·#label]/E[#label]
|
|
624
|
-
* rather than a clean conditional expectation. It is correct for a single
|
|
625
|
-
* attack.
|
|
626
|
-
*/
|
|
627
|
-
snapshot(order?: readonly OutcomeType[]): Snapshot;
|
|
628
|
-
/**
|
|
629
|
-
* PMF Transformation Methods
|
|
630
|
-
*
|
|
631
|
-
* These methods provide a fluent API for transforming dice queries by wrapping
|
|
632
|
-
* the underlying PMF transformation methods. All operations work on the combined
|
|
633
|
-
* PMF and return new DiceQuery instances.
|
|
634
|
-
*/
|
|
635
|
-
/**
|
|
636
|
-
* Returns a new DiceQuery with normalized probabilities (ensuring they sum to 1.0).
|
|
637
|
-
*
|
|
638
|
-
* @returns New DiceQuery with normalized combined PMF
|
|
639
|
-
*/
|
|
640
|
-
normalize(): DiceQuery;
|
|
641
|
-
/**
|
|
642
|
-
* Returns a new DiceQuery with low-probability outcomes removed.
|
|
643
|
-
*
|
|
644
|
-
* @param eps Minimum probability threshold (defaults to PMF epsilon)
|
|
645
|
-
* @param keepFinalBin Whether to keep the highest damage bin regardless of probability
|
|
646
|
-
* @returns New DiceQuery with compacted combined PMF
|
|
647
|
-
*/
|
|
648
|
-
compact(eps?: number, keepFinalBin?: boolean): DiceQuery;
|
|
649
|
-
/**
|
|
650
|
-
* Returns a new DiceQuery with an additional scaled branch added.
|
|
651
|
-
* Useful for conditional outcomes like "30% chance of opportunity attack".
|
|
652
|
-
*
|
|
653
|
-
* @param branch DiceQuery to add as a scaled branch
|
|
654
|
-
* @param probability Probability of the branch occurring (0-1)
|
|
655
|
-
* @returns New DiceQuery combining this query with the scaled branch
|
|
656
|
-
*
|
|
657
|
-
* @example
|
|
658
|
-
* const baseAttack = parse("(d20 + 5 AC 15) * (2d6 + 3)");
|
|
659
|
-
* const opportunityAttack = parse("(d20 + 5 AC 15) * (1d8 + 3)");
|
|
660
|
-
* const withOpportunity = baseAttack.addScaled(opportunityAttack, 0.3);
|
|
661
|
-
*/
|
|
662
|
-
addScaled(branch: DiceQuery, probability: number): DiceQuery;
|
|
663
|
-
/**
|
|
664
|
-
* Returns a new DiceQuery with all probabilities scaled by a factor.
|
|
665
|
-
* Used for conditional scenarios where the entire outcome has reduced probability.
|
|
666
|
-
*
|
|
667
|
-
* @param factor Scaling factor for probabilities
|
|
668
|
-
* @returns New DiceQuery with scaled probabilities
|
|
669
|
-
*
|
|
670
|
-
* @example
|
|
671
|
-
* const fullAttack = parse("(d20 + 5 AC 15) * (2d6 + 3)");
|
|
672
|
-
* const conditionalAttack = fullAttack.scaleMass(0.3); // 30% chance scenario
|
|
673
|
-
*/
|
|
674
|
-
scaleMass(factor: number): DiceQuery;
|
|
675
|
-
totalMass(): number;
|
|
676
|
-
/**
|
|
677
|
-
* Returns a new DiceQuery with damage values transformed by a function.
|
|
678
|
-
* Useful for applying modifiers, resistances, or other damage transformations.
|
|
679
|
-
*
|
|
680
|
-
* @param damageTransformFunction Function to transform each damage value
|
|
681
|
-
* @returns New DiceQuery with transformed damage values
|
|
682
|
-
*
|
|
683
|
-
* @example
|
|
684
|
-
* const baseAttack = parse("2d6 + 3");
|
|
685
|
-
* const withResistance = baseAttack.mapDamage(dmg => Math.floor(dmg / 2)); // Half damage
|
|
686
|
-
* const withBonus = baseAttack.mapDamage(dmg => dmg + 5); // +5 damage
|
|
687
|
-
*/
|
|
688
|
-
mapDamage(damageTransformFunction: (damageValue: number) => number): DiceQuery;
|
|
689
|
-
/**
|
|
690
|
-
* Returns a new DiceQuery with damage values scaled by a factor.
|
|
691
|
-
* Convenient wrapper around mapDamage for multiplicative scaling.
|
|
692
|
-
*
|
|
693
|
-
* @param factor Scaling factor for damage values
|
|
694
|
-
* @param rounding Rounding method: "floor" (default), "round", or "ceil"
|
|
695
|
-
* @returns New DiceQuery with scaled damage values
|
|
696
|
-
*
|
|
697
|
-
* @example
|
|
698
|
-
* const baseAttack = parse("2d6 + 3");
|
|
699
|
-
* const doubled = baseAttack.scaleDamage(2); // Double damage
|
|
700
|
-
* const halfDamage = baseAttack.scaleDamage(0.5, "round"); // Half damage, rounded
|
|
701
|
-
*/
|
|
702
|
-
scaleDamage(factor: number, rounding?: "floor" | "round" | "ceil"): DiceQuery;
|
|
703
|
-
/**
|
|
704
|
-
* Returns a new DiceQuery combining this query with another via convolution.
|
|
705
|
-
* Equivalent to rolling both queries independently and adding results.
|
|
706
|
-
* It is important to use this rather than combing()ing the PMFs directly!
|
|
707
|
-
* This method maintains the provenance of the PMFs which is needed for damage attribution.
|
|
708
|
-
* Combining the .combined PMFs directly is still valid for DPR calculations but
|
|
709
|
-
* is not statistically sound for queries.
|
|
710
|
-
*
|
|
711
|
-
* @param other DiceQuery to combine with
|
|
712
|
-
* @param eps Optional epsilon for precision control
|
|
713
|
-
* @returns New DiceQuery representing the combined outcome
|
|
714
|
-
*
|
|
715
|
-
* @example
|
|
716
|
-
* const mainAttack = parse("(d20 + 5 AC 15) * (2d6 + 3)");
|
|
717
|
-
* const bonusAttack = parse("(d20 + 3 AC 15) * (1d6 + 1)");
|
|
718
|
-
* const bothAttacks = mainAttack.convolve(bonusAttack);
|
|
719
|
-
*/
|
|
720
|
-
convolve(other: DiceQuery): DiceQuery;
|
|
721
|
-
/**
|
|
722
|
-
* First-success split over an ordered list of DISTINCT single-swing PMFs.
|
|
723
|
-
* Each PMF may have different success/subset probabilities (from labels).
|
|
724
|
-
*
|
|
725
|
-
* successOutcome: e.g., ["success"] or ["hit", "crit"]
|
|
726
|
-
* subsetOutcome: e.g., ["subset"] or ["crit"] where subset ⊆ success
|
|
727
|
-
*
|
|
728
|
-
* Returns tuple: [pFirstNonSubset, pFirstSubset, pAnySuccess, pNone]
|
|
729
|
-
*/
|
|
730
|
-
firstSuccessSplit(successOutcome: OutcomeType | OutcomeType[], subsetOutcome: OutcomeType | OutcomeType[], eps?: number): readonly [pSuccess: number, pSubset: number, pAny: number, pNone: number];
|
|
731
|
-
}
|
|
732
|
-
type OutcomeSnapshot = {
|
|
733
|
-
atLeastOneProbability: number;
|
|
734
|
-
allProbability: number;
|
|
735
|
-
damageRange: {
|
|
736
|
-
min: number;
|
|
737
|
-
avg: number;
|
|
738
|
-
max: number;
|
|
739
|
-
};
|
|
740
|
-
};
|
|
741
|
-
type Snapshot = {
|
|
742
|
-
averageDPR: number;
|
|
743
|
-
damageChance: number;
|
|
744
|
-
percentiles: {
|
|
745
|
-
p25: number;
|
|
746
|
-
p50: number;
|
|
747
|
-
p75: number;
|
|
748
|
-
};
|
|
749
|
-
outcomes: Map<OutcomeType, OutcomeSnapshot>;
|
|
750
|
-
};
|
|
751
|
-
|
|
752
|
-
declare const pmfCache: LRUCache<string, PMF>;
|
|
753
|
-
/**
|
|
754
|
-
* Probability Mass Function for discrete damage distributions.
|
|
755
|
-
*/
|
|
756
|
-
declare class PMF {
|
|
757
|
-
readonly map: Map<number, Bin>;
|
|
758
|
-
readonly epsilon: number;
|
|
759
|
-
readonly normalized: boolean;
|
|
760
|
-
readonly identifier: string;
|
|
761
|
-
private _preservedProvenance;
|
|
762
|
-
private static __anonIdCounter;
|
|
763
|
-
private _support?;
|
|
764
|
-
private _min?;
|
|
765
|
-
private _max?;
|
|
766
|
-
private _totalMass?;
|
|
767
|
-
private _mean?;
|
|
768
|
-
private _variance?;
|
|
769
|
-
private _stdev?;
|
|
770
|
-
private _fingerprint?;
|
|
771
|
-
constructor(map?: Map<number, Bin>, epsilon?: number, normalized?: boolean, identifier?: string, _preservedProvenance?: boolean);
|
|
772
|
-
static empty(epsilon?: number, identifier?: string): PMF;
|
|
773
|
-
static zero(epsilon?: number): PMF;
|
|
774
|
-
static delta(value: number, epsilon?: number): PMF;
|
|
775
|
-
/**
|
|
776
|
-
* Point mass at damage 0 tagged with the canonical `missNone` outcome.
|
|
777
|
-
*
|
|
778
|
-
* Differs from {@link PMF.zero}, which labels its zero bin `miss` — the
|
|
779
|
-
* builder's attack-resolution vocabulary. This uses the `missNone`
|
|
780
|
-
* {@link OutcomeType} that the attribution charts and outcome stats key on,
|
|
781
|
-
* so it is the correct "clean miss / no damage" delta for provenance-aware
|
|
782
|
-
* mixtures feeding those consumers.
|
|
783
|
-
*/
|
|
784
|
-
static missNone(epsilon?: number): PMF;
|
|
785
|
-
static emptyMass(): PMF;
|
|
786
|
-
[Symbol.iterator](): IterableIterator<[number, Bin]>;
|
|
787
|
-
static clearCache(): void;
|
|
788
|
-
/**
|
|
789
|
-
* Creates a conditional PMF from two branches (success and failure) and a probability.
|
|
790
|
-
* This is the core logic for modeling any probabilistic event where there are two
|
|
791
|
-
* distinct outcomes.
|
|
792
|
-
*/
|
|
793
|
-
static branch(successPMF: PMF, failurePMF: PMF, successProbability: number): PMF;
|
|
794
|
-
/**
|
|
795
|
-
* withProbability()
|
|
796
|
-
*
|
|
797
|
-
* A convenience wrapper around branch() for the common case where the "failure" branch is always zero().
|
|
798
|
-
*
|
|
799
|
-
* Think of this as a shortcut for:
|
|
800
|
-
* pmf.gate(p, PMF.zero())
|
|
801
|
-
*
|
|
802
|
-
* Use this to model a *single* Bernoulli event — an outcome that either happens or doesn't,
|
|
803
|
-
* like an opportunity attack that occurs with probability p, or a single attack that either hits or misses.
|
|
804
|
-
*
|
|
805
|
-
* This is **not** for combining multiple independent attacks or mutually exclusive multi-outcome scenarios.
|
|
806
|
-
* - For multiple independent swings, use DiceQuery with separate PMFs for each attack.
|
|
807
|
-
* - For modeling "first success" logic across multiple attacks (like Sneak Attack or Smite)
|
|
808
|
-
* use query.firstSuccessSplit() to get the exact probabilities.
|
|
809
|
-
* - For scenarios with several mutually exclusive outcomes (like crit vs hit vs none), use PMF.exclusive().
|
|
810
|
-
*
|
|
811
|
-
*/
|
|
812
|
-
static withProbability(successPMF: PMF, probability: number): PMF;
|
|
813
|
-
/**
|
|
814
|
-
* gate()
|
|
815
|
-
*
|
|
816
|
-
* A conditional wrapper around branch() that applies this PMF with probability `p`,
|
|
817
|
-
* and applies a provided fallback PMF otherwise.
|
|
818
|
-
*
|
|
819
|
-
* This is useful for modeling a binary choice between two outcomes:
|
|
820
|
-
* - The "success" outcome (this PMF) happens with probability `p`.
|
|
821
|
-
* - The "failure" outcome (fallback PMF) happens with probability `1 - p`.
|
|
822
|
-
*
|
|
823
|
-
* Examples:
|
|
824
|
-
* - 25% chance to include an opportunity attack, otherwise nothing:
|
|
825
|
-
* attackPMF.gate(0.25, PMF.zero())
|
|
826
|
-
*
|
|
827
|
-
* - 50% chance to deal fireball damage, otherwise cone of cold damage:
|
|
828
|
-
* fireballPMF.gate(0.5, coneOfColdPMF)
|
|
829
|
-
*
|
|
830
|
-
* Relationship to other helpers:
|
|
831
|
-
* - **withProbability()** is a shortcut for the common case where the fallback is `PMF.zero()`.
|
|
832
|
-
* - **exclusive()** is for three or more mutually exclusive outcomes (e.g., crit vs hit vs none).
|
|
833
|
-
*
|
|
834
|
-
* @param p Probability of applying this PMF (between 0 and 1).
|
|
835
|
-
* @param fallback PMF to apply when this PMF is *not* selected.
|
|
836
|
-
* @returns A new PMF representing the weighted mixture of this PMF and the fallback.
|
|
837
|
-
*/
|
|
838
|
-
gate(p: number, fallback: PMF): PMF;
|
|
839
|
-
/**
|
|
840
|
-
* PMF.exclusive()
|
|
841
|
-
*
|
|
842
|
-
* Builds a single PMF from a set of mutually exclusive weighted outcomes.
|
|
843
|
-
* Exactly one of the provided options will occur.
|
|
844
|
-
*
|
|
845
|
-
* Each option has:
|
|
846
|
-
* - A PMF representing its outcome (e.g., damage dice).
|
|
847
|
-
* - A weight representing its probability of being selected.
|
|
848
|
-
*
|
|
849
|
-
* Notes:
|
|
850
|
-
* - If total weight < 1 (within eps), leftover mass is assumed to be PMF.zero()
|
|
851
|
-
*
|
|
852
|
-
* @param options Array of `{ pmf, weight }` or `[PMF, number]`.
|
|
853
|
-
* @param eps Optional tolerance for floating point rounding.
|
|
854
|
-
*/
|
|
855
|
-
static exclusive(options: Array<{
|
|
856
|
-
pmf: PMF;
|
|
857
|
-
weight: number;
|
|
858
|
-
} | [PMF, number]>, eps?: number): PMF;
|
|
859
|
-
/**
|
|
860
|
-
* PMF.mix()
|
|
861
|
-
*
|
|
862
|
-
* Builds a PMF as a linear combination of input PMFs with the given weights.
|
|
863
|
-
* Unlike `exclusive`, this does NOT:
|
|
864
|
-
* - enforce that weights sum to 1
|
|
865
|
-
* - add leftover probability to δ0 (PMF.zero())
|
|
866
|
-
*
|
|
867
|
-
* Use when outcomes are not mutually exclusive, or for interpolation/blending.
|
|
868
|
-
*
|
|
869
|
-
* @param options Array of `{ pmf, weight }` or `[PMF, number]`.
|
|
870
|
-
* @param eps Optional tolerance for skipping tiny weights.
|
|
871
|
-
*/
|
|
872
|
-
static mix(options: Array<{
|
|
873
|
-
pmf: PMF;
|
|
874
|
-
weight: number;
|
|
875
|
-
} | [PMF, number]>, eps?: number): PMF;
|
|
876
|
-
/**
|
|
877
|
-
* Adds damage attribution metadata to this PMF based on existing count metadata.
|
|
878
|
-
* For each bin, sets attr[outcome] = damage × count[outcome].
|
|
879
|
-
*
|
|
880
|
-
* This enables damage attribution charts to work with builder-generated PMFs.
|
|
881
|
-
* The parser generates attr automatically, but builder PMFs only have count.
|
|
882
|
-
*
|
|
883
|
-
* @returns New PMF with attr field populated in each bin
|
|
884
|
-
*/
|
|
885
|
-
/**
|
|
886
|
-
* Returns true if this PMF already carries damage attribution metadata.
|
|
887
|
-
*
|
|
888
|
-
* Only the first positive-damage bin is inspected (parser-generated PMFs
|
|
889
|
-
* populate `attr` uniformly), so this is O(1) in practice.
|
|
890
|
-
*/
|
|
891
|
-
hasAttribution(): boolean;
|
|
892
|
-
withAttribution(): PMF;
|
|
893
|
-
/**
|
|
894
|
-
* General-purpose N-way mixture.
|
|
895
|
-
* weights: Array of [weight, PMF].
|
|
896
|
-
*
|
|
897
|
-
* Example: PMF.mixN([
|
|
898
|
-
* [pMiss, zero],
|
|
899
|
-
* [pHit, hitPMF],
|
|
900
|
-
* [pCrit, critPMF],
|
|
901
|
-
* ]);
|
|
902
|
-
*/
|
|
903
|
-
static mixN(weights: [number, PMF][], eps?: number): PMF;
|
|
904
|
-
private setPreservedProvenance;
|
|
905
|
-
preservedProvenance(): boolean;
|
|
906
|
-
private getPowerCacheKey;
|
|
907
|
-
/**
|
|
908
|
-
* Efficiently computes this PMF convolved with itself `n` times.
|
|
909
|
-
* Uses exponentiation by squaring to reduce total convolutions.
|
|
910
|
-
* n must be a positive integer.
|
|
911
|
-
* *
|
|
912
|
-
* * NOTE: This folds multiple independent attacks into a single PMF.
|
|
913
|
-
* As a result, The power() method causes a loss of data provenance.
|
|
914
|
-
* This is ONLY SAFE if you are trying to calculate masses.
|
|
915
|
-
* If you want to query any atLeast probabilities, you should use the DiceQuery class instead without power().
|
|
916
|
-
*/
|
|
917
|
-
power(n: number, eps?: number): PMF;
|
|
918
|
-
replicate(n: number): PMF[];
|
|
919
|
-
mass(): number;
|
|
920
|
-
outcomeMass(outcome: string): number;
|
|
921
|
-
faceTotal(): number;
|
|
922
|
-
normalize(): PMF;
|
|
923
|
-
/**
|
|
924
|
-
* Returns a copy with negligible probabilities removed (p < eps).
|
|
925
|
-
* If keepFinalBin is true, the bin with the largest key is always kept,
|
|
926
|
-
* even if its probability is below eps. count/attr submaps are still cleaned.
|
|
927
|
-
*/
|
|
928
|
-
compact(eps?: number, keepFinalBin?: boolean): PMF;
|
|
929
|
-
support(): number[];
|
|
930
|
-
min(): number;
|
|
931
|
-
max(): number;
|
|
932
|
-
/**
|
|
933
|
-
* Returns the expected (mean) damage value.
|
|
934
|
-
* Cached for performance since this requires iterating through all bins.
|
|
935
|
-
*/
|
|
936
|
-
mean(): number;
|
|
937
|
-
/**
|
|
938
|
-
* Returns the variance of the damage distribution.
|
|
939
|
-
* Cached for performance since this requires mean calculation plus iteration.
|
|
940
|
-
*/
|
|
941
|
-
variance(): number;
|
|
942
|
-
/**
|
|
943
|
-
* Returns the standard deviation of the damage distribution.
|
|
944
|
-
*/
|
|
945
|
-
stdev(): number;
|
|
946
|
-
/** Deep-copies a Bin, cloning its count and (optional) attr maps. */
|
|
947
|
-
private static cloneBin;
|
|
948
|
-
/** Returns a new Bin with p, count, and attr all multiplied by `factor`. */
|
|
949
|
-
private static scaleBin;
|
|
950
|
-
private static mergeInto;
|
|
951
|
-
add(other: PMF): PMF;
|
|
952
|
-
/**
|
|
953
|
-
* Returns a new PMF with a scaled branch added to this one.
|
|
954
|
-
* The branch PMF is scaled by the given probability before merging
|
|
955
|
-
* This will be very useful for conditional effects and for being
|
|
956
|
-
* able to model "I can probably have this opportunity attack 40% of rounds"
|
|
957
|
-
* Example: `pmf.addScaled(critBranch, 0.05)` → PMF including 5% crit outcomes
|
|
958
|
-
*/
|
|
959
|
-
addScaled(branch: PMF, probability: number): PMF;
|
|
960
|
-
/**
|
|
961
|
-
* Redistributes probability mass to model an effect that only occurs with
|
|
962
|
-
* probability `frequency` — a conditional attack, an on-hit rider, or a
|
|
963
|
-
* sub-one AoE target fraction.
|
|
964
|
-
*
|
|
965
|
-
* Every hit outcome (damage > 0) is scaled by `frequency` — probability mass,
|
|
966
|
-
* per-label `count`, AND per-label `attr` — and the freed mass is moved into
|
|
967
|
-
* the miss bin at damage 0, tagged with the canonical `missNone` outcome.
|
|
968
|
-
* Total probability mass is preserved.
|
|
969
|
-
*
|
|
970
|
-
* Unlike a bare {@link scaleMass} or {@link mapDamage}, this keeps damage
|
|
971
|
-
* attribution (`attr`) intact, so a frequency-scaled PMF still renders
|
|
972
|
-
* correctly in the damage-attribution charts.
|
|
973
|
-
*
|
|
974
|
-
* `frequency >= 1` (or non-finite) returns this PMF unchanged; `frequency <= 0`
|
|
975
|
-
* collapses all mass into the miss bin. The miss outcome is assumed to be
|
|
976
|
-
* encoded at damage value 0.
|
|
977
|
-
*
|
|
978
|
-
* @param frequency Probability in [0, 1] that the effect occurs.
|
|
979
|
-
*/
|
|
980
|
-
applyHitFrequency(frequency: number): PMF;
|
|
981
|
-
scaleMass(factor: number): PMF;
|
|
982
|
-
mapDamage(damageTransformFunction: (damageValue: number) => number): PMF;
|
|
983
|
-
scaleDamage(factor: number, rounding?: "floor" | "round" | "ceil"): PMF;
|
|
984
|
-
private getPMFCombineCacheKey;
|
|
985
|
-
/**
|
|
986
|
-
* A small content fingerprint (mass + bin count + face sum) so convolution
|
|
987
|
-
* cache keys change if the underlying numbers do. Memoized because a PMF is
|
|
988
|
-
* immutable once constructed — this avoids re-summing every key on each
|
|
989
|
-
* convolve() call (including cache hits).
|
|
990
|
-
*/
|
|
991
|
-
fingerprint(): string;
|
|
992
|
-
convolve(other: PMF, eps?: number, raw?: boolean): PMF;
|
|
993
|
-
combineRaw(other: PMF, eps?: number): PMF;
|
|
994
|
-
private static reduceConvolveLeft;
|
|
995
|
-
/**
|
|
996
|
-
* Convolves multiple PMFs using linear convolution with automatic caching.
|
|
997
|
-
* Uses a left-to-right accumulation approach for maximum cache reuse.
|
|
998
|
-
* Each convolve() call automatically uses the convolution cache for performance.
|
|
999
|
-
*
|
|
1000
|
-
* This linear approach provides better cache hits than pairwise because:
|
|
1001
|
-
* - Intermediate results are more predictable and stable
|
|
1002
|
-
* - Similar PMF lists share common prefixes (A+B, (A+B)+C, etc.)
|
|
1003
|
-
* - Order-independent cache keys work better with consistent build patterns
|
|
1004
|
-
*/
|
|
1005
|
-
static convolveMany(pmfList: PMF[], eps?: number): PMF;
|
|
1006
|
-
/**
|
|
1007
|
-
* Returns a plain, JSON-serializable representation of this PMF.
|
|
1008
|
-
*
|
|
1009
|
-
* Follows the standard `toJSON` contract, so `JSON.stringify(pmf)` produces
|
|
1010
|
-
* the expected output (no double-encoding). Use {@link PMF.fromJSON} to
|
|
1011
|
-
* reconstruct, or {@link PMF.toJSONString} if you need the string directly.
|
|
1012
|
-
*/
|
|
1013
|
-
toJSON(): {
|
|
1014
|
-
bins: Array<[number, Bin]>;
|
|
1015
|
-
normalized: boolean;
|
|
1016
|
-
identifier: string;
|
|
1017
|
-
};
|
|
1018
|
-
/** Serializes this PMF to a JSON string (equivalent to `JSON.stringify(pmf)`). */
|
|
1019
|
-
toJSONString(): string;
|
|
1020
|
-
static fromJSON(jsonData: {
|
|
1021
|
-
bins: Array<[number, Bin]>;
|
|
1022
|
-
normalized?: boolean;
|
|
1023
|
-
identifier?: string;
|
|
1024
|
-
}): PMF;
|
|
1025
|
-
/**
|
|
1026
|
-
* Relative pruning with optional top-K floor.
|
|
1027
|
-
* Keeps bins with p >= epsRel * peak, always keeps min and max damage,
|
|
1028
|
-
* optionally guarantees at least `minBins` survivors by adding top-K.
|
|
1029
|
-
* Returns a new, non-normalized PMF.
|
|
1030
|
-
*/
|
|
1031
|
-
prune(epsRel: number, minBins?: number): PMF;
|
|
1032
|
-
/** Probability mass at exactly x. */
|
|
1033
|
-
pAt(x: number): number;
|
|
1034
|
-
/**
|
|
1035
|
-
* P(any damage) — the mass on all non-zero outcomes, i.e. `1 - P(0)`.
|
|
1036
|
-
* Assumes a miss is encoded as the damage-0 bin (the convention used across
|
|
1037
|
-
* attack/save PMFs). The dual of {@link missProbability}.
|
|
1038
|
-
*/
|
|
1039
|
-
hitProbability(): number;
|
|
1040
|
-
/** P(no damage) — the mass at damage 0. The dual of {@link hitProbability}. */
|
|
1041
|
-
missProbability(): number;
|
|
1042
|
-
/**
|
|
1043
|
-
* Coarsen the distribution into at most `maxBuckets` contiguous, equal-width
|
|
1044
|
-
* damage buckets, aggregating probability mass (and `count`/`attr`
|
|
1045
|
-
* provenance) into each bucket's start value. Returns this PMF unchanged when
|
|
1046
|
-
* its integer support already fits within `maxBuckets`.
|
|
1047
|
-
*
|
|
1048
|
-
* This is a lossy display/downsampling transform (bucket start replaces the
|
|
1049
|
-
* exact damage value) — use it for charting wide distributions, not for DPR
|
|
1050
|
-
* math.
|
|
1051
|
-
*/
|
|
1052
|
-
rebin(maxBuckets: number): PMF;
|
|
1053
|
-
/** Dense integer support from min..max (inclusive).
|
|
1054
|
-
* Useful for showing empty bars in charts.
|
|
1055
|
-
*/
|
|
1056
|
-
denseSupport(): number[];
|
|
1057
|
-
/** CDF at x: P(X ≤ x). */
|
|
1058
|
-
cdfAt(x: number): number;
|
|
1059
|
-
/** Quantile / inverse CDF for p in [0,1]. Returns smallest x with CDF ≥ p. */
|
|
1060
|
-
quantile(p: number): number;
|
|
1061
|
-
/** Get outcome probability at specific damage value. */
|
|
1062
|
-
outcomeAt(damage: number, outcome: string): number;
|
|
1063
|
-
/** Get all outcome types present in this PMF. */
|
|
1064
|
-
outcomes(): string[];
|
|
1065
|
-
/** Get total probability of an outcome across all damage values. */
|
|
1066
|
-
outcomeProbability(outcome: string): number;
|
|
1067
|
-
/** Get damage attribution for an outcome at specific damage value. */
|
|
1068
|
-
outcomeAttributionAt(damage: number, outcome: string): number;
|
|
1069
|
-
/** Get all outcome data at specific damage value. */
|
|
1070
|
-
binAt(damage: number): {
|
|
1071
|
-
p: number;
|
|
1072
|
-
count: Record<string, number>;
|
|
1073
|
-
attr?: Record<string, number>;
|
|
1074
|
-
} | null;
|
|
1075
|
-
/** Check if outcome exists in this PMF. */
|
|
1076
|
-
hasOutcome(outcome: string): boolean;
|
|
1077
|
-
/**
|
|
1078
|
-
* Split each damage value's probability mass across outcome labels, returning
|
|
1079
|
-
* per-label maps of `damage value → probability mass attributable to that
|
|
1080
|
-
* label`. Summing over labels at a given value recovers that value's `p`.
|
|
1081
|
-
*
|
|
1082
|
-
* Damage-bearing bins are split by `attr` weight (the share of damage each
|
|
1083
|
-
* outcome contributed); the clean-miss bin at 0 is split by `count` weight
|
|
1084
|
-
* (there is no damage to attribute). Attribution is computed on demand via
|
|
1085
|
-
* {@link withAttribution} when absent, so builder-generated PMFs work too.
|
|
1086
|
-
*
|
|
1087
|
-
* This is the provenance core of the stacked damage-attribution chart — the
|
|
1088
|
-
* caller only maps these series into its rendering format (colors, binning,
|
|
1089
|
-
* axis labels).
|
|
1090
|
-
*/
|
|
1091
|
-
attributionByValue(): Map<string, Map<number, number>>;
|
|
1092
|
-
tailProbGE(t: number): number;
|
|
1093
|
-
tailProbGT(t: number): number;
|
|
1094
|
-
/**
|
|
1095
|
-
* Returns a new PMF containing only bins where the specified outcome has non-zero probability.
|
|
1096
|
-
* This creates a marginal distribution for the given outcome type, with probabilities
|
|
1097
|
-
* scaled to represent the unconditional mass attributable to that outcome.
|
|
1098
|
-
*/
|
|
1099
|
-
filterOutcome(outcome: string): PMF;
|
|
1100
|
-
/**
|
|
1101
|
-
* Calculates probabilities for first-success outcomes across n independent attempts.
|
|
1102
|
-
*
|
|
1103
|
-
* @param pSuccess - Total probability of any success on a single attempt.
|
|
1104
|
-
* @param pSpecial - Probability of a specific subset of successes (e.g., critical success).
|
|
1105
|
-
* @param n - Number of independent attempts.
|
|
1106
|
-
*
|
|
1107
|
-
* Returns:
|
|
1108
|
-
* - pSpecificSuccess: Probability that the first success was of the "special" type
|
|
1109
|
-
* - pGeneralSuccess: Probability that the first success was of the non-special type
|
|
1110
|
-
* - pNone: Probability that no successes occurred
|
|
1111
|
-
* - pAny: Probability that at least one success occurred
|
|
1112
|
-
*/
|
|
1113
|
-
static firstSuccessWeights(pSuccess: number, pSpecial: number, n: number): {
|
|
1114
|
-
pSpecificSuccess: number;
|
|
1115
|
-
pGeneralSuccess: number;
|
|
1116
|
-
pNone: number;
|
|
1117
|
-
pAny: number;
|
|
1118
|
-
};
|
|
1119
|
-
mapValues(f: (v: number) => number, eps?: number, opts?: {
|
|
1120
|
-
rounding?: Rounding;
|
|
1121
|
-
preserveCounts?: boolean;
|
|
1122
|
-
}): PMF;
|
|
1123
|
-
static fromMap(m: Map<number, number>, eps?: number, { requireIntegerValues }?: {
|
|
1124
|
-
requireIntegerValues?: boolean;
|
|
1125
|
-
}): PMF;
|
|
1126
|
-
query(): DiceQuery;
|
|
1127
|
-
}
|
|
1128
|
-
|
|
1129
|
-
export { ALL_OUTCOME_TYPES as A, type Bin as B, type CritConfig as C, type DamageDistribution as D, EPS as E, LRUCache as L, MISS_NONE_OUTCOME as M, type OutcomeLabelMap as O, PMF as P, type Rounding as R, type Snapshot as S, type OutcomeType as a, type RollType as b, critProbability as c, OUTCOME_DISPLAY_ORDER as d, onCritOnly as e, onHitOnly as f, onMissOnly as g, onMissDamageOnly as h, onSaveHalfOnly as i, onSaveFailOnly as j, onPotentCantripOnly as k, DiceQuery as l, type OutcomeSnapshot as m, onAnyHit as o, pmfCache as p, sortOutcomes as s };
|