[gnome-games/sudoku-vala] New solver, Sudokuboard class, frontend connection



commit 888acf571ff7bd31f04063fb3adb4f8f634a6579
Author: PioneerAxon <arth svnit gmail com>
Date:   Mon Jan 30 12:17:40 2012 +0530

    New solver, Sudokuboard class, frontend connection
    
    Some bug-fixes.
    -Solved: showing pop-ups ( number picker & note ) even with fixed value cell.
    -Solved: no update in actual Sudoku with selection of no from number-picker.
    Additions
    -SudokuSolver class to solve sudoku ( This will help in SudokuGenerator class )
    -SudokuBoard class, a new and faster representation of the sudoku game with dynamic size capabilities.
    -Signal raising from SudokuBoard and handling from all other required classes.
    -Proper randomly distributed hint.
    -Undo-Redo functionalities.

 gnome-sudoku/src/gnome-sudoku.vala     |  154 ++-------------
 gnome-sudoku/src/sudoku-game.vala      |  341 +++++++++++++++++++++-----------
 gnome-sudoku/src/sudoku-generator.vala |   94 +++++++++
 gnome-sudoku/src/sudoku-view.vala      |  132 ++++++++++---
 4 files changed, 438 insertions(+), 283 deletions(-)
---
diff --git a/gnome-sudoku/src/gnome-sudoku.vala b/gnome-sudoku/src/gnome-sudoku.vala
index 5d65c57..55f4b0b 100644
--- a/gnome-sudoku/src/gnome-sudoku.vala
+++ b/gnome-sudoku/src/gnome-sudoku.vala
@@ -30,19 +30,8 @@ public class Application
         toolbar = (Gtk.Toolbar) builder.get_object ("sudoku_toolbar");
         settings.bind ("show-toolbar", builder.get_object ("sudoku_toolbar"), "visible", SettingsBindFlags.DEFAULT);
         settings.bind("show-toolbar", builder.get_object ("toggle_toolbar_imagemenuitem"), "active", SettingsBindFlags.GET); 
-
-        game = SudokuGenerator.generate ("easy");
         game = new SudokuGame ();
-        game.set_from_string ("---------" +
-                              "-----3-85" +
-                              "--1-2----" +
-                              "---5-7---" +
-                              "--4---1--" +
-                              "-9-------" +
-                              "5------73" +
-                              "--2-1----" +
-                              "----4---9");
-        game.set_from_string ("1.......2.9.4...5...6...7...5.9.3.......7.......85..4.7.....6...3...9.8...2.....1");
+        game.set_from_string ("41593....8..5....7..7..41........36926.3.8.51534........67..2..7....2..3....91786");
         view = new SudokuView (game);
         view.show ();
         main_vbox.pack_start (view);
@@ -106,6 +95,20 @@ public class Application
     [CCode (cname = "G_MODULE_EXPORT quit_cb", instance_pos = -1)]
     public void quit_cb (Gtk.Widget widget)
     {
+/* FIXME: Every time the game exits with Segmentation fault.
+The stack trace is 
+#0  0x00007ffff7267b70 in ?? () from /usr/lib/x86_64-linux-gnu/libgio-2.0.so.0
+#1  0x00007ffff726a315 in ?? () from /usr/lib/x86_64-linux-gnu/libgio-2.0.so.0
+#2  0x00007ffff68799f0 in g_object_unref () from /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0
+#3  0x0000000000406b98 in application_finalize (obj=<optimized out>) at gnome-sudoku.c:832
+#4  0x0000000000406f26 in application_unref (instance=0x6ba980) at gnome-sudoku.c:869
+#5  application_unref (instance=0x6ba980) at gnome-sudoku.c:865
+#6  0x0000000000407322 in gnome_sudoku_main (args=0x7fffffffe108, args_length1=1) at gnome-sudoku.c:959
+#7  0x00007ffff5fd730d in __libc_start_main () from /lib/x86_64-linux-gnu/libc.so.6
+#8  0x0000000000405b39 in _start ()
+
+Hope this helps.
+*/
         Gtk.main_quit ();
     }
 
