Skip to content

Commit

Permalink
Move dlx to class
Browse files Browse the repository at this point in the history
  • Loading branch information
MrHen committed Mar 31, 2016
1 parent f843710 commit cb38f4c
Show file tree
Hide file tree
Showing 2 changed files with 183 additions and 173 deletions.
353 changes: 181 additions & 172 deletions src/dlx.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// Adapted from http://taeric.github.io/DancingLinks.html
namespace dlx {
export interface DlxLink<T> {
name: string;
Expand Down Expand Up @@ -36,180 +37,188 @@ namespace dlx {
};
}

////// DANCING LINKS
////// Adapted from http://taeric.github.io/DancingLinks.html
export function solveWithDancingLinks<T>(constraintMatrix: DlxMatrix<T>, dlx_showSteps: boolean) {
let dlx_headers: DlxLink<T>;
let dlx_solutions: DlxSolution<T>[] = [];
let dlx_O: T[] = [];
let dlx_current: DlxTree<T> = {
parent: null,
children: [],
depth: 0,
node: null
};

function dlx_search(k: number) {
let c: DlxLink<T>, r: DlxLink<T>;
if (dlx_showSteps || dlx_headers.right === dlx_headers) {
let solution = {
nodes: _.clone(dlx_O),
success: dlx_headers.right === dlx_headers
};

dlx_current.success = dlx_headers.right === dlx_headers;
dlx_solutions.push(solution);
if (dlx_headers.right === dlx_headers) {
return;
}
}
c = dlx_smallestColumn();
dlx_cover(c);
r = c.down;
while (r !== c) {
dlx_current = {
parent: dlx_current,
children: [],
depth: k,
node: r.node
};
dlx_current.parent.children.push(dlx_current);

dlx_O.push(r.node);
r = r.right;
while (r.col !== c) {
dlx_cover(r.col);
r = r.right;
}
dlx_search(k + 1);
r = r.left;
while (r.col !== c) {
dlx_uncover(r.col);
r = r.left;
}
r = r.down;
dlx_O.pop();
dlx_current = dlx_current.parent;
}
dlx_uncover(c);
}

function dlx_cover(c: DlxLink<T>) {
let r = c.down;
c.right.left = c.left;
c.left.right = c.right;
while (r !== c) {
r = r.right;
while (r.col !== c) {
r.up.down = r.down;
r.down.up = r.up;
r.col.size--;
r = r.right;
}
r = r.down;
}
}

function dlx_uncover(c: DlxLink<T>) {
let r = c.up;
c.right.left = c;
c.left.right = c;
while (r !== c) {
r = r.left;
while (r.col !== c) {
r.up.down = r;
r.down.up = r;
r.col.size++;
r = r.left;
}
r = r.up;
}
}

function dlx_smallestColumn(): DlxLink<T> {
let h: DlxLink<T>, c: DlxLink<T>, s = Number.MAX_VALUE;
h = dlx_headers.right;
while (h !== dlx_headers) {
if (h.size < s) {
c = h;
s = c.size;
}
h = h.right;
}
return c;
}

function dlx_initializeHeaders() {
let i: string, j: string;

let rowTrackers: { [index: string]: DlxLink<T> } = {};

dlx_headers = {
name: "root",
right: null,
left: null,
up: null,
down: null
};
dlx_headers.right = dlx_headers;
dlx_headers.left = dlx_headers;

for (i in constraintMatrix) {
let curCol: DlxLink<T> = {
name: i,
right: dlx_headers,
left: dlx_headers.left,
size: 0,
down: null,
up: null,
};

dlx_headers.left.right = curCol;
dlx_headers.left = curCol;
curCol.up = curCol;
curCol.down = curCol;

for (j in constraintMatrix[i]) {
let constraint = constraintMatrix[i][j];
let value = constraint.value;
if (!value || constraint.skip) {
continue;
}
let curRow: DlxLink<T> = {
name: j,
node: constraint.node,
right: null,
left: null,
down: curCol,
up: curCol.up,
col: curCol
};
curCol.size++;
curCol.up.down = curRow;
curCol.up = curRow;

let prevRow = rowTrackers[j];
if (!prevRow) {
curRow.right = curRow;
curRow.left = curRow;

rowTrackers[j] = curRow;
} else {
curRow.right = prevRow;
curRow.left = prevRow.left;
prevRow.left.right = curRow;
prevRow.left = curRow;
}
}
}
}

dlx_initializeHeaders();
dlx_search(0);
export class Dlx<T> {
private dlx_headers: DlxLink<T>;
private dlx_solutions: DlxSolution<T>[] = [];
private dlx_O: T[] = [];
private dlx_current: DlxTree<T> = {
parent: null,
children: [],
depth: 0,
node: null
};

private constraintMatrix: DlxMatrix<T>;
private showSteps: boolean;

constructor(constraintMatrix: DlxMatrix<T>, dlx_showSteps: boolean = false) {
this.constraintMatrix = constraintMatrix;
this.showSteps = dlx_showSteps;
}

public solve = () => {
this.dlx_initializeHeaders();
this.dlx_search(0);

return {
tree: dlx_current,
solutions: dlx_solutions
tree: this.dlx_current,
solutions: this.dlx_solutions
};
};

private dlx_search(k: number) {
let c: DlxLink<T>, r: DlxLink<T>;
if (this.showSteps || this.dlx_headers.right === this.dlx_headers) {
let solution = {
nodes: _.clone(this.dlx_O),
success: this.dlx_headers.right === this.dlx_headers
};

this.dlx_current.success = this.dlx_headers.right === this.dlx_headers;
this.dlx_solutions.push(solution);
if (this.dlx_headers.right === this.dlx_headers) {
return;
}
}
c = this.dlx_smallestColumn();
this.dlx_cover(c);
r = c.down;
while (r !== c) {
this.dlx_current = {
parent: this.dlx_current,
children: [],
depth: k,
node: r.node
};
this.dlx_current.parent.children.push(this.dlx_current);

this.dlx_O.push(r.node);
r = r.right;
while (r.col !== c) {
this.dlx_cover(r.col);
r = r.right;
}
this.dlx_search(k + 1);
r = r.left;
while (r.col !== c) {
this.dlx_uncover(r.col);
r = r.left;
}
r = r.down;
this.dlx_O.pop();
this.dlx_current = this.dlx_current.parent;
}
this.dlx_uncover(c);
}

private dlx_cover(c: DlxLink<T>) {
let r = c.down;
c.right.left = c.left;
c.left.right = c.right;
while (r !== c) {
r = r.right;
while (r.col !== c) {
r.up.down = r.down;
r.down.up = r.up;
r.col.size--;
r = r.right;
}
r = r.down;
}
}

private dlx_uncover(c: DlxLink<T>) {
let r = c.up;
c.right.left = c;
c.left.right = c;
while (r !== c) {
r = r.left;
while (r.col !== c) {
r.up.down = r;
r.down.up = r;
r.col.size++;
r = r.left;
}
r = r.up;
}
}

private dlx_smallestColumn(): DlxLink<T> {
let h: DlxLink<T>, c: DlxLink<T>, s = Number.MAX_VALUE;
h = this.dlx_headers.right;
while (h !== this.dlx_headers) {
if (h.size < s) {
c = h;
s = c.size;
}
h = h.right;
}
return c;
}

private dlx_initializeHeaders() {
let i: string, j: string;

let rowTrackers: { [index: string]: DlxLink<T> } = {};

this.dlx_headers = {
name: "root",
right: null,
left: null,
up: null,
down: null
};
this.dlx_headers.right = this.dlx_headers;
this.dlx_headers.left = this.dlx_headers;

for (i in this.constraintMatrix) {
let curCol: DlxLink<T> = {
name: i,
right: this.dlx_headers,
left: this.dlx_headers.left,
size: 0,
down: null,
up: null,
};

this.dlx_headers.left.right = curCol;
this.dlx_headers.left = curCol;
curCol.up = curCol;
curCol.down = curCol;

for (j in this.constraintMatrix[i]) {
let constraint = this.constraintMatrix[i][j];
let value = constraint.value;
if (!value || constraint.skip) {
continue;
}
let curRow: DlxLink<T> = {
name: j,
node: constraint.node,
right: null,
left: null,
down: curCol,
up: curCol.up,
col: curCol
};
curCol.size++;
curCol.up.down = curRow;
curCol.up = curRow;

let prevRow = rowTrackers[j];
if (!prevRow) {
curRow.right = curRow;
curRow.left = curRow;

rowTrackers[j] = curRow;
} else {
curRow.right = prevRow;
curRow.left = prevRow.left;
prevRow.left.right = curRow;
prevRow.left = curRow;
}
}
}
}
}
}
3 changes: 2 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ namespace LatinSquare {
let nodes: LatinSquare.Node[] = latinHive.buildNodes(cells);

let constraints: LatinSquare.ConstraintMatrix = latinConstraints.build(size, nodes);
let result = dlx.solveWithDancingLinks(constraints, true);
let solver = new dlx.Dlx(constraints, true);
let result = solver.solve();
let solutions: LatinSquare.Solution[] = result.solutions;

// One for each cell + solution combination (64 at size 4 with 4 solutions)
Expand Down

0 comments on commit cb38f4c

Please sign in to comment.