Package edu.uw.tcss.model
Overview
This package contains all the classes necessary to run a Tetris game, including game state management, piece movement, collision detection, and event notifications. You are responsible for creating the graphical user interface that interacts with this API.
Getting Started
The main entry point is TetrisGame, which
implements the PropertyChangeEnabledGameControls
interface. Create an instance, register listeners to receive game events,
and call methods to control the game.
Minimal Example (Console):
TetrisGame game = new TetrisGame();
// Listen for game events
game.addPropertyChangeListener(evt -> {
if (evt.getNewValue() instanceof GameEvent event) {
System.out.println("Event: " + event.getClass().getSimpleName());
}
});
// Start the game
game.newGame(); // Fires: GameStateChanged, NextPieceChanged,
// CurrentPieceChanged, FrozenBlocksChanged
// Move and rotate pieces
game.left();
game.rotateCW();
game.down();
// Advance the game by one step (typically called by a timer)
game.step();
Key Classes
TetrisGame- The main game implementation. Create an instance of this class to run a game.
GameControls- Defines the core game operations (newGame, step, move, rotate, etc.) and data structures (Block, Point, IndividualPiece, FrozenBlocks, GameState).
PropertyChangeEnabledGameControls- Extends GameControls with PropertyChangeListener support. Contains comprehensive documentation on the event system and usage patterns.
GameEvent- Sealed interface defining all game events. Each event is a record containing specific game data (state changes, piece updates, score information, etc.).
Event System
The game uses Java's PropertyChangeListener framework with type-safe
GameEvent records. When game state changes, listeners
receive events containing the updated data.
Available Events
GameEvent.GameStateChanged- Game transitions between NEW, RUNNING, PAUSED, or OVERGameEvent.CurrentPieceChanged- Current piece moves, rotates, or spawnsGameEvent.NextPieceChanged- Next piece is selected (after current piece freezes)GameEvent.FrozenBlocksChanged- A piece freezes into place on the boardGameEvent.RowsCleared- One or more complete rows are cleared
Listening to Events
Example: Handle Game State and Score Updates
game.addPropertyChangeListener(evt -> {
if (evt.getNewValue() instanceof GameEvent event) {
switch (event) {
case GameEvent.GameStateChanged e ->
System.out.println("State: " + e.newState());
case GameEvent.CurrentPieceChanged e ->
System.out.println("Piece at: " +
Arrays.toString(e.piece().location()));
case GameEvent.RowsCleared e ->
System.out.println("Cleared " + e.count() + " rows!");
default -> { }
}
}
});
Example: Listen to Specific Events Only
// Only receive RowsCleared events
game.addPropertyChangeListener("RowsCleared", evt -> {
if (evt.getNewValue() instanceof GameEvent.RowsCleared e) {
int score = calculateScore(e.count());
System.out.println("Score: " + score);
}
});
Common Usage Patterns
Starting a Game
game.newGame(); // Required before the game can be played
Game Loop (with Timer)
Timer timer = new Timer(DELAY_MS, e -> game.step());
timer.start();
Handling User Input
// Map keyboard to game controls
addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent e) {
switch (e.getKeyCode()) {
case KeyEvent.VK_LEFT -> game.left();
case KeyEvent.VK_RIGHT -> game.right();
case KeyEvent.VK_DOWN -> game.down();
case KeyEvent.VK_UP -> game.rotateCW();
case KeyEvent.VK_SPACE -> game.drop();
case KeyEvent.VK_P -> game.togglePause();
}
}
});
Pausing and Resuming
game.pause(); // Pause the game
game.unPause(); // Resume the game
game.togglePause(); // Toggle between paused and running
Important Notes
- Call newGame() first: The game will not respond to controls
until
newGame()is called. - Multiple events per action: Some operations fire multiple events.
For example,
newGame()fires GameStateChanged, NextPieceChanged, CurrentPieceChanged, FrozenBlocksChanged, and GameStateChanged again (NEW → RUNNING). See individual method documentation for details. - Property names are case-sensitive: When registering listeners for
specific events, use exact class names: "GameStateChanged", "CurrentPieceChanged", etc.
Or call
event.getPropertyName()for type safety. - Frozen blocks representation: The
FrozenBlocksdata usesnullto represent empty spaces on the board. Check for null when iterating through blocks. - Game state restrictions: Most game controls only work when the
game is in the RUNNING state. Check
GameStatebefore enabling UI controls.
Data Structures
All data structures are defined as nested types in GameControls:
GameControls.Point- A coordinate on the game boardGameControls.Block- Enum of Tetris block types (I, J, L, O, S, T, Z, EMPTY)GameControls.IndividualPiece- A tetromino at a specific board locationGameControls.FrozenBlocks- All blocks that have frozen in placeGameControls.GameState- Enum of game states (NEW, RUNNING, PAUSED, OVER)
- Version:
- Autumn 2025
- Author:
- Charles Bryan
- See Also:
-
ClassDescriptionThe controls for a Tetris game.The different types of blocks that can be stored in a Board's grid.Data class that represents all Tetris
Blockobjects that have frozen into place.Specifies a game state for Tetris.Data class that represents a single Tetris tetromino in a location on the Tetris board.Data class that represents a "point" on the Tetris game board.Sealed interface representing type-safe events for the Tetris game's PropertyChangeListener framework.Event fired when the current movable piece changes state.Event fired when the game's frozen blocks change state.Event fired when the game state changes.Event fired when the next movable piece changes.Event fired when one or more lines clear from the frozen blocks.Allows implementing classes to leverage thePropertyChangeframework of the Observer Design Pattern to inform interested parties of updates to the Tetris game state.The main implementation of a Tetris game.