refactor: collapse the skill predicates and give the reroll a home - #69
Merged
Merged
Conversation
**One predicate instead of two (#50, #44).** `canSpendSkill` and `canSpendSkillButton` were the same lines with `===` flipped to `!==`, called one after the other in the same render. There is one underlying fact — this skill applies to this attribute and has not been spent — and one variable on top of it: whether the player has already pressed the button this paragraph. `skillAvailableFor` is the fact; the call site derives the rest. `makeSkillButton` inlines: eight lines with one caller that always passed `() => v.repaint()`, so the parameter never varied. That collapse is what deletes `if (skillBonusActive) v.state.draft.skillUsedHere = attr;` (#44). It could never change anything, because `skillBonusActive` already required `skillUsedHere === attr` — a symptom of the split rather than a defect of its own, so it disappears with the restructure. **The reroll gets a home and its first test (#43).** `planRoll` set `rerollPolicy: 'highest'` and stopped; the mechanic lived 400 lines away inside the roll button's callback. Neither file answered the question a maintainer has — `rules.ts` did not say what a reroll costs, `play.ts` did not say which scenarios trigger it. `applyReroll(dice, policy, rng?)` now sits in `rules.ts` beside the `RollPlan` that requests it, and the roll callback is two lines. `roll` already took an injectable RNG, so it is deterministically testable — and nothing tested the splice before: `tests/rules.test.ts` had two `reroll_highest` cases and both asserted on the flag. Six cases added, including the one that matters: a highest die that was already a success is replaced anyway. That is what makes the rule a hazard rather than a bonus, and it was the untestable half. Confirmed by probe — making the reroll spare a successful die fails three of them. **#39 finishes.** The hand-written `'penmanship' | 'language' | 'heart'` union is gone from the file; `Attribute` is imported. Two of its five original sites had already gone with the draft's move to `src/paragraph.ts` and with #46. Verified in the browser on The Art Dealer, the only scenario carrying `reroll_highest`, with Illumination so the skill and the reroll land on the same Penmanship roll: `3 dice, re-roll the highest` becomes `4 dice, re-roll the highest, skill applied` on spending, the button disappears, the reference card reads "spent", and paragraph II is back to 3 dice with no button. `src/screens/play.ts` 695 → 677 lines. Closes #50 Closes #44 Closes #43 Closes #39 Co-Authored-By: Claude
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #50, #44, #43, #39 — all of milestone 5, one PR, since all four land in the same ~40 lines.
One predicate instead of two (#50, #44)
canSpendSkillandcanSpendSkillButtonwere the same lines with===flipped to!==, called one after the other in the same render. There's one underlying fact — this skill applies to this attribute and hasn't been spent — and one variable on top of it: has the player already pressed the button this paragraph.skillAvailableForis the fact; the call site derives the rest.makeSkillButtoninlines — eight lines with one caller that always passed() => v.repaint(), so the parameter never varied.That collapse is what deletes #44.
if (skillBonusActive) v.state.draft.skillUsedHere = attr;could never change anything, becauseskillBonusActivealready requiredskillUsedHere === attr. It's a symptom of the split rather than a defect of its own, so it disappears with the restructure rather than as a separate edit — which is what both issues predicted.The reroll gets a home and its first test (#43)
planRollsetrerollPolicy: 'highest'and stopped; the mechanic lived 400 lines away inside the roll button's callback. Neither file answered the question a maintainer actually has —rules.tsdidn't say what a reroll costs,play.tsdidn't say which scenarios trigger it.applyReroll(dice, policy, rng?)now sits inrules.tsbeside theRollPlanthat requests it, and the roll callback drops to two lines.rollalready took an injectable RNG, so it's deterministically testable — and nothing tested the splice before:tests/rules.test.tshad tworeroll_highestcases and both asserted on the flag.Six cases added, including the one that matters:
That's what makes
reroll_highesta hazard rather than a bonus, and it was precisely the half that couldn't be tested where the code sat. Probed by making the reroll "helpfully" spare a successful die — three tests fail.#39 finishes
The hand-written
'penmanship' | 'language' | 'heart'union is gone from the file;Attributeis imported. Two of its five original sites had already gone — one with the draft's move tosrc/paragraph.ts, one with #46 — which is why this lands as a two-line change rather than five.Verification
bun run check:ciclean. 77 → 83 tests.src/screens/play.ts695 → 677.Smoke-tested on The Art Dealer — the only scenario carrying
reroll_highest— with Illumination, so the skill bonus and the reroll land on the same Penmanship roll:Roll Penmanship (3 dice, re-roll the highest) for a fine hand.Roll Penmanship (4 dice, re-roll the highest, skill applied) …Roll Penmanship (3 dice, re-roll the highest) …Button disappears on spend, reference card reads
The Monk — Illumination spent, and no button reappears on paragraph II. That's the check #50 asked for, plus the interaction with the reroll.Note
Two of these issues were smaller than filed, because milestone 4 had already taken part of them: #50's complaint about a triple
skillByIdlookup was resolved by #46, and #39 was down from five sites to two. What remained was the genuine twin collapse and the mechanical import.THEORY.mdandWALKTHROUGH.mduntouched.