Tetris! Final heights (Day 3) Challenge Taken from my university code challenge contest

This is actually Day 0 but yesterdays' challenge was too easy and can be a dupe of another question here.



Tetris is a video game that became popular in the 80s. It consists of placing a series of pieces with different shapes that fall on a board, so that they fit in the most compact way possible.

In this problem we will assume a sequence of pieces that fall, each in a certain position and with a certain orientation that can not be changed. The pieces are piled up as they fall and the complete rows are not eliminated (as in the original game). The objective is to determine the final height of each column of the board after all the pieces fall.

There are a total of 7 different pieces, shown in the figure:



Challenge

Given a list of pieces, output the height of all the columns from the board after all pieces falls

A piece consists of three numbers: I, R and P. The first number, I, is the identifier of the piece (a number between 1 and 7, in the same order as in the figure). The second number, R, is the rotation of the piece. It can take the values ​​0, 90, 180 or 270 and represents the angle of rotation of the piece in the anti-clockwise direction. The third number, P, indicates the position of the piece. Represents the column on the left occupied by the piece (this can be 1 or 0 Index. Please specify).

Example and Test case (1 Index)


Given [[1, 0, 1], [4, 0, 1], [5, 90, 4]]





Output [3, 3, 1, 3, 2]





Given [[6, 270, 4], [1, 180, 5], [1, 90, 6], [7, 0, 4]]





Output [0, 0, 0, 9, 9, 8, 3, 3]





Given [[3,0,1],[3,180,3]] Output [1,1,4,4,4]
Given [[2,180,1],[2,0,3]] Output [2,2,4,3,3]


Notes


This is code-golf
Row/Column can be 1 or 0 Index. Please specify.
You can redefine input values (maybe you want to call piece 1 as A, etc..). In that case please specify


Questions


Can we use any 4 distinct values instead of an angle in degrees?: Yes
Are we supposed to handle "holes" if a piece doesn't exactly fit over the previous ones?: Yes
Is the height or the width of the board bounded? No. Neither the width nor the height are bounded




Thanks @Arnauld for the images and the test cases *.*
JavaScript (Node.js),  286 284 270  266 bytes
Pieces and positions are 0-indexed. Angles are in \$[0..3]\$.

a=&gt;a.map(([p,r,x])=&gt;(g=y=&gt;y&gt;3?g(+!Y--):b[Y+y]&amp;(m[y]=('0x'+`717433667233ff4717333327661${1e12+0x5e7056a566ffff57efa65n.toString(4)}`[(p*2+r*56+y*99+13)%113])&lt;&lt;x)?m.map(v=&gt;(g=x=&gt;v&amp;&amp;g(x+1,H[x]=v&amp;1?Y:~~H[x],v&gt;&gt;=1))(0,b[++Y]|=v)):g(y+1))(Y=a.length*4),m=[b=[-1]],H=[])&amp;&amp;H

Try it online!
or try an enhanced version that also displays the final board.
Shape encoding
All pieces are stored as exactly 4 nibbles (4x4 bits) with the rows sorted in reverse order and the leftmost pixel mapped to the least significant bit. In other words, the binary representation of the shape is mirrored both vertically and horizontally.
Example:

Hash function and lookup table
Given a piece \$p \in [0..6]\$, a rotation \$r \in [0..3]\$ and a row \$y \in [0..3]\$, we use the following hash function to get the index \$n\$ of the corresponding bitmask from a lookup table:
$$n=(2p+56r+99y+13)\bmod 113$$
Only the first \$82\$ entries are explicitly stored. Everything else is set to \$0\$.
These entries are packed as:
`717433667233ff4717333327661${1e12+0x5e7056a566ffff57efa65n.toString(4)}`

which expands to the following 82 nibbles:
&quot;717433667233ff47173333276611000000000000113213001112221112123333333311133233221211&quot;

Using hexadecimal in the final format is only required for the two horizontal representations of the \$I\$ piece, hence the &quot;ff&quot; in the above string.
The parameters of the hash function were brute-forced in a way that optimizes leading and trailing zeros. The fact that the string can be compressed some more by using 1e12 for the zeros in the middle and a conversion from base-16 to base-4 for the right part is just a welcome but unexpected side effect. :-)
Here is a demonstration of the unpacking process for all pieces and all rotations.
Commented
a =&gt; a.map(([p, r, x]) =&gt; (     // for each piece p with rotation r and position x:
  g = y =&gt;                      //   g = recursive function taking y
    y &gt; 3 ?                     //   if y is greater than 3:
      g(+!Y--)                  //     reset y to 0, decrement Y and try again
    :                           //   else:
      b[Y + y] &amp; (              //     test if we have a collision of the board with
        m[y] =                  //     the y-th row m[y] of the current piece
          ('0x' + `717...`[     //     which is extracted from a lookup table
            (p * 2 + r * 56 +   //     using the hash function described in the
             y * 99 + 13) % 113 //     previous paragraph
          ]) &lt;&lt; x               //     and shifted to the left according to x
      ) ?                       //     if we have a collision:
        m.map(v =&gt; (            //       we iterate again on the piece rows stored in m[]
          g = x =&gt;              //         g = recursive function taking x
            v &amp;&amp;                //         if v is not equal to 0:
            g(                  //           do a recursive call:
              x + 1,            //             increment x
              H[x] =            //             update the height at x:
                v &amp; 1 ?         //               if this bit is set:
                  Y             //                 set it to Y
                :               //               else:
                  ~~H[x],       //                 leave it unchanged or force it to 0
                                //                 if it was still undefined
              v &gt;&gt;= 1           //             shift v to the right
            )                   //           end of recursive call
          )(0,                  //         initial call to g with x = 0
               b[++Y] |= v)     //         increment Y and copy the piece row to the board
        )                       //     end of map()
      :                         //   else (no collision):
        g(y + 1)                //     do a recursive call to test the next row
  )(Y = a.length * 4),          //   initial call to g with y = Y = 4 * the number of pieces
                                //   (assuming the worst case: piled vertical I pieces)
  m = [b = [-1]], H = []        //   initialize m[], b[] and H[]
                                //   we set a full line at the bottom of b[]
) &amp;&amp; H                          // end of map(); return H[]