История проекта
1. Искра (2013)
Летом 2013 года, ещё учась в школе, я наткнулся на статью об Альтеративных Крестиках-Ноликах. И я влюбился в эту игру мгновенно:
- Она предлагала небольшой набор простых правил, дающих огромную сложность, подобно «Игре жизни» Джона Конвея.
- Она требовала глубокого стратегического мышления, но без жёсткого багажа шахматных фигур.
Я начал периодически играть в неё с друзьями и семьёй, тратя горы бумаги на рисование вложенных сеток.
2. Видение: от бумаги к коду (2016)
Позже, в университете, я хотел играть с друзьями из разных городов. Не найдя качественных онлайн-версий, решил сделать свою.
С детства я одержим обобщением: изучением или разработкой универсальных теорий, описывающих все существенные детали и охватывающих максимум практики, будь то физика или игровая логика. Поэтому мне доставило невероятное удовлетворение обобщить игровой движок для работы с произвольным набором параметров:
- S (Длина стороны блока): выход за рамки стандарта
3×3, включая уменьшенные, динамичные2×2(где ничья невозможна) и увеличенные4×4или5×5. - N (Глубина вложенности): поддержка классических крестиков-ноликов, Super/Meta/Ultimate Tic-Tac-Toe и даже более глубоких фрактальных уровней.
- Несколько команд: поддержка более чем двух игроков (
XиO); хотя поначалу это не казалось достаточно увлекательным.
Например, классическая игра XO представляется как S3 N1 в моём формате, а Ultimate Tic-Tac-Toe – как S3 N2, и есть множество других конфигураций!
Я погрузился в игровой ИИ, реализовав алгоритм Minimax с альфа-бета отсечением и специализированной эвристической оценкой, способной адаптироваться к различным конфигурациям полей.
3. Начальная разработка
Изначально я исследовал C++/Qt для кросс-платформенности. Однако, увидев результат на смартфоне, я остался недоволен – UI выглядел неуклюже, он не использовал нативные элементы. Я отказался выпускать «некрасивый» продукт. Я мог бы интегрировать нативный код для доступа к элементам интерфейса ОС, но это казалось сшиванием монстра Франкенштейна.
Я решил пойти полностью нативным путём: отдельные реализации на Objective-C для iOS и Java для Android. Это позволило создать отполированный, платформенно-специфичный опыт, несмотря на параллельные кодовые базы. Для бэкенда я построил сервер на Python с использованием асинхронного фреймворка Tornado (до того как async/await стал стандартом). Я даже реализовал двойной API с фолбэком: игра пытается установить WebSocket-соединение для скорости, но переключается на HTTP, если Wi-Fi-сеть блокирует протокол WS; разумеется, всё через SSL/TLS. Это был мой первый опыт с бэкенд-разработкой и взаимодействием с Linux. Я также разработал клиентскую браузерную версию, хотя она не поддерживала мультиплеер.
Я также посвятил время брендингу – созданию названия и иконки. Хотел нечто простое, но уникальное, запоминающееся и явно связанное с наследием «X и O». В итоге я пришёл к дизайну, который, как мне кажется, по-настоящему резонирует с идентичностью игры.
4. Реальность рынка
Чтобы сделать игру глобальной, я работал с друзьями над переводом на восемь языков. Но быть студентом-разработчиком дорого. Между ежегодной платой Apple Developer в $100 и стоимостью серверов я добавил рекламу, чтобы игра окупала себя. Я даже вложил дополнительные $100 в рекламу для привлечения аудитории – главная причина, по которой игра набрала тысячи установок на Android и сотни на iOS.
Однако финансовая реальность оказалась холодным душем: я заработал всего $12 за год – даже недостаточно для достижения порога вывода средств! ROI (возврат инвестиций) составил фактически -90%… Парадоксально (хотя, возможно, неудивительно), при том что на Android было больше пользователей, он принёс лишь ~$1.50, а остальное – с платформы Apple. По крайней мере, через внутриигровой чат я познакомился со множеством интересных людей со всего мира!
Из-за отсутствия маркетинговых знаний (я ещё не слышал о Reddit или Discord) и постоянного обслуживания, требуемого меняющимися политиками Google и Apple, оригинальные мобильные версии в итоге исчезли из App Store и Play Market.
5. Веб-перерождение (2022)
Весной 2022 года я решил создать единую полнофункциональную браузерную версию игры с использованием современных технологий:
- новый сервер с async-native FastAPI, сохраняющий практически тот же API.
- новую веб-версию TacticToy на React.js, которую гораздо проще разрабатывать благодаря модульности (версия на vanilla JS была одним файлом с 2000 строк!), но с поддержкой мультиплеера через WS/HTTP, как в старых мобильных версиях. Она всё ещё доступна: v2.TacticToy.com
Это было сделано в основном по личной мотивации: чтобы сохранить моё «детище» живым и лучше изучить React.js, поэтому я потратил почти ноль усилий на маркетинг, лишь опубликовал на паре сайтов по геймдеву, но, похоже, без результата.
6. Универсальный движок TT на Rust
Имея опыт с различными языками программирования и технологиями (даже больше, чем упомянуто здесь) и страсть к разработке разнообразных проектов, я устал от необходимости поддерживать разные базы знаний для работы над ними. Поэтому годами я искал «единственное кольцо» – технологию, способную обеспечить высокую производительность и истинную кросс-платформенность. Я присматривался к Dart/Flutter, React Native, GoLang и т.д., иногда экспериментировал, но они меня не устраивали. Напротив, чем больше я изучал Rust, тем больше он мне нравился. Решил начать активно изучать его и использовать для проектов в июле 2023.
Параллельно с работой, другими проектами и жизненными событиями я думал о TacticToy, периодически играя с друзьями, и представлял, как ещё можно улучшить, расширить, усовершенствовать игру.
- Помимо анимаций и звуковых эффектов, я планировал разделить длину стороны блока и длину выигрышной линии (концепция, известная как m,n,k-игра или k-в-ряд). Теперь это параметр L.
- Это также помогает гораздо лучше поддерживать несколько команд!
- Параметр S больше не ограничен 5, он может быть до 16. Вместо этого есть ограничения на общую длину стороны поля (
S^N < 100) и объём (S ^ (N*D) < 1000). - Я подумал, что мой алгоритм легко обобщить для 3D и даже 4-мерных полей, и это слишком интересно, чтобы не сделать! Это, конечно, параметр D.
- Хотя алгоритм Minimax надёжен и довольно эффективен с альфа-бета отсечением, он сталкивается с взрывным ростом пространства состояний на больших полях, что недопустимо с учётом вышеуказанных изменений. Поэтому я думал о переходе к алгоритму Monte Carlo Tree Search (MCTS) для лучшего контроля времени «размышления» ботов.
- Почему он лучше подходит? Minimax – детерминированный алгоритм, он пытается увидеть «конец света» (хотя бы частично), а MCTS – вероятностный: он исследует наиболее перспективные сценарии без полного перебора, что делает его единственным жизнеспособным выбором для управления взрывным ростом пространства состояний.
- Я знал, что ультимативные версии К0 различаются в обработке уже выигранных блоков: одни позволяют отправлять и ходить в них, другие запрещают. Я хотел дать игрокам выбор стиля.
- Условие победы: поддержка «Контроля территории» (победа по большинству блоков) помимо классической линии из трёх блоков.
- Контроль времени: важная механика для мультиплеера, делающая игру настоящим соревнованием! Игра должна позволять выбирать предпочтительный лимит времени, будь то 10 секунд или 10 часов, а также желаемое наказание за просрочку: случайный ход или мгновенная дисквалификация.
- Мультиплеер оригинальной версии TT позволял иметь нескольких игроков в одной команде, но они должны были ходить в порядке очереди, а нескольким людям зачастую сложно скоординироваться, поэтому я хотел позволить любому члену команды действовать во время хода команды.
- Улучшенная интернационализация (i18n): я решил добавить более 20 самых распространённых языков и даже несколько искусственных! Это было непросто, но современные ИИ хорошо помогают.
7. Нет читерству: правило Кю
Я также размышлял о злоупотреблении игровыми механиками – диктовке ходов соперника с самого начала, удержании его в одном блоке, полном контроле игрового процесса и принуждении к проигрышу:
- Я разработал правило, запрещающее отправлять соперника в один и тот же блок последовательно, назвав его правило Кю 窮, означающее «истощение» или «удушение». Это тематическая отсылка к правилу Ко в Го – игре, которую я тоже люблю – означающему «вечность» или «повторение» и предотвращающему рекурсивно бесполезные ходы.
- Проблему также можно решить случайными начальными ходами (следуя метафорам Го, я назвал бы их разведчиками) или клетками, в которые нельзя ходить (своего рода препятствиями), что также делает дебют свежим и непредсказуемым – я решил ввести и то, и другое как часть параметров поля.
8. Современная разработка
В августе 2024 я начал окончательную переработку. Используя Rust для общего ядра между сервером и клиентом, я создаю определяющую версию TacticToy. Моя цель проста: создать самую полную, стратегически глубокую и технически отточенную реализацию N-мерных крестиков-ноликов.
Начав новую версию TT на Rust, я сначала переделал старый игровой движок и алгоритм Minimax. Нуждаясь в тестировании и отладке, но не имея единственного очевидно лучшего кросс-платформенного фреймворка рендеринга для Rust, я решил сосредоточиться на расширении ядра, тестируя и отлаживая в терминале. Раньше я никогда не играл в консольные игры и не делал их, но создал одну для TacticToy, и хотя вряд ли опубликую её, мне понравилась экосистема Rust с мощными и простыми крейтами для работы с терминалом. И мне очень нравится мой алгоритм визуализации произвольных полей, включая 3D и даже 4D в виде 2D-слоёв.
После введения параметров L и D, реализации алгоритма MCTS, пришло время заняться отрисовкой и UI. Нуждаясь в поддержке 2D и 3D рендеринга, я попробовал использовать Bevy, осторожно изучая парадигму Entity-Component System (ECS) – хотя она кажется отличной для игр, полных различных объектов и механик, на кажется слишком громоздкой для игры с единственной структурой состояния фиксированного размера, хотя может быть полезна для разделения UI и физического состояния для анимаций.
Помимо технических проблем, проектирование UI для настоящей 3D стратегической игры – это действительно сложно. Я решил сосредоточиться на других функциях, оставаясь в 2D, переделывая и обновляя старый веб-UI через WASM и бэкенд-сервер, всё на Rust. Я исследовал Leptos, Yew и Dioxus – и выбрал последний за его кросс-платформенную поддержку помимо веба и множество других приятных возможностей.
На данный момент, эта версия ещё в статии тестирования, но уже весьма играбельна: 2D.TacticToy.com.