@@ -159,135 +162,10 @@ public class Application
         view.show_warnings = !view.show_warnings;    
     }
 
-    private bool row_has_value (int row, int value)
-    {
-        for (var col = 0; col < 9; col++)
-        {
-            if (game.cells[row, col].value == value)
-                return true;
-        }
-        return false;
-    }
-
-    private int row_count_possibilities (int row, int value, out int col_match)
-    {
-        int n = 0;
-        for (var col = 0; col < 9; col++)
-        {
-            if (game.cells[row, col].value == 0 && game.cells[row, col].can_be (value))
-            {
-                col_match = col;
-                n++;
-            }
-        }
-        return n;
-    }
-
-    private bool col_has_value (int col, int value)
-    {
-        for (var row = 0; row < 9; row++)
-        {
-            if (game.cells[row, col].value == value)
-                return true;
-        }
-        return false;
-    }
-
-    private int col_count_possibilities (int col, int value, out int row_match)
-    {
-        int n = 0;
-        for (var row = 0; row < 9; row++)
-        {
-            if (game.cells[row, col].value == 0 && game.cells[row, col].can_be (value))
-            {
-                row_match = row;
-                n++;
-            }
-        }
-        return n;
-    }
-
-    private bool cell_has_value (int row, int col, int value)
-    {
-        for (var r = 0; r < 3; r++)
-            for (var c = 0; c < 3; c++)
-            {
-                if (game.cells[row*3+r, col*3+c].value == value)
-                    return true;
-            }
-        return false;
-    }
-
-    private int cell_count_possibilities (int row, int col, int value, out int match_row, out int match_col)
-    {
-        int n = 0;
-        for (var r = 0; r < 3; r++)
-            for (var c = 0; c < 3; c++)
-            {
-                var cell = game.cells[row*3+r, col*3+c];
-                if (cell.value == 0 && cell.can_be (value))
-                {
-                    match_row = row*3+r;
-                    match_col = col*3+c;
-                    n++;
-                }
-            }
-        return n;
-    }
-
     [CCode (cname = "G_MODULE_EXPORT hint_cb", instance_pos = -1)]
     public void hint_cb (Gtk.Widget widget)
     {
-        stdout.printf ("TODO: Show hint\n");
-
-        // TEMP: Fill any square with only one possible value
-        for (int col = 0; col < 9; col++)
-            for (int row = 0; row < 9; row++)
-            {
-                var cell = game.cells[row, col];
-                if (cell.value != 0 || cell.n_possibilities != 1)
-                    continue;
-                for (int value = 1; value <= 9; value++)
-                    if (cell.can_be (value))
-                    {
-                        cell.value = value;
-                        return;
-                    }
-            }
-
-        for (int value = 1; value < 9; value++)
-        {
-            for (int row = 0; row < 9; row++)
-            {
-                int col = 0;
-                if (!row_has_value (row, value) && row_count_possibilities (row, value, out col) == 1)
-                {
-                    game.cells[row, col].value = value;
-                    return;
-                }
-            }
-            for (int col = 0; col < 9; col++)
-            {
-                int row = 0;
-                if (!col_has_value (col, value) && col_count_possibilities (col, value, out row) == 1)
-                {
-                    game.cells[row, col].value = value;
-                    return;
-                }
-            }
-            for (int r = 0; r < 3; r++)
-            {
-                for (int c = 0; c < 3; c++)
-                {
-                    int row = 0, col = 0;
-                    if (!cell_has_value (r, c, value) && cell_count_possibilities (r, c, value, out row, out col) == 1)
-                    {
-                        game.cells[row, col].value = value;
-                        return;
-                    }
-                }
-            }
-        }
+        view.hint ();
     }
 
     [CCode (cname = "G_MODULE_EXPORT clear_top_notes_cb", instance_pos = -1)]
