HexaRegex: เป็นบรรณาการให้มาร์ตินพลิก


37

มาร์ตินเอนเดอร์เพิ่งตี 100K และมีภาษาที่ยอดเยี่ยม เรากำลังจะได้สนุกไปกับหนึ่งในนั้นคือHexagony (และ regex สำหรับRetina )

ในภาพรวมโดยย่อคุณต้องเขียนโปรแกรมที่ป้อนกริด Hexagony และกำหนดว่ามีเส้นทางบนกริดนั้นที่ตรงกับสตริงข้อความหรือไม่

ฝ่ายผลิต

Hexagony สร้าง hexagons จากสตริงข้อความโดยใช้ขั้นตอนต่อไปนี้:

  1. คำนวณขนาดหกเหลี่ยมขั้นต่ำ (ใช้ความยาวของสตริงและปัดเศษขึ้นเป็นเลขฐานสิบหกที่ใกล้ที่สุด)
  2. การตัดข้อความเป็นรูปหกเหลี่ยมของขนาดด้านบน
  3. .กรอกสถานที่ที่เหลือด้วย

ตัวอย่างเช่นสตริงข้อความabcdefghijklmต้องมีรูปหกเหลี่ยมด้านยาว 3 จึงกลายเป็น:

   a b c
  d e f g
 h i j k l
  m . . .
   . . .

สังเกตว่ามี 6 ทิศทางที่เป็นไปได้ที่คุณสามารถเดินทางเป็นรูปหกเหลี่ยมได้ ยกตัวอย่างเช่นในรูปหกเหลี่ยมข้างต้นอยู่ติดกับeabfjid

ห่อ

นอกจากนี้ใน Hexagony, hexagons wrap:

   . . . .          . a . .          . . f .          . a . .   
  a b c d e        . . b . .        . . g . .        . b . . f  
 . . . . . .      g . . c . .      . . h . . a      . c . . g . 
. . . . . . .    . h . . d . .    . . u . . b .    . d . . h . .
 f g h i j k      . i . . e .      . j . . c .      e . . i . . 
  . . . . .        . j . . f        k . . d .        . . j . .  
   . . . .          . k . .          . . e .          . k . .   

หากคุณดูตัวอย่างที่ 2 และ 4 ให้สังเกตว่าaและkอยู่ในจุดเดียวกันอย่างไรแม้ว่าคุณจะล้อมรอบในทิศทางที่แตกต่างกัน เนื่องจากข้อเท็จจริงนี้จุดเหล่านี้อยู่ติดกับตำแหน่งอื่น 5 แห่งเท่านั้น

วิธีทำให้ชัดเจนยิ่งขึ้น:

   a b c d
  e f g h i
 j k l m n o
p q r s t u v
 w x y z A B
  C D E F G
   H I J K
  1. ขอบตัดกับเพื่อนบ้านของพวกเขา ( b->IและG->j)
  2. ด้านบน / มุมด้านล่างห่อจะตรงข้ามศูนย์มุมและขึ้น / ลง ( d->K,pและH->a,v)
  3. มุมกึ่งกลางพันทั้งมุมบนและล่าง ( v->a,H)

เส้นทาง

เส้นทางที่จะเป็นลำดับของสถานที่อยู่ติดกันโดยไม่ต้องกลับไปที่สถานที่เดียวกัน

   a b c
  d e f g
 h i f k l
  m . . .
   . . .

ในรูปหกเหลี่ยมด้านบนaefkgmเป็นเส้นทางที่ถูกต้อง อย่างไรก็ตามabfdไม่ใช่เส้นทางที่ถูกต้อง ( fและdไม่ติดกัน) และabeaไม่ถูกต้อง (กลับไปที่aตำแหน่ง)

เราสามารถใช้เส้นทางเหล่านี้เพื่อให้ตรงกับข้อความ (เช่น regex) อักขระตัวเลขและตัวอักษรจับคู่ตัวเอง (และตัวเองเท่านั้น) และ.ตรงกับตัวละครใด ๆ ยกตัวอย่างเช่นเส้นทางที่aej..lgmจะตรงกับaej..lgm, aejAAlgm, หรือaeja.lgmaej^%gm

