Minecraftのクラフトグリッドのようなものを実装するにはどうすればよいですか?


55

Minecraftクラフトシステムは、2x2または3x3グリッドを使用します。材料をグリッドに配置し、適切な材料を適切なパターンで配置すると、レシピがアクティブになります。

設計に関するいくつかの興味深い点:

  • 一部のレシピでは、特定の材料を他の材料と交換できます。たとえば、ピックは柄に棒を使用し、木製の板、玉石、鉄のインゴット、金のインゴット、またはダイヤモンドの宝石を頭に使用する場合があります。
  • パターン内の相対位置は重要であり、グリッド上の絶対位置ではありません。つまり、スティックと石炭(または木炭)を3x3グリッドの6つの位置のいずれかに正しいパターンで配置することにより、トーチを作成できます。
  • パターンは水平方向に反転する場合があります。

私はそれを考え直しているかもしれませんが、これは興味深い検索/セット削減の問題のようです。それで、アルゴリズム的に言えば、これはどのように機能しますか(またはできますか)?


6
素晴らしい質問です!私はとても興味があります!私はすでにいくつかのアイデアを持っていますが、家に帰ったらすぐにそれらを共有します。
jcora

私は実際に「可能な限り近い」Minecraftのコピーを作成しようとしています。私はすべてのインベントリとレシピマネージャーのものをすでに書きました、そしてそれは非常にきちんと働いていると言わなければなりません。きれいなコードに満足しています。しかし、それを書くのは簡単ではありませんでした。レシピ、グリッド、インベントリなどがすべてうまく機能するように、約10のクラスを作成しました。時間があれば、答えを書きます。
マーティンコートー

Minecraftが作成レシピをどのようにコーディングするかを確認する必要があります。
Bradman175

回答:


14

別の解決策は、やや複雑なツリーを使用することです。ツリーのブランチノードは、レシピを繰り返し処理して作成されます(もう一度を使用for (y) { for (x) })。これは、ストック標準の書籍ごとのツリー構造です。最終ノードには、寸法をレシピにマップする追加の構造(Dictionary/ HashMap)が含まれます。

基本的にあなたがしようとしているのはこれです:

レシピツリー

黒いノードはアイテムの種類を示すブランチです。赤いノードはサイズ(方向)を区別できるリーフ(ターミネーター)です。

このツリーを検索するには、最初にバウンディングボックスを見つけ(最初の回答で説明したとおり)、その後、ツリーを移動するのと同じ順序でノードを繰り返します。最後に、Dictionaryまたはでディメンションを検索するだけHashMapで、レシピの結果が得られます。

キックのためだけに行ってこれ実装しました -これはおそらく私の答えを明確にするでしょう。また、私はこれが異なる答えであることを理解しています-そして当然のことながら、それは別の解決策です。


とても簡単です。私はそれが好きです。
デビッドエイク

私はこれを受け入れます。なぜなら、私は問題の中心にある木、ハッシュ、図が好きだからです。図を見ると、私はあなたのアルゴリズムをすぐに理解しました。
デビッドエイク

23

Minecraftは非常に少数の可能なレシピのみを使用するため、それほどスマートなものは必要ありません。

つまり、適合する最小のグリッドを見つけることです(つまり、空の行と列を無視して、2x2、3x3、または2x3(ドア)かどうかを確認します)。次に、そのサイズのレシピのリストをループし、アイテムタイプが同じかどうか(つまり、Minecraftでアイテムとブロックに整数タイプIDを使用するため、最悪でも9つの整数比較)をチェックし、一致するものが見つかったら停止します。

この方法により、アイテムの相対位置も無関係になります(トーチはクラフトグリッド上のどこにでも配置でき、ほとんど空の3x3ボックスではなく1x2ボックスとして表示されるため機能します)。

大量のレシピがあるため、一致する可能性があるものを線形検索するのに時間がかかる場合、リストを並べ替えてバイナリ検索を実行することができます(O(log(N))vs O(N))。これにより、リストの作成に余分な作業が発生しますが、これは起動時に一度実行し、その後メモリに保存できます。

