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;}//
Recientemente fui informado de los atributos de función de GNU, y lo más interesante del constructor
atributo, que permite una implementación más concisa de lo que estaba haciendo de una manera más indirecta en mi enfoque anterior de este problema.
El objetivo de la idea es el mismo que antes: construir una cadena y buscarla en una lista para identificar qué bloque de tetris se presenta el código. Esto se hace llamando a funciones, cada una agregando un carácter a la cadena. La complicación fue y sigue siendo que el número de funciones varía.
La definición de una función attribute((constructor(x)))
hace que la función se ejecute antes de que main()
se ingrese, x
siendo la prioridad la opción opcional (menor significa que se ejecuta antes). Esto elimina la necesidad de punteros de función, lo que nos permite descartar una macro, algunas declaraciones y la cadena de llamada.
Usar __LINE__
para prioridad es dudoso, ya que los niveles de prioridad 0-100 están reservados. Sin embargo, no genera errores, solo advertencias, y son abundantes cuando se juega al golf, entonces, ¿qué más?
Hubiera ayudado a eliminar otra columna para no usar prioridades en absoluto, pero el orden de ejecución no parece estar definido. (Se invierten en este caso, pero otras pruebas no son concluyentes).
Ejemplo de L v2 aquí
Enfoque más antiguo y 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;} //
Uno de mis problemas favoritos que he resuelto en este sitio.
Comencé imaginando que cada bloque adivinaría sus propias coordenadas de alguna manera. Las filas son fáciles __LINE__
y el número de bloques adyacentes horizontalmente se puede encontrar usando la longitud de un literal de cadena, de esta manera:
char*s=//char*s=//
" "" "
; ;
Tome la longitud de la cadena resultante y divida por un número apropiado y tendrá el ancho. Lamentablemente, cualquier espacio vacío antes del bloque es invisible por este método. Todavía sospechosos cuerdas sería la solución, ya que los espacios en blanco sólo tiene sentido fuera de cadenas muy raramente, en cosas como a+++b
vs a+ ++b
. Brevemente consideré algo así, pero no se me ocurrió nada útil. Otra posibilidad habría sido permitir que los identificadores se "peguen" juntos donde se encuentran los bloques:
A BA B
No me sorprendería si esto todavía podría ser una solución interesante.
A pesar de su simplicidad, me llevó bastante tiempo encontrar la solución de cadena, que se basa en este fragmento de bloque:
s=//
" \
";//
Si el fragmento no tiene vecinos horizontales, la nueva línea en la segunda línea se escapa por la barra invertida, creando una cadena de longitud 2. Sin embargo, si tiene un vecino, la barra invertida escapará de la comilla al comienzo de la línea 2 del siguiente bloque:
s=//s=//
" \" \
";//";//
Esto creará la cadena "\" "de longitud 5.
Más importante aún, esto también permite la detección de espacio vacío antes del bloque:
s=//
" \
";//
Nuevamente, la nueva línea se escapa y el espacio en blanco del bloque vacío a la izquierda se incluye en la cadena resultante "" de longitud 6.
En total, hay siete configuraciones diferentes de bloques en una fila de las que debemos preocuparnos, y todas ellas forman cadenas de longitudes únicas:
2 " "
---
s=//
" \
";//
5 " \" "
---
s=//s=//
" \" \
";//";//
6 " "
---
s=//
" \
";//
9 " \" "
----
s=//s=//
" \" \
";//";//
10 " "
---
s=//
" \
";//
8 " \" \" "
---
s=//s=//s=//
" \" \" \
";//";//";//
11 " \" \" \" "
----
s=//s=//s=//s=//
" \" \" \" \
";//";//";//";//
Los bloques finales, por supuesto, no tendrán una longitud tan corta, pero el principio es el mismo independientemente del tamaño del bloque. Esto también tiene la ventaja de que no es necesario un mecanismo separado para detectar el ancho. Al agregar un carácter correspondiente a la longitud de esta cadena a una cadena de resultados, cada una de las 19 configuraciones produce una cadena única, que solo necesita ser comparada con una lista adecuada una vez que se han ejecutado todos los bloques.
Una vez que esto se resolvió, el siguiente gran problema fue cómo "visitar" cada fila de bloques. En C, estamos muy limitados a lo que se puede hacer fuera de las funciones. También necesitamos main()
aparecer, pero solo una vez. Esto último se logra fácilmente por algunos #define
s, pero si queremos que el código de los bloques posteriores esté dentro main()
, el problema de cómo saber cuándo colocar el corchete final de cierre. Después de todo, no sabemos cuántas filas de bloques se usarán realmente. Por lo tanto, necesitamos tener main()
estática y, de alguna manera, el resto para ser dinámicos.
Si las otras filas de bloques deben ser autocontenidas, deben ser funciones, pero debemos asegurarnos de que cada función tenga un nombre que sea único y, al mismo tiempo, sea lo suficientemente predecible como para ser invocable main()
. También necesitamos un mecanismo para saber qué funciones están realmente allí para ser llamadas. La generación de nombres únicos se resuelve mediante macros de ayuda:
#define M(a,b) a##b //
#define W(z,x)M(z,x) //
#define F W(f,__LINE__) //
#define A W(a,__LINE__) //
Llamar F
creará un identificador cuyo nombre comienza con una f y termina con el número de línea. A
hace lo mismo pero con un prefijo as, que se usa para la segunda parte de la solución, que es punteros de función. Declaramos cuatro de estos indicadores:
typedef(*T)();T a17,a36,a55,a74;
Como se declaran como variables globales, se establecen convenientemente en NULL. Más adelante, cada fila de bloques tendrá el siguiente código:
F();T A=F;F()
Esto declarará primero una función, definirá el puntero de función apropiado para apuntar a esa función (solo podemos definir globales una vez, pero la declaración anterior no contaba como una definición, incluso si se inicializaba a NULL), y luego definía el real función. Esto permite main()
llamar a cualquier puntero de función que no sea NULL (a17 nunca será NULL):
a17(),a36&&a36(),a55&&a55(),a74&&a74()
Al hacerlo, se generará la cadena r
, que luego se busca en la tabla de cadenas y, si se encuentra, se genera la letra correspondiente.
El único truco que queda es que la lista de cadenas con las que se debe coincidir se acorta cada vez que se puede evitar la ambigüedad o se pueden combinar cadenas superpuestas.
Ejemplo de L v2 aquí