diff --git a/gnome-sudoku/src/sudoku-game.vala b/gnome-sudoku/src/sudoku-game.vala
index fe4709d..20f4cd0 100644
--- a/gnome-sudoku/src/sudoku-game.vala
+++ b/gnome-sudoku/src/sudoku-game.vala
@@ -1,70 +1,197 @@
-public class SudokuCell
+public class SudokuBoard
 {
-    public int row;
-    public int col;
-
-    public bool possible_values[9];
-
-    public int n_possibilities
+    /* Implemented in such a way that it can be extended for other sizes ( like 2x3 sudoku or 4x4 sudoku ) instead of normal 3x3 sudoku. */
+    
+    public int[,] cells;                        /* stores the value of cell */
+    public bool[,] is_fixed;                    /* if the value at location is fixed or not */
+    private bool[,] possible_in_row;            /* if specific value is possible in specific row */
+    private bool[,] possible_in_col;            /* if specific value is possible in specific col */
+    private bool[,,] possible_in_block;         /* if specific value is possible in specific block */
+    private int _row;                           /* size of sudoku board */
+    private int _col;                           /* size of sudoku board */
+    private int block_row;                      /* size of sub-part ( block ) in board */
+    private int block_col;                      /* size of sub-part ( block ) in board */
+    private int _max_val;                       /* maximum possible val on board. 9 for 3x3 sudoku*/
+    private int _filled;
+        
+    public signal void value_changed (int row, int col, int old_value, int new_value);
+    public signal void value_changing (int row, int col, int old_value, int new_value);
+    
+    public int row
     {
         get
         {
-            int n = 0;
-            for (int i = 1; i <= 9; i++)
-                if (possible_values[i-1])
-                    n++;
-            return n;
+            return _row;
         }
     }
-
-    public bool can_be (int value)
+    
+    public int col
     {
-        return possible_values[value-1];
+        get
+        {
+            return _col;
+        }
     }
-
-    private int _value = 0;
-    public int value
+    
+    public int max_val
     {
-        set
+        get
         {
-            if (is_fixed)
-                return;
-            if (_value == value)
-                return;
-            before_value_changed ();
-            _value = value;
-            value_changed ();
+            return _max_val;
         }
-        get { return _value; }
     }
-
-    public bool _is_fixed = false;
-    public bool is_fixed
+    
+    public int filled
     {
-       set
-       {
-          _is_fixed = value;
-          value_changed ();
-       }
-       get { return _is_fixed; }
+        get
+        {
+            return _filled;
+        }
+    }
+    
+    public SudokuBoard (int row = 9, int col = 9, int block_row = 3, int block_col = 3)
+    {
+        _row = row;
+        _col = col;
+        this.block_row = block_row;
+        this.block_col = block_col;
+        _max_val = block_row * block_col;
+        cells = new int[_row, _col] ;
+        is_fixed = new bool[_row, _col];
+        /* as any row would contain "col" different values */
+        possible_in_row = new bool[_row, _col];   
+        /* as any col would contain "row" different values */
+        possible_in_col = new bool[_col, _row];   
+        possible_in_block = new bool[block_row, block_col, block_row * block_col];
+        
+        for (int l1 = 0; l1 < _row; l1++)
+        {
+            for (int l2 = 0; l2 < _col; l2++)
+            {
+                cells[l1, l2] = 0;
+                is_fixed[l1, l2] = false;
+                possible_in_row[l1, l2] = true;
+                possible_in_col[l2, l1] = true;
+            }
+        }
+        for (int l1 = 0; l1 < block_row; l1++)
+        {
+            for (int l2 = 0; l2 < block_col; l2++)
+            {   
+                for (int l3 = 0; l3 < _max_val;l3++)
+                {
+                    possible_in_block[l1, l2, l3] = true;
+                }
+            }
+        }
+    }
+    
+    public void set_from_string (string s)
+    {
+        for (int l1 = 0; l1 < s.length; l1++)
+        {
+            /* TODO: extend for all size of sudoku. */
+            if (s[l1] >= '0' && s[l1] <= '9')                        
+                this.silent_insert (l1 / _col, l1 % _col, s[l1] - '0', true);
+        }
+    }
+    
+    public bool is_possible (int row, int col, int val)
+    {
+        --val;
+        return (possible_in_row[row,val] && possible_in_col[col,val] && possible_in_block [ row / block_col, col / block_row, val]);
+    }
+    
+    public int count_possible (int row, int col)
+    {
+        int count = 0;
+        if (cells [row, col] != 0)
+            return (0);
+        for (int l = 1; l <= this.max_val; l++)
+        {
+            if (is_possible (row, col, l))
+                count++;
+        }
+        return count;
+    }
+    
+    public void silent_insert (int row, int col, int val, bool is_fixed = false)
+    /* This function should only be used by SudokuSolver to stop call to SudokuViewer. */
+    {
+        if (cells[row, col] != 0)
+            /* This should not happen when coded properly ;) */
+            return;             
+        if (!is_possible (row, col, val))
+            /* This should not happen when coded properly ;) */
+            return;             
+        cells[row, col] = val;
+        this.is_fixed[row, col] = is_fixed;
+        --val;
+        possible_in_row[row, val] = false;
+        possible_in_col[col, val] = false;
+        possible_in_block[row / block_col, col / block_row, val] = false;
+        _filled++;
+    }
+    
+    public void insert (int row, int col, int val, bool is_fixed = false)
+    {
+        int old_val = cells[row, col];
+        value_changing (row, col, old_val, val);
+        if (cells [row, col] != 0)
+            silent_remove (row, col);
+        silent_insert (row, col, val, is_fixed);
+        value_changed (row, col, old_val, val);
+    }
+    
+    public void silent_remove (int row, int col)
+    /* This function should only be used by SudokuSolver to stop call to SudokuViewer. */
+    {
+        if (is_fixed[row, col])
+            /* This should not happen when coded properly ;) */ 
+            return;
+        if (cells[row, col] == 0)
+            /* Empty cell, Just pretend everything is okay... */
+            return;
+        int val = cells[row, col];
+        --val;
+        cells[row, col] = 0;
+        possible_in_row[row, val] = true;
+        possible_in_col[col, val] = true;
+        possible_in_block[row / block_col, col / block_row, val] = true;
+        _filled--;
+    }
+    
+    public void remove (int row, int col)
+    {
+        if (is_fixed[row, col])
+            /* This should not happen when coded properly ;) */ 
+            return;
+        if (cells[row, col] == 0)
+            /* Empty cell, Just pretend everything is okay... */
+            return;
+        int old_val = cells[row, col];
+        value_changing (row, col, old_val, 0);
+        silent_remove (row, col);
+        value_changed (row, col, old_val, 0);
     }
-
-    public signal void value_changed ();
-    public signal void before_value_changed ();
     
-    public SudokuCell (int row, int col)
+    public void to_initial_state ()
     {
-        this.row = row;
-        this.col = col;
-        for (var i = 0; i < 8; i++)
-            possible_values[i] = true;
+        for (int l1 = 0; l1 < _row; l1++)
+        {
+            for (int l2 = 0; l2 < _col; l2++)
+            {
+                if (!is_fixed[l1, l2])
+                    remove (l1, l2);
+            }
+        }
     }
 }
 
 public class SudokuGame
 {
-    public SudokuCell[,] cells;
-
+    public SudokuBoard board;
+    
     private struct UndoItem
     {
         public int row;
@@ -75,56 +202,30 @@ public class SudokuGame
     private SList<UndoItem?> undostack;
     private SList<UndoItem?> redostack;
     
-    public signal void cell_changed (SudokuCell cell);
+    
+    public signal void cell_changed (int row, int col, int old_val, int new_val);
 
     public SudokuGame ()
     {
-        cells = new SudokuCell[9,9];
+        board = new SudokuBoard ();
         undostack = null;
         redostack = null;
-        for (int row = 0; row < 9; row++)
-            for (int col = 0; col < 9; col++)
-            {
-                var cell = new SudokuCell (row, col);
-                cells[row, col] = cell;
-                cell.value_changed.connect (cell_changed_cb);
-                cell.before_value_changed.connect (update_undo);
-            }
+        board.value_changing.connect (update_undo);
+        board.value_changed.connect (cell_changed_cb);
     }
-
+     
     public SudokuGame.from_string (string game)
     {
         this ();
-        set_from_string (game);
+        board.set_from_string (game);
     }
-    
+
     public void set_from_string (string game)
     {
-        assert (game.length == 81);
-        for (var i = 0; i < game.length; i++)
-        {
-            var col = i % 9;
-            var row = i / 9;
-            var digit = 0;
-            var is_fixed = false;
-
-            if (game[i] >= '1' && game[i] <= '9')
-            {
-                digit = (int) game[i] - '0';
-                is_fixed = true;
-            }
-
-            var cell = cells[row, col];
-            cell.is_fixed = false;
-            cell.value = digit;
-            cell.is_fixed = is_fixed;
-        }
-
-        /* No undo for preset cells */
+        board.set_from_string (game);
         undostack = null;
         redostack = null;
     }
-
     public void undo ()
     {
         apply_stack (ref undostack, ref redostack);
@@ -137,48 +238,59 @@ public class SudokuGame
 
     public void reset ()
     {
-        int num = 0;
-        for (int i = 0; i < 9; i++)
+        int count = board.filled;
+        board.to_initial_state ();
+        count -= board.filled;
+        add_to_stack (ref undostack, -1, -1, count);
+    }
+    
+    public void hint (ref int row, ref int col)
+    {
+        int[,] total_pos = new int [board.row, board.col];
+        int min = board.max_val + 1;
+        int count = 0;
+        for (int l1 = 0; l1 < board.row; l1++)
         {
-            for (int j = 0; j < 9; j++)
+            for(int l2 = 0; l2 < board.col; l2++)
             {
-                if (!cells[i,j].is_fixed && cells[i,j].value != 0)
+                total_pos [l1, l2] = board.count_possible (l1, l2);
+                if (total_pos [l1, l2] < min && total_pos [l1, l2] > 0)
                 {
-                    cells[i,j].value = 0;
-                    num++;
+                    min = total_pos [l1, l2];
+                    count = 0;
                 }
+                if (total_pos [l1, l2] == min)
+                    count++;
             }
         }
-        add_to_stack (ref undostack, -1, -1, num);
-    }
-
-    private void cell_changed_cb (SudokuCell cell)
-    {
-        /* Blank out adjacent squares */
-        if (cell.value > 0)
+        if (count == 0)
+            return;
+        count = Random.int_range (0, count);
+        for (int l1 = 0; l1 < board.row; l1++)
         {
-            for (int row = 0; row < 9; row++)
+            for (int l2 = 0; l2 < board.col; l2++)
             {
-                for (int col = 0; col < 9; col++)
+                if (total_pos[l1, l2] == min)
                 {
-                    if (row == cell.row && col == cell.col)
-                        continue;
-                    if (row == cell.row ||
-                        col == cell.col ||
-                        (row % 3 == cell.row % 3 && col % 3 == cell.col % 3))
+                    if (count-- == 0)
                     {
-                        cells[row, col].possible_values[cell.value-1] = false;
+                        row = l1;
+                        col = l2;
+                        return;
                     }
-                }
+                }   
             }
         }
-
-        cell_changed (cell);
     }
 
-    private void update_undo (SudokuCell cell)
+    public void cell_changed_cb (SudokuBoard board, int row, int col, int old_val, int new_val)
     {
-        add_to_stack (ref undostack, cell.row, cell.col, cell.value);
+        cell_changed (row, col, old_val, new_val);
+    }
+    
+    public void update_undo (SudokuBoard board, int row, int col, int old_val, int new_val)
+    {
+        add_to_stack (ref undostack, row, col, old_val);
         redostack = null;
     }
 
@@ -191,26 +303,25 @@ public class SudokuGame
     private void apply_stack (ref SList<UndoItem?> from, ref SList<UndoItem?> to)
     {
         if (from == null) return;
-
         /* Undoing change of single cell */
         if (from.data.row >= 0 && from.data.col >= 0)
         {
-            var cell = cells[from.data.row, from.data.col];
-
-            cell.before_value_changed.disconnect (update_undo);
-            add_to_stack (ref to, from.data.row, from.data.col, cell.value);
-            cell.value = from.data.val;
+            board.value_changing.disconnect (update_undo);
+            add_to_stack (ref to, from.data.row, from.data.col, board.cells [from.data.row, from.data.col] );
+            board.silent_remove (from.data.row, from.data.col);
+            board.insert (from.data.row, from.data.col, from.data.val);
             from.remove (from.data);
-            cell.before_value_changed.connect (update_undo);
+            board.value_changing.connect (update_undo);
         }
         /* Undoing reset action */
         else
         {
             int num = from.data.val;
             from.remove (from.data);
-            for (int i = 0; i < num; i++)
+            for (int l = 0; l < num; l++)
                 apply_stack (ref from, ref to);
             add_to_stack (ref to, -1, -1, num);
         }
+        
     }
 }
diff --git a/gnome-sudoku/src/sudoku-generator.vala b/gnome-sudoku/src/sudoku-generator.vala
index b47a240..109e9c6 100644
--- a/gnome-sudoku/src/sudoku-generator.vala
+++ b/gnome-sudoku/src/sudoku-generator.vala
@@ -18,3 +18,97 @@ public class SudokuGenerator
         return new SudokuGame.from_string (x);
     }
 }
+
+public class SudokuSolver
+{
+    private SudokuBoard board;
+    
+    public SudokuSolver (ref SudokuBoard board)
+    {
+        this.board = board;
+    }
+    
+    public bool has_solution ()
+    /*Check if current SudokuBoard has at lesat one solution or not*/
+    {
+        int solutions = 0;
+        solve (-1, -1, -1, -1, ref solutions);
+        return (solutions > 0);
+    }
+    
+    public bool has_unique_solution ()
+    /*Check if current SudokuBoard has unique solution or not*/
+    {
+        int solutions = 0;
+        solve (-1, -1, -1, -1, ref solutions);
+        return (solutions == 1);
+    }
+    
+    public bool has_many_solution ()
+    /*Check if current SudokuBoard has more thatn one solution or not*/
+    {
+        int solutions = 0;
+        solve (-1, -1, -1, -1, ref solutions);
+        return (solutions > 1);
+    }
+    
+    private void solve (int row, int col, int no, int filled, ref int solution)
+    {
+        if (filled == -1)
+        {
+            filled = board.filled;
+        }
+        if (filled == board.row * board.col)
+        {
+            solution++;
+            print_sol ();
+            return;
+        }
+        if (row == -1 || col == -1)
+        {
+            for (int l1 = 0; l1 < board.row; l1++)
+            {
+                for (int l2 = 0;l2 < board.col; l2++)
+                {
+                    if (board.cells[l1, l2] == 0)
+                    {
+                        solve (l1, l2, no, filled, ref solution);
+                        return;
+                    }
+                }
+            }
+        }
+        if (no == -1)
+        {
+            for (int l1 = 1; l1 <= board.max_val; l1++)
+            {
+                if (board.is_possible (row, col, l1))
+                {
+                    solve (row, col, l1, filled, ref solution);
+                    if (solution > 1)
+                    /*Break at solutions == 2 as we don't need exact count of possible solutions*/
+                        return;
+                }
+            }
+            return;
+        }
+        board.silent_insert (row, col, no);
+        solve (-1, -1, -1, filled + 1, ref solution);
+        board.silent_remove (row, col);
+    }
+    
+    private void print_sol ()
+    /* For checking purpose only. Should be removed from final release */
+    {
+        stdout.printf ("Solution found..\n");
+        for (int l1=0;l1<9;l1++)
+        {
+            for (int l2=0;l2<9;l2++)
+            {
+                stdout.printf ("%d ",board.cells[l1,l2]);
+            }
+            stdout.printf ("\n");
+        }
+        stdout.flush ();
+    }
+}
diff --git a/gnome-sudoku/src/sudoku-view.vala b/gnome-sudoku/src/sudoku-view.vala
index b0399a2..0410061 100644
--- a/gnome-sudoku/src/sudoku-view.vala
+++ b/gnome-sudoku/src/sudoku-view.vala
@@ -6,52 +6,107 @@ private class SudokuCellView : Gtk.DrawingArea
 
     private Gtk.Window? popup = null;
 
