Üzgünüm millet, bu sefer altıgen yok ...
Bayt sayısı, ISO 8859-1 kodlamasını varsayar.
.+¶
$.'$*_¶$&
^_¶
¶
((^_|\2_)*)_\1{5}_+
$2_
^_*
$.&$*×_$&$&$.&$*×
M!&m`(?<=(?=×*(_)+)\A.*)(?<-1>.)+(?(1)!)|^.*$
O$`(_)|.(?=.*$)
$1
G-2`
T`d`À-É
m`\A(\D*)(?(_)\D*¶.|(.)\D*¶\2)((.)(?<=(?<4>_)\D+)?((?<=(?<1>\1.)\4\D*)|(?<=(?<1>\D*)\4(?<=\1)\D*)|(?<=\1(.(.)*¶\D*))((?<=(?<1>\D*)\4(?>(?<-7>.)*)¶.*\6)|(?<=(?<1>\D*)(?=\4)(?>(?<-7>.)+)¶.*\6))|(?<=(×)*¶.*)((?<=(?<1>\1.(?>(?<-9>¶.*)*))^\4\D*)|(?<=(?<1>\D*)\4(?>(?<-9>¶.*)*)(?<=\1)^\D*)|(?<=(?<1>\1\b.*(?(9)!)(?<-9>¶.*)*)\4×*¶\D*)|(?<=(?<1>\D*\b)\4.*(?(9)!)(?<-9>¶.*)*(?<=\1.)\b\D*))|(?<=(?<1>\1.(?>(?<-11>.)*)¶.*)\4(.)*¶\D*)|(?<=(?<1>\1(?>(?<-12>.)*)¶.*)\4(.)*¶\D*)|(?<=(?<1>\1.(?>(?<-13>.)*¶\D*))\4(\w)*\W+.+)|(?<=(?<1>.*)\4(?>(?<-14>.)*¶\D*)(?<=\1.)(\w)*\W+.+))(?<=\1(\D*).+)(?<!\1\15.*(?<-1>.)+))*\Z
İlk satırdaki hedef dizgiyi ve girişin ikinci satırındaki altıgeni bekler. 0
Veya 1
buna göre yazdırır .
Çevrimiçi deneyin! (İlk satır, ¦
bir satır besleme yerine ayırmak için kullanılan her satırın bir sınama durumu olduğu bir test takımını etkinleştirir .)
Bu zorluğu çözmenin doğru yolu elbette ki regex. ;) Ve bu zorluğun altıgenin açılma prosedürünü de içermemesi gerçeği olmasaydı, bu cevap aslında ~ 600 bayt uzunluğundaki tek bir regex'ten başka bir şeyden oluşmuyordu.
Bu henüz en iyi şekilde golf oynamadı, ancak sonuçtan oldukça memnunum (ilk çalışan sürümüm, adlandırılmış grupları ve akıl sağlığı için gereken diğer şeyleri çıkardıktan sonra 1000 bayt civarındaydı). Ben dize ve altıgen sırasını değiştirerek yaklaşık 10 bayt tasarruf edebileceğimi düşünüyorum ama sonunda regex tam bir yeniden yazma gerektirir, bu şu anda kadar hissetmiyorum. G
Sahneyi atlayarak 2 baytlık bir tasarruf da var , ancak çözümü oldukça yavaşlatıyor, bu yüzden elimden geldiğince golf oynayana kadar bu değişikliği yapmayı bekleyeceğim.
açıklama
Bu çözümün ana kısmı, dengeleme gruplarını kapsamlı bir şekilde kullanır , bu yüzden bunların nasıl çalıştığını anlamak istiyorsanız, onları okumanızı öneririm (eğer yapmazsanız sizi suçlamam ...).
Çözümün ilk kısmı (yani, son iki satır dışındaki her şey), Hexagony kaynak kodunu çözme yanıtımın değiştirilmiş bir versiyonudur . Hedef dizgiye dokunulmadan bırakarak altıgeni inşa eder (ve gerçekte hedef dizgiden önce altıgen oluşturur). Baytları kaydetmek için önceki kodda bazı değişiklikler yaptım:
- Arkaplan karakteri
×
bir boşluk yerine girişteki potansiyel boşluklarla çatışmayacak şekildedir.
- No-op / joker karakter
_
yerine .
, ızgara hücreleri güvenilir bir şekilde kelime karakterleri olarak tanımlanabilir.
- Altıgen ilk yapıldıktan sonra herhangi bir boşluk veya girintiye eklemem. Bu bana eğimli bir altıgen veriyor, ancak bu gerçekten çalışmak için daha uygun ve bitişik kurallar oldukça basit.
İşte bir örnek. Aşağıdaki test durumu için:
ja
abcdefghij
Biz alırız:
××abc
×defg
hij__
____×
___××
ja
Bunu altıgenin normal mizanpajıyla karşılaştırın:
a b c
d e f g
h i j _ _
_ _ _ _
_ _ _
Komşuların, kuzey-batı ve güney-doğu komşuları hariç, şimdi her zamanki 8 Moore-komşusunun tümü olduğunu görebiliyoruz. Bu nedenle yatay, dikey ve güney-batı / kuzey-doğu komşulığını kontrol etmemiz gerekiyor (peki ve sonra sarma kenarları var). Bu daha küçük yerleşimi kullanmak ××
, ihtiyaç duyduğumuzda anında altıgenin boyutunu belirlemek için sonunda bunları kullanabileceğimiz avantajına da sahip .
Bu form oluşturulduktan sonra, dizgenin tamamında bir değişiklik daha yaparız:
T`d`À-É
Bu, rakamları genişletilmiş ASCII harfleriyle değiştirir.
ÀÁÂÃÄÅÆÇÈÉ
Hem altıgen hem de hedef dizgede değiştirildiklerinden bu, dizginin eşleşip eşleşmediğini etkilemez. Ayrıca, çünkü onlar harfler \w
ve \b
hala altıgen hücre olarak tanımlamak. Bu ikame yapmanın faydası \D
, şimdiki regex'te herhangi bir karakterle (özellikle satır çizgileri ve satır çizgisi olmayan karakterler) eşleşmek için kullanabileceğimizdir . Bunu s
gerçekleştirmek için seçeneği kullanamayız , çünkü .
birkaç yerde satır içi olmayan karakterlerle eşleşmemiz gerekir .
Şimdi son bit: herhangi bir yolun verilen dizgimize eşleşip eşleşmediğini belirleme. Bu tek canavarca bir regex ile yapılır. Kendine neden sorabilirsin ? Peki, bu temelde bir geri izleme sorunudur: bir yere başlarsınız ve dizeyle eşleştiği sürece bir yol denersiniz ve bir kez geri dönüp, çalışan son karakterden farklı bir komşu denemezsiniz. bir şeyregex ile çalışırken ücretsiz olarak alabilmeniz geri izlemedir. Kelimenin tam anlamıyla regex motorunun yaptığı tek şey bu. Dolayısıyla, geçerli bir yolu tanımlamanın bir yolunu bulursak (bu tür bir sorun için yeterince zor, ancak kesinlikle dengeleme gruplarıyla mümkün), o zaman regex motoru bizim için olası olanların arasından bu yolu bulacaktır. Aramanın birden fazla aşamada ( ve daha önce de yaptım) elle yapılması kesinlikle mümkün olacaktır , ancak bu özel durumda daha kısa olacağından şüpheliyim.
Bunun bir regex ile uygulanmasındaki bir sorun, regex motorunun imlecini geri izleme sırasında (yol yukarı veya aşağı gidebileceğinden beri ihtiyacımız olan) dizge boyunca keyfi olarak öremememizdir. Bunun yerine, yakalama grubundaki kendi "imlecimizi" izliyor ve her adımda bunu güncelliyoruz (imleci geçici olarak bir bakış açısıyla hareket ettirebiliriz). Bu aynı zamanda, daha önce mevcut pozisyonu ziyaret etmediğimizi kontrol etmek için kullanacağımız tüm pozisyonları kaydetmemizi sağlar.
Öyleyse başlayalım. İşte regex'in biraz daha akılda kalıcı hali, isimlendirilmiş gruplar, girintiler, daha az rastgele komşular sırası ve bazı yorumlar:
\A
# Store initial cursor position in <pos>
(?<pos>\D*)
(?(_)
# If we start on a wildcard, just skip to the first character of the target.
\D*¶.
|
# Otherwise, make sure that the target starts with this character.
(?<first>.)\D*¶\k<first>
)
(?:
# Match 0 or more subsequent characters by moving the cursor along the path.
# First, we store the character to be matched in <next>.
(?<next>.)
# Now we optionally push an underscore on top (if one exists in the string).
# Depending on whether this done or not (both of which are attempted by
# the engine's backtracking), either the exact character, or an underscore
# will respond to the match. So when we now use the backreference \k<next>
# further down, it will automatically handle wildcards correctly.
(?<=(?<next>_)\D+)?
# This alternation now simply covers all 6 possible neighbours as well as
# all 6 possible wrapped edges.
# Each option needs to go into a separate lookbehind, because otherwise
# the engine would not backtrack through all possible neighbours once it
# has found a valid one (lookarounds are atomic).
# In any case, if the new character is found in the given direction, <pos>
# will have been updated with the new cursor position.
(?:
# Try moving east.
(?<=(?<pos>\k<pos>.)\k<next>\D*)
|
# Try moving west.
(?<=(?<pos>\D*)\k<next>(?<=\k<pos>)\D*)
|
# Store the horizontal position of the cursor in <x> and remember where
# it is (because we'll need this for the next two options).
(?<=\k<pos>(?<skip>.(?<x>.)*¶\D*))
(?:
# Try moving north.
(?<=(?<pos>\D*)\k<next>(?>(?<-x>.)*)¶.*\k<skip>)
|
# Try moving north-east.
(?<=(?<pos>\D*)(?=\k<next>)(?>(?<-x>.)+)¶.*\k<skip>)
)
|
# Try moving south.
(?<=(?<pos>\k<pos>.(?>(?<-x>.)*)¶.*)\k<next>(?<x>.)*¶\D*)
|
# Try moving south-east.
(?<=(?<pos>\k<pos>(?>(?<-x>.)*)¶.*)\k<next>(?<x>.)*¶\D*)
|
# Store the number of '×' at the end in <w>, which is one less than the
# the side-length of the hexagon. This happens to be the number of lines
# we need to skip when wrapping around certain edges.
(?<=(?<w>×)*¶.*)
(?:
# Try wrapping around the east edge.
(?<=(?<pos>\k<pos>.(?>(?<-w>¶.*)*))^\k<next>\D*)
|
# Try wrapping around the west edge.
(?<=(?<pos>\D*)\k<next>(?>(?<-w>¶.*)*)(?<=\k<pos>)^\D*)
|
# Try wrapping around the south-east edge.
(?<=(?<pos>\k<pos>\b.*(?(w)!)(?<-w>¶.*)*)\k<next>×*¶\D*)
|
# Try wrapping around the north-west edge.
(?<=(?<pos>\D*\b)\k<next>.*(?(w)!)(?<-w>¶.*)*(?<=\k<pos>.)\b\D*)
)
|
# Try wrapping around the south edge.
(?<=(?<pos>\k<pos>.(?>(?<-x>.)*¶\D*))\k<next>(?<x>\w)*\W+.+)
|
# Try wrapping around the north edge.
(?<=(?<pos>.*)\k<next>(?>(?<-x>.)*¶\D*)(?<=\k<pos>.)(?<x>\w)*\W+.+)
)
# Copy the current cursor position into <current>.
(?<=\k<pos>(?<current>\D*).+)
# Make sure that no matter how many strings we pop from our stack of previous
# cursor positions, none are equal to the current one (to ensure that we use
# each cell at most once).
(?<!\k<pos>\k<current>.*(?<-pos>.)+)
)*
# Finally make sure that we've reached the end of the string, so that we've
# successfully matched all characters in the target string.
\Z
Umarım genel fikir bundan kabaca netleşir. Yol boyunca bu hareketlerden birinin nasıl çalıştığına bir örnek olarak imleci güneye hareket ettiren bit'e bakalım:
(?<=(?<pos>\k<pos>.(?>(?<-x>.)*)¶.*)\k<next>(?<x>.)*¶\D*)
Gözbebeklerinin sağdan sola (ya da alttan üste) okunması gerektiğini unutmayın, çünkü onların uygulandıkları sıra budur:
(?<=
(?<pos>
\k<pos> # Check that this is the old cursor position.
. # Match the character directly on top of the new one.
(?>(?<-x>.)*) # Match the same amount of characters as before.
¶.* # Skip to the next line (the line, the old cursor is on).
) # We will store everything left of here as the new
# cursor position.
\k<next> # ...up to a match of our current target character.
(?<x>.)* # Count how many characters there are...
¶\D* # Skip to the end of some line (this will be the line below
# the current cursor, which the regex engine's backtracking
# will determine for us).
)
\k<pos>
Bunun gerçekte dizenin başına ulaştığından emin olmak için önüne bir çapa koymak gerekli olmadığını unutmayın . <pos>
her zaman ×
başka hiçbir yerde bulunamayan bir miktarla başlar , bu yüzden bu zaten kapalı bir çapa görevi görür.
Bu gönderiyi gereğinden fazla şişirmek istemiyorum, bu yüzden diğer 11 vakayı da ayrıntılı olarak ele almayacağım, ancak prensip olarak hepsi aynı şekilde çalışıyor. <next>
Dengeleme gruplarının yardımıyla eski imleç konumundan belirli (kabul edilebilir) bir yönde bulunup bulunmadığını kontrol ettik ve ardından yeni imleç konumu olarak dizgiyi bu maça uydurduk <pos>
.