-
Notifications
You must be signed in to change notification settings - Fork 0
Closed
Labels
featureNew functionalityNew functionality
Description
Implement AST evaluator that produces roll results, including keep/drop modifier logic.
Rationale
Evaluator is the final pipeline stage that produces actual dice roll results:
AST → [Evaluator + RNG] → RollResult
The evaluator must:
- Traverse AST nodes recursively
- Execute dice rolls using injected RNG
- Apply modifiers (keep/drop) to dice pools
- Track metadata (criticals, fumbles, dropped dice)
References
- PRD.md Section 3.6-3.7 — Mechanics checklist and negative number handling
- PRD.md Section 5.3 — Structured output schema
- PLAN.md Phase 4 — Evaluator implementation details
Things to Consider
- CRITICAL: Negative results must NOT be clamped —
1d4-5can produce-4 - Modifier order: Keep/drop applied before arithmetic
- Critical/Fumble: Only on dice that roll max/1, not on computed totals
- Dropped dice: Must be tracked in result metadata
Implementation
Important
This is not a step-by-step guide — it's a functional checklist ordered logically.
Files to Create
-
/src/types.ts— Shared result types:interface RollResult { total: number; notation: string; // Original input expression: string; // Normalized: "1d20 + 5" rendered: string; // "1d20[15] + 5 = 20" rolls: DieResult[]; } interface DieResult { sides: number; result: number; modifiers: ('dropped' | 'kept')[]; critical: boolean; // Rolled max fumble: boolean; // Rolled 1 }
-
/src/evaluator/evaluator.ts— AST visitor/evaluator:evaluate(ast: ASTNode, rng: RNG): RollResult- Handlers for each AST node type
- Dice pool accumulation for modifiers
-
/src/evaluator/modifiers/keep-drop.ts— Modifier logic:function applyKeepHighest(dice: DieResult[], count: number): DieResult[] function applyKeepLowest(dice: DieResult[], count: number): DieResult[] function applyDropHighest(dice: DieResult[], count: number): DieResult[] function applyDropLowest(dice: DieResult[], count: number): DieResult[]
-
/src/evaluator/evaluator.test.ts— Unit tests with MockRNG
Test Cases (with MockRNG)
| Notation | Mock Values | Expected | Notes |
|---|---|---|---|
1d6 |
[4] |
total: 4 | Basic roll |
1d6+3 |
[4] |
total: 7 | Arithmetic |
1d4-5 |
[1] |
total: -4 | Negative NOT clamped! |
-1d4 |
[3] |
total: -3 | Unary minus |
4d6kh3 |
[3,1,4,2] |
total: 9 | Keep highest 3 |
4d6dl1 |
[3,1,4,2] |
total: 9 | Drop lowest 1 |
1d20 |
[20] |
critical: true | Max roll |
1d20 |
[1] |
fumble: true | Min roll |
Modifier Logic
Keep Highest (kh)
4d6kh3 with [3,1,4,2]:
Sort descending: [4,3,2,1]
Keep first 3: [4,3,2] → total: 9
Mark [1] as dropped
Drop Lowest (dl)
4d6dl1 with [3,1,4,2]:
Sort ascending: [1,2,3,4]
Drop first 1: [1] dropped
Keep remaining: [2,3,4] → total: 9
Acceptance Criteria
- Basic arithmetic operations (+, -, *, /, %, **) work correctly
- Dice rolls use injected RNG
- CRITICAL: Negative results are NOT clamped to 0
- Keep highest modifier works (
kh,k) - Keep lowest modifier works (
kl) - Drop highest modifier works (
dh) - Drop lowest modifier works (
dl) - Critical detection: die rolled max value
- Fumble detection: die rolled 1
- Dropped dice tracked in result metadata
- All test cases pass with MockRNG
Drafted with AI assistance
Metadata
Metadata
Assignees
Labels
featureNew functionalityNew functionality