Class TetrisGame

java.lang.Object
edu.uw.tcss.model.TetrisGame
All Implemented Interfaces:
GameControls, PropertyChangeEnabledGameControls

public class TetrisGame extends Object implements PropertyChangeEnabledGameControls
The main implementation of a Tetris game. This is the primary class you will interact with to control game logic and receive game state updates.

This class implements PropertyChangeEnabledGameControls, providing access to all game controls (movement, rotation, pause, etc.) and firing GameEvent notifications when the game state changes. Register PropertyChangeListeners to receive these events and update your user interface accordingly.

Singleton Pattern

Important: This class enforces a singleton pattern - only one instance of TetrisGame can exist at a time. Attempting to create a second instance will throw an IllegalStateException. This design ensures a single source of truth for the game state and simplifies event management.

Basic Usage

// Create the game (only once in your application)
TetrisGame game = new TetrisGame();

// Register listeners to receive game events
game.addPropertyChangeListener(evt -> {
    // Handle game events here
});

// Start the game
game.newGame();

// Control the game
game.step();  // Advance one step (typically called by a timer)
game.left();  // Move piece left
game.rotateCW();  // Rotate clockwise

Important Implementation Details

  • Default board size: 10 columns × 20 rows
  • Frozen blocks: This implementation uses null to represent empty spaces in the frozen blocks collection
  • Coordinate system: (0, 0) is bottom-left; positive x moves right, positive y moves up
  • Initial state: Game starts in GameState.OVER. Call newGame() before playing

See the package documentation for a complete getting started guide with more examples.

