Настройка системы Contester для школьной олимпиады. Часть 1

Подготовка к олимпиаде
Уже четвёртый год я являюсь членом жюри и системным администратором регионального этапа школьной олимпиады по информатике. В мои задачи входит подготовка всей информационной системы для автоматизированной проверки решений. Это настройка необходимого количества рабочих станций (по числу участников + резерв), а также установка и настройка сервера. Сервер необходим для централизованной проверки решений всех участников в ходе олимпиады. Работает это так: участник решает задачи по программированию и тестирует их локально. После того, как он убедится, что программа работает верно, он загружает исходный код решения на сервер с помощью веб-интерфейса. На сервере код автоматически компилируется и прогоняется на большом наборе тестов, неизвестных участникам.
Для серверной части я выбрал систему Contester. Однако он уже давно не обновлялся и поэтому ничего не знает о новых версиях компиляторов. К тому же, каждый год требования меняются. Также отсутствуют какие-либо возможности изменения работы алгоритмов тестирования. В этой статье я решил опубликовать свои конфигурационные файлы, а также поделиться методами итоговой проверки. Вся информация представленная в статье является открытой.

Итак, для начала ставим на сервер Contester и все необходимые компиляторы. В моём случае список был такой:
  • Borland Delphi 7;
  • Microsoft Visual Studio 2013 Express;
  • JDK 1.8.0.71;
  • Python 2.7.9;
  • Python 3.5.1;
  • Free Pascal 2.6.4;
  • CodeBlocks (он содержит необходимый GNU C++ MinGW).
