class XWingStrategy implements Strategy { static getTests() { // x-wing rows def t = new Test( '...13...5.4....2..8..9.........5.9....2...4....3.6.........3..6..5....1.7...28...' ) [ new Test( /* * (1,5) and (1,8) are only row 1 cells with '9' as candidate * (8,5) and (8,8) are the same * thus columns 5 and 8 cannot have '9' anywhere else (namely (2,5), (2,8), and (9,5)) */ '080705203705200000002080657' + '957642138846003972213978000' + '009000000008001004500800020', { for (c in [it.getCell(2, 5), it.getCell(2, 7), it.getCell(9, 5)]) { if (c.hasMark(9)) { return false } } true } ), t, // flip it to x-wing cols new Test( t.spec.flipPrimaryDiagonal() ), new Test( '..6......1.......3.4..7..2..2.3....9...145...4....8.6..7..9..8.3.......1......4..' ) ] } boolean play(Board board) { def madePlay = false // the rows/col and cols/row pairings are identical, so we parameterize and loop // the former is what we're looking for matches in, the latter is what we clear for (dir in [[houses: "rows", house: "col"], [houses: "cols", house: "row"]]) { for (house in board[dir.houses]) { for (n in 1..9) { def matches = house.findAll { ! it.isKnown() && it.hasMark(n) } if (matches.size() == 2) { // kick ass, we have the top of a potential x-wing for (bottom in board[dir.houses].findAll { it.num > house.num }) { def cells = bottom.findAll { ! it.isKnown() && it.hasMark(n) } if (cells.size() == 2) { if (cells[0][dir.house] == matches[0][dir.house] && cells[1][dir.house] == matches[1][dir.house]) { // it's an x-wing. lets see if it's useful matches*."$dir.house".flatten().each { if (! (it in matches) && ! (it in cells)) { if (! it.isKnown() && it.hasMark(n)) { madePlay = it.removeMark(n, this) || madePlay } } } if (madePlay) { return true } } } } } } } } madePlay } }