Игра 2048 на Обероне для ZX Spectrum

Не так давно, читая zxbyte.ru, наткнулся на игру 2048 для самодельного компьютера ЮТ-88, написанную Kakos_Nonos’ом. Практически сразу в моей голове родилась идея адаптировать ее для Спектрума. Не став откладывать в долгий ящик, я решил проработать концепт.

Примерно так я видел игру перед началом разработки

Для ее разработки я сначала использовал два модуля: основной и вспомогательный (чуть позже вспомогательных стало два). В основном происходили собственно вычисления, а вспомогательный работал с вводом/выводом, Таким образом, в основном модуле практически нет каких-либо специфических для Спектрума процедур, и его практически без изменений можно использовать для портирования на другие платформы.

Итак, прежде всего я начал разметку поля. Возможно, это не совсем правильно, но я всегда начинаю с такого, дабы легче было отслеживать изменения в игре. Попробовав несколько вариантов, я остановился на примерно таком:

Потом я добавил цветов, еще чуть позже — вывод слова «Счет», все это назвал процедурой OTRIS и поместил в вспомогательный модуль Graph.

К сожалению, скриншотов промежуточной стадии без блоков с числами не сохранилось

В том же модуле первоначально находились и функции, отвечающие за вывод чисел. Выводились они так:

  1. В зависимости от номинала выбирались цвета;
  2. В нужном квадрате печатались пробелы (при совершении хода — во всех квадратах);
  3. «Поверх» пробелов печаталось число.

Программируем игру

Собственно после этого перешел к выводу и хранению чисел. Для этого дела я решил организовать массив размером 4х4, где каждый элемент соответствует «ячейке». То есть, если T[i,j]=N, то в i-й по горизонтали и j-й по вертикали ячейке содержится «фишка» номиналом N.

По правилам игры, в каждом раунде появляется плитка номинала «2» с вероятностью 90% или «4» (10%) в произвольном пустом квадрате. Для определения номинала я использовал соответствующую процедуру из библиотеки BASIC, по функционалу примерно соответствующей стандартному BASIC ZX Spectrum’а. А поскольку поле относительно небольшое, то для выбора случайной плитки я написал очень примитивный и грубый, но вполне рабочий алгоритм. Его суть в следующем: та же самая процедура генерирует 2 случайных числа от 0 до 3 (нумерация элементов массива начинается с нуля). Эти числа соответствуют X и Y. Затем проверяется, нет ли в ячейке (X,Y) фишек, т.е. равен ли T[x,y] нулю. Если да, то этой ячейке присваивается значение 2 или 4, а если нет — процесс выбора ячейки начинается сначала. Несмотря на примитивность, никаких задержек с ростом занятых клеток я не заметил.

Следующим этапом было программирование алгоритмов самой игры. Перед этим я написал еще одну процедуру, реализующую опрос клавиатуры, вернее, регистрирующей нажатие клавиш QAOP, которые используются для управления. Полностью сами алгоритмы «сдвига» чисел и то, как я к ним пришел, расписать вряд ли получится. Стоит сказать, что я не использовал чужой код, так как основной идеей для меня было проверить, смогу ли я сам написать что-то такое.

Итак, суть алгоритмов заключалась в следующем:

  1. Несколько раз поле «сканируется» (другой термин я подобрать не могу) в сторону, противоположную сбросу. При этом все фишки, которые могут перейти на одну клетку в сторону сброса, выполняют такой переход. Если посчитать, то для поля 4х4 количество таких «проходов» не превышает трех.
  2. Если по направлению сброса две фишки имеют одинаковый номинал, то они суммируются, причем в ячейку, ближайшую к стороне сброса, записывается нуль, а в другую — сумма.
  3. Пункт 1 выполняется еще раз.

Итак, фактически игра была готова. Но был один недостаток: не определялся момент проигрыша, т.е. момент, когда сделать ход невозможно. Условиями такого момента являются: все клетки поля заняты фишками (т.е. T[i,j]<>0 для любых i,j) И среди них нет двух одинаковых соседних. Этот момент также был учтен.

Еще одним недостатком было отсутствие анимации. И если с плавностью движений я ничего сделать не смог, то простую анимацию появления новой двойки (или четверки) я прописал. В центра ячейки печатается небольшой квадрат белого цвета, и выглядит это как вполне нормальная вспышка. Личто мне существенно помогает отслеживать изменения на поле.

Ходов уже нет, а игра не заканчивается. Непорядок.

Где-то в процессе, кстати, была добавлена процедура счета очков, которая активировалась при каждом «сбросе» фишек. Оставалось добавить только сообщения о получении плитки 2048 с предложением продолжить или начать сначала, и сообщение о конце игры.

С очепяткой, но из песни слов не выкинешь.
Продолжение игры выше. Продвинулся не сильно.

В общем, игра уже фактически была готова, пара ошибок была выявлена и устранена. Но дизайн откровенно хромал. Для исправления ситуации я нарисовал цифры в ZX-Paintbrush, экспортировал их в бинарный файл, а затем добавил несколько процедур для их печати. Кстати, эти процедуры я поместил уже в другой модуль, чтоб не загромождать старый.

Вот они, новые циферки.
А в игре они вот так.

Затем мне захотелось использовать русскоязычный шрифт. Ну а с латинскими буквами он и в качестве белорусского может быть, поэтому я решил поиграться со шрифтами как следует. В целом вышло вполне прилично:

Как видно, добавил еще возможность перезапустить игру в любой момент нажатием CS+R.

И вот все было готово. Оставалось только написать загрузчик и соединить все это в один .tap-файл. Поскольку объем получился сравнительно небольшой (10 Кб), то загрузочный экран я рисовать не стал, да и не придумал ничего для такой игры. Поэтому в качестве заставки использовал просто черный экран с названием игры и надписями про авторов оригинала и «порта».

И русско-, и белорусскоязычную версию можно скачать здесь.

Савелий Иванков,
Гродно, Беларусь.