Запускаем Contester и видим, что автоматически подхватился только Delphi. Ещё в списке присутствовал .NET 2.0 и 3.5 для C#, но по-положению у нас должна быть более поздняя версия, поэтому его уберём. Кстати, ещё проблема была при попытке заставить всё это работать на 64-битной винде (C# и Java нормально не подключались). На 32-битной всё завелось без проблем.
Теперь надо рассказать контестеру о всех наших компиляторах. Для этого редактируем файл contestercfg.xml (C:\Program Files\Contester\contestercfg.xml). Там мы указываем строки компиляции и запуска решений:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
<?xml version="1.0" encoding="windows-1251"?>
<ContesterConfiguration>
 
  <!-- Файл конфигурации. См. страницу "Помощь" -->
 
  <!-- Linux: Learn it from /opt/firebird/SYSDBA.password -->
  <DatabasePassword>masterkey</DatabasePassword>
 
  <Compiler syntax="csnet" state="set">
    <CompilerName>Microsoft Visual C# 2013 Express</CompilerName>
    <SourceFile>solver.cs</SourceFile>
    <CompileLine1>csc.exe solver.cs</CompileLine1>
    <TargetFile1>solver.exe</TargetFile1>
    <ExecuteLine>solver.exe</ExecuteLine>
    <AddPathVariable>C:\Windows\Microsoft.NET\Framework\v4.0.30319</AddPathVariable> 
  </Compiler>
 
  <Compiler syntax="java" state="set">
    <CompilerName>Java Development Kit 1.8.0.71</CompilerName>
    <SourceFile>solver.java</SourceFile>
    <CompileLine1>javac.exe -g solver.java</CompileLine1>
    <TargetFile1>solver.class</TargetFile1>
    <ExecuteLine>java.exe -Xss32m solver</ExecuteLine>
    <AddPathVariable>C:\Program Files\Java\jdk1.8.0_71\bin</AddPathVariable>
  </Compiler>
 
  <Compiler syntax="python" state="set">
    <CompilerName>Python 2.7.9</CompilerName>
    <SourceFile>solver.py</SourceFile>
    <ExecuteLine>C:\Python27\python.exe solver.py</ExecuteLine>
    <AddPathVariable>C:\Python27</AddPathVariable>
  </Compiler>
 
  <Compiler syntax="python" state="set">
    <CompilerName>Python 3.5.1</CompilerName>
    <SourceFile>solver.py</SourceFile>
    <ExecuteLine>C:\Users\username\AppData\Local\Programs\Python\Python35-32\python.exe solver.py</ExecuteLine>
    <AddPathVariable>C:\Users\username\AppData\Local\Programs\Python\Python35-32</AddPathVariable>
  </Compiler>
 
  <Compiler syntax="vbnet" state="set">
    <CompilerName>Microsoft Visual Basic 2013 Express</CompilerName>
    <SourceFile>solver.vb</SourceFile>
    <CompileLine1>vbc.exe solver.vb</CompileLine1>
    <TargetFile1>solver.exe</TargetFile1>
    <ExecuteLine>solver.exe</ExecuteLine>
    <AddPathVariable>C:\Windows\Microsoft.NET\Framework\v4.0.30319</AddPathVariable>
  </Compiler>
 
  <Compiler syntax="pas" state="set">
    <CompilerName>Free Pascal 2.6.4</CompilerName>
    <SourceFile>solver.pas</SourceFile>
    <CompileLine1>fpc.exe -Mtp -osolver.exe solver.pas</CompileLine1>
    <TargetFile1>solver.exe</TargetFile1>
    <ExecuteLine>solver.exe</ExecuteLine>
    <AddPathVariable>C:\FPC\2.6.4\bin\i386-win32</AddPathVariable>
  </Compiler>
 
  <Compiler syntax="cpp" state="set">
    <CompilerName>Microsoft Visual C++ 2013 Express</CompilerName>
    <SourceFile>solver.cpp</SourceFile>
    <CompileLine1>C:\Program Files\Microsoft Visual Studio 12.0\VC\vcvarsall.bat && cl.exe solver.cpp /I"C:\Program Files\Microsoft Visual Studio 12.0\VC\include"</CompileLine1>
    <TargetFile1>solver.exe</TargetFile1>
    <ExecuteLine>solver.exe</ExecuteLine>
    <AddPathVariable>C:\Program Files\Contester\utils</AddPathVariable>
  </Compiler>
 
  <Compiler syntax="cpp" state="set">
    <CompilerName>GNU C++ MinGW</CompilerName>
    <SourceFile>solver.cpp</SourceFile>
    <CompileLine1>"C:\Program Files\CodeBlocks\MinGW\mingwvars.bat" && mingw32-g++ solver.cpp -o solver.exe -I "C:\Program Files\CodeBlocks\MinGW\lib\gcc\mingw32\4.7.1\include\c++"</CompileLine1>
    <TargetFile1>solver.exe</TargetFile1>
    <ExecuteLine>solver.exe</ExecuteLine>
    <AddPathVariable>C:\Program Files\CodeBlocks\MinGW\bin</AddPathVariable>
  </Compiler>
 
  <Compiler syntax="csnet" state="deny">
    <CompilerName>Microsoft .NET 3.5 Framework</CompilerName>
  </Compiler>
  <Compiler syntax="csnet" state="deny">
    <CompilerName>Microsoft .NET 2.0 Framework</CompilerName>
  </Compiler>
  <Compiler syntax="vbnet" state="deny">
    <CompilerName>Microsoft .NET 3.5 Framework</CompilerName>
  </Compiler>
  <Compiler syntax="vbnet" state="deny">
    <CompilerName>Microsoft .NET 2.0 Framework</CompilerName>
  </Compiler>
  <Compiler syntax="cpp" state="deny">
    <CompilerName>Microsoft Visual C++ 2005 Express</CompilerName>
  </Compiler>
 
