h1. Альтернативная установка задач с интерактивным вводом-выводом

{note:title=Данная инструкция описывает усложнённый процесс установки}
Здесь описан минимальный процесс конверсии задачи, установленной под *StdSwp*, для запуска *tester*'ом.
Это следует делать в случае если требуется использовать групповое тестирование, или для обхода проблем запуска глобально установленного *StdSwp* на новых DelTA'х.
{note}
*Tester* (программа, изначально предназначенная для тестирования задач IOI) с некоторых пор поддерживает тестирование задач с интерактивным вводом-выводом, запускаемых через *StdSwp*.
По умолчанию под Windows используется путь {{D:\DelTA\stdswp}} (под Linux/Mac - локальный файл {{./stdswp}}); однако в конфигурации можно указать путь к альтернативному исполнимому файлу.
Также (в отличие от DelTA) у *tester*'а нет проблем с использованием группового тестирования для интерактивных задач.

Более подробная инструкция по использованию *tester*'а находится [тут|Установка задач IOI];
с общей инструкцией по установке задач с интерактивным вводом-выводом (*StdSwp*) можно ознакомиться [здесь|Интерфейс тестирования задач с интерактивным вводом-выводом].

{warning:title=При несоблюдении данной инструкции тестирование работать не будет!}
Креативная замена важных файлов (из описания ниже) на какие попало (особенно *{{checker.exe}}*, который должен быть скомпилирован из *{{checker.pas}}*) помешает работе системы тестирования.
Также, нижеописанные операции _не рассчитаны_ на [интерактивные задачи NEERC|Установка интерактивных задач NEERC (Питер)]; их можно опознать по файлам *{{coact.exe}}*, *{{coact.bat}}*, *{{Check.jar}}*, *{{Interact.jar}}*, *{{InteractChecker.jar}}* и т.п.
{warning}

h2. Конверсия интерактивной задачи под Tester

* файл чекера ({{checker.exe}}) следует переименовать в {{check.exe}} (из-за особенностей реализации tester'а)
* программа взаимодействующая с решением всё так же зовётся player'ом
* файл {{stdswp.cfg}} работает так же как описано в инструкции по StdSwp
* также нужны файлы конфигурации и исходники для tester'а (помимо {{src/CheckLib.pp}} и {{src/TesterLib.pp}}) нижеследующего содержания

{code:title=src/checker.pas (выполняет запуск отдельного теста)}
Uses
CheckLib;

BEGIN
Run(ProgName, OutFile);
Prepend(OutFile, 'OK');
END.
{code}
{code:title=src/tester.pas (выполняет процесс тестирования)}
Uses
TesterLib;

BEGIN
Body();
POut();
END.
{code}
{code:title=task.cfg (передаёт контроль над тестированием задач Tester'у)}
TYPE = USERS
CHECKSUBJECT = FILE
CHECKFILES = {*.*}
CHECKER = 'tester.exe $SOLUTION$'
{code}
{code:title=tester.cfg (управляет поведением Tester'а)}
PreRun = prepare.bat # скрипт компиляции решения!
Program = yes # решение - не библиотека а программа
Interactive = yes # тестировать с интерактивным вводом-выводом
InFile = input.txt # название input-файла
OutFile = output.txt # название output-файла
ExtChecker = check # название оригинального чекера (без расширения)
<
# сюда внести описание групп тестов, по одной на строку
# (для раздельного тестирования достаточно указать число баллов за соответствующий тест)
>
{code}
{code:title=prepare.bat (отвечает за компиляцию файла решения на Pascal, C/C++, Java или Python3)}
@echo off
for %%f in ("%~n1") do (
if /i %%~xf==.java goto rename
)
set sol=%1
set solext=%~x1
set runnerfile=solution.bat
set runner=%~n1.exe
if not exist 2.in cmd /C unrar -o- x tests
if /i %solext%==.pas goto pascal
if /i %solext%==.ppw24 goto pascal
if /i %solext%==.c goto c
if /i %solext%==.cpp goto cpp
if /i %solext%==.g54 goto cpp
if /i %solext%==.g73 goto cpp
if /i %solext%==.java goto java
if /i %solext%==.py goto py3

echo not found
@exit /B 1

:pascal
echo %runner%> %runnerfile%
cmd /C D:\DelTA\Lngs\compile_ppw302 %sol% .
@exit /B %errorlevel%

:c
echo %runner%> %runnerfile%
cmd /C D:\DelTA\Lngs\compile_c73 %sol% .
@exit /B %errorlevel%

:cpp
echo %runner%> %runnerfile%
cmd /C D:\DelTA\Lngs\compile_g73_old %sol% .
@exit /B %errorlevel%

:java
echo %runner%> %runnerfile%
cmd /C D:\DelTA\Lngs\compile_java8 %sol% .
@exit /B %errorlevel%

:py3
echo %runner%> %runnerfile%
cmd /C D:\DelTA\Lngs\compile_py %sol% .
@exit /B %errorlevel%

:rename
copy %1 %~n1
prepare %~n1
{code}
{code:title=limiter.bat (обёртка вокруг системного Limiter'а)}
@echo off
if "%~4" == "" goto 5
if "%~5" == "" goto 4
D:\DelTA\DelTA3DL\limiter %1 %2 "%3 < %4 > %5" %6 > lim.log 2> lim.err
goto :eof
:4
D:\DelTA\DelTA3DL\limiter %1 %2 "%3 < %4" %6 > lim.log 2> lim.err
goto :eof
:5
@if "%~5" == "" goto 0
D:\DelTA\DelTA3DL\limiter %1 %2 "%3 > %5" %6 > lim.log 2> lim.err
goto :eof
:0
D:\DelTA\DelTA3DL\limiter %1 %2 "%~3" %6 > lim.log 2> lim.err
{code}

h2. Альтернативный StdSwp

При наличии проблем с версией StdSwp используемой по умолчанию, можно использовать какую-нибудь альтернативную версию (лично я предлагаю использовать скрипт на Python).
Пример такой конфигурации:
{code:title=tester.cfg (добавить перед описанием тестов)}
StdSwp = stdswp-py.cmd
{code}
{code:title=stdswp-py.cmd (обёртка для запуска Python)}
D:\DelTA\Lngs\Python-3.11.3\python stdswp.py %*
{code}
{code:title=stdswp.py (простая реализация, игнорирует stdswp.cfg и подразумевает KillSolution=yes)}
#!/usr/bin/env python
from concurrent.futures import wait, ThreadPoolExecutor, FIRST_COMPLETED
from subprocess import Popen, PIPE
from sys import argv

def interact (player, solution, kill_player=False, kill_solution=True):
player = Popen(player, stdin=PIPE, stdout=PIPE)
solution = Popen(solution, stdin=player.stdout, stdout=player.stdin)
try:
executor = ThreadPoolExecutor(max_workers=2)
futures = [executor.submit(x.wait) for x in (player, solution)]
wait(futures, return_when=FIRST_COMPLETED)
if kill_player and solution.returncode is not None:
player.terminate()
assert {solution.returncode} <= {0, None}
elif kill_solution and player.returncode is not None:
solution.terminate()
assert {player.returncode} <= {0, None}
else:
assert {player.returncode, solution.returncode} <= {0, None}
wait(futures)
assert {player.returncode, solution.returncode} <= {0, None}
finally:
player.kill()
solution.kill()

if __name__ == '__main__':
interact(argv[1], argv[2])
{code}