Rétine , 353 339 178 175 150 130 129 129 117 octets
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 sortie est en unaire, séparée par deux points. Cela signifie que vous ne verrez pas vraiment de zéros dans la sortie (bien que la présence de deux points vous dira laquelle des deux coordonnées est nulle, s'il n'y en a qu'une).
Essayez-le en ligne!
C'était vraiment amusant et a fini par être étonnamment court. :)
Explication
Quelques antécédents en premier. Il existe plusieurs systèmes de coordonnées pour décrire les grilles hexagonales. Celui qui a demandé utilise des coordonnées de décalage. C'est essentiellement comme les coordonnées de la grille rectangulaire, sauf qu'un axe "oscille" un peu. En particulier, la question demande la disposition "impaire-q" affichée sur la page liée. Ce système de coordonnées est un peu ennuyeux à utiliser, car la façon dont les coordonnées changent pendant un mouvement dépend non seulement de la direction du mouvement, mais également de la position actuelle.
Un autre système de coordonnées utilise des coordonnées axiales. C'est essentiellement imaginer l'hexgrid comme une tranche diagonale à travers un volume de cubes, et utiliser deux des axes (par exemple x et z) pour trouver une position sur le plan 2D. Sur la grille hexagonale, cela signifie que les deux axes forment un angle de 60 (ou 120) degrés. Ce système est un peu moins intuitif mais beaucoup plus facile à utiliser, car chaque direction correspond à un vecteur "delta" fixe. (Pour une meilleure explication de la façon d'arriver à ce système de coordonnées, consultez le lien et les jolis diagrammes et animations là-bas.)
Alors, voici ce que nous allons faire: nous calculons le mouvement en coordonnées axiales (en prenant soin de la rotation comme suggéré dans le défi, en remappant la signification des commandes), et lorsque nous avons terminé, nous convertissons l'axial en offset impair-q coordonnées.
Les six mouvements correspondent aux vecteurs delta suivants en coordonnées axiales (xz):
q => (-1, 0)
w => ( 0, -1)
e => ( 1, -1)
d => ( 1, 0)
s => ( 0, 1)
a => (-1, 1)
Attendez, c'est Retina, nous devrons travailler avec des nombres unaires. Comment travaillons-nous avec des nombres unaires négatifs? L'idée est d'utiliser deux chiffres différents. L'un représente +1
et l'autre représente -1
. Cela signifie que, que nous voulions ajouter ou soustraire 1
de la position actuelle, nous pouvons toujours le faire en ajoutant un chiffre. Lorsque nous avons terminé, nous réduisons le résultat dans son amplitude (du chiffre correspondant) en annulant les chiffres équilibrés. Ensuite, nous déterminons le signe en fonction du chiffre restant et remplaçons tous les chiffres par 1
.
Le plan est de construire les composantes axiales x et z à gauche et à droite de a :
(comme séparateur), devant l'entrée. w
et s
s'ajoutera au côté droit. q
et d
s'ajoutera au côté gauche, et e
et a
s'ajoutera aux deux côtés. Puisque w
et s
sont déjà du bon côté de :
(qui ira devant), nous les utiliserons respectivement comme les chiffres -1
et +1
.
Passons en revue le code.
R
5$*r
Nous commençons par transformer chacun R
en cinq r
s. Bien sûr, un virage à gauche est le même que cinq virages à droite sur une grille hexadécimale, et ce faisant, nous pouvons faire beaucoup de duplication sur l'étape de remappage.
T`aq\we\ds`so`r.+
Il s'agit d'une étape de translittération qui fait tourner les six commandes, si elles sont trouvées après la première r
(traitant ainsi la première r
). w
et d
doivent être échappés pour les empêcher de se développer dans les classes de caractères. L' o
insère l'ensemble source dans l'ensemble cible, ce qui enregistre un tas d'octets pour ces tâches de rotation. Le mappage de caractères est donc:
aqweds
saqweds
où le dernier s
de la deuxième ligne peut simplement être ignoré.
)`r(.*)
$1
Cela supprime le premier r
de la chaîne, car il a été traité (j'aurais aimé avoir déjà implémenté des limites de substitution ...). Le )
indique également à Retina d'exécuter toutes les étapes jusqu'à celle-ci en boucle jusqu'à ce que la chaîne cesse de changer. Lors des itérations suivantes, la première étape est un no-op car il n'y a plus de R
s et la deuxième étape appliquera une autre rotation tant qu'il restera des r
s dans la chaîne.
Lorsque nous avons terminé, nous avons mappé toutes les commandes dans la direction à laquelle elles correspondent sur la grille non tournée et pouvons commencer à les traiter. Bien sûr, ce mouvement n'est qu'une somme de ces vecteurs delta, et les sommes sont commutatives, donc peu importe dans quel ordre nous les traitons maintenant que les rotations ont été éliminées.
^
:
Insérez le délimiteur de coordonnées à l'avant.
Maintenant, nous n'avons pas vraiment besoin de traiter s
et w
. Ils sont nos +1
et -1
chiffres et ils sont déjà du bon côté du :
donc ils vont juste abandonner comme requis à la fin. Nous pouvons faire une autre simplification: a
est simplement s + q
et e
est w + d
. Faisons cela:
a
sq
e
wd
Encore une fois, ceux-ci s
et w
abandonneront. Tout ce que nous devons faire est de déplacer ces q
s et d
s vers l'avant et de les transformer en w
s et s
s eux-mêmes. Nous le faisons avec deux boucles distinctes:
+`(.+)q
w$1
+`(.+)d
s$1
Alors c'est fait. Temps de conversion des coordonnées axiales en coordonnées décalées. Pour cela, nous devons réduire les chiffres. Cependant, pour l'instant, nous ne nous soucions que du côté gauche. En raison de la façon dont nous avons traité les q
s et les d
s, nous savons que tous les s
s du côté gauche apparaîtront devant tous les w
s, nous n'avons donc qu'à vérifier une paire pour les réduire:
+`sw
Maintenant, la conversion réelle. Voici le pseudocode, extrait du lien ci-dessus:
# convert cube to odd-q offset
col = x
row = z + (x - (x&1)) / 2
Droite, donc le côté gauche est déjà correct. Le côté droit a cependant besoin du terme de correction (x - (x&1)) / 2
. La prise &1
est la même que pour le modulo 2. Elle est essentiellement analysée comme une x/2
division entière, arrondie vers moins l'infini. Donc, pour le positif x
, nous ajoutons la moitié du nombre de chiffres (arrondi vers le bas), et pour le négatif x
, nous soustrayons la moitié du nombre de chiffres (arrondi vers le haut). Cela peut être exprimé de manière étonnamment concise en expression régulière:
(.*)(\1w?):
$0$2
En raison de la gourmandise, même x
, le groupe 1 correspondra exactement à la moitié des chiffres, \1
l'autre moitié et nous pouvons ignorer le w?
. Nous insérons cette moitié après le :
(qui est x/2
). Si x
c'est pair, alors nous devons distinguer le positif du négatif. Si x
est positif, alors w?
ne correspondra jamais, donc les deux groupes devront toujours correspondre au même nombre de chiffres. Ce n'est pas un problème si le premier s
est simplement ignoré, alors nous arrondissons. Si x
est négatif et impair, alors la correspondance possible est avec \1
(la moitié de l' x
arrondi vers le bas) et celle facultative w
. Étant donné que les deux vont dans le groupe 2
, nous écrirons x/2
avec la magnitude arrondie (au besoin).
+`sw|ws
Maintenant, nous réduisons les chiffres sur le côté droit. Cette fois, nous ne connaissons pas l'ordre des s
et w
, nous devons donc tenir compte des deux paires.
w+
-$0
Les deux parties sont maintenant réduites à un seul chiffre répété (ou rien). Si ce chiffre est w
, nous insérons un signe moins devant.
\w
1
Et enfin, nous transformons à la fois en w
et s
en un seul chiffre unaire raisonnable. (Je suppose que je pourrais enregistrer un octet en utilisant w
ou s
comme chiffre unaire, mais cela semble un peu étiré.)