PyChallenge
{{ loadingMsg }}
Loading your exercises
+{{ xpPopup.amount }} XP
{{ activeTrack.emoji }} {{ activeTrack.label }}
{{ trackDone }}/{{ totalExercises }}
Today
Appearance
Mode
Shortcuts
Run code{{ runKeyLong }}
Previous / next track[ ]
Undo a reset{{ isMacKey }}Z
Close menusEsc
Theme
{{ streak }}
{{ xp }}
{{ currentExercise ? currentExercise.topic : activeTrack.label }} {{ topicPos }}/{{ topicList.length }}

Exercises {{ EXERCISES.length }}

{{ currentExercise ? 'Continue where you left off' : 'Start here' }} {{ (currentExercise || nextInTrack(selectedTrack)).title }} {{ activeTrack.emoji }} {{ activeTrack.label }} · {{ (currentExercise || nextInTrack(selectedTrack)).topic }}
{{ completedCount }} solved {{ streak }} day streak {{ streakSafe ? '· kept today' : (streak ? '· at risk' : '') }} {{ xp }} XP Today

{{ activeTrack.emoji }} {{ activeTrack.label }} · {{ browseCount }} match{{ browseCount === 1 ? '' : 'es' }}

{{ trackPct(selectedTrack) }}% done
{{ topic }} {{ exercises.filter(e => isCompleted(e.id)).length }}/{{ exercises.length }}
{{ topic }} {{ exercises.filter(e => isCompleted(e.id)).length }}/{{ exercises.length }}
Nothing in this track matches “{{ search }}”.
Progress & streaks
{{ nextMilestone.left }} more to {{ nextMilestone.target }} solved {{ completedCount }} / {{ nextMilestone.target }}

{{ currentExercise.title }}

{{ currentExercise.difficulty }} +{{ currentExercise.xp }} XP
Expected output
{{ currentExercise.expectedOutput }}
💡 {{ i }}
{{ currentExercise.hints[i - 1] }}
Working solution
{{ solutionText }}
🐍
Pick an exercise to start
solution.py
{{ pyodide ? 'Python 3.11' : 'starting…' }}
⌘Z to undo
{{ pyodide ? 'Python 3.11 · Pyodide' : 'starting runtime' }} {{ runKeyLong }} to run Ln {{ cursor.line }}, Col {{ cursor.col }} UTF-8 Python
tap to open
Passed{{ lastEarned ? ' · +' + lastEarned + ' XP' : '' }}
Output doesn’t match