A remote job listing drifted past recently: a part-time USACO competitive-programming tutor for K–12 students in North America, algorithm drills, mock contests, the usual. Buried in the "nice to have" section was the part that actually caught my eye. If you're into ed-tech, it said, you might help build the algorithm learning platform, the problem bank, the AI-assisted teaching tools, maybe even an online judge. That throwaway line is a whole product, and it's small enough that one person can ship a working version of it.
So this is a debrief on building the core of that: a mini judge where a verdict means the code ran and passed, not that a model nodded at it.
The signal worth catching: the model keeps getting handed the gavel
If you look at what people are shipping lately, there's a pattern where the model quietly becomes the grader. One builder put together a German B1 speaking-exam tool where an AI plays the examiner and scores the sentences you said out loud. For a speaking test that's reasonable, there's no single correct answer and fuzzy judgment is kind of the point. There's also a louder, more anxious thread going around about how fast AI is improving and how it's handing expert-level "superpowers" to people who never studied the domain, which makes it very tempting to just trust whatever the model says.
Algorithm practice is the opposite situation. It has a ground truth. The submission either produces the expected output on the test cases within the limits, or it doesn't. Asking a model "does this look correct?
and treating the answer as a grade is a category error. The whole value of competition-style practice is the brutal, honest verdict. If you soften that into vibes, you've built a worse version of the thing students already have.
So the design rule writes itself: execution decides the verdict, the model is allowed to talk but never to grade.
What the build actually is
Three moving parts, and they map cleanly onto confirmed capabilities.
The problem bank, the test cases, and every submission's history live in a database. I used SQLite here because it's enough for a single-tutor or small-group setup and it keeps the whole thing portable. A rough schema: a `problems` table (id, title, statement, time limit, memory note), a `test_cases` table (problem_id, stdin, expected_stdout, is_sample), and a `submissions` table (problem_id, student_id, source, verdict, failed_case, created_at). Being able to open a lightweight SQLite editor to add a problem or fix a wrong expected output by hand is more useful than it sounds, because test-case curation is where this kind of project actually lives or dies.
The judge itself is Python running the submission against each stored test case. You feed the student's source the test input on stdin, capture stdout, strip trailing whitespace on both sides, and compare. First mismatch wins and you record which case failed. The ability to run Python online is what turns this from a grading rubric into an actual judge, because the verdict is a byproduct of execution rather than a judgment call. Wrong Answer means a specific case diverged. Accepted means every case matched. There's no "probably fine."
The practice surface is a hosted page. Students pick a problem, read the statement, paste a solution, hit submit, and see a verdict plus which sample case broke. Being able to run HTML online and publish it means the practice page is a real URL you can hand to a student, not a localhost demo. The page talks to the Python judge and reads from the database; the student never sees the plumbing.
Where the model is allowed to speak
Here's the part I want to be careful about. The job description talks about explaining time and space complexity, comparing solution approaches, and giving targeted feedback. That's genuinely useful, and it's exactly where a Model Center model earns its keep, through the platform's APIs and in-platform tool calls.
But I wired it so the model only runs after the deterministic verdict is already set. It can look at an accepted solution and say "this reads like an O(n log n) sort-based approach, you could probably argue it down," or look at a Wrong Answer and suggest where an off-by-one tends to hide. Every one of those lines gets rendered under an explicit label: hint, not grade, and treated as unverified. The complexity it names is a guess until you reason about it yourself. The model does not get a vote on Accepted versus Wrong Answer, ever. That separation is the entire integrity story, and it's cheap to enforce because the two paths literally don't touch.
Friction and honest boundaries
First and biggest: VicroCode runs Python. The listing mentions C++, Python, or Java as fine languages for a tutor, and a lot of USACO students live in C++. This judge, built on these capabilities, is Python-only. I'm not going to pretend otherwise. If your cohort is C++-heavy, this covers the Python track and the algorithm-reasoning practice, and the rest falls outside what I can map here. State that to students up front so the tool's scope is clear.
Second, you're executing code that other people wrote, which is always the uncomfortable part of a judge. I kept submissions constrained to standard-input / standard-output problems with no file or network expectations, which shrinks the surface a lot. How far the online Python execution isolates or limits a given run is something you should confirm against the platform's actual behavior rather than assume; I'd treat any specific sandboxing or hard resource-limit guarantee as unverified until you've tested it, and I'd design the problem set to not depend on it. Start with CPU-light problems and generous but finite expectations.
Third, test-case quality is the real work. A judge is only as honest as its cases. Weak tests pass wrong solutions and teach students the wrong lesson. I spent more time writing edge cases, empty input, maximum sizes, duplicates, than on any of the code. The SQLite-backed bank makes that iterative: run a known-bad solution, watch it pass, add the case that should have caught it, repeat.
Why this shape holds up
The reason I'd rather build it this way than lean on a model-as-grader shortcut is durability. Models change fast, and a grading pipeline that depends on today's model behaving a certain way is fragile. A judge whose verdict is "the code produced this output" doesn't drift. The model layer on top can be swapped, improved, or turned off entirely, and the core still works. Submission history stays queryable in the database, so a tutor can actually see where a student keeps failing instead of trusting a summary.
It's a small build. Problem bank in a database, Python doing the judging, a hosted page as the front door, and a clearly-fenced model that hints without grading. The discipline is in that last fence. Keep the model on the hinting side of it and the thing stays trustworthy.