class Cell { Board _board Integer _row Integer _col Set marks Integer value def Cell(b, r, c) { _board = b _row = r _col = c marks = new TreeSet(1..9) } def Cell(b, r, c, cell) { this(b, r, c) marks.retainAll(cell.marks) } def getMarkCount() { marks.size() } def removeMark(Integer c, byWho) { if (isKnown()) { return false } if (hasMark(c)) { if (marks.size() == 2) { // the other one is all that is left def other = marks.find { it != c }; for (h in houses) { if (h.find { it.isKnown() && it.value == other }) { println "($_row, $_col) wants to remove $c (by ${byWho.class.name}) | $candidateString" throw new IllegalStateException("If you remove $c from ($_row, $_col) only $other will be left, and that'll be illegal") } } } marks.remove(c) if (byWho.class != GameRulesStrategy.class) { println('' + this + " removed $c (by ${byWho.class.name}) | $candidateString") } return true } false } def hasMark(Integer c) { marks.contains(c) } def isKnown() { if (marks.size() == 1) { if (value == null) { value = marks.iterator().next() } true } else { false } } def getValue() { isKnown() ? value : null } def setValue(Integer v, byWho) { setValue(v) //println("($_row, $_col) set to $v (by ${byWho.class.name}) | " + toDetailString()) } def setValue(Integer v) { if (! marks.contains(v)) { throw new IllegalStateException("You cannot set cell ($_row, $_col) to $v as it is not in the candidate collection ($marks).") } for (h in houses) { def other = h.find { it.isKnown() && it.value == v } if (other != null) { throw new IllegalStateException("If you place $v at ($_row, $_col) it will conflict with the $v at ($other._row, $other._col)") } } marks.retainAll([v]) } Row getRow() { _board.getRow(_row) } Column getCol() { _board.getCol(_col) } Block getBlock() { _board.getBlock(_row, _col) } Band getBand() { _board.getBand(_row) } Stack getStack() { _board.getStack(_col) } Collection getHouses() { [row, col, block] } Collection getChutes() { [band, stack] } String toString() { "($_row,$_col,${isKnown() ? value : '-'})" } String getCandidateString() { def sb = new StringBuilder() (1..9).each { sb.append(marks.contains(it) ? it : ' ') } sb.toString() } }