Movimento em uma grade hexagonal

15

Dada a entrada de uma série de caracteres representando movimentos em uma grade hexagonal, produza as coordenadas finais do "ponteiro".

Nossos hexágonos serão numerados da seguinte forma (imagine uma grade retangular com cada coluna de número ímpar deslocada levemente para baixo):

  _____         _____         _____         _____
 /     \       /     \       /     \       /     \
/ -3,-2 \_____/ -1,-2 \_____/  1,-2 \_____/  3,-2 \
\       /     \       /     \       /     \       /
 \_____/ -2,-1 \_____/  0,-1 \_____/  2,-1 \_____/
 /     \       /     \       /     \       /     \
/ -3,-1 \_____/ -1,-1 \_____/  1,-1 \_____/  3,-1 \
\       /     \       /     \       /     \       /
 \_____/ -2,0  \_____/  0,0  \_____/  2,0  \_____/
 /     \       /     \       /     \       /     \
/ -3,0  \_____/ -1,0  \_____/  1,0  \_____/  3,0  \
\       /     \       /     \       /     \       /
 \_____/ -2,1  \_____/  0,1  \_____/  2,1  \_____/
 /     \       /     \       /     \       /     \
/ -3,1  \_____/ -1,1  \_____/  1,1  \_____/  3,1  \
\       /     \       /     \       /     \       /
 \_____/       \_____/       \_____/       \_____/

O ponteiro começa em (0, 0).

As instruções que você deve oferecer suporte são as seguintes:

  • q: mover para cima-esquerda
  • w: subir
  • e: mover para cima-direita
  • a: mover para baixo-esquerda
  • s: descer
  • d: mover para baixo-direita
  • r: girar a grade no sentido horário
  • R: girar a grade no sentido anti-horário

Os comandos de rotação giram a grade inteira, mantendo o ponteiro nas mesmas coordenadas. (Por quê qweasd? Eles combinam bem com as instruções em um teclado QWERTY.)

Para ajudar a visualizar isso, eis o que os comandos de movimento fariam, assumindo que o ponteiro comece no meio:

         _____
        /     \
  _____/   w   \_____
 /     \       /     \
/   q   \_____/   e   \
\       /     \       /
 \_____/       \_____/
 /     \       /     \
/   a   \_____/   d   \
\       /     \       /
 \_____/   s   \_____/
       \       /
        \_____/

Após uma rotação no sentido horário ( r), os comandos são remapeados para (imagine que ele gire toda a grade hexagonal, mas ainda mantenha "w" ativo, etc., o que equivale ao seguinte):

         _____
        /     \
  _____/   e   \_____
 /     \       /     \
/   w   \_____/   d   \
\       /     \       /
 \_____/       \_____/
 /     \       /     \
/   q   \_____/   s   \
\       /     \       /
 \_____/   a   \_____/
       \       /
        \_____/

Da mesma forma, girar no sentido anti-horário ( R) depois disso retornaria a grade ao normal e girar no sentido anti-horário novamente "remapearia" qwedsapara aqweds.

A entrada deve ser fornecida como uma única string e a saída pode ser uma única string unida por caracteres não numéricos (ex. 1 2Ou 3,4) ou uma matriz de números inteiros.

Como esse é o , o código mais curto em bytes será vencedor.

Casos de teste:

In                         Out
---------------------------------
edeqaaaswwdqqs             -2, 0
dddddddddd                 10, 5
wswseaeadqdq               0, 0
<empty string>             0, 0
esaaqrweesrqrq             -1, 0
wrwrwrwrw                  -1, 0
RRssrrrs                   -1, -1
aRRRRwddrqrrqqq            -1, -4
rrrrrrrrrrrrRRRRRRrrrrrrq  -1, -1
rrRrRrrRrrrrRRrRrRR        0, 0
Maçaneta da porta
fonte

Respostas:

2

Pitão, 81 bytes

J_K1=Y"qwedsa"A,ZZFNz=kxYN ?<kZ=Y?<x\rNZ.>Y1.<Y1A,+G@[JZ1KZJ)k+H@[J_2JK2K)k;,G/H2

Saída é uma lista de números inteiros representando as coordenadas.

Minha solução é realmente muito chata; apenas procura o caractere inserido em uma matriz (the qwedsa) e acessa duas matrizes que representam as respectivas alterações nas coordenadas. Por exemplo, se a entrada for w, obtemos 1 (como é o segundo caractere na matriz). Em seguida, adicionamos A[1]a x(onde Aestá o array para as alterações em xrelação às diferentes entradas) e B[1]a y(onde Bestão as alterações y). re Rsão alcançados apenas girando a qwedsamatriz.

Tenho certeza que alguém pode fazer muito melhor usando Pyth. Vou continuar tentando jogar minha resposta!

Você pode experimentá-lo aqui .

Rhyzomatic
fonte
12

Retina , 353 339 178 175 150 130 129 117 bytes

R
5$*r
T`aq\we\ds`so`r.+
)`r(.*)
$1
^
:
a
sq
e
wd
+`(.+)q
w$1
+`(.+)d
s$1
+`sw

(.*)(\1w?):
$0$2
+`sw|ws

w+
-$0
\w
1

A saída é unária, separada por dois pontos. Isso significa que você realmente não verá zeros na saída (embora a presença de dois pontos indique qual das duas coordenadas é zero, se houver apenas uma).

Experimente online!

Isso foi muito divertido e acabou sendo surpreendentemente curto. :)

Explicação

Alguns antecedentes primeiro. Existem vários sistemas de coordenadas para descrever grades hexagonais. A pessoa solicitada usa coordenadas de deslocamento. Isso é essencialmente como coordenadas retangulares da grade, exceto que um eixo "oscila" um pouco. Em particular, a pergunta solicita o layout "ímpar-q" mostrado na página vinculada. Esse sistema de coordenadas é um pouco chato de se trabalhar, porque o modo como as coordenadas mudam durante um movimento depende não apenas da direção do movimento, mas também da posição atual.

Outro sistema de coordenadas usa coordenadas axiais. Isso é essencialmente imaginar a hexgrid como uma fatia diagonal através de um volume de cubos e usar dois dos eixos (por exemplo, x e z) para encontrar uma posição no plano 2D. Na grade hexagonal, isso significa que os dois eixos formam um ângulo de 60 (ou 120) graus. Esse sistema é um pouco menos intuitivo, mas muito mais fácil de trabalhar, pois todas as direções correspondem a um vetor "delta" fixo. (Para uma melhor explicação de como chegar a esse sistema de coordenadas, confira o link e os adoráveis ​​diagramas e animações.)

Então, aqui está o que faremos: calculamos o movimento em coordenadas axiais (cuidando da rotação como sugerido no desafio, remapeando o significado dos comandos) e, quando terminamos, convertemos axial em offset-q impar coordenadas.

Os seis movimentos são mapeados para os seguintes vetores delta em coordenadas axiais (xz):

q => (-1,  0)
w => ( 0, -1)
e => ( 1, -1)
d => ( 1,  0)
s => ( 0,  1)
a => (-1,  1)

Espera, aqui é Retina, teremos que trabalhar com números unários. Como trabalhamos com números unários negativos? A idéia é usar dois dígitos diferentes. Um representa +1e o outro representa -1. Isso significa que, independentemente de querermos adicionar ou subtrair 1da posição atual, sempre podemos fazer isso adicionando um dígito. Quando terminamos, reduzimos o resultado em sua magnitude (do dígito correspondente) cancelando dígitos balanceados. Depois descobrimos o sinal com base no dígito restante e substituímos todos os dígitos por 1.

O plano é construir os componentes axiais x e z à esquerda e à direita de a :(como um separador), na frente da entrada. we sserá adicionado ao lado direito. qe dserá adicionado ao lado esquerdo ee aserá adicionado aos dois lados. Como we sjá estamos no lado correto do :(que irá na frente), os usaremos como os dígitos -1e +1, respectivamente.

Vamos analisar o código.

R
5$*r

