[Перевод] Игра 2048 в Wolfram Mathematica
Перевод поста 2048, Wolfram Style, написанного для официального блога компании Wolfram Research Дэном Фортунато, младшим программистом Wolfram|Alpha Parser Content.Перевод сделал Сергей Шевчук (opckSheff на Хабрахабре)Архив с файлом Wolfram Mathematica, в котором содержится код, вы можете скачать здесь.
Если в течение последних нескольких недель вы выходили в интернет, то вы вряд ли могли не встретиться с игрой под названием 2048, разработанной Габриэлем Чирулли. Будучи основанной на похожих играх, 1024! от Veewo Studio и THREES от Ашера Воллмера, эта игра имеет простую механику, которая затянет вас надолго — перемещайте по полю фишки, на которых написаны степени числа 2 и соединяйте их попарно, чтобы получить ещё более высокие степени. Главная цель игры — получить фишку 2048. Достаточно сложно объяснить, насколько в действительности интересна и увлекательна эта игра, поэтому я рекомендую вам самим сыграть в нее.
Чтобы отдать должное этой простой игре (и в честь всех математических игр!), я решил продемонстрировать всю мощь Языка Wolfram, используя его, чтобы разработать нашу собственную версию 2048. Начнём! Основной структурой для игрового поля послужит матрица 4×4, заполненная пустыми элементами (строками нулевой длины):
In[1]:=
Out[2]=
После начала новой игры я случайным образом расположу на поле две фишки со значениями 2 или 4. Больший приоритет я отдам фишке 2, таким образом, она будет появляться на поле чаще.
In[3]:=
In[4]:=
Out[4]=
Настало время сделать наше игровое поле немного красивее. Я могу полностью повторить стиль оригинальной игры, импортировав её CSS-стиль (CSS — Cascading Style Sheets, каскадные таблицы стилей). Как вы можете видеть, в CSS я смог найти цвета для фона и текста всех фишек.
In[5]:=
Out[8]=
Теперь у меня есть список, содержащий каждый номер фишки и соответствующие ему цвета! Далее я могу обработать его, создав функцию для поиска цвета и перевода её из HEX (шестнадцатиричной записи цвета, используемой в CSS) в RGB (цветовую модель, основанную на сочетании красного, зелёного и синего цветов). Также я определяю цвета для остального игрового поля и, на всякий случай, для некоторых цветов по-умолчанию.
In[9]:=
In[10]:=
In[13]:=
Теперь я использую цветовую информацию для написания функции, отрисовывающей фишки. Необходимо проявить осмотрительность и сделать размер шрифта меньше, когда количество цифр в числе растёт.
In[15]:=
In[16]:=
In[17]:=
Я определил функцию drawTile таким образом, чтобы впоследствии её можно было легко изменить…
In[18]:=
Out[18]=
Чтобы применить этот стиль к оригинальному игровому полю, я просто рассматриваю каждый элемент игрового поля в матрице и располагаю соответствующую ему фишку на нужном месте.
In[19]:=
In[20]:=
Out[20]=
Выглядит красиво! Теперь нам нужно научиться управлять игрой.
Когда происходит нажатие одной из клавиш, я хочу, чтобы все фишки сдвигались по доске в указанном направлении настолько далеко, насколько это возможно, при этом одинаковые фишки должны объединяться. Я могу использовать опцию NotebookEventActions, чтобы регистрировать нажатия клавиш и соответствующе реагировать. Я сделаю так, чтобы управление осуществлялось клавишами wasd, вы можете выбрать любые другие:
In[21]:=
In[22]:=
Теперь давайте подумаем о том, что же происходит, когда фишки сдвигаются, скажем, влево. В первую очередь мне необходимо реализовать объединение одинаковых фишек. Каждая строка должна рассматриваться отдельно, поскольку при горизонтальном смещении вертикальные совпадения не объединяются. Я хочу найти последовательности из двух одинаковых чисел, возможно, с пустыми клетками между ними, и заменить их суммой. Здесь на помощь приходит мощь Языка Wolfram, и я могу использовать сопоставление с заданным шаблоным выражением, чтобы легко это осуществить.
In[23]:=
После сложения одинаковых фишек, всё, что я должен сделать — это добавить несколько пустых элементов справа, чтобы заполнить строку. Процедура повторяется для всех строк игрового поля.
In[24]:=
Сдвиг вправо осуществляется так же, с одним незначительным изменением — я хочу, чтобы сочетания одинаковых фишек справа объединялись до сочетаний, находящихся левее. Рассмотрим в качестве примера строку {$empty, 2, 2, 2}. Используя функцию combineLeft и нажав стрелку влево, я получу строку {$empty, $empty, 4, 2}, но на самом деле я хочу, чтобы сначала объединилась правая пара двоек. Переворот строки, сдвиг ячеек влево и обратный переворот легко решают проблему.
In[25]:=
In[26]:=
Теперь, когда у нас есть две этих функции, реализовать сдвиг вверх и вниз очень просто! Сдвиг вверх — это то же самое, что сдвиг влево на транспонированном игровом поле, за которым следует обратное транспонирование. Таким же отношением связаны и операции сдвига вправо и вниз.
In[27]:=
Если клавиша нажата, новые фишки на поле должны добавляться только в том случае, если состояние поля изменилось, то есть если некоторые фишки переместились или объединились. Для этого необходимо отслеживать предыдущее состояние игрового поля.
Теперь научимся вести подсчёт очков в течение игры. Каждый раз, когда объединяются две фишки, я собираю их сумму функцией Sow, а потом использую на всех суммах функцию Reap, когда все возможные объединения совершены. Также я вывожу значение наибольшей полученной на данный момент фишки.
In[29]:=
Наконец, давайте добавим проверку на выигрыш и проигрыш. Игра считается выигранной, если наибольшая фишка на поле имеет значение 2048 или выше. Я проиграл, если доска заполнена и у меня больше нет возможных ходов. В данном случае я снова могу использовать язык шаблонных выражений, чтобы определить, остались ли на доске возможные сочетания фишек.
In[30]:=
In[31]:=
Используя функцию Dynamic, я могу поддерживать отображаемое игровое поле в полном соответствии с внесёнными изменениями. Кроме того, я могу окружить всё это функцией DynamicModule и использовать Initialization, чтобы настроить обработку нажатий клавиш и игровое поле. Поместив DynamicModule внутрь CreateDialog мы можем открывать игру в отдельном окне.
Наконец, игра полностью готова.
In[32]:=
Сейчас всё выглядит вполне симпатично, но у нас в компании Wolfram Research мы любим что-нибудь более… заостренное. Такое, как Spikey — логотипы различных версий системы Mathematica и Wolfram|Alpha. Давайте немного изменим цвета фишек и их форму.
In[33]:=
In[34]:=
Out[34]=
In[35]:=
In[36]:=
In[37]:=
In[39]:=
Теперь мы можем переключаться между двумя стилями при помощи кнопок.
In[40]:=
Out[40]=
In[41]:=
Удачной вам игры!