Установка задач с нестандартными авторскими чекерами

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

Эти же файлы можно также найти в каталоге задач из курса "Олимпиады по информатике" Гомельская обл. \2005\День 1\3 - "Задача 3" 25883 или Белорусская\2005\День 1\1 - "Перетягивание каната" 28826 или скачать отсюда.

checker.cmd
@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.mvc
// Это универсальная заглушка для запуска 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;
}