#JavaScript (4 Tweets / 385 bytes)
Not quite sure about the requirements of the header & Tweets format, so please advise if it needs correcting. Still a bit more can be shaved off I'm certain.
###Tweet 1 - P0 (11 bytes) chrome a.js
###Tweet 2 - P1 (108 bytes) a=0,y=0,z=0,j=0,window.onkeyup=(b=>(40==(k=b.keyCode)?D():2z(d=37==k?2:39==k?.5:0)&65|jd&a||(z=d),R())),
###Tweet 3 - P2 (135 bytes) Z=(a=>(y=6,z=12/(new Date%3+1))),R=(b=>{j=z<<5*y;for(o="",i=30;i--;)o+=1<<i&(a|j)?"#":"_",o+=i%5?"":"
";document.body.innerHTML=o}),
###Tweet 4 - P3 (131 bytes) D=(b=>(!y||a&j>>5?(y>4?a=0:31^31&(a|=j)>>5y||(a=a>>5(y+1)<<5y|a&-1>>>5-(y+1)>>5),Z()):(y--,j<<=5),R())),setInterval(D,700),Z();
#Run it in JSFiddle: https://jsfiddle.net/CookieJon/pjd8gcqc/2/
(Click on the output pane to provide focus for keyboard events)
Readable Code
a = 0, // game board y = 0, // falling in row z = 0, // piece shape j = 0 // piece transposed to row Z = v => ( // Drop a new piece from top y = 6, z = 12/(new Date %3+1) // 4, 6, or 12 ) R = v => { // Render board j = z << y * 5 for (o = '', i = 30; i--;) { o += (1 << i) & (a | j) ? '#' : '_' o += i%5?'':'<br>' // ▥ box } document.body.innerHTML = o // document.getElementById('output').innerHTML = o } window.onkeyup = e => { // key up ((k = e.keyCode) == 40) ? D() : z*2* (d = k == 37 ? 2 : k == 39 ? .5 : 0) & 65 | j*d & a // L/R check for collision with board & boundaries. ? 0 // Can't move L/R : z*=d // Apply L/R move R() } // move down D = v => { !y || a & j >> 5 // Current y=0 or j>>5=1 ? ( y > 4 // top row? ? a = 0 // Game over : 16 ^ 16 & (a|=j) >> (y * 5) || (a = a >> (y+1) * 5 << y * 5 | a & ~0 >>> - (y+1) * 5 >> 5) // Remove matched rows from board ,Z() // Drop a fresh Z from top ) : (y--, j<<=5) // Move Z down R() } // start up setInterval(D, 700) Z()