-    private SudokuCell _cell;
-    public SudokuCell cell
+    private SudokuBoard board;
+    private int _row;
+    private int _col;
+    
+    public int row
     {
+        get
+        {
+            return _row;
+        }
         set
         {
-            _cell = value;
-            _cell.value_changed.connect (value_changed_cb);
-            value_changed_cb (value);
+            _row = value;
         }
-        get { return _cell; }
     }
 
-    public string top_notes { set; get; default = ""; }
-    public string bottom_notes { set; get; default = ""; }
-
-    private string _text;
-    public string text
+    public int col
     {
+        get
+        {
+            return _col;
+        }
         set
         {
-            _text = value;
-            layout = create_pango_layout (_text);
-            layout.set_font_description (style.font_desc);
-            queue_draw ();
+            _col = value;
         }
-        get { return _text; }
     }
     
     public int value
     {
+        get
+        {
+            return board.cells [_row, _col];
+        }
         set
         {
+            if (is_fixed)
+            {
+                text = "%d".printf (board.cells [_row, _col]);
+                return;
+            }
             if (value == 0)
+            {
                 text = "";
-            else
+                if (board.cells [_row, _col] != 0)
+                    board.remove (_row, _col);
+                return;
+            }
+            if (value == board.cells [_row, _col])
+            {
                 text = "%d".printf (value);
+                return;
+            }
+            if (board.is_possible(_row, _col, value))
+            {
+                board.insert (_row, _col, value);
+            }
+            else
+            {
+                stdout.printf("TODO: Notification Invalid value.\n");
+                /* TODO: Notify user. Wrong value selected */
+            }
+        }
+    }
+    
+    public bool is_fixed
+    {
+        get
+        {
+            return board.is_fixed[_row, _col];
         }
     }
 
