Không thể đọc được , 2199 2145 2134 2104 2087 2084 byte
Hỗ trợ cả k
/ j
cũng như ▲
/ ▼
cú pháp.
Theo truyền thống tốt không thể đọc được, đây là chương trình được định dạng theo phông chữ tỷ lệ, để làm xáo trộn sự khác biệt giữa dấu nháy đơn và dấu ngoặc kép:
"" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" " "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" " "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" """" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" " "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" " "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" """ "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" " "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" " "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" """ "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" """ "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" " "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" " "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" """ "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" " "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" " "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" """ "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" " "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" " "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" """ "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" " "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" """ "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" """" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" " "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" """" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" """" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" " "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" " "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" """" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" " "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" """" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" """" "" "" "" "" ""
Đây là một thử thách đáng kinh ngạc. Cảm ơn đã đăng bài viết!
Giải trình
Để cảm nhận những gì Unreadable có thể và không thể làm, hãy tưởng tượng Brainfuck với một cuộn băng vô hạn theo cả hai hướng, nhưng thay vì một con trỏ bộ nhớ di chuyển một ô tại một thời điểm, bạn có thể truy cập bất kỳ ô nhớ nào bằng cách hủy bỏ một con trỏ. Điều này khá hữu ích trong giải pháp này, mặc dù các phép toán số học khác - bao gồm cả modulo - phải được thực hiện bằng tay.
Đây là chương trình dưới dạng mã giả với lời bình luận của đạo diễn:
// 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
Quá nhiều cho logic chương trình. Bây giờ chúng tôi cần dịch cái này sang Unreadable và sử dụng một vài thủ thuật chơi golf thú vị hơn.
Các biến luôn được quy định số lượng trong Không thể đọc được (ví dụ: a = 1
trở thành một cái gì đó giống như *(1) = 1
). Một số chữ số dài hơn những chữ khác; ngắn nhất là 1, theo sau là 2, v.v. Để chỉ ra số âm dài hơn bao nhiêu, đây là các số từ -1 đến 7:
-1 '""""""""'""""""""'""" 22
0 '""""""""'""" 13
1 '""" 4
2 '""'""" 7
3 '""'""'""" 10
4 '""'""'""'""" 13
5 '""'""'""'""'""" 16
6 '""'""'""'""'""'""" 19
7 '""'""'""'""'""'""'""" 22
Rõ ràng, chúng tôi muốn phân bổ biến số 1 cho biến xảy ra thường xuyên nhất trong mã. Trong vòng lặp while đầu tiên, điều này chắc chắn mod5
, xuất hiện 10 lần. Nhưng chúng ta không cần mod5
nữa sau vòng lặp while đầu tiên, vì vậy chúng ta có thể phân bổ lại cùng một vị trí bộ nhớ cho các biến khác mà chúng ta sử dụng sau này. Đây là ptr2
và ptr3
. Bây giờ biến được tham chiếu tổng cộng 21 lần. (Nếu bạn đang cố gắng tự đếm số lần xuất hiện, hãy nhớ đếm thứ gì đó như a++
hai lần, một lần để nhận giá trị và một lần để đặt giá trị đó.)
Chỉ có một biến khác mà chúng ta có thể sử dụng lại; sau khi chúng tôi tính toán các giá trị modulo, ch
không còn cần thiết nữa. up
và dn
đưa ra cùng một số lần, vì vậy hoặc là tốt. Hãy hợp nhất ch
với up
.
Điều này để lại tổng cộng 8 biến số duy nhất. Chúng ta có thể phân bổ các biến từ 0 đến 7 và sau đó bắt đầu khối bộ nhớ (chứa các ký tự và số dòng) ở 8. Nhưng! Vì 7 có cùng độ dài mã với −1, chúng tôi cũng có thể sử dụng các biến −1 đến 6 và bắt đầu khối bộ nhớ ở mức 7. Bằng cách này, mọi tham chiếu đến vị trí bắt đầu của khối bộ nhớ đều ngắn hơn một chút trong mã! Điều này để lại cho chúng tôi các bài tập sau:
-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]
Bây giờ điều này giải thích việc khởi tạo ở trên cùng: đó là 5 vì nó là 7 (bắt đầu của khối bộ nhớ) trừ 2 (mức tăng bắt buộc trong điều kiện đầu tiên). Điều tương tự cũng xảy ra với hai lần xuất hiện khác của 5 trong vòng lặp cuối cùng.
Lưu ý rằng, vì 0 và 4 có cùng độ dài mã ptr
và minLine
có thể được phân bổ theo cách khác. ... Hay họ có thể?
Điều gì về 2 bí ẩn trong vòng lặp thứ hai cuối cùng? Đây không phải là số 6 sao? Chúng tôi chỉ muốn giảm số trong khối dữ liệu, phải không? Khi chúng tôi đạt đến 6, chúng tôi ở ngoài khối dữ liệu và chúng tôi nên dừng lại! Nó sẽ là một lỗi tràn bộ đệm lỗi lỗi bảo mật lỗi!
Chà, nghĩ về những gì sẽ xảy ra nếu chúng ta không dừng lại. Chúng tôi giảm các biến 6 và 4. Biến 6 là mod4
. Điều đó chỉ được sử dụng trong vòng lặp while đầu tiên và không còn cần thiết ở đây nữa, vì vậy không có hại gì. Còn biến 4 thì sao? Bạn nghĩ gì, biến 4 nên ptr
hay nên là minLine
? Điều đó đúng, minLine
cũng không còn được sử dụng tại thời điểm này! Do đó, biến số 4 là minLine
và chúng ta có thể giảm nó một cách an toàn và không gây sát thương!
CẬP NHẬT 1! Được đánh gôn từ 2199 đến 2145 byte bằng cách nhận ra rằng cũngdn
có thể được hợp nhất với , mặc dù vẫn được sử dụng trong tính toán giá trị cho ! Việc gán biến mới hiện là:mod5
mod5
dn
0 ptr
1 mod5, dn, ptr2, ptr3
2 curLine
3 maxLine
4 minLine
5 ch, up
6 mod4
7... [data block]
CẬP NHẬT 2! Đã đánh gôn từ 2145 đến 2134 byte bằng cách nhận ra rằng, vì mod5
bây giờ có cùng biến với dn
, được tính thành 0 trong một vòng lặp while, mod5
không còn cần phải được khởi tạo rõ ràng thành 0.
CẬP NHẬT 3! Chơi gôn từ 2134 đến 2104 byte bằng cách nhận ra hai điều. Đầu tiên, mặc dù ý tưởng modulo phủ định của YouTube là đáng giá mod5
, nhưng lý do tương tự không áp dụng mod4
vì chúng tôi không bao giờ kiểm tra, mod4+2
v.v. Do đó, việc thay đổi mod4 ? mod4+1 : -3
sẽ mod4 ? mod4-1 : 3
đưa chúng tôi đến 2110 byte. Thứ hai, vì mod4
luôn là 0 hoặc 2, chúng ta có thể khởi tạo mod4
thành 2 thay vì 0 và đảo ngược hai ternary ( mod4 ? 3 : 1
thay vì mod4 ? 1 : 3
).
CẬP NHẬT 4! Được đánh dấu từ 2104 đến 2087 byte bằng cách nhận ra rằng vòng lặp while tính toán các giá trị modulo luôn chạy ít nhất một lần và trong trường hợp đó, Unreadable cho phép bạn sử dụng lại giá trị của câu lệnh cuối cùng trong một biểu thức khác. Do đó, thay vì while --ch: [...]; up = (mod5 ? mod5+1 ? [...]
bây giờ chúng ta có up = ((while --ch: [...]) ? mod5+1 ? [...]
(và bên trong vòng lặp while đó, chúng ta tính toán mod4
trước, vì vậy đó mod5
là câu lệnh cuối cùng).
CẬP NHẬT 5! Được đánh gôn từ 2087 đến 2084 byte bằng cách nhận ra rằng thay vì viết ra các hằng số 32
và 10
(dấu cách và dòng mới), tôi có thể lưu trữ số 10 trong biến số 2 (hiện chưa được sử dụng) (hãy gọi nó ten
). Thay vì ptr3 = 5
chúng ta viết ten = (ptr3 = 5) + 5
, sau đó 32
trở thành ten+22
và print 10
trở thành print ten
.