class Board { List> _cells // internal 2d grid Collection cells // 81 cells w/ 9 candidates Collection rows // 9 rows of 9 cells Collection cols // 9 cols of 9 cells Collection blocks // 9 blocks of 9 cells Collection houses // 27 houses (9 rows, 9 cols, and 9 blocks) Collection bands // 3 bands of 3 blocks Collection stacks // 3 stacks of 3 blocks Collection chutes // 6 chutes (3 bands and 3 stacks) def Board() { _cells = [] (1..9).each { row -> _cells[row] = [] (1..9).each { col -> _cells[row][col] = new Cell(this, row, col) } } rows = (1..9).collect { new Row(this, it) } cols = (1..9).collect { new Column(this, it) } blocks = (1..9).collect { new Block(this, it) } houses = [] + rows + cols + blocks bands = (1..3).collect { new Band(this, it) } stacks = (1..3).collect { new Stack(this, it) } chutes = [] + bands + stacks cells = rows.flatten() } def setCell(int row, int col, int value) { _cells[row][col].value = value } Cell getCell(row, col) { _cells[row][col] } Row getRow(row) { rows[row - 1] } Column getCol(col) { cols[col - 1] } Block getBlock(block) { blocks[block - 1] } Block getBlock(row, col) { getBlock((row <= 3 ? 0 : row <= 6 ? 3 : 6) + (col <= 3 ? 1 : col <= 6 ? 2 : 3)) } Band getBand(row) { bands[row <= 3 ? 0 : row <= 6 ? 1 : 2] } Stack getStack(col) { stacks[col <= 3 ? 0 : col <= 6 ? 1 : 2] } def isSolved() { for (row in 1..9) { for (col in 1..9) { if (! _cells[row][col].isKnown()) { return false } } } for (house in houses) { def vals = new HashSet() house.each { vals += it.value } if (vals.size() != 9) { throw new IllegalStateException(house.toString()) } } true } def getCellsLeft() { cells.sum(0) { it.isKnown() ? 0 : 1 } } def getSpec() { new BoardSpec(this) } String toString() { def sb = new StringBuilder() sb.append('+-------+-------+-------+\n') (1..9).each { row -> sb.append('|') (1..9).each { col -> sb.append(' ').append(_cells[row][col].value ?: ' ') if (col == 3 || col == 6 || col == 9) { sb.append(' |') } } sb.append('\n') if (row == 3 || row == 6) { sb.append('+-------+-------+-------+\n') } } sb.append('+-------+-------+-------+\n') sb.toString().trim() } String toDetailString() { def sb = new StringBuilder() sb.append('+-+' + (('-' * 17) + '++') * 3 + '\n') sb.append('| | 1 | 2 | 3 || 4 | 5 | 6 || 7 | 8 | 9 ||\n'); sb.append('+-+' + (('-' * 17) + '++') * 3 + '\n') (1..9).each { row -> (0..2).each { s -> sb.append('|' + (s == 1 ? row : ' ') + '|') (1..9).each { col -> sb.append(' ').append(_cells[row][col].candidateString.substring(s * 3, (s + 1) * 3)) sb.append(' |') if (col == 3 || col == 6 || col == 9) { sb.append('|') } } sb.append('\n') } sb.append('+-+' + (('-' * 17) + '++') * 3 + '\n') if (row == 3 || row == 6 || row == 9) { sb.append('+-+' + (('-' * 17) + '++') * 3 + '\n') } } sb.toString().trim() } } interface House extends List { } abstract class AbstractHouse extends ArrayList implements House { Board board Integer num def AbstractHouse(board, num) { this.board = board this.num = num } String toString() { getClass().name + "#$num" + super.toString() } } class Row extends AbstractHouse { def Row(board, num) { super(board, num) (1..9).each { col -> add(board._cells[num][col]) } } } class Column extends AbstractHouse { def Column(board, num) { super(board, num) (1..9).each { row -> add(board._cells[row][num]) } } } class Block extends AbstractHouse { def Block(board, num) { super(board, num) int row = Math.floor((num - 1) / 3) * 3 + 1 int col = ((num - 1) % 3) * 3 + 1 (row..(row + 2)).each { r -> (col..(col + 2)).each { c -> add(board._cells[r][c]) } } } } interface Chute extends List { } abstract class AbstractChute extends ArrayList implements Chute { Board board Integer num def AbstractChute(board, num) { this.board = board this.num = num } } class Band extends AbstractChute { def Band(board, num) { super(board, num) add(board.getBlock((num - 1) * 3 + 1)) add(board.getBlock((num - 1) * 3 + 2)) add(board.getBlock((num - 1) * 3 + 3)) } } class Stack extends AbstractChute { def Stack(board, num) { super(board, num) add(board.getBlock(num + 0)) add(board.getBlock(num + 3)) add(board.getBlock(num + 6)) } }