-    public SudokuCellView ()
+    public string top_notes { set; get; default = ""; }
+    public string bottom_notes { set; get; default = ""; }
+
+    private string _text;
+    public string text
     {
+        set
+        {
+            _text = value;
+            layout = create_pango_layout (_text);
+            layout.set_font_description (style.font_desc);
+            queue_draw ();
+        }
+        get { return _text; }
+    }
+    
+    public SudokuCellView (int row, int col, ref SudokuBoard board)
+    {
+        this.board = board;
+        this._row = row;
+        this._col = col;
         can_focus = true;
         style.font_desc.set_size (Pango.SCALE * 13);
         text = "";
+        this.value = board.cells[_row, _col];
         events = Gdk.EventMask.EXPOSURE_MASK | Gdk.EventMask.BUTTON_PRESS_MASK | Gdk.EventMask.KEY_PRESS_MASK;
         focus_out_event.connect (focus_out_cb);
+        board.value_changed.connect (value_changed_cb);
     }
 
     public override void get_preferred_width (out int minimal_width, out int natural_width)
@@ -80,7 +135,6 @@ private class SudokuCellView : Gtk.DrawingArea
             grab_focus ();
             return true;
         }
-
         if (event.y / get_allocated_height () < 0.25)
             show_note_editor (0);
         else if (event.y / get_allocated_height () > 0.75)