</ContesterConfiguration>
Обязательно сверьте пути к компиляторам со своими. Последние строки с параметром state=»deny» необходимы для отключения автоматически определённых компиляторов, но которые нам не нужны.
Теперь перезапускаем контестер, убеждаемся, что перечислены все компиляторы и проверяем работу их всех на какой-нибудь простой задаче. Например, «A+B». Если всё хорошо, то можно загружать задачи.
contest1
Здесь возникает вторая проблема: по-умолчанию поддерживаются только чекеры (тестирующие программы) на паскале. А в последнее время их всё больше стало на C++. Можно конечно попробовать переписать чекер на паскаль, а можно поискать библиотеку testlib.h. Последняя версия такой библиотеки находится здесь. Но она не подходит к самому контестеру. В Интернете я потом нашёл исправленную старую версию testlib для контестера (ссылку потерял), добавил в неё несколько функций из последней официальной версии. Скачать testlib.h (56.16 KB). Библиотеку надо поместить в папку utils контестера.

Так, теперь по изменениям в чекерах. В первую очередь меняем название библиотеки и путь до неё. Если чекер на паскале, то меняем «testlib» на

unJudge17 in '..\utils\unJudge17.pas'

Было:

1
2
3
4
5
program checker1;
 
uses
  testlib, sysutils;
...

Стало:

1
2
3
4
5
program checker1;
 
uses
  unJudge17 in '..\utils\unJudge17.pas', sysutils;
...

Если чекер на C++, то меняем так:

1
2
3
4
5
6
#include "testlib.h"
 
using namespace std;
 
int main(int argc, char * argv[]) {
...

на

1
2
3
4
5
6
#include "..\utils\testlib.h"
 
using namespace std;
 
int main(int argc, char * argv[]) {
...
Теперь, если чекер на паскале, то необходимо внести ещё некоторые изменения. Список доступных функций можно посмотреть в файле utils\unJudge17.pas.

Чтение слова:

ja := ans.readword(blanks, blanks);
pa := ouf.readword(blanks, blanks);

меняем на

ja := ans.readstring;
pa := ouf.readstring;

Чтение действительного числа:

ja := ans.readreal;
pa := ouf.readreal;

меняем на

ja := ans.readfloat;
pa := ouf.readfloat;

и т.д.
Итогом должны получиться принятые (Approved!) чекеры для каждой задачи:
contest2

Теперь пробуем запускать «эталонные» решения жюри. При этом учитываем особенность контестера: Входные данные допускается читать как с консоли, так и из файла input.txt, выходные данные необходимо выводить в консоль. То есть, если решение выглядело так:
1
2
3
4
5
6
7
8
9
10
#include <cstdio>
#include <iostream>
#include <vector>
using namespace std;
 
int main()
{
    freopen("zadacha.in", "rt", stdin);
    freopen("zadacha.out", "wt", stdout);
...

то его надо исправить примерно так:

1
2
3
4
5
6
7
8
9
10
#include <cstdio>
#include <iostream>
#include <vector>
using namespace std;
 
int main()
{
    freopen("input.txt", "rt", stdin);
    //freopen("zadacha.out", "wt", stdout);
...
На всякий случай можно проверить решения жюри на разных языках (в этом году были C++, GNU C++, Python), естественно учитывая особенности ввода/вывода.

В результате, если всё хорошо, получаем вот такую картину:
contest3

Все задачи готовы и для каждой было проверено решение жюри (зелёные галочки). Так как олимпиада проходит в два этапа (два дня), то во избежании утечки, из второго тура названия задач пока убираем, оставим только цифры.

Устанавливаем для каждого тура время начала и окончания:
contest4

Теперь перенастраиваем сеть. На сервере (DHCP + DNS + ActiveDirectory) создаём отдельную подсеть, переносим в неё все компьютеры участвующие в олимпиаде, блокируем им Интернет. Создаём в домене отдельного пользователя («Олимпиада», «Olimp» и т.п.) с урезанными правами. Ещё раз проверяем работу всей системы, пробуем зайти с компьютеров на сервер контестера и идём отдыхать 🙂

Продолжение следует…

Похожие записи:

4 Комментарии “Настройка системы Contester для школьной олимпиады. Часть 1

    1. К сожалению, я её до сих пор не опубликовал, лежит в черновиках. Но раз Вас это заинтересовало, то постараюсь в ближайшие выходные дописать её. В комментариях здесь сообщу.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *