Библиотека DLChecker
Библиотека DLChecker разработана с целью упростить написание checker'ов для системы тестирования DelTA. Она включает следующие возможности:
- интеграция с системой DelTA
- автоматическая обработка списка аргументов
- завершение тестирования и генерация файла с результатом проверки по вызову
- выдача сообщения об ошибке при нестандартном завершении работы
- минимизация boilerplate-кода
- вспомогательный функционал для тестирования
- средства, упрощающие написание и тестирование сложных checker'ов
- выдача длительности работы checker'а (для отладочного вывода – упрощает поиск медленного кода)
- чтение строк, подсчёт значений и тип целого по умолчанию (для нецелочисленного индексирования массивов)
- шаблон (см. C++ templates) векторов с операциями включая перебор, сортировку и поиск (logN, N)
- шаблон множеств по сортируемым типам с фиксированным надмножеством и операции над множествами (logN, N)
- шаблон словарей по сортируемым ключам (фиксированный набор) и групповым обновлением значений (logN, N)
- сгенерированные типы векторов, множеств и словарей для базовых типов и генераторы векторов
Библиотека спроектирована таким образом, чтобы воспользоваться преимуществами стиля Pascal (в частности, отсутствие потребности в прямой работе с памятью), при этом обходя ограничения, заложенные в реализацию.
Использование библиотеки DLChecker
Перед началом выполнения программы библиотека подменяет ExitProc программы, считывает аргументы запуска checker'а и сохраняет время запуска в переменные. При желании их значения можно изменить, но в большинстве случаев это не требуется.
Для взаимодействия с файлами входных и проверочных данных теста, а также файлом вывода, реализуем простые обработчики, использующие поток ввода по умолчанию. Валидация вывода решения производится в процессе чтения.
Проверку правильности решения следует выполнять после завершения валидации; код проверки также (для удобства читабельности) следует вынести в отдельную процедуру.
Uses
DLChecker;
CONST
MaxRes = 1000*1000*1000;
VAR
Res, Ans : LongWord;
Procedure ReadChk ();
Begin
ReadLn(Ans); End;
Procedure ReadOut ();
Begin
Res := ReadInt(1, MaxRes); AssertEOF(); End;
Procedure Check ();
Begin
CAssert(Res = Ans, WrongAnswer); Succeed(); End;
BEGIN
ReadAll(Nil, @ReadChk, @ReadOut); Check();
END.
Для упрощения разработки были добавлены дополнительные средства. Примеры их использования можно найти ниже.
Интерфейс библиотеки DLChecker
Базовые значения и подпрограммы checker'а
Библиотека представляет набор наиболее распространённых комментариев к результату тестирования:
CONST
Success = 'OK!'; Full = 'Full mark!'; Partial = 'Partial mark'; CheckerFail = 'Checker stopped with an error!'; BadIO = 'Invalid output!'; NoFile = 'File not found!'; OutOfRange = 'Given value out of range!'; WrongAnswer = 'Wrong answer!'; Excessive = 'Excessive output!'; NoSubtask = 'Bad check file (no subtask id)'; BadSubtask = 'Bad check file (bad subtask id)';
Данные инициализации доступны для дальнейшего чтения и модификации (в случае необходимости):
VAR
InFile, ChkFile, OutFile : AnsiString; MaxPoints : Word; StartTime : TDateTime;
Для работы с интерфейсом определены вспомогательные типы:
TYPE
TProcedure = Procedure (); TRound = (RoundUp, RoundDown, RoundMath);
Оценка выставляется следующими процедурами:
Procedure RageExit (ErrMsg : AnsiString = BadIO); Inline;
Procedure RageExit (Fmt : AnsiString; Args : Array of Const); Inline;
Procedure CAssert (Condition : Boolean; Const ErrMsg : AnsiString = BadIO);
Procedure CAssert (Condition : Boolean; Const Fmt : AnsiString; Const Args : Array of Const); Inline;
Procedure Succeed (Comment : AnsiString = Success); Inline;
Procedure Succeed (Fmt : AnsiString; Args : Array of Const); Inline;
Procedure Succeed (Points : Word; Comment : AnsiString = Success); Inline;
Procedure Succeed (Points : Word; Fmt : AnsiString; Args : Array of Const); Inline;
Procedure ScoreTest (Penalty, Mistakes : Word; IsPartial : Boolean = True);
Procedure PercentScoreTest (Res, Ans : Int64; IsPartial : Boolean = True; RoundType : TRound = RoundMath);
Procedure PercentScoreTest (Res, Ans : Extended; IsPartial : Boolean = True; RoundType : TRound = RoundMath);
Все, кроме CAssert, завершают выполнение программы и выставляют оценку; CAssert проверяет условие и завершает выполнение, если оно не выполняется.
Аргументы Fmt и Args используются для форматирования комментария, скажем, вот так:
Succeed('Goal achieved in %d turns, used %.1f%% vertices.', [k, 100*Res/N]);
ScoreTest вычитает из максимального балла Penalty за каждую ошибку, подсчитанную в Mistakes; PercentScoreTest вычисляет процент баллов по соотношению Res/Ans (результат решения / проверочное значение). В обоих случаях результат ограничен диапазоном [0; MaxPoints], и в обоих случаях при IsPartial = False для неполного решения возвращается 0.
Для чтения файлов и валидации используются следующие подпрограммы:
Procedure ReadAll (ReadIn, ReadChk, ReadOut : TProcedure); Inline;
Procedure AssertEOF (); Inline;
Procedure AssertEOF (Var F : Text);
Function GetSubtask (Var F : Text): AnsiString; Inline;
Function GetSubtask (Var F : Text; Const Ids : AnsiString): AnsiString; Inline;
Function GetSubtask (Const Ids : AnsiString): AnsiString; Inline;
Function GetSubtask (): AnsiString; Inline;
Function ParseInt (Const S : AnsiString; MinValue, MaxValue : LongInt): LongInt; Inline;
Function ParseUInt (Const S : AnsiString; MaxValue : LongWord): LongWord; Inline;
Function ReadInt (MinValue, MaxValue : LongInt): LongInt; Inline;
Function ReadUInt (MaxValue : LongWord): LongWord; Inline;
Для проверки производительности можно получить текущее время работы checker'а:
Function SecondsRunning (): Double; Inline;
Утилитарные типы и подпрограммы
Для упрощения разработки определены дополнительные типы и функции:
TYPE
TCharSet = Set of Char; TWords = Array of AnsiString; TIndex = {$IfDef CPU64} Int64 {$Else} Integer {$EndIf};
TIndex предназначен для индексирования по нецелым значениям (перегружаем присваивание типа в TIndex, и его можно использовать как индекс массива или строки).
Function CharSet (Const S : AnsiString): TCharSet;
Function Split (Const S : AnsiString; Const SplitChars : TCharSet = [' ']): TWords;
Function Split (Const S, SplitChars : AnsiString): TWords; Inline;
Function ReadSplit (Const SplitChars : TCharSet = [' ']): TWords; Inline;
Function ReadSplit (Const SplitChars : AnsiString): TWords; Inline;
Function ReadString (): AnsiString; Inline;
Procedure Count (Var Counter : LongWord; Condition : Boolean; X : LongWord = 1);
Procedure Count (Var Counter : Word; Condition : Boolean; X : Word = 1);
Procedure Count (Var Counter : Byte; Condition : Boolean; X : Byte = 1);
Function CountSet (Const S : Array of Boolean): LongWord;
Function CountSet (Const S : TCharSet): Word;
Для сортировки, поиска и проверки на равенство используются функции-компараторы:
CONST LT = -1;
EQ = 0;
GT = +1;
Function CompareInt (Const A, B : LongInt): Integer;
Function CompareUInt (Const A, B : LongWord): Integer;
Function CompareBool (Const A, B : Boolean): Integer;
Векторы
Для упрощения работы с массивами был реализован тип-обёртка Вектор, с реализацией наиболее распространённых операций (для поддержки произвольных типов он был определён как шаблон). Также были сгенерированы типы векторов для базовых типов Pascal.
TYPE
Generic GVector<_T> = Object
Type
_Data = Array of _T; _Cmp = Function (Const A, B : _T): Integer; _Gen = Function (): _T; _IGen = Function (Index : LongWord): _T; _Upd = Procedure (Var X : _T); _Enum = Object Public
Constructor Create (Const Vector : GVector);
Function MoveNext (): Boolean; Inline;
Property Current: _T Read _Current;
End;
Public
Constructor Wrap (Arr : _Data; DefOrder : _Cmp = Nil);
Constructor Create (Size : LongWord; DefOrder : _Cmp = Nil);
Constructor CreateFilled (Size : LongWord; Const DefValue : _T; DefOrder : _Cmp = Nil);
Constructor Generate (Size : LongWord; Gen : _Gen; DefOrder : _Cmp = Nil);
Constructor Generate (Size : LongWord; Gen : _IGen; DefOrder : _Cmp = Nil);
Constructor Generate (Size : LongWord; Upd : _Upd; DefOrder : _Cmp = Nil);
Constructor Clone (Const From : Array of _T; DefOrder : _Cmp = Nil);
Constructor Clone (Const Other : GVector);
Constructor CloneSorted (Const Other : GVector; Order : _Cmp; Unique : Boolean = False);
Function Equals (Const Other : GVector): Boolean; Procedure Resize (Size : LongWord); Inline; Procedure Fill (Const Value : _T);
Procedure FillGen (Gen : _Gen);
Procedure FillGen (Gen : _IGen);
Procedure PMap (Upd : _Upd);
Function Get (Index : LongWord): _T; Inline;
Procedure Put (Index : LongWord; Const Value: _T); Inline;
Procedure Apply (Index : LongWord; Upd : _Upd); Inline;
Function IsSorted (): Boolean; Inline;
Function AllUnique (): Boolean; Inline;
Function Concat (Const Other : GVector): GVector; Inline;
Procedure Append (Const Other : GVector);
Procedure AddAll (Const Other : GVector);
Procedure AddAll (Const Other : GVector; Unique : Boolean);
Procedure Reverse ();
Function Contains (Value : _T): Boolean; Inline;
Function Find (Value : _T): LongWord;
Procedure Sort (Unique : Boolean = False); Inline;
Procedure Sort (Order : _Cmp; Unique : Boolean = False);
Function Copy (): GVector; Inline;
Function Reversed (): GVector; Inline;
Function Sorted (Unique : Boolean = False): GVector; Inline;
Function Sorted (Order : _Cmp; Unique : Boolean = False): GVector; Inline;
Function GetEnumerator (): _Enum; Inline;
Property Items[Index : LongWord]: _T Read Get Write Put; Default;
Property RawData: _Data Read Data; Property Length: LongWord Read Len;
Property Comparator: _Cmp Read Cmp;
End;
TInts = Specialize GVector<LongInt>;
TUInts = Specialize GVector<LongWord>;
TBools = Specialize GVector<Boolean>;
TStrings = Specialize GVector<AnsiString>;
Для поддержки сравнения, сортировки и поиска требуется задать компаратор, поэтому для базовых типов были определены функции генерации векторов:
Function Ints (Size : LongWord): TInts; Inline;
Function UInts (Size : LongWord): TUInts; Inline;
Function Bools (Size : LongWord): TBools; Inline;
Function Strings (Size : LongWord): TStrings; Inline;
Function IStrings (Size : LongWord): TStrings; Inline;
Function Ints (Size : LongWord; Gen : TInts._Gen): TInts; Inline;
Function UInts (Size : LongWord; Gen : TUInts._Gen): TUInts; Inline;
Function Bools (Size : LongWord; Gen : TBools._Gen): TBools; Inline;
Function Strings (Size : LongWord; Gen : TStrings._Gen): TStrings; Inline;
Function IStrings (Size : LongWord; Gen : TStrings._Gen): TStrings; Inline;
Function Ints (Size : LongWord; Upd : TInts._Upd): TInts; Inline;
Function UInts (Size : LongWord; Upd : TUInts._Upd): TUInts; Inline;
Function Bools (Size : LongWord; Upd : TBools._Upd): TBools; Inline;
Function Strings (Size : LongWord; Upd : TStrings._Upd): TStrings; Inline;
Function IStrings (Size : LongWord; Upd : TStrings._Upd): TStrings; Inline;
Function Ints (Const From : Array of LongInt): TInts; Inline;
Function UInts (Const From : Array of LongWord): TUInts; Inline;
Function Bools (Const From : Array of Boolean): TBools; Inline;
Function Strings (Const From : Array of AnsiString): TStrings; Inline;
Function IStrings (Const From : Array of AnsiString): TStrings; Inline;
Function Range (Num : LongInt): TInts; Inline;
Function Range (Num : LongWord): TUInts; Inline;
Function Range (Fro, To_ : LongInt; Step : LongInt = 1): TInts;
Function Range (Fro, To_ : LongWord; Step : LongInt = 1): TUInts;
Множества
Вектор поддерживает операции для работы над множествами (поиск элемента, слияние множеств), но для использования вектора в таком качестве требуется пересортировка (O(NlogN)) при каждом изменении, что удобно отнюдь не во всяком случае. Поэтому на основе сортируемых векторов был реализован шаблон множеств. Эта реализация использует изначально заданное значение полного множества (вхождение определяется булевским вектором), но поддерживает добавление/удаление элемента за O(logN); групповые операции дают оценку O(NlogN) в худшем случае (эквивалентно поодиночной обработке значений). Множества одного типа, но с разными надмножествами совместимы; единственное техническое ограничение – элемент, не входящий в надмножество данного множества, добавлен не будет (при создании нового множества за основу берётся первое). При потребности расширения можно сделать новое множество на основе текущего (расширив копию надмножества).
TYPE
Generic GSet<_E> = Object
Type
_Elems = Specialize GVector<_E>; Public
Constructor Create (Const From : _Elems; Full : Boolean = False);
Constructor Create (Const From, Initial : _Elems);
Constructor Clone (Const Other : GSet);
Function Convert (Const From : _Elems): GSet; Inline; Static;
Function Equals (Const Other : GSet): Boolean; Inline;
Procedure Clear (); Inline;
Function Contains (Const Elem : _E): Boolean; Inline;
Procedure Put (Const Elem : _E);
Procedure Del (Const Elem : _E);
Function IsEmpty (): Boolean; Inline;
Function Comparator (): _Elems._Cmp; Inline;
Function GetFullSet (): _Elems; Inline;
Function GetItems (): _Elems;
Procedure PutAll (Const Elems : _Elems); Procedure DelAll (Const Elems : _Elems); Procedure KeepAll (Const Elems : _Elems); Function ContainsAll (Const Elems : _Elems): Boolean;
Procedure Merge (Const Other : GSet); Inline;
Procedure Intersect (Const Other : GSet); Inline;
Procedure Subtract (Const Other : GSet); Inline;
Procedure MergeExclusively (Const Other : GSet); Inline;
Function IsSubset (Const Other : GSet): Boolean; Inline;
Function Union (Const Other : GSet): GSet; Inline;
Function Intersection (Const Other : GSet): GSet; Inline;
Function Difference (Const Other : GSet): GSet; Inline;
Function SymmetricDifference (Const Other : GSet): GSet; Inline;
Property RawValues: TBools Read Values; Property Size: LongWord Read Cardinality; Property Length: LongWord Read Len; End;
TIntSet = Specialize GSet<LongInt>;
TUIntSet = Specialize GSet<LongWord>;
TStringSet = Specialize GSet<AnsiString>;
Словари
Также на основе векторов был реализован шаблон словарей. Они (как и множества) используют логарифмический поиск по отсортированному вектору изначально заданных ключей. Если такой подход слишком медленный (и не выходит подобрать более подходящий алгоритм), можно использовать альтернативную реализацию множества или вектора на основе конвертирования типа в целочисленный, сбалансированных деревьев или хэш-таблиц по строковым ключам (последние два варианта требуют самостоятельной работы с памятью и указателями).
Также в словари была добавлена возможность завершить тестирование (с непройденным тестом) при попытке получения значения по несуществующему ключу (см. GetOrFail).
TYPE
Generic GDict<_K, _V> = Object
Type
_I = Record Key : _K;
Value : _V;
End;
_Keys = Specialize GVector<_K>; _Values = Specialize GVector<_V>; _Items = Array of _I; _Upd = Procedure (Var Value : _V); Public
Constructor Create (Const KeyList : _Keys);
Constructor Create (Const KeyList : _Keys; Const Default : _V);
Constructor Create (Const KeyList : _Keys; Const Initial : _Values);
Constructor Clone (Const From : Array of _I; Order : _Keys._Cmp);
Constructor Clone (Const Dict : GDict);
Constructor Clone (Const Dict : GDict; Const Default : _V);
Function Equals (Const Other : GDict; VCmp : _Values._Cmp = Nil): Boolean;
Procedure Reset (Const Value : _V);
Function HasKey (Const Key : _K): Boolean; Inline;
Function Get (Const Key : _K): _V; Inline;
Function Get (Const Key : _K; Const Default : _V): _V;
Function GetOrFail (Const Key : _K; Const ErrMsg : AnsiString = BadIO): _V; Inline;
Function GetOrFail (Const Key : _K; Const ErrMsg : AnsiString; Const Args : Array of Const): _V; Inline;
Procedure Put (Const Key : _K; Const Value : _V); Inline;
Procedure Apply (Const Key : _K; Upd : _Upd; Fail : TProcedure = Nil);
Function GetKeys (): _Keys; Inline;
Function GetValues (): _Values; Inline;
Function GetItems (): _Items;
Function Comparator (): _Keys._Cmp; Inline;
Property Items[Key : _K]: _V Read Get Write Put; Default;
Property RawValues: _Values Read Values; Property Length: LongWord Read Len;
End;
TStringInts = Specialize GDict<AnsiString, LongInt>;
TStringUInts = Specialize GDict<AnsiString, LongWord>;
TStringBools = Specialize GDict<AnsiString, Boolean>;
TStringStrings = Specialize GDict<AnsiString, AnsiString>;
Примеры применения
С использованием DLChecker были установлены несколько олимпиад, в частности, Московские олимпиады 2014-2016 годов.
Приведённые примеры:
{$Mode ObjFPC}
Uses
DLChecker;
VAR
Chk, Out : TStrings;
Function ReadLines(): TStrings;
Var
N : LongWord;
Begin
Result := Strings(16);
N := 0;
While not EoF Do Begin Inc(N);
If (N > Result.Length)
Then Result.Resize(N+N);
Result[N] := ReadString();
End;
While (N > 0) and (Result[N] = '') Do Dec(N);
Result.Resize(N);
End;
Procedure ReadChk ();
Begin
Chk := ReadLines();
End;
Procedure ReadOut ();
Begin
Out := ReadLines();
End;
Procedure Check ();
Begin
CAssert(Chk.Equals(Out), WrongAnswer);
Succeed();
End;
BEGIN
ReadAll(Nil, @ReadChk, @ReadOut);
Check();
END.
{$Mode ObjFPC}
Uses
DLChecker, Math;
TYPE
TMap = Array [0..MaxM] of Boolean;
TCrd = Record
X, Y : SmallInt;
End;
Function Crd (Const X, Y : SmallInt): TCrd; Inline;
Begin
Result.X := X;
Result.Y := Y;
End;
Operator + (Const A, B : TCrd): TCrd; Inline;
Begin
Exit( Crd(A.X+B.X, A.Y+B.Y) );
End;
VAR
Sol : Array of TMap;
H, W : Word;
N : Byte;
Operator := (Const XY : TCrd): TIndex; Begin
With XY
Do If (0 < X) and (X <= W) and
(0 < Y) and (Y <= H)
Then Exit(X + (Y-1) * W)
Else Exit(0); End;
Function FindLocation (Const Mark : TMap; Var S : TCrd): Boolean;
Var
X, Y : Word;
Begin
For X:=1 To W
Do For Y:=1 To H Do Begin
S := Crd(X, Y);
If Mark[S]
Then Exit(True);
End;
Exit(False);
End;
Function IsConnected (Const Map : TMap): Boolean;
Var
Mark : TMap;
Q : Array [1..MaxM] of TCrd;
P, Dxy : TCrd;
BQ, EQ : LongWord;
Begin
Mark := Map;
BQ := 0;
EQ := 1;
If not FindLocation(Map, Q[1])
Then Exit(False);
Mark[ Q[1] ] := False;
While BQ < EQ Do Begin
Inc(BQ);
For Dxy in Steps Do Begin
P := Q[BQ] + Dxy;
If not Mark[P]
Then Continue;
Mark[P] := False;
Inc(EQ);
Q[EQ] := P;
End;
End;
Exit(CountSet(Mark) = 0);
End;
Procedure Check();
Var
i : Byte;
Begin
For i:=1 To N
Do CAssert(IsConnected(Sol[i-1]), 'Shape %d is not connected', [i]);
CAssert(Combinations(Sol) = 2**N, 'Not all combinations are present');
Succeed();
End;
BEGIN
ReadAll(@ReadIn, Nil, @ReadOut);
Check();
END.
{$Mode ObjFPC}
Uses
DLChecker;
CONST
Penalty = 10;
VAR
Subtask : AnsiString;
ChkAlways, ChkOften,
OutAlways, OutOften : TStrings;
Procedure ReadChk ();
Begin
Subtask := GetSubtask('1 2'); ChkAlways := Strings( ReadSplit() ); ChkOften := Strings( ReadSplit() );
End;
Procedure ReadOut ();
Begin
OutAlways := Strings( ReadSplit() );
OutOften := Strings( ReadSplit() );
End;
Procedure Check();
Function Diff (Chk, Out : TStrings): LongWord;
Var
Used : TStringSet;
Begin
Used.Create(Chk, True);
Used.KeepAll(Out);
Exit(Chk.Length + Out.Length - 2*Used.Size)
End;
Var
Mistakes : LongWord;
Begin
Mistakes := Diff(ChkAlways, OutAlways) + Diff(ChkOften, OutOften);
ScoreTest(Penalty, Mistakes, Subtask = '2'); End;
BEGIN
ReadAll(Nil, @ReadChk, @ReadOut);
Check();
END.
{$Mode ObjFPC}
Uses
DLChecker, SysUtils, Math;
TYPE
TWordKind = (w_Noun, w_Verb, w_Adjective);
TLengths = Array [TWordKind] of Int64;
TWordInfo = Record
Kind : TWordKind;
Importance : Byte;
End;
TDict = Specialize GDict<AnsiString, TWordInfo>;
TInput = Record
Dict : TDict;
MinSize, MaxSize : LongWord;
End;
TInputs = Specialize GVector<TInput>;
CONST
Invalid = -1;
VAR
Inputs : TInputs;
Ans, Sol : TStrings;
Partial : Word;
Function ReadWord (Kind : TWordKind): TDict._I;
Var
WI : TWords;
Begin
With Result Do Begin
WI := ReadSplit();
Key := WI[0];
Value.Kind := Kind;
Value.Importance := StrToInt(WI[1]); End;
End;
Function ReadDict (Const Len : TLengths): TDict._Items;
Var
M, j : LongWord;
Kind : TWordKind;
Begin
SetLength(Result, SumInt(Len));
M := 0;
For Kind in TWordKind
Do For j:=1 To Len[Kind] Do Begin
Result[M] := ReadWord(Kind);
Inc(M);
End;
End;
Function ReadInput (): TInput;
Var
L : TLengths;
Begin
With Result Do Begin
ReadLn(L[w_Noun], L[w_Verb], L[w_Adjective]);
Dict.Clone(ReadDict(L), @CompareText);
ReadLn(MinSize, MaxSize);
End;
End;
Procedure ReadIn ();
Var
T : LongWord;
Begin
ReadLn(T);
Inputs.Generate(T, @ReadInput); End;
Procedure ReadChk ();
Begin
Partial := ParseUInt(GetSubtask(), MaxPoints);
Ans.Generate(Inputs.Length, @ReadString); End;
Procedure ReadOut ();
Begin
Sol.Generate(Inputs.Length, @ReadString);
End;
Procedure MarkWord (Var B : Boolean); Begin
CAssert(not B, 'Word already used!'); B := True;
End;
Procedure NoWord (); Begin
RageExit('Unknown word');
End;
Procedure Check();
Function Calc (Const Line : AnsiString; Const TestCase : TInput): LongInt;
Var
Used : TStringBools; Sentence, Word, S : AnsiString;
N, V : LongWord;
LastAdj : Boolean;
Size : LongWord;
Begin
With TestCase Do Begin
CAssert((Line <> '') and (Line[Length(Line)] = '.'), 'Invalid line');
Used.CreateReset(TStrings(Dict.GetKeys), False);
Result := 0;
For Sentence in Split(' '+Line, '.') Do Begin
N := 0;
V := 0;
LastAdj := False;
S := Copy(Sentence, 3, Length(Sentence));
CAssert((Length(Sentence) > 1) and (Sentence[1] = ' ') and
(UpCase(Sentence[2]) = Sentence[2]) and
(LowerCase(S) = S), 'Bad formatting');
For Word in Split(Sentence) Do Begin
Used.Apply(Word, @MarkWord, @NoWord);
With Dict[Word] Do Begin
Inc(Result, Importance);
Case Kind of
w_Noun : Begin
LastAdj := False;
Inc(N);
End;
w_Verb: Begin
CAssert(not LastAdj, 'LastAdj');
Inc(V);
End;
Else
CAssert(not LastAdj, 'LastAdj');
LastAdj := True;
End;
End;
End;
CAssert((V = 1) and (N > 0) and not LastAdj, 'Invalid sentence');
End;
Size := CountSet(Used.RawValues.RawData);
If (MinSize > Size) or (Size > MaxSize)
Then Exit(Invalid);
End;
End;
Var
Score, N, i : Word;
Begin
N := Inputs.Length;
Score := 0;
For i:=1 To N
Do Count(Score, Calc(Ans[i], Inputs[i]) = Calc(Sol[i], Inputs[i]));
If Score = N
Then Succeed();
CAssert(Partial > 0, WrongAnswer);
Succeed(Partial * Score div N, '%d/%d', [Score, N]);
End;
BEGIN
ReadAll(@ReadIn, @ReadChk, @ReadOut);
Check();
END.
|
|
|