Цифры в названии – это размерность лабиринта. Трехмерный лабиринт 7×7×7.
Игровая предыстория. Вы историк-археолог будущего, занимающийся исследованиями заброшенных объектов. Для путешествия к ним используете индивидуальный аппарат подпространственного перемещения – джампер. Во время очередного прыжка произошёл сбой и ваш джампер из-за крохотного временного залипания отстал от вас в месте высадки. Т. е. на место вы попали, но вот сам джампер оказался где-то рядом, но не с вами. Так как в целях безопасности он автоматически перешёл в режим невидимости, то придётся вам поискать его. К счастью, когда вы будете рядом, то он автоматически активируется.
Посмотрим, где же вы оказались. Какие-то ряды комнат со слабо видимыми энергетическими оболочками (стенками) между ними. Причём, в горизонтальный ряд они прозрачны, а между рядами нет. В ряд виднеется восемь комнат. С левой стороны на стене написан номер ряда, с правой стороны, за дополнительной недоступной комнатой и какой-то конструкцией ещё цифры – это отображается номер этажа.
Перед отправкой вы, как обычно, надели защитный костюм (скафандр), поэтому энергетические стены вам по силам преодолеть. К сожалению, разные комнаты по-разному реагируют на выход их них (вход проходит без затрат энергии). Есть обычные комнаты, выход из которых идёт без затрат и заряд скафандра, первоначально равный девяти, увеличивается на автоподзарядке на одну единицу. Есть комнаты, где разлита какая-то жидкость (лужи) и выход из этих комнат уже съедает этот накопленный заряд (по нулям). Но есть ещё комнаты с локальными джамперами, которые могут переместить на этаж выше (лестница) или ниже (яма). Причём не важно, используете вы локальный джампер или нет, преодоление барьера в таких комнатах отнимает 5-6 единиц зарядка скафандра (с учётом автоподзарядки – 4-5).
Цель – найти комнату, где активируется ваш персональный джампер. Причем нужно следить за уровнем заряда скафандра. В здании на каждом этаже 7 рядов. Всего в здании 7 этажей. Работёнка предстоит не быстрая.
Перед первым запуском внесём константы в регистры. Если используете эмулятор, то уже вбито:
Регистр | Значение |
---|---|
R6 | 7 |
R7 | 64 |
R8 | 9.5555555 (быстрее 86/9) |
R9 | 81 |
Ra | 88 |
Rc | Любое случайное число в диапазоне 0…1. Можно оставить ноль. |
Положение переключателя Р-ГРД-Г обязательно должно быть в положении Р (начальном по умолчанию).
Начало игры: В/О С/П. ПМК отобразит положение игрока.
Теперь о том, как это выглядит. Экран ПМК отображает текущий ряд со всеми комнатами в нём, потому что у комнат в ряд стенки прозрачны. Слева отображается номер ряда, справа за отдельной недоступной обычной комнатой – номер этажа. Также положение игрока в ряду выделяется отдельно.
Для управления движением используются клавиши 2, 4, 5, 6, 8. Значение определяется положением относительно центра. Где 4, 6 – движение влево или вправо по ряду. В крайних позициях попытка движения ни к чему не приводит. 2, 8 – движение вперёд или назад по рядам. Также выход за границы ограничен. Для использования лестниц и ям используется клавиша 5. Можно и просто уйти из комнаты, пользоваться локальными джамперами совсем не обязательно.
После нанажатия клавиши направления и С/П ПМК отобразит результат нового положения игрока. Или сразу ноль, что означает, что в данном месте оказался персональный джамер и игрок успешно покинул неприветливый офис – победа. В регистре Y отображается уровень заряда скафандра. В случае, если уровень оказался недостаточным, то ПМК остановится с отрицательным числом (сколько не хватило). Игра считается проигранной. Независимо от способа окончания игры последующее С/П начнёт новую игру.
Поясним, как отображаются комнаты.
Символ | Пояснение |
---|---|
8 | Игрок в обычной комнате. При выходе заряд +1 |
9 | Игрок в комнате с лужами. При выходе заряд +0 |
- | Обычная команата |
L | Комната с лужами |
C | Игрок в комнате с лестницей. При выходе заряд −4 |
Г | Игрок в комнате с ямой. При выходе заряд −5 |
E | Комната с лестницей |
Комната с ямой |
Вот как выглядит положение на экране ПМК при нулевом начальном значении датчика случайных чисел. 5.ЕL8 --L-05 Здесь, слева направо, номер ряда, точка (начало ряда), лестница, лужа, игрок в обычной комнате, яма, два раза обычная комната, лужа. А затем недоступная обычная комната (конец ряда) и за ней странное сооружение (0) и номер этажа.
Для пояснения покажем весь этаж (но только один) при том же нулевом начальном значении датчика случайных чисел, в том виде, как покажет ПМК (без положения игрока). Как видите, положение ям и лестниц не симметрично.
7.LЕE --L-05 |
6.Е--Е--L-05 |
5.ЕL- --L-05 |
4.L -Е----05 |
3.-LEL----05 |
2.Е E-----05 |
1.L-L---L-05 |
ПМК делает небольшую проверку того, что ввели команду направления движения, а не просто случайно нажали С/П. В том случае, когда на экране отображается ряд, после нажатия С/П ещё раз отобразится то же самое изображение. Эту же проверку можно использовать, если вы случайно стёрли изображение и хотите вспомнить, где находитесь. В этом случае введите ноль и С/П.
Подсказка: для накопления заряда скафандра походите туда-сюда по обычным комнатам.
Напомню, что по окончании игры, не важно успешно (0) или нет (минус), можно сразу нажать С/П для начала новой игры. В этом случае значение уровня заряда снова устанавливается в 9, генерируется новый лабиринт и новое случайное положение игрока и выхода в нём.
Приятной игры!
R0 | Рабочий регистр |
---|---|
R1 | Положение в текущем ряду [1..7] |
R2 | Числовое представление текущего ряда |
R3 | Номер ряда на этаже [1..7] |
R4 | Уровень заряда скафандра |
R5 | Номер этажа |
R6 | Константа 7 |
R7 | 64 – адрес процедуры генерации случайного положения |
R8 | Константа 9.5555555 – битовая маска |
R9 | 81 – адрес процедуры перевода числа в диапазон [1..7] |
Ra | 88 – адрес процедуры генерации плана текущего положения |
Rb | Не используется |
Rc | Случайное число (0..1) |
Rd | Рабочий регистр |
Re | Положение выхода |
# | | 00 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 |
---|---|---|---|---|---|---|---|---|---|---|
00 | | В/О | КПП7 | КППa | x→Пe | П→xc | КПП7 | x→Пc | П→xe | КППa | − |
10 | | Fx≠0 | 63 | П→x4 | П→x3 | FВx | F↻ | КНОП | ВП | 1 | /-/ |
20 | | FL0 | 16 | С/П | К[x] | Кx≠02 | П→x2 | П→x1 | F10x | 5 | + |
30 | | К∧ | К{x} | − | ВП | x→П0 | <-> | 5 | − | Fx=0 | 46 |
40 | | Fcos-1 | − | Кx≥02 | Fsin | КЗН | × | К∣x∣ | x→Пd | КП→xd | В↑ |
50 | | FВx | КЗН | + | КПП9 | Кx→Пd | − | Кx≠02 | КП→x4 | П→x4 | П→x0 |
60 | | − | x→П4 | Кx<02 | С/П | 6 | x→П0 | F↻ | 7 | П→xc | × |
70 | | Fπ | + | К{x} | x→Пc | 9 | x→П4 | × | К[x] | КПП9 | Кx→П0 |
80 | | FL0 | 66 | Кmax | FВx | − | КЗН | − | В/О | П→x8 | П→xc |
90 | | П→x5 | x→П0 | Fsin | ÷ | П→x3 | × | К∧ | x→П2 | − | 2 |
A0 | | П→x1 | F10x | ÷ | + | КИНВ |
Основная идея програмы – не хранение плана этажа, но вычисление его по стандартному алгоритму. Что при одних и тех же входных параметрах даёт одинаковое представление, что создаёт иллюзию того, что ПМК помнит весь лабиринт.
Для этого используется процедура, начинающаяся с адрес 88 (R7). Она на основе трех координат положения игрока и случайного числа формирует видеизображение текущего ряда по формуле C / sin(Z) * Y, где Y и Z это длина (номер ряда) и высота (номер эатаж), а C – случайное число. У каждой цифры полученного числа оставлем только биты 0 и 2 (число 5 в битовой маске из R8). Это вариант сохраняется в отдельном регистре (R2) для анализа ячейки текущего положения, а результат сначала инвертируется на трех первых битах (через вычитания из R8), а затем на всех битах, через КИНВ (предварительно добавив положение игрока в текущем ряду), чтобы получилось понятное видеизображение. Оно ещё не содержит номер ряда и номер этажа, который между делом копируется в R0 для последующего использования. Тут команда В/О перенесена на адрес 00 (A5) не из-за каких-то экономий, а просто ради подгонки адресации для R6 и R2.
Далее рассмотрим процедуру, начинающуюся с адреса 81 (R9). Да, там стоит адрес перехода по FLO, но этот также код для операции П→x6. Эта процедура предполагает, что на входе (RX) число из диапазона [0..8], а она безусловно переводит его в диапазон [1..7]. Для 8-ки понятно, что излишняя единица будет вычтена, потому что в Кmax 8 вытеснит 7, а разница и составляет единицу. А вот для нуля используется тот факт, что ПМК считает его самым большим числом, поэтому семёрка будет вычитаться из него. Для пребразования в −1 и используется КЗН.
Продолжим разбор подпрограмм. Следующая – генерация случайного положения (все три
кординаты X, Y, Z) с адреса 64 (Ra). Для генератора случайных чисел используется
формула N = {7 * N + π}. Благодаря тому, что R0 используется не только в цикле,
но и для косвенного сохранения, получается сохранять значения в регистрах
через одного
– R5, R3, R1.
Промежуточное сохранение 9-ки в R4 служит для начального заряда скафандра.
Фактически хватило бы умножения числа на 8, но процедура в R9 (смотри выше)
всё равно вернёт 8-ку в нужный диапазон, а так мы даём чуть больше начальной энергии.
Тут нужно пояснить, что процедура хитро очищает стек. В цикле, за счет оператора по
адресу 66, только что использованное в RX число забывается. А за счёт того, что
по окончании цикла идёт переход на процедуру из R9 (такой трюк для экономии на
В/О) без извлечения 7-ки из R6, то и последнее
сгенерированное число тоже уберётся из стека, оставив в нём то, что было до вызова
этой процедуры. Это потому, что в стеке до этого будет число меньше
чем один, а процедура сгенерит число из диапазона [1..7], которое явно больше.
Тоже трюк, а совместо с двойным использование команды по адресу 61 получается уже третий
в одной подпрограмме.
Ну а теперь рассмотрим всё по порядку. Почему В/О в начале, уже упоминалось, но напомним – чтобы цикл процедуры Ra начинался в адреса 66 = R6, потому что R5 уже используется для другого. А также потому, что начало основного цикла приходится на адрес 07, что удобно для воврата на него по регистру R2, где в результате бинарной операции будет 8.{с чем-то}.
Далее. Сначала, с помощью КПП7 формируется координаты выхода. Затем с помощью КППa формируется видеоизображения для выхода и сохраняется для дальнейшей проверки в Re.
Затем использованное для этого случайное число сохраняется в стеке, а потом с помощью КПП7 формируется уже начальное положение игрока. Напомню, что особенностью этой процедуры является восстановление стека до выполнения. Поэтому сохранившееся в нём случайное число восстанавливаем обратно, иначе нам то же самое изображение, что и выход, не получить.
С адреса 07 начинается основной цикл программы. Для дальнейшего сравнения изображение выхода извлекается в стек из Re, затем вызывается процедура формирования изображения по текущим координатам. Если разница нулевая, то с полученным нулём идёт на выход. В обычном случае извлекаем в стек значения уровня заряда скафандра и номер ряда, а затем извлекаем из X1 сформированное изображение ряда. Далее идёт нестандартное использование возможностей ПМК, описанное в документе Недокументированные возможности в разделах про X2. В частности команда FВx не только извлекает видеоизображение из X1, но и запоминает его в недокументированном регистре R2. Именно его значение восстанавливается в RX после команды ВП, сохраняя только первый разряд, который к тому времени и есть номер ряда. Получается, что первая цифра видеоизображения будет показывать номер ряда. Такой вот трюк, который при пошаговом режиме, кстати, не работает. Значение заряда остаётся в RY. В цикле по номеру этажа убавляется номер порядка на 1, что приводит к добавлению к видеоизображению номера этажа.
После остановки мы ожидаем, что игрок введёт код команды (2, 4, 5, 6, 8). Для простой проверки, что код введён, а не осталось видеоизображение, и используется К[x] Кx≠02.
Далее, тоже с помощью трюка, получаем цифру из запомненного в R2 числа, на месте текущего X положения игрока в ряду (R1). Для этого с помощью числа вида 10…05 получаем битовую пятёрку на месте нужном месте. Для примера, для X = 2 будет 102 + 5 = 105. С помощью К∧ выделяем только биты в нужном разряде. Дробной часть оставляет только это число, но с неизвестной степенью. А трюк с ВП (здесь игнорируемый ПМК минус используется, чтобы убрать из стека число с пятёркой в конце) восстанавливает X2 (последняя команда 5), не меняя первый разряд. Это то, что нам и нужно. Сохраняем это число (0, 1, 4 или 5) в R0 и возвращаемся к команде игрока.
Благодаря вычитанию 5, возможные команды распадаются на -1, 1 (движение по ряду),
на -3, 3 (движение по рядам) и 0 – движение между этажами. Сначала его и проанализируем.
Через Fcos-1 из нуля
делаем что-то около 1.5 (π/2). Что при вычитании из 0 или 1 (то, что сохранилось
в R0 и осталось в стеке) отбросит вариант не лестницы (4) или ямы (5). Более того,
эта разность с помощью Fsin
удачно получается разного знака для 4 и 5. А команда пользователя,
которая для данного случая равна пяти, в результате вычислений дошла
в стеке до регистра Z, а значит после всех этих вычислений уже в RY, поэтому
мы знак числа просто умножаем на эту 5.
По адресу 46 на входе в RX у нас ±1, ±3 или ±5. Как удачно, у нас как раз в регистрах с этими номерами и хранятся координаты 😊. Поэтому модуль числа мы сохраняем в Rd, который и будет использовать для косвенного обращения к значениям координат, а знак числа используем для приращения координат. Предварительно неизменённое значение координаты заталкивается в стек. Потом делаем сложение и вызов КПП9, чтобы убедиться, что новые координаты в диапазоне [1..7]. А вычитание из предыдущего их значения позволяет проверить, а было ли вообще движение – проверка границ.
Финально происходит увеличение уровня заряда в R4 на 1, а потом уменьшение на цифру из текущего положения (R0). Если получается минус, то игра завершается, если нет, то возврат на основной цикл программы. Благодаря тому, что после окончательного С/П идёт процедура, которая по В/О вернёт на начало, для начала новой игры можно просто продолжить и нажать С/П.