class PointingQuadStrategy implements Strategy { static getTests() { [ new Test( '003000000000425000000001000000904000030000000000076000' + '0' * 9 * 3, { for (r in 7..9) { for (c in 4..5) { if (it.getCell(r, c).hasMark(3)) { return false } } } true } ), new Test( '0' * 9 * 2 + '000000002' + '0' * 9 + '560000430' + '0' * 9 * 2 + '002000000' + '0' * 9, { for (r in [4, 6]) { for (c in 4..5) { if (it.getCell(r, c).hasMark(2)) { return false } } } true } ), new Test( '.....39483.9..85....4.....25..9.......7.1.6.......7..16.....1....87..2.91753.....' ) ] } boolean play(Board board) { def madePlay = false // the stack/col and band/row pairings are identical so we parameterize and loop [[chutes:"stacks", house:"col"],[chutes:"bands", house:"row"]].each { dir -> board[dir.chutes].each { s -> (1..9).each { n -> // learn about the houses within each block that have candidate n def bi = s.collect { b -> [ block: b, houses: b.findAll { ! it.isKnown() && it.hasMark(n) }.collect { it[dir.house] }.unique().sort() ] } // process pairs of blocks to look for pointing quads [ [bi[0], bi[1]], [bi[0], bi[2]], [bi[1], bi[2]] ].each { pair -> def left = pair[0] def right = pair[1] if (left.houses.size() == 2 && right.houses.size() == 2) { if (left.houses == right.houses) { // there's a pointing quad, so cull candidate n from the // third house in the third block. s.find { it != left.block && it != right.block }.each { if (! it.isKnown() && it[dir.house] in left.houses) { madePlay = it.removeMark(n, this) || madePlay } } } } } } } } madePlay } }