web-dev-qa-db-ja.com

動的な無制限のサイズの「迷路」のデータ構造を構築するにはどうすればよいですか?

「迷路」が正しい用語かどうか、実際にはわかりません。基本的に、ユーザーは4つのドア(N、S、E、およびW)を持つ単一のRoomから始めます。彼らは任意の方向に行くことができ、後続の各部屋には、他の部屋に行く1から4の出入り口のある別の部屋が含まれます。

「迷路」のサイズは無制限で、部屋を移動するにつれて大きくなります。利用可能なRoomsの数は限られていますが、利用可能な数は動的であり、変更される可能性があります。

私の問題は、このタイプのパターンに最適なデータ構造がわからないことです

Roomオブジェクトの[X] [X]配列を使用することを最初に考えましたが、物事はあらゆる方向に拡大することになっており、「訪問された」部屋のみが想定されているので、それを避けたいと思います。構築する必要があります。

もう1つの考えは、各Roomクラスに、N、S、E、およびWの4つのリンクされたRoomプロパティを含め、以前のRoomにリンクすることでしたが、問題はそのため、隣接する部屋がすでに「構築済み」である部屋にユーザーが入るかどうかを識別する方法がわかりません

例えば、

 --- --- ---------- 
 | | | | 
開始5 4 
 | | | | 
 --- --- --- --- 
 --- --- ---------- --- --- 
 | | | | | | 
 | 1 2 3 
 | | | | | | 
 --- --- --- --- ---------- 
 

ユーザーがスタート> 1> 2> 3> 4> 5から移動する場合、Room#5は、Wに開始ルームが含まれていることを知っている必要があり、Sはルーム#2であり、この場合は使用できません。 Nは、新しいRoomまたは壁(なし)のいずれかです。

たぶん、配列とリンクされた部屋の組み合わせが必要なのかもしれません。あるいは、これを間違った方法で見ているだけかもしれません。

このタイプの「迷路」のデータ構造を構築するより良い方法はありますか?または、私は現在の思考プロセスで正しい軌道に乗っており、いくつかの情報が欠けているだけですか?

(興味がある場合は、プロジェクトは Munchkin Quest に非常に似たゲームです)

43
Rachel

各部屋の座標を指定し(開始は(0,0)になります)、生成された各部屋を座標ごとに辞書/ハッシュマップに格納します。

各部屋の隣接座標を決定するのは簡単です。これを使用して、部屋がすでに存在するかどうかを確認できます。ヌル値を挿入して、部屋が存在しないと既に判断されている場所を表すことができます。 (またはそれが不可能な場合は、ATM、部屋を含まない座標の別の辞書/ハスマップがわかりません)

44
Bubblewrap

ほとんどの場合、あなたが説明しているものはグラフのように聞こえます。いくつかの要件(拡大する側面)を考慮して、実装として 隣接リスト を選択します(他の一般的なオプションは 隣接マトリックス です)。

使用している言語はわかりませんが、.NETの隣接リストで実装されたグラフの実装の詳細withの優れたチュートリアル/説明は ここ です。

15
Steven Evers

実装に関するいくつかの考え(私は、マトリックスを構築するために他の多くの挑戦的な側面を持っているカルカソンヌを考えました-それは私が一度尋ねられたインタビューの質問でさえありました).

この構造について尋ねられるいくつかの質問があります:

  1. x、Yに部屋はありますか?
  2. 空の場所X、Yの部屋[N/S/E/W]はありますか?

単純なリストとグラフの問題

単純なリストの難しさは、特定の場所に何かを配置できるかどうかをテストするために、構造を歩く必要があることです。システムには、任意の場所O(1)にアクセスする方法が必要です。

考慮してください:

1 2 3 ? 4
5 . . * 6
7 8 9 A B

可能な場所 '?'の情報を見つけるには、3のとき、4に到達するためにずっと歩く必要があります。リンクの答えは、ジャンプするノードの数に関する追加情報を含みます(そのため、3は4にリンクされます) 「ジャンプ1」の追加情報を含む)は、6またはAから「*」の隣接を見つけるときに、依然として歩行が必要です。

シンプルで大きなアレイ

利用できる部屋の数には限りがあります

これがそれほど大きくない場合は、2N x 2Nの配列を作成し、そのままにしておきます。 100 x 100の配列は、10,000個のポインターと50個のオブジェクトのみです。配列に直接インデックスを付けます。

境界外のアクティビティが配列を常に制約内に移動する場合、これをNxNに減らすことができます。たとえば、配列をアンダーフローする部屋を追加する場合は、グリッドにすべての要素を1つの位置にシフトして、アンダーフローが発生しないようにします。この時点で、50の部屋の世界のサイズは、2500ポインタと50オブジェクトになります。

疎な配列と行列

これをより最適に作成するには、要素の大部分が0またはnullである sparse array を調べます。 2次元の場合、これは sparse matrix として知られています。多くのプログラミング言語には、この構造を操作するためのさまざまなライブラリがあります。ライブラリが整数に制限されている場合、この整数を部屋の固定配列へのインデックスとして使用できます。

