Skip to content

feat(#606): game — Skip level (forfeit to 0) for stuck players#607

Merged
atlas-apex merged 1 commit into
devfrom
feature/GH-606-skip-level
Jun 9, 2026
Merged

feat(#606): game — Skip level (forfeit to 0) for stuck players#607
atlas-apex merged 1 commit into
devfrom
feature/GH-606-skip-level

Conversation

@atlas-apex

Copy link
Copy Markdown
Collaborator

Summary

  • New "Skip level" option for players who get stuck. A persistent SKIP button in the footer (next to HINT), shown during every level's play phase and hidden on teaching slides / intro / outro / after a level is submitted.
  • Tap-to-confirm guard — first tap arms it ("SKIP? (0 pts)" + a footnote warning), a second tap within 4s actually skips; it auto-disarms. Prevents accidentally zeroing a level.
  • On confirm: records 0 for the current level (total score unchanged), then calls the game's universal advance() to the next concept — including correctly going to the outro when the last level is skipped.
  • Zero new behaviour in the 11 level renderers — reuses the .hint-btn styling and the existing advance() flow; one footer button + one skipLevel() function drives all levels.

Testing

  1. node --check on the inline script — clean; LEVELS still 11.
  2. On the deploy preview / local: during any level, tap SKIP → it arms + warns; tap again → advances and that level shows 0 in the final tally. Skipping the final level → outro. SKIP is absent on slides, intro, outro, and after submitting a level.
  3. Works by touch on mobile (it's a plain button); no desktop regression.

Closes #606


Glossary

Term Definition
Play phase state.phase === 'play' — the interactive challenge (vs. the teaching 'slide'). SKIP only shows here.
advance() The game's single transition function: intro → [slide → level] × N → outro. Skip routes through it so navigation stays consistent.
Tap-to-confirm A two-tap pattern (arm → confirm) used instead of a modal, so it works cleanly on touch without a confirm() dialog.

Adds a persistent SKIP button in the footer (next to HINT), shown during every
level's play phase and hidden on teaching slides / intro / outro / after submit.
Tap-to-confirm guard (first tap arms + warns, second tap within 4s skips) so a
stuck player can't zero a level by accident. On confirm: records 0 for the level
and calls advance() to the next concept (or outro on the last level). Reuses the
.hint-btn styling and the universal advance() flow — no per-level edits, no new
behaviour in the 11 render functions.

Closes #606

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@atlas-apex atlas-apex left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review: PR #607

Commit: a728befe09ac631ef9909ff82a5d4955b5055543

Verdict is APPROVED. Posted as a comment because GitHub blocks self-approval on a single-account setup — the Rex approval marker has been written for the merge gate.

Summary

Adds a "Skip level" (forfeit-to-0) feature to the public site/game.html. A SKIP button is grouped with HINT in a new .footer-btns flex container; a skipLevel() function implements a tap-to-confirm guard (arm + warn on first tap, skip on second tap within 4s); SKIP is shown/hidden at the correct flow points. +33/-1, single file.

Checklist Results

  • Architecture & Design: Pass — single self-contained static page; reuses the existing advance() transition function rather than adding a parallel flow.
  • Code Quality: Pass — small focused function, clear comments, defensive == null guard before writing the 0 score.
  • Testing: N/A — static site, no test suite. node --check on the extracted inline script passes clean (verified independently).
  • Security: Pass — no user input, no injection surface, no secrets. Pure client-side DOM.
  • Performance: Pass — one CSS rule, one button, one function. No hot-path impact.
  • PR Description & Glossary: Pass — Summary + Testing + Glossary all present; Closes #606 (OPEN); narrative bullets.
  • Summary Bullet Narrative: Pass — bullets state what changed AND why (e.g. tap-to-confirm "prevents accidentally zeroing a level").
  • Technical Decisions (AgDR):N/A — UI feature, no library/framework/architecture decision introduced.
  • Adopter Handbooks: N/A — no architecture/general handbook rules apply to a single static HTML file; no language handbook matches .html.

Verification performed

  • Syntax: extracted the single inline <script> block and ran node --check → clean. LEVELS array still has 11 render entries.
  • Skip flow correctness: skipLevel() early-returns unless state.phase === 'play' AND no .result-bar is present in the stage. After a normal submit, state.phase stays 'play' until Continue is clicked — so the .result-bar check is the load-bearing guard that prevents skip-after-submit (belt-and-suspenders with the button being hidden in showResult). Both guards confirmed necessary and correct.
  • Last-level routing: skip calls advance() while phase === 'play', hitting the else branch → state.level++; when state.level >= LEVELS.length it routes to renderOutro() and returns. No crash, no out-of-bounds LEVELS[state.level] access. Verified.
  • Scope: skipBtn, skipArmed, state, advance are all module-level; skipLevel is a hoisted function declaration, so the skipBtn.onclick = () => skipLevel() wiring above its definition is fine.
  • Score model: skip records 0 into state.levelScores[state.level] (per-level breakdown) guarded by == null, and does NOT touch state.score (running total) — total is correctly unchanged and the outro tally shows 0/100 for the skipped level. Verified against all state.score / levelScores mutation sites.
  • No double-fire / bad state: SKIP is hidden in renderSlide, renderIntro, renderOutro, and showResult; re-shown only in advance()'s slide→play branch. skipArmed resets on the 4s timeout and on entering each play phase.
  • HINT regression: HINT keeps its own id="hintBtn" and its per-level onclick handlers; SKIP shares only the .hint-btn styling class. No duplicate IDs. No regression.
  • Footer layout: .footer-btns wraps both buttons in a flex group; the footnote <span> remains a sibling. Pure cosmetic grouping.

Issues Found

None blocking.

Suggestions

  • nit (non-blocking): benign self-healing race in the 4s arm timeout — if SKIP is armed and the player then transitions to a different level within that window, the stale timeout's disarm could clear a freshly-armed state on the next level, costing at most one extra tap. It cannot leave the game in a bad state (advance() resets skipArmed on each play phase). Optional hardening: capture the round in a closure and no-op the timeout if it changed. Not required for ship.

Verdict

APPROVED


🤖 Reviewed by Rex (Code Reviewer Agent)
📌 Reviewed commit: a728befe09ac631ef9909ff82a5d4955b5055543

@atlas-apex atlas-apex merged commit ebd3582 into dev Jun 9, 2026
7 checks passed
@atlas-apex atlas-apex deleted the feature/GH-606-skip-level branch June 9, 2026 11:12
atlas-apex pushed a commit that referenced this pull request Jun 9, 2026
The merge of dev brought the Skip-level feature (#607/#606) into this release;
record it in the v3.1.1 entry (### Added) and Closes line. Still a patch — game
is marketing-site content, no framework API change.

Refs #604
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants