Skip to content

Instantly share code, notes, and snippets.

@brunolm
Created December 3, 2024 22:34
Show Gist options
  • Save brunolm/94a237f697633a9c36e55ad951796237 to your computer and use it in GitHub Desktop.
Save brunolm/94a237f697633a9c36e55ad951796237 to your computer and use it in GitHub Desktop.
Sudoku Solver
<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>
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