The next sudoku strategy is called a "pointing pair" which I'm going to start by generalizing into "pointing triple". The strategy is pretty straightforward: if, for a given number in a given block, all the potential cells are in the same row or column, then that number cannot exist in any other block's cells of the same row or column.
A pointing pair is easier to see than a pointing triple, but necessitates making the definition slightly tighter: if a block contains only two potential cells for a given number and they're in the same row or column, then that number cannot exist in any other block's cells of the same row or column.
Of course, if you crank it down one more step (to a "pointing single"), you have the definition of a known cell (either a given or one already solved for). But enough prose, on to the code:
boolean play(Board board) { def madePlay = false board.blocks.each { b -> (1..9).each { n -> def cells = b.findAll { it.hasMark(n) } if (cells.size() >= 2) { if (cells*.col.unique().size() == 1) { // all in one col cells[0].col.each { if (it.block != b) { // different block madePlay = it.removeMark(n, this) || madePlay } } } if (cells*.row.unique().size() == 1) { // all in one row cells[0].row.each { if (it.block != b) { // different block madePlay = it.removeMark(n, this) || madePlay } } } } } } madePlay }
This is the longest strategy so far, but it's pretty straightforward. For each block, consider each number. Find all the candidate cells, and if there are two or more, see if they're all in a single column. If so, loop over the column and remove the number from each cell not in the current block. Then do the same check for rows.
Using Groovy's getAt (bracket) notation, I could have wrapped the col and row checks into a single loop to reduce some duplication, but I haven't here. You'll see that technique in some of the later strategies, however.
Finally, you'll notice that the whole board is iterated over and potentially many plays are made before the method returns. As such, using each iterators wasn't a big deal. This is probably somewhat wasteful, because a pointing pair can potentially do a lot of elimination (and therefore falling back to the GameRulesStrategy would be useful), but I haven't done it here.
The raw source (including the test cases) is available at PointingPairStrategy.groovy, should you be interested.
Comments are closed.