Retina , 353 339 178 175 150 130 129 117 byte
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
Keluaran dalam unary, dipisahkan oleh titik dua. Itu berarti Anda tidak akan benar-benar melihat nol di output (walaupun keberadaan titik dua akan memberi tahu Anda yang mana dari dua koordinat tersebut nol, jika hanya ada satu).
Cobalah online!
Ini benar-benar menyenangkan dan akhirnya menjadi sangat singkat. :)
Penjelasan
Beberapa latar belakang terlebih dahulu. Ada beberapa sistem koordinat untuk menggambarkan kisi heksagonal. Yang meminta menggunakan koordinat offset. Itu pada dasarnya seperti koordinat kotak persegi panjang, kecuali bahwa satu sumbu "bergoyang" sedikit. Secara khusus, pertanyaannya menanyakan tata letak "odd-q" yang ditunjukkan pada halaman yang ditautkan. Sistem koordinat ini agak menjengkelkan untuk digunakan, karena bagaimana koordinat berubah selama gerakan tidak hanya bergantung pada arah gerakan tetapi juga pada posisi saat ini.
Sistem koordinat lain menggunakan koordinat aksial. Itu pada dasarnya membayangkan hexgrid sebagai irisan diagonal melalui volume kubus, dan menggunakan dua sumbu (misalnya x dan z) untuk menemukan posisi pada bidang 2D. Pada kisi hex, itu berarti bahwa kedua sumbu membentuk sudut 60 (atau 120) derajat. Sistem ini sedikit kurang intuitif tetapi jauh lebih mudah untuk dikerjakan, karena setiap arah sesuai dengan vektor "delta" yang diperbaiki. (Untuk penjelasan yang lebih baik tentang cara tiba di sistem koordinat ini, lihat tautan dan diagram serta animasi yang indah di sana.)
Jadi inilah yang akan kita lakukan: kita menghitung pergerakan dalam koordinat aksial (menjaga rotasi seperti yang disarankan dalam tantangan, dengan memetakan kembali makna perintah), dan ketika kita selesai kita mengonversi aksial ke aneh-q offset koordinat.
Enam bergerak peta ke vektor delta berikut di (xz) koordinat aksial:
q => (-1, 0)
w => ( 0, -1)
e => ( 1, -1)
d => ( 1, 0)
s => ( 0, 1)
a => (-1, 1)
Tunggu, ini Retina, kita harus bekerja dengan angka-angka unary. Bagaimana cara kita bekerja dengan angka unary negatif? Idenya adalah menggunakan dua digit berbeda. Satu mewakili +1
dan yang lainnya mewakili -1
. Itu berarti terlepas dari apakah kita ingin menambah atau mengurangi 1
posisi sekarang, kita selalu dapat melakukannya dengan menambahkan angka. Setelah selesai, kami menciutkan hasilnya menjadi besarnya (dari angka yang sesuai) dengan membatalkan angka seimbang. Kemudian kami mencari tanda berdasarkan digit yang tersisa, dan mengganti semua digit 1
.
Rencananya adalah untuk membangun komponen aksial x dan z ke kiri dan kanan a :
(sebagai pemisah), di depan input. w
dan s
akan ditambahkan ke sisi kanan. q
dan d
akan menambah ke sisi kiri, dan e
dan a
akan menambah ke kedua sisi. Karena w
dan s
sudah berada di sisi yang benar dari :
(yang akan berada di depan), kita akan menggunakannya sebagai masing-masing -1
dan +1
digit.
Mari kita lihat kodenya.
R
5$*r
Kita mulai dengan mengubah masing R
- masing menjadi lima r
. Tentu saja, satu belokan kiri sama dengan lima belokan kanan pada hex grid, dan dengan melakukan itu kita dapat banyak duplikasi pada langkah pemetaan ulang.
T`aq\we\ds`so`r.+
Ini adalah tahap transliterasi yang memutar enam perintah, jika ditemukan setelah yang pertama r
(dengan demikian memproses yang pertama r
). w
dan d
perlu melarikan diri untuk mencegah mereka berkembang ke kelas karakter. The o
menyisipkan sumber set ke target yang ditetapkan yang menyimpan banyak byte untuk tugas-tugas rotasi ini. Karenanya pemetaan karakter:
aqweds
saqweds
di mana yang terakhir s
di baris kedua bisa diabaikan begitu saja.
)`r(.*)
$1
Ini menghapus yang pertama r
dari string, karena sudah diproses (saya berharap saya sudah menerapkan batas substitusi ...). The )
juga memberitahu Retina untuk menjalankan semua tahapan hingga satu ini dalam satu lingkaran sampai string berhenti berubah. Pada iterasi berikutnya, tahap pertama adalah no-op karena tidak ada lagi R
s dan tahap kedua akan menerapkan rotasi lain selama ada yang r
tersisa di string.
Setelah selesai, kami telah memetakan semua perintah ke arah yang sesuai pada kisi yang tidak diputar dan dapat mulai memprosesnya. Tentu saja pergerakan ini hanyalah jumlah dari vektor-vektor delta itu, dan penjumlahannya bersifat komutatif, jadi tidak masalah dalam urutan mana kita memprosesnya sekarang karena rotasi telah dihilangkan.
^
:
Masukkan pembatas koordinat di depan.
Sekarang kita tidak benar-benar perlu memproses s
dan w
. Mereka adalah milik kita +1
dan -1
angka dan mereka sudah berada di sisi yang benar :
sehingga mereka hanya akan keluar seperti yang dibutuhkan pada akhirnya. Kita dapat membuat penyederhanaan lain: a
sederhana s + q
dan e
sedang w + d
. Ayo lakukan itu:
a
sq
e
wd
Sekali lagi, itu s
dan w
hanya akan putus. Yang perlu kita lakukan adalah memindahkan q
itu dan d
ke depan dan mengubahnya menjadi w
dan s
itu sendiri. Kami melakukannya dengan dua loop terpisah:
+`(.+)q
w$1
+`(.+)d
s$1
Jadi sudah selesai. Saatnya konversi dari koordinat aksial ke offset. Untuk itu kita perlu menciutkan digit. Namun, untuk saat ini kami hanya peduli dengan sisi kiri. Karena cara kami memproses huruf q
s dan d
s, kami tahu bahwa semua s
huruf s di sisi kiri akan muncul di depan huruf w
s, jadi kami hanya perlu memeriksa satu pasangan untuk menciutkannya:
+`sw
Sekarang konversi yang sebenarnya. Ini adalah kodesemu, diambil dari tautan di atas:
# convert cube to odd-q offset
col = x
row = z + (x - (x&1)) / 2
Benar, jadi sisi kiri sudah benar. Sisi kanan membutuhkan jangka waktu koreksi (x - (x&1)) / 2
. Pengambilan &1
sama dengan modulo 2. Ini pada dasarnya diurai sebagai x/2
, pembagian bilangan bulat, dibulatkan ke arah minus tanpa batas. Jadi untuk positif x
, kita tambahkan setengah jumlah digit (dibulatkan ke bawah), dan untuk negatif x
, kita kurangi setengah jumlah digit (dibulatkan ke atas). Hal ini dapat diekspresikan secara mengejutkan dalam regex:
(.*)(\1w?):
$0$2
Karena keserakahan, untuk genap x
, grup 1 akan cocok persis setengah digit, \1
separuh lainnya dan kita bisa mengabaikannya w?
. Kami memasukkan setengah setelah :
(yang x/2
). Jika x
genap, maka kita perlu membedakan positif dan negatif. Jika x
positif, maka w?
tidak akan pernah cocok, sehingga kedua kelompok masih harus mencocokkan jumlah digit yang sama. Itu tidak masalah jika yang pertama s
hanya dilewati, jadi kami membulatkannya. Jika x
negatif dan ganjil, maka kemungkinan kecocokannya adalah dengan \1
(setengah x
dibulatkan ke bawah) dan opsional itu w
. Karena keduanya masuk dalam grup 2
, kita akan menulis x/2
dengan magnitudo yang dibulatkan (sesuai kebutuhan).
+`sw|ws
Sekarang kita meruntuhkan angka di sisi kanan. Kali ini, kami tidak tahu urutan s
dan w
, jadi kami harus memperhitungkan kedua pasangan.
w+
-$0
Kedua bagian sekarang dikurangi menjadi satu digit berulang (atau tidak sama sekali). Jika digit itu adalah w
, kami menyisipkan tanda minus di depan.
\w
1
Dan akhirnya kita mengubah keduanya menjadi w
dan s
menjadi satu digit unary yang masuk akal. (Saya kira saya bisa menyimpan byte dengan menggunakan w
atau s
sebagai digit unary, tapi sepertinya sedikit peregangan.)