Input / Output

โปรแกรมของคุณควรใช้สองสาย (ในลำดับใดก็ได้) [a-zA-Z0-9]สายแรกจะว่างและประกอบด้วยตัวอักษรและตัวเลขเท่านั้น สิ่งนี้จะแสดงถึงรูปหกเหลี่ยมที่คุณกำลังใช้งาน สตริงที่สองจะประกอบด้วยอักขระที่พิมพ์ได้

คุณจำเป็นต้องส่งกลับค่าความจริงถ้ามีเส้นทางในรูปหกเหลี่ยมที่ตรงกับสตริงข้อความที่กำหนดมิฉะนั้นเป็นค่าเท็จ

กรณีทดสอบ

Truthy:

"a","a"
"ab","a"
"ab","b"
"ab","ba"
"ab","aba"
"ab","&"
"ab","#7.J!"
"ab","aaaaaa"
"ab","bgjneta"
"ab","cebtmaa"
"abcdefg","dfabcg"
"AbCDeFG","GCbAeFD"
"aaaabbb","aaababb"
"abcdefghijklmnopqrs","alq"
"abcdefghijklmnopqrs","aqnmiedh"
"abcdefghijklmnopqrs","adhcgkorbefjimnqlps"
"11122233344455","12341345123245"
"abcdefgh","h%a"
"abcdefghijklm","a)(@#.*b"
"abcdefghijklm","a)(@#.*i"
"abcdefghij","ja"
"abcdefghijklmno","kgfeia"
"abcdefghijklmno","mmmmmiea"
"abcdefghijklmno","mmmmmlae"
"abcdefghijklmno","ja"
"abcdefghijklmnopqrs","eijfbadhmnokgcsrql"

Falsy:

"a","b"
"a","%"
"a","."
"a","aa"
"a","a."
"ab","#7.J!*"
"ab","aaaaaaa"
"ab","aaaabaaa"
"ab","123456"
"abcdefg","bfgedac"
"abcdefg","gecafdb"
"abcdefg","GCbaeFD"
"aaaabbb","aaaaabb"
"abcdefghijklmnopqrs","aqrcgf"
"abcdefghijklmnopqrs","adhlcgknbeifjm"
"abcdefghijklmnopqrs","ja"
"abcdefghijklm","a)(@#.*&"
"abcdefghijklmno","a)(@bfeijk"
"abcdefghijklmno","kgfeic"
"abcdefghijklmno","mmmmmmiea"

นี่คือเพื่อให้คำตอบของคุณสั้นที่สุดในภาษาที่คุณชื่นชอบ


21
ใครบางคนควรทำสิ่งนี้ใน Hexagony : D
DJMcMayhem

2
ที่เกี่ยวข้อง: codegolf.stackexchange.com/q/66708/29750
NinjaBearMonkey

9
ตอนแรกฉันสับสนมากกับตัวอย่างที่เป็นความจริงจนกระทั่งฉันรู้ว่ารูปหกเหลี่ยมเป็นแหล่งกำเนิดของ regexดังนั้นพูดไม่ใช่สายที่สอง ซึ่งยังคงเป็นใจ ... : P
El'endia Starman

5
ฉัน @DrGreenEggsandIronMan จะนำเสนอเงินรางวัล 500 ตัวแทนถ้ามีคนไม่ทำเช่นนี้ใน Hexagony
AdmBorkBork

2
@Blue ตัวอย่างของรูปหกเหลี่ยมที่ไม่สำเร็จเป็นสิ่งสำคัญ ที่สำคัญฉันได้แยกความแตกต่างระหว่าง "เส้นทาง" และ "regex"
Nathan Merrill

คำตอบ:


14

เรติน่า 744 ไบต์

ขออภัยในครั้งนี้ไม่มี Hexagony ...

จำนวนไบต์ถือว่าการเข้ารหัส 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

คาดว่าสตริงเป้าหมายในบรรทัดแรกและรูปหกเหลี่ยมบนบรรทัดที่สองของอินพุต พิมพ์0หรือ1ตาม

