Как я могу найти действительные слова в сетке символов?

12

Я создаю игру, похожую на Tetris, с двумя основными отличиями: экран уже начинает заполняться плитками (как в Puzzle Quest для Nintendo DS и ПК), и в каждой отдельной плитке есть буква. Цель игрока состоит в том, чтобы устранить плитки, формируя из них правильные слова. Слова формируются путем размещения букв рядом друг с другом, в любом направлении, кроме по диагонали.

Игрок может переместить целый ряд плиток влево или вправо или целый столбец плиток вверх или вниз на столько промежутков, сколько он пожелает (если движение ряда / столбца превышает пределы поля, буква, которая пересекает предел, будет «зацикливаться», появляясь на другом конце строки / столбца). После действий игрока игра должна проверить всю доску, чтобы найти правильные слова и удалить буквы, которые образуют эти слова с доски. Буквы над теми, которые были удалены, будут падать вместо тех букв, которые были удалены, и новые буквы будут падать с верхней части экрана, пока доска не заполнится снова.

Я уже написал линейный алгоритм, который, учитывая последовательность символов, определяет, является ли оно правильным английским словом. У меня проблема в следующем: как я могу проверить правильность слов на доске? Является ли грубая сила единственным способом? Тестирование всех возможных комбинаций на доске, чтобы увидеть, являются ли они действительными, очень медленное, даже для маленькой (5x5) доски. Любая помощь будет очень признательна, спасибо!

Tavio
источник
К сожалению, ты прав. Это очень медленно из-за цифр (все комбинации из 3, 4, 5, ... 25 букв). Может быть, ограничить его словами «слова должны быть выровнены по горизонтали или вертикали» для повышения производительности (а не для получения случайных слов, которые игрок не видел)?
ashes999
Я думаю, вам нужно снова взглянуть на ваш алгоритм, который соответствует последовательности символов в слова. По моим подсчетам, сетка 5x5 будет иметь 2700 потенциальных слов, которые должен пройти ваш алгоритм, см., Например, ответ Джоша.
Темыр
Я получаю 2700 слов следующим образом; начните с слов слева направо в первом ряду. Есть 1 позиция, состоящая из 5 букв, 2 4 букв, 3 3 букв, 4 2 букв и 5 1 букв. Мы можем обменять одну из букв в слове на букву из другого столбца. Мы можем, не теряя общности, предполагать, что никакие буквы не заменяются на 1-буквенные слова и что первая буква не заменяется на 2-буквенные слова. Это дает; 5 * 5 * 1 + 4 * 5 * 2 + 3 * 5 * 3 + 1 * 5 * 4 + 1 = 135. Умножить на количество строк и направлений; 135 * 5 * 4 = 2700
Темыр
Я думаю, что я не прояснил это, но слова могут быть сформированы в любом направлении, кроме по диагонали, и даже сделать углы (например, первый фрагмент из первого ряда, затем второй фрагмент справа в первом ряду, за которым следует плитка снизу на втором ряду).
Тавио
@Tavio Некоторые мысли: проверка должна идти вперед более длинными словами (если я выделю «в стороне», я не хочу «как». Кроме того, однобуквенные слова могут быть лучше проигнорированы, иначе вы никогда не сможете использовать любые символы «а»). Когда я закончу, я хотел бы знать, какое имя вы даете этой игре, чтобы я мог ее проверить
Дэвид Старки

Ответы:

22

Грубая сила не единственный путь.

Решение вашей игровой доски похоже на решение Boggle , но проще. Вы хотите проверить каждую плитку на доске, глядя, есть ли какие-нибудь слова, которые можно сделать в соответствующих направлениях.

Вы все еще хотели бы улучшить свое пространство поиска, чтобы не беспокоиться о поиске по направлению, если знаете, что не можете составить слово. Например, если вы найдете две qбуквы подряд, вам следует прервать операцию. Для этого вам понадобится какая-то структура данных, позволяющая определить, является ли данный набор символов префиксом правильного слова. Для этого вы можете использовать три или дерево префиксов; полезная структура данных при решении подобных задач.

Дерево префиксов - это иерархическая структура, основанная на узлах, где каждый узел представляет некоторый префикс своих дочерних элементов, а конечные узлы (как правило) представляют конечные значения. Например, если ваш словарь допустимых слов содержит слова «cat», «car» и «cell», то дерево может выглядеть так:

    0        The root of the trie is the empty string here, although you 
    |        can implement the structure differently if you want.
    c
   / \ 
  a   e
 / \   \
t  r    l
         \
          l

Таким образом, начните с заполнения префиксного дерева каждым действительным словом в вашей игре.

Фактический процесс поиска допустимых слов на доске в любой момент времени будет включать в себя запуск рекурсивного поиска по каждой клетке на доске. Поскольку каждый поиск в пространстве доски, начинающийся с некоторой данной плитки, является независимым, он может быть распараллелен при необходимости. Во время поиска вы «следите» за префиксным деревом, основываясь на значении буквы в том направлении, в котором вы ищете.

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

Пример кода и обсуждение этого метода (и других, таких как решение для динамического программирования, которое может быть еще быстрее за счет «инвертирования» пространства поиска после моды) можно найти в блоге этого сотрудника здесь ; он обсуждает решение Boggle, но адаптация решений к вашей игре более или менее зависит от изменения направления поиска.


