文字のグリッドで有効な単語を見つけるにはどうすればよいですか?


12

私はテトリスに似たゲームを作成していますが、主な違いは2つあります。画面は既にタイルで満たされ始め(ニンテンドーDSやPCのパズルクエストのように)、個々のタイルには文字があります。プレーヤーの目的は、タイルを使用して有効な単語を形成することでタイルを排除することです。単語は、対角線以外の任意の方向に文字を隣同士に配置することによって形成されます。

プレーヤーは、タイルの行全体を左または右に移動したり、タイルの列全体を上下に移動したりすることができます(行/列の移動がボードの制限を超える場合、制限を超える文字は「循環」し、行/列のも​​う一方の端に表示されます)。プレイヤーのアクションの後、ゲームはボード全体をチェックして有効な単語を探し、ボードからそれらの単語を形成する文字を削除する必要があります。削除された文字の上の文字は、削除された文字の場所に落ち、ボードが再びいっぱいになるまで新しい文字が画面の上部からドロップします。

文字のシーケンスを指定して、それが有効な英語の単語であるかどうかを判断する線形アルゴリズムを既に作成しました。私が抱えている問題は、ボード上の有効な単語を確認するにはどうすればよいですか?ブルートフォースが唯一の方法ですか?ボードから可能なすべての組み合わせをテストして、それらが有効かどうかを確認するのは、小さな(5x5)ボードでも非常に遅いです。どんな助けでも大歓迎です、ありがとう!


残念ながら、あなたは正しい。数字(3、4、5、... 25文字のすべての組み合わせ)のため、非常に遅いです。パフォーマンスを改善するために、「単語を水平または垂直に並べる必要がある」に制限することもできます(プレーヤーが見なかったランダムな単語を作成しないでください)。
ashes999

文字のシーケンスを単語に一致させるアルゴリズムをもう一度検討する必要があると思います。私の考えでは、5x5グリッドには2700個の潜在的な単語があり、これはアルゴリズムで吹き飛ばされるはずです。たとえば、ジョシュの答えを参照してください。
Taemyr

次の方法で2700ワードに到達します。最初の行の左から右の単語から始めます。5文字の単語、2 4文字の単語、3 3文字の単語、4 2文字の単語、5 1文字の単語という1つの位置があります。単語の文字の1つを別の列の文字と交換できます。一般性を失うことなく、1文字の単語と文字が交換されず、最初の文字が2文字の単語と交換されないと仮定できます。これは与える; 5 * 5 * 1 + 4 * 5 * 2 + 3 * 5 * 3 + 1 * 5 * 4 + 1 = 135。行数と方向で乗算します。135 * 5 * 4 = 2700
テミール

私はこれを明確にしなかったと思いますが、言葉は斜めを除いて任意の方向に形成でき、角を作ることさえできます(たとえば、最初の列の最初のタイル、次に最初の列の右側の2番目のタイル、下から2列目のタイル)。
タビオ

@Tavioいくつかの考え:チェックは最初に長い単語を実行する必要があります( "脇"にした場合、 "as"は不要です。また、1文字の単語は無視した方がよい場合があります。終了したら、私はそれをチェックアウトすることができますので、私はあなたがこのゲームを与えるの名前を知っていると思います。
デビッド・スターキーに

回答:


22

ブルートフォースが唯一の方法ではありません。

ゲームボードの解法は、単純な点を除き、Boggleボードの解法に似ています。ボード内のすべてのタイルをチェックして、適切な方向に沿って作成できる単語があるかどうかを確認します。

検索スペースをさらに絞り込んで、単語を作成できないことがわかっている場合でも、その方向に沿って検索する必要はありません。たとえば、2つqのsが連続して見つかった場合、中止する必要があります。そのためには、特定の文字セットが有効な単語の接頭辞であるかどうかを判断できるようなデータ構造が必要になります。このために、トライ、またはプレフィックスツリーを使用できます。このような問題を解決するときに役立つデータ構造。

プレフィックスツリーは階層的なノードベースの構造であり、すべてのノードがその子のプレフィックスを表し、リーフノードが(一般的に)最終的な値を表します。たとえば、有効な単語の辞書に「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

素晴らしい答え。「単語」とは、ゲームの辞書にあるすべての意味です。
アダムエーベルバッハ

OPは、単語を文字列に一致させるアルゴリズムを持っていると述べています。だから、これが質問に答えるとは思わない。
Taemyr

OTOH OPは、現在持っているものよりも効率的な文字列照合アルゴリズムが必要だと思います。
Taemyr

1
@Taemyr、プレーントライを使用します、はい。しかし、わずかに修正されたトライを利用するAho-Corasickアルゴリズムを使用すると、はるかに効果的です(線形)。Aho-Corasickアルゴリズムを使用すると、O(n ^ 2)時間でnxn行列のすべての有効な単語を見つけることができます。
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)
}

そして些細な質問:

コンパイラの速度の最適化を設定していますか?(使用している場合)

単語検索アルゴリズムをまったく最適化できますか?とにかく?


プレーヤーが行を回転できることを除いて、3番目の列で単語を見つけると、他の列に影響します。
Taemyr

@Taemyr IF(rowMoved){ checkColumns(); checkMovedRow(); } IF(columnMoved){ checkRows() checkMovedColumn();} ユーザーが一度に1つしか移動できない場合、その移動の終了時に、平行する文字は移動していないため、それらを再確認する必要はありません。
デビッドスターキー

2

すべての文字が値であることを忘れないでください。だからあなたの利益にそれを使用します。部分文字列を反復処理するときにすばやく計算できるハッシュ関数がいくつかあります。たとえば、すべての文字に5ビットのコードを付けるとしましょう(c - 'a' + 1Cで行います)。

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

これにより、各タイルにマップが作成され、このタイルがグリッド内の周囲の単語にどのように接続されているかが示されます。列または行が移動されたら、移動された、または移動に隣接しているすべてのタイルを確認し、情報を再計算します。単語を見つけて、その単語にこれ以上タイルを追加できない場合。それを除く。これがもっと速くなるかどうかはわかりませんが、実際には半分の単語がいくつ作成されるかがわかります。これの利点は、ユーザーがボード上の完全な単語から単語を作成しようとしている可能性が高いことです。これらの単語をすべて保存しておくと、単語が完成したかどうかを簡単に確認できます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.