Challenge format: folder, stages, questions and tests
A challenge is a repository folder with a challenge.md file, stages in stages/, questions and tests in checks/ and starting files in starter/. All of it is ordinary markdown and code — written by hand or edited after generation.
In this article
The folder#
A challenge is any repository folder that contains challenge.md. A repository can have any number
of challenges, and the repository root is a folder too. Challenges are read from the default branch.
parser/
challenge.md title, description, level, tags
stages/
01-why.md a stage without checks — theory
02-questions.md a stage with questions
03-code.md a stage with code and tests
checks/ answers and tests
02-questions.md questions of stage 02
03-code/test_parser.py tests of stage 03
starter/ what the attempt repository starts with
parser.py
- stages/ — the stages. Their order is the order of the file names, so names start with a number
with a leading zero:
01-…,02-…,10-…. The name without.mdis the stage address:/{owner}/{repository}/learn/{folder}/stages/02-questions. A challenge at the repository root is written as-in the address. - checks/ — question files with marked answers, and tests. They are not shown on the stage screen and are not copied to the attempt repository. But they are ordinary repository files: in a public repository anyone can open them — like the answers at the back of a textbook.
- starter/ — the files the attempt repository starts with. Needed only if the challenge has stages with file checks.
A folder with challenge.md inside another challenge's starter/ or checks/ is part of that
challenge's content, not a separate challenge.
challenge.md#
The header is flat — only strings and lists, no nested objects:
| field | what it holds |
|---|---|
title |
the title — required |
summary |
one line for the list of challenges |
language |
the language of the texts: ru, en |
level |
beginner, intermediate or advanced |
tags |
a list [git, basics] or a single word |
source |
the path to the material the challenge was made from, relative to the challenge folder: ../git.md. The file must exist; the challenge page shows a «The material it was made from» link |
The body is the introduction: why this challenge and what you will have at the end. The challenge page shows it.
A stage#
A stage is a stages/NN-name.md file: a header and the text of the task or theory in markdown. Three
header fields describe the stage:
| field | what it holds |
|---|---|
title |
the stage title — required |
summary |
a short description |
minutes |
how many minutes it takes, as a whole number — «5 min» in the list of stages |
Every other header key is a check. An unknown key is a format error.
| key | how to write it | the stage counts when |
|---|---|---|
quiz |
quiz: checks/02-questions.md |
every question of the file has been answered correctly; wrong ones come back at the end of the round |
self_check |
self_check: [item, item] or a list of - item lines |
all items are ticked — you check yourself |
files |
files: [notes.md, src/parser.py] |
the files exist and are not empty |
contains |
contains: and lines path: text or path: ["text", "text"] |
the file contains every text, case-insensitive |
min_words |
min_words: and lines path: 150 |
the file has at least that many words; the file's header does not count |
numeric |
numeric: and lines path: answer.md, answer: 42, tolerance: 0.5 |
the last number in the file matches the answer within the tolerance; the default tolerance is 0, comma and dot are equal |
tests |
tests: checks/03-code/test_parser.py |
Python (.py) or JavaScript (.js) tests passed in the sandbox |
How it adds up:
- A stage without checks is theory. The Read, next button counts it.
- Checks combine. A stage counts when all its parts pass: questions, self-check and files. On the stage screen this is the «What is checked» list.
- Two kinds of paths.
quizandtestspoint at challenge files — relative to its folder, and the file must exist.files,contains,min_wordsandnumericpoint at files in the attempt repository — relative to its root. A path cannot leave the root through... - File checks (
files,contains,min_words,numeric,tests) read the attempt repository at its latest commit. A challenge with such stages gets an attempt repository at start, see Challenges. - The stage kind in the list comes from its checks: «Task» — there are file checks, otherwise
«Questions» — there is a
quiz, otherwise «Self-check», otherwise «Theory».
A stage header with a file check, like notes taken with the Cornell method:
---
title: Write your notes
files:
contains:
notes.md:
min_words:
notes.md: 150
---
A calculation with a numeric answer, and a self-check:
---
title: How long did it take
files:
numeric:
path: speed.md
answer: 250
tolerance: 1
---
---
title: Check your notes yourself
self_check:
- Every notes block holds a thought in your own words, not a quote
- The summary fits into five sentences and names the main point
---
The question file#
A stage's questions live in a separate markdown file in checks/, and the stage header refers to it:
quiz: checks/02-questions.md. The file reads like an ordinary note in any editor.
- A question is a second-level heading
## …. A first-level heading and the text before the first question are an introduction, not a question. - The heading ends with an id in curly braces:
## What is HEAD? {#head}. An id is lowercase Latin letters, digits,-and_, up to 64 characters, unique across the whole challenge. The attempt's progress and the review schedule live by the id: you can edit the question text, but changing the id starts the question from zero. - The question kind is recognized by the shape of the answer. It can be named explicitly with a
class next to the id:
{#head .card}. - The body is read from the end. The last
> …lines are the explanation shown after answering. Before them is the answer block. Everything above is the question text, so a numbered list or code in the question is not confused with the answer. One answer block holds one kind of lines: options, order, pairs or= …answers.
| kind | how to write it | what the user does |
|---|---|---|
choice (single) |
options - [ ] text, one right one - [x] text |
picks one option |
several answers (multiple) |
several - [x] marked, or the .multiple class |
ticks all that apply; part of the right ones gives «Partially correct» |
order (order) |
a numbered 1. list in the right order |
arranges the shuffled items |
pairs (pairs) |
lines - left => right, at least two |
matches them; the right column is shuffled |
gaps (blanks) |
___ in the question text and one = answer line per gap |
fills in the words |
number (numeric) |
= 42 or = 2.5 ± 0.1 (+- works too) |
enters a number; comma or dot |
short answer (text) |
= answer | another variant |
types an answer; case, ё/е, quotes and a final period do not matter |
card (card) |
the .card class, the back side as text under the heading |
recalls, opens the answer and rates themselves |
Options need at least two, and at least one marked right; an order needs at least two items; a card has no answer block, only the text of its back side.
Tests#
A code stage refers to a test file in checks/, and the files the work starts with go into starter/
and are listed in the header under files.
- Tests are an ordinary Python or JavaScript script; the language comes from the extension. The stage
counts if the script runs to the end without an error:
assertwith a message is handy. Exiting before the end of the file — for example,sys.exit(0)in the solution file — does not count. - Attempt files are imported by name (
from wordcount import count_words) or by path (from src.parser import parse). Attempt files of the same language go into the sandbox. - The run happens in a sandbox with a time limit: an infinite loop is a failure, not a hang. The test output is shown under the task.
How format errors are shown#
A challenge with errors cannot be started. The repository's Learn tab shows how many errors the challenge files have, and the challenge page lists them: file, line and what is wrong. For example:
parser/stages/03-code.md: the stage header has no `title`
parser/stages/03-code.md: unknown check `test`; available: quiz, self_check, files, contains, min_words, numeric, tests
parser/stages/02-questions.md: `quiz` refers to `checks/02.md`, and the challenge has no such file
parser/checks/02-questions.md:7: the question «What is HEAD?» has no id — add `{#short-id}` to the end of the heading
parser/checks/02-questions.md:15: question `merge-blank`: the text has 2 `___` gaps but 1 `= …` answer line
Fix the file and commit — the list updates. Attempts already started are not affected: each is pinned to the version of the challenge it started with.
Example: a challenge of questions#
A challenge about git: two theory stages, two question stages and cards. It has no attempt repository — only progress.
git-basics/challenge.md:
git-basics/stages/01-snapshots.md — theory, no checks:
1. 2.3.
git-basics/stages/02-commit.md — a question stage:
git-basics/checks/02-commit.md — single choice, multiple choice and order:
- ---
- --
1. 2.3.
- ---
- --
git-basics/stages/03-branches.md — the second theory stage:
git-basics/stages/04-branch-questions.md:
git-basics/checks/04-branches.md — pairs, a number, a short answer and a gap:
- --
- --
- --
git-basics/stages/05-cards.md and git-basics/checks/05-cards.md — cards:
Example: a stage with code and tests#
The «Word counter in Python» challenge: the user writes wordcount.py in their attempt repository, and
tests check each stage.
python-wordcount/
challenge.md
stages/
01-task.md theory: what counts as a word
02-count.md files + tests
03-top.md files + tests
04-questions.md quiz
checks/
02-count/test_count.py
03-top/test_top.py
04-questions.md
starter/
wordcount.py
python-wordcount/stages/02-count.md:
python-wordcount/checks/02-count/test_count.py:
assert == 4,
assert == 0,
assert == 0,
assert == 3,
assert == 6,
python-wordcount/starter/wordcount.py — the attempt repository starts with this file:
"""Word frequency analysis.
The functions appear stage by stage — start with `count_words`.
"""
"""How many words the text has."""
"""The n most frequent words: a list of (word, count) pairs."""
On the starter the test fails with NotImplementedError, so the stage does not count until the function
is written. It is handy to start your own challenge from a copy of these files: create a folder in your
repository, put challenge.md and stages/ into it, commit and open the Learn tab.
Was this helpful?