From ce935b9a1ac3cf9a7458f5d2a4f89c4e2109021b Mon Sep 17 00:00:00 2001
From: Alberto Venturini <aventurini@gmail.com>
Date: Sat, 2 Nov 2019 15:17:12 +0200
Subject: [PATCH] Implemented backtracking algorithm

---
 spec/board_spec.cr  |  4 ++--
 spec/solver_spec.cr | 21 ++++++++++-----------
 src/board.cr        |  2 +-
 src/solver.cr       | 44 ++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 57 insertions(+), 14 deletions(-)

diff --git a/spec/board_spec.cr b/spec/board_spec.cr
index 3af7d03..d77f94f 100644
--- a/spec/board_spec.cr
+++ b/spec/board_spec.cr
@@ -64,11 +64,11 @@ describe Sudoku::Board do
     "...3"
   end
 
-  it "should not be solved if it's not solved" do
+  it "should not be complete if there are nulls" do
     board = Sudoku::Board.new(4, 2)
     (0..3).each { |i| board.put(i, i, i) }
 
-    board.solved?.should be_false
+    board.complete?.should be_false
   end
 
 
diff --git a/spec/solver_spec.cr b/spec/solver_spec.cr
index 1317523..12b8a19 100644
--- a/spec/solver_spec.cr
+++ b/spec/solver_spec.cr
@@ -17,17 +17,16 @@ describe Sudoku::Parser do
     board = Sudoku::Parser.parse(board_string, block_size: 3)
     Sudoku::Solver.solve(board)
 
-    true
-    # board.to_s.should eq \
-    # "812753649\n" \
-    # "943682175\n" \
-    # "675491283\n" \
-    # "154237896\n" \
-    # "369845721\n" \
-    # "287169534\n" \
-    # "521974368\n" \
-    # "438526917\n" \
-    # "796318452"
+    board.to_s.should eq \
+    "812753649\n" \
+    "943682175\n" \
+    "675491283\n" \
+    "154237896\n" \
+    "369845721\n" \
+    "287169534\n" \
+    "521974368\n" \
+    "438526917\n" \
+    "796318452"
 
   end
 
diff --git a/src/board.cr b/src/board.cr
index a211e56..3b6b022 100644
--- a/src/board.cr
+++ b/src/board.cr
@@ -71,7 +71,7 @@ module Sudoku
       }.join("\n")
     end
 
-    def solved?
+    def complete?
       @grid.flatten.none? { |i| i == nil }
     end
       
diff --git a/src/solver.cr b/src/solver.cr
index c5b6599..d3ea033 100644
--- a/src/solver.cr
+++ b/src/solver.cr
@@ -2,6 +2,50 @@ module Sudoku
   class Solver
 
     def self.solve(board)
+      solve_rec(board, find_next_nil(board, 0))
+    end
+
+    # Simple solver with backtracking
+    private def self.solve_rec(board, index)
+      if board.complete?
+        true
+      else
+        coords = index_to_coordinates(board, index)
+        x = coords[:x]
+        y = coords[:y]
+
+        result = (1..board.size).any? { |v|
+          board.valid?(v, x, y) && begin
+            board.put(v, x, y)
+            solve_rec(board, find_next_nil(board, index))
+          end
+        }
+
+        if !result
+          board.put(nil, x, y)
+        end
+
+        result
+      end
+    end
+
+    private def self.find_next_nil(board, index)
+      max = (board.size ** 2) - 1
+
+      new_index = (index..max).find(-1) { |i|
+        coords = index_to_coordinates(board, i)
+        board.get(coords[:x], coords[:y]) == nil
+      }
+
+      new_index
+    end
+
+    private def self.coordinates_to_index(board, x, y)
+      y * board.size + x
+    end
+
+    private def self.index_to_coordinates(board, index : Int32)
+      { x: index % board.size, y: index // board.size }
     end
 
   end