Командная работа (решение олимпиад прошлых лет)

Работа с архивами олимпиад

Архивы с прошедшими олимпиадами (условия, тесты, решения) можно найти на сайтах (список неполный).

Список будет пополняться ;)

Содержимое архива

Задача, как правило, содержит тесты, решения жюри, условия и чекер (checker) - программу для проверки ответа на однин тест.

1. Компиляция чекера

Чекер (checker) проверяет ответ участника на одном тесте. Он получает три файла: входной файл теста, вывод программы участника и эталонный ответ жюри. Имена файлов передаются в командной строке в этом порядке: check input.txt output.txt answer.txt. Вердикт чекер сообщает кодом возврата (соглашение библиотеки testlib, коды по умолчанию):

Чекер обычно пишут с библиотекой testlib.h (Mike Mirzayanov), см. раздел «Чекер на С/C++» ниже. Ниже приведён пример чекера для задачи A+B, который обходится без testlib.h и собирается без дополнительных файлов: он сам читает три файла и возвращает те же коды.

Для компиляции можно использовать строку вида:

g++ -std=c++17 -O2 -Wall check.cpp -o check.exe

Запуск: check.exe input.txt output.txt answer.txt (в Linux: ./check.exe input.txt output.txt answer.txt). Код возврата выводится командой echo %errorlevel% в Windows или echo $? в Linux.

Если аргументов командной строки не три, пример печатает четыре встроенных случая, по одному на каждый код возврата.

Реализация на C++

#include <charconv>
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include <system_error>

// Коды возврата чекера (соглашение testlib по умолчанию).
enum Verdict { OK = 0, WA = 1, PE = 2, FAIL = 3 };

// Целое число записано полностью (без знака '+', без лишних символов) и помещается в long long.
bool parse_int(const std::string& text, long long& value) {
    const char* end = text.data() + text.size();
    const auto result = std::from_chars(text.data(), end, value);
    return result.ec == std::errc() && result.ptr == end;
}

// Сравнивает ответ участника с ответом жюри и печатает вердикт.
Verdict check(std::istream& input, std::istream& output, std::istream& answer) {
    std::string a_text, b_text, jury_text, contestant_text, extra;
    long long a = 0, b = 0, jury = 0, contestant = 0;
    if (!(input >> a_text >> b_text) || !parse_int(a_text, a) || !parse_int(b_text, b)) {
        std::cout << "FAIL: input does not contain two integers\n";
        return FAIL;
    }
    if (!(answer >> jury_text) || !parse_int(jury_text, jury) || jury != a + b) {
        std::cout << "FAIL: jury answer is wrong or missing\n";
        return FAIL;
    }
    if (!(output >> contestant_text) || !parse_int(contestant_text, contestant)) {
        std::cout << "WA: no integer in the contestant output\n";
        return WA;
    }
    if (contestant != jury) {
        std::cout << "WA: expected " << jury << ", got " << contestant << "\n";
        return WA;
    }
    if (output >> extra) {
        std::cout << "PE: extra text after the answer: " << extra << "\n";
        return PE;
    }
    std::cout << "OK\n";
    return OK;
}

// Если аргументов не три, программа показывает встроенные примеры: по одному на каждый код возврата.
int demo() {
    struct Test { const char* input; const char* output; const char* answer; };
    const Test tests[] = {
        {"2 3\n", "5\n", "5\n"},
        {"2 3\n", "6\n", "5\n"},
        {"2 3\n", "5 7\n", "5\n"},
        {"2 3\n", "4\n", "4\n"},
    };
    for (const Test& test : tests) {
        std::istringstream input(test.input), output(test.output), answer(test.answer);
        const Verdict verdict = check(input, output, answer);
        std::cout << "exit code " << verdict << "\n";
    }
    return 0;
}

// Запуск: check input.txt output.txt answer.txt; код возврата - вердикт.
int main(int argc, char* argv[]) {
    if (argc != 4) return demo();
    std::ifstream input(argv[1]), output(argv[2]), answer(argv[3]);
    return check(input, output, answer);
}

Чекер на Delphi

Обычно файл сheck.dpr, может быть check.pas (на Borland Pascal).

Для компиляции нужна библиотека testlib.pas, которую, как правило, можно найти там же в архиве.

Для компиляции можно: положить testlib.pas в ту же папку, что и check.dpr и выполнить команду dcc32 check.dpr.

Пример чекера check.dpr (русские буквы должны быть в DOS-кодировке cp866):

{$APPTYPE CONSOLE}
Uses TestLib, SysUtils;

{ Чекер для задачи A+B (вывести сумму чисел, без длинной арифметики) }
var A,B,C,J : int64;
begin
  A := inf.readLongint; { Число A из входного файла }
  B := inf.readLongint; { Число B из входного файла }
  C := ouf.readLongint; { C (Contestant) - ответ участника }
  J := ans.readLongint; { J (Jury) - ответ жюри, может не использоваться! }
  if C <> J then { Wrong Answer }
    Quit(_WA,Format('%d + %d = %d, а выведено %d',[A,B,A+B,C]));
  Quit(_OK,''); { Тест пройден! }
end.

Чекер на С/C++

Обычно файл называется check.cpp. Для компиляции чаще всего нужна библиотека testlib.h (Mike Mirzayanov), с сайта http://code.google.com/p/testlib/.

Для компиляции можно использовать строку вида:

g++ -x c++ -O2 -Wall -Wl,--stack=67108864 check.cpp -o check.exe

Пример чекера check.cpp:

#include "testlib.h"

int main( int argc, char *argv[] ) {
  setName("Чекер для задачи A+B");
  registerTestlibCmd(argc, argv); // Обработка параметров командной строки
  int a = inf.readInt(), b = inf.readInt(); // Чтение исходных данных
  int pa = ouf.readInt(); // Ответ участника
  int ja = ans.readInt(); // Ответ жюри
  if (ja != pa) // Wrong Answer
    quitf(_wa, "%d + %d = %d, а выведено %d", a, b, ja, pa );
  quitf(_ok, "ans = %d, a=%d,b=%d", pa, a, b); // Тест пройден
}

2. Прогон на всех тестах с контролем времени и памяти

Для этого можно использовать программу run.exe, которую удобно поместить в папку с решением и тестами. По-умолчанию программа ищет решение solution.dpr, компилирует его Delphi (dcc32), и запускает на всех тестах (01, 02,... до 99) используя имя входного файла input.txt, а выходного output.txt. Для изменения этих настроек можно запустить программу run c параметрами командной строки или создать файл task.cfg. Пример файла task.cfg:

InputFile := bubble.in;
OutputFile := bubble.out;
CheckResult := true;

3. Сборка условий задач (если они формате TeX)

Под Windows можно использовать MikTex - http://miktex.org. В разделе Download выберите подходящую версию. После установки удобно указать автоматическую доустановку пакетов.

Для сборки можно использовать bat/cmd файл следующего вида:

@echo off
set CURRENT=ИМЯ_TEX_ФАЙЛА
latex %CURRENT%
dvips -t landscape %CURRENT%
dvipdfm -p a4 -l %CURRENT%