2D Maze Minus 1D


27

Questa sfida riguarda la conversione di labirinti 2D in labirinti 1D.

Panoramica

+-+-+-+-+-+-+   +-+-+-+-+-+-+                    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

Ai fini di questa sfida, un labirinto 2D tradizionale è un labirinto rettangolare formato da punti reticolari in cui sono presenti tutti i seguenti elementi:

  • È chiuso (il bordo esterno è collegato da pareti).
  • Tutti i punti reticolari sono collegati alle pareti
  • È collegato (per ogni due spazi X e Y c'è un percorso tra di loro)
  • È aciclico (non ci sono percorsi da qualsiasi spazio da X a X senza backtracking)

La Figura 1 mostra un labirinto 2D tradizionale. Questi labirinti hanno tre aree di interesse:

  • Strade senza uscita : luoghi da cui esiste un solo percorso disponibile
  • Corridoi : luoghi da cui sono disponibili due percorsi
  • Punti decisionali : luoghi da cui sono disponibili tre o quattro percorsi

Per ogni labirinto di questo tipo, è possibile creare un grafico in cui i vicoli ciechi e i punti di decisione sono nodi e c'è un bordo tra ogni due nodi collegati da un percorso lungo un corridoio. La Figura 2 mostra lo stesso labirinto con tali nodi etichettati e la Figura 3 il grafico del labirinto (in notazione con punti ASCII e Graphviz).

Labirinti 1D

I labirinti 1D incorporano punti di curvatura, che vengono in coppia, e sono identificati usando una lettera (in entrambi i casi). La Figura 4 mostra un labirinto 1D di esempio. Questo è altrimenti identico a un labirinto 2D con un'altezza di 1, come mostrato nella Figura 5. Notare in particolare nella Figura 5 le posizioni dei punti reticolari contrassegnate da +, che si alternano da sinistra a destra; nel labirinto 1D, ogni altro personaggio che inizia con la parete più a sinistra è anche un punto reticolare.

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

Le regole per navigare in questo labirinto sono le seguenti. Ogni mossa può essere rappresentata come avanti ( >) o indietro ( <). Avanti e indietro qui di default hanno lo stesso significato della nostra consapevolezza spaziale intuitiva; avanti va immediatamente nella posizione a destra e indietro immediatamente a sinistra.

I punti di curvatura rappresentano posizioni che scambiano asimmetricamente la connessione con i vicini. Se vieni da un vicino a un punto di curvatura, la posizione dei due punti di curvatura viene scambiata; se provieni da un punto di curvatura a un vicino, questi non vengono scambiati. Ad esempio, nella Figura 6, spostarsi all'indietro da 1 ti porta a 2 (poiché 1 è il vicino di G, e ci stiamo spostando dal vicino, i punti 2 e @ vengono scambiati). Andare avanti da 2 (punto di curvatura G) ti porta a 3 (qui, stiamo iniziando da un punto di curvatura, quindi non c'è scambio). Allo stesso modo, spostarsi indietro da 3 ti porta a @.

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

La Figura 6 mostra anche un esempio di navigazione da X a Y, usando la sequenza di mosse <<>><>>>>>. Queste mosse portano ai punti etichettati 123456789^rispettivamente, in quell'ordine. Sentiti libero di esplorarlo tu stesso usando lo snippet di codice nella sezione successiva.

Conversione da 2D a 1D

Dato un labirinto 1D, si può creare un grafico in cui ogni nodo è una strada senza uscita o una coppia di punti di curvatura, e esistono dei bordi tra due nodi collegati collegati lungo un corridoio. Questo grafico ci consente di confrontare labirinti 1D e 2D.

Ad esempio, il labirinto 1D nella Figura 4 è lo stesso labirinto nella Figura 1. Per capire perché, la Figura 7 aggiunge etichette ai vicoli ciechi. Usando quelle etichette per costruire un grafico, il grafico di Figura 7 è semplicemente di nuovo Figura 3. La Figura 8 mostra una panoramica della costruzione di questo grafico.

|  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

(Si noti che le etichette e il layout di ciascun grafico sono stati scelti artificialmente per essere allineati a scopo illustrativo; in generale si tratta di un problema di isomorfismo del grafico ).

Il seguente frammento viene fornito per aiutare a visualizzare la meccanica del labirinto 1D e la connessione tra il labirinto 1D, il grafico di equivalenza e il labirinto 2D.