最後に、レシピを水平方向に反転させるのが最も簡単なのは、ミラーバージョンをリストに追加するだけです。

2つ目のレシピを追加せずに実行したい場合は、入力レシピの[0,0]に[0,2]よりも高いIDの項目があるかどうかを確認できます(または2x2の場合は[0,1]は不要です) 1x2で、もしそうならそれをミラーリングし、最後に到達するまで次の行のチェックを続けないこれを使用して、レシピが正しいローテーションで追加されることを確認する必要があります。


1
Minecraftの少数のレシピが線形検索に役立たないのではないかと思いました。
デビッドエイク

1
それは基本的に、上で書いたものです(バイナリ検索の最適化が可能です)。ただし、トーチを作成するためのいくつかのオプションがあり、基本的にどこでも使用できます。最初にレシピのサイズを取得することにより、トーチ用の6つのレシピを追加する必要がなく、1ステップで正しいサイズのレシピのみに検索を制限します。-基本的に、ポイント2のオプションはサイズごとにグループ化するか、レシピをコーナーに移動するか、重複するレシピを追加します。
エルバ

15

3x3グリッドを文字列としてエンコードし、正規表現一致を使用する場合、特定のグリッド構成が特定のレシピに一致するかどうかを確認するのは簡単です。ルックアップの高速化は別の問題です。これについては最後に説明します。詳細についてはお読みください。

手順1)グリッドを文字列としてエンコードする

各タイプのセルにchar idを指定し、すべてをこの順序で並べて連結します。

123
456 => 123456789
789

さらに具体的な例として、Wが木材を表し、Eが空のセルであるスティックレシピを考えます(空の文字 ''を使用できます)。

EEE
WEE => EEEWEEWEE
WEE

手順2)正規表現を使用したレシピの一致(または文字列。データの処理が少し含まれています)

上記の例を続けると、フォーメーションを移動しても、文字列にはまだパターンがあります(両側にEが埋め込まれたWEEW):

EEW
EEW => EEWEEWEEE
EEE

したがって、スティックをどこに動かしても、次の正規表現と一致します。 /^E*WEEWE*$/

正規表現を使用すると、前述の条件付き動作も実行できます。たとえば、レシピを作成した場合、鉄または石で作られたつるはしに同じ結果が必要な場合、つまり:

III    SSS
EWE or EWE
EWE    EWE

両方を正規表現に結合できます。 /^(III)|(SSS)EWEEWE$/

水平反転も同様に簡単に追加できます(|演算子も使用)。

編集: とにかく、正規表現の部分は厳密には必要ありません。問題を単一の式にカプセル化する方法の1つにすぎませんが、変数の場所の問題については、任意のパディングスペース(またはこの例ではE)のグリッド文字列をトリミングして、String.Contains()を実行できます。また、複数成分の問題またはミラー化されたレシピの場合、それらすべてを同じ出力を持つ複数の(つまり、別々の)レシピとして処理できます。

ステップ3)ルックアップの高速化

検索を減らすために、レシピをグループ化し、検索を支援するためのデータ構造を作成する必要があります。グリッドを文字列として扱うことには、ここでもいくつかの利点があります

  1. レシピの「長さ」は、最初の空でないキャラクターと最後の空でないキャラクターの間の距離として定義できます。シンプルなTrim().Length()情報でこの情報が得られます。レシピは長さでグループ化し、辞書に保存できます。

    または

    「長さ」の代替定義は、空でない文字の数です。他に何も変わりません。この基準でレシピをグループ化することもできます。

  2. ポイント1が十分でない場合、レシピは、レシピに表示される最初の材料のタイプによってさらにグループ化される場合があります。これは、実行するのと同じくらい簡単ですTrim().CharAt(0)(そして、Trimが空の文字列になることを防ぐ)。

したがって、たとえば、レシピを次の場所に保存します。

Dictionary<int, Dictionary<char, List<string>>> _recipes;

そして、次のようなものとしてルックアップを実行します。

// A string encode of your current grid configuration 
string grid;

// Get length and first char in our grid
string trim = grid.Trim();
int length = trim.Length();
char firstChar = length==0 ? ' ' : trim[0];

