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()з’явитися, але лише один раз. Останнє легко досягається деякими #defines, але якщо ми хочемо, щоб код наступних блоків знаходився всередині 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робить те ж саме, але з префіксом, який використовується для другої частини рішення, яка є покажчиками функції. Ми оголошуємо чотири таких покажчики:
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 тут