Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,11 @@ export {
isUnaryOp,
} from './parser/ast';

// TODO: [Phase 3] Export RNG interface
// * RNG exports
export type { RNG } from './rng/types';
export { SeededRNG } from './rng/seeded';
export { createMockRng, MockRNGExhaustedError } from './rng/mock';

// TODO: [Phase 4] Export evaluator and result types
// TODO: [Phase 5] Export public API (roll, evaluate)

Expand Down
9 changes: 9 additions & 0 deletions src/rng/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/**
* RNG module - Seedable random number generation.
*
* @module rng
*/

export type { RNG } from './types';
export { SeededRNG } from './seeded';
export { createMockRng, MockRNGExhaustedError } from './mock';
59 changes: 59 additions & 0 deletions src/rng/mock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/**
* Mock RNG for deterministic testing.
*
* @module rng/mock
*/

import type { RNG } from './types';

/**
* Error thrown when MockRNG exhausts its predefined values.
*
* This is intentional behavior to catch incorrect roll counts in tests.
* If you see this error, your test is consuming more random values than expected.
*/
export class MockRNGExhaustedError extends Error {
readonly consumed: number;

constructor(consumed: number) {
super(`MockRNG exhausted: consumed ${consumed} values, no more available`);
this.name = 'MockRNGExhaustedError';
this.consumed = consumed;
}
}

/**
* Creates a mock RNG that returns predefined values in sequence.
*
* IMPORTANT: Throws MockRNGExhaustedError when all values are consumed.
* This behavior catches incorrect roll counts in tests - it never wraps around.
*
* @param values - Array of values to return (dice results for nextInt, floats for next)
* @returns RNG instance returning predefined values
*
* @example
* ```typescript
* const rng = createMockRng([4, 2, 6]);
* rng.nextInt(1, 6); // Returns 4
* rng.nextInt(1, 6); // Returns 2
* rng.nextInt(1, 6); // Returns 6
* rng.nextInt(1, 6); // Throws MockRNGExhaustedError
* ```
*/
export function createMockRng(values: number[]): RNG {
let index = 0;

const getNext = (): number => {
const value = values[index];
if (value === undefined) {
throw new MockRNGExhaustedError(index);
}
index++;
return value;
};

return {
next: getNext,
nextInt: (_min: number, _max: number): number => getNext(),
};
}
Loading