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;}//
Fui informado recentemente dos atributos de função do GNU, e o mais interessante, o constructor
atributo, que permite uma implementação mais concisa do que eu estava fazendo de uma maneira mais indireta na minha abordagem anterior a esse problema.
O impulso da idéia é o mesmo de antes: Crie uma string e procure-a em uma lista para identificar em qual bloco de tetris o código está definido. Isso é feito chamando funções, cada uma adicionando um caractere à string. A complicação foi e continua sendo que o número de funções varia.
Definir uma função com attribute((constructor(x)))
faz com que a função seja executada antes de main()
ser inserida, com o opcional x
sendo a prioridade (menor significa que ela é executada anteriormente). Isso elimina a necessidade de ponteiros de função, o que nos permite eliminar uma macro, algumas declarações e a cadeia de chamada.
O uso __LINE__
da prioridade é duvidoso, pois os níveis de prioridade de 0 a 100 são reservados. No entanto, isso não resulta em erros, apenas avisos, e esses são muitos quando se joga, então o que é mais?
Ajudaria a raspar outra coluna para não usar prioridades, mas a ordem de execução não parece estar definida. (Eles são revertidos neste caso, mas outros testes são inconclusivos.)
Exemplo de L v2 aqui
Abordagem mais antiga e mais portátil
#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;} //
Um dos meus problemas favoritos que resolvi neste site.
Comecei imaginando que cada bloco iria adivinhar suas próprias coordenadas de alguma forma. As linhas são fáceis de usar __LINE__
e o número de blocos adjacentes horizontalmente pode ser encontrado usando o comprimento de uma cadeia de caracteres literal, assim:
char*s=//char*s=//
" "" "
; ;
Pegue o comprimento da string resultante e divida por um número apropriado e você terá a largura. Infelizmente, qualquer espaço vazio antes do bloco é invisível por esse método. Eu ainda suspeitos cordas seria a solução, uma vez que os espaços em branco só tem sentido fora das cordas muito raramente, em coisas como a+++b
vs. a+ ++b
. Eu considerei brevemente algo assim, mas não consegui encontrar nada útil. Outra possibilidade seria permitir que os identificadores fossem "colados" juntos onde os blocos se encontrassem:
A BA B
Eu não ficaria surpreso se isso ainda pudesse constituir uma solução interessante.
Apesar de sua simplicidade, levei algum tempo para encontrar a solução de string, baseada neste fragmento de bloco:
s=//
" \
";//
Se o fragmento não tiver vizinhos horizontais, a nova linha na segunda linha será escapada pela barra invertida, criando uma sequência de comprimento 2. Se, no entanto, tiver um vizinho, a barra invertida escapará da marca de aspas no início da linha 2 do próximo bloco:
s=//s=//
" \" \
";//";//
Isso criará a string "\" "de comprimento 5.
Mais crucialmente, isso também permite a detecção de espaço vazio antes do bloco:
s=//
" \
";//
Novamente, a nova linha é escapada e o espaço em branco do bloco vazio à esquerda é incluído na sequência "" resultante de comprimento 6.
No total, existem sete configurações diferentes de blocos em uma linha com as quais precisamos nos preocupar, e todas elas formam cadeias de comprimentos únicos:
2 " "
---
s=//
" \
";//
5 " \" "
---
s=//s=//
" \" \
";//";//
6 " "
---
s=//
" \
";//
9 " \" "
----
s=//s=//
" \" \
";//";//
10 " "
---
s=//
" \
";//
8 " \" \" "
---
s=//s=//s=//
" \" \" \
";//";//";//
11 " \" \" \" "
----
s=//s=//s=//s=//
" \" \" \" \
";//";//";//";//
Obviamente, os blocos finais não terão tamanho tão curto, mas o princípio é o mesmo, independentemente do tamanho do bloco. Isso também tem o bônus de que um mecanismo separado para detectar a largura é desnecessário. Ao adicionar um caractere correspondente ao comprimento dessa string a uma string de resultados, cada uma das 19 configurações produz uma string única, que precisa ser comparada apenas a uma lista adequada depois que todos os blocos forem executados.
Depois que isso foi resolvido, o próximo grande problema foi como "visitar" cada linha de blocos. Em C, estamos muito limitados ao que pode ser feito fora das funções. Também precisamos main()
aparecer, mas apenas uma vez. O último é facilmente alcançado por alguns #define
s, mas se queremos que o código dos blocos subseqüentes esteja dentro main()
, o problema de como saber quando colocar o colchete final de fechamento. Afinal, não sabemos quantas linhas de blocos serão realmente usadas. Então, precisamos ter main()
estática e, de alguma forma, o resto para ser dinâmico.
Se as outras linhas de bloco devem ser independentes, elas precisam ser funções, mas precisamos garantir que cada função tenha um nome exclusivo, além de ser previsível o suficiente para ser possível chamar main()
. Também precisamos de um mecanismo para saber quais funções realmente existem para serem chamadas. A geração de nomes exclusivos é resolvida por macros auxiliares:
#define M(a,b) a##b //
#define W(z,x)M(z,x) //
#define F W(f,__LINE__) //
#define A W(a,__LINE__) //
A chamada F
criará um identificador cujo nome começa com um f e termina com o número da linha. A
faz o mesmo, mas com um prefixo as, que é usado para a segunda parte da solução, que é ponteiros de função. Declaramos quatro desses indicadores:
typedef(*T)();T a17,a36,a55,a74;
Como essas são declaradas como variáveis globais, elas são convenientemente definidas como NULL. Posteriormente, cada linha de bloco terá o seguinte trecho de código:
F();T A=F;F()
Isso primeiro declarará uma função, definirá o ponteiro de função apropriado para apontar para essa função (só podemos definir globais uma vez, mas a declaração anterior não contou como definição, mesmo se inicializou como NULL) e, em seguida, defina o real função. Isso permite main()
chamar qualquer ponteiro de função que não seja NULL (a17 nunca será NULL):
a17(),a36&&a36(),a55&&a55(),a74&&a74()
Fazer isso criará a string r
, que é procurada na tabela de strings e, se encontrada, a letra apropriada será impressa.
O único truque restante é que a lista de strings a serem comparadas era reduzida sempre que a ambiguidade pudesse ser evitada, ou strings sobrepostos podiam ser confundidos.
Exemplo de L v2 aqui