Wie finde ich die gültigen Wörter in einem Zeichenraster?


12

Ich erstelle ein Tetris-ähnliches Spiel mit zwei Hauptunterschieden: Der Bildschirm ist bereits mit Kacheln gefüllt (wie in Puzzle Quest für Nintendo DS und PC) und auf jeder einzelnen Kachel befindet sich ein Buchstabe. Ziel des Spielers ist es, Kacheln zu eliminieren, indem er mit ihnen gültige Wörter bildet. Wörter werden gebildet, indem Buchstaben in einer beliebigen Richtung, außer diagonal, nebeneinander platziert werden.

Der Spieler kann eine ganze Reihe von Plättchen nach links oder rechts oder eine ganze Spalte von Plättchen nach oben oder unten verschieben, und zwar für so viele Felder, wie er möchte (wenn die Bewegung einer Reihe / Spalte die Grenzen des Spielfelds überschreitet) Ein Buchstabe, der die Grenze überschreitet, wird "durchlaufen" (erscheint am anderen Ende der Zeile / Spalte). Nach der Aktion des Spielers sollte das Spiel das gesamte Spielfeld nach gültigen Wörtern durchsuchen und die Buchstaben, die diese Wörter bilden, vom Spielfeld entfernen. Die Buchstaben über denen, die entfernt wurden, fallen an die Stelle der Buchstaben, die entfernt wurden, und neue Buchstaben fallen von oben auf den Bildschirm, bis die Tafel wieder gefüllt ist.

Ich habe bereits einen linearen Algorithmus geschrieben, der anhand einer bestimmten Zeichenfolge feststellt, ob es sich um ein gültiges englisches Wort handelt. Das Problem, das ich habe, ist: Wie kann ich nach gültigen Wörtern an der Tafel suchen? Ist rohe Gewalt der einzige Weg? Das Testen aller möglichen Kombinationen von der Karte aus, um festzustellen, ob sie gültig sind, ist sehr langsam, selbst für eine kleine (5x5) Karte. Jede Hilfe wird sehr geschätzt, danke!


Leider hast du recht. Aufgrund der Zahlen ist es sehr langsam (alle Kombinationen aus 3, 4, 5, ... 25 Buchstaben). Vielleicht auf "Wörter müssen horizontal oder vertikal ausgerichtet sein" beschränken, um die Leistung zu verbessern (und keine zufälligen Wörter zu erhalten, die der Spieler nicht gesehen hat)?
Asche999

Ich denke, Sie müssen sich Ihren Algorithmus noch einmal ansehen, der die Zeichenfolge mit den Wörtern vergleicht. Nach meiner Zählung hätte ein 5x5-Gitter 2700 mögliche Wörter, durch die Ihr Algorithmus blasen sollte, siehe z. B. Joshs Antwort.
Taemyr

Ich komme auf folgende Weise zu den 2700 Wörtern; Beginnen Sie mit den Worten von links nach rechts in der ersten Zeile. Es gibt 1 Position, an der ein 5-Buchstaben-Wort, 2 4-Buchstaben-Wörter, 3 3-Buchstaben-Wörter, 4 2-Buchstaben-Wörter und 5 1-Buchstaben-Wörter stehen. Wir können einen der Buchstaben im Wort gegen einen Buchstaben aus einer anderen Spalte austauschen. Wir können ohne Einschränkung der Allgemeinheit davon ausgehen, dass keine Buchstaben gegen Wörter mit einem Buchstaben ausgetauscht werden und dass der erste Buchstabe nicht gegen Wörter mit zwei Buchstaben ausgetauscht wird. Das gibt; 5 * 5 * 1 + 4 * 5 * 2 + 3 * 5 * 3 + 1 * 5 * 4 + 1 = 135. Mit der Anzahl der Zeilen und Richtungen multiplizieren. 135 * 5 * 4 = 2700
Taemyr

Ich glaube, ich habe das nicht klargestellt, aber Wörter können in jede Richtung, außer diagonal, geformt werden und sogar Ecken bilden (z. B. erstes Plättchen aus der ersten Reihe, dann zweites Plättchen rechts in der ersten Reihe, gefolgt vom Kachel von unten in der zweiten Reihe).
Tavio

