HexaRegex: un omaggio a Martin Ender


37

Martin Ender ha recentemente raggiunto i 100K ed ha escogitato alcune lingue fantastiche . Ci divertiremo un po 'con uno di loro, Hexagony (e un po' di regex per Retina )

Come breve panoramica, è necessario scrivere un programma che inserisca una griglia Hexagony e determini se esiste un percorso su quella griglia che corrisponde a una stringa di testo

Generazione

Hexagony genera esagoni da una stringa di testo usando i seguenti passi:

  1. Calcola la dimensione minima dell'esagono (prendi la lunghezza della stringa e arrotonda per eccesso al numero esadecimale più vicino )
  2. Avvolgere il testo in un esagono delle dimensioni sopra indicate
  3. Riempiendo le posizioni rimanenti con ..

Ad esempio, la stringa di testo abcdefghijklmrichiede un esagono di lunghezza laterale 3 e quindi diventa:

   a b c
  d e f g
 h i j k l
  m . . .
   . . .

Ora, nota che ci sono 6 possibili direzioni che puoi percorrere in un esagono. Ad esempio, nell'esagono sopra, eè adiacente a abfjid.

involucro

Inoltre, in Hexagony, gli esagoni avvolgono:

   . . . .          . a . .          . . f .          . a . .   
  a b c d e        . . b . .        . . g . .        . b . . f  
 . . . . . .      g . . c . .      . . h . . a      . c . . g . 
. . . . . . .    . h . . d . .    . . u . . b .    . d . . h . .
 f g h i j k      . i . . e .      . j . . c .      e . . i . . 
  . . . . .        . j . . f        k . . d .        . . j . .  
   . . . .          . k . .          . . e .          . k . .   

Se guardi il 2o e il 4o esempio, nota come ae ksono negli stessi punti, nonostante il fatto che tu stia avvolgendo in direzioni diverse. Per questo motivo, questi punti sono solo adiacenti ad altre 5 posizioni .

Per chiarire questo:

   a b c d
  e f g h i
 j k l m n o
p q r s t u v
 w x y z A B
  C D E F G
   H I J K
  1. I bordi si avvolgono nel vicino opposto ( b->Ie G->j).
  2. Gli angoli superiore / inferiore si avvolgono nell'angolo centrale opposto e su / giù ( d->K,pe H->a,v).
  3. Gli angoli centrali si avvolgono su entrambi gli angoli superiore e inferiore ( v->a,H)

percorsi

Un percorso per essere una sequenza di posizioni adiacenti senza tornare nella stessa posizione.

   a b c
  d e f g
 h i f k l
  m . . .
   . . .

Nell'esagono sopra, aefkgmè un percorso valido. Tuttavia, abfdnon è un percorso valido ( fe dnon è adiacente) e abeanon è valido (torna alla aposizione).

Possiamo usare questi percorsi per abbinare il testo (come regex) . Un carattere alfanumerico corrisponde a se stesso (e solo a se stesso), e .corrisponde a qualsiasi carattere. Ad esempio, il percorso aej..lgmsarebbe partita aej..lgm, aejAAlgm, aeja.lgm, o aej^%gm.

Input Output

Il tuo programma dovrebbe prendere due stringhe (in qualsiasi ordine). La prima stringa sarà non vuota e sarà composta solo da caratteri alfanumerici [a-zA-Z0-9]. Questo rappresenterà l'esagono su cui stai operando. La seconda stringa sarà composta da caratteri stampabili.

È necessario restituire un valore di verità se esiste un percorso nell'esagono che corrisponde alla stringa di testo fornita, altrimenti un valore di falsa.

Casi test

Truthy:

"a","a"
"ab","a"
"ab","b"
"ab","ba"
"ab","aba"
"ab","&"
"ab","#7.J!"
"ab","aaaaaa"
"ab","bgjneta"
"ab","cebtmaa"
"abcdefg","dfabcg"
"AbCDeFG","GCbAeFD"
"aaaabbb","aaababb"
"abcdefghijklmnopqrs","alq"
"abcdefghijklmnopqrs","aqnmiedh"
"abcdefghijklmnopqrs","adhcgkorbefjimnqlps"
"11122233344455","12341345123245"
"abcdefgh","h%a"
"abcdefghijklm","a)(@#.*b"
"abcdefghijklm","a)(@#.*i"
"abcdefghij","ja"
"abcdefghijklmno","kgfeia"
"abcdefghijklmno","mmmmmiea"
"abcdefghijklmno","mmmmmlae"
"abcdefghijklmno","ja"
"abcdefghijklmnopqrs","eijfbadhmnokgcsrql"

