Skip to content

vignayy/sudoku-app

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

5 Commits
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Sudoku Solver – Algorithmic Web Application

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


Overview

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.


Core Capabilities

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

Technical Architecture

Backend: Spring Boot REST API

The backend serves as the computational engine, implementing a recursive backtracking algorithm optimized for 9x9 constraint satisfaction.

Technology Stack

  • Framework: Spring Boot 3.5.4
  • Language: Java 17
  • Build Tool: Maven
  • Dependencies: Spring Web, Lombok

Core Algorithm: Recursive Backtracking

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:

  1. Row Uniqueness: No duplicate in the current row
  2. Column Uniqueness: No duplicate in the current column
  3. 3x3 Box Uniqueness: No duplicate in the 3x3 subgrid

REST Endpoint

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.


Frontend: Angular Single-Page Application

The frontend delivers an interactive, responsive user experience with real-time validation and asynchronous state management via RxJS.

Technology Stack

  • 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

User Experience Features

Two-Way Data Binding:

[(ngModel)]="board[i][j]"  // Real-time input synchronization

Users 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.

Service Layer: RxJS Observables

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

Component Integration

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

Project Structure

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

Frontend-Backend Interaction Flow

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                     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                      β”‚ β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Features

Algorithmic Excellence

βœ“ 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

User Experience

βœ“ 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

Architecture & Performance

βœ“ 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


Getting Started

Prerequisites

  • Java 17+ (for Spring Boot backend)
  • Node.js 18+ (for Angular frontend)
  • Maven 3.6+ (for building Spring Boot application)

Backend Setup (Spring Boot)

cd sudoku-api

# Build the application
./mvnw clean package

# Run the application
./mvnw spring-boot:run

The 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]
    ]
  }'

Frontend Setup (Angular)

cd sudoku-ui

# Install dependencies
npm install

# Start the development server
npm start

The application will open at http://localhost:4200

Available Commands:

  • npm start – Start development server
  • npm run build – Production build
  • npm test – Run unit tests

Technical Specifications

Backend Dependencies

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

Frontend Dependencies

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

Algorithm Deep Dive: Recursive Backtracking Efficiency

The Sudoku solver uses a backtracking algorithm that explores the solution space systematically while pruning invalid branches:

Execution Steps

  1. Find Empty Cell: Iterate through the 9x9 board seeking unfilled cells (value = 0)
  2. Try Candidates: For each empty cell, attempt digits 1–9
  3. Validate Constraints: Before placement, verify the digit doesn't violate Sudoku rules
  4. Recurse: If valid, place the digit and recursively solve the remainder
  5. Backtrack: If recursion fails, reset the cell (0) and try the next candidate
  6. Success: When all cells are filled and constraints satisfied, return true

Time Complexity Analysis

  • 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

Why This Approach Works

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 false when no candidates exist (triggers backtrack)

API Reference

POST /api/solve

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"
}

Development Workflow

Building the Backend

cd sudoku-api
./mvnw clean package          # Compile, test, package
./mvnw spring-boot:run        # Run application

Building the Frontend

cd sudoku-ui
npm install                   # Install dependencies
npm start                     # Development server with hot reload
npm run build                 # Optimized production build

Running Both Concurrently

Open two terminal windows:

Terminal 1 – Backend:

cd sudoku-api && ./mvnw spring-boot:run

Terminal 2 – Frontend:

cd sudoku-ui && npm start

Navigate to http://localhost:4200 and enter a Sudoku puzzle to test.


Author

Vignay Bandari


Built with ❀️ for Sudoku enthusiasts and algorithm learners.

About

Algorithmic Web Application

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors