Crea un labirinto multilivello 5x5x5 con una sola soluzione


11

Lo scopo di questa sfida è creare il codice più breve (in caratteri) che esegua correttamente le seguenti operazioni:

Caratteristiche tecniche :

  • Deve creare 5x5x5 labyrinthesattamente un 1 possible solution(niente di più, niente di meno)
  • Il labirinto deve essere creato randomly Deve essere in grado di generare ogni soluzione esistente se lasciato in funzione per anni
  • L' starte finishdeve essere inserito in*opposite corners
  • La mappa outputdeve essere in uno dei seguenti formati:

Formato output opzione 1 strings, printed or alerted :

xxxxx,xxxxx,xxxxx,xxxxx,xxxxx/
xxxxx,xxxxx,xxxxx,xxxxx,xxxxx/
xxxxx,xxxxx,xxxxx,xxxxx,xxxxx/
xxxxx,xxxxx,xxxxx,xxxxx,xxxxx/
xxxxx,xxxxx,xxxxx,xxxxx,xxxxx

Formato di output dell'opzione 2 arrays :

[[xxxxx,xxxxx,xxxxx,xxxxx,xxxxx],
[xxxxx,xxxxx,xxxxx,xxxxx,xxxxx],
[xxxxx,xxxxx,xxxxx,xxxxx,xxxxx],
[xxxxx,xxxxx,xxxxx,xxxxx,xxxxx],
[xxxxx,xxxxx,xxxxx,xxxxx,xxxxx]]

Note sull'uscita:

  • Utilizzare 0per emptye 1persquares

  • Le linee di interruzione NON sono necessarie

  • Decidi tu indexcos'è, ma assicurati di spiegarlo bene


* Ecco un esempio di cosa intendo per angoli opposti:

inserisci qui la descrizione dell'immagine

Chiarimenti :

  • NON può spostarsidiagonal
  • NON può passare due volte sullo stesso percorso
  • Avere inaccessible areasè permesso
  • Puoi go up/downpiù di un livello di fila

Suggerimenti:

  • Non vederli come muri, invece vederli come una 5x5x5pila di quadrati che mancano ad alcuni di essi e puoi passare attraverso quelli mancanti

Se qualcosa non è chiaro,
chiedimi

3
Tuttavia, c'è un dettaglio su cui vorrei un chiarimento: i muri sono posti tra i quadrati o un muro riempie un intero quadrato?
Ilmari Karonen,

1
dici 5x5 (un array 2D) in alcuni punti, ma i campioni di codice e l'immagine suggeriscono 5x5x5 (un array 3D). Presumo che l'array 3D sia ciò che si intende?
Kae Verens,

1
come si decide che la soluzione sia un labirinto valido? Voglio dire, è il numero di derivati ​​che ha la strada giusta? ha a che fare con il rapporto da 1s a 0s?
Kae Verens,

2
Quando dici "Il labirinto deve essere creato casualmente", quali limiti dovremmo dedurre? Presumo, ad esempio, che tu non intenda consentire, come una lettura letterale delle regole attualmente, un programma che sceglie casualmente due uscite hardcoded.
Peter Taylor,

Risposte:


10

C ++ C, circa 1000 670 643 395 297 248 caratteri

Uscita campione:

00111,10011,10111,00110,11000,
11001,01010,00100,11011,10101,
10111,10101,10001,01010,00101,
11001,11110,11100,11110,10101,
11100,10010,11001,10101,00000,

Come funziona: il programma utilizza Brownian Motion per generare soluzioni. Il punto iniziale è impostato. Quindi, un punto casuale viene selezionato e spostato ripetutamente in modo casuale fino a quando non tocca uno e un solo punto sul ramo iniziale. Il punto viene quindi impostato e, se tocca anche il punto finale, il programma termina e viene visualizzata la matrice. Poiché nessun punto può unire due rami, c'è solo un percorso attraverso il labirinto. Il programma utilizza la funzione rand e un argomento intero della riga di comando come seme, quindi con una funzione rand sufficiente dovrebbe essere possibile eventualmente generare tutti i labirinti validi (questo algoritmo non creerà comunque aree non connesse, quindi non genererà tutto possibili labirinti).

Il movimento browniano è stato abbandonato poiché si è rivelato non necessario e la sua rimozione semplifica notevolmente il codice. Penso che abbia reso più belli i labirinti. Allo stesso modo, l'argomento seed è stato abbandonato, poiché richiedere un generatore di numeri casuali senza stato ha più senso per me di un seed a 128 bit.

