[+] Auto cross, basic error checking
This commit is contained in:
+81
-33
@@ -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)))
|
||||
}
|
||||
</script>
|
||||
|
||||
<main>
|
||||
@@ -79,17 +126,18 @@
|
||||
bind:this={grid}>
|
||||
{#each range(rows) as y}
|
||||
{#each range(cols) as x}
|
||||
<Number x={x} y={y} n={numbers[y * cols + x]} masked={numberMasked[y * cols + x] === 0} />
|
||||
<Number x={x} y={y} n={numbers[y * cols + x]}
|
||||
masked={numberMasked[y * cols + x] === 0} state={numberState[y * cols + x]} />
|
||||
{/each}
|
||||
{/each}
|
||||
{#each range(eRows) as y}
|
||||
{#each range(eCols) as x}
|
||||
<Line sx={x} sy={y} state={hedgeStates[y * eCols + x]} />
|
||||
<Line sx={x} sy={y} state={hStates[y * eCols + x]} />
|
||||
{/each}
|
||||
{/each}
|
||||
{#each range(rows) as y}
|
||||
{#each range(cols + 1) as x}
|
||||
<Line sx={x} sy={y} vertical state={vedgeStates[y * eCols + x]} />
|
||||
<Line sx={x} sy={y} vertical state={vStates[y * eCols + x]} />
|
||||
{/each}
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user