The most ridiculous thing about this is I am not a game developer of any sort. I've never programmed Tetris before. I probably haven't even played the game since the Gameboy original era more than 25 years ago. As a result, this absolutely demonstrates without the shadow of a doubt the superiority of Glimmer DSL for SWT as a desktop development GUI framework in Ruby. If I was able to make this game in one day, you might be able to make it in a few hours if you're a seasoned game developer. In any case, let's get into the code, shall we!
Top-level Glimmer GUI code:
Believe me when I tell you the code above is the cleanest Tetris implementation on Earth! In fact, the final part, which is the GUI body is only about 6 lines of code. This is testament to Glimmer DSL for SWT's great architecture and design.
That is thanks in part to Glimmer DSL for SWT's ultra-modularity. After all, Tetris was declared as a Custom Shell (meaning it is a reusable app that can run standalone or as a window in another Glimmer app). Additionally, the GUI was broken up into multiple Custom Widgets (`playfield` and `block`):
- Playfield: represents the play area of the GUI that has the Tetromino blocks falling in
- Block: represents a single square block
Architecture is MVC with a bit of MVP (or MVVM with bidirectional data-binding), having the following models:
- Game: embodies the rules of the game with the ability to start or restart a game (happens automatically upon launching the app or encountering game over)
- Tetromino: represents the Tetris shapes with all their intricate operations such as moving down, right, and left, and rotation right (clockwise) and left (counterclockwise). It also houses the majority of the collision detection logic in collaboration with Game
- Block: represents a single block that could form a tetromino shape or be a block in the playfield
Tetromino logic including collision detection obviously relied on some heavy math skills like calculus and linear algebra, so I had to dust up my Computer Science learning in Computer Graphics with matrix operations such as clockwise and counterclockwise rotations. Thankfully, Ruby supports matrix mathematical operations via the Matrix class.
Keyboard events are handled by a general display on_swt_keydown
filter handler that ensures correct action by the model based on user
key presses (e.g. left, right, down, right-shift for rotate right and
left-shift for rotate left with some other alternatives for rotation
like the letters 'a' and 'd'). It is declared in the `before_body`block on the Tetris custom shell class, meaning configuration is done before building the GUI since the GUI relies on it.
Multi-threaded programming is used where necessary, mainly to kick start the main loop for moving the tetrominoes down the playfield about every second while allowing the user to speed up the movement with the down arrow key if desired. Keep in mind that any interaction with the GUI from a thread other than the main thread must happen through
sync_exec. It is declared as a `after_body` block on the Tetris custom shell class, meaning it runs after building the GUI body to ensure it is available for interaction, but before showing the GUI to the user, which happens last.
Otherwise, bidirectional data-binding handles everything relating to updating the GUI, so the code is squeaky concise and clean. The model logic just worries about the model of the game logic and is completely oblivious to the intricacies of the GUI.
The square blocks took advantage of the newly added Glimmer Canvas Shape DSL for border decorations.
Stay tuned for more changes coming to the Tetris elaborate sample like:
- Preview Next Tetromino
This article is part of the Tetris Saga