ลองออนไลน์! (บรรทัดแรกเปิดใช้งานชุดการทดสอบโดยที่แต่ละบรรทัดเป็นกรณีทดสอบโดยใช้¦สำหรับการแยกแทนการป้อนบรรทัด)

วิธีที่เหมาะสมในการแก้ปัญหานี้คือการลงทะเบียนแน่นอน ;) และถ้าไม่ใช่เพราะความจริงที่ว่าความท้าทายนี้เกี่ยวข้องกับขั้นตอนการตีแผ่ของรูปหกเหลี่ยมคำตอบนี้จะประกอบด้วยอะไรเลยนอกจาก regex ยาว 600 ~ ไบต์เดียว

นี่ยังไม่เหมาะกับการเล่นกอล์ฟ แต่ฉันมีความสุขมากกับผลลัพธ์ (เวอร์ชันการทำงานแรกของฉันหลังจากลบกลุ่มที่มีชื่อและสิ่งอื่น ๆ ที่จำเป็นสำหรับการมีสติอยู่ประมาณ 1,000 ไบต์) ฉันคิดว่าฉันสามารถประหยัดได้ประมาณ 10 ไบต์โดยการเปลี่ยนลำดับของสตริงและรูปหกเหลี่ยม แต่จะต้องมีการเขียน regex ที่สมบูรณ์ในตอนท้ายซึ่งฉันไม่รู้สึกถึงตอนนี้ นอกจากนี้ยังมีการประหยัดแบบ 2 ไบต์ด้วยการเว้นGระยะ แต่มันทำให้การแก้ปัญหาช้าลงอย่างมากดังนั้นฉันจะรอจนกว่าจะมีการเปลี่ยนแปลงจนกว่าฉันจะแน่ใจว่าฉันเล่นกอล์ฟนี้ได้ดีเท่าที่จะทำได้

คำอธิบาย

ส่วนหลักของการแก้ปัญหานี้ใช้ประโยชน์จากกลุ่มที่มีความสมดุลดังนั้นฉันขอแนะนำให้อ่านพวกเขาหากคุณต้องการที่จะเข้าใจวิธีการทำงานในรายละเอียด (ฉันจะไม่โทษคุณถ้าคุณไม่ ... )

ส่วนแรกของการแก้ปัญหา (เช่นทุกอย่างยกเว้นสองบรรทัดสุดท้าย) เป็นรุ่นที่ปรับเปลี่ยนคำตอบของฉันแฉรหัสที่มา Hexagony มันสร้างรูปหกเหลี่ยมในขณะที่ปล่อยสตริงเป้าหมายที่ไม่ถูกแตะต้อง (และมันสร้างรูปหกเหลี่ยมก่อนที่จะเป็นสตริงเป้าหมาย) ฉันได้ทำการเปลี่ยนแปลงรหัสก่อนหน้าเพื่อบันทึกไบต์:

  • อักขระพื้นหลัง×แทนการเว้นวรรคเพื่อไม่ให้ขัดแย้งกับช่องว่างที่อาจเกิดขึ้นในอินพุต
  • อักขระ no-op / wildcard _แทน.เพื่อให้เซลล์กริดสามารถระบุได้อย่างน่าเชื่อถือว่าเป็นอักขระคำ
  • ฉันไม่ได้ใส่ช่องว่างหรือการเยื้องหลังจากสร้างรูปหกเหลี่ยมเป็นครั้งแรก นั่นทำให้ฉันกลายเป็นรูปหกเหลี่ยมที่เป๋ แต่จริง ๆ แล้วมันสะดวกกว่ามากที่จะทำงานด้วยและกฎการประชดประชันนั้นค่อนข้างง่าย

นี่คือตัวอย่าง สำหรับกรณีทดสอบต่อไปนี้:

ja
abcdefghij

เราได้รับ:

××abc
×defg
hij__
____×
___××
ja

เปรียบเทียบสิ่งนี้กับรูปแบบปกติของรูปหกเหลี่ยม:

  a b c
 d e f g
h i j _ _
 _ _ _ _
  _ _ _

