Ho un problema per chiunque voglia provare.
Il tuo compito è prendere un tesseract di dimensione 10x10x10x10 e progettare un labirinto che si adatta. Il labirinto deve essere un labirinto perfetto (nessun loop, un percorso non può essere seguito e trova linizio del suo percorso senza che il risolutore giri intorno) e deve avere almeno 25 vicoli ciechi e almeno 8 diramazioni di altri vicoli ciechi.
La risposta che fornisci dovrebbe essere unimmagine e al massimo il tesseract (se non tutti) dovrebbe essere usato.
nota; Il divertimento sta nel creare il labirinto 4D! 2D e persino 3D è troppo facile per voi rompicapo, vi sfiderò!
A limitare le possibilità di risposte, affinché il tuo labirinto venga accettato, deve essere il labirinto più corto possibile che soddisfi tutti i requisiti già indicati. Per più breve intendo il labirinto con larea più piccola del tesseract riempita.
Commenti
- ‘ non è una buona domanda. 1-Non ‘ spieghi cosè un labirinto perfetto. Certo, posso cercarlo su Google, ma non dovrei ‘ farlo. 2-Come dovremmo rispondere alla domanda? Disegnare un labirinto 2D è abbastanza facile. Il 3D è più complicato, ma 4D? ‘ non sembra divertente. E descrivere solo il labirinto significherebbe che il labirinto dovrebbe essere banale. ” Cè un percorso rettilineo dallinizio alla fine e 10 brevi vicoli ciechi si diramano e la maggior parte del tesseract è inutilizzata. ” È una risposta valida?
- @Golden I ‘ ci lavorerò, forse allora ‘ ll ottenere una risposta migliore.
- +1 da me perché penso che sia una buona idea sulla costruzione di puzzle. Tuttavia, alcune illustrazioni che hai in mente, o alcune idee su come potrebbe essere un potenziale formato di risposta, sarebbero buone. Altrimenti mi aspetterò almeno unauto-risposta in seguito …
- @BmyGuest ‘ lascerò solo alla comunità come dovrebbe essere fatto . Se ‘ non ricevo alcuna risposta, ‘ alla fine aggiungerò una taglia.
- Unidea che ho su questa domanda: forse invece di chiedere singoli labirinti come risposte, puoi chiedere tecniche per progettare labirinti 4D e rappresentarli in un modo che consenta di presentare e persino risolvere labirinti non banali.
Risposta
Eccola. Accedi al link diretto per vederlo a grandezza naturale (o ingrandisci limmagine).
Questoèun piano di assi (orizzontale è $ w $ e verticale è $ z $) dove ogni tavola è un piano 2D (orizzontale è $ x $ e verticale è $ y $). Per cambiare le tue posizioni $ x $ e $ y $, cammina nel tabellone attuale.
Le frecce ti permettono di cambiare le tue posizioni $ w $ e $ z $. Ti fanno saltare una tavola su (blu), giù (giallo), sinistra (rosso) o destra (verde), a seconda della sua direzione.
Quindi, se ti trovi in un $ (a, b) $ posizione di una tavola:
- Usando la freccia su (blu) atterrerai nella $ (a, b) $ posizione della tavola immediatamente sopra quella corrente.
- Usando la freccia giù (gialla) atterrerai nella posizione $ (a, b) $ del tabellone immediatamente sotto quella corrente.
- Usando la freccia sinistra (rossa ), atterrerai nella posizione $ (a, b) $ del tabellone immediatamente a sinistra di quella corrente.
- Usando la freccia destra (verde), atterrerai nella $ (a, b) $ posizione della scheda immediatamente a destra di quella corrente.
Per generare questo, ho creato questo programma 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()); }; } }
Mi dispiace che questo sito non abbia la colorazione della sintassi.
Ci sono centinaia o migliaia di punti morti e diramazioni. Molto più di 25 e 8 richiesti dallOP.
Per ogni due posizioni cè esattamente un percorso luna verso laltra. Non ci sono cicli nel grafico ed è connesso. Il programma garantisce che (growMaze
metodo).
Non esiste un punto iniziale o finale definito. Ottieni solo due punti a caso e prova a trovare un percorso. Come vedi, trovare manualmente un percorso qui dovrebbe essere difficile, dal momento che ci sono diecimila posizioni in questo labirinto e guardarsi intorno alle dimensioni $ w $ e $ z $ per trovare un percorso utile è più difficile per gli occhi umani rispetto a $ Dimensioni x $ e $ y $.
Puoi utilizzare il programma per generare casualmente altri labirinti. Anche cambiare le sue dimensioni è facile: sono quelle quattro decine allinizio del metodo main
. Nel metodo main
puoi modificare il file in cui viene salvato il labirinto generato.Per dimostrarlo, ecco un labirinto $ 4 \ times 5 \ times 3 \ times 2 $ molto più piccolo e semplice generato dal programma:
A proposito, impostando $ w $ su 1 e $ z $ su 1, puoi usarlo per generare labirinti 2D. Se ne imposti solo uno a 1, sarà un labirinto 3D.
Commenti
- Bello, Victor. Mi ‘ mi hai battuto tutta la notte. Stavo per scrivere una risposta questa mattina, non ora però. (@warspyking: potresti essere dannato per sempre per avermi tenuto sveglio alluna di notte.Era una chiamata per rimanere gentile, accogliente e al caldo a letto, o uscire di nuovo per digitare alcune lettere fredde in una stanza fredda in un PC indifferente … )
- @BmyGuest è stato un buon esercizio di programmazione. Ed è difficile trovarne uno qui. 🙂
- @BmyGuest Edited. Va meglio adesso?
- Bello: D Lo adoro, buon lavoro Victor!
- Da dove inizi? : /
Risposta
Ora che è stato riaperto, voglio pubblicare il ” soluzione ” Ho avuto per questo la notte prima della risposta di Victor. Victor mi ha battuto e da allora Warspyking ha modificato le condizioni di vittoria , quindi non è più una risposta valida. Volevo ancora postare questo per commentare (e verificare / confutare.)
La risposta funziona per labirinti n-dimensionali ed è anche un algoritmo non un labirinto specifico. Non inserisco codice qui, ma il concetto di algoritmo.
La mia soluzione si basa sullidea che ogni pixel / voxel / n-dim-elemento ( dora in poi chiamato voxel ) può essere un percorso (0) o un muro (1). Questo è diverso da ciò che Victor ha creato, ma può essere trasformati luno nellaltro piuttosto facilmente. ( Basta convertire i muri & aperture in voxel aggiuntivi o viceversa. )
- Le dimensioni delle dimensioni dei dati sono chiamate n,m,k,l…
- Lindicizzazione inizia con 0 per il primo voxel
Inizializza i dati:
Crea i dati del labirinto come array booleano della dimensionalità necessaria
Inizializza tutti i voxel come 0 (vuoto)
Crea un elenco per gli indici dei voxel dei muri (vuoto )
Imposta ogni voxel con almeno un indice pari su 1 (wall)
Memorizza lindice wall-voxel nellelenco.
Dora in poi, ogni volta che un muro viene rimosso:
- rimuovi lindice corrispondente dallelenco
- imposta il booleano corrispondente su 0 (vuoto)
Definisci inizio & posizione finale:
Imposta essenzialmente i due ” angoli ” opposti inizio e fine.
- Imposta il voxel di index (0,0, …) come posizione iniziale
- Imposta il voxel di index (n, m, …) come destinazione dellobiettivo
- Se il voxel di destinazione è un muro, rimuovilo. (Tutte le dimensioni sono pari)
Crea il labirinto:
Il labirinto attuale è una griglia con spazi vuoti isolati.
- Etichetta tutti gli spazi vuoti con unetichetta univoca.
Ora procedi in modo iterativo ( WHILE-loop ):
- Scegli a caso un muro dallelenco degli indici.
- Rimuovi lindice dallelenco ( non testare mai un wall due volte )
Test : la rimozione di questo wall unirà due spazi vuoti di etichetta diversa? NO: prossima iterazione. ALTRIMENTI:
Rimuovi muro
Di tutte le etichette coinvolte scegli quella con il valore più basso
Imposta tutti i voxel con etichette coinvolte su questo valore scelto (= unisci spazi vuoti)
iterazione successiva
Interrompi literazione quando non sono presenti indici nellelenco.
Senza verificarlo, penso che questo algoritmo ti darà:
- 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
Inizialmente avevo intenzione di codificare questo algoritmo e poi pensare a un bel modo di visualizzare il caso 4D, ma Victor ha già fatto un ottimo lavoro, quindi lo lascio con questo .
Commenti
- Grazie per questa risposta verbale a portata di mano! È sicuramente utile per me.