body {
font-family: ‘Segoe UI’, Tahoma, Geneva, Verdana, sans-serif;
display: flex;
flex-direction: column;
align-items: center;
padding: 20px;
background-color: #f9f9f9;
color: #333;
}
.container {
max-width: 500px;
width: 100%;
margin: 0 auto;
}
h1 {
color: #2c3e50;
text-align: center;
margin-bottom: 20px;
}
.difficulty-controls {
display: flex;
justify-content: center;
margin-bottom: 20px;
flex-wrap: wrap;
gap: 10px;
}
button {
background-color: #3498db;
color: white;
border: none;
padding: 8px 15px;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
transition: background-color 0.2s;
}
button:hover {
background-color: #2980b9;
}
button.active {
background-color: #2c3e50;
}
.sudoku-container {
display: grid;
grid-template-columns: repeat(9, 1fr);
gap: 1px;
margin: 0 auto;
border: 2px solid #2c3e50;
max-width: 100%;
aspect-ratio: 1 / 1;
background-color: #2c3e50;
}
.cell {
background-color: white;
display: flex;
justify-content: center;
align-items: center;
font-size: calc(10px + 2vmin);
font-weight: bold;
aspect-ratio: 1 / 1;
position: relative;
cursor: pointer;
}
.cell:focus {
outline: 2px solid #3498db;
}
.cell.given {
background-color: #f0f0f0;
color: #2c3e50;
}
.cell.selected {
background-color: #d6eaf8;
}
.cell.invalid {
color: #e74c3c;
}
.cell.same-value {
background-color: #eaecee;
}
.cell.highlight-row, .cell.highlight-col, .cell.highlight-box {
background-color: #ebf5fb;
}
/* Borders for 3×3 boxes */
.cell:nth-child(3n) {
border-right: 2px solid #2c3e50;
}
.cell:nth-child(9n) {
border-right: none;
}
.cell:nth-child(n+19):nth-child(-n+27),
.cell:nth-child(n+46):nth-child(-n+54) {
border-bottom: 2px solid #2c3e50;
}
.game-controls {
display: flex;
justify-content: center;
margin-top: 20px;
flex-wrap: wrap;
gap: 10px;
}
.number-pad {
display: grid;
grid-template-columns: repeat(5, 1fr);
gap: 5px;
max-width: 300px;
margin: 15px auto;
}
.number-btn {
padding: 10px;
font-size: 16px;
}
.controls-container {
display: flex;
flex-direction: column;
align-items: center;
margin-top: 10px;
width: 100%;
}
.message {
margin-top: 15px;
padding: 10px;
text-align: center;
border-radius: 4px;
font-weight: bold;
}
.success {
background-color: #d4edda;
color: #155724;
}
.error {
background-color: #f8d7da;
color: #721c24;
}
.timer {
margin-top: 10px;
font-size: 16px;
font-weight: bold;
}
@media (max-width: 500px) {
.sudoku-container {
width: 100%;
}
.cell {
font-size: 16px;
}
.number-pad {
grid-template-columns: repeat(5, 1fr);
}
}
Sudoku
Medium
Hard
New Game
2
3
4
5
6
7
8
9
Clear
Solve Puzzle
Get Hint
document.addEventListener(‘DOMContentLoaded’, () => {
const grid = document.getElementById(‘sudoku-grid’);
const newGameBtn = document.getElementById(‘new-game-btn’);
const validateBtn = document.getElementById(‘validate-btn’);
const solveBtn = document.getElementById(‘solve-btn’);
const hintBtn = document.getElementById(‘hint-btn’);
const easyBtn = document.getElementById(‘easy-btn’);
const mediumBtn = document.getElementById(‘medium-btn’);
const hardBtn = document.getElementById(‘hard-btn’);
const numberBtns = document.querySelectorAll(‘.number-btn’);
const messageElement = document.getElementById(‘message’);
const timerElement = document.getElementById(‘timer’);
let board = [];
let solution = [];
let selectedCell = null;
let difficulty = ‘easy’;
let startTime = null;
let timerInterval = null;
let gameOver = false;
// Set up the grid
function initializeGrid() {
grid.innerHTML = ”;
for (let row = 0; row < 9; row++) {
for (let col = 0; col < 9; col++) {
const cell = document.createElement('div');
cell.className = 'cell';
cell.dataset.row = row;
cell.dataset.col = col;
cell.tabIndex = 0; // Make cell focusable
// Handle click events
cell.addEventListener('click', () => selectCell(cell));
// Handle keyboard events
cell.addEventListener(‘keydown’, (e) => {
if (gameOver) return;
if (e.key >= ‘1’ && e.key Array(9).fill(0));
solution = Array(9).fill().map(() => Array(9).fill(0));
// Fill diagonal boxes (they can be filled independently)
fillDiagonalBoxes();
// Solve the rest of the board
solveSudoku(solution);
// Create the playing board by removing numbers
copyBoard(solution, board);
// Remove numbers based on difficulty
removeNumbers(difficulty);
// Update the UI
updateBoardUI();
// Start the timer
startTimer();
}
function fillDiagonalBoxes() {
for (let box = 0; box < 9; box += 3) {
fillBox(box, box);
}
}
function fillBox(row, col) {
const nums = shuffleArray([1, 2, 3, 4, 5, 6, 7, 8, 9]);
let index = 0;
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
solution[row + i][col + j] = nums[index++];
}
}
}
function shuffleArray(array) {
for (let i = array.length - 1; i > 0; i–) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
return array;
}
function solveSudoku(grid) {
for (let row = 0; row < 9; row++) {
for (let col = 0; col < 9; col++) {
// Skip filled cells
if (grid[row][col] !== 0) continue;
// Try each number
for (let num = 1; num {
messageElement.style.display = 'none';
}, 5000);
}
function startTimer() {
// Clear existing timer
stopTimer();
// Reset the timer display
timerElement.textContent = 'Time: 00:00';
// Start the timer
startTime = new Date();
timerInterval = setInterval(updateTimer, 1000);
gameOver = false;
}
function updateTimer() {
const currentTime = new Date();
const elapsedTime = Math.floor((currentTime - startTime) / 1000);
const minutes = Math.floor(elapsedTime / 60).toString().padStart(2, '0');
const seconds = (elapsedTime % 60).toString().padStart(2, '0');
timerElement.textContent = ;
}
function stopTimer() {
clearInterval(timerInterval);
}
function provideHint() {
if (gameOver) return;
// Find an empty cell
const emptyCells = [];
for (let row = 0; row < 9; row++) {
for (let col = 0; col < 9; col++) {
if (board[row][col] === 0) {
emptyCells.push({ row, col });
}
}
}
// If there are empty cells, fill one with the correct value
if (emptyCells.length > 0) {
const randomIndex = Math.floor(Math.random() * emptyCells.length);
const { row, col } = emptyCells[randomIndex];
board[row][col] = solution[row][col];
// Update the UI
const cell = document.querySelector();
cell.textContent = board[row][col];
cell.classList.add(‘given’);
// Check for completion
if (isBoardFull()) {
checkSolution();
}
} else {
showMessage(‘The board is already filled.’, ‘error’);
}
}
function solvePuzzle() {
// Fill the board with the solution
copyBoard(solution, board);
updateBoardUI();
stopTimer();
gameOver = true;
showMessage(‘Puzzle solved!’, ‘success’);
}
// Set up event listeners
newGameBtn.addEventListener(‘click’, () => {
generateSudoku(difficulty);
});
validateBtn.addEventListener(‘click’, () => {
checkSolution();
});
solveBtn.addEventListener(‘click’, () => {
solvePuzzle();
});
hintBtn.addEventListener(‘click’, () => {
provideHint();
});
// Difficulty buttons
easyBtn.addEventListener(‘click’, () => {
difficulty = ‘easy’;
easyBtn.classList.add(‘active’);
mediumBtn.classList.remove(‘active’);
hardBtn.classList.remove(‘active’);
generateSudoku(difficulty);
});
mediumBtn.addEventListener(‘click’, () => {
difficulty = ‘medium’;
easyBtn.classList.remove(‘active’);
mediumBtn.classList.add(‘active’);
hardBtn.classList.remove(‘active’);
generateSudoku(difficulty);
});
hardBtn.addEventListener(‘click’, () => {
difficulty = ‘hard’;
easyBtn.classList.remove(‘active’);
mediumBtn.classList.remove(‘active’);
hardBtn.classList.add(‘active’);
generateSudoku(difficulty);
});
// Number buttons
numberBtns.forEach(btn => {
btn.addEventListener(‘click’, () => {
if (!selectedCell || gameOver) return;
const number = parseInt(btn.dataset.number);
setValueToSelectedCell(number);
});
});
// Initialize the game
initializeGrid();
generateSudoku(difficulty);
});