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;}//
Niedawno zostałem poinformowany o atrybutach funkcji GNU, a co najciekawsze, o constructor
atrybucie, który pozwala na bardziej zwięzłą implementację tego, co robiłem w bardziej okrągły sposób w moim wcześniejszym podejściu do tego problemu.
Idea tego samego pomysłu jest taka sama jak poprzednio: Zbuduj ciąg i wyszukaj go na liście, aby określić, który blok tetris jest ułożony jako kod. Odbywa się to przez wywołanie funkcji, z których każda dodaje znak do łańcucha. Powikłaniem było i pozostaje, że liczba funkcji jest różna.
Zdefiniowanie funkcji za pomocą attribute((constructor(x)))
powoduje, że funkcja jest uruchamiana przed main()
wprowadzeniem, z opcjonalnym x
priorytetem (niższy oznacza, że jest uruchamiany wcześniej). Eliminuje to potrzebę używania wskaźników funkcji, co pozwala nam upuszczać makro, niektóre deklaracje i łańcuch wywołań.
Użycie __LINE__
priorytetu jest niepewne, ponieważ poziomy priorytetu 0–100 są zarezerwowane. Nie powoduje to jednak błędów, a jedynie ostrzeżenia, które są obfite podczas gry w golfa, więc co jeszcze?
Pomogłoby to ogolić kolejną kolumnę, aby w ogóle nie używać priorytetów, ale kolejność wykonywania nie wydaje się być określona. (W tym przypadku są one odwrócone, ale inne testy są niejednoznaczne).
Przykład L v2 tutaj
Starsze, bardziej przenośne podejście
#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;} //
Jeden z moich ulubionych problemów, które rozwiązałem na tej stronie.
Zacząłem od stwierdzenia, że każdy blok w jakiś sposób wyznaczy własne współrzędne. Rzędy są łatwe __LINE__
, a liczbę sąsiadujących poziomo bloków można znaleźć, używając długości literału łańcuchowego, tak:
char*s=//char*s=//
" "" "
; ;
Weź długość wynikowego łańcucha i podziel przez odpowiednią liczbę, a uzyskasz szerokość. Niestety ta pusta przestrzeń przed blokiem jest niewidoczna. Wciąż podejrzewa ciągi byłoby rozwiązanie, ponieważ odstępy tylko ma sens poza strun bardzo rzadko, w rzeczy jak a+++b
vs. a+ ++b
. Przez chwilę zastanawiałem się nad czymś takim, ale nie mogłem znaleźć niczego przydatnego. Inną możliwością byłoby umożliwienie „sklejenia” identyfikatorów w miejscu, w którym spotykają się bloki:
A BA B
Nie zdziwiłbym się, gdyby to wciąż mogło stanowić ciekawe rozwiązanie.
Pomimo swojej prostoty zajęło mi sporo czasu znalezienie rozwiązania łańcuchowego, które opiera się na tym fragmencie bloku:
s=//
" \
";//
Jeśli fragment nie ma poziomych sąsiadów, nowa linia w drugiej linii jest poprzedzana ukośnikiem odwrotnym, tworząc ciąg długości 2. Jeśli jednak ma sąsiad, odwrotny ukośnik zamiast tego unika znaku cudzysłowu na początku linii 2 następnego bloku:
s=//s=//
" \" \
";//";//
Spowoduje to utworzenie ciągu „\” „o długości 5.
Co ważniejsze, pozwala to również na wykrycie pustej przestrzeni przed blokiem:
s=//
" \
";//
Znów znak nowej linii jest zastępowany, a spacja pustego bloku po lewej stronie jest zawarta w wynikowym ciągu „” o długości 6.
W sumie istnieje siedem różnych konfiguracji bloków w rzędzie, o które musimy się martwić, i wszystkie tworzą ciągi o unikalnej długości:
2 " "
---
s=//
" \
";//
5 " \" "
---
s=//s=//
" \" \
";//";//
6 " "
---
s=//
" \
";//
9 " \" "
----
s=//s=//
" \" \
";//";//
10 " "
---
s=//
" \
";//
8 " \" \" "
---
s=//s=//s=//
" \" \" \
";//";//";//
11 " \" \" \" "
----
s=//s=//s=//s=//
" \" \" \" \
";//";//";//";//
Ostateczne bloki oczywiście nie będą miały tak krótkiej długości, ale zasada jest taka sama niezależnie od wielkości bloku. Ma to również tę zaletę, że oddzielny mechanizm wykrywania szerokości jest niepotrzebny. Dodając znak odpowiadający długości tego ciągu do ciągu wyników, każda z 19 konfiguracji daje unikalny ciąg, który wystarczy porównać z odpowiednią listą po uruchomieniu wszystkich bloków.
Po posortowaniu, kolejnym dużym problemem było to, jak „odwiedzić” każdy rząd bloków. W C jesteśmy bardzo ograniczeni do tego, co można zrobić poza funkcjami. Musimy się także main()
pojawić, ale tylko raz. To drugie jest łatwo osiągalne przez niektóre #define
s, ale jeśli chcemy, aby kod kolejnych bloków był w środku main()
, problem, jak się dowiedzieć, kiedy umieścić końcowy nawias zamykający. W końcu nie wiemy, ile wierszy bloków faktycznie zostanie wykorzystanych. Musimy więc mieć main()
statyczne, a reszta być dynamiczna.
Jeśli pozostałe wiersze bloków mają być samowystarczalne, muszą to być funkcje, ale musimy upewnić się, że każda funkcja ma unikalną nazwę, a jednocześnie jest wystarczająco przewidywalna, aby można ją było wywoływać main()
. Potrzebujemy również mechanizmu rozpoznawania, które funkcje są rzeczywiście wywoływane. Generowanie unikalnych nazw rozwiązują makra pomocnicze:
#define M(a,b) a##b //
#define W(z,x)M(z,x) //
#define F W(f,__LINE__) //
#define A W(a,__LINE__) //
Wywołanie F
spowoduje utworzenie identyfikatora, którego nazwa zaczyna się na literę f, a kończy na numerze linii. A
robi to samo, ale z prefiksem as, który jest używany dla drugiej części rozwiązania, czyli wskaźników funkcji. Deklarujemy cztery takie wskaźniki:
typedef(*T)();T a17,a36,a55,a74;
Ponieważ są one zadeklarowane jako zmienne globalne, wygodnie są ustawione na NULL. Później każdy wiersz bloku będzie zawierał następujący fragment kodu:
F();T A=F;F()
To najpierw zadeklaruje funkcję, zdefiniuje odpowiedni wskaźnik funkcji wskazujący na tę funkcję (możemy zdefiniować globały tylko raz, ale wcześniejsza deklaracja nie liczyła się jako definicja, nawet jeśli zainicjowała wartość NULL), a następnie zdefiniowała rzeczywistą funkcjonować. Pozwala main()
to wywołać dowolny wskaźnik funkcji, który nie jest NULL (a17 nigdy nie będzie NULL):
a17(),a36&&a36(),a55&&a55(),a74&&a74()
Spowoduje to zbudowanie ciągu znaków r
, który jest następnie szukany w tabeli ciągów i jeśli zostanie znaleziony, wyświetlana jest odpowiednia litera.
Jedyną sztuczką jest to, że lista pasujących ciągów znaków została skrócona, ilekroć można było uniknąć dwuznaczności, lub nakładających się łańcuchów można połączyć.
Przykład L v2 tutaj