Começamos transformando cada um Rem cinco rsegundos. Obviamente, uma curva à esquerda é igual a cinco curvas à direita em uma grade hexadecimal e, ao fazer isso, podemos duplicar bastante na etapa de remapeamento.

T`aq\we\ds`so`r.+

Este é um estágio de transliteração que gira os seis comandos, se forem encontrados após o primeiro r(processando assim o primeiro r). we dprecisa ser escapado para impedir que eles se expandam para as classes de personagens. Ele oinsere o conjunto de origem no conjunto de destino, o que economiza um monte de bytes para essas tarefas de rotação. O mapeamento de caracteres é, portanto:

aqweds
saqweds

onde o último sna segunda linha pode ser simplesmente ignorado.

)`r(.*)
$1

Isso remove o primeiro rda string, porque foi processado (eu gostaria de já ter implementado limites de substituição ...). O )comando também diz ao Retina para executar todos os estágios até este em um loop até que a string pare de mudar. Nas iterações subseqüentes, o primeiro estágio é um no-op porque não há mais se Ro segundo estágio aplicará outra rotação enquanto houver rs restantes na string.

Quando terminamos, mapeamos todos os comandos na direção a que correspondem na grade não rotacionada e podemos começar a processá-los. É claro que esse movimento é apenas uma soma desses vetores delta e as somas são comutativas, portanto, não importa realmente em que ordem os processamos agora que as rotações foram eliminadas.

^
:

Insira o delimitador de coordenadas na frente.

Agora realmente não precisamos processar se w. Eles são nossos +1e -1dígitos e já estão no lado correto da tela, :para que acabem desistindo conforme necessário no final. Podemos fazer outra simplificação: aé simples s + qe eé w + d. Vamos fazer isso:

a
sq
e
wd

Mais uma vez, aqueles se wsimplesmente desistirão. Tudo o que precisa fazer é mover essas qs e ds para a frente e transformá-los em ws e ss eles mesmos. Fazemos isso com dois loops separados:

+`(.+)q
w$1
+`(.+)d
s$1

Então está feito. Hora da conversão de coordenadas axiais para deslocadas. Para isso, precisamos recolher os dígitos. No entanto, por enquanto, nos preocupamos apenas com o lado esquerdo. Devido à forma como temos processados os qs e ds, sabemos que todas as ss em lado esquerdo vai aparecer na frente de quaisquer ws, por isso, só precisa verificar um par para o colapso do-los:

+`sw

Agora a conversão real. Aqui está o pseudocódigo, retirado do link acima:

# convert cube to odd-q offset
col = x
row = z + (x - (x&1)) / 2

Certo, então o lado esquerdo já está correto. O lado direito precisa do termo de correção (x - (x&1)) / 2. Tomar &1é o mesmo que o módulo 2. Isso basicamente analisa como x/2, divisão inteira, arredondada para menos infinito. Portanto, para positivo x, adicionamos metade do número de dígitos (arredondado para baixo) e, para negativo x, subtraímos metade do número de dígitos (arredondado para cima). Isso pode ser expresso surpreendentemente concisa na regex:

(.*)(\1w?):
$0$2

Devido à ganância, para o par x, o grupo 1 corresponderá exatamente a metade dos dígitos, \1a outra metade e podemos ignorar o w?. Nós inserimos essa metade após o :(que é x/2). Se xfor par, precisamos distinguir positivo e negativo. Se xfor positivo, w?nunca corresponderá, portanto os dois grupos ainda terão que corresponder ao mesmo número de dígitos. Não há problema se o primeiro sfor simplesmente pulado, então arredondamos para baixo. Se xfor negativo e ímpar, a possível correspondência será com \1(metade do xarredondado para baixo) e opcional w. Como os dois entram em grupo 2, escreveremos x/2com a magnitude arredondada para cima (conforme necessário).

+`sw|ws

Agora, reduzimos os dígitos no lado direito. Desta vez, não sabemos a ordem do se w, portanto, precisamos considerar os dois pares.

w+
-$0

Agora, ambas as partes são reduzidas a um único dígito repetido (ou nada). Se esse dígito for w, inserimos um sinal de menos na frente.

\w
1

E, finalmente, transformar tanto em we spara um único dígito unário razoável. (Suponho que eu poderia salvar um byte usando wou scomo o dígito unário, mas isso parece um pouco exagerado.)

Martin Ender
fonte
10
(Eu sou o único que viu esta resposta chegar na página do site principal e caro esperava que foi escrito em hexagony?)
Addison Crump
9
@FlagAsSpam As demandas dessa comunidade estão aumentando seriamente quando é possível decepcionar 8 pessoas (e contando), resolvendo um desafio envolvendo números inteiros assinados e grades hexagonais com uma linguagem que só pode processar sua entrada por meio de expressões regulares. ;)
Martin Ender
1

Python (3,5) 193 185 182 bytes

Eu também calculo em coordenadas axiais e converto no final.

Eu adiciono alguma otimização de acordo com a solução @Martin Büttner: substituo R por r * 5, ele não altera a contagem de bytes. Mas com essa mudança, podemos substituir o segundo teste elif j=='r'apenaselse

A solução assume que não podemos ter caracteres inválidos na entrada.

def f(i):
 x=y=0;u=-1,0,-1,1,0,1,1,0,1,-1,0,-1;v='dewqas'
 for j in i.replace('R','r'*5):
  w=v.find(j)*2
  if-1<w:x+=u[w];y+=u[w+1]
  else:u=u[2:]+u[:2]
 print(-x,-x-y+(x-(x%2))/2)

Ungolfed

def f(i):
  x=y=0
  u=-1,0,-1,1,0,1,1,0,1,-1,0,-1    # operations list xd,yd,xe,ye...
  v='dewqas'                       # letters list in clockwise order 
  i.replace('R','r'*5)             # replace 'R' by 5*'r'
  for j in i:
    w=v.find(j)*2                  # extract letter index
    if-1<w:
      x+=u[w]                      # apply operations
      y+=u[w+1]
    else:
      u=u[2:]+u[:2]                # rotate clockwise the operation string
  print(-x,-x-y+(x-(x%2))/2)       # convert coordinates axial to "odd-q"

Uso

>>> f('wrwrwrwrw')
-1 0.0
>>> f('dddddddddd')
10 5.0
>>> f('edeqaaaswwdqqs')
-2 0.0
Erwan
fonte
0

Lote, 708 636 586 569 bytes

Eu usei coordenadas y duplicadas porque tornava a matemática mais simples. Não tenho certeza de ter levado em consideração a rotação da maneira mais ideal, mas é melhor contar o número de rs.

Editar: economizou 72 bytes melhorando o manuseio de Rs. Economizei 60 bytes otimizando minhas set/adeclarações. Economizou 17 bytes com algumas otimizações menores.

@echo off
set m=%1
set/ay=x=0
set r=r
set g=goto l
:l
set/a"z=y>>1
if "%m%"=="" echo %x% %z%&exit/b
set c=%m:~0,1%
set m=%m:~1%
goto %r:rrrrrr=%%c%
:a
:rq
:rrw
:rrre
:rrrrd
:rrrrrs
set/ax-=2
:w
:re
:rrd
:rrrs
:rrrra
:rrrrrq
set/ax+=1,y-=1
%g%
:q
:rw
:rre
:rrrd
:rrrrs
:rrrrra
set/ay-=2
%g%
:s
:ra
:rrq
:rrrw
:rrrre
:rrrrrd
set/ax-=2
:e
:rd
:rrs
:rrra
:rrrrq
:rrrrrw
set/ax+=+1,y+=1
%g%
:d
:rs
:rra
:rrrq
:rrrrw
:rrrrre
set/ay+=2
%g%
:r
:rr
:rrr
:rrrr
:rrrrr
:rrrrrr
if %c%==R set c=rrrrr
set r=%c%%r%
%g%
Neil
fonte
0

05AB1E , 60 bytes

.•F?äM•U2Å0IvXy'rQiÀUëy'RQiÁUëykÐ5α‚ßsD3%_s3›·+‚<+]Ć`DÉ-2÷+‚

Experimente online ou verifique todos os casos de teste .

Explicação:

Explicação geral:

Começamos com uma seqüência de caracteres "qwedsa"e uma coordenada [0,0], e passamos pelos caracteres da entrada.
Se for um "r" ou "R", rotacionamos essa corda para a esquerda ou direita, respectivamente.
Caso contrário, obtemos o índice com base em 0 nessa sequência e o mapeamos da seguinte maneira:

q → 0 → [-1,  0]
w → 1 → [ 0, -1]
e → 2 → [ 1, -1]
d → 3 → [ 1,  0]
s → 4 → [ 0,  1]
a → 5 → [-1,  1]

xy

 x   indices     y   indices
-1 ← 0;5        -1 ← 1;2
 0 ← 1;4         0 ← 0;3
 1 ← 2;3         1 ← 4;5

k

x=mEun(k,umabs(k-5))-1
y=(0 0k(mod3))+2(k>3)-1

yxx

y=y+x-x(mod2)2

Explicação do código:

.•FM         # Push compressed string "qwedsa"
       U        # Pop and store it in variable `X`
2Å0             # Push list [0,0]
                # (many 3-byte alternatives for this: `00S`; `т¦S`; `0D‚`; `1¾‰`; etc.)
   Iv           # Loop over each character `y` of the input:
     X          #  Push string `X`
      y'rQi    '#  If `y` equals "r":
           À    #   Rotate string `X` once towards the left
            U   #   And pop and store it as new value for `X`
      ëy'RQi   '#  Else-if `y` equals "R":
            ÁU  #   Do the same, but rotate right instead
      ë         #  Else:
       yk       #   Get the 0-based index of `y` in the string `X`
         Ð      #   Triplicate this index
          5α    #   Take the absolute difference with 5
            ‚ß  #   Pair it with the original index, and pop and push the minimum
                #   (maps 0→[0,5]→0; 1→[1,4]→1; 2→[2,3]→2;
                #         3→[3,2]→2; 4→[4,1]→1; 5→[5,0]→0)
         sD     #   Swap to get the original index again, and duplicate it
           3%   #   Take modulo 3
             _  #   And check if it's equals to 0 (1 if truthy; 0 if falsey)
          s3   #   Swap to take the index again, and check if it's larger than 
                #   (again, 1 if truthy; 0 if falsey)
             ·  #   Double this
          +     #   And add both checks together
                #   (maps 0→1+0→1; 1→0+0→0; 2→0+0→0;
                #         3→1+0→1; 4→0+2→2; 5→0+2→2)
               #   Pair both mapped values together
          <     #   Decrease both by 1, so it becomes: 0→-1; 1→0; 2→1
           +    #   And add it to the current coordinates
    ]           # After the loop with inner if-else statements:
     Ć          # Enclose the coordinate, appending its own head: [x,y] becomes [x,y,x]
      `         # Push all three values separated to the stack
       D        # Duplicate this x
        É       # Check if its odd (1 if truthy; 0 if falsey)
         -      # Subtract it from the duplicated x
          2÷    # Integer-divide it by 2
            +   # Add it to y
               # And pair it with the original x again
                # (after which the result is output implicitly)

Veja este 05AB1E ponta do meu (seção Como cordas compressa não fazem parte do dicionário? ) Para entender por que .•F?äM•é "qwedsa".

Kevin Cruijssen
fonte
-1

Python 3, 227 bytes

def G(s):
 q='qwedsa'
 d=[-1,0,1,1,0,-1,-1,-2,-1,1,2,1]
 X=Y=0
 for c in s:
  if c in q:
   n = q.find(c)
   X += d[n]
   Y += d[n+6]
  if c == 'r':
   q = q[1:]+q[0]
  if c == 'R':
   q = q[5]+q[0:5]
 print(X, int((Y-X%2)/2))
Austin Hastings
fonte
Estou usando Python 3.5.0b3no MacOS e, apesar de ter recebido um erro nos dias 5 e 6, devido ao arredondamento, o restante estava correto. (Desde que corrigido via Editar). Qual versão do Python você está usando?
Austin Hastings
1
@AustinHastings Estou no Python 3 no Debian instável.
Maçaneta