diff options
| -rw-r--r-- | src/App.svelte | 98 | ||||
| -rw-r--r-- | src/lib/Saving.ts | 25 | ||||
| -rw-r--r-- | src/lib/Sudoku.ts | 58 |
3 files changed, 157 insertions, 24 deletions
diff --git a/src/App.svelte b/src/App.svelte index 9aab903..3a1a685 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -1,45 +1,99 @@ <script lang="ts"> - import { validate } from "./lib/Sudoku"; + import { readGridFromFile, save } from "./lib/Saving"; + + import { validate, type Coord } from "./lib/Sudoku"; let selTile: HTMLDivElement | EventTarget; + let fileInput: HTMLInputElement; const getSelTile = () => selTile as HTMLDivElement; - const grid = (new Array(10)).fill((new Array(10)).fill(".")) as string[][]; + const getCoord = (): Coord => ({ + x: parseInt(getSelTile().dataset.i), + y: parseInt(getSelTile().dataset.j), + }); + let grid = [] as string[][]; //new Array(9).fill(new Array(9).fill(".")); + let immutableTiles = [] as Coord[]; //new Array(9).fill(new Array(9).fill(".")); + const tiles = [] as HTMLDivElement[][]; //new Array(9).fill(new Array(9).fill(".")); + for (let i = 0; i < 9; i++) { + grid.push([]); + tiles.push([]); + for (let j = 0; j < 9; j++) { + grid[i].push("."); + tiles[i].push(null); + } + } function keyDownHandler(e: KeyboardEvent) { - if(e.key === "Backspace") { - setSelTile(".") + const foundImmu = immutableTiles.find( + (el) => el.x === getCoord().x && el.y === getCoord().y + ); + console.log(immutableTiles); + console.log(foundImmu); + if (foundImmu) return; + + if (e.key === "Backspace") { + setSelTile("."); } const num = parseInt(e.key); - if(Number.isNaN(num) || num === 0) return; - setSelTile(num) - validate(grid) - } - function showTile(val: string) { - return val === "." ? "" : val; + if (Number.isNaN(num) || selTile == null || num === 0) return; + setSelTile(num); + for (const tile of tiles.flat()) { + tile.classList.remove("err"); + tile.classList.remove("good"); + } + const bads = validate(grid); + for (const bad of bads) { + tiles[bad.x][bad.y].classList.add("err"); + } } + // function showTile(val: string) { + // return val === "." ? "" : val; + // } function setSelTile(val: string | number) { - grid[parseInt(getSelTile().dataset.i)][parseInt(getSelTile().dataset.j)] = val.toString(); - getSelTile().innerText = val === "." ? "" : val.toString(); + grid[getCoord().x][getCoord().y] = val.toString(); + // getSelTile().innerText = val === "." ? "" : val.toString(); + } + + async function load() { + const maybeGrid = await readGridFromFile(fileInput.files[0]); + if (!maybeGrid) { + alert("Error while loading file"); + return; + } + for (const [i, row] of maybeGrid.entries()) { + for (const [j, tile] of row.entries()) { + if (tile === ".") continue; + immutableTiles.push({ x: i, y: j }); + tiles[i][j].classList.add("const"); + } + } + grid = maybeGrid; + fileInput.value = null; } </script> -<!-- <svelte:body on:keydown|preventDefaultstopPropagation={keyDownHandler} /> --> +<!-- <svelte:body on:keydown|preventDefault|stopPropagation={keyDownHandler} /> --> <svelte:body on:keydown={keyDownHandler} /> <main> <div class="grid"> - {#each {length: 9} as _, i} - {#each {length: 9} as _, j} - <div - class="tile" + {#each grid as row, i} + {#each row as tile, j} + <div + class="tile" class:br={j % 3 === 2 && j !== 8} class:bb={i % 3 === 2 && i !== 8} - on:click|self={e => selTile = e.target} + on:click|self={(e) => (selTile = e.target)} data-i={i} data-j={j} - on:keypress={null}>{grid[i][j] === "." ? "" : grid[i][j]}</div> + bind:this={tiles[i][j]} + on:keypress={null} + > + {tile === "." ? "" : tile} + </div> {/each} {/each} </div> + <button on:click={() => save(grid)}>Save</button> + <input type="file" on:change={load} bind:this={fileInput} /> </main> <style> @@ -70,4 +124,10 @@ .br { border-right: 3px solid black; } + :global(.err) { + background-color: rgba(150, 0, 0, 0.6) !important; + } + :global(.const) { + color: rebeccapurple; + } </style> diff --git a/src/lib/Saving.ts b/src/lib/Saving.ts new file mode 100644 index 0000000..0d71057 --- /dev/null +++ b/src/lib/Saving.ts @@ -0,0 +1,25 @@ +import type { Grid } from "./Sudoku"; + +export function save(grid: Grid) { + const a = document.createElement("a"); + const fileParts = [JSON.stringify(grid)]; + const blob = new Blob(fileParts, { type: 'application/json' }); // the blob + a.href = URL.createObjectURL(blob); + a.download = "sudoku-save.json"; + a.click(); +} +export function readGridFromFile(f: File): Promise<string[][] | null> { + return new Promise(res => { + let reader = new FileReader(); + reader.onload = e => { + try { + //@ts-expect-error + const json = JSON.parse(e.target.result); + res(json) + } catch (error) { + res(null) + } + }; + reader.readAsText(f); + }) +} diff --git a/src/lib/Sudoku.ts b/src/lib/Sudoku.ts index 0da0d77..94b972e 100644 --- a/src/lib/Sudoku.ts +++ b/src/lib/Sudoku.ts @@ -1,9 +1,57 @@ -import sudoku from "sudoku-umd"; +export type Grid = Array<Array<string>>; +export type Coord = { x: number; y: number }; -type Grid = Array<Array<string>>; +export function validate(board: Grid) { + const bads = [] as Coord[]; + for (let i = 0; i < 9; i++) { + for (let j = 0; j < 9; j++) { + const value = board[i][j]; + if (value !== '.') { + if (!validateRow(board, i, j, value) || !validateColumn(board, i, j, value) || !validateBox(board, i, j, value)) { + bads.push({ x: i, y: j }) + } + } + } + } + return bads; +}; -export function validate(grid: Grid) { - const list = sudoku.board_grid_to_string(grid) +function validateRow(board: Grid, row: number, col: number, value: string) { + for (let j = 0; j < 8; j++) { + if (j !== col) { + if (board[row][j] === value) { + return false; + } + } + } - return true + return true; +} + +function validateColumn(board: Grid, row: number, col: number, value: string) { + for (let i = 0; i < 8; i++) { + if (i !== row) { + if (board[i][col] === value) { + return false; + } + } + } + + return true; +} + +function validateBox(board: Grid, row: number, col: number, value: string) { + const startRow = row - (row % 3), startCol = col - (col % 3); + + for (let i = startRow; i < startRow + 3; i++) { + for (let j = startCol; j < startCol + 3; j++) { + if (i !== row && j !== col) { + if (board[i][j] === value) { + return false; + } + } + } + } + + return true; } |
