Removed distinction between Row/Box/Column.
[pseudoku.git] / pseudoku / grid / __init__.py
index 94d0833..e91a1bd 100644 (file)
@@ -35,34 +35,21 @@ class Cell(object):
         return None
     value = property(_get_value)
 
-    def _get_row(self):
-        """Returns the Row object associated with this cell."""
-        return self._grid._rows[self._row]
-    row = property(_get_row)
-
-    def _get_column(self):
-        """Returns the Column object associated with this cell."""
-        return self._grid._columns[self._col]
-    column = property(_get_column)
-
-    def _get_box(self):
-        """Returns the Box object associated with this cell."""
-        # Some actual math required here!
-        # Row 0..2 -> box 0..2
-        # Col 0..2 -> box 0, 3, 6 (box col 0)
-        box_row = self._row // self._grid._box_height
-        box_col = self._col // self._grid._box_width
-        box_idx = box_row * self._grid._box_height + box_col
-        return self._grid._boxes[box_idx]
-    box = property(_get_box)
+    def _get_constraints(self):
+        return self._constraints
+    constraints = property(_get_constraints)
 
     def __init__(self, grid, row, column):
         self._grid = proxy(grid)
         self._row = row
         self._col = column
         self._values = range(self._grid.size)
+        self._constraints = []
         self._normalized = False
 
+    def add_constraint(self, constraint):
+        self._constraints.append(constraint)
+
     def set(self, value, normalize=True):
         """Sets the value of this cell.  If `normalize` is True or omitted, the
         grid will be updated accordingly.
@@ -92,9 +79,8 @@ class Cell(object):
             return
 
         # Elimination time
-        for group_type in 'row', 'column', 'box':
-            group = getattr(self, group_type)
-            for cell in group.cells:
+        for constraint in self.constraints:
+            for cell in constraint.cells:
                 if cell == self:
                     continue
                 cell.eliminate(self.value)
@@ -170,9 +156,9 @@ class Grid(object):
         return self._size
     size = property(_get_size)
 
-    def _get_cell_groups(self):
-        return self._rows + self._columns + self._boxes
-    cell_groups = property(_get_cell_groups)
+    def _get_constraints(self):
+        return self._constraints
+    constraints = property(_get_constraints)
 
     ### Constructors
 
@@ -184,16 +170,15 @@ class Grid(object):
         self._box_width = box_width
         self._size = box_height * box_width
 
-        self._rows = [Row(self, i) for i in xrange(self._size)]
-        self._columns = [Column(self, i) for i in xrange(self._size)]
-        self._boxes = [Box(self, i) for i in xrange(self._size)]
-
         self._cells = range(self._size ** 2)
         for row in xrange(self._size):
             for col in xrange(self._size):
                 self._cells[self._cellidx(row, col)] \
                     = Cell(self, row, col)
 
+        self._constraints = []
+        self.add_default_constraints()
+
     @classmethod
     def from_matrix(cls, rows, box_height=None, box_width=None):
         """Creates and returns a grid read from a list of lists."""
@@ -253,6 +238,21 @@ class Grid(object):
 
         return self
 
+    ### Constraints
+
+    def add_constraint(self, constraint):
+        self._constraints.append(constraint)
+        for cell in constraint.cells:
+            cell.add_constraint(constraint)
+
+    def add_default_constraints(self):
+        for i in xrange(self._size):
+            self.add_constraint(Row(self, i))
+            self.add_constraint(Column(self, i))
+            self.add_constraint(Box(self, i))
+
+        return
+
     ### Inspectors
 
     def cell(self, row, column):
@@ -282,7 +282,7 @@ class Grid(object):
         self.normalize_cells()
 
         # Step 1: Find values that can only go in one cell in a group
-        for group in self.cell_groups:
+        for group in self.constraints:
             group.resolve_uniques()