Crie um labirinto 5x5x5 multinível com apenas uma solução

11

O objetivo desse desafio é criar o código mais curto (em caracteres) que faça com sucesso o seguinte:

Especificações :

  • É necessário criar um 5x5x5 labyrinthcom exatamente 1 possible solution(nem mais, nem menos)
  • O labirinto deve ser criado randomly Ele deve ser capaz de gerar todas as soluções existentes se permanecer em funcionamento por anos
  • O starte finishdeve ser colocado em*opposite corners
  • O mapa outputdeve em um dos seguintes formatos:

Formato de saída da opção 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 de saída da opção 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]]

Notas de saída:

  • Use 0para emptye 1parasquares

  • NÃO são necessárias linhas de quebra

  • Você decide o que indexé o quê, mas apenas explique bem


* Aqui está um exemplo do que quero dizer com cantos opostos:

insira a descrição da imagem aqui

Esclarecimentos :

  • NÃO pode se mudardiagonal
  • NÃO pode passar duas vezes no mesmo caminho
  • Ter inaccessible areasé permitido
  • Você pode go up/downmais de um nível em uma linha

Dicas:

  • Não os veja como paredes, mas como uma 5x5x5pilha de quadrados que alguns deles estão faltando e você pode passar pelos que estão faltando
ajax333221
fonte
Se algo não estiver claro, basta perguntar-me :)
ajax333221
3
No entanto, há um detalhe que eu gostaria de esclarecer: as paredes são colocadas entre quadrados ou uma parede preenche um quadrado inteiro?
Ilmari Karonen
1
você diz 5x5 (uma matriz 2D) em alguns lugares, mas os exemplos de código e a imagem sugerem 5x5x5 (uma matriz 3D). Presumo que a matriz 3D é o que se entende?
27412 Kae Verens
1
como é decidido que a solução é um labirinto válido? Quero dizer, é o número de ramificações que o caminho certo tem? isso tem algo a ver com a proporção de 1s para 0s?
Kae verens
2
Quando você diz "O labirinto deve ser criado aleatoriamente", que limitações devemos inferir? Presumo, por exemplo, que você não pretende permitir, como uma leitura literal das regras atualmente faz, um programa que escolhe entre duas saídas codificadas aleatoriamente.
Peter Taylor

Respostas:

10

C ++ C, cerca de 1000 670 643 395 297 248 caracteres

Saída de amostra:

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,

Como funciona: O programa usa o Brownian Motion para gerar soluções. O ponto inicial está definido. Em seguida, um ponto aleatório é selecionado e movido aleatoriamente repetidamente até tocar um e apenas um ponto no ramo inicial. O ponto é então definido e, se também tocar no ponto final, o programa sai e a matriz é exibida. Como nenhum ponto pode unir dois ramos, há apenas um caminho através do labirinto. O programa usa a função rand, e um argumento inteiro da linha de comando como a semente, portanto , com uma função rand suficiente, deve ser possível gerar eventualmente todos os labirintos válidos (esse algoritmo não criará áreas não conectadas, portanto, não gerará tudo labirintos possíveis ).

O movimento browniano foi descartado, pois acabou sendo desnecessário e sua remoção simplifica significativamente o código. Eu acho que isso fez labirintos mais agradáveis. Da mesma forma, o argumento de propagação foi descartado, pois exigir um gerador de números aleatórios sem estado faz mais sentido para mim do que uma propagação de 128 bits.

É possível que o programa fique preso em um loop infinito, pois é possível em situações em que qualquer ponto adicionado às ramificações criaria vários caminhos. Isso é corrigível, mas acho que é raro o suficiente para não ser uma preocupação para o código de golfe.

#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);
}

Adicionei novas linhas e recuo ao código exibido para facilitar a leitura.

Sir_Lagsalot
fonte
Eu acho que você venceu este ;-) não tem como eu encolher o meu até agora
Kae Verens 27/03
Eu realmente gostei da competição :-) Estou um pouco surpreso por ainda sermos as únicas respostas, eu esperava que um roteirista de golfe ou similar já tivesse nos derrotado.
Sir_Lagsalot
De alguma forma, um caminho simples, sem garfos ou nós de decisão, parece não se qualificar como um verdadeiro labirinto. Tente adicionar alguns becos sem saída.
8262
@ David Carraher O algoritmo gera becos sem saída e caminhos de ramificação, como mostrado na amostra. Não permitir que um novo ponto conecte duas ramificações já existentes simplesmente impede várias soluções ou ciclos no labirinto.
Sir_Lagsalot
@Sir_Lagsalot Obrigado pelo esclarecimento
DavidC
5

