From 64b4e1934698976fb13656489a80dc6851ef9351 Mon Sep 17 00:00:00 2001 From: Azalea <22280294+hykilpikonna@users.noreply.github.com> Date: Mon, 16 Dec 2024 05:23:04 -0500 Subject: [PATCH] [+] Auto cross, basic error checking --- src/App.svelte | 114 +++++++++++++++++++++++++++++++++++-------------- 1 file changed, 81 insertions(+), 33 deletions(-) diff --git a/src/App.svelte b/src/App.svelte index ea64b92..da62cc1 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -2,7 +2,7 @@ import svelteLogo from './assets/svelte.svg' import viteLogo from '/vite.svg' import Number from './lib/Number.svelte' - import { cfg, randInt, range } from "./utils"; + import { cfg, eStates, nStates, randInt, range } from "./utils"; import Line from "./lib/Line.svelte"; import { cat } from "./examples"; @@ -10,38 +10,61 @@ const [eRows, eCols] = [rows + 1, cols + 1] let numbers = Int8Array.from({ length: rows * cols }, () => 0) let numberMasked = Int8Array.from({ length: rows * cols }, () => 1) - let hedgeStates = Int8Array.from({ length: eRows * eCols }, () => 0) - let vedgeStates = Int8Array.from({ length: eRows * eCols }, () => 0) + let numberState = Int8Array.from({ length: rows * cols }, () => 0) + let hStates = Int8Array.from({ length: eRows * eCols }, () => 0) + let vStates = Int8Array.from({ length: eRows * eCols }, () => 0) let grid: HTMLDivElement const editMode = true - // Editor mode - if (editMode) { - cat.solution.forEach(edge => { - const [ sx, sy, ex, _ ] = edge - const isVertical = sx === ex - if (isVertical) { - // vedgeStates[sy * (eCols) + sx] = 1 - if (ex != cols) numbers[sy * cols + sx] += 1 - if (sx != 0) numbers[sy * cols + sx - 1] += 1 - } else { - // hedgeStates[sy * (eCols) + sx] = 1 - if (ex != rows) numbers[sy * cols + sx] += 1 - if (sx != 0) numbers[(sy - 1) * cols + sx] += 1 - } - }) + // Run something on the edges of a cell + function updateEdges(x: number, y: number, condition: (st: number) => boolean, op: (st: number) => number) { + const [ex, ey] = [x + 1, y + 1] + const [idx, vIdx, hIdx] = [y * eCols + x, y * eCols + ex, ey * eCols + x]; + [idx, vIdx].filter(i => condition(vStates[i])).map(i => [i, op(vStates[i])]) + .map(([i, res]) => vStates[i] != res ? vStates[i] = res : null); + [idx, hIdx].filter(i => condition(hStates[i])).map(i => [i, op(hStates[i])]) + .map(([i, res]) => hStates[i] != res ? hStates[i] = res : null); } - // Positions for click handling - const borders = [ - // [offset x, offset y, horizontal/vertical], [center rel pos x, rel pos y] - [[0, 0, 0], [cfg.totalW / 2, 0]], - [[0, 0, 1], [0, cfg.totalW / 2]], - [[0, 1, 0], [cfg.totalW / 2, cfg.totalW]], - [[1, 0, 1], [cfg.totalW, cfg.totalW / 2]] - ] + const withEdges = (x: number, y: number, cond: (st: number) => boolean, cb: (st: number) => any) => + updateEdges(x, y, cond, st => { cb(st); return st }) + const inBounds = (x: number, y: number) => x >= 0 && y >= 0 && x < cols && y < rows + // Check the validity of a position and auto cross + function clearAutoMark(x: number, y: number) { + if (!inBounds(x, y)) return + updateEdges(x, y, (st) => st === eStates.autoCrossed, (_) => eStates.none) + } + + function checkPos(x: number, y: number) { + if (!inBounds(x, y)) return + + // Count the number of neighboring edges + let count = 0 + withEdges(x, y, (st) => st === 1, (_) => count++) + + const n = numbers[y * cols + x] + // If count > n, invalidate the cell + numberState[y * cols + x] = +(count > n) + + if (count === n) { + // If count == n, cross out remaining edges + updateEdges(x, y, (st) => st === eStates.none, (_) => eStates.autoCrossed) + numberState[y * cols + x] = nStates.complete + } + } + + // Positions for click handling [offset x, offset y, horizontal/vertical], [center rel pos x, rel pos y] + const borders = [ + [[0, 0, 0], [cfg.totalW / 2, 0]], [[0, 0, 1], [0, cfg.totalW / 2]], + [[0, 1, 0], [cfg.totalW / 2, cfg.totalW]], [[1, 0, 1], [cfg.totalW, cfg.totalW / 2]] + ] + // 5x5 grid around the clicked cell + const updateArea = Array.from({ length: 5 }, (_, i) => Array.from({ length: 5 }, (_, j) => + [i - 2, j - 2, +(Math.abs(i - 2) == 2 || Math.abs(j - 2) == 2)])).flat() + + // Called when the user clicks on the grid function clickDiv(event: MouseEvent) { // Compute the x and y coordinates of the clicked cell const rect = grid.getBoundingClientRect() @@ -55,15 +78,39 @@ return (ox + oy < acc[1]) ? [idx, ox + oy] : acc }, [0, Infinity])[0]][0]; const idx = (sy + osy) * eCols + (sx + osx) + + // Flip the state of the edge const state = event.type === 'click' ? 1 : 2 + if (vertical) vStates[idx] = vStates[idx] === state ? 0 : state + else hStates[idx] = hStates[idx] === state ? 0 : state - // if (vertical) vedgeStates[idx] = (vedgeStates[idx] + 1) % 3 - if (vertical) vedgeStates[idx] = vedgeStates[idx] === state ? 0 : state - else hedgeStates[idx] = hedgeStates[idx] === state ? 0 : state + // Check the validity of the clicked cell and its neighbors (check 5x5 but clean only 3x3) + updateArea.forEach(([dx, dy, outer]) => outer ? 0 : clearAutoMark(sx + dx, sy + dy)) + updateArea.forEach(([dx, dy, _]) => checkPos(sx + dx, sy + dy)) - console.log(fx, fy, sx, sy, ofx, ofy) event.preventDefault() } + + // Editor mode + if (editMode) { + cat.solution.forEach(edge => { + const [ sx, sy, ex, _ ] = edge + const isVertical = sx === ex + if (isVertical) { + // The commented lines will show the solution (edges) + // vedgeStates[sy * (eCols) + sx] = 1 + if (ex != cols) numbers[sy * cols + sx] += 1 + if (sx != 0) numbers[sy * cols + sx - 1] += 1 + } else { + // hedgeStates[sy * (eCols) + sx] = 1 + if (ex != rows) numbers[sy * cols + sx] += 1 + if (sx != 0) numbers[(sy - 1) * cols + sx] += 1 + } + }) + + // Check the validity of all cells + range(rows).forEach(y => range(cols).forEach(x => checkPos(x, y))) + }
@@ -79,17 +126,18 @@ bind:this={grid}> {#each range(rows) as y} {#each range(cols) as x} - + {/each} {/each} {#each range(eRows) as y} {#each range(eCols) as x} - + {/each} {/each} {#each range(rows) as y} {#each range(cols + 1) as x} - + {/each} {/each}