A Java Scrabble implementation with GUI using MVC and Observer patterns.
- Deliverables: Source code, UML diagrams, sequence diagrams, documentation, JUnit tests
- Authors: Bashar Saadi, Kalid Wehbe, Madhav Sharma, Sammy Eyongorock
- Last Updated: November 26, 2025
cd "scrabble/src"
javac *.java
java Main- Enter 2-4 players and names at console prompt
- Choose whether each player is Human or AI
- Select a board (1. Standard, 2. Diagonal, 3 Corner Star)
- GUI window opens with board, command input, and player info
- Type commands in top text field and press Enter (for human players)
- AI players automatically take their turns
- Use File menu to Save/Load games (Ctrl+S / Ctrl+L)
- Use Edit menu for Undo/Redo (Ctrl+Z / Ctrl+Y)
PLACE <WORD> <H|V>
Example: PLACE HELLO H
PLACE <WORD> <ROW> <COL> <H|V>
Example: PLACE CAT 8 H H
Rows: 1-15, Columns: A-O, Direction: H (horizontal) or V (vertical)
PLACE <WORD> <ROW> <COL> <H|V> BLANK <LETTERS>
Example: PLACE CAT 8 H H BLANK T
(Uses a blank tile to represent the letter T)
SWAP <TILES> - Example: SWAP ABC
PASS - Skip turn
EXIT - End game
- Players can select a time limit on every turn then automatically pass after expiery
- Players can select from multiple custom board layouts at game start
- Standard Board: Traditional Scrabble premium square placement
- Diagonal Board: Premium squares arranged in diagonal patterns
- Corner Star Board: Star-shaped premium square arrangement radiating from corners
- Boards are defined in XML format for easy customization
- Undo: Revert the last move (Ctrl+Z or Edit → Undo)
- Redo: Re-apply an undone move (Ctrl+Y or Edit → Redo)
- Multi-level: Supports multiple consecutive undo/redo operations
- Complete game state restoration including board, scores, and tile racks
- Efficient implementation using stack-based state snapshots
- Save Game: Save current game state to a file (Ctrl+S or File → Save Game)
- Load Game: Resume a previously saved game (Ctrl+L or File → Load Game)
- Saves complete game state including:
- Board configuration and placed tiles
- All player scores and tile racks
- Current turn and game progress
- Undo/redo history
- File format: Serialized
.savfiles with error handling - User-friendly file chooser dialogs
- Two blank tiles are included in the tile bag (standard Scrabble rules)
- Blank tiles can represent any letter when placing a word
- Blank tiles are worth 0 points, even when representing high-value letters
- Players can use multiple blank tiles in a single word
The board now includes all standard Scrabble premium squares:
- Double Letter Score (DL): Doubles the value of a letter placed on it
- Triple Letter Score (TL): Triples the value of a letter placed on it
- Double Word Score (DW): Doubles the entire word score (including center star)
- Triple Word Score (TW): Triples the entire word score (corners)
Premium squares only apply when a tile is first placed on them. Subsequent words using that tile do not receive the bonus.
- Users can configure 2-4 players as either Human or AI
- AI players automatically take their turns without user input
- AI uses a greedy strategy to maximize score each turn (see AI Strategy section below)
The AI player (AIPlayer.java) implements a greedy strategy that selects the highest-scoring legal move available. Here is how the AI makes decisions:
-
Build Available Letters: The AI examines its rack, noting regular tiles and blank tiles (represented as
*internally). -
Iterate Through Dictionary: The AI checks every word in the dictionary to see if it can be formed using the current rack letters (including blank substitutions).
-
Try All Positions: For each formable word, the AI tries every position (row 0-14, column 0-14) and both orientations (horizontal and vertical).
-
Validate Placement: For each position, the AI checks:
- The word fits within board boundaries
- The player has the necessary tiles (using
canPlaceWordWithRack) - For the first move: the word must cover the center square (7,7)
- For subsequent moves: the word must connect to at least one existing tile
- New tiles do not create invalid perpendicular adjacencies
-
Score Calculation: The AI computes the score for each valid placement, including premium square bonuses (DL, TL, DW, TW).
-
Select Best Move: The AI tracks the highest-scoring valid move found and executes it.
-
Pass if No Move: If no valid word placement is found, the AI passes its turn.
makeMove(GameModel model): Main entry point that orchestrates move selectioncanFormWord(String word, List<Character> rackLetters): Checks if a word can be built from available tilesconnectsToBoard(Board board, String word, int row, int col, boolean horizontal): Ensures word connects to existing tilescheckNeighbors(...): Validates perpendicular adjacency rules
- Greedy: Always selects the immediate highest-scoring move
- Exhaustive Search: Considers all dictionary words and board positions
- Legal Moves Only: All AI moves are validated against game rules
- Blank Tile Support: Can use blank tiles as wildcards when forming words
- Model:
GameModel.java- Game state, logic, word validation, scoring - View:
GameViewGUI.java- Swing GUI, implementsGameObserver - Controller:
GameController.java- Parses commands, coordinates model-view, manages AI turns
GameObserverinterface enables automatic view updatesGameModelnotifies observers on state changes- Decouples model from view
Board.java- 15x15 grid management, premium square setupPlayer.java- Player data (name, score, tiles)AIPlayer.java- Computer player with greedy move selection (extends Player)Tile.java- Letter, score, and blank tile supportSquare.java- Single board position with bonus typeDictionary.java- Word validation via HashSet
- Kalid, Bashar: Board class, word placement logic
- Kalid, Bashar, Madhav: Player class, tile management
- Kalid, Bashar, Madhav: Dictionary, game loop
- All: Code review, testing
- Kalid, Bashar, Madhav, Sammy: GameViewGUI (Swing), board display
- Kalid, Bashar, Madhav: GameController refactor, command parsing
- Kalid, Bashar, Madhav, Sammy: Observer pattern, MVC integration
- All: JavaDocs, testing, documentation
- Kalid, Bashar: Blank tile implementation, premium square scoring
- Kalid, Bashar: AI Player implementation, greedy strategy
- Kalid, Bashar: Premium square board setup (DL, TL, DW, TW)
- Kalid, Bashar: JUnit tests for Milestone 3 features, documentation
- Madhav, Sammy: UML and Sequence Diagram
- All: Code Review, testing
- Kalid, Bashar: Custom board XML files (Standard, Diagonal, Corner Star)
- Kalid, Bashar: Undo/Redo implementation with state snapshots
- Kalid, Bashar: Save/Load game functionality with serialization
- Kalid, Bashar: JUnit tests for undo/redo and serialization
- Kalid, Bashar: Project organization and documentation updates
- Madhav: UML and Sequence Diagram
- All: 90 Second Time Limit per Turn
- Added: Observer pattern (
GameObserverinterface) - Replaced: Console view →
GameViewGUI(Swing) - Enhanced:
GameControllerhandles GUI commands
GameModel:
- Added
observers: List<GameObserver>for observer pattern - Added
firstMove: booleanfor center placement rule - Added methods:
addObserver(),notifyObservers(),isFirstMove(),setFirstMoveDone()
Player:
- Added
lastError: Stringfor error propagation through observer updates
Dictionary:
- Added
loadedSuccessfully: boolean,loadError: String - Added methods:
isLoaded(),getLoadError(),getWordCount() - Enhanced error handling (FileNotFoundException, IOException, empty file detection)
GameController:
- Refactored
handleCommand()into smaller methods:handleSwapCommand(),handlePlaceCommand(),handleFirstMove(),handleNormalMove()
Code Quality:
- Made fields
finalwhere appropriate (Tile, Player, GameModel, GameController, Dictionary) - Added complete JavaDocs to all classes
Rationale: Observer pattern decouples view from model. First move flag simplifies center placement logic. Error tracking enables better user feedback. Refactored controller follows Single Responsibility Principle. Immutability improves thread safety.
AIPlayer.java:
- Extends
Playerclass - Implements greedy move selection algorithm
- Methods:
makeMove(),canFormWord(),connectsToBoard(),checkNeighbors()
Tile.java:
- Added
blank: booleanfield to track blank tiles - Added static factory methods:
blankTile(),placedBlank(char) - Added
isBlank()method - Added
getScore()accessor method
Board.java:
- Added
setupPremiumSquares()method - Premium square positions now initialized in constructor
- Added center square (7,7) as Double Word Score
Square.java:
- Added
Bonusenum:NONE,DL,TL,DW,TW - Added
bonus: Bonusfield - Added
setBonus()andgetBonus()methods
GameModel.java:
- Made
boardanddictionaryfields private (MVC encapsulation) - Added
LETTER_VALUESstatic map for official Scrabble scoring - Added
computeWordScore()method with premium square calculations - Added
placeWordWithBlanks()method for blank tile support - Fixed blank tile count from 4 to 2 (standard Scrabble rules)
- Added
addPlayer()method for AI player support
GameController.java:
- Added
maybeDoAITurn()method to handle AI player turns - AI turns execute automatically after human moves
- Removed debug print statements (MVC compliance)
Player.java:
- Added
takeTileForLetter()method for blank tile handling
| Class | Change | Rationale |
|---|---|---|
| Tile | Added blank boolean |
Support for blank/wildcard tiles |
| Square | Added Bonus enum |
Track premium square types |
| Board | Added premium square arrays | Standard Scrabble board layout |
| GameModel | Added LETTER_VALUES map |
Official Scrabble letter scoring |
| AIPlayer | New class extending Player | Computer-controlled players |
- Blank tiles: Enable wildcard gameplay per Scrabble rules
- Premium squares: Add strategic depth with score multipliers
- AI Player: Enables single-player and mixed human/AI games
- Private fields in GameModel: Proper MVC encapsulation
- computeWordScore(): Centralized scoring with premium square logic
Custom Board Selection:
- Added board selection menu at game startup
- Created 3 XML board configuration files with different premium square layouts
- Enhanced
Board.javato load boards from XML files
Undo/Redo System:
- Added
GameStateclass to capture complete game snapshots - Implemented
createStateSnapshot()andrestoreState()inGameModel - Added undo/redo methods in
GameControllerwith stack-based history - Integrated undo/redo menu items in
GameViewGUIwith keyboard shortcuts
Save/Load Functionality:
- Made all model classes
Serializable(Board, Player, Tile, Square, GameModel, GameState, Dictionary) - Added
saveGame()andloadGame()methods inGameController - Implemented file chooser dialogs with
.savfile extension filter - Added comprehensive error handling for I/O operations
- Created nested
SaveStateclass to encapsulate model and undo/redo stacks
GameModel.java:
- Added
Serializableinterface withserialVersionUID - Enhanced JavaDocs for
createStateSnapshot()andrestoreState() - Made
observersfieldtransientto prevent GUI serialization - Added null checks in
addObserver()andnotifyObservers()for transient field handling
GameController.java:
- Added imports for serialization and file choosers
- Implemented
saveGame()method with file dialog and serialization - Implemented
loadGame()method with deserialization and state restoration - Added nested
SaveStateclass for complete game persistence - Added undo/redo stack management
GameViewGUI.java:
- Updated menu bar to include File menu with Save/Load options
- Added Edit menu with Undo/Redo options
- Added keyboard shortcuts (Ctrl+S, Ctrl+L, Ctrl+Z, Ctrl+Y)
- Integrated with controller's save/load and undo/redo methods
Board.java, Player.java, Tile.java, Square.java, GameState.java, Dictionary.java:
- All implement
Serializableinterface - Added
serialVersionUIDfor version control - Added
copy()methods where needed for deep cloning
Main.java:
- Updated to include board file path selection
- Board file paths now point to
boards/directory
| Class | Change | Rationale |
|---|---|---|
| GameController | Added SaveState inner class |
Encapsulates complete save data including undo/redo history |
| GameController | Added undo/redo stacks | Manages game state history for undo/redo |
| GameModel | Made observers field transient |
Prevents GUI components from being serialized |
| GameState | Made Serializable |
Enables saving undo/redo history |
| All model classes | Added Serializable |
Required for save/load functionality |
| Dictionary | Made Serializable |
Required since it's contained in GameModel |
| Board | Added XML file loading support | Supports custom board selection |
- Custom Boards: Adds variety and replayability to the game
- Undo/Redo: Essential quality-of-life feature for players to correct mistakes
- Save/Load: Enables long game sessions across multiple play sessions
- Serialization: Standard Java approach for object persistence
- State Snapshots: Immutable copies prevent reference issues in undo/redo
- Transient Observers: Prevents Swing GUI components from being serialized, avoiding platform-specific serialization issues
Custom Timer:
- Added a timer to automatically pass turns if players take too long
- Custom time limit or default to 90s
- No Game End Detection: Manual EXIT required
- No Cross-Word Validation: Perpendicular words formed are not validated against dictionary
- AI Performance: Large dictionaries may cause slight delays on AI turns
- Save File Compatibility: Saved games may not be compatible across different Java versions due to serialization
GameModelTest.java includes comprehensive tests (requires JUnit 4):
Basic Tests (Milestone 2):
- Valid/invalid word placement
- Turn passing
- Out-of-bounds validation
- Tile swapping (valid/invalid)
- Score calculation
Blank Tile Tests (Milestone 3):
testBlankTilePlacement- Placing words with blank tilestestBlankTileScoresZero- Blank tiles contribute 0 pointstestMultipleBlankTiles- Using multiple blanks in one word
Premium Square Tests (Milestone 3):
testDoubleLetterScore- DL squares double letter valuetestTripleLetterScore- TL squares triple letter valuetestDoubleWordScore- DW squares double word scoretestTripleWordScore- TW squares triple word scoretestPremiumSquareOnlyAppliesOnce- Bonuses only on first tile placement
AI Player Tests (Milestone 3):
testAIPlayerCreation- AI player instantiation and inheritancetestAIPlayerMakesLegalMove- AI only places valid dictionary wordstestAIPlayerPassesWhenNoMoves- AI passes when no moves availabletestAIPlayerSelectsHighScoringMove- AI greedy strategy verificationtestAIPlayerUsesBlankTiles- AI can utilize blank tiles
Undo/Redo Tests (Milestone 4):
testUndoPlacedWord- Undo correctly reverts a placed wordtestRedoPlacedWord- Redo re-applies an undone movetestMultipleUndo- Multiple consecutive undo operationstestMultipleRedo- Multiple consecutive redo operationstestGameStateSnapshotPreservesData- State snapshots preserve all game datatestUndoSwapTiles- Undo tile swapping operations
Serialization/Deserialization Tests (Milestone 4):
testSerializeGameModel- GameModel can be serializedtestDeserializeGameModel- GameModel can be deserializedtestSerializationPreservesPlayerData- Player scores and names preservedtestSerializationPreservesBoardState- Board tiles preserved after save/loadtestSerializeGameState- GameState objects are serializabletestDeserializationErrorHandling- Proper error handling for corrupted datatestSerializationAfterMultipleMoves- Save/load works after multiple turnstestSerializationPreservesTileBag- Tile bag state is preserved
src/
├── Main.java # Entry point
├── GameModel.java # Model (game logic, scoring)
├── GameViewGUI.java # View (Swing GUI)
├── GameController.java # Controller (command handling, AI turns)
├── GameObserver.java # Observer interface
├── Board.java # 15x15 grid with premium squares
├── Player.java # Player data
├── AIPlayer.java # AI player with greedy strategy
├── Tile.java # Tile representation (including blanks)
├── Square.java # Board square with bonus type
├── Dictionary.java # Word validation
├── GameModelTest.java # Unit tests
└── dictionary.txt
- File Organization
- Automatic game end detection (empty bag + empty rack)
- Cross-word validation for perpendicular words
- Network multiplayer support
- Timer for timed games
- Hint system for suggesting valid words
- Score history and statistics tracking