only-a-test / index.html
adilchbada's picture
improve the ui - Initial Deployment
baa3915 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Sudoku Master</title>
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://unpkg.com/aos@2.3.1/dist/aos.css" rel="stylesheet">
<script src="https://unpkg.com/aos@2.3.1/dist/aos.js"></script>
<script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script>
<script src="https://unpkg.com/feather-icons"></script>
<style>
.sudoku-cell {
width: 100%;
height: 100%;
text-align: center;
font-size: 1.5rem;
font-weight: bold;
border: none;
outline: none;
background-color: white;
transition: all 0.2s ease;
}
.sudoku-cell:focus {
background-color: rgba(59, 130, 246, 0.1);
box-shadow: inset 0 0 0 2px #3b82f6;
z-index: 10;
}
.sudoku-grid {
display: grid;
grid-template-columns: repeat(9, 1fr);
grid-template-rows: repeat(9, 1fr);
gap: 1px;
width: 450px;
height: 450px;
border: 2px solid #1e40af;
}
.sudoku-cell:nth-child(3n) {
border-right: 3px solid #1e40af;
}
.sudoku-cell:nth-child(9n) {
border-right: none;
}
.sudoku-row:nth-child(3n) {
border-bottom: 3px solid #1e40af;
}
.sudoku-row:last-child {
border-bottom: none;
}
.fixed-number {
color: #1e40af;
font-weight: 800;
}
.error {
color: #ef4444;
background-color: #fee2e2;
animation: shake 0.5s;
}
@keyframes shake {
0%, 100% { transform: translateX(0); }
25% { transform: translateX(-5px); }
75% { transform: translateX(5px); }
}
@media (max-width: 640px) {
.sudoku-grid {
width: 90vw;
height: 90vw;
}
.sudoku-cell {
font-size: 1rem;
}
}
</style>
</head>
<body class="bg-gradient-to-br from-blue-50 to-indigo-50 min-h-screen flex flex-col items-center justify-center p-4">
<div class="max-w-4xl w-full">
<header class="text-center mb-8" data-aos="fade-down">
<h1 class="text-5xl font-bold bg-gradient-to-r from-blue-600 to-indigo-600 bg-clip-text text-transparent mb-2">Sudoku Master</h1>
<p class="text-gray-600 text-lg">Challenge your mind with this classic puzzle game</p>
</header>
<div class="flex flex-col lg:flex-row items-center justify-center gap-8">
<div class="bg-white rounded-2xl shadow-2xl p-6 border-2 border-blue-100" data-aos="zoom-in">
<div class="sudoku-grid bg-gray-200">
<!-- Sudoku grid will be generated by JavaScript -->
</div>
</div>
<div class="bg-white rounded-2xl shadow-2xl p-8 w-full max-w-md border-2 border-blue-100" data-aos="fade-left">
<div class="flex justify-between items-center mb-6">
<div>
<h2 class="text-xl font-semibold text-gray-800">Game Controls</h2>
<p class="text-gray-500 text-sm">Difficulty: <span id="difficulty-level" class="font-medium">Medium</span></p>
</div>
<div class="flex items-center space-x-2">
<span id="timer" class="text-lg font-mono bg-gray-100 px-3 py-1 rounded">00:00</span>
<button id="pause-btn" class="p-2 rounded-full bg-gray-100 hover:bg-gray-200">
<i data-feather="pause"></i>
</button>
</div>
</div>
<div class="grid grid-cols-3 gap-3 mb-6">
<button id="easy-btn" class="btn-difficulty py-3 px-4 rounded-xl bg-green-50 text-green-700 hover:bg-green-100 border-2 border-green-200 font-medium transition-all" data-level="easy">Easy</button>
<button id="medium-btn" class="btn-difficulty py-3 px-4 rounded-xl bg-blue-50 text-blue-700 hover:bg-blue-100 border-2 border-blue-200 font-medium transition-all" data-level="medium">Medium</button>
<button id="hard-btn" class="btn-difficulty py-3 px-4 rounded-xl bg-red-50 text-red-700 hover:bg-red-100 border-2 border-red-200 font-medium transition-all" data-level="hard">Hard</button>
</div>
<div class="grid grid-cols-3 gap-3 mb-6">
<button id="new-game-btn" class="col-span-2 py-3 px-6 rounded-xl bg-indigo-600 hover:bg-indigo-700 text-white flex items-center justify-center shadow-md hover:shadow-lg transition-all">
<i data-feather="refresh-cw" class="mr-2"></i> New Game
</button>
<button id="hint-btn" class="py-3 px-6 rounded-xl bg-amber-500 hover:bg-amber-600 text-white flex items-center justify-center shadow-md hover:shadow-lg transition-all">
<i data-feather="help-circle" class="mr-2"></i> Hint
</button>
</div>
<div class="grid grid-cols-2 gap-3">
<button id="check-btn" class="py-3 px-6 rounded-xl bg-gray-800 hover:bg-gray-900 text-white flex items-center justify-center shadow-md hover:shadow-lg transition-all">
<i data-feather="check" class="mr-2"></i> Check
</button>
<button id="solve-btn" class="py-3 px-6 rounded-xl bg-purple-600 hover:bg-purple-700 text-white flex items-center justify-center shadow-md hover:shadow-lg transition-all">
<i data-feather="eye" class="mr-2"></i> Solution
</button>
</div>
</div>
</div>
<div class="mt-8 text-center text-gray-500 text-sm" data-aos="fade-up">
<p>Use numbers 1-9 to fill the grid. Each row, column, and 3x3 box must contain all digits from 1 to 9.</p>
</div>
</div>
<script>
// Initialize AOS animations
AOS.init({
duration: 800,
once: true
});
// Initialize feather icons
feather.replace();
// Sudoku game logic
document.addEventListener('DOMContentLoaded', function() {
const sudokuGrid = document.querySelector('.sudoku-grid');
const newGameBtn = document.getElementById('new-game-btn');
const hintBtn = document.getElementById('hint-btn');
const checkBtn = document.getElementById('check-btn');
const solveBtn = document.getElementById('solve-btn');
const difficultyBtns = document.querySelectorAll('.btn-difficulty');
const difficultyLevel = document.getElementById('difficulty-level');
const timerElement = document.getElementById('timer');
const pauseBtn = document.getElementById('pause-btn');
let board = Array(9).fill().map(() => Array(9).fill(0));
let solution = Array(9).fill().map(() => Array(9).fill(0));
let fixedCells = Array(9).fill().map(() => Array(9).fill(false));
let selectedCell = null;
let timerInterval;
let seconds = 0;
let isPaused = false;
let currentDifficulty = 'medium';
// Initialize the game
function initGame() {
createGrid();
generatePuzzle(currentDifficulty);
startTimer();
addEventListeners();
}
// Create the Sudoku grid
function createGrid() {
sudokuGrid.innerHTML = '';
for (let i = 0; i < 9; i++) {
for (let j = 0; j < 9; j++) {
const cell = document.createElement('input');
cell.type = 'text';
cell.maxLength = 1;
cell.className = 'sudoku-cell';
cell.dataset.row = i;
cell.dataset.col = j;
sudokuGrid.appendChild(cell);
}
}
}
// Generate a Sudoku puzzle
function generatePuzzle(difficulty) {
// Reset the board
board = Array(9).fill().map(() => Array(9).fill(0));
fixedCells = Array(9).fill().map(() => Array(9).fill(false));
// Generate a complete solution
generateSolution(0, 0);
solution = JSON.parse(JSON.stringify(board));
// Remove numbers based on difficulty
let cellsToRemove;
switch(difficulty) {
case 'easy':
cellsToRemove = 40;
break;
case 'hard':
cellsToRemove = 60;
break;
default: // medium
cellsToRemove = 50;
}
// Remove numbers while keeping the puzzle solvable
let cellsRemoved = 0;
while (cellsRemoved < cellsToRemove) {
const row = Math.floor(Math.random() * 9);
const col = Math.floor(Math.random() * 9);
if (board[row][col] !== 0) {
const temp = board[row][col];
board[row][col] = 0;
// Check if the puzzle still has a unique solution
const tempBoard = JSON.parse(JSON.stringify(board));
if (solveSudoku(tempBoard)) {
cellsRemoved++;
} else {
board[row][col] = temp;
}
}
}
// Mark fixed cells and update the UI
updateUI();
}
// Generate a complete Sudoku solution
function generateSolution(row, col) {
if (row === 9) {
return true;
}
if (col === 9) {
return generateSolution(row + 1, 0);
}
if (board[row][col] !== 0) {
return generateSolution(row, col + 1);
}
const nums = [1, 2, 3, 4, 5, 6, 7, 8, 9];
shuffleArray(nums);
for (let num of nums) {
if (isValidPlacement(row, col, num)) {
board[row][col] = num;
if (generateSolution(row, col + 1)) {
return true;
}
board[row][col] = 0;
}
}
return false;
}
// Shuffle an array
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;
}
// Check if a number can be placed in a cell
function isValidPlacement(row, col, num) {
// Check row
for (let i = 0; i < 9; i++) {
if (board[row][i] === num) return false;
}
// Check column
for (let i = 0; i < 9; i++) {
if (board[i][col] === num) return false;
}
// Check 3x3 box
const boxRow = Math.floor(row / 3) * 3;
const boxCol = Math.floor(col / 3) * 3;
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
if (board[boxRow + i][boxCol + j] === num) return false;
}
}
return true;
}
// Update the UI based on the current board state
function updateUI() {
const cells = document.querySelectorAll('.sudoku-cell');
cells.forEach(cell => {
const row = parseInt(cell.dataset.row);
const col = parseInt(cell.dataset.col);
if (board[row][col] !== 0) {
cell.value = board[row][col];
if (fixedCells[row][col]) {
cell.classList.add('fixed-number');
cell.readOnly = true;
} else {
cell.classList.remove('fixed-number');
cell.readOnly = false;
}
} else {
cell.value = '';
cell.classList.remove('fixed-number');
cell.readOnly = false;
}
cell.classList.remove('error');
});
}
// Solve the Sudoku puzzle
function solveSudoku(grid) {
for (let row = 0; row < 9; row++) {
for (let col = 0; col < 9; col++) {
if (grid[row][col] === 0) {
for (let num = 1; num <= 9; num++) {
if (isValidPlacementForGrid(grid, row, col, num)) {
grid[row][col] = num;
if (solveSudoku(grid)) {
return true;
}
grid[row][col] = 0;
}
}
return false;
}
}
}
return true;
}
// Check if a number can be placed in a cell for a given grid
function isValidPlacementForGrid(grid, row, col, num) {
// Check row
for (let i = 0; i < 9; i++) {
if (grid[row][i] === num) return false;
}
// Check column
for (let i = 0; i < 9; i++) {
if (grid[i][col] === num) return false;
}
// Check 3x3 box
const boxRow = Math.floor(row / 3) * 3;
const boxCol = Math.floor(col / 3) * 3;
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
if (grid[boxRow + i][boxCol + j] === num) return false;
}
}
return true;
}
// Start the game timer
function startTimer() {
clearInterval(timerInterval);
seconds = 0;
updateTimerDisplay();
timerInterval = setInterval(() => {
if (!isPaused) {
seconds++;
updateTimerDisplay();
}
}, 1000);
}
// Update the timer display
function updateTimerDisplay() {
const minutes = Math.floor(seconds / 60);
const remainingSeconds = seconds % 60;
timerElement.textContent = `${minutes.toString().padStart(2, '0')}:${remainingSeconds.toString().padStart(2, '0')}`;
}
// Add event listeners
function addEventListeners() {
// Cell input
const cells = document.querySelectorAll('.sudoku-cell');
cells.forEach(cell => {
cell.addEventListener('input', function(e) {
const row = parseInt(this.dataset.row);
const col = parseInt(this.dataset.col);
const value = parseInt(this.value) || 0;
if (value >= 1 && value <= 9) {
board[row][col] = value;
this.classList.remove('error');
} else if (this.value === '') {
board[row][col] = 0;
this.classList.remove('error');
} else {
this.value = '';
}
});
cell.addEventListener('click', function() {
cells.forEach(c => c.classList.remove('bg-blue-100'));
this.classList.add('bg-blue-100');
selectedCell = this;
});
cell.addEventListener('keydown', function(e) {
if (e.key === 'ArrowUp' || e.key === 'ArrowDown' ||
e.key === 'ArrowLeft' || e.key === 'ArrowRight') {
e.preventDefault();
moveSelection(e.key);
}
});
});
// Keyboard number input
document.addEventListener('keydown', function(e) {
if (selectedCell && !selectedCell.readOnly && e.key >= '1' && e.key <= '9') {
selectedCell.value = e.key;
const row = parseInt(selectedCell.dataset.row);
const col = parseInt(selectedCell.dataset.col);
board[row][col] = parseInt(e.key);
selectedCell.classList.remove('error');
} else if (selectedCell && e.key === 'Backspace') {
selectedCell.value = '';
const row = parseInt(selectedCell.dataset.row);
const col = parseInt(selectedCell.dataset.col);
board[row][col] = 0;
selectedCell.classList.remove('error');
}
});
// New game button
newGameBtn.addEventListener('click', function() {
generatePuzzle(currentDifficulty);
startTimer();
});
// Hint button
hintBtn.addEventListener('click', function() {
if (selectedCell && selectedCell.value === '') {
const row = parseInt(selectedCell.dataset.row);
const col = parseInt(selectedCell.dataset.col);
selectedCell.value = solution[row][col];
board[row][col] = solution[row][col];
selectedCell.classList.add('fixed-number');
selectedCell.readOnly = true;
fixedCells[row][col] = true;
}
});
// Check button
checkBtn.addEventListener('click', function() {
const cells = document.querySelectorAll('.sudoku-cell');
let hasErrors = false;
cells.forEach(cell => {
if (!cell.readOnly && cell.value !== '') {
const row = parseInt(cell.dataset.row);
const col = parseInt(cell.dataset.col);
if (parseInt(cell.value) !== solution[row][col]) {
cell.classList.add('error');
hasErrors = true;
} else {
cell.classList.remove('error');
}
}
});
if (!hasErrors) {
alert('Congratulations! Your solution is correct!');
}
});
// Solve button
solveBtn.addEventListener('click', function() {
if (confirm('Are you sure you want to see the solution?')) {
board = JSON.parse(JSON.stringify(solution));
updateUI();
clearInterval(timerInterval);
}
});
// Difficulty buttons
difficultyBtns.forEach(btn => {
btn.addEventListener('click', function() {
currentDifficulty = this.dataset.level;
difficultyLevel.textContent = this.dataset.level.charAt(0).toUpperCase() + this.dataset.level.slice(1);
difficultyBtns.forEach(b => b.classList.remove('ring-2', 'ring-offset-2', 'ring-blue-500'));
this.classList.add('ring-2', 'ring-offset-2', 'ring-blue-500');
generatePuzzle(currentDifficulty);
startTimer();
});
});
// Pause button
pauseBtn.addEventListener('click', function() {
isPaused = !isPaused;
if (isPaused) {
this.innerHTML = feather.icons['play'].toSvg();
} else {
this.innerHTML = feather.icons['pause'].toSvg();
}
});
}
// Move selection with arrow keys
function moveSelection(direction) {
if (!selectedCell) return;
const row = parseInt(selectedCell.dataset.row);
const col = parseInt(selectedCell.dataset.col);
let newRow = row;
let newCol = col;
switch(direction) {
case 'ArrowUp': newRow = Math.max(0, row - 1); break;
case 'ArrowDown': newRow = Math.min(8, row + 1); break;
case 'ArrowLeft': newCol = Math.max(0, col - 1); break;
case 'ArrowRight': newCol = Math.min(8, col + 1); break;
}
if (newRow !== row || newCol !== col) {
const cells = document.querySelectorAll('.sudoku-cell');
cells.forEach(c => c.classList.remove('bg-blue-100'));
const newCell = document.querySelector(`.sudoku-cell[data-row="${newRow}"][data-col="${newCol}"]`);
newCell.classList.add('bg-blue-100');
newCell.focus();
selectedCell = newCell;
}
}
// Start the game
initGame();
});
</script>
</body>
</html>