@Tavio Einige Überlegungen: Beim Prüfen sollten zuerst längere Wörter verwendet werden (wenn ich "beiseite" mache, möchte ich nicht "als". Außerdem sollten Wörter mit einem Buchstaben besser ignoriert werden, da Sie sonst niemals in der Lage sein werden, a zu verwenden. Wenn Sie fertig sind, würde ich gerne wissen, welchen Namen Sie diesem Spiel geben, damit ich es überprüfen kann.
David Starkey

Antworten:


22

Brute Force ist nicht der einzige Weg.

Das Lösen Ihres Spielbretts ähnelt dem Lösen eines Boggle- Bretts, ist jedoch einfacher. Sie möchten jedes Feld auf der Tafel überprüfen und prüfen, ob Wörter vorhanden sind, die in die entsprechenden Richtungen geschrieben werden können.

Sie möchten Ihren Suchraum dennoch weiter verfeinern, damit Sie sich nicht die Mühe machen, in eine Richtung zu suchen, wenn Sie wissen, dass Sie kein Wort sagen können. Wenn Sie beispielsweise zwei qs hintereinander finden, sollten Sie den Vorgang abbrechen. Zu diesem Zweck benötigen Sie eine Datenstruktur, mit der Sie feststellen können, ob ein bestimmter Zeichensatz ein Präfix eines gültigen Wortes ist. Hierfür können Sie einen Trie- oder Präfixbaum verwenden. eine nützliche Datenstruktur bei der Lösung solcher Probleme.

Ein Präfixbaum ist eine hierarchische knotenbasierte Struktur, bei der jeder Knoten ein Präfix seiner untergeordneten Knoten darstellt und die Blattknoten (im Allgemeinen) die Endwerte darstellen. Wenn Ihr Wörterbuch gültiger Wörter beispielsweise "cat", "car" und "cell" enthält, sieht ein Versuch möglicherweise folgendermaßen aus:

    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

Füllen Sie daher zunächst einen Präfixbaum mit jedem gültigen Wort in Ihrem Spiel.

Der eigentliche Vorgang, zu einem bestimmten Zeitpunkt gültige Wörter auf der Tafel zu finden, beinhaltet das Starten einer rekursiven Suche von jeder Tafel auf der Tafel. Da jede Suche durch den Platinenraum ab einer bestimmten Kachel unabhängig ist, können diese bei Bedarf parallelisiert werden. Während Sie suchen, "folgen" Sie dem Präfixbaum basierend auf dem Wert des Buchstabens in der Richtung, in der Sie suchen.

Sie werden irgendwann einen Punkt erreichen, an dem keiner der umgebenden Buchstaben Ihrem aktuellen Präfixbaumknoten untergeordnet ist. Wenn Sie an diesem Punkt angelangt sind und der aktuelle Knoten ein Blatt ist, haben Sie ein gültiges Wort gefunden. Andernfalls haben Sie kein gültiges Wort gefunden und können die Suche abbrechen.

Beispielcode und eine Diskussion über diese Technik (und andere, wie beispielsweise eine dynamische Programmierlösung, die durch "Invertieren" des Suchraums auf eine bestimmte Weise noch schneller sein kann) finden Sie auf dem Blog dieses Kollegen hier . Er diskutiert das Lösen von Boggle, aber das Anpassen der Lösungen an Ihr Spiel hängt mehr oder weniger davon ab, in welche Richtungen Sie suchen lassen.


Brute Force ist nicht der einzige Weg, wie Sie sich selbst erklärt haben. :) Es gibt viele Präfixe, die andeuten, dass es keinen Grund gibt, weiter zu suchen. (Die meisten [zufälligen] Zeichenfolgen sind keine Wörter. +1
AturSams

Gute Antwort. Ein "Wort" ist alles im Wörterbuch des Spiels Punkt.
Adam Eberbach

OP gibt an, dass er einen Algorithmus hat, um das Wort mit einer Zeichenkette abzugleichen. Ich glaube nicht, dass dies die Frage beantwortet.
Taemyr

OTOH Ich denke, OP wird einen effizienteren String-Matching-Algorithmus wollen als den, den er derzeit hat.
Taemyr

1
@ Taemyr mit einfachen Trie, ja. Aber man könnte einen Aho-Corasick-Algorithmus verwenden, der leicht modifizierte Trie verwendet, ist viel effektiver (linear). Mit dem Aho-Corasick-Algorithmus kann man alle gültigen Wörter in der nxn-Matrix in O (n ^ 2) -Zeit finden.
el.pescado

3

Sie haben dies vielleicht versucht, dies bereits implementiert, vielleicht besser durch eine andere Antwort usw. begleitet. Aber ich habe sie (noch) nicht erwähnt, also hier ist es:

Sie können viele Schecks verwerfen, indem Sie nachverfolgen, was sich geändert hat und was nicht. Beispielsweise:

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.

oder im Psudo-Code

// 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)
}

