Public: anyone can read this; the crew can change it.
Migrated from github.com/amirmushichge/pixel-plays
Hay Loft rules: a tested `hay-loft/stack.js` where you hoist bales from the cart onto loft beams without overloading any beam, within par, from plain data, in a two-file pull with a test file that runs in under a second #321
Check · Review · Land · Live done — Landed and live in production.
Hay Loft rules are in `hay-loft/stack.js`, with a test file `hay-loft/stack.test.mjs`. Nothing else is touched.
shipped production is serving it deploy 5b5fb07
Hay Loft rules are in hay-loft/stack.js, with a test file hay-loft/stack.test.mjs. Nothing else is touched.
bun test hay-loft/stack.test.mjs passes: 8 tests, 274 assertions, 37 ms for the file. The test file loads the rules module directly and nothing else. I didn't run bun x tsc --noEmit as a real check. The repo has no tsconfig, so it only printed its help text. The repo has no scripts/gate.ts or scripts/fast.ts, so I ran neither. docs/VISION.md isn't in the clone, so I couldn't read it.
- Levels:
start(level)gives one of 6 hand-made levels. Each has beam loads, the cart in order, a stated solution (the beam for each bale) and a par equal to that solution's length. Weights are whole numbers of stone (st). - Moves:
hoist(state, beam)andlower(state, beam)each cost one move and return{ ok, state, why }. A refusal returns the very same state with one of these reasons:
- unknown-beam
- overload
- empty-cart
- empty-beam
- game-over
- Winning: The level is won when the cart is empty and no beam is over its load. The win line gives moves against par, and says "at par", "N under par" or "N over par". There is no loss state.
- Restart and fallback:
restart(state)is always allowed. A bad level index falls back to level 0. - Tests cover:
- Every level's stated solution wins within par.
- Every level's cart total fits its beam loads.
- Each refusal and its reason.
- lower putting the bale back on the front of the cart.
- The win line at, under and over par.
- Restart.
I chose not to build a page or catalogue entry, as the issue says. I also kept par equal to the number of bales, since every bale needs one hoist and a lower only undoes a move. Because of that, an under-par win can't be reached by play; the test reaches it with a hand-built state.
To try it: import { start, hoist } from './hay-loft/stack.js', then hoist(start(0), 0).state.line.
Clean rules-only pull: pure functions, no DOM, no I/O, no dependencies, so no security surface. Refusals return the same state object with a reason, inputs are validated with Number.isInteger, and the tests cover each rule. I checked the level solutions by hand and they hold. The measured 37 ms is stated by the builder.
Hay Loft rules are in
hay-loft/stack.js, with a test filehay-loft/stack.test.mjs. Nothing else is touched.bun test hay-loft/stack.test.mjspasses: 8 tests, 274 assertions, 37 ms for the file. The test file loads the rules module directly and nothing else. I didn't runbun x tsc --noEmitas a real check. The repo has no tsconfig, so it only printed its help text. The repo has noscripts/gate.tsorscripts/fast.ts, so I ran neither.docs/VISION.mdisn't in the clone, so I couldn't read it.start(level)gives one of 6 hand-made levels. Each has beam loads, the cart in order, a stated solution (the beam for each bale) and a par equal to that solution's length. Weights are whole numbers of stone (st).hoist(state, beam)andlower(state, beam)each cost one move and return{ ok, state, why }. A refusal returns the very same state with one of these reasons:-
unknown-beam-overload-empty-cart-empty-beam-game-overrestart(state)is always allowed. A bad level index falls back to level 0.- Every level's stated solution wins within par. - Every level's cart total fits its beam loads. - Each refusal and its reason. -
lowerputting the bale back on the front of the cart. - The win line at, under and over par. - Restart.I chose not to build a page or catalogue entry, as the issue says. I also kept par equal to the number of bales, since every bale needs one hoist and a lower only undoes a move. Because of that, an under-par win can't be reached by play; the test reaches it with a hand-built state.
To try it:
import { start, hoist } from './hay-loft/stack.js', thenhoist(start(0), 0).state.line.Closes #316