Ferramentas de julgamento e de enunciado do MOJ, o juiz online do CD
(moj.naquadah.com.br). Tudo aqui é bash.
O mojtools roda em dois lugares diferentes, e isso explica a divisão do repositório:
| Metade | Onde roda | O que faz |
|---|---|---|
| Julgamento | em cada máquina de juiz | compila e executa código de aluno dentro de um sandbox, compara a saída, decide o veredito, mede o tempo-limite |
| Enunciado e validação | no servidor web | renderiza o enunciado, confere se o pacote está completo, gera o índice que o aluno consome |
Uma máquina de juiz precisa de dois repositórios apenas: o judge (o agente) e este. Ela não
precisa do cdmoj.
O formato do pacote de problema está documentado em
cdmoj/docs/PACOTE.md. Aquele documento é a referência (o que é cada arquivo, o que são orgs e coleções, o que são os metadados). Este aqui é o manual das ferramentas: o roteiro de montagem e o que cada comando faz.
- Preparar a máquina
- Roteiro: montar um pacote do zero
- Recursos opcionais
- O caminho normal do autor: a CLI
- Referência de comandos
- As linguagens (
lang/) - Como um problema customiza o julgamento
- Mapa do repositório
Para renderizar e validar enunciados basta ter pandoc e jq. Para julgar código de aluno
você precisa do sandbox (bwrap, do bubblewrap) e dos compiladores.
make deps # confere o que falta na máquina (roda o check-deps.sh)O make deps lista o que está faltando. Dependência dura faz o comando sair com erro;
dependência opcional (como cset ou podman) só vira aviso.
Os compiladores podem vir de dois lugares:
- Da própria máquina (o default). Simples, mas o toolchain é o que estiver instalado ali.
- De um rootfs próprio, um Ubuntu com todos os compiladores na versão certa. É o modo recomendado para um juiz de verdade, porque o toolchain fica igual em todas as máquinas:
make sysroot # constrói o rootfs (precisa de podman) e exporta p/ ~/moj-sysroot
export CAGE_ROOT=$HOME/moj-sysroot # a partir daqui, a jaula roda dentro do rootfsAntes de usar a máquina numa prova de verdade, rode a bateria de estresse do sandbox:
bash stress-cage.shEla tenta fork bomb, alocar memória sem parar, encher o disco, abrir rede e ler segredos do
/etc. Se qualquer caso falhar, não use aquela máquina em prova. Detalhes do sandbox, da
escolha da raiz e do endurecimento: SANDBOX.md.
Vamos montar um problema chamado soma, que lê dois inteiros e imprime a soma. O roteiro é o
caminho manual, usando os scripts direto. É o que a CLI e o editor web fazem por baixo, e conhecê-lo
ajuda a entender o que deu errado quando algo dá errado.
mkdir -p soma/docs soma/tests/input soma/tests/output soma/sols/good
cd somaO arquivo é docs/enunciado.md. As seções ## Entrada e ## Saída são obrigatórias: sem elas
a validação reprova o problema.
cat > docs/enunciado.md <<'EOF'
Dados dois números inteiros, calcule a soma dos dois.
## Entrada
A entrada contém dois inteiros $a$ e $b$ ($0 \le a, b \le 1000$), um em cada linha.
## Saída
Imprima um único inteiro, a soma de $a$ e $b$.
EOFRepare em duas coisas que não estão no arquivo:
- Não há título. O título é um campo do metadado, não uma linha do texto. Se você escrever
% Somana primeira linha, o renderizador vai apagar (é um formato legado). - Não há exemplo. Os exemplos são montados a partir dos arquivos de teste, no passo seguinte, e injetados no fim do enunciado. Se você escrever um exemplo à mão aqui, ele vai aparecer duas vezes.
Exemplo é todo teste cujo nome começa com sample. Ele aparece no enunciado e corrige a
submissão, como qualquer outro teste.
printf '2\n3\n' > tests/input/sample1
printf '5\n' > tests/output/sample1O nome do arquivo de entrada e o do arquivo de saída têm que ser iguais. A validação confere isso nos dois sentidos.
Qualquer nome que não comece com sample é um teste oculto: corrige, mas o aluno não vê.
printf '0\n0\n' > tests/input/test-001
printf '0\n' > tests/output/test-001
printf '1000\n1000\n' > tests/input/test-002
printf '2000\n' > tests/output/test-002Pelo menos uma solução correta em sols/good/ é obrigatória. A extensão do arquivo é o que
diz a linguagem.
cat > sols/good/sol.c <<'EOF'
#include <stdio.h>
int main(void){ int a, b; scanf("%d %d", &a, &b); printf("%d\n", a + b); return 0; }
EOFPonha uma solução good em cada linguagem que você quer liberar para o aluno. O tempo-limite é
calibrado por linguagem, e linguagem sem solução good aceita não ganha tempo-limite, ou seja, o
aluno não consegue usá-la.
Os limites de execução. Para a maioria dos problemas os defaults servem, e o conf fica curto:
cat > conf <<'EOF'
TLMOD[calibrafactor]=1.35
ULIMITS[-u]=10000
ALLOWPARALLELTEST=y
EOFO calibrafactor é a folga: o tempo-limite vai ser o tempo da sua solução good multiplicado por
1.35. O ULIMITS[-u] é o número de processos (Java e outras runtimes precisam de bem mais do que o
default de 1024). A lista completa das chaves está em cdmoj/docs/PACOTE.md.
Falta ainda o arquivo author, que é obrigatório:
echo 'Fulano de Tal' > author
printf '#implementacao\n#iniciante\n' > tagsAgora dá para rodar o julgamento na sua própria solução, para ver se o pacote funciona:
bash ../mojtools/build-and-test.sh c sols/good/sol.c . yA saída tem duas linhas que importam:
- a primeira linha é o diretório de trabalho, onde ficaram os artefatos (inclusive o
report.html, o mesmo relatório que o aluno vê); - a última linha é o veredito, por exemplo
Accepted,100p.
Se der Compilation Error, o compilador reclamou. Se der Wrong Answer, a saída não bateu. Abra o
report.html do diretório de trabalho: ele mostra, teste a teste, a entrada, o que era esperado, o
que saiu, o tempo e o pico de memória.
Este passo precisa de um sandbox de verdade. Se na sua máquina o
bwrapfor ofbwrap(o no-op do firejail, comum em máquina de desenvolvimento), o julgamento local não roda. Isso não é defeito do pacote: mande o problema para o servidor e deixe a calibração num juiz de verdade fazer a conferência.
bash ../mojtools/validate-problem.sh . soma#somaO comando sai com 0 se passou, e escreve um relatório em run/validation/<id>.json dizendo, item a
item, o que passou e o que não passou. Ele confere: autor, enunciado, as seções ## Entrada e
## Saída, se o pandoc renderiza, se há exemplo, se todo teste está pareado, se há solução good,
e se a good é aceita.
Todas as checagens precisam passar. Se o comando reprovar, ele imprime no stderr a lista do que falhou.
Se passar, ele já indexa o problema (chama o gen-problem-json.sh sozinho).
bash ../mojtools/calibreitor.sh .O calibrador roda cada solução de sols/good/, mede o pior tempo por linguagem, multiplica pelo
calibrafactor e grava o resultado em tl.<nome-da-máquina> (e uma cópia em tl). Depois roda as
soluções de pass/, slow/ e wrong/, se existirem, para você conferir que o tempo-limite reprova
o que tem que reprovar.
Sem o arquivo tl não é possível julgar submissão nenhuma: o julgamento precisa saber o
tempo-limite. Num juiz de verdade isso é automático, o agente calibra o problema na primeira vez que
recebe uma submissão dele.
Você não escreve o tempo-limite à mão. Ele é medido.
bash ../mojtools/gen-problem-json.sh . soma#somaGera contests/treino/var/jsons/soma#soma.json, que é o que o frontend consome: o título, o autor, as
tags, as coleções, os tempos-limite, o enunciado já em HTML e os exemplos. É o passo final, e ele já
foi executado pelo passo 8 se a validação passou.
Pronto: o pacote está completo, validado, calibrado e indexado.
Os três recursos abaixo são opcionais, e cada um já tem um guia próprio. Aqui vai só o suficiente para você saber que existem e quando usar.
Por padrão a nota do problema é a porcentagem de testes que passaram. Se você quer subtarefas
(o estilo da OBI: um grupo de testes fáceis vale 40 pontos, o grupo geral vale 60), crie o arquivo
tests/score:
sample* - 0 pontos
soma_facil_* - 40 pontos
soma_geral_* - 60 pontos
Cada linha é um grupo: um ou mais globs de nome de teste, -, e o peso. O grupo é tudo ou nada
(um teste falhou, o grupo vale 0), e o valor do problema é a soma dos pesos. Quem interpreta o
arquivo é o score-summary.sh, sozinho, sem você precisar chamar nada.
Se a resposta não é única (tolerância de ponto flutuante, várias ordens válidas, qualquer caminho mínimo serve), a comparação exata não funciona. Você precisa de um checker, um programa que olha a entrada, a resposta do aluno e o gabarito, e decide.
O jeito recomendado é escrever o checker em C++ com a testlib (o padrão do Codeforces) e instalá-lo:
bash mojtools/testlib/install-checker.sh <pacote> checker.cppIsso põe o fonte em scripts/checker.cpp e um stub de 10 linhas em scripts/compare.sh,
que chama a bridge do mojtools (testlib/checker-bridge.sh) — é ela que compila o checker no juiz
sob demanda. O pacote não carrega a bridge nem o testlib.h. Nunca commite o binário do
checker.
Guia de autoria, com receitas prontas: docs/checker-testlib.md.
Quando a solução do aluno precisa conversar com um árbitro (fazer perguntas e receber respostas, como num jogo de adivinhação), o problema é interativo:
bash mojtools/interactive/install-interactive.sh <pacote> arbitro.cpp [--score]O árbitro recebe o teste como argumento e conversa com a solução por FIFOs. A última linha do
stderr do árbitro é o resultado (WRONG <motivo> reprova). Com --score, o problema vira um ranking
(a nota é o desempenho, não o acerto).
Guia de autoria: docs/problema-interativo.md.
Submissão de função (o aluno entrega só a função, não o programa), proibir uma função da biblioteca
para forçar a implementação na mão, comparador com tolerância de ponto flutuante: tudo isso é
scripts/, e está em docs/correcao-especial.md.
Há templates prontos em script-templates/, que o editor web oferece num seletor.
Na prática, quem escreve problema não chama estes scripts na mão: usa a CLI moj (o repositório
moj-cli), que fala com a API do servidor. A CLI e o editor web fazem exatamente o que o roteiro da
seção 2 descreve.
moj login
moj new <org> soma # cria o esqueleto do pacote
# (edita enunciado, testes, soluções)
moj test soma --run # pré-voo local e julga, chamando o build-and-test.sh daqui
moj preview soma # abre o enunciado renderizado, pelo render-statement.sh daqui
moj push soma # envia o pacote para o servidor
moj publish <org>#soma # o servidor valida e calibra; se passar, o problema entra no treino
moj check <org>#soma # acompanha: validação, tempo-limite por juiz, good sem TLPara os recursos opcionais, a CLI tem atalhos que chamam os instaladores deste repositório:
moj checker <dir> <checker.cpp> e moj interactive <dir> <arbitro>.
Para a CLI achar o mojtools, tenha um checkout dele ao lado (~/moj/mojtools) ou aponte a variável
MOJTOOLS_DIR.
Os scripts abaixo estão todos na raiz do repositório. Salvo indicação em contrário, todos aceitam caminho relativo e escrevem mensagens de erro no stderr.
O coração do juiz. Compila uma solução e a roda contra todos os testes do pacote, dentro do sandbox.
build-and-test.sh <linguagem> <arquivo-fonte> <pacote> [y|n]
O quarto argumento (y) manda rodar todos os testes mesmo depois de já ter dado erro (útil para
calibrar e para pontuação por grupos).
A saída tem um contrato, e os dois lados dela importam:
- a primeira linha do stdout é o diretório de trabalho, onde ficam todos os artefatos;
- a última linha do stdout é o veredito, com a nota embutida (
Accepted,100p,Wrong Answer,40p).
Artefatos que ficam no diretório de trabalho (ele não é apagado; quem chamou é que recolhe):
| Arquivo | O que é |
|---|---|
report.html |
o relatório da submissão, autocontido. É o que o aluno vê |
report.env |
o veredito estruturado, para quem for consumir por programa (ver abaixo) |
compile.log.* |
o que o compilador disse |
<teste>-team_output, <teste>-log.* |
por teste: a saída do aluno, o tempo, o veredito |
O report.env é a fonte de verdade para o servidor. Ele traz VERDICT_CANON (o veredito limpo,
sem a nota: Accepted, Wrong Answer, Time Limit Exceeded, Memory Limit Exceeded,
Runtime Error, Compilation Error), SCORE e SCORE_MAX, SCORE_KIND (tests para porcentagem
de testes, points para soma de grupos), SCORE_GROUPS (os grupos, em JSON, na ordem do
tests/score) e CORRECT/TOTALTESTS.
Códigos de saída: 0 julgou (o veredito está na última linha), 1 erro de uso ou Compilation
Error, 3 a linguagem não está disponível, ou falta o arquivo tl, ou o pacote não tem testes.
Lê do ambiente: CAGE_ROOT (a raiz do sandbox), CAGE_ROOT_<LANG> (raiz diferente para uma
linguagem específica), MOJ_PROBLEM_ID.
Lê do conf do problema: todas as chaves de limite (ver cdmoj/docs/PACOTE.md).
O sandbox propriamente dito, feito com bubblewrap (bwrap). Roda um script isolado: sem
rede, sem acesso ao seu $HOME, com o sistema de arquivos só-leitura, como usuário sem privilégio, e
com limite de tempo e de memória.
Você normalmente não chama este script à mão: quem chama é o build-and-test.sh, uma vez para
compilar e uma vez por teste.
cage-run.sh -d <dir> -i <entrada> -o <saída> -s <log-stderr> -t <log-tempo> -r <script> -T <limite> -B <arq>
[-w <dir-rw>] [-b <bind>]... [-R <rootfs>] [-M <MB>] [-S <cpus> -U <user>]
| Flag | O que faz |
|---|---|
-r |
o script a executar (precisa ter o bit de execução) |
-d |
diretório com os arquivos, entra na jaula como /tmp/dir, só-leitura |
-i / -o |
a entrada (/tmp/in, só-leitura) e a saída (/tmp/out, escrita) |
-w |
um diretório de escrita em /tmp/rwdir. É o modo usado na compilação |
-T |
tempo-limite duro (maior que o do problema, é uma rede de segurança) |
-M |
limite de memória, em MB |
-R |
a raiz do sistema de arquivos da jaula (o rootfs). Também vem da variável CAGE_ROOT |
-b |
um bind extra para dentro da jaula (usado pelos prep.sh das linguagens) |
-S / -U |
fixa CPU e usuário (só como root; os dois têm que vir juntos) |
O /etc entra inteiro na jaula, mas com máscaras: shadow, sudoers, chaves de ssh e afins
são zerados, e passwd/group viram arquivos sintéticos de uma linha. Detalhes em
SANDBOX.md.
calibreitor.sh <pacote>
Roda cada solução de sols/good/, pega o pior tempo por linguagem, multiplica pelo
TLMOD[calibrafactor] (1.35 por padrão) e grava tl.<máquina> e tl dentro do pacote. Só emite
tempo-limite para linguagem que teve pelo menos uma solução good aceita naquela máquina.
Depois, roda as soluções de pass/, slow/ e wrong/ para conferência (o CALIBRATE_ONLY_GOOD=1
pula essa parte, e é o que o agente do juiz usa quando está com pressa).
Grava também um report.html por solução em .calib-reports/, que o agente sobe para o servidor.
Concorrência (juiz multi-slot): a tabela de TL de trabalho é PRIVADA — o calibreitor exporta
MOJ_TLFILE (um temp) para os build-and-test.sh filhos, e essa env vence a seleção
tl.<máquina>/tl (é o único jeito de apontar um TL fora do pacote). Os tl.<máquina>/tl
finais são publicados atomicamente (mktemp no mesmo diretório + mv) e o .calib-reports/
é montado em staging e trocado por rename no fim: outro slot julgando o MESMO problema nunca vê
placeholder, tabela parcial nem dir de reports pela metade. (Duas calibrações do mesmo pacote no
mesmo host continuam NÃO devendo rodar em paralelo — o agente serializa com flock por-problema;
rodando na mão, não dispare duas.)
validate-problem.sh <pacote> [<id>]
Sai com 0 se o pacote passou, diferente de 0 se reprovou, e sempre escreve um relatório em
$RUNDIR/validation/<id>.json com o resultado de cada checagem. Todas as checagens são
obrigatórias: has_author, has_statement, html_builds, secao_entrada, secao_saida,
examples_present, tests_paired, has_good_sol, good_sol_accepts.
Alguns avisos são só informativos e não reprovam: LaTeX vazando na prosa do enunciado, exemplo escrito à mão no texto, checker commitado como binário.
Se o pacote passa, o script chama o gen-problem-json.sh sozinho.
Sem um sandbox de verdade (isto é, quando o bwrap é o fbwrap), a checagem good_sol_accepts é
adiada para a calibração, que roda num juiz real. Não é bug.
Variáveis: VALIDATE_RUN_SOLS=0 pula a execução das soluções; RUNDIR diz onde escrever o relatório.
gen-problem-json.sh <pacote> [<id>]
Lê o pacote e escreve contests/treino/var/jsons/<id>.json, que é o que o frontend consome:
{ "id": "...", "title": "...", "author": "...", "time_limits": {...}, "tags": [...],
"collections": [...], "languages": [...], "statement_html_b64": "..." }Os exemplos vêm sempre dos arquivos de teste (tests/input/sample*, na ordem), nunca do texto do
enunciado, e são injetados no HTML. As explicações de cada exemplo vêm de docs/sample-notes.json.
O editorial (docs/solucao.md) é ignorado de propósito: ele não pode chegar ao aluno.
Os tempos-limite vêm do store dos juízes (run/tl/<id>.json) e são o máximo entre as máquinas,
mas só valem se o checksum do pacote ainda bate. Se o pacote mudou e ninguém recalibrou, o campo sai
vazio.
Se o problema é privado, o JSON vai só para jsons-private/, e o do jsons/ é removido.
render-statement.sh <arquivo-do-enunciado> [formato] [html-dos-exemplos] [título]
Escreve o HTML completo no stdout. Usa pandoc com --mathml (a matemática vira MathML de verdade) e
--embed-resources (as imagens entram embutidas, o HTML é autocontido). Injeta o <h1> a partir do
título, que é um argumento, e remove um % Título legado da primeira linha.
Este é o único renderizador de enunciado do MOJ. O botão "Pré-visualizar" do editor, o HTML que o aluno lê e o que a validação confere passam todos por aqui. Se você precisa mudar como o enunciado é renderizado, é neste arquivo, e a mudança vale para os três de uma vez. Não crie um segundo pandoc por fora.
gen-report.sh <diretório-de-trabalho>
Recebe o diretório que o build-and-test.sh imprimiu na primeira linha e gera o report.html:
veredito, barra de tempo de cada teste em relação ao limite, pico de memória, e o diff colorido entre
o que saiu e o que era esperado. O HTML é autocontido.
Você não costuma chamar este script: o build-and-test.sh já o chama no fim.
tl-checksum.sh <pacote> # imprime 16 dígitos hexadecimais
O checksum cobre só o que pode mudar o tempo de execução: o conf, os tests/input/*, as
sols/good/* e o scripts/* (conteúdo e bit de execução). Não cobre o enunciado, as tags, o
autor nem os tests/output/*.
É por isso que corrigir um typo no enunciado não força recalibração, mas trocar um teste, uma
solução good, o conf ou um script força.
Não é um comando: é um trecho que o build-and-test.sh carrega sozinho quando o pacote tem
tests/score (e não tem um scripts/summary.sh próprio). Interpreta os grupos, aplica o tudo ou nada
por grupo, soma os pesos e reescreve o veredito com a nota.
Sem argumentos. Varre todos os pacotes e escreve
contests/treino/var/problem-owners.json, que a gestão de problemas usa para saber, de cada problema:
quem é o dono, em que coleções está, se é público, o checksum atual, quando foi publicado pela
primeira vez, e em quais linguagens existe solução good.
Ele mantém um cache por commit de cada problema, para não ter que recalcular o checksum de um pacote que não mudou.
make-sysroot.sh [--base ubuntu:24.04] [--out DIR] [--tag moj-sysroot] [--pkgs "..."] [--apl arq.deb]
Constrói uma imagem com todos os compiladores (a partir de sysroot/Containerfile, com podman) e a
exporta para um diretório. Aponte a variável CAGE_ROOT para esse diretório e a jaula passa a rodar
dentro dele.
Vale a pena porque o toolchain fica igual em todas as máquinas de juiz, e não depende do que alguém instalou no host.
check-deps.sh [--rootfs DIR] [--quiet]
Diz o que falta na máquina. Sem --rootfs, confere os compiladores no PATH do host. Com
--rootfs, confere os compiladores dentro do rootfs e só o runtime no host. Sai com erro apenas se
faltar dependência dura.
stress-cage.sh [limite-de-memória-em-MB]
Seis ataques contra a jaula: fork bomb, alocação infinita, escrita em massa, acesso à rede, leitura do
seu $HOME e leitura dos segredos do /etc. Imprime PASS ou FAIL para cada um. Qualquer FAIL
significa que a máquina não deve receber prova hostil.
convert-enunciado.sh <pacote> [--write]
Converte um docs/enunciado.tex ou .org para markdown canônico. Sem --write só imprime o
resultado, para você conferir. É uma ferramenta de mão, usada em migração, e o resultado sempre pede
uma revisão humana.
| Alvo | O que faz |
|---|---|
make help |
lista os alvos |
make check |
roda bash -n em todos os .sh do repositório. Rode antes de commitar |
make deps |
o check-deps.sh |
make sysroot |
constrói o rootfs e exporta para um diretório |
make sysroot-image |
só a imagem, sem exportar |
make sysroot-tar |
um tarball do rootfs, para máquinas que não têm podman |
make sysroot-push |
publica a imagem no registry |
Cada linguagem aceita é um diretório lang/<lang>/, e todas seguem o mesmo contrato. Adicionar
uma linguagem é criar um diretório novo, não mexer no julgador.
As 17 linguagens de hoje: apl, c, cpp, cs, go, hs, java, js, kt, ml, pas, pl
(Prolog), py, riscv, rs, sh, spim.
Python é uma linguagem só:
py, rodada com pypy3. As extensões.py2e.py3são legadas: o julgador as normaliza parapysozinho. Olang/py/compile.shfaz uma checagem de sintaxe, então erro de sintaxe em Python vira Compilation Error, e não Runtime Error.
Roda dentro da jaula, num diretório de escrita (/tmp/rwdir) que já tem o fonte do aluno.
exec 2>/tmp/stderrlog > /tmp/out # stderr vai p/ o log, stdout vai p/ /tmp/out
cd /tmp/rwdir
gcc -O2 -static sol.c -o main # ... compila ...
echo BIN=main # OBRIGATÓRIOA regra que importa: o script tem que imprimir BIN=<artefato> no stdout. Se não imprimir, o
julgador entende que a compilação falhou e o veredito é Compilation Error. É assim que se
implementa "proibir a função exp()": o compile.sh faz um grep no fonte do aluno e, se achar,
sai sem imprimir o BIN=.
O tempo-limite da compilação é de 30 segundos. Uma linguagem de compilador lento sobe isso com um
arquivo lang/<lang>/compile-tl (em segundos). Hoje só o Kotlin usa (120 segundos, porque a JVM do
kotlinc demora a esquentar).
Roda dentro da jaula, com o binário em /tmp/dir (só-leitura), a entrada em /tmp/in e a saída
em /tmp/out.
exec &>/tmp/stderrlog
cd /tmp/dir
source binfile.sh # define BIN, MOJ_MEMLIMITMB, MOJ_STACKKB
exec ./$BIN < /tmp/in > /tmp/outO binfile.sh não é um arquivo deste repositório: ele é gerado em tempo de execução pelo
build-and-test.sh, e é o canal por onde os limites do problema entram na jaula. É por isso que o
run.sh do Java consegue dimensionar a JVM com o limite certo:
exec java -Xmx${MOJ_MEMLIMITMB:-500}m -Xss${MOJ_STACKKB:-131072}k $(basename $BIN .class) < /tmp/in > /tmp/outOpcional. É carregado no host, fora da jaula, antes de compilar. Só serve para duas coisas: copiar
um arquivo para o diretório de trabalho, e acrescentar um bind à jaula
(EXTRABINDINGS+=" -b /opt/kotlin"). Precisa do bit de execução, e nunca deve chamar exit (use
return), porque ele é carregado, não executado.
O comparador padrão, usado quando o problema não traz um checker próprio. Compara a saída do aluno com o gabarito, tolerando cada vez mais diferença, e responde pelo código de saída:
| Código | Significado |
|---|---|
4 |
as saídas batem exatamente. Accepted |
5 |
batem ignorando espaços em branco e maiúsculas. Accepted, com erro de formatação |
6 |
não batem. Wrong Answer |
| outro | erro do próprio comparador. Erro de juiz |
Este é o mesmo contrato que um scripts/compare.sh de problema tem que cumprir, e ele recebe três
argumentos: $1 a saída do aluno, $2 o gabarito, $3 a entrada.
Uma bateria de fumaça: um "olá, mundo" em cada uma das linguagens, julgado contra um pacote mínimo.
cd lang-test && make alltestsSe alguma linguagem parar de funcionar na máquina, é aqui que aparece primeiro.
O build-and-test.sh procura os scripts do problema antes dos padrões da linguagem. Essa é a
única coisa que você precisa entender sobre correção especial:
| Ele procura primeiro | Se não achar, usa |
|---|---|
<pacote>/scripts/<lang>/compile.sh |
mojtools/lang/<lang>/compile.sh |
<pacote>/scripts/<lang>/run.sh |
mojtools/lang/<lang>/run.sh |
<pacote>/scripts/<lang>/prep.sh |
mojtools/lang/<lang>/prep.sh |
<pacote>/scripts/compare.sh |
mojtools/lang/compare.sh |
<pacote>/scripts/summary.sh |
mojtools/score-summary.sh (se houver tests/score) |
É só isso. Submissão de função, ban de função da biblioteca, checker com tolerância, problema interativo: tudo é alguma combinação dessa tabela. Os detalhes estão em docs/correcao-especial.md.
Lembre que qualquer mexida em scripts/ muda o checksum do pacote e dispara recalibração no
juiz.
| Diretório | O que é |
|---|---|
lang/ |
uma pasta por linguagem aceita, todas com o mesmo contrato (seção 6) |
lang-test/ |
o "olá, mundo" de cada linguagem, para conferir a máquina |
interactive/ |
o driver comum dos problemas interativos, mais o instalador. Técnico: interactive/README.md |
testlib/ |
a testlib vendorada, a ponte de compilação e o instalador de checker. Técnico: testlib/README.md |
script-templates/ |
templates de correção especial que o editor web oferece num seletor. Criar um template é criar uma pasta aqui |
kattis/ |
importar e exportar problemas no formato Kattis (usado no ICPC). Ver kattis/README.md |
sysroot/ |
a receita do rootfs com os compiladores (Containerfile) |
docs/ |
os guias de autoria: correção especial, checker testlib, problema interativo |
- SANDBOX.md: como a jaula funciona, como escolher a raiz, o endurecimento.
- docs/correcao-especial.md:
scripts/por problema. - docs/checker-testlib.md: escrever um checker.
- docs/problema-interativo.md: escrever um problema interativo.
cdmoj/docs/PACOTE.md(no repositóriocdmoj): o formato do pacote, orgs, coleções e metadados. É a referência.cdmoj/docs/FLOW.md: o caminho de uma submissão, do browser até o placar.
make check # bash -n em todos os .shLicença: GPLv3 ou posterior. Ver LICENSE.