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 +1
e o outro representa -1
. Isso significa que, independentemente de querermos adicionar ou subtrair 1
da 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. w
e s
será adicionado ao lado direito. q
e d
será adicionado ao lado esquerdo e
e a
será adicionado aos dois lados. Como w
e s
já estamos no lado correto do :
(que irá na frente), os usaremos como os dígitos -1
e +1
, respectivamente.
Vamos analisar o código.
R
5$*r
Começamos transformando cada um R
em cinco r
segundos. 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
). w
e d
precisa ser escapado para impedir que eles se expandam para as classes de personagens. Ele o
insere 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 s
na segunda linha pode ser simplesmente ignorado.
)`r(.*)
$1
Isso remove o primeiro r
da 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 R
o segundo estágio aplicará outra rotação enquanto houver r
s 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 s
e w
. Eles são nossos +1
e -1
dí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 + q
e e
é w + d
. Vamos fazer isso:
a
sq
e
wd
Mais uma vez, aqueles s
e w
simplesmente desistirão. Tudo o que precisa fazer é mover essas q
s e d
s para a frente e transformá-los em w
s e s
s 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 q
s e d
s, sabemos que todas as s
s em lado esquerdo vai aparecer na frente de quaisquer w
s, 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, \1
a outra metade e podemos ignorar o w?
. Nós inserimos essa metade após o :
(que é x/2
). Se x
for par, precisamos distinguir positivo e negativo. Se x
for 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 s
for simplesmente pulado, então arredondamos para baixo. Se x
for negativo e ímpar, a possível correspondência será com \1
(metade do x
arredondado para baixo) e opcional w
. Como os dois entram em grupo 2
, escreveremos x/2
com 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 s
e 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 w
e s
para um único dígito unário razoável. (Suponho que eu poderia salvar um byte usando w
ou s
como o dígito unário, mas isso parece um pouco exagerado.)