[gnome-games] sudoku:warn-unfillable-squares option fixup



commit 7da374d1d92dd8226a8252ea45f2e6cf35d8fd06
Author: Jim Ross <jimbo dimensia com>
Date:   Thu Apr 15 11:42:32 2010 -0400

    sudoku:warn-unfillable-squares option fixup
    
    GNOME bug #615862

 gnome-sudoku/src/lib/gsudoku.py    |  101 +++++++++++++++++++++++++++++++-----
 gnome-sudoku/src/lib/main.py       |    4 +-
 gnome-sudoku/src/lib/number_box.py |    7 ++-
 gnome-sudoku/src/lib/sudoku.py     |    6 +-
 4 files changed, 98 insertions(+), 20 deletions(-)
---
diff --git a/gnome-sudoku/src/lib/gsudoku.py b/gnome-sudoku/src/lib/gsudoku.py
index 52497cd..0a1233d 100644
--- a/gnome-sudoku/src/lib/gsudoku.py
+++ b/gnome-sudoku/src/lib/gsudoku.py
@@ -292,6 +292,9 @@ class SudokuGameDisplay (SudokuNumberGrid, gobject.GObject):
                 if e.get_value():
                     self.remove(x, y)
                 e.set_read_only(False)
+        for imp_cell in self.impossibilities:
+            self.__entries__[imp_cell].set_text('')
+        self.impossibilities = []
         self.grid = None
         self.clear_notes()
 
@@ -350,8 +353,6 @@ class SudokuGameDisplay (SudokuNumberGrid, gobject.GObject):
             self.remove(widget.x, widget.y)
         else:
             self.entry_validate(widget)
-        if self.show_impossible_implications:
-            self.mark_impossible_implications(widget.x, widget.y)
 
     def update_all_hints (self):
         for x in range(self.group_size):
@@ -426,6 +427,8 @@ class SudokuGameDisplay (SudokuNumberGrid, gobject.GObject):
         # Update all hints if we need to
         if self.always_show_hints and not self.doing_initial_setup:
             self.update_all_hints()
+        if not self.doing_initial_setup:
+            self.mark_impossible_implications(x, y)
 
     @simple_debug
     def remove (self, x, y, do_removal = False):
@@ -452,6 +455,8 @@ class SudokuGameDisplay (SudokuNumberGrid, gobject.GObject):
         # Update all hints if we need to
         if self.grid and self.always_show_hints and not self.doing_initial_setup:
             self.update_all_hints()
