Ilegível , 2199 2145 2134 2104 2087 2084 bytes
Suporta tanto k
/ j
como também ▲
/ ▼
sintaxe.
Em boa tradição ilegível, eis o programa formatado em fonte proporcional, para ofuscar a distinção entre apóstrofos e aspas duplas:
'"" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" " "" '""' "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "'" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" ""'"" ""' "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" " '""' "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "'" ""' "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" ""'" "'" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" " '""' "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "'" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" """ "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" '"" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" '"" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" ""'""' "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" '"" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" '""' "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" ""'" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" " "" '"" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "'" "'" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "'""' "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "'" ""' "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" '""' "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" """" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" '"" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" " "'" "'" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "'"" "'" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" '"" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" " "" "" '"" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" ""'"" ""' "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" '""' "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" """" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" " "'" ""' "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" " '"" "'" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" """ '""' "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" " "'" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" " '""' "" "" "" "" '"" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" """'" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" " "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" '""' "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" ""'" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" '""' "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" " "" '"" "'" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" """ "" "" "" "" "
Este foi um desafio incrível. Obrigado por postar!
Explicação
Para ter uma idéia do que o ilegível pode ou não fazer, imagine Brainfuck com uma fita infinita nas duas direções, mas, em vez de um ponteiro de memória mover uma célula de cada vez, você pode acessar qualquer célula de memória desmarcando um ponteiro. Isso é bastante útil nessa solução, embora outras operações aritméticas - incluindo o módulo - tenham que ser feitas manualmente.
Aqui está o programa como pseudocódigo com o comentário do diretor:
// 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
Tanta coisa para a lógica do programa. Agora precisamos traduzir isso para Ilegível e usar mais alguns truques de golfe interessantes.
As variáveis são sempre desreferenciadas numericamente em Ilegível (por exemplo, a = 1
torna-se algo parecido *(1) = 1
). Alguns literais numéricos são mais longos que outros; o menor é 1, seguido por 2, etc. Para mostrar o número de números negativos mais longos, aqui estão os números de -1 a 7:
-1 '""""""""'""""""""'""" 22
0 '""""""""'""" 13
1 '""" 4
2 '""'""" 7
3 '""'""'""" 10
4 '""'""'""'""" 13
5 '""'""'""'""'""" 16
6 '""'""'""'""'""'""" 19
7 '""'""'""'""'""'""'""" 22
Claramente, queremos alocar a variável nº 1 à que ocorre com mais frequência no código. No primeiro loop while, esse é definitivamente o mod5
que ocorre 10 vezes. Mas não precisamos mod5
mais depois do primeiro loop while, para que possamos realocar o mesmo local de memória para outras variáveis que usamos mais tarde. Estes são ptr2
e ptr3
. Agora a variável é referenciada 21 vezes no total. (Se você estiver tentando contar o número de ocorrências, lembre-se de contar algo como a++
duas vezes, uma para obter o valor e outra para defini-lo.)
Há apenas uma outra variável que podemos reutilizar; depois que calculamos os valores do módulo, ch
não é mais necessário. up
e dn
suba o mesmo número de vezes, então qualquer um está bem. Vamos mesclar ch
com up
.
Isso deixa um total de 8 variáveis únicas. Poderíamos alocar variáveis de 0 a 7 e iniciar o bloco de memória (contendo os caracteres e os números de linha) em 8. Mas! Como 7 tem o mesmo comprimento de código que -1, também poderíamos usar as variáveis -1 a 6 e iniciar o bloco de memória em 7. Dessa forma, toda referência à posição inicial do bloco de memória é um pouco mais curta em código! Isso nos deixa com as seguintes atribuições:
-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]
Agora, isso explica a inicialização no topo: é 5 porque é 7 (o início do bloco de memória) menos 2 (o incremento obrigatório na primeira condição while). O mesmo vale para as outras duas ocorrências de 5 no último loop.
Observe que, como 0 e 4 têm o mesmo comprimento no código, ptr
e minLine
podem ser alocados de qualquer maneira. ... Ou poderiam?
E os misteriosos 2 no penúltimo loop while? Isso não deveria ser um 6? Queremos apenas diminuir os números no bloco de dados, certo? Quando chegamos a 6, estamos fora do bloco de dados e devemos parar! Seria uma vulnerabilidade de segurança de falha de erro de bug de estouro de buffer!
Bem, pense no que acontece se não pararmos. Nós decrementamos as variáveis 6 e 4. A variável 6 é mod4
. Isso é usado apenas no primeiro loop while e não é mais necessário aqui, para não causar danos. E a variável 4? O que você acha que deveria ser a variável 4ptr
ou deveria ser minLine
? Isso mesmo, minLine
também não é mais usado neste momento! Assim, a variável nº 4 é minLine
e podemos diminuí-la com segurança e não causar danos!
ATUALIZAÇÃO 1! Jogou golfe de 2199 a 2145 bytes ao perceber que tambémdn
pode ser mesclado , embora ainda seja usado no cálculo do valor paramod5
mod5
dn
! A nova atribuição de variável agora é:
0 ptr
1 mod5, dn, ptr2, ptr3
2 curLine
3 maxLine
4 minLine
5 ch, up
6 mod4
7... [data block]
ATUALIZAÇÃO 2! Golfou de 2145 a 2134 bytes ao perceber que, desdemod5
agora está na mesma variável que dn
, que é contada como 0 em um loop while, mod5
não precisa mais ser explicitamente inicializada como 0.
ATUALIZAÇÃO 3! Jogou de 2134 a 2104 bytes ao realizar duas coisas. Primeiro, embora valha a pena a idéia do “módulo negativo” mod5
, o mesmo raciocínio não se aplica a mod4
porque nunca testamos contra mod4+2
etc. Portanto, mudar mod4 ? mod4+1 : -3
para mod4 ? mod4-1 : 3
nos leva a 2110 bytes. Segundo, como mod4
sempre é 0 ou 2, podemos inicializar mod4
para 2 em vez de 0 e reverter os dois ternários (em mod4 ? 3 : 1
vez de mod4 ? 1 : 3
).
ATUALIZAÇÃO 4! Jogou de 2104 a 2087 bytes ao perceber que o loop while que calcula os valores do módulo sempre é executado pelo menos uma vez e, nesse caso, Ilegível permite reutilizar o valor da última instrução em outra expressão. Assim, em vez de while --ch: [...]; up = (mod5 ? mod5+1 ? [...]
agora termosup = ((while --ch: [...]) ? mod5+1 ? [...]
(e dentro desse loop while, calculamos mod4
primeiro, de modo que essa mod5
é a última declaração).
ATUALIZAÇÃO 5! Joguei de 2087 a 2084 bytes ao perceber que, em vez de escrever as constantes 32
e 10
(espaço e nova linha), posso armazenar o número 10 na variável # 2 (agora não utilizada) (vamos chamá-lo ten
). Em vez de ptr3 = 5
escrever ten = (ptr3 = 5) + 5
, 32
torna - se ten+22
e print 10
torna - se print ten
.