ไม่สามารถอ่านได้ , 2199 2145 2134 2104 2087 2084 ไบต์
รองรับทั้งk
/ j
เช่นเดียวกับ▲
/ ▼
ไวยากรณ์
ในประเพณีที่อ่านไม่ได้ดีนี่คือโปรแกรมที่จัดรูปแบบเป็นฟอนต์ตามสัดส่วนเพื่อทำให้งงงวยความแตกต่างระหว่างอะพอสโทรฟีและเครื่องหมายคำพูดคู่:
' '' '' ''' "" "" "" "" " '' ' ' '''' ' ' ''''" '' "" '' ''" ' '' ''"' "" ' ''' "" '"' "" " ' '''" "" "" " ' '' '' '''" "" "" """ " '' ' ' '''' " ' '' '' '' ' ' '' '' '' '''' '''" "" "" "" "" " ' '' '''" "" "" '' ''' '' ''' "" "" ' '' '' ''' "" ' ''' "" ' ''' " ' '' ' '"'' ''' "" "" "" ' ''' "" ' ''' " ' ''"' '' '' '' '' "" '' '' ' ' '' '' '''' ' ' ''''"" " " '' ' ' '' '' '' '''' '' '' ''"" " ' '''" " '"'" "" ' '' ''' "" """" ' '''" " ' '''" " '"'" "" ' '' '' '' ''"' '' '' '' ' ' '''' ' ' '''' " '"' "" " ' '' '' '' '''" "" "" "" ' ''' "" ' ''' "" '"' "" " ' '''"' "" " ' '' ''"'" "" ' '' '' '' ''"' '' '' '' " ' '' ' ' '''' '' '' '''"" " ' '' '' '' '''" "" "" "" " ' '' '' '' '''" "" "" "" " ' '' '' '' '''" "" ' '' '' '' '' ' ' '''' '' '' ''' "" "" '' '' '' '' ' ' '''' ' '"'' '' "" "" " '' ' ' '' ' ' '''' ' '' ' '"'' '' "' '' '' '' '''' '' '' ''' ''' "" ' ''' "" '"' """ " '' ' ' '''' ' '' ' ' '' ''"'' '' "' '' '' "" "" ' '' '' '' "' '' "" '' '' '' '' ' ' ''''" ' '' '' '' "' '' "" '' ''" "" "" ' ''' "" ' ''' "" ' '' '' '' "' '' "" '' ' ' ''''" '' ' ' '''' "" ' '' '' '' '' ' ' '' '' '' ' ' '''' ' ' '''' ' '"'' '''' ' '"'' '''" "" ' '' '' ' ' '' '' '''' ' ' '''' ' '"'' ''' "" "" "" "" ' '' '' ''"' " " ' '''" " '"'" "" ' '' ''' "" "" ' '' '' '' ''"' '' '' '' ' ' '''' "'"' "" " ' '' '' '''" " ' '''" " '' ' ' '''' '' '' '' "" " ' '''" "" '' " '' ' ' '' '' '''' ' '"'' '' "" "" '' '' '' ' ' '''' ' '' "' '' '' '' '"' "" " ' '' '' '' '''" "" "" "" '"' "" " ' '' '' ' ' '' ''"'' '''"" "" "" "" ' '' '' '' "' '' ' ' '' '''' '' ' ' '' '' '''' ' ' ''''" '' " ' '' '' '' ''' "" "" "" " ' '''" " '"'" "" ' '' '' '' ''"' '' '' '' """ " '"'" "" ' '' '' ''' "" '"' "" " ' '' '' '' '''" "" "" "" ' ''' """ ''" '' ' ' '' '' '''' ' '' ' ' '''' '' '' '' "'" '' ' ' '' '' '''' " ' '' '' '' ' ' '' '' '' '''' ''' "" ' '' '' '' ' '"'' ''' "" """ " '' '' "" "" ' '' '' ''' "" ' ''' " ' '' ' ' '''' '' '' ''' "" ' '''" " ''"' '' ''' "" "" "" '"' "" " ' '' '' '' '''" "" "" "" '"' "" "" '' '' '' ' ''' " ' '' ' ' '''' '' '' ''' "" '"' "" "" '' '' ' ' '' '' '' '''' '' "" " '"'" "" ' ''' "" "" "" " '"'" "" ' '' ''' "" "" "" '' " '"'"" "" "" " ' '' '' '' '''" "" ' '' '' ' ' '' '' '' '''' '' '' '' "' '' ''" " '' ' ' '' '' '' '''' '' '' ''" ' '' '' '' "' '' ' ' '' '' '''' '' '' '' ' '' ' ' '''' '' '' ''' "" "" "" "" ''"" '' '' ' ' '''' " ' '' ''"'"" '"' "" " ' '' '' '' '''" "" "" "" ' '''" ' '' ' ' '' '''' '' "' ''" " ' '' '' '''" " '' ' ' '''' ' ' '''' ' ' '''' '' '' ' '' ' ' ''''"' '' '"' """ " '' '' ' ' '' '' '' '''' '' '' '' ' ' '' '' '' '''' '' '' ''" ' '' '' '' '''" "" "" " ' '' '' '' '''" "" ' '' '' '' ''' "" "" "" "" "" "" "" "" ' '' '' '' ''' "" """" '' '' '' '' ' ' '' '' '''' ' ' '' '' '''' "" ' ''' "" ' '' '' '' ' ' '' ' ' '' '' '' '''' '' '' '''' "' '' '' ''" '' ' ' '''' ' ' '''' ' ' '''' ' ' '''' ' ' '''' ' ' '''' ' ' ''''" " '' ' ''' "" ' ''' "" ' ''' "" ' ''' "" "" "" " '"'" "" ' '' '' ''"' '' '' " " ' '' ' ' '' '' '''' '' '' ''"' '' ' ' '''' "" '' '' ''" ' ''"' "" ''"" "" " '"'" ""
นี่เป็นความท้าทายที่น่าอัศจรรย์ ขอบคุณสำหรับการโพสต์!
คำอธิบาย
เพื่อให้รู้สึกถึงสิ่งที่ไม่สามารถอ่านได้และไม่สามารถทำได้ให้จินตนาการ Brainfuck ด้วยเทปที่ไม่มีที่สิ้นสุดในทั้งสองทิศทาง แต่แทนที่จะใช้ตัวชี้หน่วยความจำเคลื่อนที่หนึ่งเซลล์ในแต่ละครั้งคุณสามารถเข้าถึงเซลล์หน่วยความจำใดก็ได้โดยการยกเลิกการชี้ สิ่งนี้มีประโยชน์มากในการแก้ปัญหานี้แม้ว่าการคำนวณทางคณิตศาสตร์อื่น ๆ รวมถึงโมดูโลจะต้องทำด้วยมือ
นี่คือโปรแกรมเป็น pseudocode พร้อมคำอธิบายของผู้อำนวยการ:
// Initialize memory pointer. Why 5 will be explained at the very end!
ptr = 5
// FIRST PASS:
// Read all characters from stdin, store them in memory, and also keep track of the
// current line number at each character.
// We need the +1 here so that EOF, which is -1, ends the loop. We increment ptr by 2
// because we use two memory cells for each input character: one contains the actual
// character (which we store here); the other will contain the line number at which the
// character occurs (updated at the end of this loop body).
while ch = (*(ptr += 2) = read) + 1:
// At this point, ch will be one more than the actual value.
// However, the most code-economical way for the following loop is to
// decrement inside the while condition. This way we get one fewer
// iteration than the value of ch. Thus, the +1 comes in handy.
// We are now going to calculate modulo 4 and 5. Why? Because
// the mod 4 and 5 values of the desired input characters are:
//
// ch %5 %4
// ^ 1
// v 2
// k 3
// j 4
// ▲ 0 2
// ▼ 0 0
//
// As you can see, %5 allows us to differentiate all of them except ▲/▼,
// so we use %4 to differentiate between those two.
mod4 = 0 // read Update 2 to find out why mod5 = 0 is missing
while --ch:
mod5 = mod5 ? mod5 + 1 : -4
mod4 = mod4 ? mod4 + 1 : -3
// At the end of this loop, the value of mod5 is ch % 5, except that it
// uses negative numbers: -4 instead of 1, -3 instead of 2, etc. up to 0.
// Similarly, mod4 is ch % 4 with negative numbers.
// How many lines do we need to go up or down?
// We deliberately store a value 1 higher here, which serves two purposes.
// One, as already stated, while loops are shorter in code if the decrement
// happens inside the while condition. Secondly, the number 1 ('""") is
// much shorter than 0 ('""""""""'""").
up = (mod5 ? mod5+1 ? mod5+3 ? 1 : 3 : 2 : mod4 ? 3 : 1)
dn = (mod5 ? mod5+2 ? mod5+4 ? 1 : 3 : 2 : mod4 ? 1 : 3)
// As an aside, here’s the reason I made the modulos negative. The -1 instruction
// is much longer than the +1 instruction. In the above while loop, we only have
// two negative numbers (-3 and -4). If they were positive, then the conditions in
// the above ternaries, such as mod5+3, would have to be mod5-3 etc. instead. There
// are many more of those, so the code would be longer.
// Update the line numbers. The variables updated here are:
// curLine = current line number (initially 0)
// minLine = smallest linenum so far, relative to curLine (always non-positive)
// maxLine = highest linenum so far, relative to curLine (always non-negative)
// This way, we will know the vertical extent of our foray at the end.
while --up:
curLine--
minLine ? minLine++ : no-op
maxLine++
while --dn:
curLine++
minLine--
maxLine ? maxLine-- : no-op
// Store the current line number in memory, but +1 (for a later while loop)
*(ptr + 1) = curLine + 1
// At the end of this, minLine and maxLine are still relative to curLine.
// The real minimum line number is curLine + minLine.
// The real maximum line number is curLine + maxLine.
// The total number of lines to output is maxLine - minLine.
// Calculate the number of lines (into maxLine) and the real minimum
// line number (into curLine) in a single loop. Note that maxLine is
// now off by 1 because it started at 0 and thus the very line in which
// everything began was never counted.
while (++minLine) - 1:
curLine--
maxLine++
// Make all the row numbers in memory positive by adding curLine to all of them.
while (++curLine) - 1:
ptr2 = ptr + 1
while (ptr2 -= 2) - 2: // Why -2? Read until end!
*ptr2++
// Finally, output line by line. At each line, we go through the memory, output the
// characters whose the line number is 0, and decrement that line number. This way,
// characters “come into view” in each line by passing across the line number 0.
while (--maxLine) + 2: // +2 because maxLine is off by 1
ptr3 = 5
while (ptr -= 2) - 5:
print (*((ptr3 += 2) + 1) = *(ptr3 + 1) - 1) ? 32 : *ptr3 // 32 = space
ptr = ptr3 + 2
print 10 // newline
มากสำหรับตรรกะของโปรแกรม ตอนนี้เราต้องแปลสิ่งนี้เป็น Unreadableและใช้เทคนิคการตีกอล์ฟที่น่าสนใจ
ตัวแปรจะถูกอ้างอิงเป็นตัวเลขใน Unreadable เสมอ (เช่นa = 1
กลายเป็นสิ่งที่คล้ายกัน*(1) = 1
) ตัวอักษรตัวเลขบางตัวมีความยาวมากกว่าตัวอักษรอื่น ที่สั้นที่สุดคือ 1, ตามด้วย 2, และอื่น ๆ เพื่อแสดงว่าจำนวนลบเป็นเท่าใดอีกต่อไปนี่คือตัวเลขตั้งแต่ -1 ถึง 7:
-1 '""""""""'""""""""'""" 22
0 '""""""""'""" 13
1 '""" 4
2 '""'""" 7
3 '""'""'""" 10
4 '""'""'""'""" 13
5 '""'""'""'""'""" 16
6 '""'""'""'""'""'""" 19
7 '""'""'""'""'""'""'""" 22
เห็นได้ชัดว่าเราต้องการจัดสรรตัวแปร # 1 ให้กับตัวแปรที่เกิดขึ้นบ่อยที่สุดในรหัส ในขณะแรกที่วนซ้ำนี่จะเป็นmod5
10 เท่า แต่เราไม่ต้องการmod5
อีกต่อไปหลังจากครั้งแรกในขณะที่วนรอบดังนั้นเราสามารถจัดสรรตำแหน่งหน่วยความจำเดียวกันให้กับตัวแปรอื่น ๆ ที่เราใช้ในภายหลัง เหล่านี้เป็นและptr2
ptr3
ตอนนี้ตัวแปรอ้างอิงไปแล้ว 21 ครั้ง (หากคุณกำลังพยายามนับจำนวนครั้งที่เกิดขึ้นด้วยตัวเองอย่าลืมนับบางสิ่งเช่นa++
สองครั้งหนึ่งครั้งเพื่อรับค่าและอีกครั้งสำหรับการตั้งค่า)
มีอีกหนึ่งตัวแปรที่เราสามารถใช้ซ้ำได้ หลังจากที่เราคำนวณค่าโมดูโลแล้วch
ไม่จำเป็นอีกต่อไป up
และdn
มีจำนวนเท่ากันดังนั้นก็ดี ผสาน Let 's ด้วยch
up
ทำให้มีตัวแปรเฉพาะทั้งหมด 8 ตัว เราสามารถจัดสรรตัวแปร 0 ถึง 7 แล้วเริ่มบล็อกหน่วยความจำ (ที่มีตัวอักษรและหมายเลขบรรทัด) ที่ 8 แต่! เนื่องจาก 7 มีความยาวเท่ากันในรหัส −1 เราสามารถใช้ตัวแปร −1 ถึง 6 และเริ่มบล็อกหน่วยความจำที่ 7 ด้วยวิธีนี้ทุกการอ้างอิงถึงตำแหน่งเริ่มต้นของบล็อกหน่วยความจำจะสั้นลงเล็กน้อยในรหัส! สิ่งนี้ทำให้เรามีงานที่ได้รับมอบหมายต่อไปนี้:
-1 dn
0 ← ptr or minLine?
1 mod5, ptr2, ptr3
2 curLine
3 maxLine
4 ← ptr or minLine?
5 ch, up
6 mod4
7... [data block]
ทีนี้สิ่งนี้จะอธิบายการเริ่มต้นที่ด้านบนสุด:มันคือ 5 เพราะมันคือ 7 (จุดเริ่มต้นของบล็อกหน่วยความจำ) ลบ 2 (การเพิ่มภาระผูกพันในขณะที่เงื่อนไขแรก) สิ่งเดียวกันจะเกิดขึ้นอีกสองเหตุการณ์ที่เกิดขึ้น 5 ครั้งในลูปสุดท้าย
โปรดทราบว่าตั้งแต่ 0 และ 4 มีความยาวเท่ากันในรหัสptr
และminLine
สามารถจัดสรรได้ทั้งสองทาง ... หรือพวกเขาจะ?
สิ่งที่เกี่ยวกับ 2 ลึกลับในวินาทีสุดท้ายขณะที่วนรอบ? นี่ไม่ควรเป็น 6 หรือ เราต้องการลดจำนวนลงในบล็อคข้อมูลใช่ไหม เมื่อเราไปถึง 6 เราจะอยู่นอกบล็อคข้อมูลและเราควรหยุด! มันจะเป็นข้อผิดพลาดบัฟเฟอร์ล้นข้อผิดพลาดความปลอดภัยช่องโหว่!
ลองคิดดูว่าจะเกิดอะไรขึ้นถ้าเราไม่หยุด เราพร่องตัวแปร 6 และ 4 ตัวแปร mod4
6 ใช้เฉพาะในครั้งแรกขณะที่วนซ้ำและไม่ต้องการที่นี่อีกต่อไปจึงไม่เกิดอันตรายใด ๆ แล้วตัวแปร 4 ล่ะ? คุณคิดว่าตัวแปร 4 ptr
ควรเป็นหรือควรเป็นminLine
อย่างไร ใช่แล้วminLine
ไม่ใช้ตอนนี้อีก! ดังนั้นตัวแปร # 4 คือminLine
และเราสามารถลดมันได้อย่างปลอดภัยและไม่เกิดความเสียหาย!
อัปเดต 1! แข็งแรงเล่นกอล์ฟ 2199-2145 ไบต์โดยตระหนักว่าdn
สามารถยังถูกรวมเข้ากับmod5
แม้ว่าmod5
จะยังคงใช้ในการคำนวณค่าสำหรับdn
! การกำหนดตัวแปรใหม่อยู่ในขณะนี้:
0 ptr
1 mod5, dn, ptr2, ptr3
2 curLine
3 maxLine
4 minLine
5 ch, up
6 mod4
7... [data block]
อัปเดต 2! เริ่มจาก 2145 ถึง 2134 ไบต์โดยตระหนักว่าเนื่องจากmod5
ขณะนี้อยู่ในตัวแปรเดียวกับdn
ซึ่งนับเป็น 0 ในขณะที่ลูปmod5
ไม่จำเป็นต้องเริ่มต้นเป็น 0 อย่างชัดเจนอีกต่อไป
อัปเดต 3! เล่นกอล์ฟตั้งแต่ 2134 ถึง 2104 ไบต์โดยตระหนักถึงสองสิ่ง ครั้งแรกแม้ว่าแนวคิด "เชิงลบแบบโมดูโล" นั้นมีค่าสำหรับมันmod5
แต่การใช้เหตุผลแบบเดียวกันไม่สามารถทำได้mod4
เพราะเราไม่เคยทดสอบกับmod4+2
อื่น ๆ ดังนั้นการเปลี่ยนmod4 ? mod4+1 : -3
เป็นmod4 ? mod4-1 : 3
พาเราไปที่ 2110 ไบต์ ที่สองเนื่องจากmod4
เป็น 0 หรือ 2 เสมอเราสามารถกำหนดค่าเริ่มต้นmod4
เป็น 2 แทน 0 และย้อนกลับทั้งสามส่วน ( mod4 ? 3 : 1
แทนmod4 ? 1 : 3
)
อัพเดท 4! มีขนาดตั้งแต่ 2104 ถึง 2087 ไบต์โดยตระหนักว่าขณะที่ลูปที่คำนวณค่าโมดูโล่ทำงานอย่างน้อยหนึ่งครั้งและในกรณีเช่นนี้ Unreadable ช่วยให้คุณสามารถใช้ค่าของข้อความสั่งสุดท้ายในนิพจน์อื่นได้อีกครั้ง ดังนั้นแทนที่จะเป็นwhile --ch: [...]; up = (mod5 ? mod5+1 ? [...]
ตอนนี้เรามีup = ((while --ch: [...]) ? mod5+1 ? [...]
(และภายในขณะที่วนซ้ำเราจะคำนวณmod4
ก่อนเพื่อให้mod5
เป็นคำสั่งสุดท้าย)
อัพเดท 5! เล่นกอล์ฟตั้งแต่ 2087 ถึง 2084 ไบต์โดยตระหนักว่าแทนที่จะเขียนค่าคงที่32
และ10
(เว้นวรรคและขึ้นบรรทัดใหม่) ฉันสามารถเก็บหมายเลข 10 ในตัวแปร (ตอนนี้ไม่ได้ใช้) # 2 (ลองเรียกมันว่าten
) แทนการptr3 = 5
ที่เราเขียนten = (ptr3 = 5) + 5
แล้ว32
จะกลายเป็นten+22
และจะกลายเป็นprint 10
print ten