Programming Tetris Blocks (Literally)

<>< (Fish) - 12 * 32 = 384

I was planning to go for a more elegant solution, but I somehow ended up with this, which is pretty brute-force:

c  0  g84*%\
c2*0  g84*%\
0  84*g84*%\
c  84*g84*%\
c2*84*g84*%\
0  88*g84*%\
c  88*g84*%\
?v         \
;>?v~~?vv   
"L" o;  >   
"S" o; >~?v 
"T" o;    > 
;  >~?v"L"o 
;     >"J"o 
?v         \
 >~?v~~?vv  
"I"  o;  >  
"J"  o; >   
    \~~?vv  
"T"  o;  >  
"Z"  o; >   
?v         \
 >?v"J"o;   
   >?v"Z"o; 
"L"o;>?!v   
"J"o;   >?v 
"T"o;     > 
?v?v"I"o;  >
   >"L"o;   
 >?v"T"o;   
   >?v"O"o; 
     >"S"o; 

It's pretty simple, it checks the code in a 3x3 square for text and uses the results to see which tetrimino corresponds to the code's shape. I didn't take a lot of effort to golf it yet.

Try the code here (after using the snippet to shape it like a tetrimino)

Example of code in shape Z (v1) here


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;}//

I was recently informed of GNU's function attributes, and most interestingly the constructor attribute, which allows for a more terse implementation of what I was doing in a more roundabout way in my earlier approach to this problem.

The thrust of the idea is the same as before: Build a string and search for it in a list to identify which tetris block the code is laid out as. This is done by calling functions, each one adding a character to the string. The complication was and remains that the number of functions varies.

Defining a function with attribute((constructor(x))) makes it so that the function is run before main() is entered, with the optional x being the priority (lower means it is run earlier). This removes the need for function pointers, which allows us to drop a macro, some declarations, and the calling chain.

Using __LINE__ for priority is iffy, since priority levels 0-100 are reserved. However, it does not result in errors, only warnings, and those are plentiful when golfing, so what's a few more?

It would have helped shave another column off to not use priorities at all, but the order of execution does not seem to be defined. (They are reversed in this case, but other tests are inconclusive.)

Example of L v2 here

Older, more portable, approach

#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;}  //

One of my favourite problems I've solved on this site.

I began by figuring each block would divine its own coordinates somehow. Rows are easy with __LINE__, and the number of horizontally adjacent blocks could be found by using the length of a string literal, like so:

char*s=//char*s=//
"       ""       "
;        ;        

Take the length of the resulting string and and divide by a proper number and you have the width. Sadly, any empty space before the block is invisible by this method. I still suspected strings would be the solution, since whitespace only has meaning outside of strings very rarely, in things like a+++b vs. a+ ++b. I briefly considered something like that, but could not come up with anything useful. Another possibility would have been to let identifiers be "glued" together where blocks met:

A  BA  B

I would not be surprised if this could still make for an interesting solution.

Despite its simplicity, it took me quite some time to find the string solution, which is based on this block fragment:

s=//
"  \
";//

If the fragment has no horizontal neighbours, the newline on the second line is escaped by the backslash, creating a string of length 2. If, however, it does have a neighbour, the backslash will instead escape the quotion mark at the start of line 2 of the next block:

s=//s=//
"  \"  \
";//";//

This will create the string " \" " of length 5.

More crucially, this also allows for detection of empty space before the block:

    s=//
    "  \
    ";//

Again, the newline is escaped, and the whitespace of the empty block to the left is included in the resulting string " " of length 6.

In total there are seven different configurations of blocks on a row that we need to worry about, and they all make strings of unique lengths:

2 "  "
---
s=//
"  \
";//

5 "  \"  "
---
s=//s=//
"  \"  \
";//";//

6 "      "
---
    s=//
    "  \
    ";//

9 "  \"      "
----
    s=//s=//
    "  \"  \
    ";//";//

10 "          "
---
        s=//
        "  \
        ";//

