ZOHO L3 - FIFTTEEN PUZZLE
Description of the Question
The "Fifteen Puzzle" is a classic sliding tile puzzle consisting of a 4x4 grid with 15 numbered
tiles (1 to 15) and one empty space. The goal is to rearrange the tiles from a random
configuration to a solved state (tiles in ascending order with the empty space at the
bottom-right) by sliding tiles into the empty space.
Logic and Approach to Solve the Problem
Logic of the Fifteen Puzzle
1.Board Representation: Use a 4x4 grid (16 cells) to store tiles numbered 1 to 15, with 0
representing the empty space.
2.Initial State: Generate a solvable random configuration of the tiles.
3.Moves: Allow the empty space to swap with an adjacent tile (up, down, left, or right) if the
move is valid (within grid boundaries).
4.Win Condition: The puzzle is solved when the tiles are in order: 1 to 15 from left to right,
top to bottom, with 0 in the bottom-right cell (position [3][3]).
5.User Interaction: Accept directional inputs (e.g., arrow keys or W/A/S/D) to move the
empty space and update the board.
Implementation
Model: Manages the puzzle logic and state
class PuzzleModel {
private int[][] board;
private int emptyRow;
private int emptyCol;
private static final int SIZE = 4;
public PuzzleModel() {
board = new int[SIZE][SIZE];
initializeBoard();
}
private void initializeBoard() {
// Fill board with 1 to 15 and 0 (empty)
int value = 1;
for (int i = 0; i < SIZE; i++) {
for (int j = 0; j < SIZE; j++) {
if (i == SIZE - 1 && j == SIZE - 1) {
board[i][j] = 0;
emptyRow = i;
emptyCol = j;
} else {
board[i][j] = value++;
}
}
}
// Shuffle to a solvable state
shuffleBoard();
}
private void shuffleBoard() {
Random rand = new Random();
int shuffles = 100; // Number of random moves
for (int i = 0; i < shuffles; i++) {
int direction = rand.nextInt(4); // 0=up, 1=down, 2=left, 3=right
if (direction == 0) moveUp();
else if (direction == 1) moveDown();
else if (direction == 2) moveLeft();
else moveRight();
}
}
public boolean moveUp() {
if (emptyRow < SIZE - 1) {
board[emptyRow][emptyCol] = board[emptyRow + 1][emptyCol];
board[emptyRow + 1][emptyCol] = 0;
emptyRow++;
return true;
}
return false;
}
public boolean moveDown() {
if (emptyRow > 0) {
board[emptyRow][emptyCol] = board[emptyRow - 1][emptyCol];
board[emptyRow - 1][emptyCol] = 0;
emptyRow--;
return true;
}
return false;
}
public boolean moveLeft() {
if (emptyCol < SIZE - 1) {
board[emptyRow][emptyCol] = board[emptyRow][emptyCol + 1];
board[emptyRow][emptyCol + 1] = 0;
emptyCol++;
return true;
}
return false;
}
public boolean moveRight() {
if (emptyCol > 0) {
board[emptyRow][emptyCol] = board[emptyRow][emptyCol - 1];
board[emptyRow][emptyCol - 1] = 0;
emptyCol--;
return true;
}
return false;
}
public boolean isSolved() {
int value = 1;
for (int i = 0; i < SIZE; i++) {
for (int j = 0; j < SIZE; j++) {
if (i == SIZE - 1 && j == SIZE - 1) {
return board[i][j] == 0;
}
if (board[i][j] != value++) {
return false;
}
}
}
return true;
}
public int[][] getBoard() {
return board;
}
}
ViewModel: Mediates between Model
class PuzzleViewModel {
private PuzzleModel model;
private boolean isGameWon;
public PuzzleViewModel() {
model = new PuzzleModel();
isGameWon = false;
}
public int[][] getBoard() {
return model.getBoard();
}
public boolean isGameWon() {
return isGameWon;
}
public boolean processMove(char direction) {
boolean moved = false;
if (direction == 'w' || direction == 'W') {
moved = model.moveDown(); // Down moves empty space up
} else if (direction == 's' || direction == 'S') {
moved = model.moveUp(); // Up moves empty space down
} else if (direction == 'a' || direction == 'A') {
moved = model.moveRight(); // Right moves empty space left
} else if (direction == 'd' || direction == 'D') {
moved = model.moveLeft(); // Left moves empty space right
}
if (moved) {
isGameWon = model.isSolved();
}
return moved;
}
}
View: Handles UI (console-based)
class PuzzleView {
private PuzzleViewModel viewModel;
private Scanner scanner;
public PuzzleView(PuzzleViewModel viewModel) {
this.viewModel = viewModel;
scanner = new Scanner(System.in);
}
public void displayBoard() {
int[][] board = viewModel.getBoard();
System.out.println("-------------");
for (int i = 0; i < 4; i++) {
System.out.print("| ");
for (int j = 0; j < 4; j++) {
if (board[i][j] == 0) {
System.out.print(" ");
} else {
if (board[i][j] < 10) {
System.out.print(" " + board[i][j]);
} else {
System.out.print(board[i][j]);
}
}
System.out.print(" ");
}
System.out.println("|");
}
System.out.println("-------------");
}
public void displayMessage(String message) {
System.out.println(message);
}
public char getUserInput() {
System.out.print("Enter move (w=up, s=down, a=left, d=right, q=quit): ");
String input = scanner.nextLine();
if (input.length() > 0) {
return input.charAt(0);
}
return ' ';
}
public void closeScanner() {
scanner.close();
}
}
Main class
public class FifteenPuzzle {
public static void main(String[] args) {
PuzzleViewModel viewModel = new PuzzleViewModel();
PuzzleView view = new PuzzleView(viewModel);
view.displayMessage("Welcome to the Fifteen Puzzle!");
while (!viewModel.isGameWon()) {
view.displayBoard();
char move = view.getUserInput();
if (move == 'q' || move == 'Q') {
view.displayMessage("Game quit.");
break;
}
boolean validMove = viewModel.processMove(move);
if (!validMove) {
view.displayMessage("Invalid move. Try again.");
}
}
if (viewModel.isGameWon()) {
view.displayBoard();
view.displayMessage("Congratulations! You've solved the puzzle!");
}
view.closeScanner();
}
}
Explanation of the Code
- **PuzzleModel**:
● - Maintains a 4x4 `int[][]` array for the board.
● - Initializes tiles in order (1 to 15, 0 at bottom-right) and shuffles by making random
valid moves to ensure solvability.
● - Provides methods to move the empty tile (up, down, left, right) with boundary
checks, update the empty tile’s position, and check if the board is solved.
- **PuzzleViewModel**:
●
- Holds a `PuzzleModel` instance and tracks the game’s win state.
●
- Exposes the board state via `getBoard()` and processes moves via
`processMove(char)`, mapping user inputs (W/A/S/D) to Model methods.
● - Updates `isGameWon` when the puzzle is solved..
●
- **PuzzleView**:
● - Uses console output to draw the board with borders, showing numbers (or a blank
for 0) aligned for readability.
● - Prompts for user input and forwards it to the ViewModel.
● - Displays messages for invalid moves or game completion.
- **FifteenPuzzle** (Main):
● - Initializes MVVM components and runs the game loop: show board, get input,
process move, and check win condition.
● - Allows quitting with ‘q’ and closes the scanner to prevent resource leaks.
Testing
Welcome to the Fifteen Puzzle!
-------------
| 1 2 3 4|
| 5 6 7 8|
| 9 10 11 12 |
| 13 14 15 |
-------------
Enter move (w=up, s=down, a=left, d=right, q=quit): d
-------------
| 1 2 3 4|
| 5 6 7 8|
| 9 10 11 12 |
| 13 14 15 |
-------------
Enter move (w=up, s=down, a=left, d=right, q=quit): w
-------------
| 1 2 3 4|
| 5 6 7 8|
| 9 10 11 |
| 13 14 15 12 |
-------------
Enter move (w=up, s=down, a=left, d=right, q=quit): s
Invalid move. Try again.
-------------
| 1 2 3 4|
| 5 6 7 8|
| 9 10 11 |
| 13 14 15 12 |
-------------
Enter move (w=up, s=down, a=left, d=right, q=quit): a
-------------
| 1 2 3 4|
| 5 6 7 8|
| 9 10 11 |
| 13 14 15 12 |
-------------
Enter move (w=up, s=down, a=left, d=right, q=quit): q
Game quit.
```
Solved State Example
If the user continues to the solved state:
```
-------------
| 1 2 3 4|
| 5 6 7 8|
| 9 10 11 12 |
| 13 14 15 |
-------------
Enter move (w=up, s=down, a=left, d=right, q=quit): d
-------------
| 1 2 3 4|
| 5 6 7 8|
| 9 10 11 12 |
| 13 14 15 |
-------------
Congratulations! You've solved the puzzle!
```
Explanation
Input:
The user enters `w`, `a`, `s`, `d` to move the empty space (up, left, down, right) or `q` to
quit.
Output :
The board is displayed after each valid move, with invalid moves prompting an error.
When solved, a win message appears.
The empty space is shown as a blank, and numbers are aligned (single-digit numbers
have an extra space for formatting).
The shuffle ensures a solvable puzzle, but the exact starting board varies per run.
---