Given a list of Tetris moves, return the number of completed lines Description

We consider a slightly simplified version of Tetris where each move consists of:


rotating the piece clockwise, 0 to 3 times
positioning the piece at a given column
fast drop


The goal is to determine the number of completed lines, given a list of such Tetris moves.

Completed rows are removed as pieces are dropped, following standard Tetris rules.

Playfield

The playfield is 10-column wide. There's no Game Over and it is assumed that there's always enough space and time to perform the above actions, no matter the configuration of the playfield. The height of the playfield doesn't really matter here, but you can use the standard 22 rows as an upper limit.

Shapes of Tetrominoes



Input / Output

Input

A comma separated list of Tetris moves encoded with 3 characters. The first two character describe the Tetromino shape to use and the last one describes the position where it's dropped.


Tetromino: I, O, T, L, J, Z or S, in the same order as above.
Number of clockwise rotations: 0 to 3
Column: 0 to 9. This is the column in which the top-left corner of the piece (marked with an x on the above picture) is located after the rotation 1


It is assumed that all moves in the provided list are valid. There's no need to check for invalid entries such as I07 (horizontal I shape put too far on the right).

1 You are free to either implement a real rotation algorithm or to hardcode all the different shapes, as long as the x is located in the column given by the third character of the move.

Output

Number of completed lines.

Example



O00,T24 will generate the first position and O00,T24,S02,T01,L00,Z03,O07,L06,I05 will generate the second position.

Therefore, the following sequence will generate a Tetris and should return 4:

O00,T24,S02,T01,L00,Z03,O07,L06,I05,I19


Test cases

1) "O00,T24,S02,T01,L00,Z03,O07,L06,I05,I19" -&gt; 4
2) "S00,J03,L27,Z16,Z18,I10,T22,I01,I05,O01,L27,O05,S13" -&gt; 5
3) "I01,T30,J18,L15,J37,I01,S15,L07,O03,O03,L00,Z00,T38,T01,S06,L18,L14" -&gt; 4
4) "S14,T00,I13,I06,I05,I19,L20,J26,O07,Z14,Z10,Z12,O01,L27,L04,I03,S07,I01,T25,J23,J27,O01,
    I10,I10" -&gt; 8
5) "O00,T24,L32,T16,L04,Z11,O06,L03,I18,J30,L23,Z07,I19,T05,T18,L30,I01,I01,I05,T02" -&gt; 8


Test page

You can use this JSFiddle to test a move list.
C, 401 392 383 378 374 351 335 324 320 318 316 305 bytes

d[99]={'3Z3Z',983177049,513351321,1016270793,970073931,~0},*L=d+5,V,s;char c;main(x){B:for(c=0;c[++L]|V;V&gt;&gt;=4)c-=(L[c]=*L|(V&amp;15)&lt;&lt;x)&gt;1022;s-=c;if(scanf("%c%1d%d,",&amp;c,&amp;V,&amp;x)&gt;2)for(V=c^79?d[c/4%5]&gt;&gt;24-V*8:27,V=c^73?V/64%4&lt;&lt;8|V/8%8&lt;&lt;4|V%8:V&amp;32?15:4369;;--L)for(c=4;c--;)if(L[c]&gt;&gt;x&amp;V&gt;&gt;c*4&amp;15)goto B;return s;}


Takes comma-separated input on stdin, returns the score in the exit status.

Requires char to be signed (which is the default for GCC) and requires '3Z3Z' to be interpreted as 861549402 (which is the case for GCC on little endian machines, at least).



Usage example:

echo "O00,T24,S02,T01,L00,Z03,O07,L06,I05,I19" | ./tetris; echo "$?";




High-level explanation:

All shapes except the line can fit in a 3x3 grid with one corner missing:

6 7 -
3 4 5
0 1 2


That means it's easy to store them in a byte each. For example:

- - -         0 0 -
- # -   --&gt;   0 1 0   --&gt;   0b00010111
# # #         1 1 1


(we align each piece to the bottom-left of the box to make dropping it easier)

Since we get at least 4 bytes to an int, this means we can store all 4 rotations of each piece in a single integer, with a special case for the line. We can also fit each row of the game grid into an int (only needs 10 bits), and the currently falling piece into a long (4 lines = 40 bits).



Breakdown:

d[99]={             // General memory
  '3Z3Z',           //  Shape of "S" piece (multi-char literal, value = 861549402)
  983177049,        //  Shape of "T" piece
  513351321,        //  Shape of "Z" piece
  1016270793,       //  Shape of "J" piece
  970073931,        //  Shape of "L" piece
  ~0                //  Bottom of game grid (solid row)
},                  //  Rest of array stores lines in the game
*L=d+5,             // Working line pointer (start &gt;= base of grid)
V,                  // Current piece after expansion (also stores rotation)
s;                  // Current score (global to initialise as 0)
char c;             // Input shape identifier (also counts deleted lines)
main(x){            // "x" used to store x location
  B:                                // Loop label; jumps here when piece hits floor
  for(c=0;c[++L]|V;V&gt;&gt;=4)           // Paint latest piece &amp; delete completed lines
    c-=(L[c]=*L|(V&amp;15)&lt;&lt;x)&gt;1022;
  s-=c;                             // Increment score
  if(scanf("%c%1d%d,",&amp;c,&amp;V,&amp;x)&gt;2)  // Load next command
    for(V=c^79?                     // Identify piece &amp; rotation
          d[c/4%5]&gt;&gt;24-V*8          //  Not "O": load from data
        :27,                        //  Else "O": always 27
        V=c^73?                     // If not "I" (line):
          V/64%4&lt;&lt;8|V/8%8&lt;&lt;4|V%8    //  expand shape to 16-bits
        :                           // Else we are a line:
          V&amp;32?                     //  "I" coincides with "J"; use this to check
               15:4369;             //  horizontal/vertical
        ;--L)                       // Loop from top-to-bottom of grid
      for(c=4;c--;)                 //  Loop through rows of piece
        if(L[c]&gt;&gt;x&amp;V&gt;&gt;c*4&amp;15)       //   If collides with existing piece:
          goto B;                   //    Exit loops
  return s;                         // Return score in exit status
}




-4, -1 thanks to @Titus, and -23, -11 with inspiration from their answer