Illeggibile , 2199 2145 2134 2104 2087 2084 byte
Supporta sia k
/ j
che ▲
/▼
sintassi .
Nella buona tradizione illeggibile, ecco il programma formattato in caratteri proporzionali, per offuscare la distinzione tra apostrofi e virgolette doppie:
' '' '' ''' "" "" "" "" ' '' ' ' '''' ' ' '''' "' '' "' '' ''" ' '' '' ''' "" ' ''' "" ' ''' "" " ' '''" "" "" " ' '' '' '''" "" "" "" " ' '' ' ' '''' " ' '' '' '' ' ' '' '' '' '''' '''" "" "" "" "" " ' '' '''" "" " ' '' ''' '' ''' "" "" ' '' '' ''' "" ' ''' "" ' ''' "" ' '' ' ' '''' ''' "" "" "" ' ''' "" ' ''' "" ' '' "' '' '' '' '' "' '' '' ' ' '' '' '''' ' ' ''''"'" " ' '' ' ' '' '' '' '''' '' '' '''" " ' '''" " ' '''" "" ' '' ''' "" """" ' '''" " ' '''" " ' '''" "" ' '' '' '' '' "' '' '' '' ' ' '''' ' ' ''''" ' ''' "" " ' '' '' '' '''" "" "" "" ' ''' "" ' ''' "" ' ''' "" " ' '''" "' "" " ' '' '' '''" "" ' '' '' '' '' "' '' '' ''" ' '' ' ' '''' '' '' '''"" " ' '' '' '' '''" "" "" "" " ' '' '' '' '''" "" "" "" " ' '' '' '' '''" "" ' '' '' '' '' ' ' '''' '' '' ''' "" " ' '' '' '' '' ' ' '''' ' ' '''' '' "" "" ' '' ' ' '' ' ' '''' ' ' '' ' ' '''' '''' '' '' '' '''' '' '' ''' ''' "" ' ''' "" ' ''' "" " ' '' ' ' '''' ' ' '' ' ' '' '' '''' '''' '' '' "" "" ' '' '' '' "' '' "' '' '' '' '' ' ' ''''" ' '' '' ''"' '' "' '' ''" "" "" ' ''' "" ' ''' "" ' '' '' '' "' '' "' '' ' ' ''''"' '' ' ' ''''"" ' '' '' '' '' ' ' '' '' '' ' ' '''' ' ' '''' ' ' '''' '''' ' ' '''' '''" "" ' '' '' ' ' '' '' '''' ' ' '''' ' ' '''' ''' "" "" "" "" ' '' '' '' "'" " ' '''" " ' '''" "" ' '' ''' "" "" ' '' '' '' '' "' '' '' '' ' ' ''''"' ''' "" " ' '' '' '''" " ' '''" " ' '' ' ' '''' '' '' '''" " ' '''" " ' '' " ' '' ' ' '' '' '''' ' ' '''' '''" "' '' '' '' ' ' ''''" ' '' "' '' '' '' ' ''' "" " ' '' '' '' '''" "" "" "" ' ''' "" " ' '' '' ' ' '' '' '''' '''"" "" "" "" ' '' '' '' "' '' ' ' '' '''' '' ' ' '' '' '''' ' ' '''' "' ''" ' '' '' '' ''' "" "" "" " ' '''" " ' '''" "" ' '' '' '' '' "' '' '' '' "'" " ' '''" "" ' '' '' ''' "" ' ''' "" " ' '' '' '' '''" "" "" "" ' ''' ""' '' "' '' ' ' '' '' '''' ' ' '' ' ' '''' '' '' '''' "' '' ' ' '' '' ''''" ' '' '' '' ' ' '' '' '' '''' ''' "" ' '' '' '' ' ' '''' ''' "" "" " ' '' '' "" "" ' '' '' ''' "" ' ''' "" ' '' ' ' '''' '' '' ''' "" ' ''' "" ' ''"' '' ''' "" "" "" ' ''' "" " ' '' '' '' '''" "" "" "" ' ''' "" " ' '' '' '' ' ''' "" ' '' ' ' '''' '' '' ''' "" ' ''' "" " ' '' '' ' ' '' '' '' '''' '' "" " ' '''" "" ' ''' "" "" "" " ' '''" "" ' '' ''' "" "" "" ' '' " ' '''"" "" "" " ' '' '' '' '''" "" ' '' '' ' ' '' '' '' '''' '' '' '' "' '' ''" "' '' ' ' '' '' '' '''' '' '' ''" ' '' '' '' "' '' ' ' '' '' '''' '' '' '' ' '' ' ' '''' '' '' ''' "" "" "" "' ''"' '' '' ' ' '''' " ' '' '' '''"" ' ''' "" " ' '' '' '' '''" "" "" "" ' ''' "" ' '' ' ' '' '''' '' "' ''" " ' '' '' '''" "' '' ' ' '''' ' ' '''' ' ' '''' '' ''" ' '' ' ' '''' "' '' ' ''' "" " ' '' '' ' ' '' '' '' '''' '' '' '' ' ' '' '' '' '''' '' '' ''" ' '' '' '' '''" "" "" " ' '' '' '' '''" "" ' '' '' '' ''' "" "" "" "" '" "" "" "" ' '' '' '' ''' "" "'"' '' '' '' '' ' ' '' '' '''' ' ' '' '' '''' "" ' ''' "" ' '' '' '' ' ' '' ' ' '' '' '' '''' '' '' '''' "' '' '' ''"' '' ' ' '''' ' ' '''' ' ' '''' ' ' '''' ' ' '''' ' ' '''' ' ' ''''"' '' ' ''' "" ' ''' "" ' ''' "" ' ''' "" "" "" " ' '''" "" ' '' '' '' "' '' ''" "" ' '' ' ' '' '' '''' '' '' '' "' '' ' ' '''' "' '' '' ''" ' ''"' "' ''"" "" " ' '''" ""
Questa è stata una sfida incredibile. Grazie per la pubblicazione!
Spiegazione
Per avere un'idea di ciò che illeggibile può e non può fare, immagina Brainfuck con un nastro infinito in entrambe le direzioni, ma invece di un puntatore di memoria che sposta una cella alla volta, puoi accedere a qualsiasi cella di memoria dereferenziando un puntatore. Questo risulta molto utile in questa soluzione, sebbene altre operazioni aritmetiche - incluso il modulo - debbano essere eseguite manualmente.
Ecco il programma come pseudocodice con il commento del regista:
// 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
Questo per quanto riguarda la logica del programma. Ora dobbiamo tradurre questo in Illeggibile e usare alcuni trucchi golfistici più interessanti.
Le variabili sono sempre riferite numericamente in Illeggibile (ad esempio a = 1
diventa qualcosa di simile *(1) = 1
). Alcuni letterali numerici sono più lunghi di altri; il più corto è 1, seguito da 2, ecc. Per mostrare quanto sono più lunghi i numeri negativi, ecco i numeri da -1 a 7:
-1 '""""""""'""""""""'""" 22
0 '""""""""'""" 13
1 '""" 4
2 '""'""" 7
3 '""'""'""" 10
4 '""'""'""'""" 13
5 '""'""'""'""'""" 16
6 '""'""'""'""'""'""" 19
7 '""'""'""'""'""'""'""" 22
Chiaramente, vogliamo allocare la variabile # 1 a quella che si verifica più frequentemente nel codice. Nel primo ciclo while, questo è sicuramente mod5
, che arriva 10 volte. Ma non abbiamo mod5
più bisogno dopo il primo ciclo while, quindi possiamo riassegnare la stessa posizione di memoria ad altre variabili che useremo in seguito. Questi sono ptr2
e ptr3
. Ora la variabile viene referenziata 21 volte in totale. (Se stai cercando di contare tu stesso il numero di occorrenze, ricorda di contare qualcosa come a++
due volte, una volta per ottenere il valore e una volta per impostarlo.)
C'è solo un'altra variabile che possiamo riutilizzare; dopo aver calcolato i valori del modulo, ch
non è più necessario. up
e dn
venire lo stesso numero di volte, quindi va bene lo stesso. Uniamoci ch
con up
.
Questo lascia un totale di 8 variabili uniche. Potremmo allocare le variabili da 0 a 7 e quindi avviare il blocco di memoria (contenente i caratteri e i numeri di riga) da 8. Ma! Dato che 7 ha la stessa lunghezza nel codice di −1, potremmo anche usare le variabili da −1 a 6 e avviare il blocco di memoria da 7. In questo modo, ogni riferimento alla posizione iniziale del blocco di memoria è leggermente più breve nel codice! Questo ci lascia con i seguenti compiti:
-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]
Ora questo spiega l'inizializzazione in alto: è 5 perché è 7 (l'inizio del blocco di memoria) meno 2 (l'incremento obbligatorio nella prima condizione while). Lo stesso vale per le altre due occorrenze di 5 nell'ultimo ciclo.
Si noti che, poiché 0 e 4 hanno la stessa lunghezza nel codice ptr
e minLine
potrebbero essere allocati in entrambi i modi. ... O potrebbero?
Che dire del misterioso 2 nel penultimo ciclo while? Questo non dovrebbe essere un 6? Vogliamo solo ridurre i numeri nel blocco dati, giusto? Una volta raggiunti i 6, siamo fuori dal blocco dati e dovremmo fermarci! Sarebbe una vulnerabilità di sicurezza di errore di bug buffer overflow errore!
Bene, pensa a cosa succede se non ci fermiamo. Diminuiamo le variabili 6 e 4. La variabile 6 è mod4
. Viene utilizzato solo nel primo ciclo while e non è più necessario qui, quindi nessun danno fatto. Che dire della variabile 4? Cosa pensi, dovrebbe essere la variabile 4 ptr
o dovrebbe essere minLine
? Esatto, minLine
non è più utilizzato neanche a questo punto! Quindi, la variabile # 4 è minLine
e possiamo tranquillamente diminuirla e non fare danni!
AGGIORNAMENTO 1! Giocato a golf da 2199 a 2145 byte realizzando che dn
può anche essere unito mod5
, anche se mod5
è ancora usato nel calcolo del valore per dn
! La nuova assegnazione delle variabili è ora:
0 ptr
1 mod5, dn, ptr2, ptr3
2 curLine
3 maxLine
4 minLine
5 ch, up
6 mod4
7... [data block]
AGGIORNAMENTO 2! Giocato a golf da 2145 a 2134 byte rendendosi conto che, poiché mod5
ora è nella stessa variabile di dn
, che viene contato su 0 in un ciclo while, mod5
non è più necessario inizializzare esplicitamente su 0.
AGGIORNAMENTO 3! Giocato a golf dal 2134 al 2104 byte realizzando due cose. In primo luogo, sebbene l'idea del "modulo negativo" sia valsa la pena mod5
, lo stesso ragionamento non si applica mod4
perché non testiamo mai contro mod4+2
ecc. Pertanto, il passaggio mod4 ? mod4+1 : -3
a mod4 ? mod4-1 : 3
ci porta a 2110 byte. In secondo luogo, poiché mod4
è sempre 0 o 2, possiamo inizializzare mod4
a 2 invece di 0 e invertire i due ternari ( mod4 ? 3 : 1
anziché mod4 ? 1 : 3
).
AGGIORNAMENTO 4! Giocato a golf da 2104 a 2087 byte, rendendosi conto che il ciclo while che calcola i valori del modulo viene sempre eseguito almeno una volta, e in tal caso, Illeggibile consente di riutilizzare il valore dell'ultima istruzione in un'altra espressione. Quindi, invece di while --ch: [...]; up = (mod5 ? mod5+1 ? [...]
ora abbiamo up = ((while --ch: [...]) ? mod5+1 ? [...]
(e all'interno di quel ciclo while, calcoliamo mod4
prima, quindi questa mod5
è l'ultima affermazione).
AGGIORNAMENTO 5! Giocato a golf dal 2087 al 2084 byte realizzando che invece di scrivere le costanti 32
e 10
(spazio e newline), posso memorizzare il numero 10 nella variabile (ora non utilizzata) n. 2 (chiamiamolo ten
). Invece di ptr3 = 5
scrivere ten = (ptr3 = 5) + 5
, 32
diventa ten+22
e print 10
diventa print ten
.