@swrpg-online/dice 0.9.0 → 1.0.4

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/src/pools.ts DELETED
@@ -1,56 +0,0 @@
1
- import { DicePool } from './types';
2
-
3
- /**
4
- * Creates a basic skill check dice pool
5
- * @param ability Number of ability (green) dice
6
- * @param proficiency Number of proficiency (yellow) dice
7
- * @returns DicePool configured for a basic skill check
8
- */
9
- export const createSkillCheck = (ability: number, proficiency: number): DicePool => ({
10
- abilityDice: Math.max(0, ability),
11
- proficiencyDice: Math.max(0, proficiency)
12
- });
13
-
14
- /**
15
- * Creates a combat check dice pool with optional boost die
16
- * @param ability Number of ability (green) dice
17
- * @param proficiency Number of proficiency (yellow) dice
18
- * @param boost Number of boost (blue) dice
19
- * @returns DicePool configured for a combat check
20
- */
21
- export const createCombatCheck = (ability: number, proficiency: number, boost: number = 0): DicePool => ({
22
- abilityDice: Math.max(0, ability),
23
- proficiencyDice: Math.max(0, proficiency),
24
- boostDice: Math.max(0, boost)
25
- });
26
-
27
- /**
28
- * Creates an opposed check dice pool
29
- * @param ability Number of ability (green) dice
30
- * @param proficiency Number of proficiency (yellow) dice
31
- * @param difficulty Number of difficulty (purple) dice
32
- * @param challenge Number of challenge (red) dice
33
- * @returns DicePool configured for an opposed check
34
- */
35
- export const createOpposedCheck = (
36
- ability: number,
37
- proficiency: number,
38
- difficulty: number,
39
- challenge: number = 0
40
- ): DicePool => ({
41
- abilityDice: Math.max(0, ability),
42
- proficiencyDice: Math.max(0, proficiency),
43
- difficultyDice: Math.max(0, difficulty),
44
- challengeDice: Math.max(0, challenge)
45
- });
46
-
47
- /**
48
- * Creates a difficulty check dice pool
49
- * @param difficulty Number of difficulty (purple) dice
50
- * @param challenge Number of challenge (red) dice
51
- * @returns DicePool configured for a pure difficulty check
52
- */
53
- export const createDifficultyPool = (difficulty: number, challenge: number = 0): DicePool => ({
54
- difficultyDice: Math.max(0, difficulty),
55
- challengeDice: Math.max(0, challenge)
56
- });
package/src/types.ts DELETED
@@ -1,30 +0,0 @@
1
- export type DicePool = {
2
- boostDice?: number;
3
- abilityDice?: number;
4
- proficiencyDice?: number;
5
- setBackDice?: number;
6
- difficultyDice?: number;
7
- challengeDice?: number;
8
- };
9
-
10
- export type DiceResult = {
11
- successes: number;
12
- failures: number;
13
- advantages: number;
14
- threats: number;
15
- triumphs: number;
16
- despair: number;
17
- };
18
-
19
- export type DieType = 'boost' | 'ability' | 'proficiency' | 'setback' | 'difficulty' | 'challenge';
20
-
21
- export type DetailedDieResult = {
22
- type: DieType;
23
- roll: number;
24
- result: DiceResult;
25
- };
26
-
27
- export type RollResult = {
28
- results: DetailedDieResult[];
29
- summary: DiceResult;
30
- };
@@ -1,351 +0,0 @@
1
- import { roll } from '../src/dice';
2
- import { DicePool, RollResult } from '../src/types';
3
-
4
- // Mock Math.random for deterministic tests
5
- const mockMathRandom = (value: number) => {
6
- const originalRandom = Math.random;
7
- Math.random = jest.fn().mockReturnValue(value);
8
- return () => {
9
- Math.random = originalRandom;
10
- };
11
- };
12
-
13
- describe('SWRPG Dice Rolling', () => {
14
- afterEach(() => {
15
- jest.restoreAllMocks();
16
- });
17
-
18
- describe('Detailed Roll Breakdown', () => {
19
- test('returns detailed results for each die', () => {
20
- const cleanup = mockMathRandom(0.5); // mid-range roll
21
- const pool: DicePool = {
22
- boostDice: 1,
23
- abilityDice: 1
24
- };
25
-
26
- const result = roll(pool);
27
-
28
- expect(result).toHaveProperty('results');
29
- expect(result).toHaveProperty('summary');
30
- expect(Array.isArray(result.results)).toBe(true);
31
- expect(result.results).toHaveLength(2);
32
-
33
- // Check structure of detailed results
34
- result.results.forEach(dieResult => {
35
- expect(dieResult).toHaveProperty('type');
36
- expect(dieResult).toHaveProperty('roll');
37
- expect(dieResult).toHaveProperty('result');
38
- expect(['boost', 'ability']).toContain(dieResult.type);
39
- expect(typeof dieResult.roll).toBe('number');
40
- });
41
-
42
- cleanup();
43
- });
44
-
45
- test('properly identifies die types', () => {
46
- const cleanup = mockMathRandom(0.5);
47
- const pool: DicePool = {
48
- boostDice: 1,
49
- abilityDice: 1,
50
- proficiencyDice: 1,
51
- setBackDice: 1,
52
- difficultyDice: 1,
53
- challengeDice: 1
54
- };
55
-
56
- const result = roll(pool);
57
- const dieTypes = result.results.map(r => r.type);
58
-
59
- expect(dieTypes).toContain('boost');
60
- expect(dieTypes).toContain('ability');
61
- expect(dieTypes).toContain('proficiency');
62
- expect(dieTypes).toContain('setback');
63
- expect(dieTypes).toContain('difficulty');
64
- expect(dieTypes).toContain('challenge');
65
-
66
- cleanup();
67
- });
68
- });
69
-
70
- describe('Individual Die Results', () => {
71
- describe('Boost Die (d6)', () => {
72
- test.each([
73
- [1, { successes: 0, failures: 0, advantages: 0, threats: 0, triumphs: 0, despair: 0 }],
74
- [2, { successes: 0, failures: 0, advantages: 0, threats: 0, triumphs: 0, despair: 0 }],
75
- [3, { successes: 1, failures: 0, advantages: 0, threats: 0, triumphs: 0, despair: 0 }],
76
- [4, { successes: 1, failures: 0, advantages: 1, threats: 0, triumphs: 0, despair: 0 }],
77
- [5, { successes: 0, failures: 0, advantages: 2, threats: 0, triumphs: 0, despair: 0 }],
78
- [6, { successes: 0, failures: 0, advantages: 1, threats: 0, triumphs: 0, despair: 0 }]
79
- ])('face value %i should return correct results', (value, expected) => {
80
- const cleanup = mockMathRandom((value - 1) / 6);
81
- const pool: DicePool = { boostDice: 1 };
82
- expect(roll(pool).summary).toEqual(expected);
83
- cleanup();
84
- });
85
- });
86
-
87
- describe('Ability Die (d8)', () => {
88
- test.each([
89
- [1, { successes: 0, failures: 0, advantages: 0, threats: 0, triumphs: 0, despair: 0 }],
90
- [2, { successes: 1, failures: 0, advantages: 0, threats: 0, triumphs: 0, despair: 0 }],
91
- [3, { successes: 1, failures: 0, advantages: 0, threats: 0, triumphs: 0, despair: 0 }],
92
- [4, { successes: 2, failures: 0, advantages: 0, threats: 0, triumphs: 0, despair: 0 }],
93
- [5, { successes: 0, failures: 0, advantages: 1, threats: 0, triumphs: 0, despair: 0 }],
94
- [6, { successes: 0, failures: 0, advantages: 1, threats: 0, triumphs: 0, despair: 0 }],
95
- [7, { successes: 1, failures: 0, advantages: 1, threats: 0, triumphs: 0, despair: 0 }],
96
- [8, { successes: 0, failures: 0, advantages: 2, threats: 0, triumphs: 0, despair: 0 }]
97
- ])('face value %i should return correct results', (value, expected) => {
98
- const cleanup = mockMathRandom((value - 1) / 8);
99
- const pool: DicePool = { abilityDice: 1 };
100
- expect(roll(pool).summary).toEqual(expected);
101
- cleanup();
102
- });
103
- });
104
-
105
- describe('Proficiency Die (d12)', () => {
106
- test.each([
107
- [1, { successes: 0, failures: 0, advantages: 0, threats: 0, triumphs: 0, despair: 0 }],
108
- [2, { successes: 1, failures: 0, advantages: 0, threats: 0, triumphs: 0, despair: 0 }],
109
- [3, { successes: 1, failures: 0, advantages: 0, threats: 0, triumphs: 0, despair: 0 }],
110
- [4, { successes: 2, failures: 0, advantages: 0, threats: 0, triumphs: 0, despair: 0 }],
111
- [5, { successes: 2, failures: 0, advantages: 0, threats: 0, triumphs: 0, despair: 0 }],
112
- [6, { successes: 0, failures: 0, advantages: 1, threats: 0, triumphs: 0, despair: 0 }],
113
- [7, { successes: 1, failures: 0, advantages: 1, threats: 0, triumphs: 0, despair: 0 }],
114
- [8, { successes: 1, failures: 0, advantages: 1, threats: 0, triumphs: 0, despair: 0 }],
115
- [9, { successes: 1, failures: 0, advantages: 1, threats: 0, triumphs: 0, despair: 0 }],
116
- [10, { successes: 0, failures: 0, advantages: 2, threats: 0, triumphs: 0, despair: 0 }],
117
- [11, { successes: 0, failures: 0, advantages: 2, threats: 0, triumphs: 0, despair: 0 }],
118
- [12, { successes: 0, failures: 0, advantages: 0, threats: 0, triumphs: 1, despair: 0 }]
119
- ])('face value %i should return correct results', (value, expected) => {
120
- const cleanup = mockMathRandom((value - 1) / 12);
121
- const pool: DicePool = { proficiencyDice: 1 };
122
- expect(roll(pool).summary).toEqual(expected);
123
- cleanup();
124
- });
125
- });
126
-
127
- describe('Setback Die (d6)', () => {
128
- test.each([
129
- [1, { successes: 0, failures: 0, advantages: 0, threats: 0, triumphs: 0, despair: 0 }],
130
- [2, { successes: 0, failures: 0, advantages: 0, threats: 0, triumphs: 0, despair: 0 }],
131
- [3, { successes: 0, failures: 1, advantages: 0, threats: 0, triumphs: 0, despair: 0 }],
132
- [4, { successes: 0, failures: 1, advantages: 0, threats: 0, triumphs: 0, despair: 0 }],
133
- [5, { successes: 0, failures: 0, advantages: 0, threats: 1, triumphs: 0, despair: 0 }],
134
- [6, { successes: 0, failures: 0, advantages: 0, threats: 1, triumphs: 0, despair: 0 }]
135
- ])('face value %i should return correct results', (value, expected) => {
136
- const cleanup = mockMathRandom((value - 1) / 6);
137
- const pool: DicePool = { setBackDice: 1 };
138
- expect(roll(pool).summary).toEqual(expected);
139
- cleanup();
140
- });
141
- });
142
-
143
- describe('Difficulty Die (d8)', () => {
144
- test.each([
145
- [1, { successes: 0, failures: 0, advantages: 0, threats: 0, triumphs: 0, despair: 0 }],
146
- [2, { successes: 0, failures: 1, advantages: 0, threats: 0, triumphs: 0, despair: 0 }],
147
- [3, { successes: 0, failures: 2, advantages: 0, threats: 0, triumphs: 0, despair: 0 }],
148
- [4, { successes: 0, failures: 0, advantages: 0, threats: 1, triumphs: 0, despair: 0 }],
149
- [5, { successes: 0, failures: 0, advantages: 0, threats: 1, triumphs: 0, despair: 0 }],
150
- [6, { successes: 0, failures: 0, advantages: 0, threats: 1, triumphs: 0, despair: 0 }],
151
- [7, { successes: 0, failures: 0, advantages: 0, threats: 2, triumphs: 0, despair: 0 }],
152
- [8, { successes: 0, failures: 1, advantages: 0, threats: 1, triumphs: 0, despair: 0 }]
153
- ])('face value %i should return correct results', (value, expected) => {
154
- const cleanup = mockMathRandom((value - 1) / 8);
155
- const pool: DicePool = { difficultyDice: 1 };
156
- expect(roll(pool).summary).toEqual(expected);
157
- cleanup();
158
- });
159
- });
160
-
161
- describe('Challenge Die (d12)', () => {
162
- test.each([
163
- [1, { successes: 0, failures: 0, advantages: 0, threats: 0, triumphs: 0, despair: 0 }],
164
- [2, { successes: 0, failures: 1, advantages: 0, threats: 0, triumphs: 0, despair: 0 }],
165
- [3, { successes: 0, failures: 1, advantages: 0, threats: 0, triumphs: 0, despair: 0 }],
166
- [4, { successes: 0, failures: 2, advantages: 0, threats: 0, triumphs: 0, despair: 0 }],
167
- [5, { successes: 0, failures: 2, advantages: 0, threats: 0, triumphs: 0, despair: 0 }],
168
- [6, { successes: 0, failures: 0, advantages: 0, threats: 1, triumphs: 0, despair: 0 }],
169
- [7, { successes: 0, failures: 0, advantages: 0, threats: 1, triumphs: 0, despair: 0 }],
170
- [8, { successes: 0, failures: 1, advantages: 0, threats: 1, triumphs: 0, despair: 0 }],
171
- [9, { successes: 0, failures: 1, advantages: 0, threats: 1, triumphs: 0, despair: 0 }],
172
- [10, { successes: 0, failures: 0, advantages: 0, threats: 2, triumphs: 0, despair: 0 }],
173
- [11, { successes: 0, failures: 0, advantages: 0, threats: 2, triumphs: 0, despair: 0 }],
174
- [12, { successes: 0, failures: 0, advantages: 0, threats: 0, triumphs: 0, despair: 1 }]
175
- ])('face value %i should return correct results', (value, expected) => {
176
- const cleanup = mockMathRandom((value - 1) / 12);
177
- const pool: DicePool = { challengeDice: 1 };
178
- expect(roll(pool).summary).toEqual(expected);
179
- cleanup();
180
- });
181
- });
182
- });
183
-
184
- describe('Edge Cases', () => {
185
- test('negative numbers default to 0', () => {
186
- const pool: DicePool = {
187
- boostDice: -1,
188
- abilityDice: -2,
189
- proficiencyDice: -1,
190
- setBackDice: -3,
191
- difficultyDice: -2,
192
- challengeDice: -1
193
- };
194
- const expected = {
195
- successes: 0,
196
- failures: 0,
197
- advantages: 0,
198
- threats: 0,
199
- triumphs: 0,
200
- despair: 0
201
- };
202
- expect(roll(pool).summary).toEqual(expected);
203
- });
204
-
205
- test('undefined values default to 0', () => {
206
- const pool: DicePool = {
207
- boostDice: undefined,
208
- abilityDice: undefined,
209
- proficiencyDice: undefined,
210
- setBackDice: undefined,
211
- difficultyDice: undefined,
212
- challengeDice: undefined
213
- };
214
- const expected = {
215
- successes: 0,
216
- failures: 0,
217
- advantages: 0,
218
- threats: 0,
219
- triumphs: 0,
220
- despair: 0
221
- };
222
- expect(roll(pool).summary).toEqual(expected);
223
- });
224
-
225
- test('empty pool returns zero results', () => {
226
- const pool: DicePool = {};
227
- const expected = {
228
- successes: 0,
229
- failures: 0,
230
- advantages: 0,
231
- threats: 0,
232
- triumphs: 0,
233
- despair: 0
234
- };
235
- expect(roll(pool).summary).toEqual(expected);
236
- });
237
- });
238
-
239
- describe('Result Accumulation', () => {
240
- test('successes and failures cancel out', () => {
241
- // Mock to generate one success and one failure
242
- const cleanup1 = mockMathRandom(1/8); // ability die success (face value 2)
243
- const cleanup2 = mockMathRandom(1/8); // difficulty die failure (face value 2)
244
-
245
- const pool: DicePool = {
246
- abilityDice: 1,
247
- difficultyDice: 1
248
- };
249
-
250
- const result = roll(pool);
251
- expect(result.summary.successes).toBe(0);
252
- expect(result.summary.failures).toBe(0);
253
-
254
- cleanup1();
255
- cleanup2();
256
- });
257
-
258
- test('advantages and threats accumulate independently', () => {
259
- // Mock to generate advantages and threats
260
- const cleanup1 = mockMathRandom(4/6); // boost die success + advantage
261
- const cleanup2 = mockMathRandom(6/8); // difficulty die threat
262
-
263
- const pool: DicePool = {
264
- boostDice: 1,
265
- difficultyDice: 1
266
- };
267
-
268
- const result = roll(pool);
269
- expect(result.summary.advantages).toBeGreaterThan(0);
270
- expect(result.summary.threats).toBeGreaterThan(0);
271
-
272
- cleanup1();
273
- cleanup2();
274
- });
275
-
276
- test('triumphs and despair count independently', () => {
277
- // Mock to generate one triumph and one despair
278
- const cleanup1 = mockMathRandom(11/12); // proficiency die triumph
279
- const cleanup2 = mockMathRandom(11/12); // challenge die despair
280
-
281
- const pool: DicePool = {
282
- proficiencyDice: 1,
283
- challengeDice: 1
284
- };
285
-
286
- const result = roll(pool);
287
- expect(result.summary.triumphs).toBe(1);
288
- expect(result.summary.despair).toBe(1);
289
-
290
- cleanup1();
291
- cleanup2();
292
- });
293
- });
294
-
295
- describe('Complex Combinations', () => {
296
- test('realistic skill check - standard difficulty', () => {
297
- const pool: DicePool = {
298
- abilityDice: 2,
299
- proficiencyDice: 1,
300
- difficultyDice: 2
301
- };
302
-
303
- const result = roll(pool);
304
-
305
- // Verify result structure
306
- expect(result.summary).toHaveProperty('successes');
307
- expect(result.summary).toHaveProperty('failures');
308
- expect(result.summary).toHaveProperty('advantages');
309
- expect(result.summary).toHaveProperty('threats');
310
- expect(result.summary).toHaveProperty('triumphs');
311
- expect(result.summary).toHaveProperty('despair');
312
-
313
- // Verify ranges
314
- expect(result.summary.successes).toBeGreaterThanOrEqual(0);
315
- expect(result.summary.failures).toBeGreaterThanOrEqual(0);
316
- expect(result.summary.advantages).toBeGreaterThanOrEqual(0);
317
- expect(result.summary.threats).toBeGreaterThanOrEqual(0);
318
- expect(result.summary.triumphs).toBeGreaterThanOrEqual(0);
319
- expect(result.summary.despair).toBeGreaterThanOrEqual(0);
320
- });
321
-
322
- test('opposed check - combat scenario', () => {
323
- const pool: DicePool = {
324
- abilityDice: 1,
325
- proficiencyDice: 1,
326
- boostDice: 1,
327
- difficultyDice: 1,
328
- challengeDice: 1,
329
- setBackDice: 1
330
- };
331
-
332
- const result = roll(pool);
333
-
334
- // Verify ranges
335
- expect(result.summary.successes).toBeGreaterThanOrEqual(0);
336
- expect(result.summary.failures).toBeGreaterThanOrEqual(0);
337
- expect(result.summary.advantages).toBeGreaterThanOrEqual(0);
338
- expect(result.summary.threats).toBeGreaterThanOrEqual(0);
339
- expect(result.summary.triumphs).toBeGreaterThanOrEqual(0);
340
- expect(result.summary.despair).toBeGreaterThanOrEqual(0);
341
-
342
- // Check detailed results
343
- expect(result.results).toHaveLength(6); // One result per die
344
- result.results.forEach(dieResult => {
345
- expect(dieResult).toHaveProperty('type');
346
- expect(dieResult).toHaveProperty('roll');
347
- expect(dieResult).toHaveProperty('result');
348
- });
349
- });
350
- });
351
- });
@@ -1,86 +0,0 @@
1
- import {
2
- createSkillCheck,
3
- createCombatCheck,
4
- createOpposedCheck,
5
- createDifficultyPool
6
- } from '../src/pools';
7
-
8
- describe('Dice Pool Convenience Methods', () => {
9
- describe('createSkillCheck', () => {
10
- test('creates basic skill check pool', () => {
11
- const pool = createSkillCheck(2, 1);
12
- expect(pool).toEqual({
13
- abilityDice: 2,
14
- proficiencyDice: 1
15
- });
16
- });
17
-
18
- test('creates pool with zero dice', () => {
19
- const pool = createSkillCheck(0, 0);
20
- expect(pool).toEqual({
21
- abilityDice: 0,
22
- proficiencyDice: 0
23
- });
24
- });
25
- });
26
-
27
- describe('createCombatCheck', () => {
28
- test('creates combat check pool with boost', () => {
29
- const pool = createCombatCheck(2, 1, 1);
30
- expect(pool).toEqual({
31
- abilityDice: 2,
32
- proficiencyDice: 1,
33
- boostDice: 1
34
- });
35
- });
36
-
37
- test('creates combat check pool without boost', () => {
38
- const pool = createCombatCheck(2, 1);
39
- expect(pool).toEqual({
40
- abilityDice: 2,
41
- proficiencyDice: 1,
42
- boostDice: 0
43
- });
44
- });
45
- });
46
-
47
- describe('createOpposedCheck', () => {
48
- test('creates opposed check pool with challenge', () => {
49
- const pool = createOpposedCheck(2, 1, 2, 1);
50
- expect(pool).toEqual({
51
- abilityDice: 2,
52
- proficiencyDice: 1,
53
- difficultyDice: 2,
54
- challengeDice: 1
55
- });
56
- });
57
-
58
- test('creates opposed check pool without challenge', () => {
59
- const pool = createOpposedCheck(2, 1, 2);
60
- expect(pool).toEqual({
61
- abilityDice: 2,
62
- proficiencyDice: 1,
63
- difficultyDice: 2,
64
- challengeDice: 0
65
- });
66
- });
67
- });
68
-
69
- describe('createDifficultyPool', () => {
70
- test('creates difficulty pool with challenge', () => {
71
- const pool = createDifficultyPool(2, 1);
72
- expect(pool).toEqual({
73
- difficultyDice: 2,
74
- challengeDice: 1
75
- });
76
- });
77
-
78
- test('creates difficulty pool without challenge', () => {
79
- const pool = createDifficultyPool(2);
80
- expect(pool).toEqual({
81
- difficultyDice: 2,
82
- challengeDice: 0
83
- });
84
- });
85
- });
86
- });
package/tsconfig.json DELETED
@@ -1,14 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "es2018",
4
- "module": "commonjs",
5
- "declaration": true,
6
- "outDir": "./dist",
7
- "strict": true,
8
- "esModuleInterop": true,
9
- "skipLibCheck": true,
10
- "forceConsistentCasingInFileNames": true
11
- },
12
- "include": ["src/**/*"],
13
- "exclude": ["node_modules", "**/*.test.ts"]
14
- }