foreach(string recipe in _recipes[length][firstChar])
{
    // Check for a match with the recipe
    if(Regex.Match(grid, recipe))
    {
        // We found a matching recipe, do something with it
    }
}

3
これは...非常にスマートです。
Raveline

18
問題があり、正規表現を使用して解決したいですか?現在、2つの問題があります:)
Kromsterによると、サポートMonica

2
@Krom私はあなたがおそらく冗談を言っているだけだと思う​​が、そのコメントの背後にある理由は何か?正規表現の使用は、データのセット(グリッドの内容)のマッチングを実行するための簡潔(コードと1行のレシピとのグリッドの一致)で柔軟(この問題のすべての要件に適合)な方法です。独自のマッチングアルゴリズムをローリングすることに大きな欠点はないと思います。また、プロセス全体を簡素化します。
デヴィッドゴー

2
@DavidGouveia、これは正規表現にあまり慣れていない人のための単なるフレーズです。正規表現の問題を解決することにした場合、2つの問題があります。(1)解決したい実際の問題(2)その問題を解決するための正規表現パターンの記述
iamserious

2
「今、2つの問題があります」に戻る-正規表現プロセッサがUnicodeをサポートしており、特定の組み合わせが正規表現NFAコンパイラをトリップしないことを保証できない限り、ASCII印刷可能範囲よりも多くのアイテムがあるとすぐに正規表現に問題が発生しますアップ。パターンを一致させるために、独自のDFAを(実際には非常に簡単に)一緒に投げることができます。しかし、プロトタイプ作成中は正規表現が最適な方法です。
ジョナサンディキンソン

3

Minecraftの仕組みを説明することはできませんが、MCP(Minecraftの合法的なコピーをお持ちの場合)をご覧になればきっとわかるはずです。

これを次のように実装します。

  1. ディスクベースの辞書/ハッシュマップ(B + Tree / SQL-Light)があります-値はレシピ結果のアイテムIDになります。
  2. ユーザーが何かを作成しているときは、項目INDEPENDENTLYを持つ最初の行/列を見つけるだけです。それらを組み合わせてオフセットします。
  3. アイテムを含む最後の行/列を個別に検索します。
  4. アイテムから幅と高さを計算し、キーに追加します。
  5. 計算したバウンディングボックスの範囲をループし、各アイテムのIDを追加します(通常のを使用for (y) { for (x) })。
  6. データベースでキーを検索します。

たとえば、2つの成分があるとします。XとY、および空白である*。次のレシピを使用してください。

**X
**Y
**Y

まず、境界ボックスを作成して、を生成し(2,0)-(2,2)ます。したがって、キーは次のようになります[1][3](幅1、高さ3)。次に、バウンディングボックス内の各アイテムをループし、IDを追加します。したがって、キーがなり[1][3][X][Y][Y]ます。次に、これを辞書/ DBで検索し、そのレシピの結果を取得します。

ステップ2の独立性をより明確に説明するには、次のレシピを検討してください。

*XX
XXX
XX*

一番上/左は明らかに0,0です-ただし、通常発生する最初の項目は、ループに応じて0,1または1,0のいずれかになります。ただし、最初の空でない列と最初の空でない行を見つけ、それらの座標を結合すると、0,0が得られます。同じプリンシパルが境界ボックスの下/右に適用されます。


Bukkitリポジトリは、任意のMinecraftのコードを持っていない、あなたは、必要があると思いMCP(とのMinecraftのコピーを)
BlueRaja -ダニーPflughoeft

これは他の答えの逆ではありませんか?
デビッドエイク

@DavidEyk(これが私の最初の答えです)実際には、ハッシュマップ構造により依存しています(bigtableに似ているかメモリ内にあるか)。もう一度答えたのは、少なくともメモリ内ハッシュマップで、パフォーマンスの低下につながるハッシュ衝突が心配だったからです。私の他の答えは、より多くのアイテムとメモリ内の構造に対してより良く機能します-SQL / etcにある場合、これはより良く機能します。
ジョナサンディキンソン

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