выигрышная ситуация в настольной игре - алгоритм поиска - PullRequest
7 голосов
/ 30 ноября 2010

Я ищу, возможно, эффективный алгоритм для определения "выигрышной" ситуации в игре гомоку (пять в ряд), сыгранной на доске 19x19. Ситуация выигрыша возникает, когда одному из игроков удается получить пять и НИКОГДА больше пяти «камней» подряд (по горизонтали, диагонали или вертикали).

У меня есть следующие легко доступные данные:

  • предыдущие ходы ("камни") обоих игроков, сохраненные в 2d массиве (может быть также объектом обозначения json), с переменными "B" и "W", чтобы отличать игроков друг от друга,
  • "координаты" входящего хода (move.x, move.y),
  • количество ходов, совершенных каждым игроком

Я делаю это в javascript, но любое решение, которое не использует низкоуровневые вещи, такие как выделение памяти или высокоуровневые (python) операции с массивами, было бы хорошо.

Я нашел похожий вопрос ( Обнаружить выигрышную игру в ноль и крестики ), но приведенные там решения относятся только к маленьким доскам (5x5 и т. Д.).

Ответы [ 3 ]

6 голосов
/ 12 декабря 2010

Простое для понимания решение без чрезмерных циклов (только псевдокод предоставлен, дайте мне знать, если вам нужно больше объяснений):

Я предполагаю, что ваш 2-й массив работает так:

board = [
   [...],
   [...],
   [...],
   ...
];

Т.е. внутренние массивы представляют горизонтальные ряды доски.

Я также предполагаю, что массив заполняется буквами "b", "w" и "x", представляющими черные фигуры, белые фигуры ипустые квадраты, соответственно.

Мое решение несколько разделяй и властвуй, поэтому я разделил его на 3 случая ниже.Имейте в виду, это может показаться более сложным, чем просто запускать несколько вложенных циклов вначале, но концепция проста для понимания, чтения и правильного подхода, достаточно проста для кодирования.

Горизонтальные линии

Давайте сначала рассмотрим случай обнаружения выигрышной ситуации, ТОЛЬКО если линия горизонтальная - это самый простой способ.Сначала объедините строку в одну строку, используя что-то вроде board[0].join("").Сделайте это для каждого ряда.Вы получите массив, подобный следующему:

rows = [
   "bxwwwbx...",
   "xxxwbxx...",
   "wwbbbbx...",
   ...
]

Теперь присоединитесь к ЭТОМУ массиву, но вставьте «x» между элементами, чтобы отделить каждую строку: rows.join("x").

Теперь у вас есть одиндлинная строка, представляющая вашу доску, и это просто вопрос применения регулярного выражения, чтобы найти последовательные "w" или "b" ровно 5 длин: superString.test(/(b{5,5})|(w{5,5})/).Если тест вернется true, у вас ситуация выигрыша.Если нет, давайте перейдем к вертикальным линиям.

Вертикальные линии

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

Диагональные линии

