2

C++ を使用して、再帰的に六角形のグリッドを作成しています (乗算リンク リスト スタイルを使用)。隣接するタイルを簡単に作成できるように設定しましたが、再帰的に行っているため、特定のタイルに対して 6 つの隣接タイルすべてを実際に作成することしかできません。明らかに、これにより重複したタイルが作成されており、何らかの方法でそれらを取り除こうとしています. クラスを使用しているため、null ポインターのチェックが機能していないようです。Tile クラスから int への変換に失敗しているか、何らかの方法で変換しても適切に変換されていません。作成時にすべてのポインターを明示的に NULL に設定していますが、それがまだあるかどうかを確認すると、初期化以来一度も触れていないにもかかわらず、そうではないと表示されます。これを行うための特定の方法はありますか?なんらかの NULL なしではグリッドをトラバースすることさえできません

これが私の関連コードの一部です。はい、恥ずかしいのはわかっています。

タイル クラス ヘッダー:

class Tile
{
public:
    Tile(void);
    Tile(char *Filename);
    ~Tile(void);

    void show(void);
    bool LoadGLTextures();
    void makeDisplayList();
    void BindTexture();
    void setFilename(char *newName);

    char Filename[100];
    GLuint texture[2];
    GLuint displayList;
    Tile *neighbor[6];
    float xPos, yPos,zPos;
};`

タイルの初期化:

Tile::Tile(void)
{
    xPos=0.0f;
    yPos=0.0f;
    zPos=0.0f;
    glEnable(GL_DEPTH_TEST);
    strcpy(Filename, strcpy(Filename, "Data/BlueTile.bmp"));
    if(!BuildTexture(Filename, texture[0]))
        MessageBox(NULL,"Texture failed to load!","Crap!",MB_OK|MB_ICONASTERISK);

    for(int x=0;x<6;x++)
    {
        neighbor[x]=NULL;
    }
}

隣接するタイルの作成:

void MakeNeighbors(Tile *InputTile, int stacks)
{
    for(int x=0;x<6;x++)
    {
        InputTile->neighbor[x]=new Tile();
        InputTile->neighbor[x]->xPos=0.0f;
        InputTile->neighbor[x]->yPos=0.0f;
        InputTile->zPos=float(stacks);
    }
    if(stacks)
    {
        for(int x=0;x<6;x++)
            MakeNeighbors(InputTile->neighbor[x],stacks-1);
    }
}

最後に、グリッドをトラバースします。

void TraverseGrid(Tile *inputTile)
{
    Tile *temp;
    for(int x=0;x<6;x++)
        if(inputTile->neighbor[x])
        {
            temp=inputTile->neighbor[x];
            temp->xPos=0.0f;
            TraverseGrid(temp);
            //MessageBox(NULL,"Not Null!","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
        }

}

重要な行は「if(inputTile->neighbor[x])」であり、「if(inputTile->neighbor[x]==NULL)」にするか、何をするにしても、適切に処理されません。ああ、リストを完全に設定していないことも承知しています。今は一方向だけです。

4

5 に答える 5

10

六角形のグリッドを作成したい場合は、通常のグリッドを使用して簡単にシミュレートできることを覚えておく必要があります。

    __    __    __
\__/2 \__/4 \__/6 \
/1 \__/3 \__/5 \__/
\__/8 \__/10\__/12\
/7 \__/9 \__/11\__/
\__/  \__/  \__/  \

これにより、人生がはるかに簡単になります:)

したがって、最も簡単な方法は

  1. 一時的な正方形のグリッドを設定するm*n
  2. タイルで埋める
  3. グリッドを横断して適切に接続する

上の図に基づく接続:

A) Connect to previous and next [x-1,y], [x+1,y]
B) Connect to row above and row below [x,y-1], [x,y+1]
C) Connect to row above previous and next [x-1,y-1], [x+1,y-1]

...そして、必要なすべての接続ができました(タイルが端にないかどうかを判断するために境界を確認することを忘れないでください)-別の方法でタイルを保持すると、グリッドを削除することもできます:)。

于 2010-05-25T01:09:31.637 に答える
1

創造。 再帰は、いくつかの問題を解決するためのきちんとした洗練された方法ですが、すべての問題に最適というわけではありません。ノードを作成するための純粋に再帰的なソリューションは、 Kornel Kisielewicz の単純な反復ソリューションよりもはるかに複雑になる (つまり洗練されていない) と思います。これは、コンストラクターが、既に存在するノードの再作成を避けるために、すぐ近くにあるすべてのタイルのレイアウトを知る必要があるためです。Tile

トラバーサル。 ノードトラバーサルコードの主な問題は、すべてのノードが最終的にその親に「トラバース」してサイクルを再開するため、無限ループに陥ってスタックを吹き飛ばすという点で似ています。すべてのタイルに一度だけアクセスしようとしていると思いますよね?その場合TraverseGrid()、ノードに入る方向を示すパラメーターが必要です。これにより、その方向に戻るのを避けることができます。

しかし、それだけでは十分ではありません。どちらの方向に進むかを決定する際には、より多くの規律が必要です。入った方向以外のすべての方向に単純に広げても、3 つの隣接するタイルが無限に循環するため、無限ループとスタック オーバーフローに陥ります。これを再帰的に行うには、どの戦略が各ノードを 1 回だけ訪問することになるかをよく考える必要があります。

1 つの可能性は、署名をTraverseGrid()toに変更してからTraverseGrid(Tile *inputTile, int fromDir, bool leftmost)、次の規則を使用することです。

  • 左上から入った場合は、右上にのみトラバースし、 を渡しleftmost = falseます。
  • 左下または右上から入力した場合は、 を通過して右下のみにトラバースしますleftmost = false
  • leftmostであり、左下にノードがある場合は、そのノードまでトラバースし、 を渡しleftmost = trueます。

もちろんfromDir、 とleftmostを組み合わせて 1 つのパラメーターにすることもできますが、これで一般的な考え方がわかります。

別の代替手段はvisited、各タイルにフラグを保持し、そのタイルに移動する前にチェックすることです。次に、トラバーサルはフラッド フィルになります。しかし、繰り返しますが、単純な反復トラバーサルは、はるかに単純で理解しやすい可能性が高く、一定のスタック スペースを使用するという追加の利点があります。

于 2010-05-25T01:52:02.190 に答える
1

私は MakeNeighbors() が何をするかを推測しているだけですが、やみくもに を行う代わりにInputTile->neighbor[x]=new Tile();、新しいものを作成して初期化する前に、neighbor[x] が非 NULL かどうかを確認できます。たとえば、その親がそれを作成し、すべての近隣情報を設定する場合、その親を作成しに行くべきではありません。

親が子を作成するとき、親は子の他の隣人を認識している限り、それらを適切に定義する必要があります。したがって、child[i] も child[i-1] および child[i+1] の隣人であることを確認する必要があります。

于 2010-05-25T01:18:15.777 に答える
0

私は実際に同じことをしましたが、私のパターンは次のようなものでした:

00   01   02   03   04

   10    11   12   13   14

      20    21   22   23   24

         30   31   32   33   34

これにより、何に到達できるかを簡単に把握できますが、奇妙なオフセット パターンが強制されます。(上記の例では) 00,01,10 および 20 を削除して、次のような 16 進パターンにしました。

            02   03   04   05   06

         11   12   13   14   15

            21   22   23   24   25

         30   31   32   33   34

上記のパターンを見ると、到達可能性は常に同じです。

23 (2 を "a" と 3 を "b" と呼ぶ) から、次の場所に到達できます。

NW(a-1, b), NE(a-1, b+1), W(a, b-1), E(a, b+1), SW(a+1, b-1), SE(a+1,b)

このパターンは、グリッド全体で正しく保持される必要があります。

編集:

コメントでやろうと思ったのですが、長くなってしまいました。2 つのアプローチが見られます。

1) ノードの配列を割り当てます (null、割り当てないでください)。ノードを割り当てる必要があるときはいつでもそうしますが、ノードを参照する必要があるときはいつでも配列アドレスを使用してください。巨大な空の配列はそれほど多くのメモリを占有するべきではありませんが、そうすると...

2) ノードクラスのハッシュ値が次のように計算されるノードを保持する HashSet を作成します: (a << 32 || b)。このようにして、前のノードが存在するかどうかを即座に調べることができます。「equals」もオーバーロードすることを忘れないでください (比較されるオブジェクトが同じ型で、a と b が等しい場合にのみ true を返す必要があります)。

境界がわかっている大部分が人口の多いシステムでは、1はメモリを節約しますが、システムがまばらである場合(あなたが主張するように)、#2は効率を犠牲にすることなく大量のメモリを節約できます。

于 2010-05-25T01:25:16.977 に答える
0

クラス宣言には、2 番目のコンストラクターがありTile(char *Filename);ます。おそらく、このコンストラクターはメイン ノードの作成に使用されますが、neighbor適切に初期化されませんか? (その実装は示されていません。)

strcpy()また、無関係のノードでは、コンストラクターに重複があり、何の目的も果たさず、問題を引き起こす可能性があります。

strcpy(Filename, strcpy(Filename, "Data/BlueTile.bmp"));
于 2010-05-25T01:25:57.650 に答える