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


43

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

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

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

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

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

例えば、

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

ユーザーが[スタート]> [1]> [2]> [3]> [4]> [5]から移動する場合、Room#5はWが開始ルームを含み、Sはルーム#2であり、この場合は利用可能ではないこと、およびNは新規Roomまたは壁(なし)。

おそらく、アレイとリンクされた部屋を混在させる必要があるか、これを間違った方法で見ているだけかもしれません。

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

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


部屋はどの方向にも成長するため、どのような種類の配列も機能するとは思わない... [0,0]から開始して左に移動すると [-1、0]を試します。
ポール

@Paul行/列を追加し、すべての配列データをシフトしてから、新しいマップ配列に一致するようにすべてのプレーヤーの位置もシフトします。遅く、どれだけシフトする必要があるかによっては困難になる可能性がありますが、可能です。それでも、Bubblewrapの答えははるかに優れています。
イズカタ

私はおそらく間違っていますが、これはGameDev.SEの方が良いでしょうか?
動的

1
@Dynamicこれはデータ構造の質問なので、ここで問題なく収まります。
イズカタ

回答:


44

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

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


2
各Roomオブジェクトに他のRoomオブジェクトへの北東南西情報が含まれるようにします。そのため、辞書/ハッシュマップと部屋の基本方向の両方を使用して、迷路をナビゲートできます(絶対座標とRoomオブジェクトXの北側を知りたい場合があります。
ニール

2
@Paul私は、アイデアは部屋の辞書を作成することだと思います。新しいを作成するときRoomは、辞書で隣接する部屋を探し、Roomオブジェクトにリンクを追加してから、新しい部屋を残りの側面に追加します。したがって、辞書検索が行われるのは、新しいRoomオブジェクトを作成するときRooms
レイチェル

7
Roomオブジェクト内の他の部屋へのリンクを保存する理由はまったくありません。部屋(x,y)にいて北に移動したい場合(x,y+1)は、辞書で部屋を検索し、存在しない場合は作成します。辞書検索は非常に高速ですO(1)
カールビーレフェルト

3
@KarlBielefeldt:部屋@ (x,y+1)は存在するかもしれませんが、(x,y)北へ行くことはできません。つまり、エッジがから(x,y)に直接移動しない場合があり(x,y+1)ます。
スティーブンエバーズ

2
皆さんはこれを複雑にしています。これは基本的に配列と同じです。ただし、2次元配列ではなく辞書で検索する点が異なります。あなたがアレイで遭遇するどんな問題もそこにあるでしょう...しかし、彼はとにかくアレイを使用しようとしていました。
user606723

15

ほとんどの場合、説明しているものはグラフのように聞こえます。いくつかの要件(成長している側面)を考えると、おそらく実装として隣接リストを選択します(他の一般的なオプションは隣接行列になります)。

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


1
N / E / S / Wは実際にはグラフの概念の一部ではないため、これを説明するのにグラフだけでは十分ではありません。接続されている可能性があります。
エドワードストレンジ

3
@CrazyEddie:データ構造は特定のドメインに直接マッピングすることを意図したものではないことに注意してください(実際、それが彼らの利益です)。この特定の例では、グラフは方向性を持ち、エッジには列挙型で簡単に注釈を付けることができます。
スティーブンエバーズ

注釈は、東西、南北の関係をささいな方法で実施することができます。これにより、リンクが不良であるために、部屋Aから部屋Bに西に移動し、部屋Aの代わりに部屋Bから部屋Cに東に移動する問題がなくなります。
ピータースミス

3
プレイヤーをランダムな方向に10マステレポートすることで自分を守るウィザードが住む部屋を実装したいとしましょう。グラフベースのデータ構造でそのポイントを見つけるのは非常に高価です。特にその部屋が存在せず、その部屋をグラフ内の別の部屋にリンクするすべての部屋を生成する必要がある場合(構造では複数の部屋が許可されないため、ダンジョンの相互に切断されたセクション)。
マークブース

2
@MarkBoothですが、要件を変更しました。
ジョシュアドレイク

9

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

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

  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であるスパース配列を調べます。2次元の場合、これはスパース行列として知られています。多くのプログラミング言語には、この構造を操作するためのさまざまなライブラリがあります。ライブラリが整数に制限されている場合、この整数を部屋の固定配列へのインデックスとして使用できます。

私が好むアプローチは、部屋を構造として(隣接する部屋へのポインター、および座標)、部屋に座標でハッシュされるハッシュテーブルからのポインターを持たせることです。この状況では、空の部屋から[N / S / E / W]の部屋を尋ねるには、ハッシュテーブルを照会します。ちなみに、これはスパース行列を保存するための「DOK」形式です。


1
ニースの答えは、気泡緩衝材はかかわら示唆して、キーとして位置タプルを持つ辞書は疎行列を実装するために使用するための合理的な構造です。
マークブース

2

これはどう..

各セルに、それぞれの隣接セルへのリンクを与えます。各セルになんらかの名前を付けます(つまり、「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つの隣接しない(別の部屋に)ドアを与えるようにする必要があります。


1

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

迷宮のグラフ

これらは、多くの場合、状態の集合として表現されているQ、初期状態qは0Q、およびいくつかの遷移Δ。したがって、このように保管することをお勧めします。

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

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


0

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

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

そのために[] []を引き続き使用できます。特に最初に追加する場合、動的な成長は遅いかもしれませんが、それほど悪くないかもしれません。確認するにはプロファイルする必要があります。リンクされたリストまたはマップを操作しようとすると、実際にはさらに悪くなることがあります。

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


1
「4ウェイリンクリスト」とはどういう意味ですか。私が見つけることができた唯一のものはCodeProjectの記事で、それは要約して隣接リストになりました。
スティーブンエバーズ

0

これを行うには、「コンピューターでアドベンチャーゲームを作成する」という本を学びました。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

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

ルームオブジェクトを定義して、他のルームとの関係ではなく、ルームに関する情報を保存します。その後、1D配列、リストなどを自由に拡大できます。

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

早期の最適化を避けます。本当にシンプルな構造と簡単に理解できる愚かなアルゴリズムを使用し、使用するアルゴリズムを最適化します。

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