Иногда в систему 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; }