02621302121e5095fb62ef5825bea394f3e2228f
[pseudoku.git] / pseudoku / grid / cell.py
1 from operator import attrgetter
2 from weakref import ref
3
4 class Cell(object):
5 """Represents a single cell/value within a sudoku grid."""
6
7 ### Accessors
8
9 def _get_solved(self):
10 """True iff this cell has been solved."""
11 return len(self._values) == 1
12 solved = property(_get_solved)
13
14 def _get_value(self):
15 """Returns this cell's value, if it has one known."""
16 if self.solved:
17 return self._values[0]
18 return None
19 value = property(_get_value)
20
21 grid = property(lambda self: self._grid())
22 constraints = property(attrgetter('_constraints'))
23
24 def __init__(self, grid, row, column):
25 self._grid = ref(grid)
26 self._row = row
27 self._col = column
28 self._values = range(self.grid.size)
29 self._constraints = []
30 self._normalized = False
31
32 def add_constraint(self, constraint):
33 self._constraints.append(constraint)
34
35 def set(self, value, normalize=True):
36 """Sets the value of this cell. If `normalize` is True or omitted, the
37 grid will be updated accordingly.
38 """
39 self._values = [value]
40 if normalize:
41 self._normalized = False
42 self.normalize()
43
44
45
46 def normalize(self):
47 """Checks to see if this cell has only one possible value left. If
48 so, sets that as its value and eliminates it from every related cell.
49 This method is exhaustive; that repeated calls should have no effect.
50 """
51
52 if self._normalized:
53 # Already done
54 return
55
56 # Set this now just in case of infinite looping
57 self._normalized = True
58
59 if not self.solved:
60 # Don't know the value yet
61 return
62
63 # Elimination time
64 for constraint in self.constraints:
65 for cell in constraint.cells:
66 if cell == self:
67 continue
68 cell.eliminate(self.value)
69
70
71 def eliminate(self, value):
72 """Eliminates the given value as a possibility for this cell."""
73 if value in self._values:
74 self._values.remove(value)
75
76 if len(self._values) == 0:
77 # XXX give me a real exception here
78 raise Exception
79
80 self._normalized = False
81 self.normalize()