DocumentationProblem kinds and tracks
Guides
Problem kinds and tracks
Every problem runs through the same commands, but not every problem has the same shape. This page explains each kind, and what changes for it.
On this page
Tracks
The catalog is organised in tracks. 5 are graded by running your code; 2 are prompts you answer in notes and grade yourself with a rubric. These are the counts in cstudy 0.8.0:
| Track | Items | Graded by | What you practise |
|---|---|---|---|
Data structures and algorithms dsa | 196 | Tests, in every language | Functions and data-structure designs across 23 patterns |
Practical practical | 28 | Tests, level by level | One class whose API grows over levels, a project folder, or a business task with money and dates |
Low-level design lld | 8 | Tests, then a design rubric | Object-oriented design |
Concurrency concurrency | 6 | Stress tests against invariants | Thread-safe classes, with a concurrency model per language |
Debugging debugging | 16 | Tests | Buggy code to fix, including bugs that exist only in Go or Rust |
System design system-design | 10 | Yourself, with a rubric | A prompt, a notes template and a reference design |
Code review code-review | 6 | Yourself, with a rubric | A change to review, and the issues a strong reviewer finds |
Correctness is always behavioural: cstudy sends the inputs of a case to your code, and compares what it returns with the expected value. Your code is never compared with the reference solution’s text, so any correct approach passes. Every problem is listed in the catalog.
Function and design problems
Most problems ask for one function. The signature is declared once and mapped to each language, so two_sum(nums: list[int], target: int) in Python is fun twoSum(nums: IntArray, target: Int) in Kotlin. Where several answers are valid, the comparator accepts any of them: in any order for an unordered result, or by checking the answer itself.
A design problem asks for a class, such as an LRU cache. Each case is a sequence of operations: construct the class, then call its methods in order, and every result is compared.
The low-level design track uses design problems too. The tests check the public behaviour; after you finish, cstudy solution shows a rubric of design criteria for self-review, then a reference design, before the code.
Progressive problems
A progressive problem is one class whose API grows over levels, such as an in-memory store that adds scans, then expiry, then history. Time is always an explicit timestamp argument, never a clock.
- A new attempt opens level 1. The statement shows the open levels only; later titles stay hidden.
- The stub already lists every method, each marked with its level, so later method names are visible; their rules are not.
cstudy testruns the cases of the open levels. A run that passes all of them opens the next level and rewrites the statement. Use--level Nto run fewer levels.- Only a pass of every level is a full pass, which
cstudy done --ratingneeds.
$ cstudy test
level-store · python · level 1 of 3 · 4 cases (1 visible, 3 hidden, 0 generated)
PASS ex1 0.02ms
PASS 3 hidden cases
4/4 passed · total 0.3s
level 1 passed · unlocked level 2 of 3: Remove and size
statement .statements/level-store.md (updated)Concurrent problems
A concurrent problem asks for a thread-safe class, such as a bounded blocking queue. Besides ordinary cases, it has stress cases: many threads or tasks call your class at once, and cstudy checks invariants over everything they recorded, for example that every item put was taken exactly once. Each language uses its own model: threads in Java and Kotlin, threads or asyncio in Python, and async on Node’s event loop in TypeScript. Java and Kotlin can run a stress case on both platform and virtual threads, as 2 variants.
$ cstudy test bounded-blocking-queue --lang java
bounded-blocking-queue · java · 6 sequential (2 visible, 4 hidden) · 2 stress × 2 variants × 3 runs
build 1.6s (gradle, warm)
PASS ex1 0.05ms
PASS ex2 0.04ms
PASS 4 hidden cases
PASS s1 platform · 3/3 runs · median 38ms
FAIL s1 virtual · 1/3 runs passed
run 0 · seed 1000 equal_multisets put.arg0 vs take: 2 missing (1000017, 3000431), 0 extra
run 2 · seed 1002 distinct take: 2000005 (2 times)
STALLED s2 platform · 0/3 runs passed · deadline 5000ms
run 0 · seed 2000 producer[0] in put (op 704 of 1000) · consumer[0..2] in take
PASS s2 virtual · 3/3 runs · median 61ms
8/10 passed · 1 failed · 1 stalled · total 4.9sSTALLEDmeans a run was still unfinished at its deadline: usually a deadlock or a missed wake-up. The line says where each thread was waiting.- A failing run prints its seed. Rerun it with
--case s1 --seed 1007 --stress 1; in theasyncmodel this reproduces the run exactly.--stress Nruns each stress case N times. - A pass is evidence, not proof: the summary says
stress runs found no race (not a proof of thread safety). cstudy solution --brokenshows a deliberately broken reference with an explanation of its bug, as a spot-the-bug exercise.
Debugging problems
A debugging problem starts you with buggy code instead of an empty stub. Fix it until the tests pass. Some bugs exist only in some languages, such as integer overflow or a mutable default argument, so a debugging problem may be available in fewer languages, and a few exist only in Go or only in Rust.
Project problems
A project problem is a folder of seeded files in one language: an entry class, model types, a test fixture and build files. You edit those files or add your own, and the problem opens in levels that build on the same code. cstudy 0.8.0 has 6, one in each of Python, TypeScript, Java, Kotlin, Go, Rust, with these levels: Accounts and transfers; Top spenders; Scheduled transfers; Merges and balance history.
Starting
cstudy start copies the starter folder to projects/<slug>/, writes level 1’s test file, and opens the folder and the statement. It never overwrites a file you already have.
$ cstudy start bank-project-python
bank-project-python · hard · practical · python · project
project projects/bank-project-python (6 files)
entry bank/bank.py: Bank
tests tests/test_level_1.py (2 tests)
statement .statements/bank-project-python.md
level 1 of 4: Accounts and transfers
next cstudy test --watch, then cstudy submitTest, then submit
| Aspect | cstudy test | cstudy submit |
|---|---|---|
| Runs | The project’s own tests, with the language’s native framework | Every case of the open levels, visible and hidden |
| Purpose | Feedback while you work | Grading |
| Opens the next level | Never | When every case of every open level passes |
| Debugging | --debug stops in your code and your tests | Never debugged |
$ cstudy submit
bank-project-python · python · submit levels 1-2 of 4 · timer 33:05 / 90:00
level 1 PASS 5/5 (2 visible, 3 hidden)
level 2 FAIL 4/5 (2 visible, 3 hidden)
FAIL h3 hidden · level 2
input ops ["Bank", "create_account", ...] args [[], [1, "zoe"], ...]
expected ["abe(60)", "zoe(60)", "mia(0)"]
actual ["zoe(60)", "abe(60)", "mia(0)"]
9/10 passed · level 3 opens when levels 1-2 passA submission that passes every open level opens the next one, and tells you what the level adds:
10/10 passed · level 3 unlocked: Scheduled transfers
add schedule_transfer(timestamp: int, source_id: str, target_id: str, amount: int, delay: int) -> str, cancel_transfer(timestamp: int, account_id: str, transfer_id: str) -> bool
tests tests/test_level_3.py (2 tests)
statement .statements/bank-project-python.md (updated)Every submission grades all open levels again, because later rules can change earlier results. Grading uses the cases in the problem, never your test files, so editing a test cannot change a grade.
Generated level tests
When a level opens, cstudy writes one native test file with that level’s visible cases. The files are yours to edit and extend; cstudy never rewrites them.
| Language | File | Framework |
|---|---|---|
| Python | tests/test_level_1.py | pytest |
| TypeScript | level-1.test.ts | node:test |
| Java | Level1Test.java | JUnit 5 |
| Kotlin | Level1Test.kt | kotlin.test on JUnit 5 |
| Go | level_1_test.go | go test |
| Rust | tests/level_1.rs | cargo’s test harness |
Where you stand
cstudy status shows the open level, your latest test run and submission, and which files you changed, without running anything.
$ cstudy status
bank-project-python · python · project · attempt 41 · new
level 2 of 4: Top spenders (opened 18:02)
timer 33:05 / 90:00
tested 3/4 passed, 1 min ago
submitted level 1 5/5 · level 2 4/5, 2 min ago
files 2 changed, 1 added, 0 removed against the starter
next cstudy submit once level 2 passesDeleted or broke a starter file or a generated test? cstudy start <slug> --restore PATH writes that one file back as cstudy provides it, and does nothing else.
$ cstudy start bank-project-python --restore tests/test_level_2.py
bank-project-python · hard · practical · python · project
restored tests/test_level_2.py (level 2 tests)Finishing
cstudy done grades a project attempt from your latest submission of every open level. Finishing every level allows any grade; finishing all but the last caps it at hard; fewer grades it again. Hints open with the levels: while the next hint belongs to a locked level, cstudy hint says so.
System design and code review
These items have no tests. cstudy start copies a notes template to notes/<track>/<slug>.md and renders the prompt; a code review prompt includes the change to review.
$ cstudy start url-shortener
url-shortener · medium · system-design · 45 min
notes notes/system-design/url-shortener.md
prompt .statements/url-shortener.md
next answer in the notes, then cstudy solution url-shortenerAnswer in the notes, then run cstudy solution: it shows the rubric, what a strong answer covers, and the reference. Comparing them is the review, so revealing never lowers the grade, and cstudy done --rating schedules the item like any problem.
Study guides
System design items can link to sections of your own study notes. Set root under [study_guides] in your configuration, and statements gain a list of links to the guides that exist under it.
Written for cstudy 0.8.0. The specs in the cstudy repository are the source of truth for every detail.