Skip to content

Practice Mode

Practice mode lets users solve saved TrainingPuzzle entries from their own collection.

Unlike trainer playback validation, Practice mode validates answers by comparing the final board state (normalized FEN) rather than the exact sequence of micro-moves.

Practice mode has two usage variants:

  • Authenticated practice session (PracticeView + practiceStore) with spaced-repetition attempt tracking.
  • Shared puzzle screen (SharedPuzzleView) for a single public puzzle link without navigation menus.
  1. User starts a session from PracticeView.
  2. Frontend requests the next puzzle from GET /api/v1/training-puzzles/next.
  3. ActivePuzzle renders:
    • interactive board
    • puzzle dice
    • live timer
  4. User manipulates the board and clicks Check Answer.
  5. Frontend extracts the piece placement part of the current FEN and compares it to the puzzle’s normalized_final_fen piece placement part.
  6. Frontend sends attempt result to POST /api/v1/training-puzzles/{id}/attempts with elapsed time.
  7. User moves to the next puzzle.

practiceStore drives one explicit state machine:

  • idle — lobby view, waiting for user action.
  • playing — one active puzzle loaded and interactive.
  • finished — reserved for future session summary mode.

Primary transitions:

  • startSession()idle -> playing
  • fetchNextPuzzle() keeps playing and swaps currentPuzzle
  • endSession() resets to idle
  • src/views/PracticeView.svelte
    • Handles lobby/start flow and renders the active puzzle session UI.
  • src/components/ActivePuzzle.svelte
    • Puzzle interaction container (board, timer, reset, check, feedback, next).
  • src/lib/practiceStore.svelte.ts
    • Session state + API orchestration (startSession, fetchNextPuzzle, submitAttempt, endSession).
  • src/components/ChessgroundBoard.svelte
    • Reused in prop-driven mode for puzzle board state and reset signals.
  • src/components/DiceBox.svelte
    • Reused with dice overrides for puzzle dice display.
  • src/components/TopNavigationBar.svelte
    • Practice actions: share puzzle, open source game, notes, deactivate.

Validation uses exact equality on the piece placement FEN field only (the first field).

  • getFenBoardPart(current_fen) === getFenBoardPart(solution_fen)

Active color, castling rights, en-passant targets, and move counters are ignored during validation.

This keeps validation aligned with puzzle outcome semantics in Dice Chess, where multiple micro-move paths can still produce the same correct final position.

  • Correct/incorrect result is sent to POST /api/v1/training-puzzles/{id}/attempts.
  • Backend updates denormalized counters (success_count, failure_count, last_attempted_at).
  • Deactivate action sets is_active=false and immediately requests the next available puzzle.

When the user presses Share in practice mode, the app creates a puzzle link, not a game-position link:

  • Share URL: /api/share/puzzle/{puzzle_id}
  • OG image: /api/v1/training-puzzles/{puzzle_id}/preview.png
  • Redirect target: /?puzzle={puzzle_id}

The shared target is an isolated “Guess the Move” screen without global app navigation.

  • Timer starts when a puzzle is loaded and visible.
  • Timer updates continuously in the UI (MM:SS).
  • Resetting the board position (Reset button) does not reset the timer (the user is still thinking).
  • Timer stops at answer check.
  • Exact elapsed milliseconds are sent as time_spent_ms for spaced-repetition statistics.
  • frontend-pwa/src/views/PracticeView.svelte
  • frontend-pwa/src/components/ActivePuzzle.svelte
  • frontend-pwa/src/lib/practiceStore.svelte.ts
  • frontend-pwa/src/utils/fenUtils.ts
  • frontend-pwa/src/lib/api/client.ts
  • frontend-pwa/src/views/SharedPuzzleView.svelte