Чтобы вложенные предметы были доступны по очереди, сделал их через вложенные ссылки.
У меня получилось так.
Сам алгоритм:
existSearchDeath := FALSE; (* смерть Кощея пока не найдена *)
WHILE
(i < ds.n) (* проверка, что непросмотренные сундуки еще остались *)
& ~existSearchDeath (* проверка, что смерть Кощея еще не найдена *)
DO
IF
(ds.сhests^[i].hare # NIL) (* проверка, что в сундуке есть заяц *)
& (ds.сhests^[i].hare^.duck # NIL) (* проверка, что в зайце есть утка *)
& (ds.сhests^[i].hare^.duck^.egg # NIL) (* проверка, что в утке есть яйцо *)
& (ds.сhests^[i].hare^.duck^.egg^.needle # NIL) (* проверка, что в яйце есть игла *)
THEN
existSearchDeath := TRUE (* смерть Кощея найдена *)
END; (* проверка наличия смерти Кощея в сундуке *)
INC(i) (* переход к следующему (еще непросмотренному) сундуку, если такой есть *)
END
Модуль поиска:
MODULE searchdeathscrag;
IMPORT
StdLog, ds := deathscrag;
VAR
i-: INTEGER;
(* номер первого еще не просмотренного сундука, если такой есть *)
(* (i >= 0) & (i <= n) *)
existSearchDeath-: BOOLEAN;
(* existSearchDeath = (следующая смерть Кощея найдена при последнем поиске) *)
(* existSearchDeath -> ((номер последнего найденного сундука со смертью Кощея) = i-1) *)
(* ~existSearchDeath -> (i = n) *)
(* (i = 0) -> ~existDeath *)
k: LONGINT;
(* номер версии набора сундуков, в котором идет поиск *)
PROCEDURE ClearSearch*;
BEGIN
i := 0; (* переход к первому сундуку *)
existSearchDeath := FALSE; (* смерть Кощея не найдена *)
k := ds.k (* сохранение версии набора сундуков *)
END ClearSearch; (* очистка поиска *)
PROCEDURE SearchDeath*(*0:0*);
BEGIN
IF k # ds.k THEN (* проверка версии набора сундуков *)
StdLog.Ln;
StdLog.String("дальнейший поиск невозможен, так как массив сундуков был изменен");
StdLog.Ln;
HALT(0)
END(*0:0*); (* прерывание поиска если номер версии набора сундуков устарел *)
existSearchDeath := FALSE; (* смерть Кощея пока не найдена *)
WHILE
(i < ds.n) (* проверка, что непросмотренные сундуки еще остались *)
& ~existSearchDeath (* проверка, что смерть Кощея еще не найдена *)
DO
IF
(ds.сhests^[i].hare # NIL) (* проверка, что в сундуке есть заяц *)
& (ds.сhests^[i].hare^.duck # NIL) (* проверка, что в зайце есть утка *)
& (ds.сhests^[i].hare^.duck^.egg # NIL) (* проверка, что в утке есть яйцо *)
& (ds.сhests^[i].hare^.duck^.egg^.needle # NIL) (* проверка, что в яйце есть игла *)
THEN
existSearchDeath := TRUE (* смерть Кощея найдена *)
END; (* проверка наличия смерти Кощея в сундуке *)
INC(i) (* переход к следующему (еще непросмотренному) сундуку, если такой есть *)
END
END(*0:0*) SearchDeath; (* поиск смерти Кощея *)
(*
existDeath :=
(ds.chests^[i].hare # NIL) (* проверка, что в сундуке есть заяц *)
& (ds.chests^[i].hare^.duck # NIL) (* проверка, что в зайце есть утка *)
& (ds.chests^[i].hare^.duck^.egg # NIL) (* проверка, что в утке есть яйцо *)
& (ds.chests^[i].hare^.duck^.egg^.needle # NIL) (* проверка, что в яйце есть игла *)
(* проверка наличия смерти Кощея в сундуке *)
*)
BEGIN
ClearSearch (* очистка поиска *)
END searchdeathscrag.
И модуль Кощея:
MODULE deathscrag;
IMPORT
StdLog;
CONST
noExistDeath = TRUE;
(* разрешено не иметь ни одного сундука со смертью Кощея, если число сундуков не нуль *)
multyExistDeath = TRUE;
(* разрешено иметь несколько сундуков со смертью Кощея *)
TYPE TypeChests* =
POINTER TO
ARRAY OF
RECORD
hare*: POINTER TO
RECORD
duck*: POINTER TO
RECORD
egg*: POINTER TO
RECORD
needle*: POINTER TO
RECORD
END (* тип иглы *)
END (* тип яйца *)
END (* тип утки *)
END (* тип зайца *)
END; (* тип сундука *)
(* тип массива сундуков *)
(* тип набора сундуков *)
VAR
сhests-: TypeChests;
(* набор сундуков *)
n-: INTEGER;
(* число сундуков *)
(* (chests # NIL) -> (n = LEN(chests^)) *)
(* (chests = NIL) -> (n = 0) *)
k-: LONGINT;
(* номер версии набора сундуков *)
PROCEDURE IncK (*0:0*);
BEGIN
IF k < MAX(LONGINT) THEN
INC(k) (* переход к номеру новой версии набора сундуков *)
ELSE
StdLog.String("дальнейшие версии набора сундуков невозможны, перезагрузите модули");
HALT(0)
END(*0:0*)
END(*0:0*) IncK; (* установка нового номера версии набора сундуков *)
PROCEDURE ClearChests* (*0:0*);
BEGIN
n := 0; (* количество сундуков в новом пустом наборе *)
IncK(*0:0*); (* переход к номеру новой версии набора сундуков *)
сhests := NIL; (* создание пустого массива сундуков *)
END(*0:0*) ClearChests; (* установка пустого массива сундуков *)
(* forall j (0 <= j <= LEN(a))(0 <= a[j] <= 4) *)
(* noExistDeath OR (exist j (0 <= j <= LEN(a))(a[j] = 4)) *)
(* multyExistDeath OR
(forall j1, j2 (0 <= j1,j2 <= LEN(a))((a[j1] = 4) & (a[j2] = 4) -> (j1 = j2))) *)
PROCEDURE NewChests* (IN a: ARRAY OF BYTE; OUT b: BOOLEAN)(*0:0*);
VAR
bNo, bMulty: BOOLEAN;
j: INTEGER;
(* номер первого сундука с непрописанным содержимым, если такой есть *)
BEGIN
bNo := FALSE; (* сундука со смертью Кощея пока не обнаружено *)
bMulty := FALSE; (* несколько сундуков со смертью Кощея пока не обнаружено *)
b := TRUE; (* ошибок данных пока не обнаружено *)
n := LEN(a); (* новое количество сундуков в наборе; LEN(a) > 0 *)
NEW(сhests, n); (* создание нового набора сундуков *)
j := 0; (* переход к первому сундуку с непрописанным содержимым,
если такой есть *)
WHILE
(j < n) (* проверка, что сундуки с непрописанным содержимым еще остались *)
& b (* проверка, что ошибок данных пока не обнаружено *)
DO
IF a[j] = 0 THEN
сhests^[j].hare := NIL (* сундук остается пустым *)
ELSE
NEW(сhests^[j].hare); (* помещение зайца в сундук *)
IF a[j] = 1 THEN
сhests^[j].hare^.duck := NIL (* зайц остается пустым *)
ELSE
NEW(сhests^[j].hare^.duck); (* помещение утки в зайца *)
IF a[j] = 2 THEN
сhests^[j].hare^.duck^.egg := NIL (* утка остается пустой *)
ELSE
NEW(сhests^[j].hare^.duck^.egg); (* помещение яйца в утку *)
IF a[j] = 3 THEN
сhests^[j].hare^.duck^.egg^.needle := NIL (* яйцо остается пустым *)
ELSIF a[j] = 4 THEN
NEW(сhests^[j].hare^.duck^.egg^.needle); (* помещение иглы в яйцо *)
IF ~bNo THEN (* проверка, что сундука со смертью Кощея еще не обнаружено *)
bNo := TRUE (* первый сундук со смертью Кощея обнаружен *)
ELSIF ~bMulty THEN (* проверка, что только один сундук со смертью Кощея
обнаружен *)
bMulty := TRUE; (* несколько сундуков со смертью Кощея обнаружено *)
b := multyExistDeath (* ошибка данных обнаружена, если не разрешено иметь
несколько сундуков со смертью Кощея *)
END
ELSE
b := FALSE (* ошибка данных обнаружена *)
END
END
END
END; (* установка содержимого j-го сундука,
либо прекращение установки при ошибке данных *)
INC(j) (* переход к следующему сундуку с непрописанным содержимым,
если такой есть *)
END;
b := b & (noExistDeath OR bNo); (* ошибка данных обнаружена, если не имеется ни одного
сундука со смертью Кощея и это не разрешено *)
IF ~b THEN
ClearChests(*0:0*) (* создание пустого массива сундуков при наличии ошибки данных *)
ELSE
IncK(*0:0*) (* переход к номеру новой версии набора сундуков *)
END(*0:0*)
END(*0:0*) NewChests; (* создание нового массива сундуков с новым содержимым *)
(* b -> (в массиве 'a' ошибок данных нет,
новый массив сундуков установлен согласно содержимому массива 'a') *)
(* ~b -> (в массиве 'a' есть ошибки данных, новый массив сундуков пустой) *)
BEGIN
k := -1; (* номер версии, предшествующий версии первого набора сундуков *)
ClearChests (* установка первого массива сундуков *)
END deathscrag.
Перевод:
смерть кощей сундук заяц утка яйцо игла
death scrag chest hare duck egg needle