4D迷路の作成!

試してみたい人には問題があります。

あなたの仕事は、10x10x10x10サイズの正八胞体を使って設計することです。適合する迷路迷路は完全な迷路である必要があり(ループがなく、1つのパスをたどって、ソルバーを回転させずにパスの開始を見つけることはできません)、少なくとも25の行き止まりがあり、少なくとも8つの分岐が必要です。

提供する答えは画像である必要があり、せいぜいテッセラクト(すべてではないにしても)を使用する必要があります。

注;楽しみは4D迷路の作成にあります!2D、さらには3Dでさえ、困惑者には簡単すぎます。私はあなたに挑戦します!

To答えの可能性を制限します。迷路が受け入れられるためには、すでに述べたすべての要件を満たす最短の迷路である必要があります。最短で言うと、テッセラクトの面積が最も少ない迷路を意味します。

コメント

  • 'はあまり良い質問ではありません。 1- '完璧な迷路とは何かを説明していません。もちろん、グーグルで検索することはできますが、'する必要はありません。 2-質問にどのように答えるのですか? 2D迷路を描くのは簡単です。 3Dはトリッキーですが、4D? '楽しそうに聞こえません。そして、迷路を説明するだけでは、迷路は些細なものでなければならないことを意味します。 "最初から最後までまっすぐな道があり、10個の短い行き止まりが分岐しており、ほとんどのテッセラクトは使用されていません。"それは有効な答えですか?
  • @Golden I 'それで作業します、多分それなら私は' llより良い応答が得られます。
  • +1は、パズルの作成について良いアイデアだと思うので。ただし、念頭に置いているいくつかの図、または潜在的な回答形式がどのようになるかについてのいくつかのアイデアがあればよいでしょう。それ以外の場合は、少なくとも後で自己回答を期待します…
  • @BmyGuest I 'これをどのように行うかはコミュニティに任せます。 '回答が得られない場合は、'最終的に報奨金を追加します。
  • 私が持っているアイデアの1つこの質問:おそらく、答えとして個々の迷路を求める代わりに、4D迷路を設計し、重要な迷路を提示し、解決することさえできる方法でそれらを表現するためのテクニックを求めることができます。

回答

こちらです。 直接リンクにアクセスして、フルサイズで表示します(または画像を拡大します)。

迷路

これはボードの平面(水平は$ w $、垂直は$ z $)で、各ボードは2D平面(水平は$ x $、垂直は$)です。 y $)。 $ x $と$ y $の位置を変更するには、現在のボードを歩き回るだけです。

矢印を使用すると、$ w $と$ z $の位置を変更できます。方向に応じて、1つのボードを上(青)、下(黄色)、左(赤)、または右(緑)にジャンプさせます。

したがって、特定の$(a、 b)ボードの$位置:

  • 上(青)矢印を使用すると、現在のボードのすぐ上のボードの$(a、b)$位置に移動します。
  • 下(黄色)矢印を使用すると、現在のボードのすぐ下にあるボードの$(a、b)$の位置に移動します。
  • 左(赤)を使用します。 )矢印を押すと、現在のボードのすぐ左にあるボードの$(a、b)$の位置に着地します。
  • 右(緑)の矢印を使用すると、$に着地します。 (a、b)現在のボードのすぐ右側のボードの$位置。

これを生成するために、次のJavaプログラムを作成しました。