È possibile che il programma rimanga bloccato in un ciclo infinito, poiché è possibile per situazioni in cui qualsiasi punto aggiunto ai rami creerebbe più percorsi. Questo è risolvibile, ma penso che sia abbastanza raro da non essere un problema per il codice golf.

#define M m[*p+1][p[1]][p[2]]
#define F(a,b)for(p[a]=5;p[a]--;putchar(b))
#define f for(i=3;i--;p[i]
p[]={4,4,4},h[3],m[7][6][6]={1};
main(i){
    for(M=2;h[1]^1||(M=1)^h[2];){
        f=rand()%5)
            h[i]=0;
        f++)
            p[i]++,
            h[M]++,
            p[i]-=2,
            h[M]++;
    }
    F(0,10)
        F(1,44)
            F(2,48+!M);
}

Ho aggiunto nuove righe e rientri al codice visualizzato per leggibilità.


Penso che tu vinca questo ;-) Non c'è modo di ridurre il mio così lontano
Kae Verens,

Mi è davvero piaciuta la competizione :-) Sono un po 'sorpresa che siamo ancora le uniche risposte, mi aspettavo che uno scripter da golf o simili ci avrebbe battuti entrambi ormai.
Sir_Lagsalot,

In qualche modo, un percorso semplice, senza fork o nodi decisionali, non sembra qualificarsi come un vero labirinto. Prova ad aggiungere alcuni vicoli ciechi.
DavidC

@David Carraher L'algoritmo genera vicoli ciechi e percorsi di diramazione come mostrato nell'esempio. Non consentire a un nuovo punto di collegare due rami già esistenti impedisce semplicemente soluzioni multiple o cicli nel labirinto.
Sir_Lagsalot

@Sir_Lagsalot Grazie per il chiarimento
DavidC

5

JavaScript, 874 816 788 686 682 668 637 caratteri

uscita campione:

00000,10111,10111,01010,11000
01011,01000,01010,01111,00011
00100,11010,00111,10111,11010
01111,10001,01110,01010,01000
00000,11110,00001,10101,10110

