h1. Библиотека testlib-dl

Многие из российских задач используют для тестирования checker'ы и [player'ы|Интерфейс тестирования задач с интерактивным вводом-выводом], использующие для общих задач тестирования (ввод-вывод, выдача результата) библиотеку {{testlib.h}}. Для использования этих программ на DL можно воспользоваться [модификацией этой библиотеки для DL|https://raw.githubusercontent.com/LeXofLeviafan/testlib-dl/master/testlib.h] (из [форка на Github|https://github.com/LeXofLeviafan/testlib-dl]) -- чекер, скомпилированный с этой библиотекой, будет обрабатывать аргументы командной строки и делать вывод в соответствии с протоколом DL.
{info}Данная модификация [имеет несколько релизов|https://github.com/LeXofLeviafan/testlib-dl/tags], последний основан на {{testlib}} версии 0.9.40
{info}
{warning}Модификация совместима с {{testlib}}, но поддерживает только функционал чекеров и player'ов. Также может играть роль конкретная версия библиотеки, а в некоторых олимпиадах может использоваться собственная кастомная модификация -- в частности, форк включает версии для [IOI-2021|https://github.com/LeXofLeviafan/testlib-dl/tree/0.9.37_IOI2021_DL-SNAPSHOT] и [IOI-2022|https://github.com/LeXofLeviafan/testlib-dl/tree/0.9.37_IOI2022_DL-SNAPSHOT].
{warning}
Для использования достаточно скомпилировать программу ({{checker.cpp}} или {{interact.cpp}}) положив в ту же папку модифицированный {{testlib.h}} вместо оригинального. (Проверялось только на {{g+\+}} версии 7.3+)

 
Также для интерактивных задач доступны следующие директивы компилятора (добавляются в параметры компиляции):
* {{\-DINFILE="_имя-файла_"}} (файл ввода данных для player'а; значение по умолчанию: {{"$player$.in"}})
* {{\-DOUTFILE="_имя-файла_"}} (файл вывода данных для player'а; значение по умолчанию: {{"$player$.out"}})
* {{\-DLOGFILE="_имя-файла_"}} (файл логирования данных для player'а; по умолчанию отсутствует)
* {{\-DPLAYER_ADAPT}} (несовместим с {{\-DLOGFILE}}; включает режим совместимости интерактивных задач -- см. ниже)

{code:title=примеры строки компиляции}
g++ -O2 -o checker checker.cpp
g++ -O2 -o player interact.cpp
g++ -O2 -o player interact_new.cpp -DINFILE="eq.in" -DOUTFILE="eq.out"
{code}
{note}{{{}INFILE}}, {{OUTFILE}} и {{LOGFILE}} должны содержать корректные строки в формате C; в зависимости от особенностей используемого shell'а, может потребоваться экранировать их, скажем, взяв дополнительно в одинарные кавычки (например: {{\-DINFILE='"eq.in"'}})
{note}
Для интерактивных задач, помимо исполнимых файлов {{checker}} и {{player}}, [обычно нужно изменить {{task.cfg}} и добавить {{stdswp.cfg}}|Интерфейс тестирования задач с интерактивным вводом-выводом]:
{code:title=task.cfg (изменения)}
TYPE = INTERACTIVE
INFILE = FILE($player$.in)
OUTFILE = FILE($player$.out)
CHECKER = 1
CHECKSUBJECT = FILE
CHECKFILES = {stdswp.cfg}
{code}
{code:title=stdswp.cfg}
KillSolution=Yes
{code}

h2. Режим совместимости интерактивных задач ({{\-DPLAYER_ADAPT}})

Как правило, в интерактивных задачах большая часть проверок производится player'ом ({{interact.cpp}} в исходном архиве); в этом случае чекеру достаточно просто скопировать содержимое {{$player$.out}} в {{$result$.txt}}, умножив выведенное число очков на максимальное за тест. Однако есть и такие задачи, в которых чекеру необходимо читать как файл ответа жюри ({{1.out}} и т.п.) так и вывод чекера; в этом случае {{testlib}}\-задачи как правило используют костыль в виде отправления этих данных в {{LOGFILE}} (т.к. в {{OUTFILE}} отправляется статус самого тестирования) -- ему соответствует глобальная переменная {{tout}}.

В {{testlib-dl}} реализован режим совместимости, позволяющий заметно упростить установку подобных задач; это достигается путём совмещения файлов (а именно, при положительном результате тестирования, вместо комментария в {{OUTFILE}} выводится текст, который должен был отправляться в {{LOGFILE}}. Для его применения требуется выполнить следующее:
# перенести команду {{quitf(_ok, ...);}} из player'а в чекер (по обстоятельствам -- это комментарий, который пойдёт в протокол при пройденном тесте);
# добавить в player на место этой команды вызов {{dl_playerDone();}} -- это выполняет успешное завершение player'а с выводом данных из {{tout}};
# добавить в чекер в начало (сразу после {{registerTestlibCmd(argc, argv);}}) вызов {{dl_playerCheck();}} -- это инициирует проверку статуса завешения player'а и вывод отрицательного статуса;
# компилировать и player и чекер с директивой {{\-DPLAYER_ADAPT}} для включения означенного режима совместимости.

| {code:title=оригинальный interact.cpp}
#include <cstdint>
#include <iostream>
#include "testlib.h"

using namespace std;

int const Q_MAX = 42;

int main(int argc, char *argv[]) {
registerInteraction(argc, argv);

int n = inf.readInt();
int c = inf.readInt();
vector<int> a = inf.readInts(n);

cout << n << endl << flush;

vector<int> qs;
int queries = 0;
while (true) {
string action = ouf.readToken("!|\?");
if (action == "!") {
int res = ouf.readInt(0, n - 1, "c");
tout << "answer " << res << "\n";
tout << "queries " << queries << "\n";
break;
}

if (++queries > Q_MAX) {
cout << "-1\n" << flush;
quitf(_wa, "too much queries");
}
int x = ouf.readInt(1, n);
qs.push_back(x);
cout << a[(x - 1 + c) % n] << "\n" << flush;
}


quitf(_ok, "%d queries processed", n); // BEFORE
}
{code} | {code:title=адаптированный player.cpp (скомпилированный с \-DPLAYER_ADAPT)}
#include <cstdint>
#include <iostream>
#include "testlib.h"

using namespace std;

int const Q_MAX = 42;

int main(int argc, char *argv[]) {
registerInteraction(argc, argv);

int n = inf.readInt();
int c = inf.readInt();
vector<int> a = inf.readInts(n);

cout << n << endl << flush;

vector<int> qs;
int queries = 0;
while (true) {
string action = ouf.readToken("!|\?");
if (action == "!") {
int res = ouf.readInt(0, n - 1, "c");
tout << "answer " << res << "\n";
tout << "queries " << queries << "\n";
break;
}

if (++queries > Q_MAX) {
cout << "-1\n" << flush;
quitf(_pe, "too many queries");
}
int x = ouf.readInt(1, n);
qs.push_back(x);
cout << a[(x - 1 + c) % n] << "\n" << flush;
}


dl_playerDone(); // AFTER
}
{code} |
| {code:title=оригинальный checker.cpp}
#include <cstdint>
#include "testlib.h"

using namespace std;

typedef long long ll;
const int MAX_QUERIES = 42;
// BEFORE

int readAnswer(InStream &inStream, int n, const vector<int> &a, int c) {
inStream.readToken("answer", "_");
int answer = inStream.readInt(0, n - 1, "ans");

inStream.readToken("queries", "_");
int queries = inStream.readInt(0, INT_MAX, "queries"); // BEFORE
inStream.ensuref(queries < MAX_QUERIES,
"too match queries was done (%d)", queries);

return answer;
}

int main(int argc, char *argv[]) {
registerTestlibCmd(argc, argv);

// BEFORE
int n = inf.readInt();
int c = inf.readInt();
vector<int> a = inf.readInts(n);

ll juryAnswer = readAnswer(ans, n, a, c);
ll pAnswer = readAnswer(ouf, n, a, c);
if (juryAnswer != c) {
quitf(_fail, "jury's answer is not equal to c");
}
if (pAnswer != c) {
quitf(_wa, "participant's answer is not equal to c");
}
quitf(_ok, "%lld", juryAnswer); // BEFORE
}
{code} | {code:title=адаптированный checker.cpp (скомпилированный с \-DPLAYER_ADAPT)}
#include <cstdint>
#include "testlib.h"

using namespace std;

typedef long long ll;
const int MAX_QUERIES = 42;
int queries; // AFTER (for printing success message)

int readAnswer(InStream &inStream, int n, const vector<int> &a, int c) {
inStream.readToken("answer", "_");
int answer = inStream.readInt(0, n - 1, "ans");

inStream.readToken("queries", "_");
queries = inStream.readInt(0, INT_MAX, "queries"); // AFTER
inStream.ensuref(queries < MAX_QUERIES,
"too many queries were done (%d)", queries);

return answer;
}

int main(int argc, char *argv[]) {
registerTestlibCmd(argc, argv);

dl_playerCheck(); // AFTER
int n = inf.readInt();
int c = inf.readInt();
vector<int> a = inf.readInts(n);

ll juryAnswer = readAnswer(ans, n, a, c);
ll pAnswer = readAnswer(ouf, n, a, c);
if (juryAnswer != c) {
quitf(_fail, "jury's answer is not equal to c");
}
if (pAnswer != c) {
quitf(_wa, "participant's answer is not equal to c");
}
quitf(_ok, "%d queries processed", queries); // AFTER
}
{code} |