105 lines
3.2 KiB
TypeScript
105 lines
3.2 KiB
TypeScript
/**
|
||
* Validate a fully filled Sudoku board (81-char string)
|
||
* Returns true if:
|
||
* - 81 characters long
|
||
* - all characters are digits 1–9
|
||
* - all rows, columns, and 3×3 boxes sum to 45
|
||
*/
|
||
export function validateFullBoard(board: string): boolean {
|
||
// --- Basic sanity ---
|
||
if (board.length !== 81) return false;
|
||
if (!/^[1-9]+$/.test(board)) return false;
|
||
|
||
const getCell = (r: number, c: number) => Number(board[r * 9 + c]);
|
||
|
||
// --- Validate rows ---
|
||
for (let r = 0; r < 9; r++) {
|
||
const sum = Array.from({ length: 9 }, (_, c) => getCell(r, c))
|
||
.reduce((a, b) => a + b, 0);
|
||
if (sum !== 45) return false;
|
||
}
|
||
|
||
// --- Validate columns ---
|
||
for (let c = 0; c < 9; c++) {
|
||
const sum = Array.from({ length: 9 }, (_, r) => getCell(r, c))
|
||
.reduce((a, b) => a + b, 0);
|
||
if (sum !== 45) return false;
|
||
}
|
||
|
||
// --- Validate 3×3 boxes ---
|
||
for (let br = 0; br < 3; br++) {
|
||
for (let bc = 0; bc < 3; bc++) {
|
||
const sum = Array.from({ length: 9 }, (_, i) => {
|
||
const r = br * 3 + Math.floor(i / 3);
|
||
const c = bc * 3 + (i % 3);
|
||
return getCell(r, c);
|
||
}).reduce((a, b) => a + b, 0);
|
||
if (sum !== 45) return false;
|
||
}
|
||
}
|
||
|
||
return true;
|
||
}
|
||
/**
|
||
* 🔍 Assert that a Sudoku board string represents a valid, fully filled grid.
|
||
*
|
||
* Format:
|
||
* - 81 characters
|
||
* - Only digits 1–9
|
||
* - Each row, column, and 3×3 box contains unique digits that sum to 45
|
||
*
|
||
* Throws descriptive errors on failure.
|
||
*/
|
||
|
||
export function assertValidFullBoard(board: string): void {
|
||
// --- Basic integrity checks ---
|
||
console.assert(board.length === 81, `❌ Board length must be 81, got ${board.length}`);
|
||
if (board.length !== 81) throw new Error(`Invalid board length: ${board.length}`);
|
||
|
||
// All characters must be digits 1–9
|
||
console.assert(/^[1-9]+$/.test(board), "❌ Board must contain only digits 1–9 (no 0s or dots)");
|
||
if (!/^[1-9]+$/.test(board)) throw new Error("Board contains invalid characters");
|
||
|
||
const get = (r: number, c: number) => Number(board[r * 9 + c]);
|
||
|
||
// --- Helper to assert uniqueness and sum ---
|
||
const assertGroup = (label: string, cells: number[]) => {
|
||
const sum = cells.reduce((a, b) => a + b, 0);
|
||
const unique = new Set(cells);
|
||
console.assert(sum === 45, `❌ ${label} sum=${sum} (should be 45)`);
|
||
console.assert(unique.size === 9, `❌ ${label} has duplicates: [${cells.join(",")}]`);
|
||
|
||
if (sum !== 45) throw new Error(`${label} does not sum to 45`);
|
||
if (unique.size !== 9) throw new Error(`${label} contains duplicates`);
|
||
};
|
||
|
||
// --- Validate rows ---
|
||
for (let r = 0; r < 9; r++) {
|
||
const row = Array.from({ length: 9 }, (_, c) => get(r, c));
|
||
assertGroup(`Row ${r + 1}`, row);
|
||
}
|
||
|
||
// --- Validate columns ---
|
||
for (let c = 0; c < 9; c++) {
|
||
const col = Array.from({ length: 9 }, (_, r) => get(r, c));
|
||
assertGroup(`Column ${c + 1}`, col);
|
||
}
|
||
|
||
// --- Validate 3×3 boxes ---
|
||
for (let br = 0; br < 3; br++) {
|
||
for (let bc = 0; bc < 3; bc++) {
|
||
const box: number[] = [];
|
||
for (let r = 0; r < 3; r++) {
|
||
for (let c = 0; c < 3; c++) {
|
||
box.push(get(br * 3 + r, bc * 3 + c));
|
||
}
|
||
}
|
||
const idx = br * 3 + bc + 1;
|
||
assertGroup(`Box ${idx}`, box);
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
|