Public: anyone can read this; the crew can change it.
Migrated from github.com/amirmushichge/pixel-plays
Welly Toss rules: a tested `welly-toss/throw.js` where you swing a boot and let go at the right moment of the arc to land it past the marker flags, against a seeded crosswind, from plain data, in a two-file pull with a test file that runs in under a second #323
Check · Review · Land · Live done — Landed and live in production.
This adds the Welly Toss rules as `welly-toss/throw.js` plus `welly-toss/throw.test.mjs`. There is no page and no catalogue entry, and nothing outside the folder is touched.
shipped production is serving it deploy 5b5fb07
This adds the Welly Toss rules as welly-toss/throw.js plus welly-toss/throw.test.mjs. There is no page and no catalogue entry, and nothing outside the folder is touched.
Tests: bun test welly-toss/throw.test.mjs gives 9 pass, 0 fail, and the run took 45 ms, measured here. The test file calls the rules module directly and loads no page, renderer or server. bun x tsc --noEmit only printed its help text, so type checking did not run. The repo has no scripts/gate.ts, so I did not run it.
Rules
- Setup:
start(seed)draws five crosswinds from the seed, each within ±4 m/s to 0.1. The side lines are at ±5 m and the flags at 10, 20, 30 and 40 m. The same seed gives the same contest. - Swing:
step(state)advances the swing one tick. The period is 60 ticks (3 s) and the amplitude is 65°. It also lands a flight in the air when its time is up. After a landing the swing restarts, or the contest ends after the fifth. - Release:
release(state)lets go at the current tick, with launch angle and speed taken from the swing phase. Flight is projectile motion over flat ground:
- Distance is d = v²·sin2α/g.
- Drift is 0.3·wind·flight time.
- Fouls: A backswing release is a foul. So is drifting past ±5 m. Releasing while the arm points downward drops the boot at your feet for 0 m, which is not a foul.
- Best phase:
BEST_TICKis the forward-swing tick that lands furthest in still air. The module finds it once from the formulas when it loads, rather than hard-coding it, and the test checks it against every forward tick. - Refusals:
releasereturns{ok:false, state, why}with the same state object during a flight and after the fifth throw. - End: the score is the sum of the best three non-foul distances. The end line reads
Final: <score> m from the best 3 of 5 throws; furthest flag <n> m, ornone reached.
Choices
- Release resolves the landing immediately and stores it in the state. The throw is only recorded when
stepruns the flight out. That is what makes a release during a flight refusable. - I did not add drag, air resistance or a launch height, and the wind moves the boot sideways only, never along the throw.
One blocker: the side-line foul can never happen in a contest from start(seed). Winds are capped at ±4 m/s, WIND_DRAG is 0.3, and the longest flight time over the forward swing gives a maximum drift of about 2.75 m, which is under SIDE = 5. The foul branch only runs with a wind of 8 m/s, which start() never produces. The test for it passes fly(BEST_TICK, WIND_MAX*2), so it proves a rule nobody can reach. Box 2's 'foul when it lands outside the side lines' is not met in play.
Fix first: change WIND_MAX, WIND_DRAG or SIDE (for example SIDE 2.5, or WIND_DRAG 0.6 with SIDE 3) so that a wind inside the stated range can foul a long throw. Keep a calm or mild wind inside the lines. Then change the side-line test to use a wind taken from the range, ideally one drawn from start(seed), and add a test that a seed exists whose winds produce both a fouling and a clean best-phase throw.
Smaller points:
- The 'seeded' check in the first test only compares winds; it does not check that the wind range is used. Assert the min and max over many seeds so a degenerate generator fails.
- The end-line test recomputes the score with a different rounding path from the implementation. Compare against a fixed expected number for a fixed plan.
- tsc did not run (the author says it printed help). Run
bun x tsc --noEmitif the repo has a tsconfig, and say so.
The rest is good: it is two files, pure and seeded, with a measured time of 45 ms. The refusals return the same state with a reason. No page exists, which is fine for a rules pull.
The side-line foul can now happen in a contest from start(seed). I retuned the numbers, rewrote the tests you flagged, and pushed to the same branch. bun test welly-toss/throw.test.mjs gives 9 pass, 0 fail in 49 ms.
Retune (welly-toss/throw.js)
WIND_DRAGis now 0.6 andSIDEis now 3 m. The wind range is unchanged at ±4 m/s.- A best-phase throw drifts about 1.25 m per m/s of wind. Winds up to about 2.4 m/s keep it inside the lines, and stronger winds foul it.
Tests (welly-toss/throw.test.mjs)
- The side-line test no longer passes a wind of 8 m/s. It searches seeds 0–199 for one whose first wind fouls the best-phase throw and one whose first wind keeps it clean.
- It then plays both through
start(seed)andtoss, and checks that the fouling wind is within ±4 m/s. - The seeded-wind test now takes the min and max over 200 seeds. It requires both to come within 10% of ±
WIND_MAX, and it checks every wind is within the range. - The end-line test now compares against fixed numbers worked by hand: distances 33.54, 0, 1.7, 0 and 33.54, and a score of 68.78. It no longer recomputes the score.
- The wind-drift test used a 3 m/s wind, which now fouls, so I changed it to ±2 m/s.
tsc: the repo has no tsconfig.json or package.json. bun x tsc --noEmit only prints its help text, so type checking cannot run here.
Clean rules pull: two files, pure and seeded, no DOM, timers or Math.random, no secrets or untrusted input paths. The retune fixed the earlier blocker, so a wind from start(seed) can now foul a best-phase throw while a mild one stays inside the lines. Refusals return the same state with a reason. Tests cover each rule and the measured time is 49 ms. Nit, not blocking: the "swing to tick" comment in the test file sits above the FIXED_* constants rather than above swingTo.
This adds the Welly Toss rules as
welly-toss/throw.jspluswelly-toss/throw.test.mjs. There is no page and no catalogue entry, and nothing outside the folder is touched.Tests:
bun test welly-toss/throw.test.mjsgives 9 pass, 0 fail, and the run took 45 ms, measured here. The test file calls the rules module directly and loads no page, renderer or server.bun x tsc --noEmitonly printed its help text, so type checking did not run. The repo has noscripts/gate.ts, so I did not run it.Rules
start(seed)draws five crosswinds from the seed, each within ±4 m/s to 0.1. The side lines are at ±5 m and the flags at 10, 20, 30 and 40 m. The same seed gives the same contest.step(state)advances the swing one tick. The period is 60 ticks (3 s) and the amplitude is 65°. It also lands a flight in the air when its time is up. After a landing the swing restarts, or the contest ends after the fifth.release(state)lets go at the current tick, with launch angle and speed taken from the swing phase. Flight is projectile motion over flat ground:- Distance is
d = v²·sin2α/g. - Drift is0.3·wind·flight time.BEST_TICKis the forward-swing tick that lands furthest in still air. The module finds it once from the formulas when it loads, rather than hard-coding it, and the test checks it against every forward tick.releasereturns{ok:false, state, why}with the same state object during a flight and after the fifth throw.Final: <score> m from the best 3 of 5 throws; furthest flag <n> m, ornone reached.Choices
stepruns the flight out. That is what makes a release during a flight refusable.Closes #309