Tetris JEB
This is a project for Bucknell's CSCI 205. Code snippets may be available upon request.
My team and I built TETRIS JEB using Java 21 and JavaFX, but the challenge was working with AGILE and planning. We started with the standard Model-View-Controller pattern we’d used in a previous assignment. It seemed easy until we realized how much concurrency a game like Tetris actually requires. A piece has to fall over time, respond to keystrokes instantly, lock into place, and trigger UI updates all at once. Instead of just writing a massive controller file to handle all of this, we had to create CRC diagrams, UML, and discuss our plans.
Technical Details
Specifically, the logic was broken into Models, Views, a Controller, and now Services - our Threads wrapper class. In the Model package, we realized we needed data structures for the Board, Collider, GameState, etc. This encapsulation was important to both following OOD and producing a decent game.
The GameController doesn't do much on its own. Instead of the controller calculating everything, we built a service layer for the core mechanics. CollisionService, LineClearService, and ScoreService each handle their own specific jobs. If a piece hits the bottom, the controller just delegates the row-checking to the LineClearService. On the visual side, the JavaFX Views just render what they are told. I told my team to follow the UNIX philosophy here: "do one thing and do it well."
Reflection
Getting this architecture to actually work took some trial and error. On paper, the UML diagrams made the system look clean, but they failed to capture the reality of how things evolve during development. The biggest hurdle throughout the project was stopping the GameController from doing to much.
If I were to keep working on this, I’d abstract the rendering logic even further so the UI could be swapped out entirely, and I'd finish polishing the multiplayer synchronization. I came into this thinking I was just programming a retro puzzle game, but I came out with a much deeper, practical understanding of how to manage complex state changes and why strict API boundaries are necessary when a project scales.
Also, I have used Rhino before it was deprecated in Java SE 8, and it is such a cool feature. There are similar things like GraalJS, but I would love to make a custom mod loader using a Java Agent that is injected at runtime.