Programming Tetris Blocks (Literally)

33

2

In the game Tetris, there are 7 types of bricks or Tetriminoes, which are mathematically known as tetrominoes because they are all made with 4 square segments:

Tetris bricks

The have the names I, J, L, O, S, T, and Z, that correspond to their approximate shapes. Counting 90° rotations, there are 19 unique shapes in total:

I
I
I
I

IIII

 J
 J
JJ

JJJ
  J

JJ
J
J

J
JJJ

L
L
LL

  L
LLL

LL
 L
 L

LLL
L

OO
OO

 SS
SS

S
SS
 S

TTT
 T

T
TT
T

 T
TTT

 T
TT
 T

ZZ
 ZZ

 Z
ZZ
Z

Challenge

Write a rectangular block of code that acts as the base segment these 19 shapes are made from. When this code is arranged into one of the shapes, a program should be formed that outputs the single uppercase letter associated with that shape. This must work for all 19 shapes.

The leading empty areas present in some of the 19 shapes are filled in entirely with spaces (). The trailing empty areas are not filled with anything (so the programs are not always exactly rectangular).

Example

Suppose this was your code block:

ABC
123

Then either arrangement of the block into the S Tetris piece would be a program that prints S:

   ABCABC
   123123
ABCABC
123123
ABC
123
ABCABC
123123
   ABC
   123

(Notice that all leading empty space is filled with space characters, and that no lines have any trailing spaces.)

The same idea applies to all 6 other pieces and their respective rotations.

Notes

  • All 19 final programs are to be run in the same programming language.
  • If desired, you may add a single trailing newline to all programs (not just some, all or none).
  • Your code block may contain any characters (including spaces) that aren't line terminators.
  • Output the letter to stdout (or your language's closest alternative) with an optional trailing newline.

Scoring

The submission whose code block has the smallest area (width times height) wins. This essentially means the shortest code wins, which is why this is tagged . Tiebreaker goes to the highest voted answer.

The ABC\n123 example has area 3×2 = 6.

Snippet

Given a code block, this snippet will generate all 19 programs:

<script>function drawShape(X,n,v){for(var t="",e=0;e<v.length;e++)for(var l=0;l<n.length;l++){for(var r=0;r<v[e].length;r++)t+="X"===v[e][r]?n[l]:X[l];t+="\n"}return t}function go(){var X=document.getElementById("input").value;if(0!=X.length){var n=X.replace(/./g," ").split("\n");X=X.split("\n");for(var v="I (v1):|I (v2):|J (v1):|J (v2):|J (v3):|J (v4):|L (v1):|L (v2):|L (v3):|L (v4):|O:|S (v1):|S (v2):|T (v1):|T (v2):|T (v3):|T (v4):|Z (v1):|Z (v2):".split("|"),t="X\nX\nX\nX|XXXX| X\n X\nXX|XXX\n  X|XX\nX\nX|X\nXXX|X\nX\nXX|  X\nXXX|XX\n X\n X|XXX\nX|XX\nXX| XX\nXX|X\nXX\n X|XXX\n X|X\nXX\nX| X\nXXX| X\nXX\n X|XX\n XX| X\nXX\nX".split("|"),e="",l=0;l<v.length;l++)e+=v[l]+"\n\n"+drawShape(n,X,t[l].split("\n"))+"\n";e=e.substring(0,e.length-2),document.getElementById("output").value=e}}</script><style>html *{font-family: monospace;}</style>Code Block:<br><textarea id='input' rows='8' cols='64'>ABC&#010;123</textarea><br><button type='button' onclick='go()'>Go</button><br><br>All 19 Programs:<br><textarea id='output' rows='24' cols='64'></textarea>

Calvin's Hobbies

Posted 2015-04-11T06:45:16.480

Reputation: 84 000

So the length-width ratio is 2 to 3? Or can it be any other size? Also, what does the program have to do, at a minimum? Assuming empty programs don't count, but programs that output nothing do. – ASCIIThenANSI – 2015-04-11T14:42:05.450

@ASCIIThenANSI Any width and height are fine. I imagine something larger than 2*3 will be necessary. There are 19 programs, one for each arrangement of the block into one of the 19 distinct tetromino shapes. When one of those programs is run, it outputs the corresponding tetris piece letter. – Calvin's Hobbies – 2015-04-11T17:04:22.383

Wow! What an awesome challenge! Does it matter which language we use? – theonlygusti – 2015-04-12T09:22:18.270

@theonlygusti Almost all questions on this site allow any language. This is no exception. – Calvin's Hobbies – 2015-04-12T09:24:12.287

@Calvin'sHobbies Yeah, I know; I just misinterpreted the snippet as a controller for running JavaScript-answers. Apparently it just arranges code blocks. – theonlygusti – 2015-04-12T09:26:27.193

idk, but I am having fun just replacing the characters in your code block with words lol – takintoolong – 2018-12-02T06:43:03.590

Answers

16

<>< (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

Thijs ter Haar

Posted 2015-04-11T06:45:16.480

Reputation: 752

14

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

gastropner

Posted 2015-04-11T06:45:16.480

Reputation: 3 264

6

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

l4m2

Posted 2015-04-11T06:45:16.480

Reputation: 5 985

If you are comfortable reducing the number of supported environments somewhat, you can assume SI = 100h and use that register instead of BX for indexing, to save 3 bytes by dropping mov bx, 100h at the start. – gastropner – 2018-12-01T20:09:53.777

@gastropner Done and fixed a point where I didn't notice – l4m2 – 2018-12-02T04:56:09.750