function CellSet(definition) { definition = definition || {}; this.cells = {}; if(definition.type == 'rectangle') { this.fromRectangle(definition); } else if(definition.type == 'isochrome') { this.fromIsochrome(definition); } else if(definition.type == 'union') { this.fromUnion(definition.sets); } } CellSet.prototype.fromRectangle = function(definition) { var iMax = definition.row + definition.height; var jMax = definition.column + definition.width; for(var i = definition.row; i < iMax; i++) { for(var j = definition.column; j < jMax; j++) { this.add(i, j); } } }; CellSet.prototype.fromIsochrome = function(definition) { var originColor = definition.data[definition.row][definition.column]; var queue = [{i: definition.row, j: definition.column}]; while(queue.length > 0) { var p = queue[0]; this.add(p.i, p.j); for(var d = -1; d < 2; d += 2) { [{i: p.i + d, j: p.j}, {i: p.i, j: p.j + d}].forEach( gateKeeper(this, definition.data, queue, originColor) ); } queue.shift(); } }; CellSet.prototype.fromUnion = function(sets) { for(var i = 0; i < sets.length; i++) { for(var key in sets[i].cells) { this.cells[key] = true; } } } CellSet.prototype.add = function(i, j) { this.cells[id(i, j)] = true; }; CellSet.prototype.remove = function(i, j) { delete this.cells[id(i, j)]; }; CellSet.prototype.contains = function(i, j) { return !!this.cells[id(i, j)]; }; CellSet.prototype.isEmpty = function() { return Object.keys(this.cells).length < 1; }; CellSet.prototype.map = function(f) { var result = []; for(var key in this.cells) { result.push(f.apply(null, key.split(':'))); } return result; } CellSet.prototype.iter = function(f) { this.map(f); } CellSet.prototype.difference = function(cellSet) { var newCellSet = new CellSet(); this.iter(function(i, j) { if(!cellSet.contains(i, j)) { newCellSet.add(i, j); } }); return newCellSet; } return { make: function(definition) {return new CellSet(definition);}, union: function(sets) {return new CellSet({type: 'union', sets: sets});} }; function id(i, j) { return i + ':' + j; } function gateKeeper(cellSet, data, queue, originColor) { return function(p1) { if(p1.i >= 0 && p1.i < data.length && p1.j >= 0 && p1.j < data.length && !cellSet.contains(p1.i, p1.j) && data[p1.i][p1.j] == originColor) { queue.push(p1); } } }