Опять же, мы хотимповторно использовать функцию `testRows '.Диагональ, такая как эта:

b x x x x
x b x x x
x x b x x
x x x b x
x x x x b

Может быть преобразована в такую ​​вертикаль, как эта:

b x x x x
b x x x
b x x
b x
b

Смещением строки i на i позиции.Теперь это вопрос переноса, и мы вернулись к тестированию на горизонтали.Вам нужно будет сделать то же самое для диагоналей, которые идут другим путем, но этот ряд сдвига во времени i на length - 1 - i позиций, или, в вашем случае, 18 - i позиций.

Функциональный javascript

В качестве примечания, мое решение прекрасно сочетается с функциональным программированием, что означает, что его можно довольно легко кодировать, если у вас есть функциональные инструменты программирования, хотя в этом нет необходимости.Я рекомендую использовать underscore.js , поскольку вполне вероятно, что вам понадобятся базовые инструменты, такие как map, reduce и filter во многих различных игровых алгоритмах.Например, мой раздел о тестировании горизонтальных линий может быть написан одной строкой javascript с использованием map:

_(board).map(function (row) {return row.join("")}).join("x").test(/(b{5,5})|(w{5,5})/);
1 голос
/ 16 мая 2014

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

Я используюбитовая доска, которая используется в большинстве настольных игр и движков (шахматные движки) из-за эффективности, чтобы представлять мое поле.

Вы можете делать все, что вам нужно в этой игре, с побитовыми операциями.

Бит может иметь только 2 состояния (0 и 1), однако нам нужны 3 состояния, например, p1, p2 или пусто.Чтобы решить эту проблему, вместо этого у нас будет 2 доски, по одной для каждого игрока.

Другая проблема состоит в том, что в Gomoku есть много полей (19x19), и нет числового типа, который имеет столько битов, чтобыпредставлять поле.Мы будем использовать массив чисел для представления каждой строки и будем использовать только первые 15 бит lsb.

Вертикальные ряды

Упрощенная доска игрока 1 может выглядеть следующим образом

000000
101100
001000
011000
000000

Допустим, мы хотим обнаружить 3 подряд.Мы берем первые 3 строки (0-2) и берем их.

000000
001100
101000

С помощью оператора & (AND) вы можете проверить, есть ли 1 в каждой строке.

var result = field[player][0] & field[player][1] & field[player][2];

В этом случае результат будет 0, что означает отсутствие победителя.Давайте продолжим ... Следующий шаг - взять строки 1-3

101100
001000
011000

. Снова примените AND и получим 001000.Нам не важно, какое это число, просто 0 или нет.(результат! = 0)

Горизонтальные строки

Хорошо, теперь мы можем обнаружить вертикальные строки.Чтобы обнаружить горизонтальные ряды, нам нужно сохранить еще 2 доски, по одному для каждого игрока.Но нам нужно инвертировать оси X и Y.Затем мы можем сделать ту же проверку еще раз, чтобы обнаружить горизонтальные линии.Тогда ваш массив будет:

//[player][hORv][rows]
var field[2][2][19];

Диагонали:)

Самая сложная часть - это, конечно, диагонали, но с помощью простого трюка вы можете выполнить ту же проверку, что и выше.Простая доска:

000000
010000
001000
000100
000000

По сути, мы делаем то же самое, что и выше, но прежде чем сделать это, нам нужно сместить строки.Допустим, мы находимся в ряду 1-3.

010000
001000
000100

Первый ряд остается без изменений.Затем вы сдвигаете второй ряд влево, а третий - влево.

var r0 = field[0][0][i];
var r1 = field[0][0][i+1] << 1;
var r2 = field[0][0][i+2] << 2;

То, что вы получите:

010000
010000
010000

Примените И вы можете определить свое выигрыш,Чтобы получить другое диагональное направление, просто сделайте это снова, но вместо смещения влево <<, сдвиньте вправо >>

Я надеюсь, это кому-нибудь поможет.

0 голосов
/ 30 ноября 2010

непроверенный:

int left = max(0, move.x-5), right = min(width-1, move.x+5), top = max(0, move.y-5), bottom = min(width-1, move.y+5);    
// check the primary diagonal (top-left to bottom-right)
for (int x = left, y = top; x <= right && y <= bottom; x++, y++) {
    for (int count = 0; x <= right && y <= bottom && stones[x][y] == lastPlayer; x++, y++, count++) {
        if (count >= 5) return true;
    }
}
// check the secondary diagonal (top-right to bottom-left)
// ...
// check the horizontal
// ...
// check the vertical
// ...
return false;

альтернативно, если вам не нравятся вложенные циклы (непроверенные):

// check the primary diagonal (top-left to bottom-right)
int count = 0, maxCount = 0;
for (int x = left, y = top; x <= right && y <= bottom; x++, y++) {
    if (count < 5) {
        count = stones[x][y] == lastPlayer ? count + 1 : 0;
    } else {
        return true;
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...