A JavaFX application that lets you play the classic Lights Out puzzle manually, then watch four different algorithms solve it simultaneously โ with real-time animation and a performance comparison across 30 boards.
Lights Out is a classic puzzle played on an NรN grid of lights.
- Each cell is either ON (white โ) or OFF (dark โ)
- Clicking any cell toggles it and its 4 neighbours (up, down, left, right)
- Goal: Turn ALL cells WHITE (ON)
The challenge is that every move affects multiple cells, making naive solutions ineffective.
- Manual Play Mode โ Try to solve a randomly generated 6ร6 board yourself
- Give Up โ Let all four algorithms take over and solve it simultaneously
- Side-by-side animation โ Watch each algorithm apply moves step by step in real time
- Analysis Mode โ Bar chart and table comparing performance across 30 precomputed boards
- Parallel execution โ All four solvers run on separate threads simultaneously
How it works:
- Tries all
2^Npossible flip patterns for the first row (64 patterns for 6ร6) - For each pattern, forces the remaining rows greedily โ if a cell in row
ris OFF, flip the cell directly below it in rowr+1 - Checks if the last row ends up all ON
- Picks the pattern that results in fewest total moves
Time Complexity: O(2^N ร Nยฒ)
For 6ร6: 64 ร 36 = 2,304 operations
Correctness: โ Always finds a solution if one exists. Not always minimum moves globally but fast and reliable.
How it works:
- Divides the board into overlapping regions (quadrants, halves, full board)
- Solves each region independently using the same row-by-row greedy approach
- Tries all flip patterns for the top row of each region, forces remaining rows
Time Complexity: O(2^N ร Nยฒ)
For 6ร6: 64 ร 36 = 2,304++ operations
Correctness:
How it works:
- For every cell, makes a binary decision: press or skip
- Recursively tries both choices for all
Nยฒcells - Prunes branches where current cost already exceeds best known solution
- Has a 10-second timeout โ falls back to Graph+Greedy if exceeded
Time Complexity: O(2^(Nยฒ))
For 6ร6: 2^36 โ 68 billion in worst case (always times out on 6ร6)
Correctness: โ Optimal if it completes. For 6ร6 it always times out and uses Graph+Greedy results as fallback.
How it works:
- Represents the entire board as a single 64-bit integer (bitmask)
- Flipping a cell = XOR with a precomputed flip mask (toggles cell + 4 neighbours in one operation)
- BFS explores board states level by level โ all 1-flip states, then 2-flip, etc.
- HashMap stores every visited state (the DP table) โ any state already seen is an overlap and is skipped immediately
- First time goal state is reached = minimum flips guaranteed
- Has a 5-second timeout and 1 million state cap โ falls back to Graph+Greedy if exceeded
Time Complexity: O(Nยฒ ร 2^(Nยฒ))
For 6ร6: 36 ร 2^36 โ 2.4 trillion in worst case (always hits limits on 6ร6)
Correctness: โ Optimal and guaranteed minimum moves โ if it completes within limits.
| Algorithm | Time Complexity | 6ร6 Operations | Optimal? | Completes on 6ร6? |
|---|---|---|---|---|
| Graph+Greedy | O(2^N ร Nยฒ) | ~2,304 | Near-optimal | โ Always |
| Divide&Conquer | O(7 ร 2^(N/2) ร Nยฒ) | ~2,016 | No | |
| Backtracking | O(2^(Nยฒ)) | ~68 billion | Yes | โ Times out (10s) |
| DP (BFS) | O(Nยฒ ร 2^(Nยฒ)) | ~2.4 trillion | Yes | โ Hits state cap (5s) |
Note: For 6ร6 boards, Backtracking and DP always hit their limits and fall back to Graph+Greedy results. The analysis section uses precomputed realistic data to demonstrate expected relative performance.
The DP solver uses genuine dynamic programming because:
Overlapping Subproblems exist:
flip(A) then flip(B) โ State X
flip(B) then flip(A) โ State X โ same state, different path
Since flip order does not matter, many different move sequences reach the same board configuration. Without DP each would be explored separately. The HashMap detects and eliminates all duplicates instantly.
Optimal Substructure:
min flips to solve from State S
= 1 + min flips to solve from (S XOR flipMask[i])
BFS guarantees minimum moves because it processes states level by level โ the first time the goal state is reached it is provably at minimum depth.
FlipGameAllInOne.java
โ
โโโ Main Menu
โ โโโ Play Manual โ manual 6ร6 board
โ โโโ See Analysis โ bar chart + 30-board table
โ
โโโ Manual Play Mode
โ โโโ Click tiles to flip
โ โโโ Give Up โ triggers parallel solver animation
โ โโโ New Board / Main Menu
โ
โโโ Algorithm Animation Screen
โ โโโ 2ร2 grid of boards (one per algorithm)
โ โโโ All four solve simultaneously (parallel threads)
โ โโโ Each board animates moves step by step
โ
โโโ Solver Implementations
โโโ solveGraphGreedy()
โโโ solveDivideConquerGreedy()
โโโ solveBacktracking() (with BacktrackSolver inner class)
โโโ solveDP() (BFS + bitmask + HashMap)
- Java 11 or higher
- JavaFX SDK (11+)
# Compile
javac --module-path /path/to/javafx/lib \
--add-modules javafx.controls,javafx.fxml \
-d out src/pck/FlipGameAllInOne.java
# Run
java --module-path /path/to/javafx/lib \
--add-modules javafx.controls,javafx.fxml \
-cp out pck.FlipGameAllInOne- Add JavaFX as a library in Project Structure
- Add VM options:
--module-path /path/to/javafx/lib --add-modules javafx.controls - Run
FlipGameAllInOne.java
// Each cell = one bit in a long integer
// Flipping cell i = XOR with precomputed mask
long next = current ^ flipMasks[i];// For each cell, compute which bits it toggles
// (itself + up to 4 neighbours)
mask |= (1L << pos); // for cell and each valid neighbourif (!parent.containsKey(next)) {
parent.put(next, current); // record path
queue.add(next); // explore later
}
// Already seen = OVERLAP = skip immediatelyExecutorService executor = Executors.newFixedThreadPool(4);
// All four solvers run simultaneously on separate threads
// Results collected and animation triggered on JavaFX threadThe See Analysis screen shows precomputed performance data across 30 different 6ร6 boards:
- Bar chart comparing average time (ms) and average moves per algorithm
- Board list showing each board's miniature grid and per-algorithm stats
The analysis data uses realistic precomputed ranges because the actual Backtracking and DP solvers cannot complete within reasonable time on 6ร6 boards.
- Dynamic Programming โ memoisation via HashMap, overlapping subproblems, optimal substructure
- BFS shortest path โ level-by-level exploration guaranteeing minimum moves
- Bitmask manipulation โ entire board state as one integer, XOR for O(1) flip
- Backtracking with pruning โ binary decision tree with cost-based branch cutting
- Greedy algorithms โ row-by-row forced flip strategy
- Parallel computing โ ExecutorService with four simultaneous solver threads
- JavaFX animation โ SequentialTransition, ParallelTransition, ScaleTransition
- Divide and Conquer does not correctly solve all boards โ flips cross region boundaries
- Backtracking and DP always time out on 6ร6 and fall back to Graph+Greedy
- Analysis data is precomputed/simulated โ not from actual solver runs on those boards
- Board size is hardcoded to 6ร6 โ changing
BOARD_SIZErequires recompilation
This project is for educational purposes โ demonstrating algorithm design, complexity analysis, and dynamic programming concepts through an interactive puzzle game.