Иногда в систему DL приходится устанавливать чужие задачи по программированию (например, с Республики), для проверки решений которых авторы предоставляют тестирующие программы (чекеры), не удовлетворяющие принятым на DL соглашениям. Зачастую такие чекеры предоставляются без исходников, имеют другие порядок и число аргументов и пишут свои результаты на консоль, а не в файл.
Для того чтобы все-таки можно было правильно тестировать такие задачи на DL можно применять описанный далее прием с чекером-оболочкой.
Основная идея состоит в том, чтобы подсунуть Дельте наш checker.exe, который уже умеет передавать нужные параметры авторскому чекеру и в правильном виде сохранять его результаты.
В принципе, чекер оболочку можно было бы написать на любом компилируемом языке программирования. Но выгоднее оказалось использовать скрипт checker.cmd, т.к. в батниках технически намного проще перенаправлять ввод/вывод, манипулировать с параметрами и т.п. К тому же, батники не требуют компиляции, их проще отлаживать на скорую руку и адаптировать под конкретные задачи.
Релизованный батник-оболочка запускает переименованный авторский checker_.exe с нужными ему параметрами, затем с помощью перенаправления считывает его консольный вывод, сверяет его с маской правильного ответа и записывает результат в файл $result$.txt в принятом на DL формате.
В тоже время, Дельта ожидает, что исполняемый файл чекера будет называться checker.exe. Поэтому для вызова checker.cmd пришлось написать заглушку (на Visual C++). Чтобы для проверки использовался этот чекер необходимо внести нужную запись в task.cfg.
Далее приведены самодокументированные примеры исходных кодов оболочки и заглушки.
В большинстве случаев потребуется всего лишь подправить в checker.cmd порядок передаваемых авторскому чекеру параметров (%1 %3 %2) и маску правильного ответа ("Ok").
После установки задачи нужно обязательно проверить ее работоспособность и на DelTA, и на DelTA 2.
Эти же файлы можно также найти в каталоге задач из курса "Олимпиады по информатике"
@rem echo off @rem (CopyLeft) Vadim Kopichenko, 2005 @rem Батник-обертка для подключения нестандартного checker.exe. @rem Исходный checker.exe нужно переименовать в checker_.exe. @rem Прилагающийся checker.exe - просто заглушка для вызова этого батника с перенаправлением вывода в nul. @rem $result$.txt должен сохраняться в текущем каталоге (%cd%), @rem а авторский чекер нужно брать из каталога, где лежит этот скрипт (%~dp0). @rem В общем случае, это разные каталоги. @rem Если интересно, как это все работает, курите %windir%\help\ntcmds.chm, for /?, call /? @rem См. также кратенькое руководство по основным возможностям скриптов: http://dl.gsu.by/doc/use/ntcmds.htm @if exist $result$.txt @del /f /q $result$.txt >nul @set theresult="Checker failed" @rem Запускаем авторский нестандартный чекер с нужными параметрами и читаем первую строку его вывода на консоль. @rem (Если для чтения вывода не хватит возможностей батника, можно прикрутить вспомогательный exe). for /f "usebackq delims=" %%i IN (`%~dp0Checker_.exe %1 %3 %2`) DO @set theresult=%%i @echo theresult=%theresult% @rem Сверяем первые два символа вывода с маской правильного ответа (сравнение регистронезависимое). if /i "%theresult:~0,2%" EQU "ok" ( @echo %4 >$result$.txt @echo Approved by external checker! >>$result$.txt ) else ( @echo 0 >$result$.txt @echo %theresult% >>$result$.txt ) @goto :eof @rem Последующие строки использовались для отладки @set log=%~dpnx0.log @echo. >%log% @echo %0 %* >>%log% @echo %cd% >>%log% @dir >>%log% @rem exit /b 0
// Это универсальная заглушка для запуска checker.cmd в качестве стандартного чекера DL.
// checker.cmd должен находиться в том же каталоге, что и checker.exe, полученный из данного файла.
// Заглушка просто вызывает checker.cmd из своего каталога и передает ему все параметры.
// (CopyLeft) Vadim Kopichenko, 2005
// Компиляция:
// \\nit_server\delta\Lngs\MSVC\bin\c.cmd checker.mvc
#include <stdlib.h>
#include <stdio.h>
#include <process.h>
#include <string.h>
#include <fstream.h>
//#include <Windows.h>
// вспомогательная функция в основном для отладки
void file_trace(char* fname, char* msg) {
ofstream log(fname);
log << msg << endl;
log.close();
}
int main(int argc, char* argv[]) {
if (2*2==5 && argc!=5) { // можно было бы ругнуться на неправильное число параметров
cout << "Usage: checker.exe test.in right.out user.out points" << endl;
return 0; // Дельта не любит ненулевых кодов возврата
}
char path_buffer[_MAX_PATH];
char drive[_MAX_DRIVE];
char dir[_MAX_DIR];
char fname[_MAX_FNAME];
char ext[_MAX_EXT];
// приписываем к checker.cmd путь к checker.exe
_splitpath("checker.cmd", NULL, NULL, fname, ext);
_splitpath(argv[0], drive, dir, NULL, NULL);
_makepath(path_buffer, drive, dir, fname, ext);
//file_trace("$result$.txt", "0\nfake!");
char runstr[1000]=""; //"cmd /c ";
strcat(runstr, path_buffer);
// объединяем в одну строку все переданные параметры
for (int i=1; i<argc; ++i) {
strcat(runstr, " ");
strcat(runstr, argv[i]);
}
strcat(runstr, " >nul 2>nul"); // DelTA не любит консольного вывода
//cout << runstr << endl;
system(runstr); // запускаем
//_spawnl(_P_WAIT, path_buffer, path_buffer, argv[1], argv[2], argv[3], argv[4], NULL); // старый способ запуска
//Sleep(10000); // пауза для отладки
return 0;
}