Version:
Autumn 2025
Author:
Charles Bryan, Alan Fowler
See Also:
  • Constructor Details

    • TetrisGame

      public TetrisGame()
      Creates a standard size Tetris game board (10 columns × 20 rows).

      Singleton Restriction: Only one TetrisGame instance may exist at a time. If you attempt to create a second instance before the first is garbage collected, this constructor will throw an IllegalStateException. This restriction exists to maintain a single source of truth for game state.

      After construction, the game is in GameState.OVER. Call newGame() to initialize and start playing.

      Throws:
      IllegalStateException - if a TetrisGame instance has already been created
    • TetrisGame

      public TetrisGame(int theWidth, int theHeight)
      Creates a custom size Tetris game board.

      Singleton Restriction: Only one TetrisGame instance may exist at a time. If you attempt to create a second instance before the first is garbage collected, this constructor will throw an IllegalStateException. This restriction exists to maintain a single source of truth for game state.

      After construction, the game is in GameState.OVER. Call newGame() to initialize and start playing.

      Parameters:
      theWidth - width of the Tetris game board (number of columns)
      theHeight - height of the Tetris game board (number of rows)
      Throws:
      IllegalStateException - if a TetrisGame instance has already been created
  • Method Details

    • newGame

      public void newGame()
      Places the game in GameState.NEW followed by placing the game into GameState.RUNNING. This method must be called before the first game and before each new game. No restriction on the current GameState is enforced.

      When this action is performed, all interested parties will be notified of the action via the following GameEvents in the following order:

      • GameEvent.GameStateChanged
        • GameState.NEW
      • GameEvent.NextPieceChanged
        • A new "next" movable piece to display
      • GameEvent.CurrentPieceChanged
        • A new movable piece to display
      • GameEvent.FrozenBlocksChanged
        • An empty collection of frozen blocks
        • Note: This implementation uses null to represent empty blocks.
      • GameEvent.GameStateChanged
        • GameState.RUNNING
      Specified by:
      newGame in interface GameControls
    • endGame

      public void endGame()
      Places the game in GameState.OVER. The game must be in GameState.PAUSED or GameState.RUNNING for this action to have an effect.

      When this action is performed, all interested parties will be notified of the action via the following GameEvents:

      • GameEvent.GameStateChanged
        • GameState.OVER
      Specified by:
      endGame in interface GameControls
    • pause

      public void pause()
      Places the game into GameState.PAUSED. The game must be in GameState.RUNNING for this action to have an effect.

      If this action is performed, all interested parties will be notified of the action via the following GameEvents:

      • GameEvent.GameStateChanged
        • GameState.PAUSED
      Specified by:
      pause in interface GameControls
    • unPause

      public void unPause()
      Places the game into GameState.RUNNING. The game must be in GameState.PAUSED for this action to have an effect.

      If this action is performed, all interested parties will be notified of the action via the following GameEvents:

      • GameEvent.GameStateChanged
        • GameState.RUNNING
      Specified by:
      unPause in interface GameControls
    • togglePause

      public void togglePause()
      Toggles the game into either GameState.RUNNING or GameState.PAUSED. More formally, if the game is in GameState.PAUSED this action places the game into GameState.RUNNING and if the game is in GameState.RUNNING, this action places the game in GameState.PAUSED. The game must be in GameState.PAUSED or GameState.RUNNING for this action to have an effect.

      If this action is performed, all interested parties will be notified of the action via the following GameEvents:

      • GameEvent.GameStateChanged
        • GameState.RUNNING OR GameState.PAUSED
      Specified by:
      togglePause in interface GameControls
    • step

      public void step()
      Advances the game by one 'step'. The game must be in GameState.RUNNING for this action to have an effect.
      This action could include:
      • moving the movable piece down 1 line
      • freezing the movable piece if appropriate (which may end the game placing it into GameState.OVER)
      • clearing full lines as needed

      If this action is performed, all interested parties will be notified of the action via the following GameEvents in the following order:

      • * GameEvent.RowsCleared
        • The number of rows cleared
      • * GameEvent.NextPieceChanged
        • The new "next" movable piece
      • GameEvent.CurrentPieceChanged
        • An update to the movable piece to display
        • This may be a "new" piece
      • * GameEvent.FrozenBlocksChanged
        • An update to the frozen blocks
      * Only when needed
      Specified by:
      step in interface GameControls
    • down

      public void down()
      Try to move the movable piece down. The game must be in GameState.RUNNING for this action to have an effect.
      This action could include:
      • freezing the movable piece if appropriate (which may end the game placing it into GameState.OVER)
      • clearing full lines as needed

      If this action is performed, all interested parties will be notified of the action via the following GameEvents in the following order:

      • * GameEvent.RowsCleared
        • The number of rows cleared
      • * GameEvent.NextPieceChanged
        • The new "next" movable piece
      • GameEvent.CurrentPieceChanged
        • An update to the movable piece to display
        • This may be a "new" piece
      • * GameEvent.FrozenBlocksChanged
        • An update to the frozen blocks
      * Only when needed
      Specified by:
      down in interface GameControls
    • left

      public void left()
      Try to move the movable piece left. The game must be in GameState.RUNNING for this action to have an effect.

      If this action is performed, all interested parties will be notified of the action via the following GameEvent:

      • GameEvent.CurrentPieceChanged
        • An update to the movable piece to display
      Specified by:
      left in interface GameControls
    • right

      public void right()
      Try to move the movable piece right. The game must be in GameState.RUNNING for this action to have an effect.

      If this action is performed, all interested parties will be notified of the action via the following GameEvent:

      • GameEvent.CurrentPieceChanged
        • An update to the movable piece to display
      Specified by:
      right in interface GameControls
    • rotateCW

      public void rotateCW()
      Try to rotate the movable piece in the clockwise direction. The game must be in GameState.RUNNING for this action to have an effect.

      If this action is performed, all interested parties will be notified of the action via the following GameEvent:

      • GameEvent.CurrentPieceChanged
        • An update to the movable piece to display
      Specified by:
      rotateCW in interface GameControls
    • rotateCCW

      public void rotateCCW()
      Try to rotate the movable piece in the counter-clockwise direction. The game must be in GameState.RUNNING for this action to have an effect.

      If this action is performed, all interested parties will be notified of the action via the following GameEvent:

      • GameEvent.CurrentPieceChanged
        • An update to the movable piece to display
      Specified by:
      rotateCCW in interface GameControls
    • drop

      public void drop()
      Drop the movable piece until it is set. The game must be in GameState.RUNNING for this action to have an effect. This action will include:
      • freezing the movable piece (which may end the game placing it into GameState.OVER)
      • clearing full lines as needed

      If this action is performed, all interested parties will be notified of the action via the following GameEvents in the following order:

      • * GameEvent.RowsCleared
        • The number of rows cleared
      • GameEvent.NextPieceChanged
        • The new "next" movable piece
      • GameEvent.CurrentPieceChanged
        • A new movable piece to display
        • This will be a "new" piece
      • GameEvent.FrozenBlocksChanged
        • An update to the frozen blocks
      * Only when needed
      Specified by:
      drop in interface GameControls
    • addPropertyChangeListener

      public void addPropertyChangeListener(PropertyChangeListener theListener)
      Description copied from interface: PropertyChangeEnabledGameControls
      Add a PropertyChangeListener to the listener list. The listener is registered for all properties. The same listener object may be added more than once, and will be called as many times as it is added. If listener is null, no exception is thrown and no action is taken.
      Specified by:
      addPropertyChangeListener in interface PropertyChangeEnabledGameControls
      Parameters:
      theListener - The PropertyChangeListener to be added
    • addPropertyChangeListener

      public void addPropertyChangeListener(String thePropertyName, PropertyChangeListener theListener)
      Description copied from interface: PropertyChangeEnabledGameControls
      Add a PropertyChangeListener for a specific property. The listener will be invoked only when a call on firePropertyChange names that specific property. The same listener object may be added more than once. For each property, the listener will be invoked the number of times it was added for that property. If propertyName or listener is null, no exception is thrown and no action is taken.
      Specified by:
      addPropertyChangeListener in interface PropertyChangeEnabledGameControls
      Parameters:
      thePropertyName - The name of the property to listen on.
      theListener - The PropertyChangeListener to be added
    • removePropertyChangeListener

      public void removePropertyChangeListener(PropertyChangeListener theListener)
      Description copied from interface: PropertyChangeEnabledGameControls
      Remove a PropertyChangeListener from the listener list. This removes a PropertyChangeListener that was registered for all properties. If listener was added more than once to the same event source, it will be notified one less time after being removed. If listener is null, or was never added, no exception is thrown and no action is taken.
      Specified by:
      removePropertyChangeListener in interface PropertyChangeEnabledGameControls
      Parameters:
      theListener - The PropertyChangeListener to be removed
    • removePropertyChangeListener

      public void removePropertyChangeListener(String thePropertyName, PropertyChangeListener theListener)
      Description copied from interface: PropertyChangeEnabledGameControls
      Remove a PropertyChangeListener for a specific property. If listener was added more than once to the same event source for the specified property, it will be notified one less time after being removed. If propertyName is null, no exception is thrown and no action is taken. If listener is null, or was never added for the specified property, no exception is thrown and take no action.
      Specified by:
      removePropertyChangeListener in interface PropertyChangeEnabledGameControls
      Parameters:
      thePropertyName - The name of the property that was listened on.
      theListener - The PropertyChangeListener to be removed
    • toString

      public String toString()

      Returns a textual representation of the Tetris board. Use this for sandboxing the Tetris API and logic.

      Overrides:
      toString in class Object
      Returns:
      a textual representation of the Tetris board.