Vytváření 4D bludiště!

Mám problém s každým, kdo se o to pokusí.

Úkolem je vzít tesseract o velikosti 10x10x10x10 a navrhnout bludiště, které se hodí. Bludiště musí být dokonalým bludištěm (žádné smyčky, jednu cestu nelze sledovat a najít začátek její cesty, aniž by se řešič otočil), a musí mít alespoň 25 slepých uliček a nejméně 8 odbočujících jiných slepých uliček.

Odpovědí, kterou zadáte, by měl být obrázek a měl by se použít maximálně tesseract (pokud ne všechny).

poznámka; zábava je ve vytváření 4D bludiště! 2D a dokonce i 3D je pro vás hlavolamy příliš snadné, vyzvu vás!

To omezte možnosti odpovědí, aby vaše bludiště bylo přijato, musí to být nejkratší možné bludiště, které splňuje všechny již uvedené požadavky. Zkráceně mám na mysli bludiště s nejméně vyplněnou oblastí tesseractu.

Komentáře

  • ‚ to není moc dobrá otázka. 1 – Nevysvětlíte, co je to dokonalé bludiště. ‚ Jistě, můžu to vygooglit, ale neměl bych to ‚ dělat. 2-Jak máme odpovědět na otázku? Kreslení 2D bludiště je dost snadné. 3D je složitější, ale 4D? Nezní to ‚ legrace. A jen popsat bludiště by znamenalo, že bludiště by muselo být triviální. “ Existuje přímá cesta od začátku do konce a 10 krátkých slepých uliček odbočí a většina tesseractu je nevyužita. “ Je to platná odpověď?
  • @Golden na tom ‚ budu pracovat, možná pak ‚ ll získejte lepší odpověď.
  • +1 ode mě, protože si myslím, že je dobrý nápad na skládání puzzle. Některé ilustrace, které máte na mysli, nebo nějaké nápady, jak by mohl vypadat potenciální formát odpovědí, by však byly dobré. Jinak budu čekat alespoň sebeodpověď později …
  • @BmyGuest I ‚ prostě nechám na komunitě, jak by to mělo být provedeno . Pokud ‚ neobdržím žádné odpovědi, ‚ nakonec přidám odměnu.
  • Jeden nápad, o kterém mám tato otázka: možná místo žádání o jednotlivá bludiště jako odpovědi, můžete požádat o techniky pro navrhování 4D bludišť a jejich reprezentaci způsobem, který umožňuje prezentovat a dokonce vyřešit netriviální bludiště.

Odpověď

Tady je. Chcete-li jej zobrazit v plné velikosti (nebo přiblížit obrázek), přejděte na přímý odkaz .

Bludiště

Toto je rovina desek (horizontální je $ w $ a vertikální je $ z $), kde každá deska je 2D rovina (horizontální je $ x $ a vertikální je $ y $). Chcete-li změnit své pozice $ x $ a $ y $, projděte se na aktuální desce.

Šipky vám umožňují změnit vaše pozice $ w $ a $ z $. Nutí vás přeskočit o jednu desku nahoru (modrá), dolů (žlutá), levá (červená) nebo pravá (zelená) podle jejího směru.

Takže, pokud se nacházíte v konkrétním $ (a, b) $ pozice desky:

  • Pomocí šipky nahoru (modrá) přistanete na pozici $ (a, b) $ desky bezprostředně nad aktuální.
  • Pomocí šipky dolů (žlutá) přistanete na pozici $ (a, b) $ na desce bezprostředně pod aktuální.
  • Použitím levé (červené) Šipka), přistanete na pozici $ (a, b) $ na desce hned nalevo od aktuální.
  • Použitím pravé (zelené) šipky přistanete na $ (a, b) $ pozice desky hned napravo od aktuální.

Pro vygenerování jsem vytvořil tento program 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()); }; } } 

Je mi líto, že tento web nemá žádné syntaxe.

Existují stovky nebo tisíce slepých a větvících bodů. Mnohem více než jen 25 a 8 vyžaduje OP.

Pro každé dvě umístění existuje přesně jedna cesta k dalšímu umístění. V grafu nejsou žádné cykly a je připojen. Program to zajišťuje (growMaze metoda).

Neexistuje žádný definovaný počáteční ani koncový bod. Jen náhodně získejte libovolné dva body a pokuste se najít cestu. Jak vidíte, ruční nalezení cesty zde by mělo být těžké, protože v tomto bludišti je deset tisíc pozic a rozhlížet se kolem dimenzí $ w $ a $ z $ za účelem nalezení užitečné cesty je pro lidské oči těžší než v $ Rozměry x $ a $ y $ $.

