Skip to content

Instantly share code, notes, and snippets.

@SiestaMadokaist
Created June 9, 2020 05:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save SiestaMadokaist/c53524ab1c8414652f331039d15fa273 to your computer and use it in GitHub Desktop.
Save SiestaMadokaist/c53524ab1c8414652f331039d15fa273 to your computer and use it in GitHub Desktop.
sudoku generator
class MySet<T> extends Set<T> {
static of<T>(...xs: T[]): MySet<T> {
return new this<T>(xs);
}
intersect(other: MySet<T>): MySet<T> {
const result = new MySet<T>();
for (const entry of this.values()) {
if (other.has(entry)) { result.add(entry); }
}
return result;
}
}
const createEntries = (n: number) => {
return [ ...new Array(n * n)].map((_, i) => i + 1);
};
abstract class Entries {
#entries: MySet<number>;
abstract type: string;
constructor(size: number, private name: string = '') { this.#entries = MySet.of(...createEntries(size)); }
entries(): MySet<number> {
return this.#entries;
}
delete(n: number): void {
this.#entries.delete(n);
}
print(): void {
console.log(`${this.name}`);
console.log(Array.from(this.#entries).join(', '));
}
}
class Row extends Entries { type: 'row' = 'row'; }
class Column extends Entries { type: 'column' = 'column'; }
class Box extends Entries { type: 'box' = 'box'; }
class SudokuBoard {
private _board: number[][];
private _boxes: Box[];
private _columns: Column[];
private _rows: Row[];
static create(size: number): SudokuBoard {
const board = new this(size);
board.initialize();
return board;
}
private constructor(private size: number) {
const size2 = size * size;
const generator = [ ...new Array(size2) ];
this._board = generator.map(() => createEntries(size).map(() => 0));
this._boxes = generator.map((_, i) => new Box(size, `box#${i}`));
this._columns = generator.map((_, i) => new Column(size, `column#${i}`));
this._rows = generator.map((_, i) => new Row(size, `row#${i}`));
}
board(): number[][] {
return this._board;
}
private size2(): number { return this.size * this.size; }
private candidate(x: number, y: number): MySet<number> {
const box = this.getBox(x, y);
const column = this.getColumn(x, y)
const row = this.getRow(x, y);
const intersect = box.entries().intersect(column.entries()).intersect(row.entries());
return intersect;
}
deleteCandidate(index: {x: number, y: number}, value: number): void {
const box = this.getBox(index.x, index.y);
const column = this.getColumn(index.x, index.y)
const row = this.getRow(index.x, index.y);
box.delete(value);
column.delete(value);
row.delete(value);
}
private setValue(index: { x: number, y: number}, value: number): void {
this.deleteCandidate(index, value);
this._board[index.y][index.x] = value;
}
private setRandom(index: { x: number; y: number }): void {
const intersect = this.candidate(index.x, index.y);
const available = Array.from(intersect);
if (available.length <= 0) { throw new Error('');}
const elementIndex = Math.floor(Math.random() * available.length);
const value = available[elementIndex];
this.setValue(index, value);
}
private initialize(): void {
for (let p = 0; p < this.size2(); p++) {
const y = 0;
this.setRandom({ x: p, y });
}
const indexes: Set<{ x: number, y: number }> = new Set();
for (let y = 1; y < this.size2(); y++) {
for (let x = 0; x < this.size2(); x++) {
indexes.add({ x, y })
}
}
const size = indexes.size;
for (let i = 0; i < size; i++) {
const candidates = Array.from(indexes).map((index) => ({
index,
candidate: this.candidate(index.x, index.y)
})).sort((a, b) => b.candidate.size - a.candidate.size);
const least = candidates.pop();
if (!least) { throw new Error(''); }
indexes.delete(least.index);
this.setRandom(least.index);
}
}
print(): void {
for (let y = 0; y < this.size2(); y++) {
const buffer: string[] = [];
for (let x = 0; x < this.size2(); x++) {
const value = this._board[y][x];
if (x % this.size === 0) { buffer.push('|')}
// buffer.push(`${value}`);
if (value > 0) {
const text = `${value}`;//.padStart(10).padEnd(15);
buffer.push(text);
} else {
const candidate = this.candidate(x, y);
const content = `(${Array.from(candidate).join(',')})`;
const text = content;//.padStart(15 - content.length / 2).padEnd(15);
buffer.push(text);
}
if (x === this.size2() - 1) {
buffer.push(`|`);
continue;
}
}
const l = 2 * this.size2() + 1 + (2 * this.size);
const separator = [ ...new Array(l)].map(() => '-').join('');
if (y % this.size === 0) { console.log(separator); }
console.log(buffer.join(' '));
if (y === this.size2() - 1) { console.log(separator); }
}
}
private getBox(x: number, y: number): Box {
const xn = Math.floor(x / this.size);
const yn = Math.floor(y / this.size);
const index = (yn * this.size) + xn;
return this._boxes[index];
}
private getColumn(x: number, y: number): Column {
return this._columns[x];
}
private getRow(x: number, y: number): Row {
return this._rows[y];
}
}
function main(): void {
const tryCount = 100;
let successfull = 0;
// const board = SudokuBoard.create(3);
// board.print();
for (let i = 0; i < tryCount; i++) {
let board: SudokuBoard
try {
board = SudokuBoard.create(3);
successfull++;
console.log(`success #${successfull} #${i}/${tryCount}`);
board.print();
} catch (error) { }
}
}
main();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment