1
0

Rewrote everything to CoffeeScript.

This commit is contained in:
Markus Birth 2013-12-04 02:54:59 +01:00
parent 177e929693
commit 74149c2d41
9 changed files with 778 additions and 330 deletions

32
Makefile Normal file
View File

@ -0,0 +1,32 @@
CC=coffee
SRCDIR=coffee
BUILDDIR=js
ALL_SRC_FILES := $(wildcard $(SRCDIR)/*)
ALL_OTHER_SRC_FILES := $(filter-out %.coffee, $(ALL_SRC_FILES))
ALL_OTHER_FILES := $(ALL_OTHER_SRC_FILES:$(SRCDIR)/%=$(BUILDDIR)/%)
SRC=$(wildcard $(SRCDIR)/*.coffee)
BUILD=$(SRC:$(SRCDIR)/%.coffee=$(BUILDDIR)/%.js)
all: coffee other
# coffeescript files
coffee: $(BUILD)
$(BUILDDIR)/%.js: $(SRCDIR)/%.coffee
$(CC) -o $(BUILDDIR)/ -c $<
# other files
other: $(ALL_OTHER_FILES)
$(ALL_OTHER_FILES): $(BUILDDIR)/%: $(SRCDIR)/%
cp $< $@
# cleanup
.PHONY: clean
clean:
-rm $(BUILD)
-rm $(ALL_OTHER_FILES)

View File

@ -0,0 +1,74 @@
class @SudokuBoard
BASE_SET: '123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
constructor: (dim) ->
@dim = Number(dim)
@dim2 = @dim * @dim
@set = @BASE_SET.substr(0, @dim2)
@board = new Array(@dim2)
@checkDiags = false
@changed = false
for r in [0...@dim2] by 1
@board[r] = new Array(@dim2)
for c in [0...@dim2] by 1
@board[r][c] = new SudokuCell(0, @)
loadString: (boarddef, alsoCheckDiags) ->
boardString = ''
for i in [0...boarddef.length] by 1
continue if boarddef.charAt(i) isnt '.' and @set.indexOf(boarddef.charAt(i)) is -1
boardString += boarddef.charAt(i)
if boardString.length isnt @dim2*@dim2
console.error('Bad board definition! (Need %d chars, got %d.)', (@dim2*@dim2), boardString.length)
document.write('Bad board definition! (Need ' + (@dim2*@dim2) + ' chars, got ' + boardString.length + '.)')
return false
for i in [0...boardString.length] by 1
r = Math.floor(i / @dim2)
c = i % @dim2
@board[r][c].setValue(boardString.charAt(i))
@board[r][c].resetChangeFlag()
@setCheckDiags(alsoCheckDiags)
console.log('Board loaded.')
setCheckDiags: (newValue) ->
@checkDiags = ( newValue )
cellAt: (r, c) ->
return @board[r][c]
resetChangeFlags: ->
for r in [0...@dim2] by 1
for c in [0...@dim2] by 1
@cellAt(r, c).resetChangeFlag()
@changed = false
hasChanged: ->
return @changed
print: ->
html = ''
html += '<table class="sudoku">'
for r in [0...@dim2] by 1
html += '<tr>'
for c in [0...@dim2] by 1
cssclass = ''
cssclasses = []
cssclasses.push('tborder') if r % @dim is 0
cssclasses.push('lborder') if c % @dim is 0
cssclasses.push('changed') if @cellAt(r, c).hasChanged()
value = @cellAt(r, c).getValue()
if value is '.'
value = ''
mask = @cellAt(r, c).getMask()
for m in [0...@dim2]
testm = 1 << m
if testm & mask
value += '<div class="hint-square hint' + (m+1) + '"></div>'
cssclasses.push('mask' + mask)
cssclass = ' class="' + cssclasses.join(' ') + '"' if cssclasses.length > 0
html += "<td#{cssclass}>#{value}</td>"
html += '</tr>'
html += '</table>'
body = document.getElementsByTagName('body')
body[0].innerHTML += html

View File

@ -0,0 +1,48 @@
class @SudokuCell
constructor: (initVal, boardObj) ->
@boardObj = boardObj
@changed = false
@value = 0
@set = (1 << @boardObj.dim2) - 1 # all
@setValue(initVal) # init cell
setMask: (newSet) ->
if newSet isnt @set
@set = newSet
@changed = true
@boardObj.changed = true
setValue: (newValue) ->
return false if newValue is @value
setidx = @boardObj.set.indexOf(newValue)
if setidx isnt -1
@value = newValue
@setMask(1 << setidx)
@changed = true
@boardObj.changed = true
return true
else if newValue is -1
@value = 0
@setMask((1 << @boardObj.dim2) - 1) # all
@changed = true
@boardObj.changed = true
return false
getValue: ->
return '.' if @value is 0
return @value
getMask: ->
return @set
getUnknownsCount: ->
result = 0
for n in [0...@boardObj.dim2] by 1
result++ if ((1 << n) & @set)
return result
hasChanged: ->
return @changed
resetChangeFlag: ->
@changed = false

View File

@ -0,0 +1,71 @@
class @SudokuChecks
@unique: (board, r, c) ->
set = ~board.cellAt(r, c).getMask()
# row / column / diagonale
for i in [0...board.dim2] by 1
if i isnt c # row
board.cellAt(r, i).setMask(board.cellAt(r, i).getMask() & set)
if i isnt r # column
board.cellAt(i, c).setMask(board.cellAt(i, c).getMask() & set)
if board.checkDiags
if r is c and i isnt r # diagonale \
board.cellAt(i, i).setMask(board.cellAt(i, i).getMask() & set)
if r is board.dim2-c-1 and i isnt r # diagonale /
board.cellAt(i, board.dim2-i-1).setMask(board.cellAt(i, board.dim2-i-1).getMask() & set)
# square
rb = Math.floor(r / board.dim) * board.dim; # row base for this square
cb = Math.floor(c / board.dim) * board.dim; # col base for this square
for i in [rb...rb+board.dim] by 1
for j in [cb...cb+board.dim] by 1
if i isnt r || j isnt c
board.cellAt(i, j).setMask(board.cellAt(i, j).getMask() & set )
return true
@onePlace: (cells) ->
return if not cells[0]
for i in [0...cells[0].boardObj.dim2] by 1 # walk all possible values
n = 0
p = (1 << i)
x = -1
for k of cells
if cells[k].getValue() is '.' and (p & cells[k].getMask())
n++
x = k
if n is 1
cells[x].setMask(p)
cells[x].setValue(cells[0].boardObj.set.charAt(Math.log(p) / Math.log(2)))
return true
@oneUnknown: (cells) ->
for c of cells
if cells[c].getValue() is '.' and cells[c].getUnknownsCount() is 1
valid = cells[c].getMask()
val = cells[c].boardObj.set.charAt(Math.log(valid) / Math.log(2))
cells[c].setValue(val)
return true
@twoValPlaces: (cells) ->
return if not cells[0]
dim2 = cells[0].boardObj.dim2
console.group('twoValPlaces: (%o) %o', dim2, cells)
for i in [0...dim2-1] by 1 # walk all possible 2s combinations
for j in [i+1...dim2] by 1
n = 0
p = (1 << i) | (1 << j)
console.log('Now checking (%o, %o) mask: %o', i+1, j+1, p)
for k of cells
if cells[k].getValue() is '.' and cells[k].getMask() & p is p
n++
console.log('%d: %d, %d (%o, %o)', n, p, cells[k].getMask(), (p & cells[k].getMask()), ((p & cells[k].getMask()) is p))
console.info('Have %d matches.', n) if n > 0
if n is 2
console.warn('Two matches!')
for k of cells
if cells[k].getValue() is '.' and cells[k].getMask() is p
cells[k].setMask(p)
else if cells[k].getValue() is '.'
cells[k].setMask(cells[k].getMask() & ~p)
console.groupEnd()
return true

View File

@ -0,0 +1,105 @@
class @SudokuSolver
@uniqueAll: (board) ->
for r in [0...board.dim2] by 1
for c in [0...board.dim2] by 1
SudokuChecks.unique(board, r, c) if board.cellAt(r, c).getValue() isnt '.'
return true
# returns all cells for the square of the specified cell
@getSquareCellsForCell: (board, r, c) ->
sqrid = Math.floor(r / board.dim) * board.dim + Math.floor(c / board.dim)
return @getSquareCells(board, sqrid)
@getSquareCells: (board, squareid) ->
result = []
rb = Math.floor(squareid / board.dim) * board.dim # base row for square
cb = squareid % board.dim * board.dim # base col for square
for i in [0...board.dim2] by 1
result.push(board.cellAt(rb + Math.floor(i / board.dim), cb + (i % board.dim)))
return result
@getRowCells: (board, row) ->
result = []
for i in [0...board.dim2] by 1
result.push(board.cellAt(row, i))
return result
@getColCells: (board, col) ->
result = []
for i in [0...board.dim2] by 1
result.push(board.cellAt(i, col))
return result
@runAllRows: (board, func) ->
for i in [0...board.dim2] by 1 # walk all rows
rowcells = @getRowCells(board, i)
SudokuChecks[func](rowcells)
@runAllColumns: (board, func) ->
for i in [0...board.dim2] by 1 # walk all columns
colcells = @getColCells(board, i)
SudokuChecks[func](colcells)
@runAllSquares: (board, func) ->
for i in [0...board.dim2] by 1 # walk all squares
sqrcells = @getSquareCells(board, i)
SudokuChecks[func](sqrcells)
@runBothDiags: (board, func) ->
return if not board.checkDiags
diag1 = []
diag2 = []
for i in [0...board.dim2] by 1
diag1.push(board.cellAt(i, i))
diag2.push(board.cellAt(i, board.dim2-i-1))
SudokuChecks[func](diag1)
SudokuChecks[func](diag2)
@oneUnknownAll: (board) ->
cells = []
for r in [0...board.dim2] by 1
for c in [0...board.dim2] by 1
cells.push(board.cellAt(r, c))
SudokuChecks.oneUnknown(cells)
@onePlaceRow: (board) -> @runAllRows(board, 'onePlace')
@onePlaceColumn: (board) -> @runAllColumns(board, 'onePlace')
@onePlaceSquare: (board) -> @runAllSquares(board, 'onePlace')
@onePlaceDiag: (board) -> @runBothDiags(board, 'onePlace')
@twoValPlacesSquare: (board) -> @runAllSquares(board, 'twoValPlaces')
@twoValPlacesRow: (board) -> @runAllRows(board, 'twoValPlaces')
@twoValPlacesColumn: (board) -> @runAllColumns(board, 'twoValPlaces')
@twoValPlacesDiag: (board) -> @runBothDiags(board, 'twoValPlaces')
@solveBoard: (board) ->
checks =
'uniqueAll': 'Value must be unique in row, column' + if board.checkDiags then ', square and diagonales.' else ' and square.'
'onePlaceSquare': 'Value has only one place in square.'
'onePlaceRow': 'Value has only one place in row.'
'onePlaceColumn': 'Value has only one place in column.'
'onePlaceDiag': 'Value has only one place in diagonale.'
'twoValPlacesSquare': 'Only two possible places for pair in square.'
'twoValPlacesRow': 'Only two possible places for pair in row.'
'twoValPlacesColumn': 'Only two possible places for pair in column.'
'twoValPlacesDiag': 'Only two possible places for pair in diagonale.'
'oneUnknownAll': 'Only one possible value left.'
i = 1
while true
console.time('Checking board.')
board.resetChangeFlags()
description = ''
for c of checks
description = checks[c]
@[c](board)
break if board.hasChanged()
if board.hasChanged()
console.info('Board was changed by "%s".', description)
body = document.getElementsByTagName('body')
body[0].innerHTML += i + '. ' + description + '<br />'
board.print()
i++
console.timeEnd('Checking board.')
break unless board.hasChanged() && i<100
return 0

View File

@ -1,86 +1,114 @@
function SudokuBoard( dim ) {
this.dim = Number( dim );
this.dim2 = this.dim * this.dim;
this.BASE_SET = '123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
this.set = this.BASE_SET.substr(0, this.dim2);
this.board = new Array( this.dim2 );
this.checkDiags = false;
this.changed = false;
// Generated by CoffeeScript 1.6.3
(function() {
this.SudokuBoard = (function() {
SudokuBoard.prototype.BASE_SET = '123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
for (var r=0; r<this.dim2; r++) {
this.board[r] = new Array( this.dim2 );
for (var c=0; c<this.dim2; c++) {
this.board[r][c] = new SudokuCell(0, this);
function SudokuBoard(dim) {
var c, r, _i, _j, _ref, _ref1;
this.dim = Number(dim);
this.dim2 = this.dim * this.dim;
this.set = this.BASE_SET.substr(0, this.dim2);
this.board = new Array(this.dim2);
this.checkDiags = false;
this.changed = false;
for (r = _i = 0, _ref = this.dim2; _i < _ref; r = _i += 1) {
this.board[r] = new Array(this.dim2);
for (c = _j = 0, _ref1 = this.dim2; _j < _ref1; c = _j += 1) {
this.board[r][c] = new SudokuCell(0, this);
}
}
}
this.loadString = function( boarddef, alsoCheckDiags ) {
var boardString = '';
for ( var i=0; i<boarddef.length; i++ ) {
if ( boarddef.charAt(i) != '.' && this.set.indexOf( boarddef.charAt(i) ) == -1 ) continue;
boardString += boarddef.charAt(i);
SudokuBoard.prototype.loadString = function(boarddef, alsoCheckDiags) {
var boardString, c, i, r, _i, _j, _ref, _ref1;
boardString = '';
for (i = _i = 0, _ref = boarddef.length; _i < _ref; i = _i += 1) {
if (boarddef.charAt(i) !== '.' && this.set.indexOf(boarddef.charAt(i)) === -1) {
continue;
}
if ( boardString.length != this.dim2 * this.dim2 ) {
console.error( 'Bad board definition! (Need %d chars, got %d.)', (this.dim2*this.dim2), boardString.length );
document.write( 'Bad board definition! (Need ' + (this.dim2*this.dim2) + ' chars, got ' + boardString.length + '.)' );
return false;
boardString += boarddef.charAt(i);
}
if (boardString.length !== this.dim2 * this.dim2) {
console.error('Bad board definition! (Need %d chars, got %d.)', this.dim2 * this.dim2, boardString.length);
document.write('Bad board definition! (Need ' + (this.dim2 * this.dim2) + ' chars, got ' + boardString.length + '.)');
return false;
}
for (i = _j = 0, _ref1 = boardString.length; _j < _ref1; i = _j += 1) {
r = Math.floor(i / this.dim2);
c = i % this.dim2;
this.board[r][c].setValue(boardString.charAt(i));
this.board[r][c].resetChangeFlag();
}
this.setCheckDiags(alsoCheckDiags);
return console.log('Board loaded.');
};
SudokuBoard.prototype.setCheckDiags = function(newValue) {
return this.checkDiags = newValue;
};
SudokuBoard.prototype.cellAt = function(r, c) {
return this.board[r][c];
};
SudokuBoard.prototype.resetChangeFlags = function() {
var c, r, _i, _j, _ref, _ref1;
for (r = _i = 0, _ref = this.dim2; _i < _ref; r = _i += 1) {
for (c = _j = 0, _ref1 = this.dim2; _j < _ref1; c = _j += 1) {
this.cellAt(r, c).resetChangeFlag();
}
for ( var i=0;i<boardString.length; i++ ) {
var r = Math.floor( i / this.dim2 );
var c = i % this.dim2;
this.board[r][c].setValue( boardString.charAt(i) );
this.board[r][c].resetChangeFlag();
}
this.checkDiags = ( alsoCheckDiags );
console.log( 'Board loaded.' );
}
}
return this.changed = false;
};
this.setCheckDiags = function( newValue ) {
this.checkDiags = ( newValue );
}
SudokuBoard.prototype.hasChanged = function() {
return this.changed;
};
this.cellAt = function( r, c ) {
return this.board[r][c];
}
this.resetChangeFlags = function() {
for ( var r=0; r<this.dim2; r++ ) {
for ( var c=0; c<this.dim2; c++ ) {
this.cellAt( r, c ).resetChangeFlag();
SudokuBoard.prototype.print = function() {
var body, c, cssclass, cssclasses, html, m, mask, r, testm, value, _i, _j, _k, _ref, _ref1, _ref2;
html = '';
html += '<table class="sudoku">';
for (r = _i = 0, _ref = this.dim2; _i < _ref; r = _i += 1) {
html += '<tr>';
for (c = _j = 0, _ref1 = this.dim2; _j < _ref1; c = _j += 1) {
cssclass = '';
cssclasses = [];
if (r % this.dim === 0) {
cssclasses.push('tborder');
}
if (c % this.dim === 0) {
cssclasses.push('lborder');
}
if (this.cellAt(r, c).hasChanged()) {
cssclasses.push('changed');
}
value = this.cellAt(r, c).getValue();
if (value === '.') {
value = '';
mask = this.cellAt(r, c).getMask();
for (m = _k = 0, _ref2 = this.dim2; 0 <= _ref2 ? _k < _ref2 : _k > _ref2; m = 0 <= _ref2 ? ++_k : --_k) {
testm = 1 << m;
if (testm & mask) {
value += '<div class="hint-square hint' + (m + 1) + '"></div>';
}
}
cssclasses.push('mask' + mask);
}
if (cssclasses.length > 0) {
cssclass = ' class="' + cssclasses.join(' ') + '"';
}
html += "<td" + cssclass + ">" + value + "</td>";
}
this.changed = false;
}
html += '</tr>';
}
html += '</table>';
body = document.getElementsByTagName('body');
return body[0].innerHTML += html;
};
this.hasChanged = function() {
return this.changed;
}
return SudokuBoard;
this.print = function() {
var html = '';
html += '<table class="sudoku">';
for ( var r=0; r<this.dim2; r++ ) {
html += '<tr>';
for ( var c=0; c<this.dim2; c++ ) {
cssclass = '';
if ( r%this.dim == 0 ) cssclass = 'tborder';
if ( c%this.dim == 0 ) cssclass += ' lborder';
if ( this.cellAt( r, c).hasChanged() ) cssclass += ' changed';
if ( cssclass.length > 0 ) cssclass = ' class="' + cssclass + '"';
background = '';
value = this.cellAt( r, c ).getValue();
if ( value == '.' ) {
background = ' background="cellMask.php?dim=' + this.dim + '&mask=' + this.cellAt(r,c).getMask();
if ( this.cellAt( r, c ).hasChanged() ) background += '&changed=1';
background += '"';
value = '';
}
html += '<td' + cssclass + background + '>' + value + '</td>';
}
html += '</tr>';
}
html += '</table>';
var body = document.getElementsByTagName( 'body' );
body[0].innerHTML += html;
}
}
})();
}).call(this);

View File

@ -1,59 +1,75 @@
function SudokuCell( initVal, boardObj ) {
this.boardObj = boardObj;
this.changed = false;
this.value = 0;
this.set = ( 1 << this.boardObj.dim2 ) - 1; // all
this.setMask = function( newSet ) {
if ( newSet != this.set ) {
this.set = newSet;
this.changed = true;
this.boardObj.changed = true;
}
// Generated by CoffeeScript 1.6.3
(function() {
this.SudokuCell = (function() {
function SudokuCell(initVal, boardObj) {
this.boardObj = boardObj;
this.changed = false;
this.value = 0;
this.set = (1 << this.boardObj.dim2) - 1;
this.setValue(initVal);
}
this.setValue = function( newValue ) {
if ( newValue == this.value ) return false;
setidx = this.boardObj.set.indexOf( newValue );
if ( setidx != -1 ) {
this.value = newValue;
this.setMask( 1 << setidx );
this.changed = true;
this.boardObj.changed = true;
return true;
} else if ( newValue == -1 ) {
this.value = 0;
this.setMask( ( 1 << this.boardObj.dim2 ) - 1 ); // all
this.changed = true;
this.boardObj.changed = true;
}
SudokuCell.prototype.setMask = function(newSet) {
if (newSet !== this.set) {
this.set = newSet;
this.changed = true;
return this.boardObj.changed = true;
}
};
SudokuCell.prototype.setValue = function(newValue) {
var setidx;
if (newValue === this.value) {
return false;
}
}
setidx = this.boardObj.set.indexOf(newValue);
if (setidx !== -1) {
this.value = newValue;
this.setMask(1 << setidx);
this.changed = true;
this.boardObj.changed = true;
return true;
} else if (newValue === -1) {
this.value = 0;
this.setMask((1 << this.boardObj.dim2) - 1);
this.changed = true;
this.boardObj.changed = true;
}
return false;
};
this.setValue( initVal ); // init cell
SudokuCell.prototype.getValue = function() {
if (this.value === 0) {
return '.';
}
return this.value;
};
this.getValue = function() {
if ( this.value == 0 ) return '.';
return this.value;
}
SudokuCell.prototype.getMask = function() {
return this.set;
};
this.getMask = function() {
return this.set;
}
this.getUnknownsCount = function() {
var result = 0;
for (var n=0; n<this.boardObj.dim2; n++) {
if ( (1 << n) & this.set ) result++;
SudokuCell.prototype.getUnknownsCount = function() {
var n, result, _i, _ref;
result = 0;
for (n = _i = 0, _ref = this.boardObj.dim2; _i < _ref; n = _i += 1) {
if ((1 << n) & this.set) {
result++;
}
return result;
}
}
return result;
};
this.hasChanged = function() {
return this.changed;
}
SudokuCell.prototype.hasChanged = function() {
return this.changed;
};
this.resetChangeFlag = function() {
this.changed = false;
}
}
SudokuCell.prototype.resetChangeFlag = function() {
return this.changed = false;
};
return SudokuCell;
})();
}).call(this);

View File

@ -1,93 +1,113 @@
var SudokuChecks = {
// Generated by CoffeeScript 1.6.3
(function() {
this.SudokuChecks = (function() {
function SudokuChecks() {}
'unique' : function( board, r, c ) {
var set = ~board.cellAt( r, c).getMask();
// row / column / diagonale
for ( var i=0; i<board.dim2; i++) {
if ( i != c ) { // row
board.cellAt( r, i ).setMask( board.cellAt( r, i ).getMask() & set );
}
if ( i != r ) { // column
board.cellAt( i, c ).setMask( board.cellAt( i, c ).getMask() & set );
}
if ( board.checkDiags ) {
if ( r == c && i != r ) { // diagonale \
board.cellAt( i, i ).setMask( board.cellAt( i, i ).getMask() & set );
}
if ( r == board.dim2-c-1 && i != r ) { // diagonale /
board.cellAt( i, board.dim2-i-1 ).setMask ( board.cellAt( i, board.dim2-i-1 ).getMask() & set );
}
}
SudokuChecks.unique = function(board, r, c) {
var cb, i, j, rb, set, _i, _j, _k, _ref, _ref1, _ref2;
set = ~board.cellAt(r, c).getMask();
for (i = _i = 0, _ref = board.dim2; _i < _ref; i = _i += 1) {
if (i !== c) {
board.cellAt(r, i).setMask(board.cellAt(r, i).getMask() & set);
}
// square
var rb = Math.floor( r / board.dim ) * board.dim; // row base for this square
var cb = Math.floor( c / board.dim ) * board.dim; // col base for this square
for ( var i=rb; i<rb+board.dim; i++ ) {
for ( var j=cb; j<cb+board.dim; j++ ) {
if ( i != r || j != c ) {
board.cellAt( i, j ).setMask( board.cellAt( i, j ).getMask() & set );
}
}
if (i !== r) {
board.cellAt(i, c).setMask(board.cellAt(i, c).getMask() & set);
}
},
'onePlace' : function( cells ) {
if ( !cells[0] ) return;
for ( var i=0; i<cells[0].boardObj.dim2; i++ ) { // walk all possible values
var n = 0;
var p = (1 << i);
var x = -1;
for ( var k in cells ) {
if ( ( cells[k].getValue() == '.' ) && ( p & cells[k].getMask() ) ) {
n++;
x = k;
}
}
if ( n == 1 ) {
cells[x].setMask( p );
cells[x].setValue( cells[0].boardObj.set.charAt( Math.log(p)/Math.log(2) ) );
}
if (board.checkDiags) {
if (r === c && i !== r) {
board.cellAt(i, i).setMask(board.cellAt(i, i).getMask() & set);
}
if (r === board.dim2 - c - 1 && i !== r) {
board.cellAt(i, board.dim2 - i - 1).setMask(board.cellAt(i, board.dim2 - i - 1).getMask() & set);
}
}
},
'oneUnknown' : function( cells ) {
for ( var c in cells ) {
if ( cells[c].getValue() == '.' && cells[c].getUnknownsCount() == 1 ) {
var valid = cells[c].getMask();
var val = cells[c].boardObj.set.charAt( Math.log( valid ) / Math.log( 2 ) );
cells[c].setValue( val );
}
}
rb = Math.floor(r / board.dim) * board.dim;
cb = Math.floor(c / board.dim) * board.dim;
for (i = _j = rb, _ref1 = rb + board.dim; _j < _ref1; i = _j += 1) {
for (j = _k = cb, _ref2 = cb + board.dim; _k < _ref2; j = _k += 1) {
if (i !== r || j !== c) {
board.cellAt(i, j).setMask(board.cellAt(i, j).getMask() & set);
}
}
},
}
return true;
};
'twoValPlaces' : function( cells ) {
if ( !cells[0] ) return;
console.group( 'twoValPlaces: %o', cells );
for ( var i=0; i<cells[0].boardObj.dim2-1; i++ ) { // walk all possible 2s combinations
for ( var j=i+1; j<cells[0].boardObj.dim2; j++ ) {
var n = 0;
var p = (1 << i) | (1 << j);
console.log( 'Now checking mask: %o', p );
for ( var k in cells ) {
if ( ( cells[k].getValue() == '.' ) && ( cells[k].getMask() & p == p ) ) {
n++;
console.log( '%d: %d, %d (%o, %o)', n, p, cells[k].getMask(), ( p & cells[k].getMask() ), ( ( p & cells[k].getMask() ) == p ) );
}
}
if ( n > 0 ) console.info( 'Have %d matches.', n );
if ( n == 2 ) {
for ( var k in cells ) {
if ( ( cells[k].getValue() == '.' ) && ( cells[k].getMask() == p ) ) {
cells[k].setMask( p );
} else if ( cells[k].getValue() == '.' ) {
cells[k].setMask( cells[k].getMask() & ~p );
}
}
}
}
SudokuChecks.onePlace = function(cells) {
var i, k, n, p, x, _i, _ref;
if (!cells[0]) {
return;
}
for (i = _i = 0, _ref = cells[0].boardObj.dim2; _i < _ref; i = _i += 1) {
n = 0;
p = 1 << i;
x = -1;
for (k in cells) {
if (cells[k].getValue() === '.' && (p & cells[k].getMask())) {
n++;
x = k;
}
}
console.groupEnd();
}
};
if (n === 1) {
cells[x].setMask(p);
cells[x].setValue(cells[0].boardObj.set.charAt(Math.log(p) / Math.log(2)));
}
}
return true;
};
SudokuChecks.oneUnknown = function(cells) {
var c, val, valid;
for (c in cells) {
if (cells[c].getValue() === '.' && cells[c].getUnknownsCount() === 1) {
valid = cells[c].getMask();
val = cells[c].boardObj.set.charAt(Math.log(valid) / Math.log(2));
cells[c].setValue(val);
}
}
return true;
};
SudokuChecks.twoValPlaces = function(cells) {
var dim2, i, j, k, n, p, _i, _j, _ref, _ref1;
if (!cells[0]) {
return;
}
dim2 = cells[0].boardObj.dim2;
console.group('twoValPlaces: (%o) %o', dim2, cells);
for (i = _i = 0, _ref = dim2 - 1; _i < _ref; i = _i += 1) {
for (j = _j = _ref1 = i + 1; _j < dim2; j = _j += 1) {
n = 0;
p = (1 << i) | (1 << j);
console.log('Now checking (%o, %o) mask: %o', i + 1, j + 1, p);
for (k in cells) {
if (cells[k].getValue() === '.' && cells[k].getMask() & p === p) {
n++;
console.log('%d: %d, %d (%o, %o)', n, p, cells[k].getMask(), p & cells[k].getMask(), (p & cells[k].getMask()) === p);
}
}
if (n > 0) {
console.info('Have %d matches.', n);
}
if (n === 2) {
console.warn('Two matches!');
for (k in cells) {
if (cells[k].getValue() === '.' && cells[k].getMask() === p) {
cells[k].setMask(p);
} else if (cells[k].getValue() === '.') {
cells[k].setMask(cells[k].getMask() & ~p);
}
}
}
}
}
console.groupEnd();
return true;
};
return SudokuChecks;
})();
}).call(this);

View File

@ -1,132 +1,186 @@
var SudokuSolver = {
// Generated by CoffeeScript 1.6.3
(function() {
this.SudokuSolver = (function() {
function SudokuSolver() {}
'uniqueAll' : function( board ) {
for ( var r=0; r<board.dim2; r++ ) {
for ( var c=0; c<board.dim2; c++ ) {
if ( board.cellAt( r, c ).getValue() != '.' ) {
SudokuChecks.unique( board, r, c );
}
}
SudokuSolver.uniqueAll = function(board) {
var c, r, _i, _j, _ref, _ref1;
for (r = _i = 0, _ref = board.dim2; _i < _ref; r = _i += 1) {
for (c = _j = 0, _ref1 = board.dim2; _j < _ref1; c = _j += 1) {
if (board.cellAt(r, c).getValue() !== '.') {
SudokuChecks.unique(board, r, c);
}
}
},
}
return true;
};
// returns all cells for the square of the specified cell
'getSquareCellsForCell' : function( board, r, c ) {
var sqrid = Math.floor( r / board.dim ) * board.dim + Math.floor( c / board.dim );
return this.getSquareCells( board, sqrid );
},
SudokuSolver.getSquareCellsForCell = function(board, r, c) {
var sqrid;
sqrid = Math.floor(r / board.dim) * board.dim + Math.floor(c / board.dim);
return this.getSquareCells(board, sqrid);
};
'getSquareCells' : function( board, squareid ) {
var result = new Array();
var rb = Math.floor( squareid / board.dim ) * board.dim; // base row for square
var cb = squareid % board.dim * board.dim; // base col for square
for ( var i=0; i<board.dim2; i++ ) {
result.push( board.cellAt( rb+Math.floor( i/board.dim ), cb+(i % board.dim) ) );
SudokuSolver.getSquareCells = function(board, squareid) {
var cb, i, rb, result, _i, _ref;
result = [];
rb = Math.floor(squareid / board.dim) * board.dim;
cb = squareid % board.dim * board.dim;
for (i = _i = 0, _ref = board.dim2; _i < _ref; i = _i += 1) {
result.push(board.cellAt(rb + Math.floor(i / board.dim), cb + (i % board.dim)));
}
return result;
};
SudokuSolver.getRowCells = function(board, row) {
var i, result, _i, _ref;
result = [];
for (i = _i = 0, _ref = board.dim2; _i < _ref; i = _i += 1) {
result.push(board.cellAt(row, i));
}
return result;
};
SudokuSolver.getColCells = function(board, col) {
var i, result, _i, _ref;
result = [];
for (i = _i = 0, _ref = board.dim2; _i < _ref; i = _i += 1) {
result.push(board.cellAt(i, col));
}
return result;
};
SudokuSolver.runAllRows = function(board, func) {
var i, rowcells, _i, _ref, _results;
_results = [];
for (i = _i = 0, _ref = board.dim2; _i < _ref; i = _i += 1) {
rowcells = this.getRowCells(board, i);
_results.push(SudokuChecks[func](rowcells));
}
return _results;
};
SudokuSolver.runAllColumns = function(board, func) {
var colcells, i, _i, _ref, _results;
_results = [];
for (i = _i = 0, _ref = board.dim2; _i < _ref; i = _i += 1) {
colcells = this.getColCells(board, i);
_results.push(SudokuChecks[func](colcells));
}
return _results;
};
SudokuSolver.runAllSquares = function(board, func) {
var i, sqrcells, _i, _ref, _results;
_results = [];
for (i = _i = 0, _ref = board.dim2; _i < _ref; i = _i += 1) {
sqrcells = this.getSquareCells(board, i);
_results.push(SudokuChecks[func](sqrcells));
}
return _results;
};
SudokuSolver.runBothDiags = function(board, func) {
var diag1, diag2, i, _i, _ref;
if (!board.checkDiags) {
return;
}
diag1 = [];
diag2 = [];
for (i = _i = 0, _ref = board.dim2; _i < _ref; i = _i += 1) {
diag1.push(board.cellAt(i, i));
diag2.push(board.cellAt(i, board.dim2 - i - 1));
}
SudokuChecks[func](diag1);
return SudokuChecks[func](diag2);
};
SudokuSolver.oneUnknownAll = function(board) {
var c, cells, r, _i, _j, _ref, _ref1;
cells = [];
for (r = _i = 0, _ref = board.dim2; _i < _ref; r = _i += 1) {
for (c = _j = 0, _ref1 = board.dim2; _j < _ref1; c = _j += 1) {
cells.push(board.cellAt(r, c));
}
return result;
},
}
return SudokuChecks.oneUnknown(cells);
};
'getRowCells' : function( board, row ) {
var result = new Array();
for ( var i=0; i<board.dim2; i++ ) {
result.push( board.cellAt( row, i ) );
SudokuSolver.onePlaceRow = function(board) {
return this.runAllRows(board, 'onePlace');
};
SudokuSolver.onePlaceColumn = function(board) {
return this.runAllColumns(board, 'onePlace');
};
SudokuSolver.onePlaceSquare = function(board) {
return this.runAllSquares(board, 'onePlace');
};
SudokuSolver.onePlaceDiag = function(board) {
return this.runBothDiags(board, 'onePlace');
};
SudokuSolver.twoValPlacesSquare = function(board) {
return this.runAllSquares(board, 'twoValPlaces');
};
SudokuSolver.twoValPlacesRow = function(board) {
return this.runAllRows(board, 'twoValPlaces');
};
SudokuSolver.twoValPlacesColumn = function(board) {
return this.runAllColumns(board, 'twoValPlaces');
};
SudokuSolver.twoValPlacesDiag = function(board) {
return this.runBothDiags(board, 'twoValPlaces');
};
SudokuSolver.solveBoard = function(board) {
var body, c, checks, description, i;
checks = {
'uniqueAll': 'Value must be unique in row, column' + (board.checkDiags ? ', square and diagonales.' : ' and square.'),
'onePlaceSquare': 'Value has only one place in square.',
'onePlaceRow': 'Value has only one place in row.',
'onePlaceColumn': 'Value has only one place in column.',
'onePlaceDiag': 'Value has only one place in diagonale.',
'twoValPlacesSquare': 'Only two possible places for pair in square.',
'twoValPlacesRow': 'Only two possible places for pair in row.',
'twoValPlacesColumn': 'Only two possible places for pair in column.',
'twoValPlacesDiag': 'Only two possible places for pair in diagonale.',
'oneUnknownAll': 'Only one possible value left.'
};
i = 1;
while (true) {
console.time('Checking board.');
board.resetChangeFlags();
description = '';
for (c in checks) {
description = checks[c];
this[c](board);
if (board.hasChanged()) {
break;
}
}
return result;
},
'getColCells' : function( board, col ) {
var result = new Array();
for ( var i=0; i<board.dim2; i++ ) {
result.push( board.cellAt( i, col ) );
if (board.hasChanged()) {
console.info('Board was changed by "%s".', description);
body = document.getElementsByTagName('body');
body[0].innerHTML += i + '. ' + description + '<br />';
board.print();
}
return result;
},
'runAllRows' : function( board, func ) {
for ( var i=0; i<board.dim2; i++ ) { // walk all rows
var rowcells = this.getRowCells( board, i );
SudokuChecks[func]( rowcells );
i++;
console.timeEnd('Checking board.');
if (!(board.hasChanged() && i < 100)) {
break;
}
},
}
return 0;
};
'runAllColumns' : function( board, func ) {
for ( var i=0; i<board.dim2; i++ ) { // walk all columns
var colcells = this.getColCells( board, i );
SudokuChecks[func]( colcells );
}
},
return SudokuSolver;
'runAllSquares' : function( board, func ) {
for ( var i=0; i<board.dim2; i++ ) { // walk all squares
var sqrcells = this.getSquareCells( board, i );
SudokuChecks[func]( sqrcells );
}
},
})();
'runBothDiags' : function( board, func ) {
if ( !board.checkDiags ) return;
var diag1 = new Array();
var diag2 = new Array();
for ( var i=0; i<board.dim2; i++ ) {
diag1.push( board.cellAt( i, i ) );
diag2.push( board.cellAt( i, board.dim2-i-1 ) );
}
SudokuChecks[func]( diag1 );
SudokuChecks[func]( diag2 );
},
'oneUnknownAll' : function( board ) {
var cells = new Array();
for ( var r=0; r<board.dim2; r++ ) {
for ( var c=0; c<board.dim2; c++ ) {
cells.push( board.cellAt( r, c ) );
}
}
SudokuChecks.oneUnknown( cells );
},
'onePlaceRow' : function( board ) { this.runAllRows( board, 'onePlace' ); },
'onePlaceColumn' : function( board ) { this.runAllColumns( board, 'onePlace' ); },
'onePlaceSquare' : function( board ) { this.runAllSquares( board, 'onePlace' ); },
'onePlaceDiag' : function( board ) { this.runBothDiags( board, 'onePlace' ); },
'twoValPlacesSquare' : function( board ) { this.runAllSquares( board, 'twoValPlaces' ); },
'twoValPlacesRow' : function( board ) { this.runAllRows( board, 'twoValPlaces' ); },
'twoValPlacesColumn' : function( board ) { this.runAllColumns( board, 'twoValPlaces' ); },
'twoValPlacesDiag' : function( board ) { this.runBothDiags( board, 'twoValPlaces' ); },
'solveBoard' : function( board ) {
var checks = {
'uniqueAll': 'Value must be unique in row, column' + ((board.checkDiags)?', square and diagonales.':' and square.'),
'onePlaceSquare': 'Value has only one place in square.',
'onePlaceRow': 'Value has only one place in row.',
'onePlaceColumn': 'Value has only one place in column.',
'onePlaceDiag': 'Value has only one place in diagonale.',
'twoValPlacesSquare': 'Only two possible places for pair in square.',
'twoValPlacesRow': 'Only two possible places for pair in row.',
'twoValPlacesColumn': 'Only two possible places for pair in column.',
'twoValPlacesDiag': 'Only two possible places for pair in diagonale.',
'oneUnknownAll': 'Only one possible value left.'
};
var i = 1;
do {
console.time( 'Checking board.' );
board.resetChangeFlags();
var description = '';
for ( var c in checks ) {
description = checks[c];
this[c]( board );
if ( board.hasChanged() ) break;
}
if ( board.hasChanged() ) {
console.info( 'Board was changed by "%s".', description );
var body = document.getElementsByTagName( 'body' );
body[0].innerHTML += i + '. ' + description + '<br />';
board.print();
}
i++;
console.timeEnd( 'Checking board.' );
} while ( board.hasChanged() && i<100);
}
};
}).call(this);