Writing statements#
rbxTeX (extension .rbx.tex) is the format we recommend for writing a
problem statement in rbx, and this page is a tour of it.
An rbxTeX file is a set of named blocks of content, sprinkled with
Jinja2 for variables and logic. It is a superset of LaTeX: anything you
can write in LaTeX, you can write here. What it takes away is the document
around your text. No \documentclass, no packages, no section titles, no
\begin{document}. Those belong to a template, and the template
belongs to the contest.
That separation is worth the trouble for two reasons. You can restyle a whole contest by changing one template, without opening a single problem. And a problem written for one contest drops into the next one, with different chrome, unchanged.
Motivational problem#
Let's write the statement for a problem we already know. In
Validators we wrote a validator for a problem
that asks for a path between vertices 1 and N in a connected graph with
N vertices and M edges, where N is between 2 and 1000 and M is between
1 and N * (N - 1) / 2. The input looks like this:
That validator reads its bounds from vars:
By the end of this page, the statement will print those same bounds, and the graph picture next to them, without repeating a single number.
Writing the statement#
Point a statement entry at a file, and write the blocks. rbx-tex is the
default type, so there is nothing else to configure:
- The language code (ISO 639-1) this statement is written in.
- The
.rbx.texsource, relative to the package root.typedefaults torbx-tex, so it is omitted.
%- block legend
You are given a connected undirected graph with $N$ vertices, numbered from
$1$ to $N$, and $M$ edges. Find any path from vertex $1$ to vertex $N$.
%- endblock
%- block input
The first line contains two integers $N$ and $M$.
Each of the next $M$ lines contains two integers $u$ and $v$, indicating an
undirected edge between $u$ and $v$.
%- endblock
%- block output
Print the vertices of a path from $1$ to $N$, in order, separated by spaces.
%- endblock
The statement above carves the problem into three blocks and nothing else. Build
it with rbx st b and you get a laid-out PDF: the title, the limits box, the
section headings and the samples all come from the template.
Blocks#
A block is a chunk of content between %- block <name> and %- endblock. Block
names are free-form. There is no fixed list, and %- block whatever is a
perfectly valid block; it is up to a template whether it gets rendered and where.
That said, the bundled default template, and every preset that inherits it, renders this set of blocks by convention:
| Block | Renders as |
|---|---|
legend |
the main statement text |
input |
the input-format section |
output |
the output-format section |
interaction |
the interaction protocol (interactive problems) |
notes |
the notes section |
macros |
your \newcommands, injected before the body |
preamble |
extra preamble injected into the document head |
explanation_<i> |
inline explanation for sample #i (0-indexed) |
Reusing these names keeps your statement portable across templates, and a few of them get special treatment when packaging for Polygon.
Want a section of your own? Write the block here, and wire it into the page from the template:
Printing constraints from vars#
Here is where the statement stops being a text file. Any value from
problem.rbx.yml can be interpolated with \VAR{...}, and your problem's
vars are in scope by name:
The statement above reads N.min and N.max straight out of
problem.rbx.yml, which is where the validator we wrote reads them from too.
Change the bound in one place and the statement, the validator and the
generators all follow. This is the whole reason vars exists, and we cover it
in full in Variables.
Large bounds read better in scientific notation, and the sci filter does
that for you:
sci is one of a handful of LaTeX-aware filters rbx registers on top of the
standard Jinja2 ones. Template context lists them all.
Never type a bound twice
A hard-coded 10^9 in a statement is the single most common way a package
goes out of sync. If a number appears in both the statement and the
validator, it belongs in vars.
Spelling out the namespace#
\VAR{N.max} is the short spelling. The keys of a vars block are exposed
both directly and under the namespace that holds them, so every constraint has
a longer form that names where it came from. Both always work:
| Shorthand | Long form |
|---|---|
\VAR{N.max} |
\VAR{vars.N.max} |
\VAR{problem.N.max} |
\VAR{problem.vars.N.max} |
\VAR{contest.year} |
\VAR{contest.vars.year} |
\VAR{g.N.max} (for a group g) |
\VAR{g.vars.N.max} |
Prefer the short one. Reach for the long one when you want the reader of your template to see which namespace a value came from, which is mostly a contest template concern.
Branching and looping in a statement#
Everything in an rbxTeX file is LaTeX-flavored Jinja2, so you get more
than interpolation. A line starting with %- is a Jinja statement, which is the
same thing as \BLOCK{ ... }:
%- if vars.multitest
Each test file contains several test cases.
%- else
There is a single test case per file.
%- endif
The snippet above prints one line or the other depending on vars.multitest, so
a single statement file covers both the single-case and the multi-case flavor of
your problem. Loops work the same way, and the
template context page is where the interesting things to loop over
live.
Tip
%# starts a comment that is stripped before compilation. Plain % is a
normal LaTeX comment and survives into the generated .tex, which is handy
when you are debugging the output.
Explaining a sample#
Samples are loaded from your testset and printed by the template, so they show up without you doing anything. To explain one, rbx looks in three places, in descending priority. For the same sample, a higher source wins.
-
An inline
explanation_<i>block in the statement file (explanation_0for the first sample). Because the statement is built per language, this text is language-specific. -
A sibling file next to the sample input (recommended). Swap the suffix:
000.inbecomes000.rbx.tex. It is a blocks file with one block per language, keyed by the language code:statements/samples/000.rbx.tex%- block en The graph is a path already, so $1, 2, 3$ is a valid answer. %- endblock %- block pt O grafo já é um caminho, então $1, 2, 3$ é uma resposta válida. %- endblockOnly the block matching the statement's language is rendered, and it receives the same variables as the rest of the statement.
-
A language-agnostic
000.tex, which is the same text for every language.
We recommend the sibling file: the explanation sits next to the sample it explains, and all the languages of one sample stay in one place.
One explanation file per sample
A sample may not have both 000.rbx.tex and 000.tex. That is a hard
error, and you have to pick one.
Markdown statements use the same scheme with Markdown suffixes: 000.rbx.md for
per-language blocks, or 000.md for a language-agnostic one.
Shipping images and other resources#
The golden rule: put images, .sty files and PDFs in the same directory as
your .tex, and reference them by a plain relative path.
The line above works because rbx mirrors the directory that holds your
file, the whole subtree, into the build. Anything sitting next to your .tex
is staged for you.
Tip
Keep everything next to your .tex and you will never have to touch
\graphicspath or TEXINPUTS.
The assets field is a different concern: it is about packaging, not the
local build. It lists extra globs, relative to the package root, to ship with
the statement when you export the package, notably to Polygon. Use it for
resources that live outside the statement's directory, since those are not
picked up by the mirroring above:
statements:
- language: "en"
file: "statements/statement.rbx.tex"
assets:
- "shared/logos/*.png" # out-of-tree resources to ship on export
Reusing a recipe across languages#
Two statements in different languages usually share everything except the source
file. Repeating the rest on every entry is how they drift apart, so extends
lets one entry inherit another's build recipe and spell out only what
differs.
A problem statement extends another by language, or by a
{language, variant} pair:
statements:
- language: en
file: statements/statement-en.rbx.tex
params: { show_limits: true }
- language: pt
extends: en # (1)!
file: statements/statement-pt.rbx.tex
params: { show_limits: false } # (2)!
- Inherits
type,assetsandparamsfrom theenstatement. It does not inherit identity:ptkeeps its ownlanguageandvariant. paramsdeep-merges key by key, soptoverridesshow_limitsand keeps every other keyendefined.
The merge is an allowlist of the build recipe: type, file, params and
assets. Omit file on the child and it inherits the parent's, which is how a
single source renders under two different params. Cycles and dangling
references are errors.
Contest statements extend by name instead, and carry the two templates along.
See Reusing a recipe with extends.
Writing in a format other than rbxTeX#
rbxTeX is the default, and for the vast majority of problems it is all you
will touch. Still, type accepts a few alternatives. The one thing to keep in
mind is that only rbx-tex and rbx-md process blocks and can join into a
contest statement. The rest are standalone-only, meant for
documents or for drop-in files.
Markdown (rbx-md)#
The same block, variable and Jinja2 machinery as rbxTeX, except you write
the body in Markdown (.rbx.md). Reach for it when you are targeting HTML or
Markdown output and still want the block structure.
Jinja (jinja-tex / jinja-md)#
Full control of the whole document. You write the \documentclass, the
\begin{document} and everything else, with \VAR{...} and %- ... available
for interpolation and logic:
\documentclass{article}
\begin{document}
\title{\VAR{problem.title}}
\maketitle
%- for sample in problem.samples
\subsection*{Sample \VAR{loop.index}}
%- endfor
\end{document}
The document above is self-contained: it declares its own \documentclass and
loops over problem.samples by hand. You get the interpolation and none of the
block structure, which is why it cannot join a contest. This is the type most
documents use.
Plain LaTeX or Markdown (tex / md)#
The file is treated as a static document: no blocks, no variables, no
templating. Reach for these when you already have a finished .tex or .md and
want rbx to compile it as-is.
PDF (pdf)#
A pre-built PDF. The build is a copy, with no templating, variable substitution or asset processing. Handy for statements produced by an external tool, or pulled out of an old archive:
Now that the source side is covered, Template context is the reference for every value your blocks and templates can reach.