A High-Performance, Full-Stack Sudoku Solving Platform
Solving 9x9 puzzles with elegant recursive backtracking and a responsive Angular user interface
Overview β’ Architecture β’ Features β’ Getting Started
Sudoku Solver is a comprehensive web application designed to resolve challenging 9x9 Sudoku puzzles with efficiency and precision. The application combines a Spring Boot REST API powered by recursive backtracking algorithms with a modern Angular single-page application (SPA) that delivers an intuitive, real-time solving experience.
This project demonstrates professional full-stack development practices, emphasizing clean architecture, reactive programming patterns, and algorithmic optimization for constraint satisfaction problems.
| Capability | Description |
|---|---|
| Puzzle Solving | Resolves standard 9x9 Sudoku puzzles with validated constraints |
| Real-Time Processing | Asynchronous HTTP communication with immediate visual feedback |
| Input Validation | Client-side and server-side constraints enforcement (rows, columns, 3x3 boxes) |
| State Management | RxJS Observables for reactive, responsive state handling |
| Responsive UI | Angular standalone components with two-way data binding and dynamic visual feedback |
The backend serves as the computational engine, implementing a recursive backtracking algorithm optimized for 9x9 constraint satisfaction.
- Framework: Spring Boot 3.5.4
- Language: Java 17
- Build Tool: Maven
- Dependencies: Spring Web, Lombok
The SudokuService employs a depth-first search with constraint validation:
public boolean solve(int[][] board) {
// Iterate through each cell in the 9x9 matrix
for (int row = 0; row < 9; row++) {
for (int col = 0; col < 9; col++) {
if (board[row][col] == 0) { // Empty cell found
// Try digits 1β9
for (int num = 1; num <= 9; num++) {
if (isSafe(board, row, col, num)) {
board[row][col] = num; // Place digit
if (solve(board)) { // Recurse
return true;
}
board[row][col] = 0; // Backtrack
}
}
return false; // No valid digit found
}
}
}
return true; // Puzzle solved
}Algorithm Characteristics:
- Time Complexity: O(9^(n)) where n is the number of empty cells; typically exponential but pruned by constraint validation
- Space Complexity: O(n) for recursive call stack depth
- Optimizations:
- Early constraint checking eliminates invalid candidates before recursion
- Row, column, and 3x3 box validations reduce branching factor
- Greedy backtracking minimizes computational overhead
The isSafe() method validates placement against three Sudoku constraints:
- Row Uniqueness: No duplicate in the current row
- Column Uniqueness: No duplicate in the current column
- 3x3 Box Uniqueness: No duplicate in the 3x3 subgrid
POST /api/solve
Content-Type: application/json
Request Body:
{
"board": [[0, 0, 3, ...], [...], ...] // 9x9 array; 0 = empty cell
}
Response (200 OK):
{
"solvedBoard": [[...], [...], ...], // Completed 9x9 array
"status": "SOLVED" // or "INVALID_PUZZLE"
}
CORS Configuration: Enabled for http://localhost:4200 to allow Angular frontend communication.
The frontend delivers an interactive, responsive user experience with real-time validation and asynchronous state management via RxJS.
- Framework: Angular 19.0.0 (Standalone Components)
- Language: TypeScript 5.6.2
- HTTP Client: Angular HttpClient with RxJS Observables
- Styling: CSS3 with responsive design
- State Management: RxJS Observable patterns
Two-Way Data Binding:
[(ngModel)]="board[i][j]" // Real-time input synchronizationUsers edit cells directly; the component state updates instantaneously.
Input Constraints:
- Pattern validation:
pattern="[1-9]"restricts input to valid Sudoku digits - Max length: Single character per cell
- Read-only state: Board becomes immutable post-solving
Dynamic Visual Feedback:
[ngClass]="{'solved-cell': originalBoard.length > 0 &&
originalBoard[i][j] === null && board[i][j] !== null}"Solved cells are visually distinguished from pre-filled cells.
The SudokuApiService uses Angular's HttpClient for non-blocking asynchronous communication:
public solve(board: number[][]): Observable<SudokuResponse> {
const request = { board: board };
return this.http.post<SudokuResponse>(this.apiUrl, request);
}Observable Pattern Benefits:
- Non-blocking: UI remains responsive during server processing
- Cancellable: Requests can be unsubscribed if needed
- Error Handling: Structured error callbacks for user feedback
- Type Safety: Strong typing via TypeScript interfaces
The AppComponent orchestrates user interactions:
| Method | Responsibility |
|---|---|
solvePuzzle() |
Deep-clone board, invoke API via Observable subscription, update UI on resolution |
clearBoard() |
Reset 9x9 grid and application state |
trackByRow() / trackByCol() |
Optimize rendering performance with Angular's trackBy |
sudoku-app/
βββ sudoku-api/ # Spring Boot REST API
β βββ src/
β β βββ main/
β β β βββ java/com/example/sudokuapi/
β β β β βββ SudokuApiApplication.java # Spring Boot entry point
β β β β βββ controller/
β β β β β βββ SudokuController.java # REST endpoint handler
β β β β βββ service/
β β β β β βββ SudokuService.java # Recursive backtracking logic
β β β β βββ dto/
β β β β βββ SudokuRequest.java # API request payload
β β β β βββ SudokuResponse.java # API response payload
β β β βββ resources/
β β β βββ application.properties # Spring configuration
β β βββ test/
β β βββ java/com/example/sudokuapi/
β β βββ SudokuApiApplicationTests.java
β βββ pom.xml # Maven dependencies & build config
β βββ mvnw / mvnw.cmd # Maven wrapper scripts
β
βββ sudoku-ui/ # Angular Single-Page Application
βββ src/
β βββ index.html # HTML entry point
β βββ main.ts # Angular bootstrap
β βββ styles.css # Global styles
β βββ app/
β βββ app.component.ts # Root component (logic)
β βββ app.component.html # UI template
β βββ app.component.css # Component styles
β βββ app.module.ts # Angular module configuration
β βββ services/
β βββ sudoku-api.service.ts # HTTP client service
βββ angular.json # Angular CLI configuration
βββ package.json # Node dependencies
βββ tsconfig.json # TypeScript configuration
βββ README.md
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β ANGULAR FRONTEND β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β 1. User enters puzzle values β β
β β β Two-way binding updates component.board[][] β β
β β β β
β β 2. User clicks "Solve Puzzle" β β
β β β solvePuzzle() invoked β β
β β β Deep clone board to track original state β β
β β β Convert null β 0 for API transmission β β
β β β β
β β 3. SudokuApiService.solve(board) β β
β β β POST request with Observable β β
β β β User sees "Solving..." message β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
β HTTP POST /api/solve
β Content-Type: application/json
β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β SPRING BOOT BACKEND β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β 1. SudokuController receives request β β
β β β Deserialize JSON to SudokuRequest β β
β β β Extract 9x9 board array β β
β β β β
β β 2. SudokuService.solve(board) β β
β β β Invoke solveSudoku() recursive algorithm β β
β β β Validate constraints via isSafe() β β
β β β Execute backtracking on empty cells β β
β β β β
β β 3. Generate SudokuResponse β β
β β β Populate solvedBoard with completed array β β
β β β Set status: "SOLVED" or "INVALID_PUZZLE" β β
β β β Serialize to JSON response β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
β HTTP 200 OK
β { solvedBoard: [...], status: "SOLVED" }
β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β ANGULAR FRONTEND β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β 1. Observable subscription resolved β β
β β β next() callback invoked with SudokuResponse β β
β β β β
β β 2. Update component state β β
β β β Assign solvedBoard to component.board[][] β β
β β β Set message to "Puzzle Solved! β
" β β
β β β Solved cells highlighted with CSS class β β
β β β Original empty cells remain visually distinct β β
β β β β
β β 3. Error handling β β
β β β If error: display "This puzzle is not valid..." β β
β β β Log error details to console β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Recursive Backtracking: Efficient depth-first search with constraint propagation
β Constraint Validation: Row, column, and 3x3 box uniqueness checks
β Optimized Pruning: Invalid candidates eliminated before recursion
β Interactive Grid: Real-time input with visual feedback
β Two-Way Data Binding: Instant model-view synchronization
β Input Validation: Pattern matching and client-side constraints
β State Management: Original vs. solved cell differentiation
β Clear Messaging: Success/failure status updates
β RESTful Design: Standard HTTP conventions for API clarity
β Asynchronous Communication: Non-blocking Observable patterns
β CORS Support: Secure cross-origin communication
β Type Safety: TypeScript interfaces and Java data transfer objects
β Separation of Concerns: Frontend service layer, backend business logic
- Java 17+ (for Spring Boot backend)
- Node.js 18+ (for Angular frontend)
- Maven 3.6+ (for building Spring Boot application)
cd sudoku-api
# Build the application
./mvnw clean package
# Run the application
./mvnw spring-boot:runThe API will be available at http://localhost:8080
Verify: Test the endpoint:
curl -X POST http://localhost:8080/api/solve \
-H "Content-Type: application/json" \
-d '{
"board": [
[0, 0, 3, 0, 7, 0, 0, 2, 0],
[0, 9, 0, 0, 8, 5, 7, 0, 0],
[3, 0, 0, 9, 0, 0, 0, 0, 5],
[1, 0, 0, 0, 0, 0, 0, 8, 0],
[0, 0, 0, 0, 0, 0, 3, 0, 0],
[0, 0, 0, 0, 9, 0, 0, 0, 7],
[2, 0, 0, 6, 0, 0, 0, 0, 1],
[0, 4, 8, 0, 0, 0, 6, 0, 0],
[0, 3, 0, 0, 0, 0, 0, 4, 0]
]
}'cd sudoku-ui
# Install dependencies
npm install
# Start the development server
npm startThe application will open at http://localhost:4200
Available Commands:
npm startβ Start development servernpm run buildβ Production buildnpm testβ Run unit tests
| Dependency | Version | Purpose |
|---|---|---|
| Spring Boot Starter Web | 3.5.4 | REST API framework |
| Lombok | Latest | Reduce boilerplate annotations |
| Spring Boot Starter Test | 3.5.4 | Testing framework |
| Dependency | Version | Purpose |
|---|---|---|
| @angular/core | 19.0.0 | Core Angular framework |
| @angular/common | 19.0.0 | Common utilities and directives |
| @angular/forms | 19.0.0 | Form handling and two-way binding |
| @angular/platform-browser | 19.0.0 | DOM rendering |
| rxjs | 7.8.0 | Reactive programming library |
| TypeScript | 5.6.2 | Type-safe JavaScript |
The Sudoku solver uses a backtracking algorithm that explores the solution space systematically while pruning invalid branches:
- Find Empty Cell: Iterate through the 9x9 board seeking unfilled cells (value = 0)
- Try Candidates: For each empty cell, attempt digits 1β9
- Validate Constraints: Before placement, verify the digit doesn't violate Sudoku rules
- Recurse: If valid, place the digit and recursively solve the remainder
- Backtrack: If recursion fails, reset the cell (0) and try the next candidate
- Success: When all cells are filled and constraints satisfied, return
true
- Worst Case: O(9^(n)) where n = number of empty cells
- Average Case: Significantly reduced by constraint elimination
- Practical Performance: Most puzzles solve in milliseconds due to:
- Early constraint checking
- Aggressive backtracking
- Minimal branching factor per cell
Standard Sudoku has the property of constraint propagation: each placement eliminates multiple candidates from related cells. The recursive backtracking algorithm leverages this by:
- Validating placement before recursing (saves unnecessary exploration)
- Terminating immediately upon finding a valid solution
- Returning
falsewhen no candidates exist (triggers backtrack)
Solves a provided Sudoku puzzle using recursive backtracking.
Request:
{
"board": [
[0, 0, 3, 0, 7, 0, 0, 2, 0],
[0, 9, 0, 0, 8, 5, 7, 0, 0],
[3, 0, 0, 9, 0, 0, 0, 0, 5],
[1, 0, 0, 0, 0, 0, 0, 8, 0],
[0, 0, 0, 0, 0, 0, 3, 0, 0],
[0, 0, 0, 0, 9, 0, 0, 0, 7],
[2, 0, 0, 6, 0, 0, 0, 0, 1],
[0, 4, 8, 0, 0, 0, 6, 0, 0],
[0, 3, 0, 0, 0, 0, 0, 4, 0]
]
}Response (200 OK):
{
"solvedBoard": [
[5, 6, 3, 1, 7, 4, 9, 2, 8],
[4, 9, 1, 2, 8, 5, 7, 3, 6],
[3, 7, 2, 9, 6, 8, 1, 5, 4],
[1, 5, 9, 3, 4, 2, 5, 8, 3],
[7, 8, 4, 5, 1, 6, 3, 9, 2],
[6, 2, 5, 8, 9, 3, 4, 1, 7],
[2, 1, 7, 6, 5, 9, 8, 3, 1],
[9, 4, 8, 7, 3, 1, 6, 2, 5],
[8, 3, 6, 4, 2, 7, 5, 4, 9]
],
"status": "SOLVED"
}Response (400 Bad Request):
{
"solvedBoard": [...],
"status": "INVALID_PUZZLE"
}cd sudoku-api
./mvnw clean package # Compile, test, package
./mvnw spring-boot:run # Run applicationcd sudoku-ui
npm install # Install dependencies
npm start # Development server with hot reload
npm run build # Optimized production buildOpen two terminal windows:
Terminal 1 β Backend:
cd sudoku-api && ./mvnw spring-boot:runTerminal 2 β Frontend:
cd sudoku-ui && npm startNavigate to http://localhost:4200 and enter a Sudoku puzzle to test.
Vignay Bandari
Built with β€οΈ for Sudoku enthusiasts and algorithm learners.