Не читается , 2199 2145 2134 2104 2087 2084 байт
Поддерживает оба k
/j
так и ▲
/ ▼
синтаксис.
В доброй нечитаемой традиции, вот программа, отформатированная пропорциональным шрифтом, чтобы скрыть различие между апострофами и двойными кавычками:
«" „“ „“»«»«»«»«»«„“ „„„“““ „„„“““ "» „“ «" „“ „“»„„“ „“ „““ «» «„“» «» «„“» «» «„„““» «» «» «„„“ „“ „““» «» «» «» "" „“ „„„“““ «„„“ „“ „“ „„„“ „“ „“ „“““ „““» «» «» «» «» «„„“ „““» «» "" „“ „“«" „“»«»«»«„“ „“ „“»«»«„“»«»«„“»«»«„“ „„„“““ „“»«»«»«» «"»«»«„“»«»«„“ "» „“ „“ „“ „“ «" „“ „“ „„„“ „“ „“““ „„„“““»" " «„„“ „„„“ „“ „“ „“““ „“ „“ „““» «„„““» «„„““» «» «„“„“» «» «»»«„„““» «„„““» «„„““» «» «„“„“„“„“"»„“„“„“„„„“““„„„“““" «"»«»«„„“„“„“„““»«»«»«»«„“»«»«„“»«»«„“»«»«„„““»"» «» «„„“ „“ „““» «» «„“„“„“„“"»„“„“„“" „„“„„„“““„“„“„““«» «„„“ „“ „“ „““» «» «» «» «„„“ „“ „“ „““» «» «» «» «„„“ „“ „“ „““» «» «„“„“„“„“„„„“““„“„“„“» «» "" „“ „“ „“ „“ „„„“““ „„„“““ „“ «» «» "„“„„„“„„„“““„„„“„„„“““„“““„“„“„“„“““„“„“„“«"»«»«„“»«»«„“»«»" "„“„„„“““„„„“„„„“„“„“““„“““„“„“ «» «» «„“„“„“"»„“«"„“„“„“„“„„„“““» «„“„“„“"»„“«"„“„“» «» «» «„“» «» «„“» «» «„“„“„“"»„“«"„“„„„“““» "„“„„„“““"«„„“ „“ „“ „“ „„„“ „“ „“ „„„“““ „„„“““ „„„“““ „“““ „„„“““ „““» «» «„“„“„„„“„“„“““„„„“““„„„“““„“» «» «» «» «» «„“„“„“"»" «„„““» «„„““» «» «„“„“» «» «» «„“„“„“„“"»„“„“„“„„„“““"«"»«»«„„“„“„““»«„„““»«„„“„„„“““„“„“„““»«„„““»" "„“ «„„“ „„„“ „“ „“““ „„„“““ „““» «" „“ „“ „“ „„„“““»«„“ "» „“ „“ „“ «"»«»«„„“„“„“„““»«»«»«»«„“»«»" „„“„“„„„“„“„“““„““«» «» «» «» «„“„“„“"»„“„„„“„“““„“„„„“„“„“““„„„“““«"„“» «" „“ „“ „“»«»«»«»«„„““»«„„““»«»«„“ „“ „“ „“ "» „“ „“ „“ «"» «„„““» «» «„“„“„“» «» «„“» «» «„„“ „“ „“ „““» «» «» «» «„“» «»«" "» „“ „„„“ „“ „“““ „„„“ „„„“““ „“ „“ „“““ «" „“ „„„“ „“ „“““» «" „“ „“ „„„“ „“ „“ „“““ „“»«»«„“ „“ „“ „„„“““ „“»«»«»" "„“„“ «» «» «„“„“„“» «» «„“» «» «„“„„„“““„“„“„“» «» «„“» «» "„“»«" „“»«»«»«»«„“»«»«„„“„“„“„““»«»«»«»«„“»«»" "„“„“„“ «"»«»«„“ „„„“““ „“ „“ „“»«»«„“»«»" "„“„“„„„“„“„“„“““„“ «» «„„““» «» «„“» «» «» «» «„„““» «» «„“„“» «» «» «» "„“" „„““«» «» «» «„„“ „“ „“ „““» «» «„“„“„„„“„“„“„“““„“„“„“"»„“„“" «" „“ „„„“ „“ „“ „“““ „“ „“ „“»«„“ „“ „“ "» „“ „„„“ „“ „“““ „“ „“ „“ «" „„„“““ „“ „“ „“»«»«»«»«"„“» "„“„“„„„“““" „„“„“„““«» «„“» «» «„„“ „“ „“ „““» «» «» «» «„“» «» «„“„„„“„“““„“"»„“" «„„“ „“ „““» «" „“ „„„“““ „„„“““ „„„“““ „“ „“»«„“ „„„“““ "» „“ «"»«»" "„“„“„„„“„“„“„“““„“„“„“„„„“„“„“„“““„“„“„“«„„“ „“ „“ „““» «» «» «„„“ „“ „“ „““» «» «„“„“„“„“» «» «» «» «» "» «» «» «» «„“„“„“„“» «» «"»" „“ „“ „“ „“ „„„“ „“ „“““ „„„“ „“ „“““ «» «„“» «» «„“„“„“„„„“„„„“„“„“„“““„“„“„“““"»„“„“„“«" „“ „„„“““ „„„“““ „„„“““ „„„“““ „„„“““ „„„“““ „„„“““»" „“ «"»«»«„“»«»«„“»«»«„“»«»«»«»«„„““»«»«„“ „“ „“ "» „“ „“ " «» «„“„„„“„“„“““„“„“„“"»„“„„„“““«"„“„“„“» «„“"»" "„“«» «» «„„““» «»
Это был удивительный вызов. Спасибо за публикацию!
объяснение
Чтобы понять, что может и не может сделать нечитабельный, представьте, что Brainfuck имеет бесконечную ленту в обоих направлениях, но вместо указателя памяти, перемещающего одну ячейку за раз, вы можете получить доступ к любой ячейке памяти, разыменовав указатель. Это очень удобно в этом решении, хотя другие арифметические операции, в том числе по модулю, должны выполняться вручную.
Вот программа в виде псевдокода с комментариями директора:
// 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 той, которая чаще всего встречается в коде. В первом цикле 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]
Теперь это объясняет инициализацию в самом верху: это 5, потому что это 7 (начало блока памяти) минус 2 (обязательное приращение в первом условии while). То же самое касается двух других вхождений 5 в последнем цикле.
Обратите внимание, что, поскольку 0 и 4 имеют одинаковую длину в коде, ptr
и minLine
может быть выделена либо наоборот. ... Или они могли?
Что насчет загадочных 2 во втором-последнем цикле while?Разве это не должно быть 6? Мы хотим только уменьшить числа в блоке данных, верно? Как только мы достигнем 6, мы за пределами блока данных, и мы должны остановиться! Это была бы уязвимость безопасности ошибки ошибки переполнения буфера!
Хорошо, подумай о том, что произойдет, если мы не остановимся. Уменьшаем переменные 6 и 4. Переменная 6 есть mod4
. Это используется только в первом цикле while и больше здесь не нужно, так что никакого вреда. Как насчет переменной 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 в цикле while, 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 байтов, осознав, что цикл while, который вычисляет значения по модулю, всегда выполняется по крайней мере один раз, и в таком случае 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
.