Xin lỗi mọi người, không có Hexagony lần này ...
Số lượng byte giả định mã hóa ISO 8859-1.
.+¶
$.'$*_¶$&
^_¶
¶
((^_|\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
Yêu cầu chuỗi đích trên dòng đầu tiên và hình lục giác trên dòng thứ hai của đầu vào. In 0
hoặc 1
theo đó.
Hãy thử trực tuyến! (Dòng đầu tiên cho phép một bộ kiểm tra, trong đó mỗi dòng là một trường hợp thử nghiệm, sử dụng ¦
để phân tách thay vì một nguồn cấp dữ liệu.)
Cách thích hợp để giải quyết thách thức này là với một regex tất nhiên. ;) Và nếu thực tế là thách thức này cũng liên quan đến thủ tục mở ra của hình lục giác , thì câu trả lời này thực sự sẽ không có gì ngoài một regex dài ~ 600 byte.
Điều này chưa hoàn toàn được đánh gôn, nhưng tôi khá hài lòng với kết quả này (phiên bản làm việc đầu tiên của tôi, sau khi loại bỏ các nhóm được đặt tên và các thứ khác cần thiết cho sự tỉnh táo, là khoảng 1000 byte). Tôi nghĩ rằng tôi có thể tiết kiệm khoảng 10 byte bằng cách hoán đổi thứ tự của chuỗi và hình lục giác nhưng nó sẽ yêu cầu viết lại hoàn toàn regex ở cuối, điều mà tôi không cảm thấy ngay bây giờ. Ngoài ra còn có tiết kiệm 2 byte bằng cách bỏ qua G
giai đoạn, nhưng nó làm chậm giải pháp một cách đáng kể, vì vậy tôi sẽ chờ đợi để thực hiện thay đổi đó cho đến khi tôi chắc chắn rằng tôi đã chơi golf tốt nhất có thể.
Giải trình
Phần chính của giải pháp này sử dụng rộng rãi các nhóm cân bằng , vì vậy tôi khuyên bạn nên đọc chúng, nếu bạn muốn hiểu cách thức hoạt động của chi tiết này (tôi sẽ không đổ lỗi cho bạn nếu bạn không ...).
Phần đầu tiên của giải pháp (tức là tất cả mọi thứ trừ hai dòng cuối cùng) là phiên bản sửa đổi của câu trả lời của tôi để mở ra mã nguồn Hexagony . Nó xây dựng hình lục giác, trong khi không để lại chuỗi đích (và nó thực sự xây dựng hình lục giác trước chuỗi mục tiêu). Tôi đã thực hiện một số thay đổi đối với mã trước đó để lưu byte:
- Ký tự nền
×
thay vì một khoảng trắng để nó không xung đột với các khoảng trắng tiềm năng trong đầu vào.
_
Thay vào đó .
, ký tự no-op / wildcard thay vào đó , để các ô lưới có thể được xác định một cách đáng tin cậy là các ký tự từ.
- Tôi không chèn bất kỳ khoảng trắng hoặc thụt nào sau khi hình lục giác được xây dựng lần đầu tiên. Điều đó mang lại cho tôi một hình lục giác nghiêng, nhưng nó thực sự thuận tiện hơn nhiều để làm việc và các quy tắc kề là khá đơn giản.
Đây là một ví dụ. Đối với trường hợp thử nghiệm sau:
ja
abcdefghij
Chúng tôi nhận được:
××abc
×defg
hij__
____×
___××
ja
So sánh điều này với bố cục thông thường của hình lục giác:
a b c
d e f g
h i j _ _
_ _ _ _
_ _ _
Chúng ta có thể thấy rằng những người hàng xóm bây giờ là tất cả của 8 người hàng xóm Moore thông thường, ngoại trừ những người hàng xóm phía tây bắc và đông nam. Vì vậy, chúng ta cần kiểm tra sự phụ thuộc ngang, dọc và tây nam / đông bắc (tốt và sau đó là các cạnh bao bì). Sử dụng bố cục nhỏ gọn hơn này cũng có phần thưởng là chúng ta sẽ có thể sử dụng những thứ đó ××
ở cuối để xác định kích thước của hình lục giác khi đang cần.
Sau khi hình thức này được xây dựng, chúng tôi thực hiện thêm một thay đổi cho toàn bộ chuỗi:
T`d`À-É
Điều này thay thế các chữ số bằng các chữ cái ASCII mở rộng
ÀÁÂÃÄÅÆÇÈÉ
Vì chúng được thay thế cả trong hình lục giác và trong chuỗi mục tiêu, điều này sẽ không ảnh hưởng đến việc chuỗi có khớp hay không. Ngoài ra, vì chúng là các chữ cái \w
và \b
vẫn xác định chúng là các ô lục giác. Lợi ích của việc thực hiện thay thế này là bây giờ chúng ta có thể sử dụng \D
trong regex sắp tới để khớp với bất kỳ ký tự nào (cụ thể là các nguồn cấp dữ liệu cũng như các ký tự không phải là dòng cấp dữ liệu). Chúng tôi không thể sử dụng s
tùy chọn để thực hiện điều đó, bởi vì chúng tôi sẽ cần .
khớp các ký tự không phải là nguồn cấp dữ liệu ở một số nơi.
Bây giờ là bit cuối cùng: xác định xem có đường dẫn nào khớp với chuỗi đã cho của chúng tôi không. Điều này được thực hiện với một regex quái dị duy nhất. Bạn có thể tự hỏi tại sao?!?! Chà, về cơ bản đây là một vấn đề quay lui: bạn bắt đầu ở đâu đó và thử một con đường miễn là nó khớp với chuỗi, và một khi nó không quay lại và thử một người hàng xóm khác với nhân vật cuối cùng hoạt động. Các một điềumà bạn nhận được miễn phí khi làm việc với regex là quay lại. Đó thực sự là điều duy nhất mà động cơ regex làm. Vì vậy, nếu chúng ta chỉ tìm cách mô tả một đường dẫn hợp lệ (đủ khó cho loại vấn đề này, nhưng chắc chắn là có thể với các nhóm cân bằng), thì công cụ regex sẽ sắp xếp tìm ra đường dẫn đó trong số tất cả các đường có thể cho chúng ta. Chắc chắn có thể thực hiện tìm kiếm thủ công với nhiều giai đoạn ( và tôi đã làm như vậy trong quá khứ ), nhưng tôi nghi ngờ nó sẽ ngắn hơn trong trường hợp cụ thể này.
Một vấn đề khi thực hiện điều này với regex là chúng ta không thể tùy ý dệt con trỏ của công cụ regex qua lại chuỗi trong quá trình quay lui (mà chúng ta cần vì đường dẫn có thể đi lên hoặc xuống). Vì vậy, thay vào đó, chúng tôi theo dõi "con trỏ" của chính mình trong một nhóm bắt giữ và cập nhật nó ở mỗi bước (chúng tôi có thể tạm thời di chuyển đến vị trí của con trỏ bằng một cái nhìn). Điều này cũng cho phép chúng tôi lưu trữ tất cả các vị trí trong quá khứ mà chúng tôi sẽ sử dụng để kiểm tra xem chúng tôi chưa từng truy cập vị trí hiện tại trước đây.
Vì vậy, hay thực hiện ngay bây giơ. Đây là một phiên bản nhẹ hơn của regex, với các nhóm được đặt tên, thụt lề, thứ tự ít ngẫu nhiên hơn của hàng xóm và một số nhận xét:
\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
Tôi hy vọng rằng ý tưởng chung là gần như rõ ràng từ điều này. Để làm ví dụ cho cách một trong những chuyển động dọc theo đường dẫn hoạt động, chúng ta hãy nhìn vào bit di chuyển con trỏ về phía nam:
(?<=(?<pos>\k<pos>.(?>(?<-x>.)*)¶.*)\k<next>(?<x>.)*¶\D*)
Hãy nhớ rằng các giao diện nên được đọc từ phải sang trái (hoặc từ dưới lên trên), vì đó là thứ tự chúng được thực hiện theo:
(?<=
(?<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).
)
Lưu ý rằng không cần thiết phải đặt một cái neo ở phía trước \k<pos>
để đảm bảo rằng điều này thực sự đến đầu chuỗi. <pos>
luôn luôn bắt đầu với một lượng ×
không thể tìm thấy ở bất kỳ nơi nào khác, vì vậy điều này hoạt động như một mỏ neo ngầm.
Tôi không muốn viết bài này nhiều hơn mức cần thiết, vì vậy tôi sẽ không đi sâu vào 11 trường hợp khác một cách chi tiết, nhưng về nguyên tắc tất cả chúng đều hoạt động tương tự nhau. Chúng tôi kiểm tra <next>
có thể tìm thấy theo một số hướng cụ thể (được chấp nhận) từ vị trí con trỏ cũ với sự trợ giúp của các nhóm cân bằng, và sau đó chúng tôi lưu trữ chuỗi lên đến khớp với vị trí con trỏ mới <pos>
.