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;}//
Saya baru-baru ini diberitahu tentang atribut fungsi GNU, dan yang paling menarik adalah constructor
atributnya, yang memungkinkan untuk implementasi yang lebih singkat dari apa yang saya lakukan dengan cara yang lebih bundaran dalam pendekatan saya sebelumnya untuk masalah ini.
Dorongan ide sama dengan sebelumnya: Bangun string dan mencarinya dalam daftar untuk mengidentifikasi blok tetris mana kode diletakkan. Ini dilakukan dengan memanggil fungsi, masing-masing menambahkan karakter ke string. Komplikasi itu dan tetap bahwa jumlah fungsi bervariasi.
Menentukan fungsi dengan attribute((constructor(x)))
membuatnya agar fungsi dijalankan sebelum main()
dimasukkan, dengan opsional x
menjadi prioritas (lebih rendah berarti dijalankan sebelumnya). Ini menghilangkan kebutuhan untuk pointer fungsi, yang memungkinkan kita untuk menjatuhkan makro, beberapa deklarasi, dan rantai panggilan.
Menggunakan __LINE__
untuk prioritas adalah rapuh, karena tingkat prioritas 0-100 dicadangkan. Namun, itu tidak menghasilkan kesalahan, hanya peringatan, dan itu banyak ketika bermain golf, jadi apa lagi?
Itu akan membantu mencukur kolom lain untuk tidak menggunakan prioritas sama sekali, tetapi urutan eksekusi tampaknya tidak didefinisikan. (Mereka terbalik dalam kasus ini, tetapi tes lain tidak dapat disimpulkan.)
Contoh L v2 di sini
Pendekatan yang lebih tua, lebih portabel,
#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;} //
Salah satu masalah favorit saya yang saya pecahkan di situs ini.
Saya mulai dengan mencari tahu setiap blok akan ilahi koordinat sendiri entah bagaimana. Barisnya mudah __LINE__
, dan jumlah blok yang berdekatan secara horizontal dapat ditemukan dengan menggunakan panjang string literal, seperti:
char*s=//char*s=//
" "" "
; ;
Ambil panjang string yang dihasilkan dan dan bagi dengan jumlah yang tepat dan Anda memiliki lebar. Sayangnya, ruang kosong sebelum blok tidak terlihat oleh metode ini. Saya masih menduga string akan menjadi solusi, karena spasi hanya memiliki makna luar string sangat jarang, dalam hal-hal seperti a+++b
vs. a+ ++b
. Saya secara singkat mempertimbangkan sesuatu seperti itu, tetapi tidak dapat menemukan sesuatu yang berguna. Kemungkinan lain adalah membiarkan pengidentifikasi "direkatkan" bersama-sama ketika blok bertemu:
A BA B
Saya tidak akan terkejut jika ini masih bisa menjadi solusi yang menarik.
Terlepas dari kesederhanaannya, butuh beberapa waktu untuk menemukan solusi string, yang didasarkan pada fragmen blok ini:
s=//
" \
";//
Jika fragmen tidak memiliki tetangga horizontal, baris baru pada baris kedua diloloskan oleh garis miring terbalik, membuat string dengan panjang 2. Jika, bagaimanapun, memiliki tetangga, garis miring terbalik akan lepas dari tanda queri di awal baris 2 dari blok selanjutnya:
s=//s=//
" \" \
";//";//
Ini akan membuat string "\" "dengan panjang 5.
Lebih penting lagi, ini juga memungkinkan deteksi ruang kosong sebelum blok:
s=//
" \
";//
Lagi-lagi, baris baru diloloskan, dan spasi putih dari blok kosong di sebelah kiri termasuk dalam string "" panjang 6 yang dihasilkan.
Total ada tujuh konfigurasi blok yang berbeda pada satu baris yang perlu kita khawatirkan, dan semuanya membuat string dengan panjang yang unik:
2 " "
---
s=//
" \
";//
5 " \" "
---
s=//s=//
" \" \
";//";//
6 " "
---
s=//
" \
";//
9 " \" "
----
s=//s=//
" \" \
";//";//
10 " "
---
s=//
" \
";//
8 " \" \" "
---
s=//s=//s=//
" \" \" \
";//";//";//
11 " \" \" \" "
----
s=//s=//s=//s=//
" \" \" \" \
";//";//";//";//
Blok akhir tentu saja tidak memiliki panjang yang pendek, tetapi prinsipnya sama terlepas dari ukuran blok. Ini juga memiliki bonus yang tidak diperlukan mekanisme terpisah untuk mendeteksi lebar. Dengan menambahkan karakter yang sesuai dengan panjang string ini ke string hasil, masing-masing dari 19 konfigurasi menghasilkan string yang unik, yang hanya perlu dibandingkan dengan daftar yang sesuai setelah semua blok dijalankan.
Setelah ini disortir, masalah besar berikutnya adalah bagaimana "mengunjungi" setiap baris blok. Dalam C, kami sangat terbatas pada apa yang bisa dilakukan di luar fungsi. Kita juga perlu main()
muncul, tetapi hanya sekali. Yang terakhir mudah dicapai oleh beberapa #define
s, tetapi jika kita ingin kode blok berikutnya berada di dalam main()
, masalah bagaimana mengetahui kapan harus menempatkan braket keriting penutupan akhir. Lagi pula, kita tidak tahu berapa banyak baris balok yang akan digunakan. Jadi kita perlu memiliki main()
statis dan entah bagaimana sisanya menjadi dinamis.
Jika baris-blok lainnya harus mandiri, mereka harus fungsi, tetapi kita perlu memastikan setiap fungsi memiliki nama yang unik, sementara juga cukup dapat diprediksi untuk dapat dipanggil dari main()
. Kita juga membutuhkan mekanisme untuk mengetahui fungsi mana yang sebenarnya dipanggil. Membuat nama-nama unik diselesaikan oleh makro pembantu:
#define M(a,b) a##b //
#define W(z,x)M(z,x) //
#define F W(f,__LINE__) //
#define A W(a,__LINE__) //
Memanggil F
akan membuat pengenal yang namanya dimulai dengan f dan diakhiri dengan nomor baris. A
melakukan hal yang sama tetapi dengan awalan sebagai, yang digunakan untuk bagian kedua dari solusi, yang merupakan pointer fungsi. Kami mendeklarasikan empat petunjuk tersebut:
typedef(*T)();T a17,a36,a55,a74;
Karena ini dinyatakan sebagai variabel global, mereka dengan mudah diatur ke NULL. Nantinya, setiap blok-baris akan memiliki bagian kode berikut:
F();T A=F;F()
Ini pertama-tama akan mendeklarasikan suatu fungsi, mendefinisikan pointer fungsi yang sesuai untuk menunjuk ke fungsi itu (kita hanya dapat mendefinisikan global sekali, tetapi deklarasi sebelumnya tidak dihitung sebagai definisi, bahkan jika itu diinisialisasi ke NULL), dan kemudian mendefinisikan aktual fungsi. Ini memungkinkan Anda main()
memanggil fungsi apa saja yang bukan NULL (a17 tidak akan pernah menjadi NULL):
a17(),a36&&a36(),a55&&a55(),a74&&a74()
Melakukannya akan membangun string r
, yang kemudian dicari dalam tabel string dan jika ditemukan, huruf yang sesuai adalah output.
Satu-satunya trik yang tersisa adalah bahwa daftar string yang cocok dengan itu dipersingkat setiap kali ambiguitas dapat dihindari, atau string yang tumpang tindih dapat digabungkan.
Contoh L v2 di sini