Mentre navighi nel labirinto 1D in questo frammento, vengono evidenziati gli ultimi due nodi che tocchi. Gli stessi nodi sono evidenziati allo stesso modo nel grafico di equivalenza e nel labirinto 2D.


In generale, per qualsiasi labirinto 2D tradizionale è possibile creare un labirinto 1D equivalente di questo tipo. Un esempio leggermente più complesso è la 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

Questo labirinto ha un nodo con quattro percorsi (E in Figura 10). La Figura 11 mostra il suo grafico. La Figura 12 è un labirinto 1D equivalente; e la Figura 13 mostra lo stesso labirinto con etichette per vicoli ciechi da confrontare con la Figura 11.

Sfida

Dato un labirinto 2D come input, scrivi una funzione o un programma che trasforma il labirinto 2D in un labirinto 1D con punti di curvatura. I punti di curvatura possono usare una delle 52 lettere in ogni caso.

Garanzie di input (se uno di questi non è soddisfatto nell'input non è necessario gestirlo):

  • Il labirinto di input è collegato (vale a dire, puoi sempre passare da un punto all'altro).
  • Il labirinto di input è chiuso.
  • Il labirinto di input è rettangolare.
  • Vengono utilizzati tutti i punti reticolari +.
  • Tutte le pareti tra i punti reticolari sulla stessa riga utilizzano |
  • Tutte le pareti tra punti reticolari nella stessa colonna utilizzano -.
  • Tutti gli spazi fanno parte di un percorso (e tutti all'interno del labirinto).
  • I percorsi sono tutti spazi (questo sarà sempre tradizionale, non deformabile)
  • I percorsi hanno esattamente uno spazio.
  • Il labirinto è costruito collegando punti su un reticolo.
  • Non ci sono più di 52 nodi totali (es. Vicoli ciechi più punti di decisione) nel grafico del labirinto.

Formato di output:

  1. L'output dovrebbe essere una riga singola che mostra un labirinto 1D.
  2. L'output non dovrebbe contenere spazi iniziali / finali; tranne che una nuova riga finale va bene.
  3. Il primo personaggio e ogni altro personaggio successivo sono punti reticolari.
  4. Tutte le pareti dovrebbero essere su punti reticolari; e tutti i punti di curvatura tra di loro.
  5. Il grafico del labirinto 1D dovrebbe essere equivalente al grafico del labirinto 2D.
  6. I tuoi labirinti 1D devono essere compatti; tutti i punti non reticolari devono essere vicoli ciechi (cioè adiacenti alle pareti) o punti di curvatura.
  7. Le uniche lettere nel tuo output dovrebbero essere i punti di curvatura. Ogni punto di curvatura si presenta sulla linea esattamente due volte.

Esempio:

|  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

Questo è code-golf. Il vincitore è l'invio corretto senza scappatoie con il minor numero di byte.

analisi

Non ci sono casi di test per questa sfida, poiché esiste un numero elevato di output corretti per qualsiasi labirinto non banale.

Ho, tuttavia, creato un correttore in C ++ (questo controllore rappresenta entrambe le soluzioni attraverso una canonicalizzazione grafica ).

Inoltre, ecco alcuni esempi per illustrare la formattazione corretta:

Esempio 1

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

Esempio 2

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

Altri esempi possono essere trovati qui .


1
Non penso che la spiegazione dei labirinti 1D sia molto chiara ... Forse l'aggiunta di un esempio più piccolo / più semplice sarebbe di aiuto.
mbomb007,

È abbastanza bello. Sì, questo aiuta.
mbomb007,

Anche se il tuo script interattivo ha aiutato, è ancora un problema difficile. Quindi lo salto e basta. La mia comprensione di questo è ancora alquanto tenue.
mbomb007,

La descrizione del labirinto 1D è imprecisa. Ho dovuto leggere fino alla figura 7 per capire che i personaggi della barra verticale in un labirinto 1D sono pareti sulle quali non puoi passare.
edc65,

1
esempio 1 con il labirinto 1d impilato in un labirinto 2d dove ogni coppia di lettere è una scala: gist.github.com/sparr/36d6355cc4c785a27b12157666169082
Sparr

Risposte:


3

Python 2 con igraph , 492 369 byte

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+'|'

(La quinta e la sesta riga iniziano ciascuna con una scheda, non con quattro spazi come mostra StackExchange.)

  • Hai salvato sei byte riorganizzando l'aritmetica
  • Ho salvato sette byte usando una slice anziché zip
  • Ho salvato tre byte usando g+=tuple(v.neighbors())invece dig.add_edge(*v.neighbors())
  • Ho salvato sette byte usando g-=g.es[g.incident(v)]invece dig.delete_edges(g.incident(v))
  • Aliasing di undici byte salvato g.d=g.degree
  • Salvato 52 byte (!) Eliminando un circuito che contrasse tutti i corridoi sostituendo i vertici di grado 2 con un bordo tra i loro vicini. Al contrario, il loop di output ignora questi vertici.
  • Salvati 13 byte notando che quando si assegnano i nomi, a igraph non importa se l'iterabile fornito è troppo lungo
  • Salvati quattro byte non avendo una variabile Rper il numero di righe, spostando il suo calcolo sull'unico punto d'uso
  • Due byte salvati cambiando il rientro di secondo livello in tab invece che in spazi
  • Sono stati salvati sei byte riorganizzati 2*(i%C)in i%C*2, 2*(i/C)verso i/C*2e (C-1)*jversoj*C-j
  • Nome di quattro byte salvato N='n'
  • È stato salvato un byte determinando se un carattere è spazio utilizzando <'-'anziché anziché ==' ', presupponendo che vengano visualizzati solo caratteri validi.
  • Quindi mi sono reso conto che posso nominare l'attributo vertice ' 'invece di 'n', e riutilizzarlo Nper i due spazi letterali nel sorgente e, ==Ninvece di <'-', salvare altri cinque byte

Segue una versione un po 'non golfata. L'idea di base è di creare prima un grafico su tutti i vertici del labirinto (i punti con riga e colonna dispari quando sono indicizzati a zero.) C'è un bordo da un vertice al successivo sulla stessa riga se il carattere seguente è uno spazio e non |. C'è un bordo dal vertice a quello proprio sotto di esso se il carattere corrispondente nella riga seguente è uno spazio e non -.

Dopo aver creato questo grafico, scegliamo qualsiasi foglia e seguiamo i vertici successivamente adiacenti, scrivendo i loro nomi se non sono un corridoio ed eliminando i bordi usati, fino a quando non restiamo bloccati. Quindi prendiamo un'altra foglia e continuiamo fino a quando tutti i bordi sono scomparsi.

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+'|'

Puoi vedere i risultati per i cinque labirinti di esempio . (Sfortunatamente, igraphnon è disponibile su Try It Online; questi risultati sono stati esportati da SageMathCloud .)


4

Haskell - 481 405 387 byte

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

Questo crea un elenco di spazi nel labirinto, numerati per indice nella stringa e lo utilizza per trovare tutte le coppie di spazi adiacenti. Quindi ricucisce le coppie in sequenze di punti più lunghe in base alla corrispondenza del primo / ultimo elemento e rimuove i corridoi, in modo che ogni sequenza sia una stanza nel labirinto 1D. Le sequenze vengono quindi tradotte in una stringa sostituendo i punti all'interno di almeno una stanza (i punti di curvatura) in lettere corrispondenti e il resto in spazi.

Il labirinto 2D viene letto da STDIN e il labirinto 1D viene stampato su STDOUT.

Modifica: ridotto di 62 byte riorganizzando un sacco di cose e modificando un po 'l'algoritmo, e altri 14 sostituendoli chrcon toEnumquelli suggeriti da Laikoni.

Modifica 2: salvato altri 13 byte semplificando la logica in (!), 3 utilizzando il modello di elenco corrispondente zucchero e 2 utilizzando >>=per concatenare u.


Penso che non siano necessarie le nuove linee e gli spazi prima che le protezioni dei motivi, ad esempio, o(x:p)q|x==last q=[q++p]|1>0=[]dovrebbero funzionare anche.
Laikoni,

Inoltre toEnumdovrebbe funzionare invece di chr, quindi import Data.Charpotrebbe essere eliminato.
Laikoni,

E infine, poiché la sfida richiede un programma o una funzione, puoi sostituirla main=interact(\m->...)con just f m=.... Questo dovrebbe essere sufficiente per battere la risposta di Python, se questo significa qualcosa per te.
Laikoni,
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.