источник
Грубая сила - не единственный способ, которым вы себя объяснили. :) Есть много префиксов, которые намекают, что нет смысла продолжать искать. (Большинство [случайных] строк не являются словами. +1
AturSams
Отличный ответ. «Слово» - это все, что находится в словаре игры.
Адам Эбербах
ОП заявляет, что у него есть алгоритм для сопоставления слова с символьной строкой. Поэтому я не думаю, что это отвечает на вопрос.
Темыр
OTOH Я думаю, что OP захочет более эффективный алгоритм сопоставления строк, чем тот, который он имеет в настоящее время.
Taemyr
1
@Taemyr, используя простой три, да. Но можно было бы использовать алгоритм Aho-Corasick, который использует слегка модифицированные три, гораздо более эффективный (линейный). С помощью алгоритма Aho-Corasick можно найти все действительные слова в матрице nxn за O (n ^ 2) времени.
el.pescado
3

Вы, возможно, попробовали это, уже реализовали это, возможно, лучше сопровождали другим ответом и т. Д. Но я не видел, чтобы они упоминали (пока), так что вот оно:

Вы можете отказаться от многих проверок, отслеживая, что изменилось, а что нет. Например:

On a 5x5 field, A vertical word is found on base of the third column,
All the rows change. However, the first, second, fourth, and fifth,
columns do not change, so you dont need to worry about them (the third did change.)

On a 5x5 field, A 3 letter word is found horizontally on row 2, column 3, to column 5.
So you need to check row 1 and 2 (row 1 because the words on that one
fell down and where replaced), as-well as columns 3, 4, and 5.

или, в псевдокоде

// update the board

// and check
if (vertical_word)
{
    check(updated_column)

    for (i in range 0 to updated_row_base)
        check(i)
}
else // horizontal word
{
    for (i in range 0 to updated_row)
        check(i)

    for (i in range 0 to updated_column_start)
        check(i)

    for (i in range updated_column_end+1 to final_column)
        check(i)
}

И тривиальные вопросы:

У вас установлена ​​оптимизация скорости компиляторов? (если вы используете один)

Можно ли вообще оптимизировать алгоритм поиска слов? В любом случае?

Вольфганг Скайлер
источник
За исключением того, что игрокам разрешено вращать ряды, поэтому поиск слова в третьем столбце повлияет на другие столбцы.
Темыр
@Taemyr IF(rowMoved){ checkColumns(); checkMovedRow(); } IF(columnMoved){ checkRows() checkMovedColumn();} Если пользователь может перемещаться только по одной за раз, то в конце этого перемещения никакие параллельные буквы не перемещаются и, следовательно, нет необходимости перепроверять их.
Дэвид Старки
2

Помните, что каждый символ - это ценность. Так что используйте это в ваших интересах. Есть несколько хеш-функций, которые можно быстро вычислить при итерации подстрок. Например, допустим, мы даем каждой букве 5-битный код (просто сделаем это c - 'a' + 1на C):

space = 00000;
a = 00001;
c = 00011;
e = 00101;
t = 01100;

Чем вы можете быстро перебрать все подстроки определенного размера:

a b [c a t] e t h e = 00011 00001 01100;
//Now we just remove the 5 msb and shfit the rest 5 bits left and add the new character.
a b  c [a t e] t h e = (([c a t] & 0xffff) << 5) | e; // == a t e

Таким образом, мы можем проверять подстроки длиной до 12 букв в большинстве современных архитектур.

Если в вашем словаре есть хэш-код, вы можете быстро извлечь это слово, потому что такие хэш-коды уникальны. Когда вы достигаете максимум 12 букв, вы можете добавить еще одну структуру данных для слов, начинающихся с этих 12 букв. Если вы найдете слово, которое начинается с определенных 12 букв, то просто создайте список или другую крошечную хеш-таблицу для суффиксов каждого слова, которое начинается с этого префикса.

Хранение словаря всех существующих кодов слов не должно занимать более нескольких мегабайт памяти.

AturSams
источник
0

Вы ограничены только классическими формами тетриса при формировании слов, или подойдет любая формация? Могут ли слова изгибаться бесконечно или только один раз? Может ли слово быть так долго, как оно хочет? Это довольно сложно, если вы можете делать столько изгибов, сколько хотите, эффективно создавая максимально длинные слова длиной 25 символов. Я бы предположил, что у вас есть список принятых слов. Исходя из этого, я предлагаю вам попробовать что-то вроде этого:

At the start of the game:
  Iterate tiles:
    Use tile as starting letter
      Store previous tile
      Check the four adjacent tiles
      If a tile can continue a word started by the previous tile, carry on
      Store the next tile
      Move check to next tile

Это создаст карту для каждой плитки с информацией о том, как эта плитка связана со словами вокруг нее в сетке. Когда столбец или строка перемещены, проверьте все плитки, которые были или находятся рядом с движением, и пересчитайте информацию. Когда вы найдете слово, и к нему больше нельзя будет добавлять плитки; убери это. Я не уверен, что это будет быстрее, это действительно сводится к тому, сколько слов наполовину создано. Преимущество этого состоит в том, что пользователь, скорее всего, пытается создать слово из наполовину полного слова на доске. Сохраняя все эти слова в памяти, легко проверить, было ли слово завершено.

PMunch
источник