questo funziona partendo dal punto [0,0,0] e aggiungendo in modo casuale allegando un altro 0 vicino a uno 0 ove consentito (consentito == il nuovo 0 non è vicino ad altri 0 tranne l'originatore) fino a quando non ci sono più possibili aggiunte.

se un nuovo 0 è vicino al punto di uscita (x * y * z == 48), apriamo l'uscita.

golfed

b=[]
I=Math.random
for(i=5;i--;)for(j=5,b[i]=[];j--;)b[i][j]=[1,1,1,1,1]
b[0][0][0]=0
k=[[0,0,0]]
function q(x,y,z){J=b[x]
if(x<0||y<0||z<0||x>4||y>4||z>4||!J[y][z])return 
n=6-!x||b[x-1][y][z]
n-=!y||J[y-1][z]
n-=!z||J[y][z-1]
n-=x==4||b[x+1][y][z]
n-=y==4||J[y+1][z]
n-=z==4||J[y][z+1]
n==1&&v.push([x,y,z])}while(I){F=k.length
B=k[C=0|I(v=[])*F]
x=B[0]
q(x-1,y=B[1],z=B[2])
q(x,y-1,z)
q(x,y,z-1)
q(x+1,y,z)
q(x,y+1,z)
q(x,y,z+1)
if(D=v.length){k.push(A=v[0|I()*D])
b[A[0]][A[1]][A[2]]=0
if(A[0]*A[1]*A[2]==48)b[4][4][4]=I=0}else{for(E=[];F--;)F^C&&E.push(k[F])
k=E}}for(i=25;i--;)b[H=0|i/5][i%5]=b[H][i%5].join('')
alert(b.join("\n"))

originale

window.map=[];
for (i=0;i<5;++i) {
  map[i]=[];
  for (j=0;j<5;++j) {
    map[i][j]=[1,1,1,1,1];
  } 
} 
points=[[0,0,0]];
map[0][0][0]=0;
function spaces(x,y,z) {
  var n=6;
  if (x<0 || y<0 || z<0) return 0;
  if (x>4 || y>4 || z>4) return 0;
  if (!map[x][y][z]) return 0;
  if (!x || map[x-1][y][z]) n--;
  if (!y || map[x][y-1][z]) n--;
  if (!z || map[x][y][z-1]) n--;
  if (x==4 || map[x+1][y][z]) n--;
  if (y==4 || map[x][y+1][z]) n--;
  if (z==4 || map[x][y][z+1]) n--;
  return n;
} 
do {
  var index=Math.floor(Math.random()*points.length);
  point=points[index];
  v=[];
  x=point[0];
  y=point[1];
  z=point[2];
  spaces(x-1,y,z)==1 && v.push([x-1,y,z]);
  spaces(x,y-1,z)==1 && v.push([x,y-1,z]);
  spaces(x,y,z-1)==1 && v.push([x,y,z-1]);
  spaces(x+1,y,z)==1 && v.push([x+1,y,z]);
  spaces(x,y+1,z)==1 && v.push([x,y+1,z]);
  spaces(x,y,z+1)==1 && v.push([x,y,z+1]);
  if (v.length) {
    var point=v[Math.floor(Math.random()*v.length)];
    points.push(point);
    map[point[0]][point[1]][point[2]]=0;
    if (point[0]*point[1]*point[2]==48) {
      map[4][4][4]=0;
    } 
  } 
  else {
    var np=[];
    for (var i=0;i<points.length;++i) {
      i!=index && np.push(points[i]); 
    } 
    points=np;
  } 
} while(points.length);
for (i=0;i<5;++i) {
  for (j=0;j<5;++j) {
    map[i][j]=map[i][j].join('');
  } 
  map[i]=map[i].join();
} 
alert(map.join("\n"));

4

Mathematica: True Labyrinth (827 caratteri)

Inizialmente, ho prodotto un percorso da {1,1,1} a {5,5,5} ma poiché non c'erano possibili svolte sbagliate da fare, ho introdotto forchette o "punti di decisione" (vertici di grado> 2) in cui bisognerebbe decidere da che parte andare. Il risultato è un vero labirinto o labirinto.

I "vicoli ciechi" erano molto più difficili da risolvere che trovare un percorso semplice e diretto. La cosa più difficile è stata l'eliminazione dei cicli all'interno del percorso consentendo al contempo i cicli fuori dal percorso della soluzione.

Le seguenti due righe di codice vengono utilizzate solo per il rendering dei grafici disegnati, quindi il codice non conta, poiché non viene utilizzato nella soluzione.

o = Sequence[VertexLabels -> "Name", ImagePadding -> 10, GraphHighlightStyle -> "Thick", 
    ImageSize -> 600];

o2 = Sequence[ImagePadding -> 10, GraphHighlightStyle -> "Thick", ImageSize -> 600];

Codice utilizzato:

e[c_] := Cases[EdgeList[GridGraph[ConstantArray[5, 3]]], j_ \[UndirectedEdge] k_ /; (MemberQ[c, j] && MemberQ[c, k])]

m[] :=
Module[{d = 5, v = {1, 125}},
   While[\[Not] MatchQ[FindShortestPath[Graph[e[v]], 1, 125], {1, __, 125}],

v = Join[v, RandomSample[Complement[Range[125], v], 1]]];
   Graph[e[Select[ConnectedComponents[Graph[e[v]]], MemberQ[#, 1] &][[1]]]]]

w[gr_, p_] := EdgeDelete[gr, EdgeList[PathGraph[p]]]

y[p_, u_] := Select[Intersection[#, p] & /@ ConnectedComponents[u], Length[#] > 1 &]

g = HighlightGraph[lab = m[],  PathGraph[s = FindShortestPath[lab, 1, 125]],o]
u = w[g, s]
q = y[s, u]

While[y[s, u] != {}, u =  EdgeDelete[u, Take[FindShortestPath[u,  q[[1, r = RandomInteger[Length@q[[1]] - 2] + 1]], 
  q[[1, r + 1]]], 2] /. {{a_, b_} :> a \[UndirectedEdge] b}];

q = y[s, u]]

g = EdgeAdd[u, EdgeList@PathGraph[s]];

Partition[StringJoin /@ Partition[ReplacePart[Table["x", {125}], 
Transpose[{VertexList[g], Table["o", {Length[VertexList@g]}]}]/. {{a_, b_} :>  a -> b}], {5}], 5]

Uscita campione

{{"oxooo", "xxooo", "xoxxo", "xoxxo", "xxoox"}, {"ooxoo", "xoooo", "ooxox", "oooxx", "xooxx"}, {"oooxx", "ooxxo", "ooxox", "xoxoo", "xxxoo"}, {"oxxxx", "oooox", "xooox", "xoxxx", "oooxx"}, {"xxxxx", "ooxox", "oooox "," xoxoo "," oooxo "}}

Sotto il cappuccio

L'immagine seguente mostra il labirinto o labirinto che corrisponde alla soluzione ({{"ooxoo",...}}visualizzata sopra:

Soluzione 1

Ecco lo stesso labirinto inserito in un 5x5x5 GridGraph. I vertici numerati sono nodi sul percorso più breve fuori dal labirinto. Nota le forcelle oi punti di decisione in 34, 64 e 114. Includerò il codice utilizzato per il rendering del grafico anche se non fa parte della soluzione:

HighlightGraph[gg = GridGraph[ConstantArray[5, 3]], g,  
 GraphHighlightStyle ->"DehighlightFade", 
 VertexLabels -> Rule @@@ Transpose[{s, s}] ]

Solution2

E questo grafico mostra solo la soluzione al labirinto:

HighlightGraph[gg = GridGraph[ConstantArray[5, 3]], 
   Join[s, e[s]], GraphHighlightStyle -> "DehighlightFade", VertexLabels -> Rule @@@    Transpose[{s, s}] ]

solution3

Infine, alcune definizioni che possono aiutare a leggere il codice:

definizioni


Soluzione originale (432 caratteri, prodotto un percorso ma non un vero labirinto o labirinto)

Immagina un cubo solido 5x5x5 composto da cubi unità distinti. Quanto segue inizia senza cubetti unitari a {1,1,1} e {5,5,5}, poiché sappiamo che devono far parte della soluzione. Quindi rimuove i cubi casuali fino a quando non c'è un percorso senza ostacoli da {1,1,1} a {5,5,5}.

Il "labirinto" è il percorso più breve (se è possibile più di uno) dati i cubi unità che sono stati rimossi.

d=5
v={1,d^3}
edges[g_,c_]:=Cases[g,j_\[UndirectedEdge] k_/;(MemberQ[c,j]&&MemberQ[c,k])]

g:=Graph[v,edges[EdgeList[GridGraph[ConstantArray[d,d]]],v]];

While[\[Not]FindShortestPath[g,1,d^3]!={},
    v=Join[v,RandomSample[Complement[Range[d^3],v],1]]]

Partition[Partition[ReplacePart[
   Table["x",{d^3}],Transpose[{FindShortestPath[g,1,d^3],Table["o",{Length[s]}]}]
      /.{{a_,b_}:>  a->b}],{d}]/.{a_,b_,c_,d_,e_}:>  StringJoin[a,b,c,d,e],5]

Esempio:

{{"ooxxx", "xxxxx", "xxxxx", "xxxxx", "xxxxx"}, 
 {"xoxxx", "xoooo", "xxxxo", "xxxxo", "xxxxo"}, 
 {"xxxxx", "xxxxx", "xxxxx", "xxxxx", "xxxxo"}, 
 {"xxxxx", "xxxxx", "xxxxx", "xxxxx", "xxxxo"}, 
 {"xxxxx", "xxxxx", "xxxxx", "xxxxx", "xxxxo"}}

Tecnicamente questo non è ancora un vero labirinto, poiché non ci sono svolte sbagliate che si possano fare. Ma ho pensato che fosse interessante come inizio poiché si basa sulla teoria dei grafi.

La routine in realtà crea un labirinto ma ho inserito tutte le posizioni vuote che potrebbero dare origine a cicli. Se trovo un modo per rimuovere i cicli, includerò questo codice qui.


Bello aggiornamento, mi piace che la tua soluzione aggiornata consenta cicli su percorsi non di soluzione, creando un labirinto più confuso.
Sir_Lagsalot

Grazie. Vorrei ancora che il percorso della soluzione stessa avesse più probabilità di spostarsi di volta in volta dal nodo finale. Questo è attualmente scoraggiato (ma non completamente evitato) da FindShortestPath.
DavidC

Non ho troppa familiarità con matlab, ma potresti fare qualcosa come FindShortestPath, aggiungere un bias su ciascun nodo nel percorso più breve, quindi eseguire nuovamente FindShortestPath tenendo conto del bias in modo che eviterà i nodi nella soluzione più breve? Questo potrebbe essere fatto anche iterativamente. Sarei interessato a vedere che tipo di percorso avrebbe prodotto.
Sir_Lagsalot

@Sir_Lagsalot Ho pubblicato questa domanda come domanda per il gruppo Mathematica SE qui ( mathematica.stackexchange.com/questions/4084/… )
DavidC
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.