как нарисовать поле для крестиков ноликов
Познаем основы Java и создаем крестики-нолики
Java — это не только язык, это целая экосистема, включающая в себя средства разработки, платформу для запуска готовых приложений, огромный свод документации и активное сообщество. Одним из преимуществ Java на начальном этапе была кросс-платформенность (принцип — «написано один раз — запускается везде»).
Дело в том, что программа на Java исполняется не на прямую процессором компьютера, а виртуальной машиной Java (JVM). Это позволяет абстрагироваться от многих нюансов конкретных платформ. Программу, написанную на Java, можно без изменений кода запускать на Windows, Linux, MacOS и других операционных системах (если, конечно, программа не использует специфичные для ОС функции).
Кто застал начало 2000х, наверное помнит огромное количество мобильных телефонов (тогда еще они не были смартфонами), на каждом телефоне была по сути своя маленькая ОС, но при этом почти на каждом можно было запустить Java игру или приложение.
На сегодняшний день Java по-прежнему входит в топ языков для изучения, а Java как платформа — в топ используемых технологий в мире IT и смежных областях.
Сегодня мы начнем изучать Java, причем сразу с примера игры «Крестики-нолики».
Итак, поехали. Надеюсь как установить java SDK ты уже разобрался. Мы будем писать код в IDE IntelliJ IDEA, но если у вас какая-то другая, например Eclipse, то разницы большой не будет.
Итак, создаем новый проект: нажимаем «create new project», выбираем java и щелкаем «next» до окна, где требуется ввести имя проекта, вводим TicTacToe (крестики-нолики). В некоторых случаях на этапе создания потребуется выбрать шаблон проекта, тогда смело выбирай что-либо похожее на JavaConsoleApplication.
После этого нажимаем «Finish». Idea немного подумает и сгенерирует нам проект с классом Main, в котором определена функция main().
Давайте разберемся, что здесь что. Слева открыто окно структуры проекта «Project», как мы видим в папке src в пакете com.company находится единственный java-файл нашей программы с именем Main. Справа показано его содержимое. Тут надо сделать небольшое отступление, дело в том, что в Java почти все представлено классами. В том числе и файлы программы описывают классы, причем имя файла должно совпадать с классом, который описывается в этом файле (например, у нас файл Main.java описывает класс Main). Пусть слово «класс» не смущает на первом этапе. Пока лишь отмечу, что для глубокого изучения Java так или иначе придется познакомиться с объектно-ориентированным подходом. В двух словах, класс можно воспринимать как шаблон, идею, а экземпляры класса — как реализацию этой идеи. Экземпляры класса называются его объектами. Например, вы можете представить идею стола (нечто, на что можно класть предметы), однако конкретных экземпляров такого стола огромное множество (на одной ножке, на четырех, круглые, квадратные, из разных материалов). Примерно так соотносятся классы и объекты в объектно-ориентированном программировании.
Внутри нашего класса Main описана функция main(), в Java с этой функции начинается исполнение программы, это точка входа в наше приложение. Сейчас там написан только автоматический комментарий (комментарии в Java начинаются с двух символов //). Попробуем кое-что добавить в наш код и проверить работоспособность приложения. Внутри функции main() допишем две строки:
Встроенная функция println() просто выводит на экран текстовую информацию. Запустим наше приложение (нажимаем shift-F10 или зеленый треугольник). Внизу, во вкладке run появится вывод нашей программы:
Функция main() отработала и закончилась, вместе с ней закончилась наша программа.
В игре пользователю конечно захочется взаимодействовать с программой более продвинутым способом, поэтому нам понадобится окно. Набираем внутри функции main() следующие строки:
Смысл большинства строк понятен из комментариев к ним, отдельно отмечу строку window.setLayout() — здесь устанавливается менеджер расположения, который будет применяется к компонентам, добавляемым в наше окно. Менеджер BorderLayout может располагать новые компоненты относительно сторон света (North(верх), West(слева), East(справа), South(низ)), Center (центр)). По умолчанию он располагает компоненты по центру. Подробнее с менеджерами расположения можно познакомиться в документации.
Теперь, если запустить нашу программу, мы увидим окно:
Пока в этом окне ничего нет. Создадим свой компонент, который и будет отрисовывать графику игры.
Очевидно, что рисовать в консоли у нас не получится, нужен какой-то компонент для более продвинутого взаимодействия с пользователем. Для этой цели создадим еще один класс, назовем его TicTacToe. Щелкаем правой клавишей мыши на имени пакета приложения (в данном случае это com.company)
И в появившемся меню выбираем пункт «New» → «Java Class». В окне создания класса набираем его имя «TicTacToe» и нажимаем «Enter».
У нас в проекте появился еще один класс. В главное окно можно добавлять только объекты класса JComponent, кроме того, нам нужна область для рисования. Поэтому наследуем наш класс TicTacToe от JComponent. Ой сколько непонятных слов! Сейчас постараюсь пояснить.
Наследование классов — это как создание уточненного шаблона на базе существующего. Например, есть класс Стол, описывающий идею стола вообще. Но нам очень часто приходится создавать столы на четырех ногах с деревянной столешницей, поэтому для удобства мы можем уточнить идею класса Стол и создать шаблон ДеревянныйСтол — он будет иметь все те же основные свойства, что и родительская идея, но зато часть свойств у него уже определены и понятны — это число ног, равное четырем и материал столешницы — дерево.
С JComponent то же самое — данный класс реализует идею некоторого графического компонента пользовательского интерфейса. Такой компонент можно добавить в окно и он умеет как-то себя отрисовывать. Например, класс JButton — наследник класса JComponent, это компонент, который выглядит, как кнопка и умеет показывать анимацию клика мышкой.
Здесь же, наследуя класс JComponent, мы создадим свой компонент, в котором сможем рисовать то, что нам нужно.
Итак дописываем extends JComponent в строку описания класса:
Крестики нолики «Без границ»
Крестики-нолики… в них играли все, я уверен. Игра притягательна своей простотой, особенно когда ты тянешь часы где-нибудь на уроке, паре, а под рукой нет ничего, кроме тетрадного листа и простого карандаша. Уж не знаю, кто первым когда-то догадался рисовать кресты и кружки в 9 квадратах, но с тех пор игра нисколько не потеряла в востребованности, тем более, что народ придумал огромное множество её вариаций.
Эта статья про процесс разработки ИИ на javascript для игры в одну из таких вариаций крестиков-ноликов: получилось довольно много материала, но я разбавил его анимацией и картинками. В любом случае, хотя бы стоит попробовать поиграть в это.
Отличия этого варианта игры от оригинала в следующем:
Перед тем, как начнем
Вынужден извиниться заранее за объем статьи и местами не совсем доходчивое изложение мысли, однако у меня не получилось сжать стаю без потери в содержании и качестве.
Рекомендую сначала ознакомиться с результатом. Код
Горячие клавиши и команды:
Начнем
Начать нужно с реализации самой игры, т.е. написать приложение для двух игроков, пока без бота. Для своих целей я решил использовать javascript + jquery + bootstrap4, хотя он там практически не используется, но его лучше оставить – или таблица поплывет. Тут рассказывать особо нечего, материала по js, jquery и bootstrap на хабре полно. Скажу лишь, что использовал MVC. Да и вообще, объяснять абсолютно весь код я не буду – материала и без того получилось много.
Итак, игровое поле было готово. Можно устанавливать фигуры в клетки. Но победа кого-либо из игроков никак не фиксировалась.
Сканирование «конца игры»
Игра заканчивается, когда один из игроков поставит 5 фигур в ряд. «Все просто!» — подумал я. И начал сканировать абсолютно все клетки поля: сначала все горизонтали, потом вертикали и, наконец, диагонали.
Это тупой способ, но он работал. Однако, его можно было значительно улучшить, что я и сделал: Большая часть клеток будет оставаться пустой на протяжении всей игры – игровое поле слишком большое, чтоб его можно было заполнить целиком. Поскольку сканировать его нужно было каждый ход, а за один ход ставится только одна фигура — то можно сосредоточиться только на этой фигуре (клетке): просканировать только одну горизонталь, вертикаль и две диагонали клетки, которым принадлежит та самая клетка.
Плюс ко всему, не нужно сканировать все клетки линий. Поскольку конец игры – это 5 фигур в ряд, то фигуры, удаленные друг от друга на 6 клеток нас не интересуют. Достаточно сканировать по пять клеток в каждую из сторон. Не понятно? Смотри анимацию ниже.
Приступим к самому боту
Итак, мы уже написали страницу с крестиками-ноликами. Переходим к основной задаче – ИИ.
Нельзя просто так взять и написать код, если ты не знаешь как: нужно продумать логику бота.
Суть заключается в анализе игрового поля, хотя бы его части, и просчета цены (веса) каждой клетки на поле. Клетка с наибольшим весом – самая перспективная – туда бот и поставит фигуру. Основная сложность именно в просчете веса одной клетки.
Терминология
Крестики и нолики – это фигуры.
Атакой будем называть несколько одинаковых фигур, стоящих рядом, на одной линии. По сути, это множество. Количество фигур в атаке – её мощность. Одна отдельная фигура – тоже атака (мощностью 1).
На соседних клетках атаки (на концах) могут быть пустые клетки или фигуры противника. Логично думать, что атаку с двумя пустыми клетками на «концах», мы можем развивать в двух направлениях, что делает ее более перспективной. Количество пустых клеток на «концах» атаки будем называть её потенциалом. Потенциал может принимать значения 0, 1 или 2.
Атаки обозначаем так: [ мощность атаки, потенциал ]. Например, атака [4:1].
Рис 1. Атака [4:1]
В ходе анализа мы будем оценивать все клетки, которые входят в определенную область. У каждой клетки будет просчитываться её вес. Он вычисляется на основе весов всех атак, на которые влияет данная клетка.
Суть анализа
Представим, что на игровом поле уже есть несколько атак одного и второго игрока. Кто-то из игроков делает ход (пускай крестики). Естественно ход он делает в пустую клетку – и тем самым он может:
Суть анализа в следующем:
По сути, таким алгоритмом мы проверяем, что будет, если мы пойдем так… а что будет если так пойдет оппонент. Мы смотрим на один ход вперед и выбираем наиболее подходящую клетку – с наибольшим весом.
Если какая-то клетка имеет больший вес, нежели другая, значит она приводит к созданию более опасных атак, либо к блокировке сильных атак противника. Все логично… мне кажется.
Если зайти на страницу и написать в консоли SHOW_WEIGHTS = true, можно визуально прочувствовать работу алгоритма (Будут показаны веса клеток).
Веса атак
Пораскинул я мозгами и привел такое соответствие атак и весов:
Подобрано эмпирически – возможно это не оптимальный вариант.
Я добавил в массив атаки мощностью 5 с запредельно большим весом. Объяснить это можно тем, что бот анализирует игру, смотря на шаг вперед (подставляя фигуру в клетку). Пропуск такой атаки есть ни что иное, как поражение. Ну или победа… смотря для кого.
Атаки с большим потенциалом ценятся выше.
Атака [4:2] в большинстве случаев решает исход игры. Если игроку удалось создать такую атаку, то оппонент уже не сможет ее заблокировать. Однако это еще не победа. Противник может быстрее завершить игру, даже при наличие у нас на поле атаки [4:2], поэтому ее вес ниже, чем у атак мощностью 5. Смотри пример ниже.
Рис 2. Атака [4:2]
«Рваные» атаки
В этом абзаце код не представлен. Здесь мы вводим понятие делителя атаки и объясняем суть «рваных атак».
Рассмотрим такую ситуацию: при подстановке фигуры на удалении нескольких пустых клеток, но не более 5-и, расположена еще одна.
И вроде бы, две одинаковые фигуры, на одной линии… визуально это похоже на атаку, а по факту нет. Не порядок, так как такие «рваные» атаки также несут в себе потенциальную угрозу.
Специально для таких случаев, для каждой атаки будем просчитывать делитель. Изначально его значение равно 1.
Таким образом, «рваные» атаки так же будут учитываться ИИ. На самом деле, это будут обычные атаки, но чем они дальше находятся от сканируемой клетки, тем меньшее влияние на нее оказывают и, соответственно, имеют меньший вес (благодаря делителю).
Алгоритм поиска атак
Во-первых, создадим класс атаки. У атаки будет 3 атрибута, о которых я писал ранее:
И один метод, который будет возвращать вес данной атаки:
Далее. Поиск всех атак для одной клетки мы разделим на:
Однако, нам не нужно проверять всю линию целиком. Максимальная мощность атаки, которая нас интересует – 5. Безусловно, создать атаку мощностью, скажем, 6 – возможно. Но для ИИ, который анализирует игровую ситуацию следующего хода, все равно, что 6, что 5. Перспектива получить одну из этих атак говорит о конце игры на следующем ходу. Соответственно, вес анализируемой клетки будет в обоих случаях будет одинаковым.
Здесь надо остановиться, так как может возникнуть вопрос: зачем проверять 6-ую клетку, если максимальная мощность атаки – 5. Ответ – это нужно для определения потенциала удаленной от центра атаки.
Вот пример: атака мощностью 1 на картинке находится на границе сканируемой области. Чтобы узнать потенциал этой атаки нужно «заглянуть за границу».
Рис. 3. Сканирование 6-ых клеток. Если не просканировать 6-ую клетку, можно неправильно определить потенциал атаки.
Для завершения некоторых атак может просто не хватать места. Посчитав attackplace мы заранее можем понять, какие из атак бесперспективны.
Рис. 4. Место для атаки
1) Начнем с центральной клетки. Она должна быть пустой (мы ведь собираемся сделать в нее ход, не так ли? Однако мы не забываем, что наш ИИ должен подставлять фигуры в данную клетку для анализа следующего хода. Фигура, которую мы подставляем – this.subfig – по умолчанию крестик. Поскольку центральная клетка изначально будет содержать в себе какую-либо фигуру после подстановки, то она будет принадлежать какой-то атаке this.curAttack:
Все эти пункты мы отобразили в значениях конструктора по умолчанию – смотри код выше.
2) Далее, уменьшая итератор, перебираем 5 клеток с одной стороны от сканируемой. За это отвечает функция getAttacks( cellX, cellY, subFig, dx, dy ), где:
cellX, cellY – координаты проверяемой клетки
subFig – фигура, которую мы подставляем в проверяемую клетку
dx, dy – изменения координат x и y в циклах – так мы задаем направление поиска:
Обратите внимание, что если checkCell() что-то вернет, то выполнение цикла прекращается.
3) Проверяем фигуры данных клеток.
За это отвечает функция checkCell( x, y ):
Для начала запишем фигуру в переменную fig:
Model.Field – наше игровое поле.
fig может быть ‘x’, ‘o’, ‘b’ (граница), 0 (пустая клетка).
4) Если на 5-ой клетке фигура совпадает с центральной клеткой, значит атака «уперлась» в границу и для определения потенциала атаки придется «проверить границу» ( this.checkEdge = true).
Функция checkCell – готова. Однако продолжаем работать над классом checkLine.
5) После выполнения первого цикла, надо «развернуться». Переводим итератор в центр и центральную атаку, с индексом 0, убираем из массива атак и устанавливаем как текущую.
6) Далее идем в другую сторону от текущей клетки, увеличивая итератор.
Абсолютно такая же проверка фигур. (Код уже написан – функция getAttacks)
7) Все, мы собрали все атаки, что были на линии в один массив.
На этом с классом checkLine всё… закончили.
Ну а дальше все просто – создаем объект checkLine для каждой из линий (2 диагонали, горизонталь и вертикаль) и вызываем функцию getAttacks. То есть, для каждой линии — свой объект checkLine и, соответственно, свой набор атак.
Пусть за все это отвечает функция getAllAttacks() – уже отдельно от описанных выше классов;
На выходе имеем объект со всеми атаками для проверяемой клетки
Однако вы, возможно, заметили некую функцию-фильтр. Ее задача – отсеивать «бесперспективные» атаки:
Да соберем, наконец, все воедино
Итак, основной ад позади — описан выше. Пора слепить из него что-то рабочее. Функция countWeight( x, y ) — принимает на вход координаты клетки, а возвращает ее вес. Что же у нее под капотом?
Во-первых, получим массив всех атак, которым клетка принадлежит. ( getAllAttacks( x, y ) ). Перебирая все линии, мы считаем количество брейкпоинтов. Если 2 брейкпоинта – вспоминаем, что такая ситуация может решить исход игры, и увеличиваем вес клетки на 100.
Однако все брейкпоинты должны принадлежать одному игроку, поэтому пришлось реализовать проверку в 2 шага: сначала крестики, потом нолики.
Поскольку в массиве весов атак ( ATTACK_WEIGHTS[] ) я не предусмотрел атаки мощностью 6 и больше, мне пришлось заменить их на атаки мощностью 5. Разницы никакой – все они приводят к концу игры.
Ну и суммируем веса атак – к этому все и шло.
Еще небольшой момент: чтобы бот в конце игры не тупил, когда он уже построил атаку мощностью 4 и думает над текущем ходом, необходимо значительно увеличить вес клетки для завершения такой атаки. Без этого, ИИ, просто на просто, может начать защищаться от «опасных» атак оппонента, хотя игра, казалось бы выиграна. Последний ход важен.
Теперь при вызове этой функции для конкретной клетки мы получим ее вес. Проводим эту операцию для всех клеток и выбираем наилучшую (с наибольшим весом). Туда и ходим)
Остальной код вы сможете найти на github. Материала уже предостаточно, и его изложение, как я не пытался, оставляет желать лучшего. Но если ты смог дочитать до этого момента, дорогой читатель, то я тебе благодарен.
Мое мнение о полученном результате
Сойдет! Да, его можно обыграть, однако сделать это немножко проблематично лично для меня. Возможно я просто недостаточно внимателен. Попробуйте и вы свои силы.
Знаю, что можно проще, но не знаю как. Хотелось бы выслушать людей знающих или посмотреть на иные реализации такого бота.
Знаю, что можно лучше. Да… можно воспользоваться известными алгоритмами, вроде минимакса, но для этого нужно обладать некоторой базой знаний в области теории игр, чем похвастать увы не могу.
В будущем планирую добавить анализ брейкпоитов на несколько шагов вперед, что сделает бота еще более серьезным соперником. Однако сейчас не имею четкого представления о реализации сего; лишь имею предстоящую сессию и недописанный диплом – что меня огорчает.
Стратегические крестики-нолики (Starategic Tic-Tac-Toe)
Играть одну партию в крестики-нолики более двух часов — легко.
В статье будет рассказано о том как можно привнести элементы «стратегии и тактики» в привычные всем крестики-нолики. Будут описаны и проанализированы правила игры, рассказано об игровых полях.
Что предлагается?
Игра Starategic Tic-Tac-Toe (STTT) или Стратегические крестики-нолики это, как и её прародитель, игра для двух участников для которой необходимы лишь карандаш и бумага. Она является надмножеством игры Ultimate Tic-Tac-Toe также как Ultimate Tic-Tac-Toe является надмножеством обычных крестиков-ноликов (Ordinary Tic-Tac-Toe). Задача игры — помочь игрокам приобрести навыки стратегического мышления.
Домашняя страница проекта
Содержание
Термины и определения
Осторожно, множество похожих определений и их количество может вас оттолкнуть, но без этого базиса вы не сможете понять о чём пойдёт речь далее.
На этот момент все необходимы нам определения заданы и мы можем приступить к обсуждению самой игры.
Правила игры
Задавая и анализируя данный класс игр (надмножество игры Крестики-нолики) мы, для упрощения понимания и сравнения, разделим правила игры на три части: правила хода, правила выигрыша и ограничения. Рассмотрим игру Оперативные крестики-нолики согласно данному подходу.
Оперативные крестики-нолики
Теперь, когда правила знакомой всем игры заданы согласно предлагаемому подходу, читателю будет легче ориентироваться в правилах Тактических и Стратегических крестиков-ноликов.
Наборы правил Стратегических крестиков-ноликов основываются на правилах Тактических крестиков ноликов, поэтому приведём и их в предлагаемой форме.
Тактические крестики-нолики
Как видно первый игрок сходил в третью Оперативную клетку пятого Оперативного поля, поэтому второй игрок должен ходить в третью Тактическую клетку данного Тактического поля.
Для множества студентов игроков, с кем мне приходилось сразиться, данный набор правил был соложен для понимания на слух, но в ходе первой, пробной игры большинство разбиралось, так что на данном этапе я предлагаю читателю сыграть в Тактические крестики-нолики для чего вам понадобятся карандаш/ручка, тетрадный лист (или обычный если хорошо чертите прямые) и заинтересованный товарищ.
Настало время поговорить о самих Стратегических крестиках-ноликах. В первую очередь при создании новой игры была поставлена цель расширить текущее игровое поле за счёт увеличения количества «уровней» игры, в следствии этого возникла необходимость составить новые правила хода, поскольку старые, как мы увидим ниже, были полными и не могли предоставить новые пути задания ходов игроков. За столом обсуждений будущих правил данной игры родились три основных направления в последствии преобразовавшихся в наборы правил: Тактический, Функциональный и Гиперфункциональный. Опишем данные наборы правил.
Стратегические крестики-нолики
Общие правила
Все три набора правил сохраняют правила выигрыша и ограничения на Тактическим уровне и объявляют те же правила для Стратегического уровня. Таким образом правила выигрыша и ограничения для Стратегического уровня выглядят точно также как правила Тактических крестиков-ноликов с точностью до названий клеток. Читателю предлагается самому написать правила Стратегического уровня для проверки понимания текущих терминов и положений.
Тактический набор правил
Правила Тактических крестиков-ноликов задают отображение (mapping) из множества Клеток в множество Тактических клеток для определения того куда должен ходить текущий игрок в зависимости от хода предыдущего игрока или другими совами правила хода на Тактическом уровне. Тактический набор правил сохраняет отображение из множества Клеток предыдущего Оперативного поля в множество Тактических клеток текущего Тактического поля, при этом декларируя, что отображение из множества Тактических клеток предыдущего Тактического поля в множество Стратегических клеток Стратегического поля сохраняется таким же как и отображение из множества Клеток предыдущего Оперативного поля в множество Тактических клеток текущего Тактического поля или другими словами правила хода на Стратегическом уровне такие же как и на Тактическом уровне. Наглядную иллюстрацию данного положения можно найти под спойлером.
На картинке игрок сходил в первую Оперативную клетку четвёртого Оперативного поля пятого Тактического поля, а это означает, что следующий игрок должен ходить любую из Оперативных клеток первой Тактической клетки (зелёная) четвёртого Тактического поля (красное), что в свою очередь определит ход следующего игрока.
Функциональный набор правил
Вторая идея заключалась в сопоставлении строкам 9х1 (или столбцам 1х9, как будет показано ниже это не столь значительно и выбор в пользу строк был сделан лишь из эстетики получающегося игрового поля) клеток номера Стратегической клетки, в которую должен быть произведён следующий ход. Данная идея была реализована путём помещения номеров Стратегических клеток, для следующего хода, слева в той же строке, что и Клетка текущего хода. Чтобы понять о чём идёт речь перейдите в раздел с игровыми полями. Особенности выбора номеров следующих Стратегических клеток будут раскрыты в разделе анализа игры. Правила отображения из множества Клеток текущего Оперативного поля в множество Тактических клеток следующего Тактического поля сохраняются неизменными.
Гиперфункциональный набор правил
Третья идея заключалась в определении номера Стратегической клетки следующего хода для каждой Клетки текущего хода. Данный набор правил определяет именно такое отображение, при этом правила отображения из множества Клеток текущего Оперативного поля в множество Тактических клеток следующего Тактического поля сохраняются неизменными. Особенности выбора номеров следующих Стратегических клеток будут раскрыты в разделе анализа игры.
Игровые поля
Название поля | Размер в Клетках | Можно ли нарисовать от руки | Поместится ли на половине тетрадного листа |
---|---|---|---|
Игровые поля | |||
Базовое | 29х29 | Да | Да |
Пронумерованное | 31х31 | Да | Да |
Функциональное | 35х31 | Да | Да |
Гиперфункциональное | 35х31 | Нет | Да |
Полное | — | Да | Нет |
Вспомогательные поля | |||
Поле помощи | 11х15 | Да | Да |
Поле записи ходов | 6хN | Да | Да |
Непрерывное поле записи ходов | — | Да | Да |
Далее под соответствующими спойлерами расположены изображения полей и заметки о их дизайне и предназначении.
Базовое поле — то с чего начинается игра. Имея только его вы уже можете играть в любой вариант Стратегических крестиков-ноликов, состоит из девяти полей для игры в Тактические крестики-нолики.
Пронумерованное поле — это Базовое поле, Тактические клетки которого пронумерованы для упрощения отслеживания деятельности игроков в ходе игры. Число рядом с Тактической клеткой отражает как номер Стратегической клетки (десятки) так и номер Тактической клетки (единицы).
Функциональное поле — это Пронумерованное поле на котором возможна игра (проще следить за её ходом) с Функциональным набором правил. Для данного поля положение чисел, задающих следующую Стратегическую клетку определено слева от соответствующих строчек, что позволяет сохранять размеры поля такими, чтобы оно могло уместиться на половине тетрадного листа.
Гиперфункциональное поле — на данном поле возможна игра с Гипперфункциональным набором правил, оно не может быть нарисовано от руки так как содержит градации цвета для задания чисел, обозначающих следующую Стратегическую ячейку.
Данное поле было создано для того чтобы игроки не путались при возобновлении игры и не вспоминали кому принадлежит какая клетка (Стратегическая или Тактическая). По ходу игры игроки могут отмечать свои успехи тем самым сохраняя прогресс партии.
Данное поле было разработано с целью помощи игрокам в запоминании прогресса игры, очерёдности хода, проверки правильности игрового поля. В предлагаемом варианте данного поля в течении своего хода необходимо записать номера Стратегической (S) Тактической (T) и Операционной (O) клетки, в которую игрок производит ход. Данный вариант поля преимущественно предназначен для игры с Функциональным и Гиперфункциональным набором правил, для Тактического же набора было специально разработано Непрерывное поле записи ходов. Для данного поля существует несколько вариантов исполнения, все они доступны для скачивания.
Непрерывное поле записи ходов — специально разработанное поле записи ходов, предназначенное для игры с Тактическим набором правил. Необходимость его появления была обоснована непосредственным игровым опытом автора. В данном поле дополнительные записи и повторение чисел для Тактического набора правил было сведено к минимуму. Ниже в статье приведён пример игры происходившей с использованием данного поля. Для данного поля существует несколько вариантов исполнения, все они доступны для скачивания.
Полное поле представляет собой совокупность игровых и вспомогательных полей, представленную на одном листе. Для данного поля существует несколько вариантов исполнения, все они доступны для скачивания.
Анализ игры
В данном разделе будет рассказано о том как был обоснован выбор чисел, означающих следующую Стратегическую ячейку для Функционального и Гиперфункционального набора правил. Метод анализа игры заключается в следующем:
Весь код, реализующий этапы анализа можно найти по ссылке. Код написан на Lua 5.1 и запустится как на интерпретаторе так и на JIT-компиляторе (второй более предпочтителен из-за вычислительной сложности предлагаемого метода). Оконечные этапы анализа — построение heatmap’ов и подсчёт среднего расстояния проводился в Excel.
Проанализируем полученные результаты. Как опорный возьмём результат для Тактического набора правил. И так для данного набора правил удобно взять отображение из множества Тактических клеток в него же, среднее расстояние между Тактическими клетками получилось равным 1.(8) хода. Не много, это означает, что для успешной игры в памяти стоит хранить последние два хода и думать как минимум на два хода вперёд. Heatmap можно увидеть под спойлером. Для всех heatmap’ов шкала идёт от красного к зелёному через жёлтый на увеличение.
Далее применим метод анализа к Функциональному набору правил. Для того как именно определить числа в данном наборе правил существовали некоторые предпосылки, их обсуждение выходит за рамки данной статьи, скажем лишь, что в ходе разработки был предложен довольно эффективный метод создания наборов чисел, проанализировав который мы смогли прийти к выводам об эффективности наборов выделенных из полученных.
Для данного набора правил было удобно взять отображение из множества триплетов Тактических клеток в него же (в триплеты объединены Тактические клетки 1-3, 4-6, 7-9 для каждой Стратегической ячейки). Взглянем на результаты: оптимальными были названы два набора чисел под кодовыми названиями map34 и map67, для данных наборов среднее расстояние между триплетами составило 2.(6) хода. Их особенностью является то, что расстояние от каждого триплета до самого себя составляет ровно 3 хода.
Для визуального сравнения представлены heatmap’ы других наборов:
map14
Последним проанализируем Гиперфункциональный набор правил. При детальном рассмотрении игровых полей, созданных под данный набор правил читатель мог увидеть закономерность в расположении цифр, отвечающих за следующую Стратегическую клетку. Используя данную закономерность мы создали девять наборов чисел описывающих переходя для Гиперфункционального набора правил, из которых был найден оптимальный получивший кодовое имя hmap2. Его показатели составили 2.206 хода в среднем между Тактическими клетками и ровно 3 хода чтобы попасть в туже Тактическую клетку.
Выводы и послесловие
В статье было рассказано о новой игре в своём типе — Стратегических крестиках-ноликах, дизайнерские решения в ходе создания игры были обоснованы путём анализа.
Направления будущих работ
Основными направлениями авторам представляются:
Послесловие
Историю создания и сведения об авторах можно найти на странице проекта, а также там можно найти возможные ограничения авторских прав на производную деятельность. Авторы будут рады помощи с переводом оставшегося русского текста на английский язык на главной странице проекта.