เราจะเห็นได้ว่าเพื่อนบ้านเป็นเพื่อนบ้านของมัวร์ 8 คนยกเว้นเพื่อนบ้านทางตะวันตกเฉียงเหนือและตะวันออกเฉียงใต้ ดังนั้นเราจำเป็นต้องตรวจสอบคำคุณศัพท์แนวนอนแนวตั้งและทิศตะวันตกเฉียงใต้ / ตะวันออกเฉียงเหนือ (จากนั้นก็มีขอบห่อ) การใช้เลย์เอาต์ที่กระชับยิ่งขึ้นนี้ยังมีโบนัสที่เราจะสามารถใช้××ในตอนท้ายเพื่อกำหนดขนาดของรูปหกเหลี่ยมได้ทันทีเมื่อเราต้องการ

หลังจากสร้างแบบฟอร์มนี้แล้วเราจะทำการเปลี่ยนแปลงสตริงทั้งหมดอีกครั้ง:

T`d`À-É

สิ่งนี้จะแทนที่ตัวเลขด้วยตัวอักษร ASCII แบบขยาย

ÀÁÂÃÄÅÆÇÈÉ

เนื่องจากมีการแทนที่ทั้งในรูปหกเหลี่ยมและในสตริงเป้าหมายจึงไม่มีผลกับว่าสตริงนั้นตรงกันหรือไม่ นอกจากนี้เนื่องจากเป็นตัวอักษร\wและ\bยังระบุว่าเป็นเซลล์หกเหลี่ยม ประโยชน์ของการทำทดแทนนี้คือตอนนี้เราสามารถใช้\Dใน regex ที่จะเกิดขึ้นเพื่อจับคู่อักขระใด ๆ (โดยเฉพาะอย่างยิ่ง linefeeds และตัวละครที่ไม่ใช่ linefeed) เราไม่สามารถใช้sตัวเลือกในการทำเช่นนั้นได้เนื่องจากเราจะต้อง.จับคู่อักขระที่ไม่ใช่บรรทัดป้อนในหลาย ๆ ที่

ตอนนี้บิตสุดท้าย: กำหนดว่าเส้นทางใด ๆ ที่ตรงกับสตริงที่เรากำหนดหรือไม่ สิ่งนี้ทำด้วย regex อันมหึมา คุณอาจถามตัวเองว่าทำไม?! นี่เป็นปัญหาพื้นฐานของการย้อนรอย: คุณเริ่มต้นที่ไหนสักแห่งแล้วลองใช้เส้นทางตราบเท่าที่มันจับคู่กับสตริงและเมื่อคุณไม่ย้อนกลับและลองเพื่อนบ้านที่แตกต่างจากตัวละครตัวสุดท้ายที่ทำงาน สิ่งหนึ่งที่คุณได้รับฟรีเมื่อทำงานกับ regex คือการย้อนรอย นั่นคือสิ่งเดียวที่เอ็นจิ้นของ regex ทำ ดังนั้นหากเราเพียงแค่หาวิธีที่จะอธิบายเส้นทางที่ถูกต้อง (ซึ่งเป็นเรื่องยากพอสำหรับปัญหาแบบนี้ แต่เป็นไปได้อย่างแน่นอนกับกลุ่มสมดุล) แล้วเครื่องยนต์ regex จะเรียงลำดับการค้นหาเส้นทางนั้นในเส้นทางที่เป็นไปได้ทั้งหมดสำหรับเรา แน่นอนว่าจะเป็นไปได้ที่จะใช้การค้นหาด้วยตนเองด้วยหลายขั้นตอน ( และฉันเคยทำมาแล้วในอดีต ) แต่ฉันสงสัยว่ามันจะสั้นลงในกรณีนี้โดยเฉพาะ

ปัญหาหนึ่งของการใช้สิ่งนี้กับ regex คือเราไม่สามารถสร้างเคอร์เซอร์ของ regex ไปมาผ่านสตริงในระหว่าง backtracking (ซึ่งเราต้องการเนื่องจากเส้นทางอาจขึ้นหรือลง) ดังนั้นเราจึงติดตาม "เคอร์เซอร์" ของเราเองในกลุ่มการจับภาพและอัปเดตนั้นในทุกขั้นตอน (เราสามารถย้ายไปยังตำแหน่งของเคอร์เซอร์ชั่วคราวด้วยการค้นหา) นอกจากนี้ยังช่วยให้เราสามารถจัดเก็บตำแหน่งที่ผ่านมาทั้งหมดซึ่งเราจะใช้เพื่อตรวจสอบว่าเราไม่ได้เยี่ยมชมตำแหน่งปัจจุบันก่อนหน้านี้

