Skip to content

Latest commit

 

History

History
268 lines (256 loc) · 20.9 KB

File metadata and controls

268 lines (256 loc) · 20.9 KB

GridBot — Backlog

The living list of planned/possible work. The README shows only the juiciest highlights and links here. Items are grouped; checked = done, unchecked = future.

Playtest ideas — make it more fun (from on-device playtesting)

  • Achievements / badges (17): First Steps, Bright Spark, Hopper, Looper, Architect, Sixth Sense, Champion, On Fire, Unstoppable, Explorer, Veteran, Artist, Star Collector, Brainiac (train a brain), Mind Over Maze (clear a level with a brain), Battle-Ready (save a fighter), and Generalist (one brain clears the held-out gauntlet). Sticky bitmask in the profile, count on the stats header, celebrated on the level-intro when earned, + a badges gallery screen with icons (reachable from Stats).
  • Juice: smooth tile-to-tile tween, breadcrumb trail, character shake on bonk, and a win celebration with a multicolour confetti burst + animated star fly-in (each earned star pops into the YOU WIN overlay over dim placeholders).
  • Character emotes: happy face on win, dizzy/✖-eyes on bonk. (Future: pit splash.)
  • Reorder program lines — done via Up/Dn buttons on the selected line (moveSelected); drag-to-reorder is the unbuilt nicety.
  • An Undo button for edits.
  • Level biomes/themes: floor/wall palette varies per level band (Meadow, Cavern, Glacier, Circuit, Nebula) so progress feels visual.
  • One-line par hint ("par N") shown in the game chrome so kids can aim for the 3-star efficiency target. (Future: an in-game speed slider for animation pace.)
  • Shared-seed challenge: a 4-digit code picks one fixed-difficulty board, so friends entering the same code race the identical maze. Reached from the Arena menu; wins award no campaign progress/coins (not farmable) — just the star rating.
  • Coin collectibles + a shop for sprite colours and emojis (ties to the pixel editor and the coin-economy motif). Coins sit on the path; STAR gems are a rarer bonus on a detour off it (see Campaign polish).
  • Music: non-blocking chiptune menu theme (loops on select/intro) + a win fanfare + a badge chime; SFX briefly override and the melody resumes, plus an in-game mute toggle on the home screen and a distinct minor-key battle theme on the Arena / Puzzle Race menus (stops cleanly before a match's step-tick SFX).

Campaign polish (deferred niceties)

  • CodeLab lesson revamp — each lesson now performs its own "aha". Repeat opens the LONG way (five stacked forwards) and a Shrink it > button collapses them into one repeat 5 (the pain first, the loop as the cure). Sense gains a New maze > button that swaps between four spiral shapes while the 3-block wall-follower stays untouched — the generalisation aha inside the lesson (the HANDBOOK's "test one program on three mazes", now one tap). Functions is the zigzag staircase: F1 = one stair-step (turnL fwd turnR fwd), MAIN = repeat 5 { F1 } — 20 blocks become 6 and the program reads like a sentence. All demos regression-verified by tools/lesson_check.cpp (host harness: every lesson's exact board+program must WIN).
  • Seen anywhere = seen: watching a lesson from Learn → CodeLab now also sets its campaign auto-play bit (no double-showing for course-followers); serial debug jumps (L/fast-play) suppress auto-lessons for the session so scripted captures land on the level card.
  • Onboarding: auto-play each power's lesson the first time it unlocks. Powers used to only offer an opt-in "Learn it >" button on the level-intro card, and level 1 (Move) had no button at all (it unlocks no new power), so a brand-new kid was dropped straight into the maze with no guided intro. Now the first time the kid reaches the level that introduces a CodeLab power (Move @ L1, then Jump / Repeat / Sense / Functions as each unlocks) the game auto-plays that lesson once at the teachable moment, then lands on the level card to play. Once-only across reboots via a per-lesson Profile.tutorialsSeen bitmask; gated on level == _profile.level (their frontier) so replaying an old level never re-triggers. The intro card's "Learn it >" button is kept (and now shown on level 1 too) so any lesson is re-watchable on demand. NeuroBot lessons (L28+) stay opt-in (button only — not force-opened). Implemented via shared App::introLessonFor / launchIntroLesson helpers (single source of truth for the button and the auto-play); reuses the existing _fromIntro lesson-return path.
  • Brick-red walls + void (background-colour) pits so hazards read clearly.
  • Program list scrolls (auto-follows newest + tappable scrollbar) and the pane extends full-height — no command-count limit on screen.
  • Bigger chrome titles; compressed unlock curve (sensing at L15, not L55); multi-maze challenges interspersed every 5th level past sensing.
  • Failure feedback on the maze before returning to code (see below).
  • Smooth tile-to-tile character tween during animation (glides between tiles).
  • Press animation on buttons (brief depress + release) beyond the colour change.
  • Optional persistent mini-map in the Code view (SPEC §10; per-profile setting, default off). Field reserved in Settings.miniMap.
  • Collectibles (coins/stars to grab before the goal) — tile types COIN/STAR. Coins sprinkle on the solution path (passive bonus currency). STAR gems are a rarer bonus placed on a reachable detour OFF the path, so you must steer to grab them; collecting every gem on a board pays an all-clear coin bonus on the win. Both spend in the shop. Generation is gem-reachability unit-tested.
  • Gem Hunt / gems-before-goal (backlogged 2026-06-25). Today gems are a purely optional bonus — you win by reaching the GOAL regardless. Make them a required objective: the goal won't complete until every gem on the board is collected. Most of the plumbing already exists (placement, render, _gemTaken/_gemTotal/recountGems, all-clear payout) — the work is gating the win in GameScreen::settleOutcome behind a per-level "gems required" flag (so it doesn't retroactively harden existing levels), plus, for bot support, a GEMS_LEFT? condition + gem sensing reusing the soccer objective-sensing pattern (target = nearest uncollected gem, then the goal). Scope fork (user's call): per-level flag (recommended) vs every-gem-level vs a separate "Gem Hunt" level type/mode.
  • Fog levels (limited view) as an alternative sensing backdrop (SPEC §7.1). We ship the one-program-many-mazes variant instead.
  • ELSE branch and ON_COLOR condition (intentionally deferred, SPEC §16.4).
  • Archive/"favourite" a solved-level solution for replay (SPEC §16.7).
  • Profile rename (delete is done via long-press; rename UI TBD, SPEC §16.6).
  • Animation-speed + sound toggles surfaced in an in-game settings/pause panel (stored in Settings and honoured at runtime, but not yet user-editable on device).
  • Debounced periodic autosave of the resume slot mid-edit (currently saved on level transitions; power-off mid-edit can lose the in-progress script).
  • Star celebration animation (fly-in stars) + per-level best-stars display.
  • Condition picker for sense blocks (WALL/PIT/GOAL cycling) — basic cycling done; UX polish (a popup picker) still open.
  • Library picker UI: name entry on save + choose-which-entry on load (currently auto-named "Lib N", load pulls the most recent only).
  • Richer multi-ring spiral / varied sensing mazes (current sensing levels use a clean single-ring "C-spiral" that the wall-follower provably clears).

Engine / generation

  • Richer maze topology (branches/dead-ends) beyond the biased-walk corridor.
  • Multi-maze "generalization" set generation tuned so one hand-rule clears all (SPEC §7.1 generation note) — basic version in Phase 5.
  • Par tuning per tier (loops/functions rewarded more explicitly).

Multiplayer modes (the "mix")

  • Race (reach goal first) and Sumo (PUSH into a pit) vs House AI or Hotseat.
  • Soccer (shove a ball into your goal) vs House AI or Hotseat — see Arena mode below.
  • Radio battle + Pokemon-style trade (ESP-NOW) — hardware-pending.
  • Puzzle Race (shared maze): both players get a 90s timer to write code for the SAME maze; whoever's bot ends up closest to the goal (BFS distance, 0 = reached) wins. A code-authoring contest, not a live battle. Hotseat lock-in handoff.
  • More mode types: relay, co-op (two bots must both reach goals), king-of-the-hill.

Arena mode (SPEC §18 — post-campaign capstone)

  • Deterministic N=2 tick engine + Race match type + resolution pass (collision bounce, fall-out, win check). Determinism proven by byte-identical log test.
  • Symmetric arena generation (mirrored hazards, equidistant starts).
  • Race demo screen: player's library bot (or wall-follower) vs a house AI bot.
  • Sumo match type on the shared tick engine — the attack is zap (FIRE); the old PUSH verb was removed and the hunter bot fixed to zap. A trained brain's zap shoves too (the interpreter exposes the effective lastCmd() so the arena can resolve a brain's N_NEURO action).
  • Fuller AI bot roster — Rusty (always-forward), Bolt (dasher), Vex (ENEMY_NEAR hunter), Ace (solves the board on the fly) — + an opponent picker UI.
  • Soccer match type — a walled pitch (MazeGen::generateSoccerPitch) with a symmetric 4-tile goal mouth at each end; bots shove a ball one tile when they step onto it; timed multi-goal matches with a live scoreline (most goals at the cap wins; level → pressure-accumulator tie-break → no draws: _pressure sums ball-to-net progress each tick and decides a level scoreline). Own-goals are impossible: a push that would put the ball in the mover's OWN net auto-triggers a zap-swap (bot turns 180°, ball repositioned goalward) and a jammed ball deflects toward the attacker's goal — verified 0% own-goals across the roster (tools/owngoal_check). Fully deterministic (ball + score folded into the match hash): a random-scatter referee rehomes a long-stalled loose ball, a random kickoff after each goal, and faster deadlock resolution (8-tick rehome when both bots pin the ball to a wall, vs 28 otherwise) break bot-vs-bot deadlocks. Sensing reuses the 10-input brain layout via EnemyView.target — the brain senses the ball as its objective (aheadness/rightness/distance), its goal bearing, and the rival bearing — so no net-shape or saved-brain change. Hand-codeable too: BALL_*/NET_* conditions + AND/OR compound ifs (if A & B / if A | B). A pre-trained house team with soccer names — Strika / Dribbla / Volley / Nutmeg / Boots. Trainers: Teach (distillSoccer/distillSoccerRnn), Evolve (soccer fitness), Q-Learn (qTrainSoccer/qTrainSoccerRnn, with own-goal-penalising reward shaping). Wired into vs-Computer, Hotseat, Tournament (Cup/Ladder), and the networked Room, with fighter names shown in the match header (P1 (Strika) / P2 (player v4), You (…) vs-CPU). Demo GIF: docs/img/soccer.gif.
  • Loser-levels-up loop. After a match, Train opens the trainer pre-set to the SAME sport sparring the SAME opponent (beginVs / _pendTrain*), the soccer house team is sparrable in the trainer roster (mode-aware SOCCER_OPP), and each trainer screen shows a base-brain label (base: a fresh brain / your code's brain vs Strika) so it's clear what the training builds on.
  • Pick-screen UX — the bot/foe pickers got a Back button + scroll arrows and tap-guards (visible-rows-only, buttons checked first) so scrolling no longer mis-selects.
  • Networked tournament (Room) — VERIFIED on two boards (COM3 + COM5). ESP-NOW multi-device Cup: every board broadcasts its fighter, all ingest into a uuid-sorted roster (identical order, no central authority), the host broadcasts a shared SEED, and each device replays the SAME bracket locally from that seed (deterministic Arena → same champion everywhere). The SEED packet carries a discipline byte (Race/Sumo/Soccer), so a Room Cup can be any discipline. BotCards are chunked (CBEGIN/CCHUNK/CEND + per-sender-MAC reassembly), so each board brings its real saved fighter — any size — not a compact stand-in (only a player with an empty library falls back to a coded hunter); the Cup auto-advances in lockstep on every device. IA: Room moved into the Radio menu; Train-a-fighter promoted to the Arena top menu.
  • Author the arena bot in the Code view (currently uses the latest library entry).
  • Hotseat (2 kids, 1 device) lock-in / handoff screen (Arena + Puzzle Race + Soccer).

NeuroBot / on-device ML

  • Draw-the-path training (imitation learning by demonstration). DONE: the "Train the brain" page now has a Draw button beside Teach/Evolve. The kid taps tiles to lay a forward/jump route from the start, then Learn it distills the brain to copy that path (distillPath / pathToProgram reuse the distill backprop loop). Because it trains the working brain in place, picking a saved base first = fine-tune the old net on the newly drawn path for a level it struggled with.
  • Enemy senses exposed in maze mode — the campaign if/until cycler now offers enemy/near too (no-op without a foe), so a kid can author & test arena-bot logic while playing the campaign.
  • "Generalist" prize badge. DONE (redesigned to be winnable). A frozen reactive brain can't plan whole campaign mazes (verified: best ~1-2/50), so the prize uses a Generalization Gauntlet instead: game/Gauntlet.cpp makes reactive-solvable open mazes (verified by a memoryless navigator), a Generalize button distills the brain to imitate that navigator across a training set, and the badge is earned when the FROZEN brain clears all GAUNTLET_MAZES (10) HELD-OUT test mazes it never trained on. Winnability is regression-tested (test_gauntlet_is_winnable). Best run shown on Stats.
  • NeuroLab "One brain, many jobs" (lesson 17) — the discipline walk the hub was missing. The SAME 10→8→5 brain is taught three jobs back-to-back on real engine traces: maze (senses the goal; distillSolver), battle (senses the foe; distillHunter KOs a parked spinner on the sumo ring — the board family it trains on; walled arenas stall a reactive hunter), soccer (senses ball+net; distillSoccer dribbles one in vs an idle foe by ~tick 9). Takeaway page: "one net, new job = new senses + new data — not a new brain", ending with the hub's only explicit handoff to Arena → Train a fighter. Was the NeuroLab gap: 17 lessons, battle in exactly one (Self-play), soccer in zero, despite soccer being the course's Day 4-5 destination. Demos seed/epoch-verified by tools/lesson_check.cpp.
  • More ML topics the same engine could host (from the lessons discussion): regularization/overfitting, batching, reward shaping, exploration-vs-exploitation sliders, and a tiny recurrent/memory cell.
  • Lesson → trainer deep-link: a "Train yours >" button on the One-brain-many-jobs takeaway (and maybe Self-play) that jumps straight into Arena → Train a fighter (today it's a text pointer).
  • A CodeLab Battle/Soccer hand-coding lesson (the Day-3 hunter priority-list + get-behind-the- ball dribbler as tappable demos) — needs the Arena engine inside CodeLessonScreen; today Day 3 teaches these via the Hand-Coding Guide + the Arena itself.

Code architecture / refactor

  • Migrate GameScreen onto the shared ProgramEditor. The authoring UI (control pad + nested program list + tap handling) was extracted into src/screens/ProgramEditor.{h,cpp} and adopted by Puzzle Race and CodeLab, but GameScreen still carries its own copy of that code — so the editor currently lives in two places. Have GameScreen delegate to ProgramEditor to remove the duplication; do it carefully with full on-device re-verification (all blocks, nesting, save/load, neuro train, scrolling, fail-highlight).

Hardware / platform

  • Two 2.8" CYD variants: cyd_gridbot (dual-USB) + cyd_gridbot_elegoo (Elegoo USB-C). Both share all code/pins (ILI9341, ESP32-2432S028R) and differ only by panel-mount orientation, so each is a set of -D flags ([panel_usb2cyd] / [panel_elegoo] in platformio.ini), not a fork — mirroring ../BladeKey-Overhead's Board.h. Elegoo is portrait-native (240x320, rotation 1, no touch-axis invert); the dual-USB is landscape-native (320x240, rotation 6, reversed R/B, touch inverted). Panel geometry moved behind #ifndef guards in hal/LGFX_Config.h. Both build; the dual-USB merged bin is flash-verified on a real board. (Elegoo rendering still wants a pass on an actual Elegoo unit — none on hand.)
  • Resolve the §1.1 panel question fully on the real units (ILI9341 vs ST7789, invert/BGR) — the two ILI9341 variants above cover the known boards; ST7789 units remain unconfirmed.
  • Two-CYD ESP-NOW radio for networked deterministic matches (SPEC §18.4) — the Room tournament is verified on two boards (COM3+COM5); the 1:1 Radio battle/trade path is built but still wants a focused two-board pass.
  • 4" ST7796 and ESP32-S3 CrowPanel targets (extra envs).

Tooling (PIO_DEBUG loop)

  • tools/bot_eval.cpp host harness + captured output. Hand-coded vs trained bots across maze/battle/soccer + the soccer recipe bake-off, on the real Arena engine. Deterministic; the committed run lives in docs/bot_eval_output.txt (with a reproduce-me header) so the eval numbers the docs quote can't silently drift — regenerate after any brain/recipe/physics change. (The .exe is gitignored; build with MSYS2 g++, see the file header.)
  • Serial Y n lesson jump (capture aid): jump straight to a lesson screen for deterministic screenshots — Y 0..6 = CodeLab lesson n, Y 17 = "One brain, many jobs", Y 50 = the Arena trainer. Sets the debug no-auto-lesson flag (no profile bits burned).
  • USB-serial PIO_DEBUG loop: scripts/shot.py (screenshot via panel readback -> PNG), scripts/drive.py (tap + screenshot driver), serial commands S/T/L/X in firmware. Verified on the real CYD over COM3 — no WiFi needed.
  • Optional WiFi remote debug API (ElegantOTA /update, /api/screen.jpg, /api/tap|swipe|status) as an alternative to the serial loop.
  • Real on-device screenshots captured for verification (and available for README).

Hardware-testing fixes (found by flashing to the real CYD)

  • Back/pause button in the game returns to the profile menu (SPEC §10).
  • Failure feedback: reddened + shaken character + message on the maze, then a tap to the Code view with the failing instruction flashed red (SPEC §8.3) — no longer snaps straight back.
  • Per-line delete: select a row + a big under-pad DEL button (40px target); fixed a dangling fail-highlight pointer after delete.
  • Profile edit (name/avatar) from the stats screen, keeping all stats/progress.
  • Per-profile UUID for cross-session friend recognition (in the radio friend-card).
  • Touch reliability tuning on the resistive panel (lower Z-threshold / re-cal flow); X serial command clears calibration to force a re-cal.

Distribution

  • Online flasher: a GitHub Pages page (docs/) with ESP Web Tools so anyone can flash the current build from the browser over USB — no clone/toolchain needed. Now offers both 2.8" CYD variants via a board picker: firmware/gridbot-usb2cyd-merged.bin (default, dual-USB, manifest[-usb2cyd].json) and firmware/gridbot-elegoo-merged.bin (manifest-elegoo.json). The dropdown swaps the install button's manifest. Regenerate the merged bins per release with esptool merge_bin (0x1000 bootloader / 0x8000 partitions / 0xe000 boot_app0 / 0x10000 firmware, per env under .pio/build/).
  • CYD purchase link in the README + flasher page for people who need the hardware.
  • Tagged GitHub releases with versioned merged-firmware artifacts + a changelog.
  • Animated gameplay/arena GIFs auto-regenerated from on-device capture in CI.