Created
December 3, 2024 22:34
-
-
Save brunolm/94a237f697633a9c36e55ad951796237 to your computer and use it in GitHub Desktop.
Sudoku Solver
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<div id="sudoku-solution"></div> | |
<canvas id="canvas" style="display: none;"></canvas> | |
<div id="blocks-container"></div> | |
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/tesseract.min.js"></script> | |
screenshot:<br /> | |
<textarea> | |
const canvas = document.querySelector('#game canvas'); | |
// Convert canvas to a Blob | |
canvas.toBlob(async (blob) => { | |
try { | |
// Create a clipboard item from the Blob | |
const clipboardItem = new ClipboardItem({ 'image/png': blob }); | |
// Write the item to the clipboard | |
await navigator.clipboard.write([clipboardItem]); | |
console.log('Image copied to clipboard!'); | |
} catch (error) { | |
console.error('Failed to copy image: ', error); | |
} | |
}, 'image/png'); | |
</textarea> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function scaleCanvas(originalCanvas, newWidth, newHeight) { | |
// Create a new canvas with the desired dimensions | |
const scaledCanvas = document.createElement('canvas'); | |
const scaledContext = scaledCanvas.getContext('2d'); | |
scaledCanvas.width = newWidth; | |
scaledCanvas.height = newHeight; | |
// Draw the original canvas onto the new scaled canvas | |
scaledContext.drawImage(originalCanvas, 0, 0, newWidth, newHeight); | |
return scaledCanvas; | |
} | |
function isValid(board, row, col, num) { | |
for (let i = 0; i < 9; i++) { | |
if ( | |
board[row][i] == num || | |
board[i][col] == num || | |
board[3 * Math.floor(row / 3) + Math.floor(i / 3)][ | |
3 * Math.floor(col / 3) + (i % 3) | |
] == num | |
) { | |
return false; | |
} | |
} | |
return true; | |
} | |
function solveSudoku(board) { | |
for (let row = 0; row < 9; row++) { | |
for (let col = 0; col < 9; col++) { | |
if (board[row][col] == 0) { | |
for (let num = 1; num <= 9; num++) { | |
if (isValid(board, row, col, num)) { | |
board[row][col] = num; | |
if (solveSudoku(board)) { | |
return true; | |
} | |
board[row][col] = 0; | |
} | |
} | |
return false; | |
} | |
} | |
} | |
return true; | |
} | |
document.addEventListener("paste", async (event) => { | |
const items = event.clipboardData.items; | |
const promises = []; | |
const blockSize = 220; | |
const sudokuBoard = Array.from({ length: 9 }, () => Array(9).fill(0)); | |
for (const item of items) { | |
if (item.type.startsWith("image/")) { | |
const blob = item.getAsFile(); | |
const reader = new FileReader(); | |
reader.readAsDataURL(blob); | |
await new Promise((resolve) => { | |
reader.onload = async (e) => { | |
const img = new Image(); | |
img.onload = async () => { | |
const canvas = document.getElementById("canvas"); | |
const context = canvas.getContext("2d"); | |
canvas.width = img.width; | |
canvas.height = img.height; | |
context.drawImage(img, 0, 0); | |
const blocksContainer = document.getElementById("blocks-container"); | |
let xoffset = 0; | |
let yoffset = 0; | |
for (let row = 0; row < 9; row++) { | |
xoffset = 0; | |
if (row % 3 === 0) { | |
yoffset += row === 0 ? 8 : -2; | |
} else { | |
yoffset += 2; | |
} | |
for (let col = 0; col < 9; col++) { | |
if (col % 3 === 0) { | |
xoffset += col === 0 ? 8 : -2; | |
} else { | |
xoffset += 2; | |
} | |
const x = xoffset; | |
const y = yoffset; | |
xoffset += blockSize; | |
const block = context.getImageData( | |
x + 20, | |
y + 20, | |
blockSize - 40, | |
blockSize - 40 | |
); | |
// Convert block to grayscale and threshold light gray to white | |
const data = block.data; | |
for (let i = 0; i < data.length; i += 4) { | |
const grayscale = | |
data[i] * 0.3 + data[i + 1] * 0.59 + data[i + 2] * 0.11; | |
const isLightGray = grayscale > 200; | |
data[i] = | |
data[i + 1] = | |
data[i + 2] = | |
isLightGray ? 255 : grayscale; | |
} | |
let blockCanvas = document.createElement("canvas"); | |
const blockContext = blockCanvas.getContext("2d"); | |
blockCanvas.width = blockSize - 40; | |
blockCanvas.height = blockSize - 40; | |
blockContext.putImageData(block, 0, 0); | |
blockCanvas = scaleCanvas(blockCanvas, 40, 40) | |
// Append the block image to the document | |
blocksContainer.appendChild(blockCanvas); | |
const promise = Tesseract.recognize(blockCanvas, "eng", { | |
logger: (m) => {}, | |
}) | |
.then(({ data: { text } }) => { | |
const digit = text.trim(); | |
const d = /^(O|0)$/.test(digit) | |
? 6 | |
: digit === "38" | |
? 8 | |
: digit; | |
sudokuBoard[row][col] = d ? parseInt(d) : 0; | |
if ( | |
!sudokuBoard[row][col] || | |
Number.isNaN(sudokuBoard[row][col]) | |
) { | |
sudokuBoard[row][col] = 0; | |
} | |
}) | |
.catch((err) => { | |
console.error("Error:", err); | |
}); | |
promises.push(promise); | |
} | |
yoffset += blockSize; | |
} | |
await Promise.all(promises); | |
resolve(); | |
}; | |
img.src = e.target.result; | |
}; | |
}); | |
} | |
} | |
console.log("Sudoku Board:", sudokuBoard); | |
if (solveSudoku(sudokuBoard)) { | |
console.log("Solved Sudoku Board:"); | |
console.table(sudokuBoard); | |
// create a canvas image of the solved sudoku board | |
const canvas = document.createElement("canvas"); | |
const context = canvas.getContext("2d"); | |
canvas.width = 360; | |
canvas.height = 360; | |
const blockSize = 40; | |
for (let row = 0; row < 9; row++) { | |
for (let col = 0; col < 9; col++) { | |
const x = col * blockSize; | |
const y = row * blockSize; | |
context.fillStyle = "white"; | |
context.fillRect(x, y, blockSize, blockSize); | |
context.fillStyle = "black"; | |
context.font = "20px Arial"; | |
context.textAlign = "center"; | |
context.textBaseline = "middle"; | |
context.fillText( | |
sudokuBoard[row][col], | |
x + blockSize / 2, | |
y + blockSize / 2 | |
); | |
} | |
} | |
document.getElementById("sudoku-solution").appendChild(canvas); | |
} | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment