class NakedSubsetStrategy implements Strategy { static getTests() { [ new Test( '305000709090370006070001030604000300000405000009000004040100980900087000802000175', { for (r in 3..5) { if (it.getCell(r, 9).hasMark(2) || it.getCell(r, 9).hasMark(3)) { return false } } true } ), new Test( '3.5...7.9...37...6.7...1.3.6.....3.....4.5.....9.....4.4.1...8.9...87...8.2...1.5' ), new Test( '..8.1....23...9...5....8.2...5.2..8.3.......6.8..9.5...4.6....9...3...14....8.7..' ) ] } boolean play(Board board) { def madePlay = false for (h in board.houses) { def remaining = h.findAll { ! it.isKnown() } def subsets = remaining.subsequences().findAll { // at least two, at least one "free" it.size() >= 2 && it.size() < remaining.size() }.collect { [ cells: it, candidates: it*.marks.flatten().unique() ] }.findAll { it.cells.size() == it.candidates.size() }.sort { it.cells.size() } if (subsets.size() > 0) { def subset = subsets.first() // only process one (remaining - subset.cells).each { c -> subset.candidates.each { n -> madePlay = c.removeMark(n, this) || madePlay } } } if (madePlay) { // we might not be done, but let the board clean up break } } madePlay } }