읽을 수 없음 , 2199 2145 2134 2104 2087 2084 바이트
모두 지원 k
/j
뿐만 아니라▲
/ ▼
구문을 합니다.
읽을 수없는 전통에서는 아포스트로피와 큰 따옴표를 구별하기 위해 비례 글꼴 형식의 프로그램이 있습니다.
' "" "" ""' "" "" "" "" ' "" "" ""' "" "" "" "" "" "" " '" "" "" "" "" "" "' ' "" ' "" "" "'" " '" ""' "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" ' "" "" " " '" "" "" ""' "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" ""' "" "" "" "" "'" "" "" "" "" "" "" "" "" "" "" " '" "" "" "" "" "" "" "" "" "" ' ""' "" ' ""' "" ' "" "'" "" "" "" "" "" "" "" " '" "" "" "" "" "" "" "" "" "" " '" "" "" "" "" "" "'" "" "" "" "" " '" "" ""' "" "" "" "" "" "" "" "" "" "" '" "" ""' "" ' ""' "" ' "" "" "" "" "" "" "" "" "" "" "" "" "" ""' "" "" "" ' ""' "" " '" "" "" "" "'" "" "" "" " '" "'" " '" "'" " '" "" "" "'" "" "" " ' "" "" "" "" "" "" "" "" "" "" "" "" ' "" "" "" "" "" "" "" "" "" "" "" "' '"" "" "" "" "" "" ' "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" " "" ' "" "" "" "" "'" " '" "" "" ""' "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" ' "" "'" " '" "" "" "" ""' "" "" "" "" "" "" "" "" "" "" ""' ""' "" ' ""' "" ' ""' "" "" "" " '" "" "" "" "" "" "" "" "" "" ""' "" "" "" "" "" "" "" "" "" ' "" "'" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" " '" "'" " '" "'" "" "" "" ' "" "'" "" ' ""' "" "" "" ' "" "" """ '" "" "" "" ""' "" "" "" "" "" ' ""' "" "" " '" "" "" "" "" "" "" "" "" "" " "" ' "" "" "'" "" "" " '" "" "" "" "'" "" "" " '" "" "" "" "" "" "" "" "" "" " " '" "" ""' "" ' "" "'" "" "" "" "" ' "" "" "" "" "" "" "" "" "" "" "" ""' ""' "" " '" "" "" "'" " '" "" ""' "" "" "" "" "" "" "" "" "" "" "" "" "" "" " " '" "" "" "" "" "'" " '" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" " ' ""' "" " '" "" "" "" "'" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" ''"" "" "" "" ' "" "" "" "'" "" "" "" " '" "" "" "" "" "" "" "" "" "" "" "" "" " ' "" "" "" ""' "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" " " '" "" "" "'" "" "" " '" "" "" "" ""' "" "" "" "" "" "" "" "" "" "" "" "' "" "" "" "'" "" "" " '" "" "" "" "" "" "" "" "" "'" "" "" "" "" "" "" "" "" " ' "" "" "" "'" "" "" "" "" "" "" "" "" "" "" "" " '" "" "" "" "" "" "" "" "" " "" "" "" "" "" " '" "'" " '" "" "" "'" "" "" "" "" "" "" "" "" "" "" "" "' "" "" "" "" "" "'" " '" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" ' ""' "" ' "" "'" " '" "" "" "" "" "" "" "" "" "'" "" "" "" "" "" "" "" "" "" " "" "" " '" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "'"" "" "" "" "" "" "" "" ' "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" " " '" "" "" "" "" "" "'" "" "" "" " '" "" "" ""' "" "" "" "" "" "" "" "" "" " ' "" "" ""' "" "" "" " '" "" "" ""' "" "" "" "" "" ' ""' "" "" "" "" " ''"" ' "" "" ""' "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" " '" "" "" "'" " '" ""' "" "" " '" "'" " '" "'" "" "" "" "" "" "" " '" "" "" " ' ""' "" " '" "" ""' "" "" "" "" "" "" "" "" "" ' "" "" "" "" "" "" "" "" """ '" "" "" "" "'" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" " '" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" " "" ' "" "" "'" "" "" "" ' "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" '" "" "" "'" " '" "'" " '" "'" " '" "'" " '" "'" " '" "" ""' "" ' "" "" "" ' ""' "" ' ""' "" ' ""' "" "" "" "" "" "" "" "" " '" "" "" "" "" "" "" "" "" "" "" ' "" "" "" "" ""' "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" " '" "" """" "" " '" "'" ""
이것은 놀라운 도전이었습니다. 게시 해 주셔서 감사합니다!
설명
읽을 수없는 것과 할 수없는 것에 대한 느낌을 얻으려면 양방향으로 무한 테이프를 사용하는 Brainfuck을 상상해보십시오. 그러나 한 번에 한 셀씩 이동하는 메모리 포인터 대신 포인터를 역 참조하여 모든 메모리 셀에 액세스 할 수 있습니다. 모듈로를 포함한 다른 산술 연산은 수작업으로 수행해야하지만, 이것은이 솔루션에서 매우 유용합니다.
다음은 director의 논평과 함께 의사 코드로 작성된 프로그램입니다.
// 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
프로그램 논리에 너무 많은. 이제 이것을 읽을 수 없음 으로 변환 하고 몇 가지 더 재미있는 골프 트릭을 사용해야합니다.
변수는 읽을 수없는 상태에서 항상 숫자로 역 참조 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을 코드에서 가장 자주 발생하는 변수 에 할당 하려고 합니다. 첫 번째 while 루프에서 이것은 확실히 mod5
10 번 나타납니다. 그러나 mod5
첫 번째 while 루프 이후에는 더 이상 필요하지 않으므로 동일한 메모리 위치를 나중에 사용하는 다른 변수에 다시 할당 할 수 있습니다. 이들은 ptr2
및 ptr3
. 이제 변수는 총 21 번 참조됩니다. (발생 횟수를 직접 계산하려는 경우 다음과 같이 계산하십시오.a++
값을 얻기 위해 한 번 설정하고 설정하기 위해 한 번 두 번 .)
재사용 할 수있는 다른 변수는 하나뿐입니다. 모듈로 값을 계산 한 후에 ch
는 더 이상 필요하지 않습니다. up
그리고 dn
그렇게 두 괜찮 동일한 횟수를 온다. 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]
이제 이것은 맨 위의 초기화를 설명합니다 .7 (메모리 블록의 시작)에서 2를 뺀 값이므로 2입니다 (첫 번째 while 조건의 필수 증분). 마지막 루프에서 5의 다른 두 발생에 대해서도 마찬가지입니다.
0과 4 코드에서 같은 길이이며, 이후, 그 주 ptr
및 minLine
방법 주위 중 하나를 할당 할 수 있습니다. ... 아니면 그들이 할 수 있습니까?
두 번째 마지막 while 루프에서 신비한 2는 어떻습니까? 6이 아니어야합니까? 데이터 블록의 숫자 만 줄이려고합니다. 6에 도달하면 데이터 블록 외부에 있으므로 중지해야합니다! 버퍼 오버 플로우 버그 오류 실패 보안 취약점입니다!
우리가 멈추지 않으면 어떻게 될지 생각해보십시오. 변수 6과 4를 줄입니다 mod4
. 변수 6은 입니다. 첫 번째 while 루프에서만 사용되며 더 이상 필요하지 않으므로 해를 끼치 지 않습니다. 변수 4는 어떻습니까? 당신은 어떻게 생각하십니까, 변수 4해야 ptr
아니면해야 minLine
? 그렇습니다 minLine
.이 시점에서 더 이상 사용되지 않습니다! 따라서 변수 # 4는 minLine
우리가 안전하게 감소시키고 피해를 줄 수 없습니다!
업데이트 1! 그 실현에 의해 2145 바이트로 2199에서 Golfed 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! while 루프에서 0으로 계산 되는와 mod5
동일한 변수에 있으므로 더 이상 0으로 명시 적으로 초기화 할 필요가 없음 을 인식하여 2145 바이트에서 2134 바이트로 골프를 쳤습니다 .dn
mod5
업데이트 3! 두 가지를 실현하여 2134 바이트에서 2104 바이트로 골프를 쳤다. 첫째, "네거티브 모듈로"아이디어는 그만한 가치가 있지만 mod5
, mod4
우리는 절대로 테스트하지 않기 때문에 동일한 추론이 적용되지 않습니다 mod4+2
. 따라서 2110 바이트로 변경 mod4 ? mod4+1 : -3
하면 mod4 ? mod4-1 : 3
됩니다. 둘째, mod4
항상 0 또는 2이므로 mod4
0 대신 2로 초기화 하고 두 삼항을 반대로 할 수 있습니다 ( mod4 ? 3 : 1
대신 mod4 ? 1 : 3
).
업데이트 4! 모듈로 값을 계산하는 while 루프는 항상 한 번 이상 실행된다는 것을 인식하여 2104에서 2087 바이트로 골프를 쳤으며,이 경우 읽을 수없는 경우 다른 명령문에서 마지막 명령문의 값을 재사용 할 수 있습니다. 따라서 while --ch: [...]; up = (mod5 ? mod5+1 ? [...]
이제 우리 대신 up = ((while --ch: [...]) ? mod5+1 ? [...]
(그리고 while 루프 안에서 mod4
먼저 계산 하므로 mod5
마지막 문장입니다).
업데이트 5! 상수 32
와 10
(공백 및 줄 바꿈) 을 작성하는 대신 (현재 사용되지 않은) 변수 # 2에 전화 번호 10을 저장할 수 있음을 인식하여 2087에서 2084 바이트로 골프를 쳤 습니다 ten
. 대신에 ptr3 = 5
우리가 쓰기 ten = (ptr3 = 5) + 5
후 32
하게 ten+22
및 print 10
된다 print ten
.