C (gcc) , 26x20 = 520 25x19 = 475 23x17 = 391
#ifndef M //
#define M(a,b)a##b //
#define W(z,x)M(z,x) //
char*s,*S[]={"!!!!c",//
"8M !7! M8 878","77",//
"7!!MO887","788OM!!7"//
,"N7!78","7N87!"},r[5//
],*p=r;i=7;main(){for//
(;i--;)strstr(S[i],r)//
&&putchar("ITOJLSZ"[i//
]);} //
#endif //
__attribute__(( //
constructor(__LINE__)//
))W(f,__LINE__)(){s= //
" \
";*p++=strlen(s)+12;}//
Sono stato recentemente informato degli attributi della funzione GNU e, cosa più interessante, l' constructor
attributo, che consente un'implementazione più concisa di ciò che stavo facendo in modo più indiretto nel mio approccio precedente a questo problema.
La spinta dell'idea è la stessa di prima: costruire una stringa e cercarla in un elenco per identificare in quale blocco tetris è disposto il codice. Questo viene fatto chiamando le funzioni, ognuna aggiungendo un carattere alla stringa. La complicazione era e rimane che il numero di funzioni varia.
La definizione di una funzione con attribute((constructor(x)))
fa sì che la funzione venga eseguita prima di main()
essere immessa, con l'opzione facoltativa x
come priorità (inferiore significa che viene eseguita prima). Ciò elimina la necessità di puntatori a funzione, che ci consente di eliminare una macro, alcune dichiarazioni e la catena di chiamata.
L'uso __LINE__
per la priorità è iffy, poiché i livelli di priorità 0-100 sono riservati. Tuttavia, non si traducono in errori, solo in avvertimenti e quelli sono abbondanti quando si gioca a golf, quindi cosa c'è di più?
Avrebbe aiutato a radere via un'altra colonna per non usare affatto le priorità, ma l'ordine di esecuzione non sembra essere definito. (In questo caso sono invertiti, ma altri test non sono conclusivi.)
Esempio di L v2 qui
Approccio più vecchio, più portatile
#ifndef M //
#define M(a,b) a##b //
#define W(z,x)M(z,x) //
#define F W(f,__LINE__)//
#define A W(a,__LINE__)//
char r[5],*S[]={"####k"//
,";<<US##;",";##SU<<;",//
";;",";T<;#","<S #;# S"//
"< <;<","T;#;<"},*s,*p=//
r;i;typedef(*T)();T a17//
,a36,a55,a74;main(){for//
(a17(),a36&&a36(),a55&&//
a55(),a74&&a74();i--;) //
strstr(S[i],r)&&putchar//
("ILJOZTS"[i]);}i=7; //
#endif //
F();T A=F;F(){s= //
" \
";*p++=strlen(s)+12;} //
Uno dei miei problemi preferiti che ho risolto su questo sito.
Ho iniziato immaginando che ogni blocco divinasse in qualche modo le proprie coordinate. Le righe sono facili con __LINE__
, e il numero di blocchi orizzontalmente adiacenti può essere trovato usando la lunghezza di una stringa letterale, in questo modo:
char*s=//char*s=//
" "" "
; ;
Prendi la lunghezza della stringa risultante e dividi per un numero appropriato e avrai la larghezza. Sfortunatamente, qualsiasi spazio vuoto prima del blocco è invisibile con questo metodo. Ho ancora le stringhe sospetti sarebbe la soluzione, dal momento che gli spazi bianchi solo ha senso al di fuori delle corde molto raramente, in cose come a+++b
vs. a+ ++b
. Ho considerato brevemente qualcosa del genere, ma non sono riuscito a trovare nulla di utile. Un'altra possibilità sarebbe stata quella di "incollare" gli identificatori dove i blocchi si incontravano:
A BA B
Non sarei sorpreso se questo potesse ancora costituire una soluzione interessante.
Nonostante la sua semplicità, mi ci è voluto un po 'di tempo per trovare la soluzione di stringa, che si basa su questo frammento di blocco:
s=//
" \
";//
Se il frammento non ha vicini orizzontali, la nuova riga sulla seconda riga viene salvata dalla barra rovesciata, creando una stringa di lunghezza 2. Se, tuttavia, ha un vicino, la barra inversa sfuggirà invece al segno di quotion all'inizio della riga 2 del blocco successivo:
s=//s=//
" \" \
";//";//
Questo creerà la stringa "\" "di lunghezza 5.
Ancora più importante, ciò consente anche il rilevamento di spazio vuoto prima del blocco:
s=//
" \
";//
Ancora una volta, la nuova riga viene salvata e lo spazio bianco del blocco vuoto a sinistra viene incluso nella stringa risultante "" di lunghezza 6.
In totale ci sono sette diverse configurazioni di blocchi su una riga di cui dobbiamo preoccuparci e fanno tutti stringhe di lunghezze uniche:
2 " "
---
s=//
" \
";//
5 " \" "
---
s=//s=//
" \" \
";//";//
6 " "
---
s=//
" \
";//
9 " \" "
----
s=//s=//
" \" \
";//";//
10 " "
---
s=//
" \
";//
8 " \" \" "
---
s=//s=//s=//
" \" \" \
";//";//";//
11 " \" \" \" "
----
s=//s=//s=//s=//
" \" \" \" \
";//";//";//";//
Naturalmente i blocchi finali non avranno una lunghezza così breve, ma il principio è lo stesso indipendentemente dalle dimensioni del blocco. Questo ha anche il vantaggio che non è necessario un meccanismo separato per rilevare la larghezza. Aggiungendo un carattere corrispondente alla lunghezza di questa stringa a una stringa di risultati, ognuna delle 19 configurazioni produce una stringa univoca, che deve essere confrontata con un elenco adatto solo dopo che tutti i blocchi sono stati eseguiti.
Una volta che questo è stato risolto, il prossimo grosso problema era come "visitare" ogni fila di blocchi. In C, siamo molto limitati a ciò che può essere fatto al di fuori delle funzioni. Dobbiamo anche main()
apparire, ma solo una volta. Quest'ultimo è facilmente raggiungibile da alcuni #define
s, ma se vogliamo che il codice dei blocchi successivi sia all'interno di main()
, il problema di come sapere quando mettere la parentesi graffa di chiusura finale. Dopotutto, non sappiamo quante file di blocchi verranno effettivamente utilizzate. Quindi dobbiamo avere main()
statico e in qualche modo il resto per essere dinamico.
Se le altre righe di blocco devono essere autosufficienti, devono essere funzioni, ma dobbiamo assicurarci che ogni funzione abbia un nome univoco, pur essendo abbastanza prevedibile da poter essere richiamata main()
. Abbiamo anche bisogno di un meccanismo per sapere quali funzioni sono effettivamente lì per essere chiamate. La generazione di nomi univoci è risolta dalle macro helper:
#define M(a,b) a##b //
#define W(z,x)M(z,x) //
#define F W(f,__LINE__) //
#define A W(a,__LINE__) //
La chiamata F
creerà un identificatore il cui nome inizia con una f e termina con il numero di riga. A
fa lo stesso ma con un prefisso as, che viene utilizzato per la seconda parte della soluzione, ovvero i puntatori a funzione. Dichiariamo quattro di questi puntatori:
typedef(*T)();T a17,a36,a55,a74;
Poiché sono dichiarate come variabili globali, sono comodamente impostate su NULL. Successivamente, ogni riga di blocco avrà il seguente codice:
F();T A=F;F()
Questo prima dichiarerà una funzione, definirà il puntatore della funzione appropriata per puntare a quella funzione (possiamo definire i globali solo una volta, ma la dichiarazione precedente non contava come una definizione, anche se inizializzava su NULL), quindi definire l'effettivo funzione. Ciò consente main()
di chiamare qualsiasi puntatore a funzione diverso da NULL (a17 non sarà mai NULL):
a17(),a36&&a36(),a55&&a55(),a74&&a74()
In questo modo verrà creata la stringa r
, che viene quindi cercata nella tabella delle stringhe e, se trovata, viene emessa la lettera appropriata.
L'unico trucco rimanente è che l'elenco delle stringhe con cui confrontarsi veniva ridotto ogni volta che si poteva evitare l'ambiguità o si potevano confondere stringhe sovrapposte.
Esempio di L v2 qui