+from __future__ import division
+
+from weakref import ref
+
+class Constraint(object):
+ """Represents any group of cells in a grid that cannot repeat a digit."""
+
+ ### Accessors
+
+ # NOTE: _cells is a list of refs; _grid is a ref
+ # XXX document this
+ cells = property(lambda self: [ x() for x in self._cells ])
+ grid = property(lambda self: self._grid())
+
+ ### Methods
+ # XXX inherited __init__ to init _grid/_cells
+
+ def find_value(self, value):
+ """Returns the cells that can be a specific value."""
+ possible_cells = []
+ for cell in self.cells:
+ if value in cell._values:
+ possible_cells.append(cell)
+
+ if len(possible_cells) == 0:
+ raise Exception # XXX
+
+ return possible_cells
+
+ def resolve_uniques(self):
+ for value in xrange(self.grid._size):
+ # XXX cache values that are taken care of
+ possible_cells = self.find_value(value)
+
+ if len(possible_cells) > 1:
+ # Not unique
+ continue
+
+ target_cell = possible_cells[0]
+ if target_cell.solved:
+ # Already seen this
+ # XXX this is what cache is for
+ continue
+
+ # Only cell in the group that can be value
+ target_cell.set(value)
+
+
+class Box(Constraint):
+ def _get_box_row(self):
+ return self._pos // self.grid._box_width
+ box_row = property(_get_box_row)
+
+ def _get_box_column(self):
+ return self._pos % self.grid._box_height
+ box_column = property(_get_box_column)
+
+ def __init__(self, grid, position):
+ self._grid = ref(grid)
+ self._pos = position
+
+ self._cells = []
+ for row in xrange(self.grid._box_height):
+ for col in xrange(self.grid._box_width):
+ cell_row = row + self.box_row * self.grid._box_height
+ cell_col = col + self.box_column * self.grid._box_width
+ self._cells.append(ref(self.grid.cell(cell_row, cell_col)))
+
+
+class Row(Constraint):
+ def __init__(self, grid, position):
+ self._grid = ref(grid)
+ self._pos = position
+
+ self._cells = []
+ for col in xrange(self.grid._size):
+ self._cells.append(ref(self.grid.cell(self._pos, col)))
+
+class Column(Constraint):
+ def __init__(self, grid, position):
+ self._grid = ref(grid)
+ self._pos = position
+
+ self._cells = []
+ for row in xrange(self.grid._size):
+ self._cells.append(ref(self.grid.cell(row, self._pos)))
+
+class Diagonal(Constraint):
+ def __init__(self, grid, direction='down', offset=0):
+ self._grid = ref(grid)
+ self._direction = direction
+ self._offset = offset
+
+ if direction == 'down':
+ coords = {'row': 0, 'column': 0}
+ increment = 1
+ elif direction == 'up':
+ coords = {'row': 0, 'column': grid.size - 1}
+ increment = -1
+ else:
+ raise Exception # XXX
+
+ # XXX
+ if offset != 0:
+ raise NotImplementedError
+
+ self._cells = []
+ for i in xrange(self.grid.size):
+ self._cells.append(ref(self.grid.cell(coords['row'], coords['column'])))
+ coords['row'] += 1
+ coords['column'] += increment