import java.awt.Color; import java.awt.Graphics2D; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import javax.imageio.ImageIO; /** * @author Victor */ public class Tesseracter { public static void main(String[] args) throws IOException { TesseractMaze maze = new TesseractMaze(10, 10, 10, 10); BufferedImage im = maze.draw(); ImageIO.write(im, "png", new File("maze.png")); } public static final class Coordinate4D { private final TesseractMaze maze; private final int w, x, y, z; public Coordinate4D(TesseractMaze maze, int w, int x, int y, int z) { Objects.requireNonNull(maze); if (w < 0 || w >= maze.wSize || x < 0 || x >= maze.xSize || y < 0 || y >= maze.ySize || z < 0 || z >= maze.zSize) throw new IndexOutOfBoundsException(); this.maze = maze; this.w = w; this.x = x; this.y = y; this.z = z; } @Override public int hashCode() { return Objects.hash(maze, w, x, y, z); } @Override public boolean equals(Object another) { if (!(another instanceof Coordinate4D)) return false; Coordinate4D c4d = (Coordinate4D) another; return maze == c4d.maze && w == c4d.w && x == c4d.x && y == c4d.y && z == c4d.z; } public int squareDistance(Coordinate4D another) { Objects.requireNonNull(another); if (maze != another.maze) throw new IllegalArgumentException(); int dw = Math.abs(w - another.w); int dx = Math.abs(x - another.x); int dy = Math.abs(y - another.y); int dz = Math.abs(z - another.z); return dw + dx + dy + dz; } public Coordinate4D minusW() { return w == 0 ? null : new Coordinate4D(maze, w - 1, x, y, z); }; public Coordinate4D plusW() { return w == maze.wSize - 1 ? null : new Coordinate4D(maze, w + 1, x, y, z); }; public Coordinate4D minusX() { return x == 0 ? null : new Coordinate4D(maze, w, x - 1, y, z); }; public Coordinate4D plusX() { return x == maze.xSize - 1 ? null : new Coordinate4D(maze, w, x + 1, y, z); }; public Coordinate4D minusY() { return y == 0 ? null : new Coordinate4D(maze, w, x, y - 1, z); }; public Coordinate4D plusY() { return y == maze.ySize - 1 ? null : new Coordinate4D(maze, w, x, y + 1, z); }; public Coordinate4D minusZ() { return z == 0 ? null : new Coordinate4D(maze, w, x, y, z - 1); }; public Coordinate4D plusZ() { return z == maze.zSize - 1 ? null : new Coordinate4D(maze, w, x, y, z + 1); }; public TesseractMaze getMaze() { return maze; } } public static final class TesseractMaze { private final int wSize, xSize, ySize, zSize; private final Map<Coordinate4D, Node> nodes; private final Node start; public TesseractMaze(int w, int x, int y, int z) { this.wSize = w; this.xSize = x; this.ySize = y; this.zSize = z; nodes = new HashMap<>(w * x * y * z); fill(); this.start = chooseRandomNode(); growMaze(); } private void fill() { for (int a = 0; a < wSize; a++) { for (int b = 0; b < xSize; b++) { for (int c = 0; c < ySize; c++) { for (int d = 0; d < zSize; d++) { Coordinate4D coord = new Coordinate4D(this, a, b, c, d); nodes.put(coord, new Node(coord)); } } } } } public Node nodeAt(Coordinate4D coord) { if (coord == null) return null; return nodes.get(coord); } private Node chooseRandomNode() { int n = (int) (Math.random() * wSize * xSize * ySize * zSize); return new ArrayList<>(nodes.values()).get(n); } private void growMaze() { List<Node> frontier = new ArrayList<>(wSize * xSize * ySize * zSize); frontier.add(start); start.linked = true; while (!frontier.isEmpty()) { Collections.shuffle(frontier); Node n = frontier.get(0); Node next = n.linkRandomUnlinkedNeighbour(); if (next != null) { frontier.add(next); } else { frontier.remove(0); } } } public BufferedImage draw() { int cellWidth = 16; int cellHeight = 16; int boardWidth = cellWidth * (xSize + 1); int boardHeight = cellHeight * (ySize + 1); int arrowSize = 3; int margin = 2; Color red = Color.RED; Color blue = Color.BLUE; Color yellow = new Color(128, 128, 0); Color green = new Color(0, 128, 0); BufferedImage im = new BufferedImage(wSize * boardWidth + cellWidth - 1, zSize * boardHeight + cellHeight - 1, BufferedImage.TYPE_INT_ARGB); Graphics2D g = im.createGraphics(); for (int w = 0; w < wSize; w++) { for (int z = 0; z < zSize; z++) { for (int x = 0; x < xSize; x++) { for (int y = 0; y < ySize; y++) { Coordinate4D c = new Coordinate4D(this, w, x, y, z); Node n = nodeAt(c); int x1 = cellWidth * x + boardWidth * w + cellWidth - 1; int y1 = cellHeight * y + boardHeight * z + cellHeight - 1; int x2 = x1 + cellWidth; int y2 = y1 + cellHeight; int x3 = (x1 + x2) / 2; int y3 = (y1 + y2) / 2; g.setColor(Color.BLACK); if (!n.isLinkedTo(n.minusY())) g.drawLine(x1, y1, x2, y1); if (!n.isLinkedTo(n.plusY())) g.drawLine(x1, y2, x2, y2); if (!n.isLinkedTo(n.minusX())) g.drawLine(x1, y1, x1, y2); if (!n.isLinkedTo(n.plusX())) g.drawLine(x2, y1, x2, y2); if (n.isLinkedTo(n.minusW())) { // Board left, left arrow. g.setColor(red); for (int i = 0; i < arrowSize; i++) { g.drawLine(x1 + margin + i, y3 - i, x1 + margin + i, y3 + i); } } if (n.isLinkedTo(n.plusW())) { // Board right, right arrow. g.setColor(green); for (int i = 0; i < arrowSize; i++) { g.drawLine(x2 - margin - i, y3 - i, x2 - margin - i, y3 + i); } } if (n.isLinkedTo(n.minusZ())) { // Board up, up arrow. g.setColor(blue); for (int i = 0; i < arrowSize; i++) { g.drawLine(x3 - i, y1 + margin + i, x3 + i, y1 + margin + i); } } if (n.isLinkedTo(n.plusZ())) { // Board down, down arrow. g.setColor(yellow); for (int i = 0; i < arrowSize; i++) { g.drawLine(x3 - i, y2 - margin - i, x3 + i, y2 - margin - i); } } } } } } return im; } } public static final class Node { private final Coordinate4D coord; private final List<Node> linkedNeighbours; private List<Node> neighbours; private boolean linked; public Node(Coordinate4D coord) { Objects.requireNonNull(coord); this.coord = coord; linkedNeighbours = new ArrayList<>(8); } public Node linkRandomUnlinkedNeighbour() { List<Node> list = new ArrayList<>(getNeighbours()); list.removeIf(n -> n.linked); if (list.isEmpty()) return null; Collections.shuffle(list); Node next = list.get(0); next.getNeighbours(); linkedNeighbours.add(next); next.linkedNeighbours.add(this); next.linked = true; return next; } @SuppressWarnings("ReturnOfCollectionOrArrayField") public List<Node> getNeighbours() { if (neighbours == null) { List<Node> nodes = new ArrayList<>(Arrays.asList(minusW(), plusW(), minusX(), plusX(), minusY(), plusY(), minusZ(), plusZ())); nodes.removeIf(x -> x == null); neighbours = Collections.unmodifiableList(nodes); } return neighbours; } public boolean isDeadEnd() { return linkedNeighbours.size() == 1; } public boolean isBranch() { return linkedNeighbours.size() > 2; } public boolean isLinkedTo(Node node) { return linkedNeighbours.contains(node); } public TesseractMaze getMaze() { return coord.getMaze(); } public Coordinate4D getCoord() { return coord; } public Node minusW() { return getMaze().nodeAt(coord.minusW()); }; public Node plusW() { return getMaze().nodeAt(coord.plusW()); }; public Node minusX() { return getMaze().nodeAt(coord.minusX()); }; public Node plusX() { return getMaze().nodeAt(coord.plusX()); }; public Node minusY() { return getMaze().nodeAt(coord.minusY()); }; public Node plusY() { return getMaze().nodeAt(coord.plusY()); }; public Node minusZ() { return getMaze().nodeAt(coord.minusZ()); }; public Node plusZ() { return getMaze().nodeAt(coord.plusZ()); }; } } 

このサイトに構文の色がないことをお詫びします。

行き止まりと分岐点が数百または数千あります。 OPで必要なのは25と8だけではありません。

2つの場所ごとに、他の場所へのパスが1つだけあります。グラフにはサイクルがなく、接続されています。プログラムは、(growMazeメソッド)を保証します。

開始点または終了点が定義されていません。ランダムに任意の2つのポイントを取得し、パスを見つけてみてください。ご覧のとおり、この迷路には1万の位置があり、$ w $と$ z $の次元を見て、有用なパスを見つけるのは$よりも人間の目に難しいため、ここで手動でパスを見つけるのは難しいはずです。 x $および$ y $の寸法。

このプログラムを使用して、他の迷路をランダムに生成できます。サイズの変更も簡単です。mainメソッドの開始時の4つの10です。 mainメソッドでは、生成された迷路を保存するファイルを変更できます。これを示すために、ここでは、プログラムによって生成されたはるかに小さくて単純な$ 4 \ times 5 \ times 3 \ times 2 $迷路です:

小さな迷路

ちなみに、$ w $を1に、$ z $を1に設定すると、2D迷路を生成するために使用できます。そのうちの1つだけを1に設定すると、3D迷路になります。

コメント

  • いいですね、ビクター。あなたは'一晩で私を殴りました。私は今朝答えを書くつもりでした-しかし今ではありません。 (@warspyking:午前1時に私を起こしておくと、永遠に気が滅入るかもしれません。ベッドで快適で暖かく過ごすか、寒い部屋で気にしないPCに冷たい手紙を入力するためにもう一度出かけるのは間近でした。 )
  • @BmyGuestは優れたプログラミング演習でした。そして、ここでそれを見つけるのは難しいです。 🙂
  • @BmyGuest編集。今は良くなっていますか?
  • いいですね:D大好きです、ビクターさん、お疲れ様でした!
  • どこから始めますか? :/

回答

これが再開されたので、

solution "ビクターの答えの前夜にこれを持っていました。ビクターは私を殴り、ウォースパイキングはそれ以来勝利条件を変更しました、そのため、有効な回答ではなくなりました。コメント(および検証/反証)のためにこれを投稿したかったのです。

回答はn次元の迷路で機能し、アルゴリズムは特定の迷路ではありません。ここではコードを投稿しませんが、アルゴリズムの概念を投稿します。


私の解決策は、次のような考えに基づいています。各ピクセル/ボクセル/ n-dim要素(今後はボクセルと呼ばれる)は、パス(0)または壁(1)のいずれかになります。これは、Victorが作成したものとは異なりますが、可能です。お互いにかなり簡単に変換できます。(壁&開口部を追加のボクセルに変換するかその逆にします。

  • データディメンションのサイズは n、m、k、l …と呼ばれます…
  • 最初のボクセルのインデックス作成は0から始まります

データの初期化:

  • 迷路データをブール配列として作成します必要な次元の数

  • すべてのボクセルを0(空)として初期化します

  • 壁のボクセルインデックス(空)のリストを作成します)

  • 少なくとも1つの偶数インデックスを持つすべてのボクセルを1(壁)に設定します

  • 壁ボクセルインデックスをリストに保存します。

  • これ以降、壁が削除されるたびに:

    • リストから対応するインデックスを削除します
    • 対応するブール値を0(空)に設定します

開始位置の定義&終了位置:

基本的に2つの対向する"コーナー"を開始と終了。

  • インデックスのボクセル(0,0、…)を開始位置に設定します
  • インデックスのボクセルを設定します(n、m、 …)目標の宛先になる
  • 宛先ボクセルが壁の場合は、壁を削除します。 (すべての寸法は同じサイズです)

迷路を作成します:

現在の迷路は、孤立した空のスペースがあるグリッドです。

  • すべての空のスペースに一意のラベルを付けます。

次に、繰り返し実行します( WHILE-loop ):

  • インデックスリストから壁をランダムに選択します。
  • リストからインデックスを削除します(テストしないでください)壁を2回

テスト:この壁を削除すると2つがマージされます別のラベルの空きスペース?いいえ:次の反復。その他:

  • 壁を取り除く

  • 関連するすべてのラベルの中から、値が最も小さいものを選択します

  • ラベルが含まれるすべてのボクセルをこの選択した値に設定します(=空のスペースをマージします)

  • 次の反復

リストにインデックスがない場合は反復を停止します。

それを証明しなければ、このアルゴリズムは次のようになると思います:

- a non-looping maze from start to end - maximize the size of the maze within the volume - The identical algorithm could be used for arbitrary amount of dimensions 

元々、このアルゴリズムをコーディングしてから、4Dケースを表示するための優れた方法を考えようとしていましたが、Victorはすでにそれをうまく行っているので、そのままにしておきます。 。

コメント

  • このリーチの口頭での回答に感謝します!私にとっては確かに役立ちます。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です