Retina , 353 339 178 175 150 130 129 117 байт
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
Вывод в одинарных, разделенных двоеточием. Это означает, что вы действительно не увидите нули в выходных данных (хотя наличие двоеточия скажет вам, какая из двух координат равна нулю, если есть только одна).
Попробуйте онлайн!
Это было действительно весело и оказалось на удивление коротким. :)
объяснение
Сначала немного предыстории. Существует несколько систем координат для описания гексагональных сеток. Запрашиваемый использует координаты смещения. По сути это похоже на прямоугольные координаты сетки, за исключением того, что одна ось немного «качается». В частности, вопрос касается макета «odd-q», показанного на связанной странице. Эта система координат немного раздражает в работе, потому что то, как координаты меняются во время перемещения, зависит не только от направления движения, но и от текущей позиции.
Другая система координат использует осевые координаты. Это, по сути, представление гекс-сетки в виде диагонального среза через объем кубов и использование двух осей (например, x и z), чтобы найти положение на 2D-плоскости. На шестигранной сетке это означает, что две оси образуют угол 60 (или 120) градусов. Эта система немного менее интуитивна, но с ней гораздо проще работать, поскольку каждое направление соответствует фиксированному «дельта» вектору. (Для лучшего объяснения того, как прийти к этой системе координат, посмотрите ссылку и прекрасные схемы и анимации там.)
Итак, вот что мы сделаем: мы вычисляем движение в осевых координатах (заботясь о вращении, как предложено в задаче, переназначая значение команд), а когда мы закончим, мы преобразуем осевое в смещение odd-q координаты.
Шесть перемещений отображаются на следующие дельта-векторы в (xz) осевых координатах:
q => (-1, 0)
w => ( 0, -1)
e => ( 1, -1)
d => ( 1, 0)
s => ( 0, 1)
a => (-1, 1)
Подождите, это Retina, нам придется работать с одинарными номерами. Как мы работаем с отрицательными одинарными числами? Идея состоит в том, чтобы использовать две разные цифры. Один представляет, +1
а другой представляет -1
. Это означает, что независимо от того, хотим ли мы добавить или вычесть 1
из текущей позиции, мы всегда можем сделать это, добавив цифру. Когда мы закончим, мы свернем результат в его величину (соответствующей цифры), отменив сбалансированные цифры. Затем мы вычисляем знак на основе оставшейся цифры и заменяем все цифры на 1
.
План состоит в том, чтобы построить осевые компоненты x и z слева и справа от a :
(как разделитель), перед входом. w
и s
добавит к правой стороне. q
и d
добавит к левой стороне, и e
и a
добавит к обеим сторонам. Так как w
и s
уже на правильной стороне :
(которая будет идти впереди), мы будем использовать те, что -1
и +1
цифры соответственно.
Давайте пройдемся по коду.
R
5$*r
Мы начинаем с превращения каждого R
в пять r
с. Конечно, один левый поворот такой же, как пять правых поворотов на шестигранной сетке, и, делая это, мы можем много дублировать на шаге переназначения.
T`aq\we\ds`so`r.+
Это этап транслитерации, который вращает шесть команд, если они найдены после первой r
(тем самым обрабатывая первую r
). w
и d
должны быть экранированы, чтобы предотвратить их расширение в классы персонажей. o
Вставляет набор источника в целевой набор , который экономит кучу байтов для выполнения этих задач вращения. Следовательно, отображение символов:
aqweds
saqweds
где последний s
во втором ряду может быть просто проигнорирован.
)`r(.*)
$1
Это удаляет первое r
из строки, потому что оно было обработано (хотелось бы, чтобы уже были введены ограничения на подстановку ...). Кроме )
того, Retina сообщает, что все этапы до этой стадии должны выполняться в цикле, пока строка не перестанет меняться. На последующих итерациях первый этап - это запрет, так как больше нет R
s, а второй этап будет применять другое вращение, пока r
в строке осталось s.
Когда мы закончим, мы сопоставили все команды с направлением, которому они соответствуют в не повернутой сетке, и можем приступить к их обработке. Конечно, это движение является просто суммой этих дельта-векторов, и суммы являются коммутативными, поэтому не имеет значения, в каком порядке мы обрабатываем их сейчас, когда вращения были исключены.
^
:
Вставьте разделитель координат спереди.
Теперь нам не нужно обрабатывать s
и w
. Они наши +1
и -1
цифры, и они уже на правильной стороне, :
поэтому они просто выпадут, как требуется в конце. Мы можем сделать еще одно упрощение: a
просто s + q
и e
есть w + d
. Давайте сделаем это:
a
sq
e
wd
Опять те s
и w
просто выпадут. Все, что нам нужно сделать, это переместить эти q
s и d
s вперед и превратить их в w
s и s
s сами. Мы делаем это с двумя отдельными циклами:
+`(.+)q
w$1
+`(.+)d
s$1
Итак, все готово. Время для перевода из осевых координат в смещение. Для этого нам нужно свернуть цифры. Однако сейчас нас интересует только левая сторона. Благодаря тому, как мы обработали q
s и d
s, мы знаем, что все s
s в левой части будут отображаться перед любыми w
s, поэтому нам нужно проверить только одну пару, чтобы свернуть их:
+`sw
Теперь актуальная конверсия. Вот псевдокод, взятый по ссылке выше:
# convert cube to odd-q offset
col = x
row = z + (x - (x&1)) / 2
Правильно, поэтому левая часть уже правильная. Правая сторона требует исправления, (x - (x&1)) / 2
хотя. Взятие &1
такое же, как по модулю 2. Это в основном разбирает как x/2
целочисленное деление, округленное до минус бесконечности. Таким образом, для положительного x
, мы добавляем половину количества цифр (округляется в меньшую сторону), а для отрицательного x
мы вычитаем половину количества цифр (округляется в большую сторону). Это может быть удивительно кратко выражено в регулярном выражении:
(.*)(\1w?):
$0$2
Из-за жадности, для четной x
группы 1 будет совпадать ровно с половиной цифр, \1
с другой половиной, и мы можем игнорировать w?
. Мы вставляем эту половину после :
(что есть x/2
). Если x
чёт, то нужно различать положительное и отрицательное. Если x
положительный, то w?
никогда не будет совпадать, поэтому две группы все равно должны будут совпадать с одинаковым количеством цифр. Это не проблема, если первый s
просто пропущен, поэтому мы округляемся. Если x
он отрицательный и нечетный, то возможное совпадение с \1
(половина x
округленного вниз) и это необязательно w
. Так как оба из них идут в группу 2
, мы будем писать x/2
с округленной величиной (как требуется).
+`sw|ws
Теперь мы свернем цифры на правой стороне. На этот раз мы не знаем порядок s
и w
, поэтому нам нужно учесть обе пары.
w+
-$0
Обе части теперь уменьшены до одной повторяющейся цифры (или ничего). Если это цифра w
, мы вставляем знак минус впереди.
\w
1
И, наконец, мы превращаем w
и s
в одну разумную унарную цифру. (Я полагаю, что я мог бы сохранить байт, используя w
или s
в качестве унарной цифры, но это кажется немного натянутым.)