Program můžete použít k náhodnému generování dalších bludišť. Změna jeho velikosti je také snadná: Jsou to ty čtyři desítky na začátku metody main. V metodě main můžete změnit, do kterého souboru se vygenerované bludiště uloží.Abychom to ukázali, je to mnohem menší a jednodušší bludiště $ 4 \ krát 5 \ krát 3 \ krát 2 $ vygenerované programem:

Malé bludiště

Mimochodem, nastavením $ w $ na 1 a $ z $ na 1 můžete použít ke generování 2D bludišť. Pokud nastavíte pouze jeden z nich na 1, bude to 3D bludiště.

Komentáře

  • Hezké, Victor. Přes noc jsi mě ‚ porazil. Dnes ráno jsem se chystal napsat odpověď – teď ne. (@warspyking: Můžeš být navždy zatracený za to, že mě nechávám vzhůru v 1 ráno. Bylo to těsné volání zůstat pěkný a útulný a teplý v posteli, nebo se znovu vydat psát studená písmena v chladné místnosti do bezcitného PC … )
  • @BmyGuest Bylo dobré programovací cvičení. A je těžké je zde najít. 🙂
  • @BmyGuest Upraveno. Je to teď lepší?
  • Pěkné: D Miluji to, dobrá práce, Victore!
  • Kde začínáš? : /

Odpověď

Nyní, když to bylo znovu otevřeno, chci zveřejnit “ řešení “ K tomu jsem měl noc před Victorovou odpovědí. Victor mě k tomu porazil a Warspyking od té doby upravil podmínky vítězství , takže to již není platná odpověď. Stále jsem to chtěl zveřejnit pro komentování (a ověření / vyvrácení).

Odpověď funguje pro n-dimenzionální bludiště a je to také algoritmus není konkrétní bludiště. Nezveřejňuji zde kód , ale koncept algoritmu.


Moje řešení je založeno na myšlence, že každý pixel / voxel / n-dim-element (od nynějška zvaný voxel ) může být buď cesta (0) nebo zeď (1). To se liší od toho, co vytvořil Victor, ale může být na sebe poměrně snadno přeměněny. ( Stačí převést otvory & na další voxely nebo naopak. )

  • Velikosti datových dimenzí se nazývají n,m,k,l…
  • Indexování začíná hodnotou 0 pro první voxel

Inicializovat data:

  • Vytvořte data bludiště jako booleovské pole potřebné rozměrnosti

  • Inicializovat všechny voxely jako 0 (prázdné)

  • Vytvořit seznam indexů voxelů stěn (prázdné )

  • Nastavit každý voxel s alespoň jedním sudým indexem na 1 (zeď)

  • Uložte index wall-voxel do seznamu.

  • Od této chvíle, kdykoli bude zeď odstraněna:

    • odebrat odpovídající index ze seznamu
    • nastavit odpovídající booleovskou hodnotu na 0 (prázdný)

definovat počáteční & koncovou pozici:

V zásadě nastavte dva protichůdné “ rohy “ na start and end.

  • Nastavit voxel indexu (0,0, …) jako výchozí místo
  • nastavit voxel indexu (n, m, …) být cílem cíle
  • Pokud je cílovým voxelem zeď, zeď odstraňte. (Všechny rozměry jsou sudé velikosti)

Vytvořit bludiště:

Aktuální bludiště je mřížka s izolovanými prázdnými mezerami.

  • Všechny prázdné mezery označte jedinečným štítkem.

Nyní přejděte iterativně ( WHILE-loop ):

  • Náhodně vyberte zeď ze seznamu indexů.
  • Odeberte index ze seznamu ( nikdy netestujte zeď dvakrát )

Test : Odstranění této zdi sloučí dvě prázdná místa jiného štítku? NE: další iterace. JINÉ:

  • Odstranit zeď

  • Ze všech zúčastněných štítků vyberte ten s nejnižší hodnotou

  • Nastavit všechny voxely se zapojenými štítky na tuto zvolenou hodnotu (= sloučit prázdné mezery)

  • další iterace

Zastavit iteraci, pokud v seznamu nejsou žádné indexy.

Bez důkazu si myslím, že tento algoritmus vám poskytne:

- 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 

Původně jsem měl v úmyslu tento algoritmus kódovat a pak vymyslet pěkný způsob zobrazení případu 4D, ale Victor toho už odvedl skvělou práci, takže to s tím nechám .

Komentáře

  • Děkuji za tento zásah, slovní odpověď! Určitě je to pro mě užitečné.

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *