Морской бой

Старый добрый Морской бой, с произвольным количеством однопалубных кораблей.

Задача

Как обычно, подбить все корабли первым. Вы играете против ПМК.

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

Квадратное поле 10 x 10 (0…9 x 0…9), стрельба по очереди, первым начинает ПМК. Что считать координатой X, а что Y решайте сами, главное, чтобы быть в этом последовательными.

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

Начало

Перед первым запуском пару констант в регистры:

Регистр Значение
Ra 33333331, признак победы ПМК.
Rb -8CEC6-L-. (ВСЁСБИЛИ). Признак поражения ПМК. Вводиться как 3, 3, 1, 3, 9, 5, 4, 5, КИНВ, /-/, ВП, 7, x→Пb.
Случайное число. Можно оставить пустым.

Начало игры: вводим количество кораблей у каждого (от 1 до 99) В/О С/П.

Игра

ПМК располагает свои корабли и отвечает координатами удара (двузначное число, если ведущий ноль, то однозначное). Если ПМК попал, то вводите любое отрицательное число и С/П, тогда ПМК сделает ещё ход. Если он промазал, то вводите свои координаты удара и С/П.

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

Окончание

Если вы ввели отрицательное число (сбили), и это уже последний ваш корабль, то ПМК выдаст признак его победы 33333331. Если, наоборот, вы сбили все его корабли, то выдаст "-ВCЁСБИЛИ"( -8CEC6-L- ).

Для новой игры: новое количество кораблей В/О, С/П и далее как описано.

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


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

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

 # |  00 01 02 03 04 05 06 07 08 09
 00 |  БП 50 Fx<0 89 FL0 68 П→xa В/О 4 ПП
 10 |  76 П→xd + x→Пe <-> 7 ПП 76 F10x <->
 20 |  8 ÷ /-/ ВП F1/x К[x] КП→xe К К{x}
 30 |  В/О П→xc 7 × Fπ + К{x} x→Пc ПП 71
 40 |  КППb Кx=0a F КП→xe К Кx→Пe + КЗН x→Пd В/О
 50 |  К[x] x→П0 FLg 9 x→Пd Кx→Пd 1 Fx=0 54
 60 |  П→x0 x→П1 F10x КППa FL1 63 П→x0 x→П1 5 x→Пd
 70 |  КППa П→xc ВП 2 К[x] В/О 3E FВx <-> FВx
 80 |  ÷ К[x] В↑ F × 1 В/О F10x
 90 |  FВx К[x] КППb Fx≠0 68 ПП 42 П→x1 FL1 87
 A0 |  П→xb В/О

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

R0 Количество кораблей у игрока.
R1 Количество кораблей у ПМК.
R2…R5 Местоположение кораблей ПМК в битовом представлении.
R6…R9 Местоположение выстрелов ПМК в битовом представлении.
Ra 33333331, признак победы ПМК. А также адрес процедуры поиска свободного бита через генератор случайных чисел.
Rb -8CEC6-L-. (ВСЁСБИЛИ). Признак поражения ПМК. Также адрес процедуры (BA = 08) преобразование целого числа (0…9) из регистра X в номер регистра (в Re) для определения бита для тестирования.
Rc Случайное число.
Rd Число 1 или 5 – базовый адрес битовой карты, используемый процедурой генерации или проверки битов.
Re Текущий регистр для проверки/установки бита.

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

Основная идея программы: запомнить два поля (в битовом представлении) 10 x 10. Затем на одном, вначале пустом, расставить корабли (поставить биты). А на другом (тоже пустом) расставлять свои ходы. При этом главное проверить, а не стоит ли там ещё корабль (установлен бит).

Теперь технические детали. Для операций с битами используются шестнадцатеричные операции. Т. к. одна шестнадцатеричная цифра может представлять 4 бита, то 7 цифр (максимум в шестнадцатеричных операциях) 7 × 4 = 28. А в 4 регистрах 28 × 4 = 112, т. е. более, чем сто. Таким образом для хранения одного поля в битовом представлении достаточно 4-х регистров памяти. Теперь по используемым процедурам:

  1. Процедура целочисленного деление с остатком. Она начинается с адреса 76 (кодом 3E, см. Недокументированные возможности). На входе некое число A в регистре X, и B в регистре Y. На выходе A mod B + 1 в X, и [A/B] в Y. Плюс один для удобства следующих операций. Используется только стек. Единственная деталь: вместо и 1, + используется 1, , . Это связано с оптимизацией (пояснение ниже).
  2. Преобразование целого числа (0…9) из регистра X в номер регистра (в Re) для определения бита для тестирования, и сам результат проверки битовой операции. Используется значения регистра Rd, как базовый адрес карты. Т. е. в регистрах Rd+1,…Rd+4 храниться проверяемое поле. Подпрограмма начинается с адреса 08, или адрес BA, как часть фразы -8CEC6-L-. (Всё сбили). Сначала число целочисленно делиться на 4 (через процедуру 1). Потом остаток от деления (1…4, т. к. +1) используется как номер регистра (+ смещение из регистра Rd). Затем целая часть (0…24) делиться на 7. Остаток используется для определения разряда (10 в степени 1…7), а целая часть (0…3) преобразуется в номер бита. Но т. к. операция Fxy очень долгая (по времени исполнения), то выполняется другое преобразование: 0 → 1, 1 → 8, 2 → 4, 3 → 2. Делается это по формуле [1 / (N / 8)], т. е. 1 / (1 / 8) = 8, 1 / (2 / 8) = 4, 1 / (3 / 8) = 2.6666666, [2.6666666] = 2, а ноль преобразуется в единицу с помощью особенностей команды ВП. Но так как она хитрая, то нужно сначала выполнить X2-влияющую команду (см. недокументированные возможности). В данном случае /-/. И кстати, −0 переходит в −1 даже в ручном режиме, хотя при этом возле нуля минус не отображается. Поэтому в конце с большим числом делается не плюс, а минус. Поясню, что для функции Fxy пришлось бы ещё прибавлять малую величину и делать К[x], т. к. даже 22 вычисляется неточно. Здесь получается на одну команду длиннее, но намного быстрее (и запутаннее от подглядывания). Потом через Re поднимается существующая битовая маска, делается тест и в конце 0 или не ноль. Важно, что в регистре Y остаётся число с номером бита. Ещё важная особенность – расстановка битов по регистрам делается хитро, что даже видя число из регистра, трудно понять, что и где.
  3. Процедура поиска свободного бита через генератор случайных чисел. Начинается с адреса 31 (адрес того самого 33333331 в Ra). Формула для случайного числа N = {7 × N + π}. Затем через ПП 71 берётся 2 первые цифры (умножение на 100). Вызывается процедура 2. Если занято, то повтор, если нет, используется сохранённые ею значения Re и RY для установки бита, зачистки стека от битов (операция + и КЗН, та же защита от подглядывания), заодно сохраняя в Rd смещение 1, т. к. регистры R2…R5 используются для хранения кораблей ПМК (R6…R9 – выстрелов ПМК), и обычно первым делом проверяются в начале хода.
  4. Процедура начальной инициализации. По команде В/О, С/П, как легко видеть, перейдёт на адрес 50. Тут 50 выбран потому, что это код команды С/П. Т. е. фактически это и есть единственный С/П, на котором будет останавливаться программа. Итак (с адреса 50), первым делом отрубание дробной части, сохранение количества кораблей в R0, затем проверка на то, что больше нуля (FLg для ≤0 выдаст ошибку). Затем девятка с уменьшением (9…2) последовательно записывается в регистры R9…R2. Сделано так вот почему: для очистки обоих битовых карт (первый разряд в битовых операциях не участвует), заодно в конце операции в Rd будет 1. Затем копирование в R1 количества кораблей, заодно проверяя верхнюю границу (для x≥100 F10x выдаст ошибку). Затем очень короткий (по длине, но не по времени) цикл по вызову процедуры 3 (расстановка всех кораблей), восстановление в R1 числа кораблей. Далее R0 будет показывать оставшееся количество кораблей у игрока, R1 – у ПМК. Потом сразу идёт процедура…
  5. Ход ПМК (адрес 68). Заносит смещения (5) для выбора поля выстрелов. Вызывает процедуру 3, находя пустое нестреляное поле, и последний удачный результат извлекает из регистра случайного числа, возвращаясь на адрес 01 (команда С/П). Обратите внимание, что хвост этой операции используется в процедуре 3.
  6. Осталась основная часть с адреса 02. Если ПМК подбил, то уменьшается количество кораблей игрока, и если всё, то извлекается Ra и возврат на остановку. Если нет, то далее на адрес хода ПМК. Если это ход игрока, то с адреса 89 сначала делается проверка на ≤99 (через F10x), затем берётся целая часть (такой порядок, чтобы в случае ЕГГ0Г можно было продолжить, как раз остановка будет на команде К[x]). Вызов процедуры 2, и если игрок не попал, то снова уход на выстрел ПМК (адрес 68), а если попал, то вызов хвоста процедуры установки бита (вот зачем была операция XOR, а не OR, чтобы стереть бит), используя те же данные из Re и RY. Извлечение значения оставшихся кораблей, проверка на всё ли подбили. Тут главное, это переход, на 87, если не всё. После вызова процедуры 2 в RX останется (уже в RY, после П→x1) единица, а странно сделанное окончание процедуры 1 вычтет её и вернёт на начало. Т. е. в итоге отобразиться −число оставшихся кораблей ПМК.