Und die trivialen Fragen:

Haben Sie die Geschwindigkeitsoptimierungen des Compilers eingestellt? (falls Sie eine verwenden)

Kann Ihr Wortsuchalgorithmus überhaupt optimiert werden? In irgendeiner Weise?


Mit der Ausnahme, dass Spieler Zeilen drehen dürfen, wirkt sich das Finden eines Wortes in der dritten Spalte auf die anderen Spalten aus.
Taemyr

@Taemyr IF(rowMoved){ checkColumns(); checkMovedRow(); } IF(columnMoved){ checkRows() checkMovedColumn();} Wenn ein Benutzer immer nur einen nach dem anderen verschieben kann, wurden am Ende dieses Verschiebens keine parallelen Buchstaben verschoben und müssen daher nicht erneut überprüft werden.
David Starkey

2

Denken Sie daran, dass jedes Zeichen ein Wert ist. Nutzen Sie das also zu Ihrem Vorteil. Es gibt einige Hash-Funktionen, die beim Durchlaufen von Teilzeichenfolgen schnell berechnet werden können. Nehmen wir zum Beispiel an, wir geben jedem Buchstaben einen 5-Bit-Code (machen Sie einfach c - 'a' + 1in C):

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

Dann könnten Sie schnell über alle Teilstrings einer bestimmten Größe laufen:

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

Auf diese Weise können wir Teilzeichenfolgen mit bis zu 12 Buchstaben auf den meisten heute üblichen Architekturen überprüfen.

Wenn in Ihrem Wörterbuch ein Hash-Code vorhanden ist, können Sie das Wort schnell von dort abrufen, da Hash-Codes wie dieser eindeutig sind. Wenn Sie das Maximum von 12 Buchstaben erreichen, möchten Sie möglicherweise weitere Datenstrukturen für Wörter hinzufügen, die mit diesen 12 Buchstaben beginnen. Wenn Sie ein Wort finden, das mit 12 Buchstaben beginnt, erstellen Sie einfach eine Liste oder eine andere kleine Hash-Tabelle für die Suffixe jedes Wortes, das mit diesem Präfix beginnt.

Das Speichern eines Wörterbuchs aller vorhandenen Wortcodes sollte nicht mehr als einige Megabyte Speicherplatz beanspruchen.


0

Beschränken Sie sich beim Bilden von Wörtern nur auf die klassischen Tetris-Formen, oder reicht eine Formation aus? Können sich Wörter auf unbestimmte Zeit oder nur einmal verbiegen? Kann ein Wort so lang sein, wie es will? Dies wird ziemlich komplex, wenn Sie so viele Biegungen machen können, wie Sie möchten, sodass die längstmöglichen Wörter 25 Zeichen lang sind. Ich würde davon ausgehen, dass Sie eine Liste der akzeptierten Wörter haben. Ausgehend von dieser Annahme schlage ich vor, dass Sie Folgendes versuchen:

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

Dadurch wird auf jeder Kachel eine Karte erstellt, die Informationen darüber enthält, wie diese Kachel mit den Wörtern im Raster verbunden ist. Wenn eine Spalte oder Zeile verschoben wird, überprüfen Sie alle Kacheln, die sich neben der Bewegung befanden oder befanden, und berechnen Sie die Informationen neu. Wenn Sie ein Wort gefunden haben und diesem Wort keine Kacheln mehr hinzugefügt werden können; entfernen Sie es. Ich bin mir nicht sicher, ob dies schneller sein wird, es läuft wirklich darauf hinaus, wie viele Wörter zur Hälfte erstellt werden. Dies hat den Vorteil, dass der Benutzer höchstwahrscheinlich versucht, ein Wort aus einem halbfertigen Wort an der Tafel zu erstellen. Indem Sie alle diese Wörter speichern, können Sie leicht überprüfen, ob ein Wort vollständig ist.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.