8 "  \"  \"  "
---
s=//s=//s=//
"  \"  \"  \
";//";//";//

11 "  \"  \"  \"  "
----
s=//s=//s=//s=//
"  \"  \"  \"  \
";//";//";//";//

The final blocks will of course not have such short length, but the principle is the same regardless of block size. This also has the bonus that a separate mechanism for detecting width is unnecessary. By adding a character corresponding to the length of this string to a results string, each of the 19 configurations yields a unique string, that needs only be compared to a suitable list once all the blocks have been run.

Once this was sorted, the next big problem was how to "visit" each row of blocks. In C, we are very limited to what can be done outside of functions. We also need main() to appear, but only once. The latter is easily achieved by some #defines, but if we want the code of subsequent blocks to be inside of main(), the problem of how to know when to put the final closing curly bracket. After all, we don't know how many rows of blocks will actually be used. So we need to have main() static and somehow the rest to be dynamic.

If the other block-rows are to be self-contained, they need to be functions, but we need to make sure each function has a name that is unique, while also being predictable enough to be callable from main(). We also need a mechanism for knowing which functions are actually there to be called. Generating unique names is solved by helper macros:

#define M(a,b) a##b     //
#define W(z,x)M(z,x)    //
#define F W(f,__LINE__) //
#define A W(a,__LINE__) //

Calling F will create an identifier whose name begins with an f and ends with the line number. A does the same but with a as prefix, which is used for the second part of the solution, which is function pointers. We declare four such pointers:

typedef(*T)();T a17,a36,a55,a74;

Since these are declared as global variables, they are conveniently set to NULL. Later on, each block-row will have the following piece of code:

F();T A=F;F()

This will first declare a function, define the appropriate function pointer to point to that function (we can only define globals once, but the earlier declaration did not count as a definition, even if it did initialise to NULL), and then define the actual function. This allows main() to call any function pointer that is non-NULL (a17 will never be NULL):

a17(),a36&&a36(),a55&&a55(),a74&&a74()

Doing so will build the string r, which is then looked for in the table of strings and if found, the appropriate letter is output.

The only remaining trick is that the list of strings to match against were shortened whenever ambiguity could be avoided, or overlapping strings could be conflated.

Example of L v2 here


x86 opcode(.com), 86 82 bytes

Tester:

org 100h
macro e {
db $F6,$04,$DF,$78,$13,$75,$08,$00,$C0,$40,$83,$C6,$52,$EB,$F1,$88
db $C2,$00,$D0,$00,$D0,$46,$EB,$E8,$05,$02,$40,$73,$ED,$E8,$26,$00
db $50,$08,$43,$4D,$2C,$0C,$1C,$15,$A5,$14,$10,$13,$3F,$27,$20,$0F
db $51,$1D,$29,$49,$49,$4A,$4A,$4A,$4A,$4C,$4C,$4C,$4C,$4F,$53,$53
db $54,$54,$54,$54,$5A,$5A,$5F,$AE,$75,$FD,$8A,$55,$12,$B4,$02,$CD
db $21,$C3
}

macro n { db 82 dup $20 }

macro s { db 10 }

n
e
s
n
e
s
e
e  

Source:

BOF:    ;mov bx, 100h
p:      test [si], byte $DF
        js _a ; exist
        jnz _b ; newline
_z:     add al, al
        inc ax
q:      add si, EOF-BOF
        jmp p
_b:     mov dl, al
        add al, dl
        add al, dl
        inc si
        jmp p
_a:     add ax, 4002h
        jnc q
        call y
        db 80,8,67,77,44,12,28,21,165,20,16,19,63,39,32,15,81,29,41
        db 'IIJJJJLLLLOSSTTTTZZ'
y:      pop di
        scasb
        jnz y+1
        mov dl,[di+18]
        mov ah,2
        int $21
        ret
EOF:

Run in win7dos where init AX=0, SI=100, BX=0 References