Охота на лис

Игра Охота на лис (логическая игра).

Задача

Найти всех лис.

Общее описание

На поле 10 на 10 случайным образом расположены 9 лис. Все в разных клетках. Вы задаёте своё положение, указав координаты клетки (координаты охотника). В ответ ПМК выдаёт количество лис, пеленгуемых из указанной клетки. Это число указывает, сколько лис расположено в одной вертикали, горизонтали и диагоналях с указанной клеткой.

Если координаты клетки совпадают с положением одной из лис, то она считается найденной, исключается из дальнейшей пеленгации. В ответ ПМК выводит число оставшихся лис в виде отрицательного числа, чтобы не путать с обычным ответом.

Игровой процесс

Начало

Перед запуском укажите случайное число от 0 до 1. Оно будет использовано для генерации положений лис, затем В/О С/П.

ПМК расставит лис случайным образом и по окончании выдаст −9, как бы сообщая, что осталось 9 лис. В регистре Y тоже будет −9.

Игра

Вы задаёте координаты клетки, из которой хотите провести пеленгацию, числом в виде пары цифр, разделённых десятичной точкой. Обычно целая часть – вертикальная координата, а дробная – горизонтальная. Впрочем, можете считать наоборот, для игры это не важно – поле-то квадратное! После С/П ПМК выдаст результат на экран. Вот возможные результаты:

Число Значение
0…9 Количество лис, запеленгованных из заданной координаты. Сами координаты находятся в регистре Y. Для игрока, конечно, идеальный ответ это ноль. В этом случае сразу столько клеток поля исключаются из рассмотрения.
−8…−1 Координаты клетки совпали с координатами одной из лис. Абсолютное значение показывает количество оставшихся лис. Найденная лиса исключается, т. е. в дальнейшем не пеленгуется. Координаты клетки находятся в регистре Y.
11 Вы ввели недопустимые координаты. Разрешено дробное число больше или равное 0 и меньше 10. Неверные координаты клетки находятся в регистре Y. В подсчёте ходов неверный выбор не учитывается.

Окончание

Если вы нашли всех лис, то на экран выдаётся 3.3333333-01, как признак окончания игры. В регистре Y указано количество ходов, которые вы сделали за игру.

Для начала новой игры введите случайное число от 0 до 1 и нажмите С/П (или В/0 С/П).

Дополнительная информация

Контроль со стороны ПМК

Модификация

Общее количество лис для игры можно изменить – облегчить игру. Достаточно в программе по адресу 01 вместо 9 указать другую цифру (но не ноль!).

Если есть желание не исключать найденных лис, чтобы они по-прежнему участвовали в пеленгации, то по адресу 41 нужно поставить команду KНОП, вместо Kx→П4, а по адресу 60 общее число лис (9), вместо П→x3. В этом случае нужно быть внимательней, потому что счётчик оставшихся лис работает. И если вы случайно найдёте ту же лису повторно, то счётчик собьётся и ПМК завершит игру ранее, чем вы найдёте всех лис. Впрочем, этот счётчик тоже можно отключить, введя по адресу 99 команду БП, вместо FFL3. В этом случае, когда вы найдёте лису, ПМК будет всегда выдавать −9, и считать лис и окончании игры вам придётся самостоятельно.

История

Первая версия (Несчетный А.) впервые была опубликована в журнале Наука и жизнь. №12 за 1985. В этой версии поле было 9 x 9, пять лис, без контроля ходов, подсчета лис и т. п.

Следующая версия (Иванов Ю., Иртюга П.) была опубликована в журнале Наука и жизнь №10 за 1990. В этой версии уже 8 лис, есть учёт найденных лис, и они уже не участвуют в пеленгации. Подсчитывается количество ходов. Координаты шифруются, чтобы нельзя было подсмотреть значение в регистрах. Но в одной клетке по-прежнему может содержаться несколько лис. В программе содержится малозаметная ошибка: если в процессе начальной расстановки лиса (или несколько) попадала в клетку с координатами 0:0, то она не участвовала в пеленгации.

В дальнейшем встречалось в разных источниках ещё несколько модификаций. Вот пример:

В этом варианте (Булатов А.) по сравнению с предыдущей версией лис уже девять, и они уже все в разных клетках. Но найденные лисы не исключаются из пеленгации. Также о том что игра закончилась, можно догадаться, только увидев что осталось 0 лис. И отсутствует счётчик ходов.