Falsy:

"a","b"
"a","%"
"a","."
"a","aa"
"a","a."
"ab","#7.J!*"
"ab","aaaaaaa"
"ab","aaaabaaa"
"ab","123456"
"abcdefg","bfgedac"
"abcdefg","gecafdb"
"abcdefg","GCbaeFD"
"aaaabbb","aaaaabb"
"abcdefghijklmnopqrs","aqrcgf"
"abcdefghijklmnopqrs","adhlcgknbeifjm"
"abcdefghijklmnopqrs","ja"
"abcdefghijklm","a)(@#.*&"
"abcdefghijklmno","a)(@bfeijk"
"abcdefghijklmno","kgfeic"
"abcdefghijklmno","mmmmmmiea"

Questo è un , quindi rendi le tue risposte il più brevi possibile nella tua lingua preferita.


21
Qualcuno dovrebbe farlo in Esagonia. : D
DJMcMayhem


9
Inizialmente ero molto confuso dagli esempi sinceri fino a quando non ho capito che l'esagono è la fonte della regex (es) , per così dire, non la seconda stringa. Che è ancora
strabiliante

5
@DrGreenEggsandIronMan Offro una taglia di 500 ripetizioni se qualcuno lo fa in Esagonia.
AdmBorkBork,

2
@Blue Un esempio di esagono non riempito è importante. Ancora più importante, ho fatto la distinzione tra un "percorso" e un "regex".
Nathan Merrill,

Risposte:


14

Retina , 744 byte

Mi dispiace gente, nessuna esagonia questa volta ...

Il conteggio dei byte presuppone la codifica ISO 8859-1.

.+¶
$.'$*_¶$&
^_¶
¶
((^_|\2_)*)_\1{5}_+
$2_
^_*
$.&$*×_$&$&$.&$*×
M!&m`(?<=(?=×*(_)+)\A.*)(?<-1>.)+(?(1)!)|^.*$
O$`(_)|.(?=.*$)
$1
G-2`
T`d`À-É
m`\A(\D*)(?(_)\D*¶.|(.)\D*¶\2)((.)(?<=(?<4>_)\D+)?((?<=(?<1>\1.)\4\D*)|(?<=(?<1>\D*)\4(?<=\1)\D*)|(?<=\1(.(.)*¶\D*))((?<=(?<1>\D*)\4(?>(?<-7>.)*)¶.*\6)|(?<=(?<1>\D*)(?=\4)(?>(?<-7>.)+)¶.*\6))|(?<=(×)*¶.*)((?<=(?<1>\1.(?>(?<-9>¶.*)*))^\4\D*)|(?<=(?<1>\D*)\4(?>(?<-9>¶.*)*)(?<=\1)^\D*)|(?<=(?<1>\1\b.*(?(9)!)(?<-9>¶.*)*)\4×*¶\D*)|(?<=(?<1>\D*\b)\4.*(?(9)!)(?<-9>¶.*)*(?<=\1.)\b\D*))|(?<=(?<1>\1.(?>(?<-11>.)*)¶.*)\4(.)*¶\D*)|(?<=(?<1>\1(?>(?<-12>.)*)¶.*)\4(.)*¶\D*)|(?<=(?<1>\1.(?>(?<-13>.)*¶\D*))\4(\w)*\W+.+)|(?<=(?<1>.*)\4(?>(?<-14>.)*¶\D*)(?<=\1.)(\w)*\W+.+))(?<=\1(\D*).+)(?<!\1\15.*(?<-1>.)+))*\Z

Si aspetta la stringa target sulla prima riga e l'esagono sulla seconda riga dell'input. Stampa 0o di 1conseguenza.

Provalo online! (La prima riga abilita una suite di test, in cui ogni riga è un caso di test, utilizzando ¦per la separazione anziché un avanzamento riga.)

Il modo corretto di risolvere questa sfida è ovviamente con una regex. ;) E se non fosse per il fatto che questa sfida coinvolge anche il procedura di spiegamento dell'esagono , questa risposta in realtà consisterebbe in nient'altro che un singolo regex lungo ~ 600 byte.

Questo non è ancora perfettamente ottimizzato, ma sono abbastanza soddisfatto del risultato (la mia prima versione funzionante, dopo aver rimosso i gruppi nominati e altre cose necessarie per il buonsenso, era di circa 1000 byte). Penso che potrei risparmiare circa 10 byte scambiando l'ordine della stringa e dell'esagono, ma alla fine richiederebbe una riscrittura completa della regex, che non mi sento in questo momento. C'è anche un risparmio di 2 byte omettendo ilG stage, ma rallenta considerevolmente la soluzione, quindi aspetterò di apportare tale modifica fino a quando non sarò sicuro di aver giocato a golf come posso.

Spiegazione

La parte principale di questa soluzione fa ampio uso di gruppi di bilanciamento , quindi ti consiglio di leggerli, se vuoi capire come funziona in dettaglio (non ti biasimerò se non ...).

La prima parte della soluzione (cioè tutto tranne le ultime due righe) è una versione modificata della mia risposta a Unfolding the Hexagony source code . Costruisce l'esagono, lasciando intatta la stringa del bersaglio (e in realtà costruisce l'esagono prima della stringa del bersaglio). Ho apportato alcune modifiche al codice precedente per salvare i byte:

  • Il carattere di sfondo è ×invece di uno spazio in modo che non sia in conflitto con potenziali spazi nell'input.
  • Il carattere no-op / jolly è _ invece ., in modo che le celle della griglia possano essere identificate in modo affidabile come caratteri di parole.
  • Non inserisco spazi o rientranze dopo che l'esagono è stato costruito per la prima volta. Questo mi dà un esagono inclinato, ma in realtà è molto più conveniente lavorare con e le regole di adiacenza sono abbastanza semplici.

Ecco un esempio Per il seguente caso di prova:

ja
abcdefghij

Noi abbiamo:

××abc
×defg
hij__
____×
___××
ja

Confronta questo con il solito layout dell'esagono:

  a b c
 d e f g
h i j _ _
 _ _ _ _
  _ _ _

Ora possiamo vedere che i vicini sono tutti i soliti 8 vicini Moore, tranne i vicini nord-ovest e sud-est. Quindi dobbiamo controllare l'adiacenza orizzontale, verticale e sud-ovest / nord-est (bene e poi ci sono i bordi di avvolgimento). L'uso di questo layout più compatto ha anche il vantaggio che saremo in grado di usarli×× alla fine per determinare al volo le dimensioni dell'esagono quando ne abbiamo bisogno.

Dopo che questo modulo è stato creato, apportiamo un'altra modifica all'intera stringa:

T`d`À-É

Questo sostituisce le cifre con le lettere ASCII estese

ÀÁÂÃÄÅÆÇÈÉ

Poiché sono sostituiti sia nell'esagono che nella stringa target, ciò non influirà sul fatto che la stringa sia abbinata o meno. Inoltre, poiché sono lettere \we le \bidentificano ancora come celle esagonali. Il vantaggio di fare questa sostituzione è che ora possiamo usare \Dnella regex imminente per abbinare qualsiasi personaggio (in particolare, avanzamenti di riga e caratteri non di avanzamento di riga). Non possiamo usare l' sopzione per farlo, perché dovremo .abbinare i caratteri senza avanzamento di riga in più punti.