+        if not self.doing_initial_setup:
+            self.mark_impossible_implications(x, y)
 
     def remove_error_highlight (self):
         '''remove error highlight from [x, y] and also all errors caused by it
@@ -473,8 +478,6 @@ class SudokuGameDisplay (SudokuNumberGrid, gobject.GObject):
         for coords, val in changed:
             self.add_value(coords[0], coords[1], val)
             retval.append((coords[0], coords[1], val))
-            if self.show_impossible_implications:
-                self.mark_impossible_implications(*coords)
         if retval:
             self.auto_fills += 1
         if self.grid.check_for_completeness():
@@ -491,20 +494,92 @@ class SudokuGameDisplay (SudokuNumberGrid, gobject.GObject):
             e.set_text_interactive('')
             e.set_text_interactive(str(filled[1]))
 
+    def __set_impossible (self, coords, setting):
+        '''Call set_impossible() on a grid entry
+
+        This function is a helper for the 'Warn about unfillable squares'
+        feature.  It only calls set_impossible() if the option is on to prevent
+        a check against the show_impossible_implications flag elsewhere.  The
+        return from this function indicates whether or not the cell "may"
+        have been modified, which is basically the value of the option setting.
+        '''
+        if self.show_impossible_implications:
+            self.__entries__[coords].set_impossible(setting)
+        return self.show_impossible_implications
+
+    def display_impossible_implications (self):
+        '''Start X-marking cells that have no possible values
+        '''
+        self.show_impossible_implications = True
+        for imp_cell in self.impossibilities:
+            self.__set_impossible(imp_cell, True)
+            self.impossible_hints += 1
+        if self.always_show_hints:
+            self.update_all_hints()
+
+    def hide_impossible_implications (self):
+        '''Stop X-marking cells that have no possible values
+        '''
+        for imp_cell in self.impossibilities:
+            self.__set_impossible(imp_cell, False)
+        self.show_impossible_implications = False
+        if self.always_show_hints:
+            self.update_all_hints()
+
     @simple_debug
-    def mark_impossible_implications (self, x, y):
+    def mark_impossible_implications (self, x, y, check_conflicts = True):
+        '''Mark cells with X if they have no possible values
+
+        The hint this method provides can be turned on and off from the
+        menu Tools->'Warn about unfillable squares' option.
+
+        The check_conflicts parameter is for internal use only.  It is used as
+        a one level recursion on conflicts that the original target is involved
+        with.
+
+        Impossibilities are tracked regardless of the user's option setting.
+        This was done to allow the user to toggle the option mid-game and still
+        behave properly.  Conditional X-marking of cells happens in the
+        __set_impossible() function.
+        '''
+        # Make sure we have a grid to work with
         if not self.grid:
             return
+        # Flag whether or not we need to update hints
+        grid_modified = False
+        # Find any new impossible cells based on calling cell
         implications = self.grid.find_impossible_implications(x, y)
         if implications:
-            for x, y in implications:
-                self.__entries__[(x, y)].set_impossible(True)
-                if not (x, y) in self.impossibilities:
-                    self.impossible_hints += 1
-        for x, y in self.impossibilities:
-            if not (x, y) in implications:
-                self.__entries__[(x, y)].set_impossible(False)
-        self.impossibilities = implications
+            for imp_cell in implications:
+                grid_modified = self.__set_impossible(imp_cell, True)
+                # Add them to the list if they aren't there already...
+                if not imp_cell in self.impossibilities:
+                    self.impossibilities.append(imp_cell)
+                    # But don't score it unless the option is on
+                    if self.show_impossible_implications:
+                        self.impossible_hints += 1
+        # Reset the list of impossible cells ignoring the called cell. Use a
+        # copy to iterate over, so items can be removed while looping.
+        if self.impossibilities:
+            for imp_cell in self.impossibilities[:]:
+                if imp_cell == (x, y):
+                    continue
+                if self.grid.possible_values(*imp_cell):
+                    self.impossibilities.remove(imp_cell)
+                    grid_modified = self.__set_impossible(imp_cell, False)
+                else:
+                    grid_modified = self.__set_impossible(imp_cell, True)
+        # If any conflicts have been cleared or created, mark any impossible
+        # cells they may have caused or removed
+        if check_conflicts:
+            for xx, yy in self.grid.cleared_conflicts:
+                self.mark_impossible_implications(xx, yy, False)
+            if self.grid.conflicts.has_key((x, y)):
+                for xx, yy in self.grid.conflicts[(x, y)]:
+                    self.mark_impossible_implications(xx, yy, False)
+        # Update the hints if we need to
+        if grid_modified and self.always_show_hints:
+            self.update_all_hints()
 
     @simple_debug
     def create_tracker (self, identifier = 0):
diff --git a/gnome-sudoku/src/lib/main.py b/gnome-sudoku/src/lib/main.py
index 882aa5b..bf1b929 100644
--- a/gnome-sudoku/src/lib/main.py
+++ b/gnome-sudoku/src/lib/main.py
@@ -586,9 +586,9 @@ class UI (gconf_wrapper.GConfWrapper):
     @simple_debug
     def impossible_implication_cb (self, action):
         if action.get_active():
-            self.gsd.show_impossible_implications = True
+            self.gsd.display_impossible_implications()
         else:
-            self.gsd.show_impossible_implications = False
+            self.gsd.hide_impossible_implications()
 
     @simple_debug
     def auto_fill_cb (self, *args):
diff --git a/gnome-sudoku/src/lib/number_box.py b/gnome-sudoku/src/lib/number_box.py
index 1598aa3..5be8c2b 100644
--- a/gnome-sudoku/src/lib/number_box.py
+++ b/gnome-sudoku/src/lib/number_box.py
@@ -615,8 +615,11 @@ class SudokuNumberBox (NumberBox):
 
     def set_impossible (self, val):
         if val:
-            self.set_text('X')
-        else: self.set_text('')
+            if not self.get_text():
+                self.set_text('X')
+        elif self.get_text() == 'X':
+            self.set_text('')
+        self.queue_draw()
 
 
 gobject.type_register(NumberBox)
diff --git a/gnome-sudoku/src/lib/sudoku.py b/gnome-sudoku/src/lib/sudoku.py
index 7d0a62b..3c527d9 100644
--- a/gnome-sudoku/src/lib/sudoku.py
+++ b/gnome-sudoku/src/lib/sudoku.py
@@ -525,18 +525,18 @@ class InteractiveSudoku (SudokuSolver):
         return self.virgin.to_string() + '\n' + SudokuSolver.to_string(self)
 
     def find_impossible_implications (self, x, y):
-        """Return a list of impossibilities implied by the users actions."""
+        """Return a set of impossibilities implied by the users actions."""
         row_cells = self.row_coords[y]
         col_cells = self.col_coords[x]
         box = self.box_by_coords[(x, y)]
         box_cells = self.box_coords[box]
+        broken = set()
         for coord_set in [row_cells, col_cells, box_cells]:
-            broken = []
             # just work on the open squares
             coord_set = filter(lambda coords: not self._get_(*coords), coord_set)
             for coords in coord_set:
                 if not self.possible_values(*coords):
-                    broken.append(coords)
+                    broken.add(coords)
         return broken
 
     def check_for_completeness (self):



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]