Еще версия (Храмов С.) в которой почти всё есть. И счётчик ходов, и автозавершение игры, и шифрация, и все лисы по разным норкам. Даже количество лис можно менять, правда в трех местах, и кое-где на +1, что не всегда удобно. Но максимум лис – восемь, что меньше чем у предыдущей версии.

В моей модификации всё тоже (шифрация ещё чуть запутаннее), но максимум лис – 9. Ведётся контроль хода игрока. Координаты клетки охотника не нужно подсматривать в отдельном регистре, он находится тут же в стеке. По окончании счётчик ходов тоже выводится в стек.


Далее только для тех, кто не только играет…

Программа делится на две части. Первая – инициализация и расстановка лис на основе случайного числа, ведённого игроком. Вторая – цикл поиска лис.

Распределение регистров

R0, R7…Re Координаты 9 лис. Если задано меньше 9 лис, то часть регистров не используется. В числовом выражении регистры 7…15, где R14 = Re, а R15 = R0.
R1 В первой части счётчик для цикла проверки по ранее рассаженным лисам, чтобы исключить двух лис в одной клетке. Во второй части – ход игрока.
R2 Счётчик для пробега по всем лисам. В первой части чтобы всех расставить, во второй – чтобы сравнить с координатами охотника.
R3 Счётчик ещё не найденных лис.
R4 Индекс регистра текущей лисы, используется совместно со счётчиком в R2.
R5 В первой части это количество уже рассаженных лис + 1. Во второй – сумматор пеленгуемых лис.
R6 Счётчик ходов.

Текст программы

 # |  00 01 02 03 04 05 06 07 08 09
 00 |  x→П0 9 x→П2 x→П3 0 x→П5 x→П6 КП→x5 П→x5 x→П1
 10 |  2 F10x Fπ П→x0 7 × + К{x} x→П0 ×
 20 |  К[x] Fx2 <-> ÷ Fx2 5 x→П4 КП→x4 Fx≠0
 30 |  08 3E FL1 27 Кx→П4 FL2 07 x→П4 П→x3
 40 |  /-/ Кx→П4 П→x1 <-> С/П П→x2 F10x x→П5 × К[x]
 50 |  П→x5 ÷ x→П1 Fx≥0 93 П→x5 Fx<0 93 КП→x6
 60 |  П→x3 x→П2 6 x→П4 0 x→П5 П→x4 П→x1 КП→x4 Fx≥0
 70 |  66 F F Fx≠0 99 FВx К{x} П→x1 К{x}
 80 |  Fx≠0 93 + Fx≠0 93 FВx ÷ К∣x∣ FLg
 90 |  FLg Fx=0 94 КП→x5 FL2 66 П→x5 БП 42 FL3
 A0 |  37 П→x6 3 F1/x С/П

Объяснение работы программы

Адреса 00..06. Для случайного числа используется регистр R0. При 9 лисах он будет в конце перезаписан координатами последней лисы. Начальное значение, заданное игроком сохраняется в нём, остальные регистры инициализируются начальными значениями.

Адреса 07..09. Приращение счётчика уже рассаженных лис. Используется значение + 1, потому что циклы FLx идут до 1. Возврат из цикла расстановки идёт на адрес 07. Но если новые координаты повторились, то на адрес 08, без приращения. Технически, такой счётчик не нужен, т. к. разница между общим количеством лис (R3) и уже рассаженных (R2) и есть нужное значение. Но, для работы цикла нужно сделать ещё + 1 (циклы до 1, а не до нуля). Поэтому R3 + 1 − R2. Или в нотации ПМК П→x3 1 + П→x2 -. Получается пять команд. При использовании отдельного счётчика всего три: x→П5 KП→x5 П→x5.

Адреса 10..24. Генерация нового значения координат для лисы. Используется следующая последовательность псевдослучайных чисел: Ni+1 = {7 * Ni + π} – адреса 12..18. Для шифрации координаты R сохраняется как R4. Нам нужно число R в виде K.M, где K и M – цифры 0…9. Для этого нужно случайное число умножить на 100 (адрес 19), взять целую часть (адрес 20) и поделить на 10, т. е. KM / 10. Но при возведении в квадрат это будет (KM)2 / 100. Поэтому константа 100 нам понадобится ещё раз. Для это мы сначала вычисляем её, и при извлечении остальных параметров генератора число 100 дойдёт до регистра T в стеке, и при обратном движении продублируется. И на адресах 22, 23 мы делим квадрат на эту 100. В конце ещё раз возводим в квадрат.