@@ -95,7 +149,8 @@ private class SudokuCellView : Gtk.DrawingArea
     {
         if (popup != null)
             return;
-
+        if (is_fixed)
+            return;
         popup = new Gtk.Window (Gtk.WindowType.POPUP);
 
         var table = new Gtk.Table (3, 3, false);
@@ -117,7 +172,7 @@ private class SudokuCellView : Gtk.DrawingArea
                 label.show ();
                 
                 button.clicked.connect (() => {
-                    cell.value = n;
+                    value = n;
                     hide_popup ();
                 });
 
@@ -161,6 +216,8 @@ private class SudokuCellView : Gtk.DrawingArea
     {
         if (popup != null)
             return;
+        if (is_fixed)
+            return;
 
         editing_notes = true;
         popup = new Gtk.Window (Gtk.WindowType.TOPLEVEL);
@@ -192,6 +249,7 @@ private class SudokuCellView : Gtk.DrawingArea
         popup.show_all ();
         popup.get_size (null, out height);
         popup.move (x, y + top * (get_allocated_height () - height));
+        /* FIXME: entry doesn't get keyboard focus when note editor is shown and does not hide if focus is lost before clicking in entry. */
     }
 
     private void hide_note_editor (Gtk.Entry entry, int top)
@@ -214,17 +272,17 @@ private class SudokuCellView : Gtk.DrawingArea
 
     public override bool key_press_event (Gdk.EventKey event)
     {
-        // FIXME: Can't find vala bindings to Gdk keyvals...
+        /* FIXME: Can't find vala bindings to Gdk keyvals... */
 
         if (event.keyval >= 49 /* 1 */ && event.keyval <= 58 /* 9 */)
         {
-            cell.value = (int)event.keyval - 48;
+            value = (int)event.keyval - 48;
             return true;
         }
 
         if (event.keyval == 48 || event.keyval == 65288 /* backspace */ || event.keyval == 65535 /* delete */)
         {
-            cell.value = 0;
+            value = 0;
             return true;
         }
         
@@ -241,7 +299,7 @@ private class SudokuCellView : Gtk.DrawingArea
     {
         if (is_focus)
             Gdk.cairo_set_source_color (c, style.base[Gtk.StateType.SELECTED]);
-        else if (cell.is_fixed)
+        else if (is_fixed)
             c.set_source_rgb (0.8, 0.8, 0.8);
         else
             c.set_source_rgb (1.0, 1.0, 1.0);
@@ -279,10 +337,18 @@ private class SudokuCellView : Gtk.DrawingArea
         return false;
     }
 
-    private void value_changed_cb (SudokuCell cell)
+    public void value_changed_cb (SudokuBoard b, int row, int col, int old_val, int new_val)
     {
-        value = cell.value;
-        can_focus = !cell.is_fixed;
+        if(row == this.row && col == this.col)
+        {
+            this.value = new_val;
+        }
+    }
+    
+    public void hint()
+    {
+        /* TODO: Show hint properly. */
+        stdout.printf ("TODO: Show hint properly.\nHint at %d %d\n", _row + 1, _col + 1);
     }
 }
 
@@ -317,9 +383,8 @@ public class SudokuView : Gtk.AspectFrame
         for (int row = 0; row < 9; row++)
             for (int col = 0; col < 9; col++)
             {
-                var cell = new SudokuCellView ();
+                var cell = new SudokuCellView (row, col, ref game.board);
                 cells[row, col] = cell;
-                cell.cell = game.cells[row, col];
                 cell.show ();
                 table.attach_defaults (cell, col, col+1, row, row+1);
             }
@@ -347,6 +412,13 @@ public class SudokuView : Gtk.AspectFrame
         get { return _show_warnings; }
         set { _show_warnings = value; }
     }
+    
+    public void hint ()
+    {
+        int row=0, col=0;
+        game.hint (ref row, ref col);
+        cells [row, col].hint ();
+    }
 
     public void clear_top_notes ()
     {



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