私の好ましいアプローチは、部屋を構造体(隣接する部屋へのポインター、および座標)として持ち、部屋に、座標でハッシュされるハッシュテーブルからのポインターも持つことです。この状況では、空の部屋からどの部屋が[N/S/E/W]であるかを尋ねるために、ハッシュテーブルにクエリを実行します。ちなみに、これは疎行列を格納するための「DOK」フォーマットです。

9
user40980

これはどう..

各セルにそれぞれの隣接セルへのリンクを与えます。各セルにある種の名前を付けます(つまり、「0/0」、「-10/0」(0,0から始めると想定))。すべての名前を含むHashSetを追加します。別のセルに移動しようとするときは、ネイバーがHashSetにまだ存在していないことを確認してください。

また、別の部屋への開口部を作成した場合、それは部屋が存在することを意味しますか?したがって、どの部屋へのリンクもない空の部屋へのリンクを作成します。また、新しい部屋の隣人を確認することもできます。ドアが存在し、新しい部屋に開く場合は、その部屋へのドアを追加します。

   Empty   
   (0,1)        

---    ---            ----------
|        |            |        |
    0,0       1,0        2,0       Empty
|        |            |        |   (3,0)
---    --- ---------- ---    ---
|        | |        | |        |
|   0,-1      1,-1       2,-1      Empty
|        | |        | |        |   (3,-1)
---    --- ---    --- ----------

   Empty     Empty   
  (0,-2)    (1,-2)

HashSet = {0 | 0、1 | 0、2 | 0、3 | 0、0 | -1、1 | -1 ....} 1,0:W = 0,0/Door; 1,0:N = 1,1 /空; E = 2,0 /ドア; S = 1、-1 /壁

また、迷路がその方向に成長できるように、新しい部屋ごとに少なくとも1つの隣接していない(別の部屋に隣接する)ドアを付ける必要があります。

2
Paul

設計しているものは、グラフのように聞こえます。

A graph of the labyrinth

これらはしばしば状態のセットとして表されます[〜#〜] q [〜#〜]、初期状態q[〜#〜] q [〜#〜]、およびいくつかの遷移Δ。なので、このように保存することをお勧めします。

  • セット[〜#〜] q [〜#〜]{s、1、2、3、5、6}
  • 初期状態q:s
  • 遷移関係のマップΔ{s→1、s→5、1→2、2→3、3→4、4→5}

最も合理的な言語には、マップとセットの両方があります。

1
kba

あなたは4ウェイリンクリストを検討することができます...

Roomオブジェクトの[X] [X]配列を使用することを最初に考えましたが、物事はどの方向にも成長するはずであり、「訪問された」部屋のみを構築する必要があるため、それは避けたいと思います。

それでも[] []を使用できます。特に最初に追加する場合、動的な成長は遅くなる可能性がありますが、それほど悪くはないかもしれません。チェックするプロファイルを作成する必要があります。リンクされたリストやマップを操作しようとすると、実際には状況が悪化し、不定期ではなく一定のレベルになる場合があります。

遅延評価を実装することで、訪問済みの部屋のみを構築できます。部屋を含むオブジェクトを配置することができ、room()が呼び出されるまでその部屋は作成されません。その後は毎回同じものを返すだけです。難しいことではありません。

0
Edward Strange

「コンピュータでアドベンチャーゲームを作成する」という本を介して、これを行う方法を学びました。 BASICコードを掘り下げることを希望する場合(本はその古いものです)、ここを読んでください。

http://www.atariarchives.org/adventure/chapter1.php

ショートカットの場合、彼らがすることは部屋の配列を持ち、各配列の要素はあなたが行くことができる別の部屋へのポインターであり、0(最近は-1を使用します)ができないことを示しますそのように行きます。たとえば、部屋の構造(またはクラスまたはあなたが持っているもの)を作成します。

room {
 int north_dir;
 int south_dir;
 int west_dir;
 int east_dir;
 // All other variables and code you care about being attached to the rooms here
}

次に、配列またはリンクリストの構造を使用するか、部屋を管理する必要があります。配列スタイル(またはC++ベクトル)の場合、私はそのコードを使用し、方向はそれらがリンクする部屋のインデックスを保持します。リンクされたリストの場合、次の部屋へのポインタを使用できます。

他の人が言ったように、人々が部屋に入ったときに新しい部屋を動的に生成する必要がある場合は、それも行うことができます。

0
laaph

1つの構造ですべての問題を解決しようとしないでください。

他の部屋との関係ではなく、部屋に関するものを格納するように部屋オブジェクトを定義します。次に、1D配列、リストなどを好きなように拡張できます。

別の構造は「到達可能性」を保持できます。どの部屋に私がいる部屋からアクセスできるかです。次に、推移的な到達可能性を迅速に計算する必要があるかどうかを決定します。総当たり計算とキャッシュが必要になる場合があります。

早期の最適化は避けてください。本当にシンプルな構造と簡単に理解できるばかげたアルゴリズムを使用して、使用されるアルゴリズムを最適化します。

0
Julian