diff --git a/js/Config.js b/js/Config.js new file mode 100644 index 0000000..b30aed0 --- /dev/null +++ b/js/Config.js @@ -0,0 +1,3 @@ +return { + size: 8 +} diff --git a/js/Solver/Inclusion.js b/js/Solver/Inclusion.js new file mode 100644 index 0000000..16e46b6 --- /dev/null +++ b/js/Solver/Inclusion.js @@ -0,0 +1,82 @@ +import * as CellSet from Geometry.CellSet; +import * as Strategy from Solver.Strategy; +import {getColumn, getRow} from Geometry.Vector; + +return { + find: find +}; + +function find(getColor, zones, lines) { + return Strategy.map( + stepOfInclusion, + Strategy.tryEach([ + findInclusion(lines.rows, zones, getColor), + findInclusion(lines.columns, zones, getColor), + findInclusion(zones, lines.rows, getRow), + findInclusion(zones, lines.columns, getColumn) + ]) + ); +} + +function findInclusion(subsets, supersets, property) { + return Strategy.tryEach( + quotient(subsets.map(getClasses(property)), sameSet) + .map(keepInclusion(subsets, supersets)) + ); +} + +function keepInclusion(subsets, supersets) { + return function(set) { + return function() { + if(set.occurrences.length == set.specimen.length) { + var superset = set.specimen.map(function(i) {return supersets[i];}); + var subset = set.specimen.map(function(i) {return subsets[i];}); + empty = difference(superset, subset); + return empty.isEmpty() ? null : empty; + } + }; + }; +} + +function stepOfInclusion(inclusion) { + return { + reason: 'inclusion', + empty: difference(inclusion.superset, inclusion.subset), + }; +} + +function quotient(elements, equivalence) { + var classes = []; + elements.forEach(function(element, i) { + for(var c = 0; c < classes.length; c++) { + if(equivalence(element, classes[c].specimen)) { + classes[c].occurrences.push(i); + return; + } + } + classes.push({specimen: element, occurrences: [i]}); + }); + return classes; +} + +function getClasses(property) { + return function(cellSet) { + return Array.from(quotient( + cellSet.getAll(property), function(a, b) {return a == b;}) + .map(function(equivClass) {return equivClass.specimen;}) + ).sort(); + }; +} + +function sameSet(s0, s1) { + for(var i = 0; i < s0.length; i++) { + if(i >= s1.length || s0[i] != s1[i]) { + return false; + } + } + return true; +} + +function difference(supersets, subsets) { + return CellSet.union(supersets).difference(CellSet.union(subsets)); +} diff --git a/js/Solver/SingleCell.js b/js/Solver/SingleCell.js new file mode 100644 index 0000000..95e41ab --- /dev/null +++ b/js/Solver/SingleCell.js @@ -0,0 +1,42 @@ +import * as Strategy from Solver.Strategy; +import {diagonal, plus} from Geometry.Vector; + +return { + find: find +}; + +function find(zones, lines) { + var cellSets = zones.concat(lines.rows).concat(lines.columns); + return Strategy.map( + stepOfCell(cellSets), + Strategy.tryEach(cellSets.map(getSingleCellIn)) + ); +} + +function getSingleCellIn(cellSet) { + return function() { + if(cellSet.size() == 1) { + return cellSet.getAll(function(v) {return v;})[0]; + } + }; +} + +function stepOfCell(cellSets) { + return function(cell) { + return { + reason: 'singleCell', + empty: toEmpty(cell).intersection(cellSets), + star: CellSet.cells([cell]) + }; + }; +} + +function toEmpty(cell) { + var around = CellSet.rectangle(plus(cell, diagonal(-1)), diagonal(2)); + around.remove(v); + return CellSet.union([ + around, + CellSet.row(v.getRow()), + CellSet.column(v.getColumn()) + ]); +} diff --git a/js/Solver/Strategy.js b/js/Solver/Strategy.js new file mode 100644 index 0000000..40c1feb --- /dev/null +++ b/js/Solver/Strategy.js @@ -0,0 +1,31 @@ +return { + execute: execute, + tryEach: tryEach, + map: map +} + +function tryEach(strategies) { + return function() { + for(var i = 0; i < strategies.length; i++) { + var result = strategies[i](); + if(result != undefined) { + return result; + } + } + }; +} + +function execute(strategy, onSuccess, onError) { + var result = strategy(); + if(result != undefined) { + return onSuccess(result); + } else { + return onError(result); + } +} + +function map(f, strategy) { + return function() { + return execute(strategy, f, function() {return;}); + }; +}