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