Repository navigation
feat: Kids Math Duel — 2-player head-to-head voice math game for families - #375
Merged
Merged
Conversation
Head-to-head voice math game targeting families and kids, a user segment entirely absent from the catalog. Ten questions alternating between two players, 8-second soft countdown timer, and a steal mechanic that keeps both players engaged even when it's not their turn. Key design decisions: - Pure Python math generation (random.randint) — no LLM per question, instant and deterministic - Soft timer via session_tasks.create() parallel countdown so user_response() is never blocked; epoch counter prevents countdown bleed across questions - Steal mechanic does NOT reveal the answer before the steal attempt; answer is only spoken if the steal also fails - LLM used only for post-game debrief (2-sentence fun summary) - Per-player leaderboard (best score, best streak, games played) persisted via create_key/update_key upsert pattern - validate_ability.py clean, flake8 clean
Contributor
✅ Community PR Path Check — PassedAll changed files are inside the |
Contributor
🔀 Branch Merge CheckPR direction: ✅ Passed — |
Contributor
🔍 Lint Results🔧 Formatting fixes availableThis PR is from a fork, so fixes could not be pushed automatically. Download the git apply autofix.patch✅
|
Contributor
✅ Ability Validation Passed |
- _is_exit() was only checked at setup (name/difficulty), never during an actual answer turn - saying "stop" mid-game fell through to the LLM fallback in _check_answer() and just scored as wrong. - _ask_with_timer() awaited user_response() alone, with the countdown firing "Time's up!" in parallel but never unblocking that await - a silent player left the whole ability hanging with no recovery. Race the listen against the timer instead of awaiting the listen alone; whichever finishes first wins and the other is cancelled. An exit word on either the main answer or a steal sets _exit_requested, which now breaks _run_game's loop and skips the result announcement and leaderboard save in _run(), matching how the existing setup-time exits already behave.
Contributor
|
Pushed a fix for the two issues from review:
Tested locally against the patched file (silent-past-timer, answer-in-time, exit-mid-question, full-loop-quit) — all resolve correctly. |
uzair401
approved these changes
Sep 18, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
What makes it different
vibe-triviaspelling-bee-coachImplementation notes
Timer (epoch counter):
session_tasks.create()launches the countdown in parallel whileuser_response()waits for input. An epoch counter (_timer_epoch) prevents a previous question's countdown from firing into the next question when the user answers quickly.Steal mechanic: Wrong answer or timeout → opponent is asked the same question, without revealing the answer first. The answer is only spoken if the steal also fails.
Difficulty tiers:
+and−, numbers 1–20+,−,×, times-tables range (1–12)+,−,×,÷(division always whole-number), up to 50×12Patterns followed:
get_single_keyunwrap:.get("value", raw)✅create_key+ success check →update_key✅text_to_text_response()calls wrapped in(... or "")✅resume_normal_flow()infinallyblock ✅asyncio, noprint(), no hardcoded keys ✅Test plan
validate_ability.py✅flake8✅