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;}//
Недавно мне сообщили об атрибутах функций GNU и, что наиболее интересно, об constructor
атрибуте, который позволяет более кратко реализовать то, что я делал, более окольным путем в моем более раннем подходе к этой проблеме.
Суть идеи та же, что и раньше: создайте строку и найдите ее в списке, чтобы определить, в каком блоке тетриса выложен код. Это делается путем вызова функций, каждая из которых добавляет символ в строку. Осложнение было и остается, что количество функций варьируется.
Определение функции с помощью attribute((constructor(x)))
делает так, чтобы функция запускалась до того, main()
как была введена, с необязательным, x
являющимся приоритетом (ниже означает, что она запускается раньше). Это устраняет необходимость в указателях функций, что позволяет нам удалять макрос, некоторые объявления и цепочку вызовов.
Использование __LINE__
для приоритета ненадежно, поскольку уровни приоритета 0-100 зарезервированы. Тем не менее, это не приводит к ошибкам, а только к предупреждениям, которых много в гольфе, так что еще?
Это помогло бы сбрить еще один столбец, чтобы вообще не использовать приоритеты, но порядок выполнения, похоже, не определен. (В этом случае они меняются местами, но другие тесты не дают результатов.)
Пример L v2 здесь
Старый, более портативный, подход
#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;} //
Одна из моих любимых проблем, которую я решил на этом сайте.
Я начал с того, что подумал, что каждый блок будет как-то определять свои собственные координаты. С строками легко __LINE__
работать, а количество горизонтально смежных блоков можно найти, используя длину строкового литерала, например, так:
char*s=//char*s=//
" "" "
; ;
Возьмите длину полученной строки и разделите на правильное число, и вы получите ширину. К сожалению, этот метод невидит любое пустое пространство перед блоком. Я до сих пор подозреваемые строка будет решением, так как пробельные имеет смысл только вне строк очень редко, в таких вещах , как a+++b
против прогноза a+ ++b
. Я кратко обдумал что-то подобное, но не смог придумать ничего полезного. Другой возможностью было бы позволить идентификаторам «склеиваться» там, где встречались блоки:
A BA B
Я не удивлюсь, если это все еще может сделать интересное решение.
Несмотря на свою простоту, мне потребовалось довольно много времени, чтобы найти решение для строки, основанное на этом фрагменте блока:
s=//
" \
";//
Если фрагмент не имеет горизонтальных соседей, символ новой строки во второй строке экранируется обратной косой чертой, создавая строку длиной 2. Однако, если у него есть сосед, обратная косая черта вместо этого будет выходить из знака кавычки в начале строки. 2 следующего блока:
s=//s=//
" \" \
";//";//
Это создаст строку "\" "длиной 5.
Что еще более важно, это также позволяет обнаруживать пустое пространство перед блоком:
s=//
" \
";//
Снова, новая строка экранируется, и пробел слева от пустого блока включается в результирующую строку "" длиной 6.
Всего в строке есть семь разных конфигураций блоков, о которых нам нужно беспокоиться, и все они создают строки уникальной длины:
2 " "
---
s=//
" \
";//
5 " \" "
---
s=//s=//
" \" \
";//";//
6 " "
---
s=//
" \
";//
9 " \" "
----
s=//s=//
" \" \
";//";//
10 " "
---
s=//
" \
";//
8 " \" \" "
---
s=//s=//s=//
" \" \" \
";//";//";//
11 " \" \" \" "
----
s=//s=//s=//s=//
" \" \" \" \
";//";//";//";//
Конечные блоки, конечно, не будут иметь такой короткой длины, но принцип будет одинаковым независимо от размера блока. Это также имеет преимущество в том, что отдельный механизм для определения ширины не нужен. Добавляя символ, соответствующий длине этой строки, в строку результатов, каждая из 19 конфигураций дает уникальную строку, которую нужно сравнивать только с подходящим списком после запуска всех блоков.
Как только это было отсортировано, следующая большая проблема заключалась в том, как «посетить» каждый ряд блоков. В C мы очень ограничены тем, что можно сделать вне функций. Нам тоже нужно main()
появиться, но только один раз. Последнее легко достигается некоторыми #define
s, но если мы хотим, чтобы код последующих блоков был внутри main()
, проблема в том, как узнать, когда ставить заключительную закрывающую фигурную скобку. В конце концов, мы не знаем, сколько строк блоков будет фактически использовано. Таким образом, мы должны быть main()
статичными, а остальное - динамичным.
Если другие блочные строки должны быть автономными, они должны быть функциями, но мы должны убедиться, что каждая функция имеет уникальное имя, а также достаточно предсказуемое, чтобы его можно было вызывать main()
. Нам также нужен механизм для того, чтобы знать, какие функции на самом деле должны быть вызваны. Генерация уникальных имен решается с помощью вспомогательных макросов:
#define M(a,b) a##b //
#define W(z,x)M(z,x) //
#define F W(f,__LINE__) //
#define A W(a,__LINE__) //
Вызов F
создаст идентификатор, имя которого начинается с f и заканчивается номером строки. A
делает то же самое, но с префиксом as, который используется для второй части решения - указателей на функции. Мы объявляем четыре таких указателя:
typedef(*T)();T a17,a36,a55,a74;
Поскольку они объявлены как глобальные переменные, для них удобно установить значение NULL. Позже каждый блок-ряд будет иметь следующий фрагмент кода:
F();T A=F;F()
Сначала будет объявлена функция, определен соответствующий указатель на функцию для указания на эту функцию (мы можем определить глобальные переменные только один раз, но более раннее объявление не считается как определение, даже если оно инициализировалось в NULL), а затем определим фактическую функция. Это позволяет main()
вызывать любой указатель на функцию, который не равен NULL (a17 никогда не будет NULL):
a17(),a36&&a36(),a55&&a55(),a74&&a74()
В результате будет построена строка r
, которую затем ищут в таблице строк, и, если она найдена, выводится соответствующая буква.
Единственная оставшаяся хитрость заключается в том, что список строк для сравнения сокращался всякий раз, когда можно было избежать неоднозначности или можно было бы совмещать перекрывающиеся строки.
Пример L v2 здесь