Skip to content

Running on the judge itself#

rbx run runs your solutions in the sandbox on your machine. --runner is the other way: it runs them on the judge park, through the judge's own CLI, and reports back the verdicts and timings the judge produced.

# Run every solution on MOJ instead of locally
rbx run --runner moj

Why run there#

Your machine is not the judge. Different CPU, different compiler flags, a different amount of neighbouring load — and a solution that finishes in 0.8s here can spend 1.4s on the park. A local run answers "does this solution pass under my limits, on my laptop". The question you actually ship on is "does it pass under my limits, on the machine that will judge it".

--runner answers that second question directly. The verdicts are the judge's own, the times are measured by the judge, and the time limits enforced are the ones from the limits profile in effect — the same ones your local run would use, per language group. Two things follow:

  • A borderline accepted solution can be confirmed where it matters, instead of on a proxy.
  • A solution expected to be too slow can be confirmed slow on the hardware that has to reject it.

If what you want is a time limit rather than a verdict, reach for rbx time --runner instead: it feeds the same remote timings into the estimation.

Running on MOJ#

Everything below is specific to MOJ. rbx run --runner moj and rbx time --runner moj share all of it.

What it needs, and what it cannot tell you#

You must be logged in to the moj CLI — rbx reuses its session and never handles your credentials. On macOS the CLI also needs a bash newer than the one the system ships; if it refuses to start, see The MOJ CLI needs bash 4 or newer.

rbx uploads a throwaway problem of its own, named <your login>#rbxt-<slug> from the slug in .rbx-id at the package root — the one identity your package has on any remote judge, shared with the DOMjudge backend. The slug is minted once and then kept, so every later run reuses the same problem instead of orphaning one; commit .rbx-id if you want a co-setter to reach that problem too. The id is also written to .moj-id, which is what the moj CLI itself reads.

It never touches a problem it did not create: a package already bound to a real MOJ problem is refused by name rather than overwritten.

A judge reports less than the local sandbox does. It hands back a verdict, not the bytes your solution wrote, so some of what a local run shows you has no counterpart at all:

  • No memory usage, no .out/.err artifacts, and a verdict code rather than the checker's own message.
  • --runs greater than one, a sanitizer, and interactive (communication) problems are refused by name before anything is uploaded — each would produce a report answering a different question than the one you asked.

rbx refuses whatever it cannot answer honestly on a given backend, rather than quietly reporting less than you asked for.

The problem rbx run uploads to is named <your-login>#rbxt-<problem-id>-run.

Failing fast does work, because MOJ enforces it itself: it stops a solution at its first non-accepted verdict, and the testcases it never reached come back as skipped, exactly as they do locally.

What it costs#

The first run uploads the package and waits for the judge to calibrate it. After that, the package is stable and finished testruns are cached, so re-running costs no judge time at all.

Two things invalidate that: changing anything that goes into the package, and toggling --fail-fast, which changes MOJ's stop rule and therefore the package. Either one pays for an upload and a calibration on the next run.

rbx run and each phase of rbx time upload to a problem of their own (…-run for rbx run, … and …-slow for rbx time), so alternating between the commands never costs a re-upload.

Running on DOMjudge#

rbx run --runner domjudge

What it needs, and what it cannot tell you#

rbx talks to DOMjudge's REST API directly, so there is no CLI to install. Point it at the instance with three environment variables:

export RBX_DOMJUDGE_SERVER=https://judge.example.edu/
export RBX_DOMJUDGE_USERNAME=your-admin-user
export RBX_DOMJUDGE_PASSWORD=...

They are environment variables rather than package settings on purpose: which DOMjudge you can reach is a property of your machine, and committing one setter's server would break the package for everyone else.

The account needs admin rights. rbx creates a private rbx-timing contest the first time it runs, uploads a throwaway rbxt-… problem into it, and submits as the built-in domjudge team — none of which a plain jury account may do. The problem is named from the slug in .rbx-id at the package root — the one identity your package has on any remote judge, shared with the MOJ backend — so it stays the same problem as your testset grows, rather than leaving a trail of abandoned ones behind. The contest has no scoreboard anyone reads, and nothing rbx does there touches a real contest.

Before uploading anything, rbx checks the instance and refuses by name what cannot work, rather than leaving you watching a run that will never finish:

  • verification_required turned on. It hides every judgement and run until a human verifies it, so a timing run would poll an empty list until it gave up.
  • No enabled judgehost, or one that is enabled but has not asked for work recently. Either way submissions would be stored and never judged — but the fix differs, so the two are reported differently.

A judge reports less than the local sandbox does. It hands back a verdict, not the bytes your solution wrote:

  • No memory usage, no .out/.err artifacts, and a verdict rather than the checker's own message. DOMjudge has no memory-limit verdict at all, so a solution that runs out of memory comes back as a runtime error.
  • Times are CPU time, not wall clock. A solution killed for exceeding the limit reports the CPU it actually burned, which for a sleeping or I/O-bound solution is close to zero. The verdict is what says it was too slow.
  • --runs greater than one and sanitizers are refused by name before anything is uploaded — each would produce a report answering a different question than the one you asked.

Interactive (communication) problems do work. DOMjudge runs the interactor rbx ships as the problem's run script, so the verdict comes from the same program that judges locally — and a legacy interactor paired with a checker is chained so that both still run.

Solutions are submitted one at a time. A judgehost judges one submission at a time anyway, and two in flight on a multi-judgehost instance would land on different machines — whose timings are not comparable, which is the whole point of measuring remotely.

The problem rbx run uploads to is named rbxt-<fingerprint>-run, so it never collides with either phase of rbx time.

Failing fast is not available here. DOMjudge has already judged the whole submission by the time rbx reads the result, so there is nothing left to stop; the run is reported in full instead.