2D Maze Minus 1D


27

Este desafio consiste em converter labirintos 2D em labirintos 1D.

visão global

+-+-+-+-+-+-+   +-+-+-+-+-+-+                    graph {
| |   |     |   |A|   |    B|   A         B        A -- D
+ + + + +-+-+   + + + + +-+-+    \        |        C -- D
|   | |     |   |   | |     |     \       |        D -- E
+-+-+ +-+-+ +   +-+-+ +-+-+ +      \      |        E -- F
|           |   |C   D E   F|   C---D-E---F        E -- G
+-+-+-+ +-+ +   +-+-+-+ +-+ +         |   |        B -- F
|         | |   |      G  | |     .---G   |        F -- J
+ +-+-+-+ + +   + +-+-+-+ + +   .'   /    |        G -- H
| |       | |   |H|I      |J|   H I-'     J        G -- I
+-+-+-+-+-+-+   +-+-+-+-+-+-+     (ascii)        } // (graphviz dot)       
   Figure 1       Figure 2                 Figure 3

Para os propósitos deste desafio, um labirinto 2D tradicional é um labirinto retangular formado a partir de pontos de treliça, onde tudo o que se segue é válido:

  • Está fechado (a borda externa é conectada por paredes).
  • Todos os pontos de rede estão conectados às paredes
  • Está conectado (para cada dois espaços X e Y existe um caminho entre eles)
  • É acíclico (não há caminhos de nenhum espaço X de volta para X sem retroceder)

A Figura 1 mostra um labirinto 2D tradicional. Esses labirintos têm três áreas de interesse:

  • Becos sem saída - locais a partir dos quais existe apenas um caminho disponível
  • Corredores - locais a partir dos quais existem dois caminhos disponíveis
  • Pontos de decisão - locais a partir dos quais existem três ou quatro caminhos disponíveis

Para cada labirinto, pode-se criar um gráfico onde os becos sem saída e os pontos de decisão são nós, e há uma aresta entre cada dois nós conectados por um caminho ao longo de um corredor. A Figura 2 mostra o mesmo labirinto com esses nós rotulados e a Figura 3 o gráfico do labirinto (na notação de ponto ASCII e Graphviz).

Labirintos 1D

Os labirintos 1D incorporam pontos de distorção, que vêm em pares, e são identificados usando uma letra (em ambos os casos). A Figura 4 mostra um exemplo de labirinto 1D. Caso contrário, é idêntico a um labirinto 2D com uma altura de 1, como mostrado na Figura 5. Observe em particular na Figura 5 as posições dos pontos de treliça marcados por +, que alternam da esquerda para a direita; no labirinto 1D, todos os outros caracteres que começam com a parede mais à esquerda também são um ponto de treliça.

                                 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|  D|  D E|G E F|  F  |  G  |    |  D|  D E|G E F|  F  |  G  |
                                 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
            Figure 4                         Figure 5

As regras para navegar neste labirinto são as seguintes. Cada movimento pode ser representado como avançar ( >) ou voltar ( <). Avançar e retroceder aqui, por padrão, tem o mesmo significado que nossa percepção espacial intuitiva; frente vai para a posição imediatamente à direita e para trás imediatamente à esquerda.

Os pontos de distorção representam locais que trocam assimetricamente a conexão com os vizinhos. Se você está vindo de um vizinho para um ponto de distorção, a posição dos dois pontos de distorção é trocada; se você estiver vindo de um ponto de distorção para um vizinho, eles não serão trocados. Por exemplo, na Figura 6, retroceder de 1 leva você a 2 (como 1 é o vizinho de G e estamos saindo do vizinho, os pontos 2 e @ são trocados). Avançar de 2 (ponto de distorção G) leva você a 3 (aqui, estamos começando a partir de um ponto de distorção, portanto não há troca). Da mesma forma, retroceder de 3 leva você a @.

        54 2367    89^   @1
|  D|  D E|G E F|  F  |  G  |
                     Y     X
          Figure 6

A Figura 6 também mostra um exemplo de navegação de X para Y, usando a sequência de movimentos <<>><>>>>>. Esses movimentos levam você aos pontos marcados 123456789^respectivamente, nessa ordem. Sinta-se livre para explorar você mesmo usando o snippet de código na próxima seção.

Convertendo 2D para 1D

Dado um labirinto 1D, é possível criar um gráfico em que cada nó é um beco sem saída ou um par de pontos de distorção, e existem arestas entre quaisquer dois nós conectados ao longo de um corredor. Este gráfico nos permite comparar labirintos 1D e 2D.

Por exemplo, o labirinto 1D na Figura 4 é o mesmo labirinto na Figura 1. Para ver por que, a Figura 7 adiciona etiquetas aos becos sem saída. Usando esses rótulos para criar um gráfico, o gráfico da Figura 7 é simplesmente a Figura 3 novamente. A Figura 8 mostra uma quebra de estrutura deste gráfico.

|  D|  D E|G E F|  F  |  G  |
 A   C           B   J H   I 
          Figure 7

|  D|  D E|G E F|  F  |  G  |
+ + + + + + + + + + + + + + + <- lattice points
|A  |C    |     |B   J|H   I| <- dead ends
|A D|C D E|G E F|B F J|H G I| <- all nodes (dead ends+warp points); i.e.:
                                 "where each end is either a dead end
                                  or a warp point pair"; note that each
                                  pair of warp points is the same node.
|A-D|C-D-E|G-E-F|B-F-J|H-G-I| <- corridors; note each is a connection, since
  1   2 3   4 5   6 7   8 9      "edges exist between any two nodes
                                  connected along a corridor"
   graph {                 graph {                 
     A -- D  // 1 <---->     A -- D                
     C -- D  // 2 <---->     C -- D                
     D -- E  // 3 <---->     D -- E                
     G -- E  // 4 <---->     E -- G                
     E -- F  // 5 <---->     E -- F                
     B -- F  // 6 <---->     B -- F                
     F -- J  // 7 <---->     F -- J                
     H -- G  // 8 <---->     G -- H                
     G -- I  // 9 <---->     G -- I                
   }                ^      }
    Built from      |      From Figure 3
     1D maze         `-> isomorphic mappings
                Figure 8

(Observe que os rótulos e o layout de cada gráfico foram escolhidos artificialmente para alinhar para fins ilustrativos; geralmente, esse é um problema de isomorfismo do gráfico ).

O trecho a seguir é fornecido para ajudar a visualizar a mecânica do labirinto 1D e a conexão entre o labirinto 1D, o gráfico de equivalência e o labirinto 2D.

À medida que você navega no labirinto 1D neste trecho, os dois últimos nós que você toca são realçados. Os mesmos nós são destacados da mesma maneira no gráfico de equivalência e no labirinto 2D.


Em geral, para qualquer labirinto 2D tradicional, um labirinto 1D equivalente deste tipo pode ser criado. Um exemplo um pouco mais complexo é a Figura 9:

+-+-+-+-+-+-+   +-+-+-+-+-+-+                   graph {
| |   |   | |   |A|   |   |B|   A         B       A -- D
+ + + + + + +   + + + + + + +    \       /        C -- D
|   | | |   |   |   | | |   |     \     /         D -- E
+-+-+ + +-+-+   +-+-+ + +-+-+      \   /          B -- E
|           |   |C   D E    |   C---D-E           E -- F
+-+-+-+ +-+ +   +-+-+-+ +-+ +         |\          E -- I
|         | |   |      F  | |     .---F \         F -- G
+ +-+-+-+ + +   + +-+-+-+ + +   .'   /   \        G -- H
| |       | |   |G|H      |I|   G H-'     I       H -- I
+-+-+-+-+-+-+   +-+-+-+-+-+-+     (ascii)       } // (graphviz dot)
   Figure 9       Figure 10             Figure 11

|  D|  D E  |F E  |  F  |       |  D|  D E  |F E  |  F  |
                                 A   C     I     B G   H
      Figure 12                       Figure 13

Este labirinto possui um nó com quatro caminhos (E na Figura 10). A Figura 11 mostra seu gráfico. A Figura 12 é um labirinto 1D equivalente; e a Figura 13 mostra o mesmo labirinto com rótulos para becos sem saída para comparar com a Figura 11.

Desafio

Dado um Labirinto 2D como entrada, escreva uma função ou programa que transforma o labirinto 2D em um labirinto 1D com pontos de distorção. Os pontos de distorção podem usar qualquer uma das 52 letras em cada caso.

Garantias de entrada (se alguma delas não for atendida na entrada, você não precisará lidar com isso):

  • O labirinto de entrada está conectado (ou seja, você sempre pode ir de qualquer ponto para outro).
  • O labirinto de entrada está fechado.
  • O labirinto de entrada é retangular.
  • Todos os pontos de rede são usados +.
  • Todas as paredes entre os pontos de treliça na mesma linha usam |
  • Todas as paredes entre os pontos da rede na mesma coluna são usadas -.
  • Todos os espaços fazem parte de um caminho (e todos dentro do labirinto).
  • Caminhos são todos os espaços (isso sempre será tradicional, sem distorções)
  • Os caminhos têm exatamente um espaço amplo.
  • O labirinto é construído conectando pontos em uma treliça.
  • Não há mais de 52 nós no total (ou seja, becos sem saída mais pontos de decisão) no gráfico do labirinto.

Formato de saída:

  1. Sua saída deve ser uma única linha mostrando um labirinto 1D.
  2. Sua saída não deve ter espaços em branco à esquerda / à direita; exceto que uma nova linha à direita está correta.
  3. O primeiro caractere e todos os outros caracteres depois são pontos de treliça.
  4. Todas as paredes devem estar em pontos de treliça; e todos os pontos de distorção entre eles.
  5. O gráfico do seu labirinto 1D deve ser equivalente ao gráfico do labirinto 2D.
  6. Seus labirintos 1D devem ser compactos; todos os pontos que não são de treliça devem ter becos sem saída (isto é, adjacentes às paredes) ou pontos de distorção.
  7. As únicas letras em sua saída devem ser pontos de distorção. Cada ponto de distorção ocorre na linha exatamente duas vezes.

Exemplo:

|  D|  D E|G E F|  F  |  G  | <- (1,2) The single line output
+ + + + + + + + + + + + + + + <- lattice point spacing... (3) 
                                 (4,6) lattice points are all walls or spaces
                                 (5) See Figure 8
                                 (7) D, E, F, G appear twice; no other labels

Isso é código-golfe. O vencedor é o envio correto, sem brechas, com o mínimo de bytes.

Teste

Não há casos de teste para esse desafio, pois há um grande número de saídas corretas para qualquer labirinto não trivial.

No entanto, construí um verificador em C ++ (esse verificador representa graficamente as duas soluções por meio de uma canonização de gráfico ).

Além disso, aqui estão alguns exemplos para ajudar a ilustrar a formatação correta:

Exemplo 1

+-+-+-+-+-+-+
| |   |     |
+ + + + +-+-+
|   | |     |
+-+-+ +-+-+ +
|           |
+-+-+-+ +-+ +
|         | |
+ +-+-+-+ + +
| |       | |
+-+-+-+-+-+-+
->
|  D|  D E|G E F|  F  |  G  |

Exemplo 2

+-+-+-+-+-+-+
| |   |   | |
+ + + + + + +
|   | | |   |
+-+-+ + +-+-+
|           |
+-+-+-+ +-+ +
|         | |
+ +-+-+-+ + +
| |       | |
+-+-+-+-+-+-+
->
|  D|  D E  |F E  |  F  |

Mais exemplos podem ser encontrados aqui .


1
Eu não acho que a explicação dos labirintos 1D seja muito clara ... Talvez adicionar um exemplo menor / mais simples ajudaria.
mbomb007

Isso é bem legal. Sim, isso ajuda.
Mbomb007

Mesmo que seu script interativo tenha ajudado, ainda é um problema difícil. Então eu apenas pulo. Meu entendimento disso ainda é tênue, na melhor das hipóteses.
mbomb007

A descrição do labirinto 1D é superficial. Eu tive que ler a figura 7 para entender que os caracteres da barra vertical em um labirinto 1D são paredes que você não pode passar por cima.
edc65

1
exemplo 1 com o labirinto 1d empilhado em um labirinto 2d em que cada par de letras é uma escada: gist.github.com/sparr/36d6355cc4c785a27b12157666169082
Sparr

Respostas:


3

Python 2 com igraph , 492 369 bytes

import igraph,string
def f(s):
 C=s.find('\n')/2;N=' ';g=igraph.Graph(0,[(i,i+j)for i in range(len(s)/(4*C+4)*C)for j in(1,C)if s[(i/C*2+1)*(2*C+2)+i%C*2+2*j+j/C*3]==N]);V=g.vs;g.d=g.degree;O='';V(_d=1)[N]=N;V(_d_gt=2)[N]=list(string.ascii_letters)
 while g.es:
    v=V(_d=1)[0];O+='|'+v[N]
    while g.d(v):w=v.neighbors()[0];g-=(v,w);v=w;O+=N+v[N]if v[N]else''
 print O+'|'

(A quinta e a sexta linhas, cada uma começa com uma guia, não quatro espaços, como mostra o StackExchange.)

  • Economizou seis bytes reorganizando alguma aritmética
  • Salvou sete bytes usando uma fatia em vez de zip
  • Salva três bytes usando em g+=tuple(v.neighbors())vez deg.add_edge(*v.neighbors())
  • Salvou sete bytes usando em g-=g.es[g.incident(v)]vez deg.delete_edges(g.incident(v))
  • Alias ​​de onze bytes salvos g.d=g.degree
  • Economizou 52 bytes (!), Eliminando um loop que contraiu todos os corredores, substituindo os vértices de grau 2 por uma borda entre os vizinhos. Em vez disso, o loop de saída apenas ignora esses vértices.
  • Salva 13 bytes, notando que, ao atribuir nomes, o igraph não se importa se o iterável fornecido for muito longo
  • Salva quatro bytes por não ter uma variável Rpara o número de linhas, movendo seu cálculo para o único ponto de uso
  • Salvou dois bytes alterando a indentação de segundo nível para guias em vez de espaços
  • Salvo seis bytes reorganizando 2*(i%C)para i%C*2, 2*(i/C)para i/C*2e (C-1)*jparaj*C-j
  • Nomeação de quatro bytes salvos N='n'
  • Salvo um byte, determinando se um caractere está usando espaço, <'-'e não ==' ', supondo que apenas caracteres válidos apareçam.
  • Então percebi que posso nomear o atributo de vértice em ' 'vez de 'n'e reutilizar Npara os dois espaços literais na fonte e, em ==Nvez de <'-', salvar mais cinco bytes

A seguir, uma versão não-destruída. A idéia básica é primeiro fazer um gráfico em todos os vértices do labirinto (os pontos com linha e coluna ímpares quando indexados a zero.) Há uma aresta de um vértice para o próximo na mesma linha se o caractere a seguir for um espaço e não |. Existe uma aresta do vértice para a logo abaixo, se o caractere correspondente na linha a seguir for um espaço, e não -.

Depois de construir este gráfico, escolhemos qualquer folha e seguimos vértices sucessivamente adjacentes, escrevendo seus nomes se eles não forem um corredor e excluindo bordas usadas, até ficarmos presos. Então pegamos outra folha e continuamos até que todas as bordas se foram.

import string
import igraph
def f(s):
  C = s.find('\n')/2 # number of maze vertices in each row
  R = len(s)/(4*C+4) # number of rows
  def strpos(r, c):
    """Index of the vertex at row r, col c in the newline-delimited string s"""
    return (2*r+1)*(2*C+2) + 2*c + 1
  def vertpos(i):
    """Index of the i-th vertex in s"""
    return strpos(i/C, i%C)
  g = igraph.Graph(edges=[(i, i+(C if j else 1))
                          for i in range(R*C)
                          for j in (0, 1)
                          if s[vertpos(i)+(2*C+2 if j else 1)] == ' '])
  V = g.vs # the graph's vertex sequence
  O = ''
  V(_degree=1)['n'] = ' ' # All leaves are named space
  W = V(_degree_gt=2) # All warp points...
  W['n'] = list(string.ascii_letters[:len(W)]) # ...are named successive letters
  while g.es: # while any edges remain...
    v = V(_degree=1)[0] # find a leaf
    O += '|'+v['n'] # start a new 'block'
    while v.degree():
      w = v.neighbors()[0] # pick a neighbor
      g -= (v, w) # delete that edge
      v = w
      if v['n']: # If it's a dead end or warp point...
        O += ' '+v['n'] # ...write out the new neighbor
  print O+'|'

Você pode ver os resultados para os cinco exemplos de labirintos . (Infelizmente, igraphnão está disponível no Try It Online; esses resultados foram exportados do SageMathCloud .)


4

Haskell - 481 405 387 bytes

import Data.List
s&t=elemIndices s t
l=last
c!(x:y:z)=l$(y:c)!(x:z):do{[x:p,q]<-mapM([id,reverse]<*>)[[x],[y]];x&[l q];[[]!((q++p):c++z)]}
c![x]=x:[]!c
c!z=z
main=interact(\m->let{g=' '&m;
u=(\\[k|k<-g,length(v>>=(k&))==2])<$>[]!v;
v=[[x,y]|x<-g,y<-g,elem(y-x-1)[0,head$'\n'&m]];
}in '|':(u>>=(++"|").init.(>>=(:" ").toEnum.((+)<*>(+65).(*32).(`div`26)).l.(-1:).(&(nub$u>>=init.tail)))))

Isso cria uma lista de espaços que estão no labirinto, numerados por índice na cadeia de caracteres e o usa para encontrar todos os pares de espaços adjacentes. Em seguida, costura os pares em seqüências mais longas de pontos com base no primeiro / último elementos correspondentes e remove os corredores, de modo que cada sequência seja uma sala no labirinto 1D. As seqüências são traduzidas em uma seqüência de caracteres substituindo pontos no interior de pelo menos uma sala (os pontos de distorção) em letras correspondentes e o restante em espaços.

O labirinto 2D é lido em STDIN e o labirinto 1D é impresso em STDOUT.

Edit: Reduzido por 62 bytes, reorganizando um monte de coisas e modificando um pouco o algoritmo, e outros 14, substituindo chr-o toEnumpor sugerido por Laikoni.

Editar 2: salvou mais 13 bytes simplificando a lógica (!), 3 usando o padrão de lista correspondente a sugar e 2 usando >>=para concatenar u.


Eu acho que você não precisa das novas linhas e espaços antes dos protetores de padrão, por exemplo, o(x:p)q|x==last q=[q++p]|1>0=[]deve funcionar também.
Laikoni

Também toEnumdeve funcionar em vez de chr, então import Data.Charpode ser descartado.
Laikoni

E, por último, como o desafio pede um programa ou função, você pode substituir main=interact(\m->...)por apenas f m=.... Isso deve ser suficiente para vencer a resposta do python, se isso significa alguma coisa para você.
Laikoni
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.