Programmes de Sudoku
Programme en JavaScript pour la manipulation de Sudoku
const startTime = new Date(); const GRID_SIZE=9; function checkGrid(grid, position, number) { // Check hline let hlineOk = true; for (let i = 0; i < GRID_SIZE; i++) { hlineOk = hlineOk && (grid[position.y][i] !== number); } // Check vline let vlineOk = true; for (let i = 0; i < GRID_SIZE; i++) { vlineOk = vlineOk && (grid[i][position.x] !== number); } // Check Square let squareOk = true; const squareXStart = Math.trunc(position.x / 3) * 3; const squareYStart = Math.trunc(position.y / 3) * 3; for (let x = 0; x < 3; x++) { for (let y = 0; y < 3; y++) { squareOk = squareOk && (grid[squareYStart+y][squareXStart+x] !== number); } } return squareOk && vlineOk && hlineOk; } function holesCompare(hole1, hole2) { return (hole1.possibilities.length - hole2.possibilities.length); } function solver(grid) { const workGrid = JSON.parse(JSON.stringify(grid)); let gridModified = false; const holes = []; // Step 1 : search each hole for (let x = 0; x < GRID_SIZE; x++) { for (let y = 0; y < GRID_SIZE; y++) { if (grid[y][x] === null) { holes.push({x, y, possibilities: []}); } } } // Step 1bis : Nothing to do ? Exit (for recursion) if (holes.length == 0) { return grid; } // NB : From that point, work only with workGrid // Step 2 : for each hole, enumerate possibilities for (let i = 0; i < holes.length; i++) { for (let number = 1; number <= 9; number++) { if (checkGrid(workGrid, holes[i], number)) { holes[i].possibilities.push(number); } } // If one hole has no possitibility > The grid is a failure if (holes[i].possibilities.length === 0) { return null; } } // Step 3 : Fill hole with only 1 possibility for (let i = 0; i < holes.length; i++) { if (holes[i].possibilities.length === 1) { if (checkGrid(workGrid, holes[i], holes[i].possibilities[0])) { workGrid[holes[i].y][holes[i].x] = holes[i].possibilities[0]; gridModified = true; } else { // 2 obvious possibility exlude themselves return null; } } } // Step 4 : Repeat until no more obvious hole if (gridModified) { return solver(workGrid); } // Order holes on possibilities length holes.sort(holesCompare); // Try one possibility and recurse for (let i = 0; i < holes.length; i++) { const tryGrid = JSON.parse(JSON.stringify(workGrid)); for (let possibility = 0; possibility < holes[i].possibilities.length; possibility++) { if (checkGrid(tryGrid, holes[i], holes[i].possibilities[possibility])) { tryGrid[holes[i].y][holes[i].x] = holes[i].possibilities[possibility]; let finalGrid = solver(tryGrid); if (finalGrid != null) { // It's over return finalGrid; } else { tryGrid[holes[i].y][holes[i].x] = null; } // Try next possibility } } // no possibility fits for this hole !! return null; } // Should never be there throw `Should never end function here for grid ${JSON.stringify(workGrid)}`; } function giveMeOneHint(grid) { const finalGrid = solver(grid); const holes = []; if (finalGrid === null) { return null; } // Step 1 : search each hole for (let x = 0; x < GRID_SIZE; x++) { for (let y = 0; y < GRID_SIZE; y++) { if (grid[y][x] === null) { holes.push({x, y}); } } } // Select a hole that will be given as an hint const hintHole = Math.trunc(Math.random() * holes.length); return {x: holes[hintHole].x, y: holes[hintHole].y, number: finalGrid[holes[hintHole].y][holes[hintHole].x]}; } // Permet de créer plus facilement une grille de Sudoku à partir d'une chaine contenant tous les chiffres à la suite (et un autre caractère que 1-9 pour les trous) function stringToGrid(chaine) { const grid = []; let pos = 0; if (chaine.length != GRID_SIZE*GRID_SIZE) { return null; } for (let x = 0; x < GRID_SIZE; x++) { let line = []; for (let y = 0; y < GRID_SIZE; y++) { const num = parseInt(chaine.charAt(pos++)); if (num > 0) line.push(num); else line.push(null); } grid.push(line); } return grid; } function generateGrid(numToFill) { const grid = []; let pos = 0; // Empty Grid for (let x = 0; x < GRID_SIZE; x++) { let line = []; for (let y = 0; y < GRID_SIZE; y++) { line.push(null); } grid.push(line); } let numbers = 0; while (numbers < numToFill) { if (numbers < 25) { const x = Math.floor(Math.random() * GRID_SIZE); const y = Math.floor(Math.random() * GRID_SIZE); const number = 1 + Math.floor(Math.random() * GRID_SIZE); if ((grid[y][x] === null) && (checkGrid(grid, {x, y}, number))) { grid[y][x] = number; numbers++ } } else { const hint = giveMeOneHint(grid); if (hint !== null) { grid[hint.y][hint.x] = hint.number; numbers++; } } } // Vérification if (solver(grid)) { return grid; } else { throw 'Grid is broken'; } } exports.checkGrid = checkGrid; exports.solver = solver; exports.giveMeOneHint = giveMeOneHint; exports.stringToGrid = stringToGrid; exports.generateGrid = generateGrid; exports.GRID_SIZE = GRID_SIZE;