M49: Crazy Fruits (match-3) — single-source engine, masked DQN (+57% over random), touch+mouse web game - #38
Conversation
4-agent investigation (game provenance, repo integration, conventions/input, match-3 RL prior art) -> docs/prd/CRAZY_FRUITS_PRD.md + PLAN.md M49. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…cade + tests - crazyfruits_solver.pg: 8x8/6-fruit match-3 rules (swap legality, cascades with proportional scoring 10*(k+1) + line bonuses, gravity/refill, deadlock reshuffle), Schrage minstd RNG (exact i32 both sides), 448-float observation (6 one-hot planes + would-match plane), scripted baselines (random/greedy/expectimax-1) and the PgCfDuelingNet forward - all shared C#/TS; pgconfig routes the TS twin to ClientApp/src/app/crazy-fruits. - CrazyFruitsBoard: public facade (state serialization, LoadGrid for directed tests). - Gate GREEN: 16 tests - init/post-move invariants (stable board, always a legal swap), mask == brute force, hand-computed 3/4/5-line + 2-step-cascade scores, reshuffle multiset preservation, minstd 10,000th-draw reference (399268537), determinism + state round-trip; C#<->TS parity: seeded 1,000-move random episode checksummed per-move over action/points/full grid -> identical (78377593, score 70990) against the generated TS twin under node type-stripping. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- CrazyFruitsEnv: IEnvironment + IActionMaskProvider + IStatefulEnvironment over the .pg core; reward = points/30, truncation (never termination) at the 30-move budget, full state round-trip, viz labels. - CrazyFruitsDqnCampaign on the DqnScoreCampaign spine (masked Double+Dueling, the Snake recipe re-pointed) + AddCrazyFruitsDqnCampaign() + `--game crazyfruits` (`--baselines N` prints the gate table; `--net` adds the trained net to it). - Gate GREEN: baseline ordering over 500 seeded episodes - random 2259.7+/-49.9, greedy 2387.0+/-49.3 (CI-separated), expectimax-1 4270.9+/-98.3 (cascade planning is the skill; line size is nearly irrelevant); campaign resume->train->checkpoint-> resume contract on the real env; one end-to-end 3k-step chunk trained and saved a deployable net. 21 CrazyFruits tests green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… watch scaffolding - Engine grows a stepwise move API (clearStep / finishMove; applySwap drains the same loop) so the animating web host and the one-call training path are the same rules by construction - C#<->TS parity checksum re-verified unchanged (78377593). - crazy-fruits-game.ts: animating host recording swap -> pop -> fall [-> reshuffle] timelines from the engine; view-only fall geometry. - crazy-fruits-render.ts: fruit-stall theme (bunting, strawberry-red wordmark, vector fruit art), canvas HUD (score/moves/best drawn on-canvas - loop runs outside zone). - crazy-fruits.ts/.html/.scss: unified Pointer Events - ONE path for touch (touchstart/move/end) and mouse (mousedown/move/up): drag-swap past a 0.35-cell threshold + tap-select-tap-swap; setPointerCapture; touch-action: none. Watch-AI scaffolding: tier picker (Random/Greedy/Expectimax/net), director driving the engine's scripted baselines, net loader (falls back to expectimax until the M49.3 ckpt ships); screen wake lock during watch. - Registered: route /crazyfruits + nav link + home card; ARCHITECTURE.md env + client-side tables. - tsc --noEmit clean. Gate note: live desktop+smartphone verification pending the user-run host (down at implementation time) - to be evidenced before the PR. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…pisodes) Three-run story (all 150k moves, masked Double+Dueling DQN, one lever per intervention per the no-thrash rule): 1. gamma=0.99 (the Snake recipe verbatim): loss exploded 0.62->8.7, eval regressed below random mid-run; on the 500-episode gate the keep-best net was +1.9% (OVERLAPPING) - the documented match-3 trap: the controllable signal is almost entirely immediate, and deep bootstrapping amplifies refill-cascade noise. FAIL. 2. gamma=0 (Q = expected immediate points incl. stochastic cascades): stable loss (~0.18), +7.8% CI-SEPARATED, above greedy - real learning, still under +30%. FAIL. 3. gamma=0 + the PRD s6 Risk-1 pre-registered mitigation - observation grows two per-action feature planes (immediate score, deterministic cascade value, /100; 448 -> 672 floats): mean 3552.5 +/- 83.3 = +57.2% over random CI-SEPARATED (gate >= +30%), +48.8% over greedy; expectimax-1 (4270.9) remains the scripted ceiling = future-training headroom. Ships wwwroot/models/crazyfruits.dqn.ckpt (LFS, keep-best from run 3, eval 3392.5 at 125k). Observation change verified: 22 tests green (incl. per-action-plane pins), C#<->TS parity checksum unchanged (78377593). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- CrazyFruitsNetParityTests: byte-level ckpt parse (the line-for-line reference for crazyfruits-net.ts) -> PgCfDuelingNet forward matches the SDK DuelingQNet within f32 tolerance through real checkpoint bytes; masked netAction returns legal actions. - Node simulation of the exact browser path (shipped crazyfruits.dqn.ckpt -> TS parser -> generated net on the generated engine): all four watch tiers play full legal 30-move episodes; strength ordering reproduces (random 2379 / greedy 2520 / net 3393 / expectimax 4507 over 20 eps). - PRD + PLAN M49 updated with all measured gate results; M49.4/M49.5 marked "built, live in-browser device check pending" (the user-run dev host was down throughout; headless evidence recorded in the PRD). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
LIVE Playwright against the running host (desktop 960px mouse + emulated phone 390px with real touch events): - tap-tap select ring lands on the tapped cell on both form factors; a human tap-tap swap cleared a 3-line (score 30, move 1, screenshot-verified) - mouse drag and CDP touch-drag both fire the gesture; illegal picks revert without consuming a move; window.scrollY unchanged through the touch drag (touch-action: none holds); zero console/page errors - watch mode verified live mid-cascade (trained net, score 330 at move 7/30, "+30" pop on screen); all four tiers exercised on desktop and mobile Screenshot-driven UX fix: a failed tap-tap swap now clears the selection like a successful one (the persistent ring was both a test false-positive and off-feel). PRD + PLAN M49 marked fully SHIPPED. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Live browser gate completed (host started per the clarified rule — port was free): desktop mouse + emulated smartphone touch both verified with Playwright. Human tap-tap swap cleared a 3-line (score 30 · move 1), drag gestures fire on both input types, illegal swaps revert without consuming a move, |
Replaces the hand-drawn vector fruits with fruit-cake-art.ts (cached 256px offscreen renders, blitted scaled - the per-frame cost is a textured quad). Six visually distinct picks from the 11-tier catalog, no two share a color: strawberry, grape, dekopon (orange), pear, pineapple, watermelon. Radius bumped 0.36 -> 0.42 cell (the art fills its circle), selection ring to 0.47. C# viz labels synced to the new fruit identities (labels only - observation unchanged). Verified live: board + watch-mode cascades render with the new art, no errors. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…+ PLAN entry 3-agent investigation (canonical Candy Crush mechanics, line-referenced engine impact, AI/training impact) -> docs/prd/CRAZY_FRUITS_SPECIALS_PRD.md + PLAN M50. Key locks: fully deterministic rules (zero RNG - passive bomb clears the most- frequent type, (r+c)%2 conversion orientations, lowest-run-cell cascade spawns; striped blast PARALLEL to the match, the "perpendicular" wiki wording is sprite paint); packed base-16 cell encoding (plain fruit keep 1..6); bounded activation worklist with the wrapped double-explosion riding the grid as an internal armed kind (stepwise animation API unchanged); swapIsLegal supersedes swap-must-match (action space stays 112); obs 672->928 => from-scratch retrain; v1 keeps gamma=0 with the per-action deterministic-value feature extended to price create+fire+ combos (PBRS at gamma=0 is a provable no-op), ONE pre-registered escalation (gamma=0.5 + 3-step + PBRS) gated by a new expectimax-2 baseline; game-over = 30-move rounds (owner decision - measured zero deadlocks ever, so game-over-on- deadlock would never fire; 30 keeps round scores comparable to the trained tiers). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Packed base-16 cells (kind*16+type; plain fruit keep 1..6); typewise match scan now records runs; creation resolver (bomb > wrapped > striped, blast PARALLEL to the match, swap-cell/lowest-cell/intersection spawns); bounded activation worklist with chains; wrapped double explosion rides the grid as an internal armed kind (stepwise animation API unchanged); stageSwap + swapIsLegal (bomb/special+special always legal, action space stays 112); all six combos; passive bomb zaps the most-frequent type (zero-RNG lock); per-move creation/fired telemetry. - Fire-only scoring (owner decision): creations score nothing in-game; the training env gains ShapeCreationRewards (+40/+60/+100 to the REWARD only, train env only). - Planning (immediateScore/deterministicValue) runs REAL clearSteps and restores board+context byte-identically (purity test: no RNG, WriteState bytes unchanged). - Observation 672 -> 928 (+4 kind planes, /300 feature planes); colored specials set both planes; bombs colorless. - Gate GREEN: 42 tests - every creation shape (incl. priority + cascade spawn), every activation (wrapped two-step timeline, never-armed-on-stable invariant), every combo hand-scored exactly (bomb+bomb 640, striped+wrapped 390, ...), chain reactions, invariant sweep, legality brute-force; C#<->TS parity RE-PINNED at 533753109 via the NOW-COMMITTED tools/cf_parity.mjs (episode exercises creation, chains, combos and a mid-episode reshuffle byte-identically). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- RewardScale 30 -> 100 (random means ~86 pts/move with auto-firing specials); per-action feature planes /300; ShapeCreationRewards wired to the TRAIN env only (--no-shape opts out); stale-width ckpt guard in --baselines. - New single-source tiers: expectimax-2 (beam-8 two-ply deterministic - the baseline that can see create->fire) and specials-greedy (immediate + creation weights); per-tier specials created/fired stats in the gate table. - Gate GREEN (500 seeded episodes): random 2612.2+/-67.5, greedy 3446.0+/-99.6 (+32% - specials widened the skill gap from M49s +6%), specials-greedy 3850.4+/-113.8, expectimax-1 5841.2+/-159.8 (+124%), expectimax-2 8368.8+/-177.8 (+220%). Env validation: random = 31% of expectimax-2 (< 70%). Expectimax-2 gap +43.3% over expectimax-1 => the M50.3 escalation trigger is armed. Directed tier tests: greedy provably takes the bomb swap; specials-greedy provably builds. 45 CrazyFruits tests green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…orm-then-trigger 1. Striped blast is PERPENDICULAR to the creating match (real Candy Crush rule: horizontal 4-match -> vertically-striped fruit clearing its COLUMN; paint shows the blast). Overrules the research agents parallel reading. 2. Combo blasts centre on the gesture LAST-SELECTED cell: stageSwap(action, targetCell); web host passes the second tap / dragged-to cell; AI + baselines default deterministically to the action bottom/right cell. Directed test pins that the same swap staged toward each end fires the matching column. 3. Specials FORM before the step activations run: a fresh special lands on its spawn cell first (unmarked - survives untouched), and fires IMMEDIATELY if any blast/combo of the same step reaches it. Directed 190-point double-match test: a fresh striped triggered by a wrapped box within its own creation step. 47 tests green; parity re-pinned 533753109 -> 801202210 -> 995400597 (score 95550) via tools/cf_parity.mjs; 500-episode baselines re-measured on the final rules (random 2598.7 / greedy 3497.9 / specials-greedy 3867.1 / expectimax-1 5931.4 / expectimax-2 8135.0; env validation 32% < 70%; e2 gap +37.2% keeps the escalation trigger armed); the in-flight training run was discarded and restarted per change. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ts, 30-move rounds - Square candy wrapper (owner-requested): translucent gold rounded square with folded corner tabs behind the fruit + gloss band and double rim in front - the fruit stays visible inside. Stripes: thin (0.1r) white bars with dark outlines painted ALONG the blast axis (readable on light fruit); colorless sugar-bomb sphere with sprinkles. - Pop step enriched from the engines lastClearedBy/lastCreated: striped beam flashes, blast rings, bomb zap glows, creation sparkle-ins. - stageSwap gesture wiring (combo centre = last-selected cell); six watch tiers (+ Specials-greedy, Expectimax-2); 30-move rounds with the round-over screen (score / best / measured tier bars / tap to play again); deadlock still reshuffles mid-round; home card + copy updated. - Gate GREEN: tsc clean; headless smoke (60 greedy moves, engine-exact grids, specials firing - 8130 pts vs ~4360 pre-specials); LIVE Playwright: watch tiers screenshot the square-wrapped pineapple / striped fruit / two bombs, and a real 30-move human round (164 pixel-diff-probed swaps) ended on the round-over screen, tap restarting a fresh round; zero console errors. M50.3 training deferred by owner call - runs in background, ckpt + gate table land as a follow-up; the net tier falls back to expectimax until then (stale-width guard). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
M50 (Candy-Crush specials) is now in this PR — striped / wrapped / sugar bomb + all six combo swaps, single-sourced in the |
- "Endless mode" toggle (human play): keeps playing past the 30-move round and dismisses an already-shown round-over screen, resuming the same board. Any game endless touches is permanently exempt from the "best" indicator (bestExempt latch per game); rounds + best resume with the next normal game. HUD status shows "endless - score wont count for best". - Striped overlay bars thinned 0.22r -> 0.10r (less than half), per owner. - Verified: tsc clean; headless semantics smoke (round closes at 30 / endless never closes / resume-from-round-over / best frozen once endless touches / rounds+best resume when off); live toggle screenshot, zero console errors. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…nctionality tests - StepwiseHostProtocol_IsByteIdenticalTo_ApplySwap: the web pages exact engine calls (stageSwap -> clearStep/collapse loop -> finishMove) vs the atomic applySwap, 10 seeds x 30 mixed greedy/random moves incl. specials and combos - identical grid, score, RNG stream, reshuffles and fired-telemetry every move. The browsers game functionality IS the tested engine functionality by construction (TS equality separately pinned by the parity checksum). - tools/cf_host_tests.mjs (committed, `node tools/cf_host_tests.mjs`): the host layers own rules - round closes at exactly 30 (not 29), round-over blocks moves, endless dismisses the screen and resumes the same board past 30, best freezes the moment endless touches a game (incl. games started under endless), rounds + best resume when toggled off, illegal swaps never consume a move. Runs the real crazy-fruits-game.ts under node type stripping (temp-copy import rewrite); no rendering involved. 48 CrazyFruits xunit tests green + both node harnesses (parity, host) green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…er Q) - PassiveBomb test extended: after the move, the striped that set the bomb off AND the bomb itself are off the board. - New Wrapped_TriggersBomb_AndBothAreRemovedByMoveEnd: a wrapped 3x3 catches a bomb; exact step-0 score computed dynamically (match + box + most-frequent-type zap); by move end the wrapped (via its armed second explosion), the bomb, and any armed shell are gone. - Lab --baselines: the net row now reports specials created/fired per episode (needed for the M50.3 usage gate). 49 CrazyFruits tests green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…fallback) The gamma=0 run passed the +30% floor decisively (+53.9%, 4000.5 +/- 128.8 on 500 episodes) but missed the 64% gap-share bar (42%) and the 2x-random creation-usage bar (5.7 vs 7.3/ep) - exactly the hold-for-combo blind spot the PRD pre-registered the escalation for (expectimax-2 gap +37.2% >> 10% arms the trigger). - CrazyFruitsDqnOptions : DqnScoreOptions adds NStep (campaign feeds it to the trainer; plain options = defaults). - CrazyFruitsEnv.ShapeSpecialsPotential: potential-based shaping, Phi(s) = sum of on-board special option values (40/60/100), reward += gamma*Phi(s2) - Phi(s1) - policy-invariant at the matching gamma, prices HOLDING a special without rewarding hoarding (provable no-op at gamma=0, hence escalation-only). - Lab: --nstep, --pbrs (escalation config = --gamma 0.5 --nstep 3 --pbrs; PBRS replaces the creation bonus on the train env; eval env stays bare). 49 tests green. Escalation run launched; ONE attempt per the no-thrash stop-loss. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…est counts PRD/PLAN now carry the full honest M50.3 state ahead of the closing commit: attempt 1 (gamma=0) 4000.5 +/- 128.8 = +53.9% over random (gate 1 PASS, +14.4% over greedy) but 42% gap share and 5.7 created/ep miss gates 2/3; the ONE pre-registered escalation (gamma=0.5 + 3-step + PBRS) is running. PLAN lock text gains the third correction (form-then-trigger), fire-only scoring and the endless toggle; test counts synced to 49 + the two committed node harnesses. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…to the nearest plain cell Owner bug report: dragging a striped fruit into a line of 4 neither fired its blast nor left a new striped elsewhere in the line. placeCreation overwrote-and-unmarked whatever held the spawn cell, silently deleting the existing special on all four creation paths (striped swap-cell, wrapped pivot, bomb match-5, cascade lowest-cell). Rule (matches real Candy Crush): a spawn cell already holding a special is never overwritten - that special stays match-marked and fires through the existing form-then-trigger pass, and the creation relocates to the nearest plain cell of the shape (outward along both arms from a wrapped pivot; ties toward the lower flat index). A shape with no plain cell creates nothing - every special in it simply fires. - 8 directed tests (reported drag exact-scored at 180; wrapped variant + armed refire; plain-spawn regression guard; all-special run, zero creations; equidistant tie-break; bomb relocation with priority intact; wrapped-pivot; cascade relocation) - suite 57/57 green - Parity re-pinned 563660409 (C# = TS via tools/cf_parity.mjs); host harness green - Baselines re-measured on fixed rules (M50.3 bars updated): random 2613.9 greedy 3499.2 - e1 5974.6 - e2 8139.1; validation 32%; e2 gap +36.2% keeps the escalation armed; the in-flight escalation run was restarted on the fixed rules Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The page now carries a tribute paragraph: Crazy Fruits came from KidCity.be, the Belgian Flash-era children's portal; both are gone today. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…er keeps the promised special Owner report round 2: a wrapped dragged into a line of 4 still never left a new striped standing. Two-agent audit showed M50.5 was functionally correct (the dragged wrapped fired and the striped WAS created, all 13 geometries) - but the wrapped's own 3x3 always covers the nearest-plain relocation cell, so the relocated striped chain-fired in the same step and the armed second explosion re-covered it a step later. A dragged striped only spared it when the blast axis missed - exactly why striped drags looked fixed. Rule: a creation displaced off its preferred spawn cell is shielded from blasts (beams, boxes, armed refires, bomb zaps, combo areas) for the rest of the move - the fired special cannot consume its own replacement. Matches in later cascade steps still consume it normally, and creations placed on their preferred plain cell keep the form-then-trigger chain rule (the 190-point test is untouched). - shielded[] list, set only on relocation, skipped by markCell (the single blast-marking entry point), cleared by stageSwap - never outlives a move - collision tests updated to shield semantics: striped drag exact-scores 100 with the fresh striped surviving; wrapped drag 90 surviving BOTH explosions; wrapped-pivot 80 keeping an unarmed wrapped - 57/57 green - parity re-pinned 481681208 (score 95950, up from 86340: surviving specials fire later - the shield is score-positive), C# = TS verified; host harness green - the completed cf7train run (trained without the shield) is void; training restarts from scratch on the final rules Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…an and lost Final-rules gate eval (500 eps, seeds 5000+e; bars: >=3392.0 / >=4747.8 gap-share / created >=7.3 & fired >=5.6): - SHIPPED cf5train (gamma=0 + creation-shaping): 4040.4 +- 128.9 = +54.9% over random, CI-separated (gate 1 PASS, +15.1% over greedy); gap share 43% (gate 2 MISS); created 5.81 (MISS) / fired 5.75 (pass). The net transfers unchanged across the rule fixes because its per-action feature planes are computed by the live engine at inference. - The ONE pre-registered escalation (gamma=0.5 + 3-step + PBRS) got its clean from-scratch run on the final rules (cf8train, 400k): 3408.2 = +30.6%, gap share 24%, created 4.66 - worse on every gate. Bootstrapping loses to gamma=0 on match-3 refill noise even with PBRS; the M49 lesson reconfirmed. Two earlier escalation runs were voided by the in-flight rule fixes (cf6train killed at 210k; cf7train trained without the shield, only +33.4% under final rules). - Stop-loss invoked per protocol: best net ships, gates 2/3(created) are reported missed, no further training. Hold-for-combo (the +36% e2 gap) stays unclaimed by reactive nets; the recorded future lever is search-guided play, not another reward schedule. Round-over screen gains the net's challenge bar (~4 000). PRD/PLAN updated with the full verdict and final-rules baselines. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
M49 — Crazy Fruits (match-3) + primitive net
New game + AI in the KidCity (kidcity.be) Crazy Fruits spirit: swap two adjacent fruits to line up 3+, cascades chain, works with mouse and touch. PRD:
docs/prd/CRAZY_FRUITS_PRD.md(planned via a 4-agent investigation; the original Flash SWF is unrecoverable, so this ships assumed-standard match-3 rules under the confirmed fruit-stall theme).What's in it
crazyfruits_solver.pgsingle-source (C# + TS twins): rules, proportional cascade scoring (10·(k+1)/fruit, +20/+50 line bonuses), hard legal-move mask (match-producing swaps only), deadlock reshuffle in-engine (the mask never goes all-false), Schrage-minstd RNG (exact i32 both sides), 672-float observation, scripted random/greedy/expectimax-1 baselines, dueling-net forward, and a stepwise move API for the animating web host. Gate: 16 unit tests + a seeded 1,000-move episode byte-identical C#↔TS (per-move full-grid checksum 78377593).CrazyFruitsEnv(masked, stateful, truncation-only) on the M46DqnScoreCampaignspine;--game crazyfruits(+--baselines Ngate table). Gate: 500 seeded episodes — random 2259.7±49.9, greedy 2387.0±49.3 (CI-separated), expectimax-1 4270.9±98.3. Finding: cascade planning is the skill (+89%); line size is nearly irrelevant (+6%).wwwroot/models/crazyfruits.dqn.ckpt(LFS). Expectimax-1 (4270.9) is the future-training headroom./crazyfruits): canvas fruit-stall renderer (bunting + vector fruit art), animations driven by the engine's stepwise API, unified Pointer Events (one path covering touchstart/move/end and mousedown/move/up): drag-swap + tap-tap-select,touch-action: none,setPointerCapture.crazyfruits-net.tsckpt parser (byte-level reference:CrazyFruitsNetParityTests).Verification
dotnet run --project src/RLDemo.Weband visit/crazyfruitsto complete it. All headless evidence is recorded in the PRD.🤖 Generated with Claude Code