Адреса 25..33. Цикл сравнения новой координаты с ранее расставленными. Сначала идёт инициализация индекса регистра. Причём не в 6 (чтобы начать с регистра 7), а меньше. Это нужно для одного холостого цикла. В этом случае из R6 извлечётся 0, что точно не равно 5 и один цикл, который нам нужен, пройдёт без проблем. Тем самым мы обходимся без БП на конец цикла. Новое сгенерированное значение с помощью недокументированной команды 3E, которая копирует Y в X, дублируем для сохранения. В цикле идёт извлечение ранее введенных координат с приращением индекса. Результат сравнивается с тем, что мы получили от генератора. Если совпало, то переход на другую генерацию (адрес 08) без сохранения.

Адреса 33..36. Сохранение новой координаты и возврат на начало цикла расстановки. Если цикл проверки с предыдущими закончился, то индекс как раз принял нужное значение для новой лисы, а в X (и в Y) новая координата.

Адреса 37..41. Отображение числа оставшихся лис. На самом деле этот фрагмент используется во второй части программы для исключения найденной лисы (запись отрицательного числа). Но наша инициализация плавно переходит в этот фрагмент, а отрицательное число записывается в рабочий регистр R1, потому что на выходе в X и Y одно и тоже, а значит разность равна нулю.

Адреса 42..44. Остановка с отображением результата и выводом в регистр Y хода игрока.

Адреса 45..55. Корректировка хода игрока и сохранение в регистре R1. Нам для этого нужна константа 10. Но сразу после С/П нельзя указать 1 0, потому что ввод цифр продолжится с тем, что вводил игрок. Поэтому мы используем тот факт, что по окончании цикла в R2 осталась единица. Для дальнейшего использования сохраняем константу 10 в R5 (временно).

Адреса 53..58. Проверка, что ход игрока в нужном диапазоне: 0 ⩽ R < 10. Если нет, то идёт переход на конец цикла подсчёта, где R5 ещё увеличится и будет отображён как признак переполнения.

Адреса 59..65. Приращение счётчика ходов и инициализация переменных перед циклом проверки: число лис в R2, индекс регистра в R4, сумматор в R5.

Адреса 66..97. Основной цикл второй части программы по подсчёту пеленгаций. Сначала по адресу 66 извлекается неизменённый индекс для сохранения в стеке. Он понадобится, если координаты охотника совпадут с координатами лисы и потребуется на её место записать отрицательное число. Затем для сравнения извлекается ход игрока и координата лисы. Но сначала проверяется, что лиса есть (адрес 69, 70). Если там уже нет, то возврат на начало цикла. Если есть, то дешифрация (адреса 71, 72) и разность координат. При равенстве (ноль) уход на адрес 99. Иначе эта разность сдвигается в стек, а в X вычисляется разность дробных частей, причём в обратном порядке.
Тут удобнее ввести формулы. Обозначим координаты лисы как F.f (число), а координаты охотника (в R1), как H.h. Если перейти к дробям, то (F + f/10) и (H + h/10) соответственно. Тогда разность будет (H + h/10) − (F + f/10). А разность дробных частей, как (f/10 − h/10). Если дробные части совпали (адрес 81), то переход на конец цикла с приращением сумматора (адрес 93). Если нет, то мы просто складываем (H + h/10) − (F + f/10) + (f/10 − h/10). В результате получим (H − F). И если целые части совпали (адрес 84), то снова в конец с приращением. Иначе делим (H − F) / (f/10 − h/10). Делитель точно не ноль, это уже проверяли. Если клетки расположены на одной диагонали, то разница между вертикальными и горизонтальными координатами должна совпадать. С учётом того, что целая часть в 10 раз больше дробной, разница совпадает, если результат деления равен ровно 10 (или −10). Именно эту проверку мы делаем по адресам 88..91. Применение логарифма безопасно, потому что обе части дроби точно не нулевые. Если диагонального совпадения нет, то проскакиваем приращение сумматора и сразу на конец цикла (адрес 94). После проверки всех лис мы просто выводим сумматор (R5) в регистр X и переходим на ожидание хода игрока.

Адреса 99..A4. Лиса обнаружена. Проверка, что ещё остались лисы (R3), если нет, то завершение игры. Тут нужно пояснить фрагмент с адреса 37. Сначала в стек было загнано значение регистра R4 (адрес 66). Потом в результате сравнения получили 0 (адрес 74), а значение R4 до увеличения осталось в регистре Y. Когда мы перейдём на адрес 37, то счётчик в R3 уменьшится, разность с нулём оставит значение индекса для R4. Что и будет использовано для сохранения отрицательного числа на месте обнаруженной лисы, с одновременным отображением счётчика оставшихся лис.