- PVSM.RU - https://www.pvsm.ru -
Хочу представить вашему вниманию небольшую статью о том, как я делал для Android пулук — настольную игру индейцев Центральной Америки.
"Обучать" компьютеры человеческим играм я начал едва научившись программировать. Первым был калах (разновидность манкалы) для калькулятора МК-61. Позже — Волк и овцы, сначала для MS-DOS, а потом и для Android.
Читая на Хабре статьи GlukKazan [1], я наткнулся на интересный ЖЖ Дмитрия Скирюка [2] с описаниями настольных игр разных народов мира. Одна из них — пулук [3] — меня на столько увлекла, что я решил реализовать ее для Android.
Историю игры [3], а также ее сакральное значение для урожаев маиса можно прочесть в ЖЖ Дмитрия. Я же приведу здесь лишь правила.
Доска для пулука состоит из 11 полосок, причем первая и последняя служат "городами" для фишек игроков. Фишек у каждого игрока по пять штук. Вместо игрального кубика обычно используются четыре кукурузных зернышка, у которых одна из сторон каким-либо образом помечена. Очки считаются так:
В начале игры все фишки игроков стоят в "городах". Во время своего хода игрок бросает зерна и перемещает одну из своих фишек на соответствующее число полосок по направлению к "городу" соперника.
Две фишки одного игрока не могут занимать одну полоску (кроме "города"). Но можно ставить свою фишку на полоску, на которой стоит фишка противника — брать эту фишку в плен. Далее этот столбик продолжает двигаться как одна (верхняя) фишка и может брать в плен другие фишки противника.
Если в плен берется столбик то все нижние фишки игрока, выполнившего захват, которые были в плену, освобождаются.
Когда игрок приводит столбик в "город" на другом конце доски, то все плененные фишки выводятся из игры (бьются), а своя возвращается в свой город и снова может вступить в игру. Точный бросок для захода в "город" противника не нужен.
Побеждает игрок, который захватит или побьет все фишки противника.
Как пишет Дмитрий: "Несмотря на кажущуюся простоту и непривычно маленькую доску, пулук очень увлекателен. Тактика его уникальна, он не похож на игры других народов: это не гонки с преследованием, не военная игра и не «переходы» вроде Уголков, а некая хитрая «ловилка-уводилка»".
В своем варианте игры я сделал два изменения. Во-первых, сделал подсчет очков более похожим на игральный кубик — количество очков соответствует количеству зерен, выпавших отметками вверх. Если же все четыре зерна выпали пустой стороной вверх, это считается за пять очков.
Второе изменение связано с освобождением своих фишек из плена. У Дмитрия сказано: "а нижняя фишка, бывшая в плену, освобождается и продолжает путь самостоятельно". Но в случае одновременного освобождения нескольких фишек, возникает ситуация, когда все они занимают одну полоску. А это противоречит правилу "Две фишки одного игрока не могут занимать одну полоску". В англоязычных же правилах освобождение своих фишек происходит лишь когда столбик достигает "города" на противоположной стороне доски — все свои фишки освобождаются, а фишки противника бьются. В моем варианте освобожденные фишки сразу перемещаются в свой город. Это не приводит к нарушению других правил и позволяет вводить фишки снова в игру очень быстро.
В техническом плане игра, наверное, особого интереса не представляет, так как особых хитростей при разработке я не использовал. Программировал Android-приложение я с помощью фреймворка AndEngine. Хотя он в последнее время практически не развивается, его возможностей мне вполне хватило.
Единственный тонкий момент связан с анимацией движения фишек. Первоначально я анимировал перемещение каждой фишки. Например, при перемещении столбика из трех фишек не смотря на то, что видима лишь верхняя фишка, программа все равно перемещала все три. Естественно, такой подход приводил к довольно ощутимому потреблению ресурсов. Поэтому я сделал два псевдостолбика для демонстрации перемещений. Во время хода перемещаемые фишки сначала становились невидимыми, псевдостолбику задавалась нужная высота и запускалась анимация перемещения. После завершения анимации столбик становился невидимым, фишки устанавливались на новую позицию и делались видимыми. Такой вариант оказался немного сложнее предыдущего, но зато гораздо более экономно тратил вычислительные ресурсы.
Случайный характер выпадающий игровых очков не позволяет прогнозировать следующие ходы и использовать, например, альфа-бета алгоритм для реализации ИИ. Во время своего хода игрок может выполнить (при возможности) одно из следующих действий:
Например, при следующей ситуации:
игрок может либо вывести очередную свою фишку из "города" в поле, либо фишкой с третьей полоски захватить в плен фишку противника на пятой или же пойти фишкой с 8-й полоски и вывести из игры фишку противника.
При доступности нескольких из представленных действий одновременно выбор того или иного и будет определять характер игры. Если приписать каждому из действий определенный вес, то в общих чертах алгоритм можно представить в следующем виде:
Используя разные наборы весов, можно получить несколько вариантов ИИ:
public class OpponentAIFirstController extends OpponentAIController {
protected static final int WEIGHT_IND_HOME = 0;
protected static final int WEIGHT_IND_CAPTURE = 1;
protected static final int WEIGHT_IND_RELEASE = 2;
protected static final int WEIGHT_IND_CAPTURED_MOVE = 3;
protected float movementWeights[];
public OpponentAIFirstController(...) {
super(...);
initMovementWeights();
}
protected void initMovementWeights() {
movementWeights = new float[] {1.0f, 1.1f, 1.1f, 1.5f};
}
private void calcInitialScores(int pCornsValue) {
for(i = mFirstChipIndex; i < mLastChipIndex; i++) {
...
int newRow = mFieldController.calcNewRow(curChip, pCornsValue);
if (mFieldController.isHomeRow(newRow)) {
mMoveScores[j] += 1 + curChip.capturedCount();
mMoveScores[j] *= movementWeights[WEIGHT_IND_HOME];
continue;
}
...
}
}
...
}
public class OpponentAISecondController extends OpponentAIFirstController {
...
@Override
protected void initMovementWeights() {
movementWeights = new float[] {1.0f, 2.0f, 1.1f, 1.1f};
}
}
На самом деле кое-какие предсказания можно сделать, основываясь на теории вероятностей. Например, если перед рассмотренной выше ситуацией выпадали значения 5, 5, 4, 3, 2, 3, то с высокой долей вероятности можно предположить, что противнику выпадет значение 1. В этом случае крайне желательно спасти от пленения фишку, стоящую на 8-й полоске. Поэтому третий алгоритм, который я разработал, запоминает выпавшие значения зерен и приписывает высокий вес тем ходам, которые спасают фишки от потенциального пленения.
Я заранее не знал, какая из стратегий окажется наиболее оптимальной. С другой стороны, называть варианты ИИ как обычно Начинающий, Мастер и т.п. довольно банально. Поэтому каждый из вариантов я назвал в честь определенного индейского божества. Первый алгоритм, который в первую очередь пытается сохранить свои фишки — в честь Кетцалькоатля [4] — наверное, это самое известное индейское божество. Второй алгоритм, стремящийся захватывать фишки противника — в честь божества войны Камаштли [5]. Третий алгоритм, который по моим предположениям должен быть наилучшим, — в честь божества маиса Центеотля [6]. Ведь божество, которому посвящена игра, должно в нее играть лучше всех.
Дабы привнести некоторый образовательный элемент в игру, в диалог выбора ИИ я добавил ссылки на соответствующие статьи на Wikipedia, а в диалог "О программе" — ссылку на статью Дмитрия о пулук. После некоторого количества игр я получил следующую статистику:
Пулук действительно оригинальная и забавная игра. Теперь "играть партия за партией до изнеможения" можно на дому. С помощью Internet и Google Play Game Services даже можно сразиться с настоящим майя. Ну а фермеры могут проверить ее влияние на урожаи кукурузы.
Автор: XVadim
Источник [7]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/android-development/193723
Ссылки в тексте:
[1] GlukKazan: https://habrahabr.ru/users/glukkazan/
[2] Дмитрия Скирюка: http://skyruk.livejournal.com/
[3] пулук: http://skyruk.livejournal.com/290069.html
[4] Кетцалькоатля: https://ru.wikipedia.org/wiki/%D0%9A%D0%B5%D1%82%D1%86%D0%B0%D0%BB%D1%8C%D0%BA%D0%BE%D0%B0%D1%82%D0%BB%D1%8C
[5] Камаштли: https://ru.wikipedia.org/wiki/%D0%9A%D0%B0%D0%BC%D0%B0%D1%88%D1%82%D0%BB%D0%B8
[6] Центеотля: https://ru.wikipedia.org/wiki/%D0%A6%D0%B5%D0%BD%D1%82%D0%B5%D0%BE%D1%82%D0%BB%D1%8C
[7] Источник: https://habrahabr.ru/post/310404/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best
Нажмите здесь для печати.