Як я можу знайти дійсні слова в сітці символів?


12

Я створюю гру, схожу на Tetris, з двома основними відмінностями: екран вже починається наповненим плитками (як у Puzzle Quest для Nintendo DS та PC), і кожна окрема плитка містить літеру. Завдання гравця - усунути плитки, утворюючи з ними дійсні слова. Слова утворюються, розміщуючи літери поруч, у будь-якому напрямку, крім діагоналі.

Гравець може перемістити цілий ряд плиток ліворуч або праворуч або цілий стовпчик плиток вгору або вниз на стільки пробілів, скільки він бажає (якщо рух рядка / стовпця перевищує межі дошки, лист, що перетинає межу, буде "циклічним", з'являючись в іншому кінці рядка / стовпця). Після дії гравця гра повинна перевірити всю дошку на пошук дійсних слів і видалити літери, що утворюють ці слова з дошки. Букви вище тих, що були вилучені, падатимуть на спад тих листів, які були вилучені, і нові літери опускатимуться вгорі екрану, поки дошка знову не заповниться.

Я вже написав лінійний алгоритм, який, задавши послідовність символів, визначає, чи це дійсне англійське слово. Проблема, яка у мене виникає, полягає в тому, як я можу перевірити правильність слів на дошці? Чи є груба сила єдиним способом? Тестування всіх можливих комбінацій з дошки, щоб побачити, чи дійсні вони, дуже повільно, навіть для невеликої (5x5) дошки. Будь-яка допомога буде дуже вдячна, дякую!


На жаль, ти маєш рацію. Це дуже повільно, через цифри (всі комбінації 3, 4, 5, ... 25 букв). Можливо, обмежте його "слова повинні бути горизонтально або вертикально вишиковані", щоб поліпшити продуктивність (а не отримати випадкові слова, які гравець не бачив)?
ashes999

Я думаю, вам потрібно ще раз переглянути свій алгоритм, який відповідає послідовності символів словам. За моїм рахунком, сітка 5х5 міститиме 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 , за винятком більш простого. Ви хочете перевірити кожну плитку на дошці, дивлячись, чи є слова, які можна скласти за відповідними вказівками.

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

Дерево префіксу - це структура, заснована на ієрархічному вузлі, де кожен вузол представляє деякий префікс своїх дітей, а вузли листя (як правило) представляють кінцеві значення. Наприклад, якщо у вашому словнику дійсних слів є "кішка", "машина" та "комірка", трие може виглядати так:

    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 Я думаю, що ОП захоче більш ефективний алгоритм узгодження рядків, ніж той, що він має.
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 літер, ніж просто створити список або іншу крихітну хеш-таблицю для суфіксів кожного слова, що починається з цього префікса.

Зберігання словника всіх існуючих кодів слів не повинно займати більше кількох мегабайт пам'яті.


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

Це створить карту на кожній плитці з інформацією про те, як ця плитка пов'язана зі словами навколо неї в сітці. При переміщенні стовпця чи рядка перевірте всі плитки, які були або є поруч із рухом, та перерахуйте інформацію. Коли ви знайдете слово, і більше цього плитка не можна додати до цього слова; видали це. Я не впевнений, якщо це буде швидше, це дійсно зводиться до того, скільки слів створено наполовину. Користь для цього полягає в тому, що користувач, швидше за все, намагається створити слово з половини повного слова на дошці. Зберігаючи всі ці слова, що зберігаються, легко перевірити, чи слово було заповнене.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.