Ora l'ultimo bit: determinare se qualsiasi percorso corrisponde alla nostra stringa specificata. Questo viene fatto con una sola regex mostruosa. Potresti chiederti perché?!?! Bene, questo è fondamentalmente un problema di backtracking: si inizia da qualche parte e si tenta un percorso purché corrisponda alla stringa, e una volta che non lo fa si torna indietro e si tenta un vicino diverso dall'ultimo personaggio che ha funzionato. L' unica cosache si ottiene gratuitamente quando si lavora con regex è un backtracking. È letteralmente l'unica cosa che fa il motore regex. Quindi se troviamo solo un modo per descrivere un percorso valido (che è abbastanza complicato per questo tipo di problema, ma sicuramente possibile con i gruppi di bilanciamento), allora il motore regex risolverà trovando quel percorso tra tutti i possibili per noi. Sarebbe certamente possibile implementare la ricerca manualmente con più fasi ( e l'ho già fatto in passato ), ma dubito che sarebbe più breve in questo caso particolare.

Un problema con l'implementazione di questo con una regex è che non possiamo tessere arbitrariamente il cursore del motore regex avanti e indietro attraverso la stringa durante il backtracking (di cui avremmo bisogno poiché il percorso potrebbe andare su o giù). Quindi, invece, teniamo traccia del nostro "cursore" in un gruppo di acquisizione e lo aggiorniamo ad ogni passo (possiamo spostarci temporaneamente nella posizione del cursore con uno sguardo). Questo ci consente anche di memorizzare tutte le posizioni passate che utilizzeremo per verificare che non abbiamo già visitato la posizione corrente.

Quindi andiamo a questo. Ecco una versione leggermente più sana del regex, con gruppi denominati, rientro, ordine meno casuale dei vicini e alcuni commenti:

\A
# Store initial cursor position in <pos>
(?<pos>\D*)
(?(_)
  # If we start on a wildcard, just skip to the first character of the target.
  \D*¶.
|
  # Otherwise, make sure that the target starts with this character.
  (?<first>.)\D*¶\k<first>
)
(?:
  # Match 0 or more subsequent characters by moving the cursor along the path.
  # First, we store the character to be matched in <next>.
  (?<next>.)
  # Now we optionally push an underscore on top (if one exists in the string).
  # Depending on whether this done or not (both of which are attempted by
  # the engine's backtracking), either the exact character, or an underscore
  # will respond to the match. So when we now use the backreference \k<next>
  # further down, it will automatically handle wildcards correctly.
  (?<=(?<next>_)\D+)?
  # This alternation now simply covers all 6 possible neighbours as well as
  # all 6 possible wrapped edges.
  # Each option needs to go into a separate lookbehind, because otherwise
  # the engine would not backtrack through all possible neighbours once it
  # has found a valid one (lookarounds are atomic). 
  # In any case, if the new character is found in the given direction, <pos>
  # will have been updated with the new cursor position.
  (?:
    # Try moving east.
    (?<=(?<pos>\k<pos>.)\k<next>\D*)
  |
    # Try moving west.
    (?<=(?<pos>\D*)\k<next>(?<=\k<pos>)\D*)
  |
    # Store the horizontal position of the cursor in <x> and remember where
    # it is (because we'll need this for the next two options).
    (?<=\k<pos>(?<skip>.(?<x>.)*¶\D*))
    (?:
      # Try moving north.
      (?<=(?<pos>\D*)\k<next>(?>(?<-x>.)*)¶.*\k<skip>)
    |
      # Try moving north-east.
      (?<=(?<pos>\D*)(?=\k<next>)(?>(?<-x>.)+)¶.*\k<skip>)
    )
  |
    # Try moving south.
    (?<=(?<pos>\k<pos>.(?>(?<-x>.)*)¶.*)\k<next>(?<x>.)*¶\D*)
  |
    # Try moving south-east.
    (?<=(?<pos>\k<pos>(?>(?<-x>.)*)¶.*)\k<next>(?<x>.)*¶\D*)
  |
    # Store the number of '×' at the end in <w>, which is one less than the
    # the side-length of the hexagon. This happens to be the number of lines
    # we need to skip when wrapping around certain edges.
    (?<=(?<w>×)*¶.*)
    (?:
      # Try wrapping around the east edge.
      (?<=(?<pos>\k<pos>.(?>(?<-w>¶.*)*))^\k<next>\D*)
    |
      # Try wrapping around the west edge.
      (?<=(?<pos>\D*)\k<next>(?>(?<-w>¶.*)*)(?<=\k<pos>)^\D*)
    |
      # Try wrapping around the south-east edge.
      (?<=(?<pos>\k<pos>\b.*(?(w)!)(?<-w>¶.*)*)\k<next>×*¶\D*)
    |
      # Try wrapping around the north-west edge.
      (?<=(?<pos>\D*\b)\k<next>.*(?(w)!)(?<-w>¶.*)*(?<=\k<pos>.)\b\D*)
    )
  |
    # Try wrapping around the south edge.
    (?<=(?<pos>\k<pos>.(?>(?<-x>.)*¶\D*))\k<next>(?<x>\w)*\W+.+)
  |
    # Try wrapping around the north edge.
    (?<=(?<pos>.*)\k<next>(?>(?<-x>.)*¶\D*)(?<=\k<pos>.)(?<x>\w)*\W+.+)
  )
  # Copy the current cursor position into <current>.
  (?<=\k<pos>(?<current>\D*).+)
  # Make sure that no matter how many strings we pop from our stack of previous
  # cursor positions, none are equal to the current one (to ensure that we use
  # each cell at most once).
  (?<!\k<pos>\k<current>.*(?<-pos>.)+)
)*
# Finally make sure that we've reached the end of the string, so that we've
# successfully matched all characters in the target string.
\Z

Spero che l'idea generale sia approssimativamente chiara da questo. Come esempio di come funziona uno di quei movimenti lungo il percorso, diamo un'occhiata al bit che sposta il cursore verso sud:

(?<=(?<pos>\k<pos>.(?>(?<-x>.)*)¶.*)\k<next>(?<x>.)*¶\D*)

Ricorda che i lookbehinds dovrebbero essere letti da destra a sinistra (o dal basso verso l'alto), perché è l'ordine in cui vengono eseguiti:

(?<=
  (?<pos>
    \k<pos>       # Check that this is the old cursor position.
    .             # Match the character directly on top of the new one.
    (?>(?<-x>.)*) # Match the same amount of characters as before.
    ¶.*           # Skip to the next line (the line, the old cursor is on).
  )               # We will store everything left of here as the new 
                  # cursor position.
  \k<next>        # ...up to a match of our current target character.
  (?<x>.)*        # Count how many characters there are...
  ¶\D*            # Skip to the end of some line (this will be the line below
                  # the current cursor, which the regex engine's backtracking
                  # will determine for us).
)

Si noti che non è necessario mettere un ancoraggio davanti al \k<pos>per assicurarsi che ciò raggiunga effettivamente l'inizio della stringa. <pos>inizia sempre con una quantità ×che non può essere trovata altrove, quindi questo agisce già come un'ancora implicita.

Non voglio gonfiare questo post più del necessario, quindi non entrerò negli altri 11 casi in dettaglio, ma in linea di principio funzionano tutti in modo simile. Controlliamo che <next>può essere trovato in una direzione specifica (ammissibile) dalla vecchia posizione del cursore con l'aiuto di gruppi di bilanciamento, quindi memorizziamo la stringa fino a quella corrispondenza come nuova posizione del cursore <pos>.


13

Python 3, 990 943 770 709 byte

Prima risposta, yay!

EDIT: creazione della lista di adiacenza golfata. Ora uso una formula leggermente diversa

EDIT 2: rimosso lanugine inutili, golfato molto di più.

EDIT 3: abbreviato il codice per la conversione dall'indice in elenco alle coordinate, ha aggiunto alcune altre cose.

La maggior parte dei byte si riferisce alla creazione dell'elenco di adiacenza (ha il maggior potenziale per essere giocato a golf). Da allora, è una semplice questione di forzare brutalmente la soluzione (cosa che potrei essere in grado di fare in meno byte).

golfed:

from math import*
b=abs
c=max
e=range
f=len
A=input()
B=input()
C=ceil(sqrt((f(A)-.25)/3)+.5)
D=3*C*~-C+1
E=2*C-1
F=C-1
A+='.'*(D-f(A))
G=[set()for x in e(D)]
I=lambda H:sum(E+.5-b(t-F+.5)for t in e(int(H+F)))
for x in e(D):
 r=sum([[J-F]*(E-b(J-F))for J in e(E)],[])[x];q=x-I(r);s=-q-r;a=lambda q,r:G[x].add(int(q+I(r)));m=c(map(b,[q,r,s]))
 if m==F:
  if q in(m,-m):a(-q,-s)
  if r in(m,-m):a(-s,-r)
  if s in(m,-m):a(-r,-q)
 for K,L in zip([1,0,-1,-1,0,1],[0,1,1,0,-1,-1]):
  M,H=q+K,r+L
  if c(map(b,[M,H,-M-H]))<C:a(M,H)
def N(i,O,P):
 Q=O and O[0]==A[i]or'.'==A[i];R=0
 if(2>f(O))*Q:R=1
 elif Q:R=c([(x not in P)*N(x,O[1:],P+[i])for x in G[i]]+[0])
 return R
print(c([N(x,B,[])for x in e(D)])*(f(B)<=D))

Ungolfed con spiegazione:

from math import*

#Rundown of the formula:
# * Get data about the size of the hexagon
# * Create lookup tables for index <-> coordinate conversion
#   * q=0, r=0 is the center of the hexagon
#   * I chose to measure in a mix of cubic and axial coordinates,
#     as that allows for easy oob checks and easy retrevial  
# * Create the adjacency list using the lookup tables, while
#   checking for wrapping
# * Brute-force check if a path in the hexagon matches the
#   expression

# shorten functions used a lot
b=abs
c=max
e=range

# Get input

prog=input()
expr=input()

# sdln = Side length
# hxln = Closest hexagonal number
# nmrw = Number of rows in the hexagon
# usdl = one less than the side length. I use it a lot later

sdln=ceil(sqrt((len(prog)-.25)/3)+.5)
hxln=3*sdln*~-sdln+1
nmrw=2*sdln-1
usdl=sdln-1

# Pad prog with dots

prog+='.'*(hxln-len(prog))

# nmbf = Number of elements before in each row
# in2q = index to collum
# in2r = index to row

nmbf=[0]*nmrw
in2q=[0]*hxln
in2r=[0]*hxln

#  4    5
#   \  /
# 3 -- -- 0
#   /  \ 
#  2    1

# dirs contains the q,r and s values needed to move a point
# in the direction refrenced by the index

qdir=[1,0,-1,-1,0,1]
rdir=[0,1,1,0,-1,-1]

# generate nmbf using a summation formula I made

for r in e(nmrw-1):
    nmbf[r+1]=int(nmbf[r]+nmrw+.5-b(r-sdln+1.5))

# generate in2q and in2r using more formulas
# cntr = running counter

cntr=0
for r in e(nmrw):
    bgnq=c(-r,1-sdln)
    for q in e(nmrw-b(r-sdln+1)):
        in2q[cntr]=bgnq+q
        in2r[cntr]=r-usdl
        cntr+=1

# adjn = Adjacency sets

adjn=[set()for x in e(hxln)]

# Generate adjacency sets

for x in e(hxln):
    #Get the q,r,s coords
    q,r=in2q[x],in2r[x]
    s=-q-r
    # a = function to add q,r to the adjacency list
    a=lambda q,r:adjn[x].add(q+nmbf[r+usdl])
    # m = absolute value distance away from the center
    m=c(map(b,[q,r,s]))
    # if we are on the edge (includes corners)...
    if m==usdl:
        # add the only other point it wraps to
        if q in(m,-m):
            a(-q,-s)
        if r in(m,-m):
            a(-s,-r)
        if s in(m,-m):
            a(-r,-q)
    # for all the directions...
    for d in e(6):
        # tmp{q,r,s} = moving in direction d from q,r,s
        tmpq,tmpr=q+qdir[d],r+rdir[d]
        # if the point we moved to is in bounds...
        if c(map(b,[tmpq,tmpr,-tmpq-tmpr]))<sdln:
            # add it
            a(tmpq,tmpr)

# Recursive path checking function
def mtch(i,mtst,past):
    # dmch = Does the place we are on in the hexagon match
    #        the place we are in the expression?
    # out = the value to return
    dmch=mtst and mtst[0]==prog[i]or'.'==prog[i]
    out=0
    # if we are at the end, and it matches...
    if(2>len(mtst))*dmch:
        out=1
    # otherwise...
    elif dmch:
        # Recur in all directions that we haven't visited yet
        # replace '*' with 'and' to speed up the recursion
        out=c([(x not in past)*mtch(x,mtst[1:],past+[i])for x in adjn[i]]+[0])
    return out

# Start function at all the locations in the hexagon
# Automatically return false if the expression is longer
# than the entire hexagon
print(c([mtch(x,expr,[])for x in e(hxln)])*(len(expr)<=hxln))

Così vicino a Retina! :( Yay, batti Retina!


5

Javascript (ES6), 511 500 496 byte

(H,N)=>{C=(x,y)=>(c[x]=c[x]||[])[y]=y;S=d=>(C(x,y=x+d),C(y,x),C(s-x,s-y),C(s-y,s-x));r=(x,p,v)=>{p<N.length?(v[x]=1,c[x].map(n=>!v[n]&&(H[n]==N[p]||H[n]=='.')&&r(n,p+1,v.slice()))):K=1};for(e=x=K=0;(s=3*e*++e)<(l=H.length)-1;);H+='.'.repeat(s+1-l);for(a=[],b=[],c=[[]],w=e;w<e*2;){a[w-e]=x;b[e*2-w-1]=s-x;for(p=w;p--;x++){w-e||S(s-e+1);w<e*2-1&&(S(w),S(w+1));p&&S(1)}a[w]=x-1;b[e*3-++w]=s-x+1}a.map((v,i)=>S(b[i]-(x=v)));[N[0],'.'].map(y=>{for(x=-1;(x=H.indexOf(y,x+1))>-1;r(x,1,[]));});return K}

Ungolf e commentato

// Entry point
//   H = haystack (the string the hexagon is filled with)
//   N = needle (the substring we're looking for)
(H, N) => {
  // C(x, y) - Helper function to save a connection between two locations.
  //   x = source location
  //   y = target location
  C = (x, y) => (c[x] = c[x] || [])[y] = y;

  // S(d) - Helper function to save reciprocal connections between two locations
  //        and their symmetric counterparts.
  //   d = distance between source location (x) and target location
  S = d => (C(x, y = x + d), C(y, x), C(s - x, s - y), C(s - y, s - x));

  // r(x, p, v) - Recursive path search.
  //   x = current location in hexagon
  //   p = current position in needle
  //   v = array of visited locations
  r = (x, p, v) => {
    p < N.length ?
      (v[x] = 1, c[x].map(n => !v[n] && (H[n] == N[p] || H[n] == '.') &&
      r(n, p + 1, v.slice())))
    :
      K = 1
  };

  // Compute e = the minimum required edge width of the hexagon to store the haystack.
  // Also initialize:
  //   x = current location in hexagon
  //   l = length of haystack
  //   s = size of hexagon (number of locations - 1)
  //   K = fail/success flag
  for(e = x = K = 0; (s = 3 * e * ++e) < (l = H.length) - 1;);

  // Pad haystack with '.'
  H += '.'.repeat(s + 1 - l);

  // Build connections c[] between locations, using:
  //   x = current location
  //   w = width of current row
  //   p = position in current row
  // Also initialize:
  //   a[] = list of locations on top left and top right edges
  //   b[] = list of locations on bottom left and bottom right edges
  for(a = [], b = [], c = [[]], w = e; w < e * 2;) {
    a[w - e] = x;
    b[e * 2 - w - 1] = s - x;

    for(p = w; p--; x++) {
      // connection between top and bottom edges
      w - e || S(s - e + 1);
      // connections between current location and locations below it
      w < e * 2 - 1 && (S(w), S(w + 1));
      // connection between current location and next location
      p && S(1)
    }
    a[w] = x - 1;
    b[e * 3 - ++w] = s - x + 1
  }

  // Save connections between top left/right edges and bottom left/right edges.
  a.map((v, i) => S(b[i] - (x = v)));

  // Look for either the first character of the needle or a '.' in the haystack,
  // and use it as the starting point for the recursive search. All candidate
  // locations are tried out.
  [N[0], '.'].map(y => {
    for(x = -1; (x = H.indexOf(y, x + 1)) > -1; r(x, 1, []));
  });

  // Return fail/success flag.
  return K
}

Casi test

Lo snippet di seguito esaminerà tutti i casi di test di verità e falsità.

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.