I completed the final exercise from chapter 11 in the book "Learning
Java through Games by Lubomir Stanchev".
I worked under the assumption that a block will only collide with another block or the edges of the screen if its not hidden, and I used blocks because after the Tetris shape hits the bottom of the screen, or another block, the Tetris(composed by four blocks) explodes(like in AutoCAD explode feature) and every block contained in the Tetris will be saved in an ArrayList that gets constantly iterated in search for a completed row. This was my way of implementing it, using what I've learned so far about OOP.
The main class, that just adds the frame and runs the program.
The Frame class, that adds the panel and sets the size of the window and the playing area.
The Block class. Since a Tetris shape contains four blocks, I created this class, attempting a bottoms-up design. Only thing tricky about this class are the virtual future block methods, in which I return a block with new coordinates in the direction I want to move it(left, right or down), in order to intersect the virtual block with any other block before they overlap each other.
The TetrisShape class, which is the abstract class from which all other shapes will be created. This class works a lot like the Block class, only 10 times more difficult, because it works with a two dimension array of blocks. The same methods as the Block class just iterate the array and call the Block methods with the same name, like, hide(), draw(), move(), etc.. The intersect method iterates all the active blocks in the TetrisShape and checks if any of them intersect with another Block (not another TetrisShape, because the Tetris will be transformed into blocks after the player no longer controls them). I spend a lot of time in this class, because of a shallow copy problem with the virtual future Tetris shapes, I tried to used the cloneable interface but failed, so I tried something easier: iterate the Tetris, looking for all the blocks that are active, and save them into an ArrayList, then, I can change the coordinates of the blocks, moving them in any direction.. Created methods that receive an ArrayList to work around the shallow copy issue. Also, for the rotation of the Tetris, I rotate it, then I save the active blocks in a new ArrayList, and return the original Tetris to its previous rotation. I then use the copy of the future rotation of the Tetris to check for collisions. All Tetris share the same methods, except the ones that display and rotate the shape, so I made those abstract.
The TetrisShapeI class, which was the first I implemented and also the hardest, since its the largest one. Since I'm working with a 4x4 array even when the shape is only 1x4 or 4x1, I had to spend a little time on the method to rotate the shape, in order to prevent the shape from rotating and going out of bounds or colliding with another block.
The TetrisShapeT class
The TetrisShapeO class
The TetrisShapeL class
The TetrisShapeJ class
The TetrisShapeS class
The TetrisShapeZ class
The panel class, where the game loop is and all the methods and variables to make it work.
I invested about 30 hours to get this working, because I got stuck getting the collisions to work properly and the method to delete a line and move all the blocks above.. after the first shape was working, implementing the other 6 took me like 20 minutes. And they all worked well, in fact I could expand the game and add any 4x4 or smaller shape and it will work. If I were to improve this program, I would change the draw method in the Block class and choose a better looking block, maybe add an image and use that instead. Also, the game has no sound, does native Java handle sounds? either way, its out of my league at the moment lol.