JavaScript, 874 816 788 686 682 668 637 caracteres

saída de amostra:

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

este funciona iniciando do ponto [0,0,0] e adicionando aleatoriamente anexando mais um 0 ao lado de um 0 sempre que permitido (permitido == o novo 0 não fica próximo a nenhum outro 0, exceto o originador) até que não haja mais possíveis adições.

se qualquer novo 0 estiver próximo ao ponto de saída (x * y * z == 48), abriremos a saída.

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"))

original

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"));
Kae Verens
fonte
4

Mathematica: True Labyrinth (827 caracteres)

Originalmente, produzi um caminho de {1,1,1} a {5,5,5}, mas como não havia possíveis desvios errados, introduzi garfos ou "pontos de decisão" (vértices de grau> 2) onde seria preciso decidir qual caminho seguir. O resultado é um verdadeiro labirinto ou labirinto.

Os "becos sem saída" eram muito mais difíceis de resolver do que encontrar um caminho simples e direto. A coisa mais desafiadora foi eliminar os ciclos no caminho, permitindo ciclos fora do caminho da solução.

As duas linhas de código a seguir são usadas apenas para renderizar os gráficos desenhados, portanto, o código não conta, pois não é empregado na solução.

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

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

Código usado:

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]

Saída de amostra

{{"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 "}}

Sob o capô

A imagem abaixo mostra o labirinto ou labirinto que corresponde à solução ({{"ooxoo",...}}exibida acima:

solution1

Aqui está o mesmo labirinto inserido em um 5x5x5 GridGraph. Os vértices numerados são nós no caminho mais curto para fora do labirinto. Observe os garfos ou os pontos de decisão em 34, 64 e 114. Vou incluir o código usado para renderizar o gráfico, mesmo que não faça parte da solução:

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

solution2

E este gráfico mostra apenas a solução para o labirinto:

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

solution3

Por fim, algumas definições que podem ajudar na leitura do código:

definições


Solução original (432 caracteres, produziu um caminho, mas não um verdadeiro labirinto ou labirinto)

Imagine um cubo sólido de 5x5x5, composto por cubos de unidades distintas. O seguinte começa sem cubos de unidades em {1,1,1} e {5,5,5}, pois sabemos que eles devem fazer parte da solução. Em seguida, ele remove cubos aleatórios até que haja um caminho desimpedido de {1,1,1} para {5,5,5}.

O "labirinto" é o caminho mais curto (se mais de um for possível), considerando os cubos da unidade que foram removidos.

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]

Exemplo:

{{"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, este ainda não é um verdadeiro labirinto, já que não há giros errados que alguém possa fazer. Mas achei interessante como um começo, uma vez que se baseia na teoria dos grafos.

A rotina realmente faz um labirinto, mas liguei todos os locais vazios que poderiam dar origem a ciclos. Se eu encontrar uma maneira de remover ciclos, incluirei esse código aqui.

DavidC
fonte
Boa atualização, eu gosto que sua solução atualizada permita ciclos em caminhos que não são de solução, criando um labirinto mais confuso.
Sir_Lagsalot
Obrigado. Eu ainda gostaria que o caminho da solução em si tivesse maior probabilidade de se afastar do nó final de tempos em tempos. Atualmente, isso é desencorajado (mas não totalmente evitado) por FindShortestPath.
9133
Eu não estou muito familiarizado com o matlab, mas você poderia fazer algo como FindShortestPath, adicionar um viés contra cada nó no caminho mais curto e executar o FindShortestPath novamente, levando em consideração o viés, para evitar nós na solução mais curta? Isso também pode ser feito iterativamente. Eu estaria interessado em ver que tipo de caminho isso produziria.
Sir_Lagsalot
@Sir_Lagsalot eu postei isso como uma pergunta para o grupo Mathematica SE aqui ( mathematica.stackexchange.com/questions/4084/... )
DavidC