งั้นลองดูกัน นี่คือ regex รุ่น saner เล็กน้อยโดยมีกลุ่มชื่อการเยื้องการเรียงลำดับแบบสุ่มน้อยกว่าของเพื่อนบ้านและความคิดเห็น:

\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

ฉันหวังว่าความคิดทั่วไปนั้นชัดเจนจากเรื่องนี้ ตัวอย่างการเคลื่อนไหวของหนึ่งในเส้นทางนั้นมาดูที่บิตที่เลื่อนเคอร์เซอร์ไปทางทิศใต้:

(?<=(?<pos>\k<pos>.(?>(?<-x>.)*)¶.*)\k<next>(?<x>.)*¶\D*)

โปรดจำไว้ว่า Lookbehinds ควรอ่านจากขวาไปซ้าย (หรือจากล่างขึ้นบน) เนื่องจากเป็นลำดับที่ถูกเรียกใช้งานใน:

(?<=
  (?<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>เพื่อให้แน่ใจว่านี่จะถึงจุดเริ่มต้นของสตริง <pos>เริ่มต้นด้วยจำนวน×ที่ไม่สามารถหาได้จากที่อื่นเสมอดังนั้นสิ่งนี้จึงทำหน้าที่เป็นจุดยึดโดยนัยอยู่แล้ว

ฉันไม่ต้องการที่จะขยายโพสต์นี้เกินความจำเป็นดังนั้นฉันจะไม่ไปลงรายละเอียดอีก 11 ราย แต่ในหลักการพวกเขาทั้งหมดทำงานในทำนองเดียวกัน เราตรวจสอบว่า<next>สามารถพบได้ในเฉพาะ (ยอม) <pos>ทิศทางจากตำแหน่งของเคอร์เซอร์เก่าด้วยความช่วยเหลือของกลุ่มสมดุลบางส่วนและจากนั้นเราจะจัดเก็บสตริงขึ้นจะมีการแข่งขันที่เป็นตำแหน่งของเคอร์เซอร์ใหม่


13

Python 3, 990 943 770 709 ไบต์

คำตอบแรกใช่!

แก้ไข: ทำรายการคำคุณศัพท์ Golfed ตอนนี้ฉันใช้สูตรที่แตกต่างกันเล็กน้อย

แก้ไข 2: ลบขนที่ไม่จำเป็นออกไปเล่นกอล์ฟมากขึ้น

แก้ไข 3: ย่อโค้ดสำหรับการแปลงจากดัชนีในรายการเป็นพิกัดทำให้มีข้อมูลเพิ่มขึ้นเล็กน้อย

ไบต์ส่วนใหญ่เกี่ยวข้องกับการสร้างรายการ adjacency (มีศักยภาพมากที่สุดที่จะเล่นกอล์ฟ) จากนั้นมันเป็นเรื่องง่าย ๆ ที่จะบังคับใช้วิธีแก้ปัญหาอย่างเดียวดาย

แข็งแรงเล่นกอล์ฟ:

from math import*
b=abs
c=max
e=range
f=len
A=input()
B=input()
C=ceil(sqrt((f(A)-.25)/3)+.5)
D=3*C*~-C+1
E=2*C-1
F=C-1
A+='.'*(D-f(A))
G=[set()for x in e(D)]
I=lambda H:sum(E+.5-b(t-F+.5)for t in e(int(H+F)))
for x in e(D):
 r=sum([[J-F]*(E-b(J-F))for J in e(E)],[])[x];q=x-I(r);s=-q-r;a=lambda q,r:G[x].add(int(q+I(r)));m=c(map(b,[q,r,s]))
 if m==F:
  if q in(m,-m):a(-q,-s)
  if r in(m,-m):a(-s,-r)
  if s in(m,-m):a(-r,-q)
 for K,L in zip([1,0,-1,-1,0,1],[0,1,1,0,-1,-1]):
  M,H=q+K,r+L
  if c(map(b,[M,H,-M-H]))<C:a(M,H)
def N(i,O,P):
 Q=O and O[0]==A[i]or'.'==A[i];R=0
 if(2>f(O))*Q:R=1
 elif Q:R=c([(x not in P)*N(x,O[1:],P+[i])for x in G[i]]+[0])
 return R
print(c([N(x,B,[])for x in e(D)])*(f(B)<=D))

Ungolfed w / คำอธิบาย:

from math import*

#Rundown of the formula:
# * Get data about the size of the hexagon
# * Create lookup tables for index <-> coordinate conversion
#   * q=0, r=0 is the center of the hexagon
#   * I chose to measure in a mix of cubic and axial coordinates,
#     as that allows for easy oob checks and easy retrevial  
# * Create the adjacency list using the lookup tables, while
#   checking for wrapping
# * Brute-force check if a path in the hexagon matches the
#   expression

# shorten functions used a lot
b=abs
c=max
e=range

# Get input

prog=input()
expr=input()

# sdln = Side length
# hxln = Closest hexagonal number
# nmrw = Number of rows in the hexagon
# usdl = one less than the side length. I use it a lot later

sdln=ceil(sqrt((len(prog)-.25)/3)+.5)
hxln=3*sdln*~-sdln+1
nmrw=2*sdln-1
usdl=sdln-1

# Pad prog with dots

prog+='.'*(hxln-len(prog))

# nmbf = Number of elements before in each row
# in2q = index to collum
# in2r = index to row

nmbf=[0]*nmrw
in2q=[0]*hxln
in2r=[0]*hxln

#  4    5
#   \  /
# 3 -- -- 0
#   /  \ 
#  2    1

# dirs contains the q,r and s values needed to move a point
# in the direction refrenced by the index

qdir=[1,0,-1,-1,0,1]
rdir=[0,1,1,0,-1,-1]

# generate nmbf using a summation formula I made

for r in e(nmrw-1):
    nmbf[r+1]=int(nmbf[r]+nmrw+.5-b(r-sdln+1.5))

# generate in2q and in2r using more formulas
# cntr = running counter

cntr=0
for r in e(nmrw):
    bgnq=c(-r,1-sdln)
    for q in e(nmrw-b(r-sdln+1)):
        in2q[cntr]=bgnq+q
        in2r[cntr]=r-usdl
        cntr+=1

# adjn = Adjacency sets

adjn=[set()for x in e(hxln)]

# Generate adjacency sets

for x in e(hxln):
    #Get the q,r,s coords
    q,r=in2q[x],in2r[x]
    s=-q-r
    # a = function to add q,r to the adjacency list
    a=lambda q,r:adjn[x].add(q+nmbf[r+usdl])
    # m = absolute value distance away from the center
    m=c(map(b,[q,r,s]))
    # if we are on the edge (includes corners)...
    if m==usdl:
        # add the only other point it wraps to
        if q in(m,-m):
            a(-q,-s)
        if r in(m,-m):
            a(-s,-r)
        if s in(m,-m):
            a(-r,-q)
    # for all the directions...
    for d in e(6):
        # tmp{q,r,s} = moving in direction d from q,r,s
        tmpq,tmpr=q+qdir[d],r+rdir[d]
        # if the point we moved to is in bounds...
        if c(map(b,[tmpq,tmpr,-tmpq-tmpr]))<sdln:
            # add it
            a(tmpq,tmpr)

# Recursive path checking function
def mtch(i,mtst,past):
    # dmch = Does the place we are on in the hexagon match
    #        the place we are in the expression?
    # out = the value to return
    dmch=mtst and mtst[0]==prog[i]or'.'==prog[i]
    out=0
    # if we are at the end, and it matches...
    if(2>len(mtst))*dmch:
        out=1
    # otherwise...
    elif dmch:
        # Recur in all directions that we haven't visited yet
        # replace '*' with 'and' to speed up the recursion
        out=c([(x not in past)*mtch(x,mtst[1:],past+[i])for x in adjn[i]]+[0])
    return out

# Start function at all the locations in the hexagon
# Automatically return false if the expression is longer
# than the entire hexagon
print(c([mtch(x,expr,[])for x in e(hxln)])*(len(expr)<=hxln))

ดังนั้นใกล้กับ Retina! :(เย้ยเอาชนะเรตินา!


5

Javascript (ES6), 511 500 496 ไบต์

(H,N)=>{C=(x,y)=>(c[x]=c[x]||[])[y]=y;S=d=>(C(x,y=x+d),C(y,x),C(s-x,s-y),C(s-y,s-x));r=(x,p,v)=>{p<N.length?(v[x]=1,c[x].map(n=>!v[n]&&(H[n]==N[p]||H[n]=='.')&&r(n,p+1,v.slice()))):K=1};for(e=x=K=0;(s=3*e*++e)<(l=H.length)-1;);H+='.'.repeat(s+1-l);for(a=[],b=[],c=[[]],w=e;w<e*2;){a[w-e]=x;b[e*2-w-1]=s-x;for(p=w;p--;x++){w-e||S(s-e+1);w<e*2-1&&(S(w),S(w+1));p&&S(1)}a[w]=x-1;b[e*3-++w]=s-x+1}a.map((v,i)=>S(b[i]-(x=v)));[N[0],'.'].map(y=>{for(x=-1;(x=H.indexOf(y,x+1))>-1;r(x,1,[]));});return K}

Ungolfed และแสดงความคิดเห็น

// Entry point
//   H = haystack (the string the hexagon is filled with)
//   N = needle (the substring we're looking for)
(H, N) => {
  // C(x, y) - Helper function to save a connection between two locations.
  //   x = source location
  //   y = target location
  C = (x, y) => (c[x] = c[x] || [])[y] = y;

  // S(d) - Helper function to save reciprocal connections between two locations
  //        and their symmetric counterparts.
  //   d = distance between source location (x) and target location
  S = d => (C(x, y = x + d), C(y, x), C(s - x, s - y), C(s - y, s - x));

  // r(x, p, v) - Recursive path search.
  //   x = current location in hexagon
  //   p = current position in needle
  //   v = array of visited locations
  r = (x, p, v) => {
    p < N.length ?
      (v[x] = 1, c[x].map(n => !v[n] && (H[n] == N[p] || H[n] == '.') &&
      r(n, p + 1, v.slice())))
    :
      K = 1
  };

  // Compute e = the minimum required edge width of the hexagon to store the haystack.
  // Also initialize:
  //   x = current location in hexagon
  //   l = length of haystack
  //   s = size of hexagon (number of locations - 1)
  //   K = fail/success flag
  for(e = x = K = 0; (s = 3 * e * ++e) < (l = H.length) - 1;);

  // Pad haystack with '.'
  H += '.'.repeat(s + 1 - l);

  // Build connections c[] between locations, using:
  //   x = current location
  //   w = width of current row
  //   p = position in current row
  // Also initialize:
  //   a[] = list of locations on top left and top right edges
  //   b[] = list of locations on bottom left and bottom right edges
  for(a = [], b = [], c = [[]], w = e; w < e * 2;) {
    a[w - e] = x;
    b[e * 2 - w - 1] = s - x;

    for(p = w; p--; x++) {
      // connection between top and bottom edges
      w - e || S(s - e + 1);
      // connections between current location and locations below it
      w < e * 2 - 1 && (S(w), S(w + 1));
      // connection between current location and next location
      p && S(1)
    }
    a[w] = x - 1;
    b[e * 3 - ++w] = s - x + 1
  }

  // Save connections between top left/right edges and bottom left/right edges.
  a.map((v, i) => S(b[i] - (x = v)));

  // Look for either the first character of the needle or a '.' in the haystack,
  // and use it as the starting point for the recursive search. All candidate
  // locations are tried out.
  [N[0], '.'].map(y => {
    for(x = -1; (x = H.indexOf(y, x + 1)) > -1; r(x, 1, []));
  });

  // Return fail/success flag.
  return K
}

กรณีทดสอบ

ตัวอย่างด้านล่างนี้จะดำเนินการในทุกกรณีทดสอบที่เป็นความจริงและเป็นเท็จ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.