Cell = number instead of object, return flat array
This commit is contained in:
parent
c16a68e796
commit
a66a20b647
@ -2,9 +2,6 @@ import { gql } from "../mods";
|
||||
import { generate, GenerateArguments } from "../sudoku/index";
|
||||
|
||||
export const typeDefs = gql`
|
||||
type Cell {
|
||||
value: Int
|
||||
}
|
||||
"""
|
||||
A sudoku
|
||||
"""
|
||||
@ -19,7 +16,7 @@ export const typeDefs = gql`
|
||||
size: Int!
|
||||
|
||||
"The rows of the board, from top to bottom."
|
||||
cells: [[Cell!]!]!
|
||||
cells: [Int!]!
|
||||
}
|
||||
|
||||
type Query {
|
||||
|
@ -1,9 +1,10 @@
|
||||
import { StaticPool, isTimeoutError } from "node-worker-threads-pool";
|
||||
|
||||
import WORKERS from "physical-cpu-count";
|
||||
import { prettyPrint } from "./util";
|
||||
const TIMEOUT = 20000;
|
||||
|
||||
export type Cell = { value: number };
|
||||
export type Cell = number;
|
||||
|
||||
export type GenerateArguments = {
|
||||
regionWidth: number;
|
||||
@ -15,10 +16,10 @@ export type Sudoku = {
|
||||
regionWidth: number;
|
||||
regionHeight: number;
|
||||
size: number;
|
||||
cells: Cell[][];
|
||||
cells: Cell[];
|
||||
};
|
||||
|
||||
const pool = new StaticPool<GenerateArguments, Cell[][]>({
|
||||
const pool = new StaticPool<GenerateArguments, Cell[]>({
|
||||
size: WORKERS,
|
||||
task: "./src/sudoku/worker.js",
|
||||
});
|
||||
@ -44,6 +45,8 @@ export async function generate(
|
||||
TIMEOUT
|
||||
);
|
||||
|
||||
prettyPrint(regionWidth, regionHeight, puzzle);
|
||||
|
||||
return {
|
||||
regionWidth,
|
||||
regionHeight,
|
||||
|
@ -11,6 +11,7 @@ import { shuffle, range } from "./util";
|
||||
import { Cell } from "./index";
|
||||
|
||||
type NodeMeta = {
|
||||
index: number;
|
||||
row: number;
|
||||
col: number;
|
||||
region: number;
|
||||
@ -73,11 +74,11 @@ export class SudokuMath {
|
||||
Math.floor(row / this.regionHeight) * this.regionHeight +
|
||||
Math.floor(col / this.regionWidth);
|
||||
const val = candidate % this.values;
|
||||
return [candidate, row, col, region, val];
|
||||
return [boardIndex, row, col, region, val];
|
||||
}
|
||||
|
||||
_checkInput(cells: Cell[][]) {
|
||||
if (cells.length !== this.values || cells[0].length !== this.values) {
|
||||
_checkInput(cells: Cell[]) {
|
||||
if (cells.length !== this.values2) {
|
||||
throw new Error(
|
||||
"Given cells array does not match regionWidth & regionHeight"
|
||||
);
|
||||
@ -86,7 +87,7 @@ export class SudokuMath {
|
||||
|
||||
// this takes a bit of time and the value may need to be cached
|
||||
getDLXHeader(
|
||||
cells: undefined | Cell[][] = undefined,
|
||||
cells: undefined | Cell[] = undefined,
|
||||
randomSearch = false
|
||||
): [CNode, DNode[]] {
|
||||
if (cells) this._checkInput(cells);
|
||||
@ -111,9 +112,9 @@ export class SudokuMath {
|
||||
: this.candidates;
|
||||
|
||||
const dlxRows: DNode[] = [];
|
||||
candidates.forEach(([i, row, col, region, val]) => {
|
||||
candidates.forEach(([boardIndex, row, col, region, val]) => {
|
||||
if (cells) {
|
||||
const exist = cells[row][col].value;
|
||||
const exist = cells[boardIndex];
|
||||
if (exist && exist - 1 !== val) {
|
||||
// skip candidates matching this constraint's position, but not its value
|
||||
// the effect is the exisitng value is preserved in the output
|
||||
@ -121,7 +122,7 @@ export class SudokuMath {
|
||||
}
|
||||
}
|
||||
|
||||
const meta = { row, col, region, value: val + 1 };
|
||||
const meta = { index: boardIndex, row, col, region, value: val + 1 };
|
||||
const dlxRow = linkNodesLR(
|
||||
this.getConstraintIDs(val, row, col, region).map((id) =>
|
||||
addNodeToColumn(constraints[id], meta)
|
||||
@ -133,20 +134,11 @@ export class SudokuMath {
|
||||
return [header, dlxRows];
|
||||
}
|
||||
|
||||
_baseBoard(): Cell[][] {
|
||||
_baseBoard(): Cell[] {
|
||||
// return a sudoku board with a random set of values in the first row
|
||||
// used in generateComplete for small speedup
|
||||
const firstRow = shuffle(range(1, this.values + 1));
|
||||
return [
|
||||
firstRow.map((value) => ({ value })),
|
||||
...Array(this.values - 1)
|
||||
.fill(null)
|
||||
.map(() =>
|
||||
Array(this.values)
|
||||
.fill(0)
|
||||
.map((value) => ({ value }))
|
||||
),
|
||||
];
|
||||
return [...firstRow, ...Array(this.values2 - this.values).fill(0)];
|
||||
}
|
||||
|
||||
generateComplete() {
|
||||
@ -156,7 +148,7 @@ export class SudokuMath {
|
||||
const callback = (solution: DNode[]) => {
|
||||
solution.forEach((node) => {
|
||||
const meta: NodeMeta = node.meta;
|
||||
result[meta.row][meta.col] = { value: meta.value };
|
||||
result[meta.index] = meta.value;
|
||||
});
|
||||
// return the first solution
|
||||
return true;
|
||||
@ -176,12 +168,12 @@ export class SudokuMath {
|
||||
let solutions = 0;
|
||||
const dlx = new DLX(header, () => ++solutions >= 2);
|
||||
|
||||
const candidates: DNode[][][] = Array.from(Array(this.values), () =>
|
||||
Array.from(Array(this.values), () => Array(this.values))
|
||||
const candidates: DNode[][] = Array.from(Array(this.values2), () =>
|
||||
Array.from(Array(this.values))
|
||||
);
|
||||
dlxRows.forEach((node) => {
|
||||
const meta = node.meta;
|
||||
candidates[meta.row][meta.col][meta.value - 1] = node;
|
||||
candidates[meta.index][meta.value - 1] = node;
|
||||
});
|
||||
|
||||
// board positions which have been removed
|
||||
@ -200,12 +192,9 @@ export class SudokuMath {
|
||||
if (removed.has(n)) {
|
||||
continue;
|
||||
}
|
||||
const row = Math.floor(n / this.values);
|
||||
const col = n % this.values;
|
||||
const existValue = completed[row][col].value;
|
||||
const nodes = candidates[row][col];
|
||||
const nodes = candidates[n];
|
||||
nodes.forEach((node) => {
|
||||
if (node.meta.value !== existValue) {
|
||||
if (node.meta.value !== completed[n]) {
|
||||
masked.push(node);
|
||||
maskRow(node);
|
||||
}
|
||||
@ -257,17 +246,17 @@ export class SudokuMath {
|
||||
}
|
||||
|
||||
removed.forEach((index) => {
|
||||
completed[Math.floor(index / this.values)][index % this.values].value = 0;
|
||||
completed[index] = 0;
|
||||
});
|
||||
return completed;
|
||||
}
|
||||
|
||||
solve(existing: Cell[][]): void {
|
||||
solve(existing: Cell[]): void {
|
||||
const [header] = this.getDLXHeader(existing);
|
||||
const callback = (solution: DNode[]) => {
|
||||
solution.forEach((node) => {
|
||||
const meta: NodeMeta = node.meta;
|
||||
existing[meta.row][meta.col] = { value: meta.value };
|
||||
existing[meta.index] = meta.value;
|
||||
});
|
||||
// return the first solution
|
||||
return true;
|
||||
|
@ -48,39 +48,43 @@ export function mapArray(arr: any[], indexes: number[]) {
|
||||
}
|
||||
|
||||
export function clone(puzz: Cell[]): Cell[] {
|
||||
return puzz.map(({ value }) => ({ value }));
|
||||
return Array.from(puzz);
|
||||
}
|
||||
|
||||
export function addCellValuesToSet(set: Set<number>, cells: Cell[]) {
|
||||
cells.forEach((cell) => {
|
||||
if (!!cell.value) set.add(cell.value);
|
||||
if (cell > 0) set.add(cell);
|
||||
});
|
||||
return set;
|
||||
}
|
||||
|
||||
export function prettyPrint(puzzle: Cell[][]) {
|
||||
let width = Math.sqrt(puzzle[0].length);
|
||||
let height = Math.sqrt(puzzle.length);
|
||||
export function prettyPrint(width: number, height: number, cells: Cell[]) {
|
||||
const side = width * height;
|
||||
let line = "";
|
||||
cells.forEach((cell, i) => {
|
||||
if (i > 0 && i % side == 0) {
|
||||
console.log(line);
|
||||
line = "";
|
||||
|
||||
puzzle.forEach((row, i) => {
|
||||
let line = "";
|
||||
row.forEach(({ value: cell }, j) => {
|
||||
if (j > 0 && j % width == 0) {
|
||||
line += "| ";
|
||||
if (i > 0 && Math.floor(i / side) % height == 0) {
|
||||
let divider = "";
|
||||
Array(width * height)
|
||||
.fill(null)
|
||||
.forEach((_, j) => {
|
||||
if (j > 0 && j % width == 0) {
|
||||
divider += " ";
|
||||
}
|
||||
divider += "---";
|
||||
});
|
||||
console.log(divider);
|
||||
}
|
||||
line += ((cell ? cell : " ") + " ").padStart(3, " ");
|
||||
});
|
||||
|
||||
if (i > 0 && i % height == 0) {
|
||||
let divider = "";
|
||||
row.forEach((_, j) => {
|
||||
if (j > 0 && j % width == 0) {
|
||||
divider += " ";
|
||||
}
|
||||
divider += "-- ";
|
||||
});
|
||||
console.log(divider);
|
||||
}
|
||||
console.log(line);
|
||||
|
||||
if (i % side !== 0 && i % width === 0) {
|
||||
line += "| ";
|
||||
}
|
||||
|
||||
line += (cell > 0 ? `${cell}` : "").padStart(2, " ") + " ";
|
||||
});
|
||||
console.log(line);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user