Phoenix21's picture
Update Sudoku Game JAR files
4dd7406 verified
let currentGameId = null;
let currentBoard = [];
let originalBoard = [];
let selectedCell = null;
// Initialize the game when page loads
document.addEventListener('DOMContentLoaded', () => {
newGame();
});
async function newGame() {
const difficulty = document.getElementById('difficulty').value;
try {
const response = await fetch(`/api/sudoku/new/${difficulty}`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
currentGameId = data.gameId;
// Ensure puzzle is properly formatted
currentBoard = [];
for (let i = 0; i < 9; i++) {
const row = [];
for (let j = 0; j < 9; j++) {
row.push(data.puzzle[i][j] || '.');
}
currentBoard.push(row);
}
originalBoard = JSON.parse(JSON.stringify(currentBoard));
renderBoard();
showMessage('New game started! Good luck!', 'info');
// Focus on first empty cell
focusFirstEmptyCell();
} catch (error) {
showMessage('Error starting new game: ' + error.message, 'error');
console.error('Error:', error);
}
}
function renderBoard() {
const boardElement = document.getElementById('sudoku-board');
boardElement.innerHTML = '';
for (let i = 0; i < 9; i++) {
for (let j = 0; j < 9; j++) {
const cell = document.createElement('div');
cell.className = 'cell';
cell.dataset.row = i;
cell.dataset.col = j;
// Create input element for each cell
const input = document.createElement('input');
input.type = 'text';
input.className = 'cell-input';
input.maxLength = 1;
input.dataset.row = i;
input.dataset.col = j;
const value = currentBoard[i][j];
const isFixed = originalBoard[i][j] !== '.';
if (value !== '.') {
input.value = value;
}
if (isFixed) {
cell.classList.add('fixed');
input.readOnly = true;
input.tabIndex = -1; // Skip fixed cells when tabbing
} else {
// Add input event listeners for editable cells
input.addEventListener('input', handleCellInput);
input.addEventListener('focus', handleCellFocus);
input.addEventListener('keydown', handleCellKeydown);
}
cell.appendChild(input);
boardElement.appendChild(cell);
}
}
}
function handleCellInput(event) {
const input = event.target;
const row = parseInt(input.dataset.row);
const col = parseInt(input.dataset.col);
const value = input.value;
// Only allow numbers 1-9
if (value && !/^[1-9]$/.test(value)) {
input.value = '';
return;
}
// Update the board
if (value === '') {
currentBoard[row][col] = '.';
} else {
currentBoard[row][col] = value;
// Automatically move to next empty cell after entering a number
setTimeout(() => moveToNextEmptyCell(row, col), 100);
}
// Remove error styling if present
input.parentElement.classList.remove('error');
}
function handleCellFocus(event) {
const input = event.target;
const row = parseInt(input.dataset.row);
const col = parseInt(input.dataset.col);
selectedCell = { row, col };
// Select all text when focusing
input.select();
}
function handleCellKeydown(event) {
const input = event.target;
const row = parseInt(input.dataset.row);
const col = parseInt(input.dataset.col);
const key = event.key;
// Handle arrow key navigation
if (key === 'ArrowUp' || key === 'ArrowDown' || key === 'ArrowLeft' || key === 'ArrowRight') {
event.preventDefault();
navigateWithArrows(row, col, key);
}
// Handle Tab navigation (move to next empty cell)
else if (key === 'Tab') {
event.preventDefault();
if (event.shiftKey) {
moveToPreviousEmptyCell(row, col);
} else {
moveToNextEmptyCell(row, col);
}
}
// Handle Delete/Backspace
else if (key === 'Delete' || key === 'Backspace') {
event.preventDefault();
input.value = '';
currentBoard[row][col] = '.';
input.parentElement.classList.remove('error');
}
// Handle Enter key - validate current board
else if (key === 'Enter') {
event.preventDefault();
validateBoard();
}
// Handle number keys from numpad
else if (key >= '1' && key <= '9') {
event.preventDefault();
input.value = key;
currentBoard[row][col] = key;
setTimeout(() => moveToNextEmptyCell(row, col), 100);
}
}
function navigateWithArrows(row, col, key) {
let newRow = row;
let newCol = col;
switch(key) {
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;
}
const nextInput = document.querySelector(`input[data-row="${newRow}"][data-col="${newCol}"]`);
if (nextInput && !nextInput.readOnly) {
nextInput.focus();
}
}
function moveToNextEmptyCell(currentRow, currentCol) {
// Start from the next cell
let startIndex = currentRow * 9 + currentCol + 1;
for (let i = 0; i < 81; i++) {
let index = (startIndex + i) % 81;
let row = Math.floor(index / 9);
let col = index % 9;
if (originalBoard[row][col] === '.' && currentBoard[row][col] === '.') {
const input = document.querySelector(`input[data-row="${row}"][data-col="${col}"]`);
if (input) {
input.focus();
return;
}
}
}
}
function moveToPreviousEmptyCell(currentRow, currentCol) {
// Start from the previous cell
let startIndex = currentRow * 9 + currentCol - 1;
for (let i = 0; i < 81; i++) {
let index = (startIndex - i + 81) % 81;
let row = Math.floor(index / 9);
let col = index % 9;
if (originalBoard[row][col] === '.' && currentBoard[row][col] === '.') {
const input = document.querySelector(`input[data-row="${row}"][data-col="${col}"]`);
if (input) {
input.focus();
return;
}
}
}
}
function focusFirstEmptyCell() {
for (let i = 0; i < 9; i++) {
for (let j = 0; j < 9; j++) {
if (originalBoard[i][j] === '.' && currentBoard[i][j] === '.') {
const input = document.querySelector(`input[data-row="${i}"][data-col="${j}"]`);
if (input) {
input.focus();
return;
}
}
}
}
}
// Clear all non-fixed cells
function clearBoard() {
if (!confirm('Are you sure you want to clear all your entries?')) {
return;
}
for (let i = 0; i < 9; i++) {
for (let j = 0; j < 9; j++) {
if (originalBoard[i][j] === '.') {
currentBoard[i][j] = '.';
const input = document.querySelector(`input[data-row="${i}"][data-col="${j}"]`);
if (input) {
input.value = '';
input.parentElement.classList.remove('error', 'hint');
}
}
}
}
showMessage('Board cleared!', 'info');
focusFirstEmptyCell();
}
async function validateBoard() {
try {
// Ensure board is properly formatted as 2D array
const boardToSend = [];
for (let i = 0; i < 9; i++) {
const row = [];
for (let j = 0; j < 9; j++) {
row.push(currentBoard[i][j] || '.');
}
boardToSend.push(row);
}
const response = await fetch('/api/sudoku/validate', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
board: boardToSend,
gameId: currentGameId
})
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
// Clear all error styling first
document.querySelectorAll('.cell').forEach(cell => {
cell.classList.remove('error');
});
if (data.valid) {
if (data.complete) {
showMessage(data.message, 'success');
// Optionally disable all inputs when puzzle is solved
if (data.message.includes('Congratulations')) {
document.querySelectorAll('.cell-input').forEach(input => {
input.readOnly = true;
});
}
} else {
showMessage(data.message, 'info');
}
} else {
showMessage(data.message, 'error');
// Highlight cells with errors
highlightErrors();
}
} catch (error) {
showMessage('Error validating board: ' + error.message, 'error');
console.error('Error:', error);
}
}
function highlightErrors() {
// Check for duplicates in rows, columns, and boxes
for (let i = 0; i < 9; i++) {
for (let j = 0; j < 9; j++) {
if (currentBoard[i][j] !== '.') {
if (hasDuplicate(i, j, currentBoard[i][j])) {
const cell = document.querySelector(`input[data-row="${i}"][data-col="${j}"]`).parentElement;
cell.classList.add('error');
}
}
}
}
}
function hasDuplicate(row, col, value) {
// Check row
for (let j = 0; j < 9; j++) {
if (j !== col && currentBoard[row][j] === value) {
return true;
}
}
// Check column
for (let i = 0; i < 9; i++) {
if (i !== row && currentBoard[i][col] === value) {
return true;
}
}
// Check 3x3 box
const boxRow = Math.floor(row / 3) * 3;
const boxCol = Math.floor(col / 3) * 3;
for (let i = boxRow; i < boxRow + 3; i++) {
for (let j = boxCol; j < boxCol + 3; j++) {
if (i !== row && j !== col && currentBoard[i][j] === value) {
return true;
}
}
}
return false;
}
async function getHint() {
try {
// Ensure board is properly formatted as 2D array
const boardToSend = [];
for (let i = 0; i < 9; i++) {
const row = [];
for (let j = 0; j < 9; j++) {
row.push(currentBoard[i][j] || '.');
}
boardToSend.push(row);
}
const response = await fetch('/api/sudoku/hint', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
board: boardToSend,
gameId: currentGameId
})
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const newBoard = await response.json();
// Find and highlight the hint cell
for (let i = 0; i < 9; i++) {
for (let j = 0; j < 9; j++) {
if (currentBoard[i][j] === '.' && newBoard[i][j] !== '.') {
currentBoard = newBoard;
const input = document.querySelector(`input[data-row="${i}"][data-col="${j}"]`);
if (input) {
input.value = newBoard[i][j];
input.parentElement.classList.add('hint');
input.focus();
// Remove hint highlighting after animation
setTimeout(() => {
input.parentElement.classList.remove('hint');
}, 2000);
}
showMessage('Hint provided!', 'info');
return;
}
}
}
showMessage('No more hints available', 'info');
} catch (error) {
showMessage('Error getting hint: ' + error.message, 'error');
console.error('Error:', error);
}
}
async function showSolution() {
if (!confirm('Are you sure you want to see the solution? This will end the game.')) {
return;
}
try {
const response = await fetch(`/api/sudoku/solution/${currentGameId}`);
const solution = await response.json();
currentBoard = solution;
// Update all cells with solution
for (let i = 0; i < 9; i++) {
for (let j = 0; j < 9; j++) {
const input = document.querySelector(`input[data-row="${i}"][data-col="${j}"]`);
if (input) {
input.value = solution[i][j];
if (originalBoard[i][j] === '.') {
input.parentElement.classList.add('hint');
}
input.readOnly = true;
}
}
}
showMessage('Solution revealed!', 'info');
} catch (error) {
showMessage('Error getting solution', 'error');
console.error('Error:', error);
}
}
function showMessage(text, type) {
const messageElement = document.getElementById('message');
messageElement.textContent = text;
messageElement.className = `message ${type}`;
// Clear message after 5 seconds
setTimeout(() => {
messageElement.textContent = '';
messageElement.className = 'message';
}, 5000);
}