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
La salida está en unario, separada por dos puntos. Eso significa que realmente no verá ceros en la salida (aunque la presencia de dos puntos le indicará cuál de las dos coordenadas es cero, si solo hay una).
Pruébalo en línea!
Esto fue muy divertido y terminó siendo sorprendentemente corto. :)
Explicación
Algunos antecedentes primero. Existen varios sistemas de coordenadas para describir las cuadrículas hexagonales. El que pidió utiliza coordenadas de desplazamiento. Eso es esencialmente como coordenadas de cuadrícula rectangular, excepto que un eje "se tambalea" un poco. En particular, la pregunta solicita el diseño "impar q" que se muestra en la página vinculada. Es un poco molesto trabajar con este sistema de coordenadas, porque la forma en que cambian las coordenadas durante un movimiento depende no solo de la dirección del movimiento, sino también de la posición actual.
Otro sistema de coordenadas utiliza coordenadas axiales. Eso es esencialmente imaginar la cuadrícula hexagonal como un corte diagonal a través de un volumen de cubos, y usar dos de los ejes (por ejemplo, x y z) para encontrar una posición en el plano 2D. En la cuadrícula hexadecimal, eso significa que los dos ejes forman un ángulo de 60 (o 120) grados. Este sistema es un poco menos intuitivo pero mucho más fácil de trabajar, ya que cada dirección corresponde a un vector "delta" fijo. (Para una mejor explicación de cómo llegar a este sistema de coordenadas, consulte el enlace y los encantadores diagramas y animaciones allí).
Entonces, esto es lo que haremos: calculamos el movimiento en coordenadas axiales (cuidando la rotación como se sugiere en el desafío, reasignando el significado de los comandos), y cuando terminamos, convertimos axial a offset impar-q coordenadas
Los seis movimientos se asignan a los siguientes vectores delta en coordenadas axiales (xz):
q => (-1, 0)
w => ( 0, -1)
e => ( 1, -1)
d => ( 1, 0)
s => ( 0, 1)
a => (-1, 1)
Espera, esta es Retina, tendremos que trabajar con números unarios. ¿Cómo trabajamos con números unarios negativos? La idea es usar dos dígitos diferentes. Uno representa +1
y el otro representa -1
. Eso significa que independientemente de si queremos sumar o restar 1
de la posición actual, siempre podemos hacerlo agregando un dígito. Cuando terminamos, colapsamos el resultado en su magnitud (del dígito correspondiente) cancelando dígitos balanceados. Luego calculamos el signo en función del dígito restante y reemplazamos todos los dígitos con 1
.
El plan es construir los componentes axiales x y z a la izquierda y derecha de a :
(como separador), frente a la entrada. w
y s
se agregará al lado derecho. q
y d
se agregará al lado izquierdo, e
y a
se agregará a ambos lados. Como w
ya s
estamos en el lado correcto de :
(que irá al frente), los utilizaremos como dígitos -1
y +1
, respectivamente.
Veamos el código.
R
5$*r
Comenzamos convirtiendo cada uno R
en cinco r
s. Por supuesto, un giro a la izquierda es lo mismo que cinco giros a la derecha en una cuadrícula hexagonal, y al hacerlo podemos duplicar mucho el paso de reasignación.
T`aq\we\ds`so`r.+
Esta es una etapa de transliteración que rota los seis comandos, si se encuentran después del primero r
(procesando así el primero r
). w
y d
necesitan escapar para evitar que se expandan a clases de personajes. El o
inserta el conjunto de origen en el conjunto de objetivos que ahorra un montón de bytes para estas tareas de rotación. El mapeo de caracteres es por lo tanto:
aqweds
saqweds
donde el último s
en la segunda fila simplemente puede ignorarse.
)`r(.*)
$1
Esto elimina el primero r
de la cadena, porque se ha procesado (desearía haber implementado límites de sustitución ...). El )
también dice Retina para ejecutar todas las etapas hasta éste en un bucle hasta que la cuerda deja de cambiar. En las iteraciones posteriores, la primera etapa es un no-op porque no hay más R
sy la segunda etapa aplicará otra rotación siempre que queden r
s en la cadena.
Cuando hayamos terminado, hemos asignado todos los comandos a la dirección que corresponden en la cuadrícula no rotada y podemos comenzar a procesarlos. Por supuesto, este movimiento es solo una suma de esos vectores delta, y las sumas son conmutativas, por lo que realmente no importa en qué orden las procesemos ahora que se han eliminado las rotaciones.
^
:
Inserte el delimitador de coordenadas en el frente.
Ahora realmente no necesitamos procesar s
y w
. Son nuestros +1
y -1
dígitos y ya están en el lado correcto del, :
por lo que simplemente se abandonarán como se requiere al final. Podemos hacer otra simplificación: a
es simple s + q
y e
es w + d
. Vamos a hacer eso:
a
sq
e
wd
Una vez más, esos s
y w
simplemente abandonarán. Todo lo que tenemos que hacer es mover esos q
s y d
s en la parte delantera y los convierten en w
s y s
sí lo es. Hacemos eso con dos bucles separados:
+`(.+)q
w$1
+`(.+)d
s$1
Así que ya está hecho. Tiempo para la conversión de coordenadas axiales a offset. Para eso necesitamos colapsar los dígitos. Sin embargo, por ahora solo nos importa el lado izquierdo. Debido a la forma en que hemos procesado los q
sys d
, sabemos que todos los s
s en el lado izquierdo aparecerán frente a cualquier w
s, por lo que solo necesitamos verificar un par para colapsarlos:
+`sw
Ahora la conversión real. Aquí está el pseudocódigo, tomado del enlace de arriba:
# convert cube to odd-q offset
col = x
row = z + (x - (x&1)) / 2
Derecha, entonces el lado izquierdo ya está correcto. Sin (x - (x&1)) / 2
embargo, el lado derecho necesita el término de corrección . Tomar &1
es lo mismo que el módulo 2. Esto básicamente se analiza como x/2
, división entera, redondeada hacia menos infinito. Entonces, para positivo x
, sumamos la mitad del número de dígitos (redondeado hacia abajo), y para negativo x
, restamos la mitad del número de dígitos (redondeado hacia arriba). Esto se puede expresar de manera sorprendentemente concisa en expresiones regulares:
(.*)(\1w?):
$0$2
Debido a la codicia, incluso x
, el grupo 1 coincidirá exactamente con la mitad de los dígitos, \1
la otra mitad y podemos ignorar el w?
. Insertamos esa mitad después de :
(que es x/2
). Si x
es par, entonces necesitamos distinguir entre positivo y negativo. Si x
es positivo, entonces w?
nunca coincidirá, por lo que los dos grupos aún tendrán que coincidir con el mismo número de dígitos. Eso no es problema si el primero s
simplemente se omite, por lo que redondeamos hacia abajo. Si x
es negativo e impar, entonces la posible coincidencia es con \1
(la mitad de x
redondeado hacia abajo) y eso es opcional w
. Dado que ambos van en grupo 2
, escribiremos x/2
con la magnitud redondeada (según sea necesario).
+`sw|ws
Ahora colapsamos los dígitos en el lado derecho. Esta vez, no sabemos el orden de los s
y w
, por lo que debemos tener en cuenta ambos pares.
w+
-$0
Ambas partes ahora se reducen a un solo dígito repetido (o nada). Si ese dígito es w
, insertamos un signo menos al frente.
\w
1
Y, finalmente, nos volvemos a ambos en w
y s
en un solo dígito unario razonable. (Supongo que podría guardar un byte usando w
o s
como dígito unario, pero eso parece un poco exagerado).