Implemented backtracking algorithm

This commit is contained in:
Alberto Venturini 2019-11-02 15:17:12 +02:00
parent c8ee8c311e
commit ce935b9a1a
4 changed files with 57 additions and 14 deletions

View file

@ -64,11 +64,11 @@ describe Sudoku::Board do
"...3" "...3"
end 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) board = Sudoku::Board.new(4, 2)
(0..3).each { |i| board.put(i, i, i) } (0..3).each { |i| board.put(i, i, i) }
board.solved?.should be_false board.complete?.should be_false
end end

View file

@ -17,17 +17,16 @@ describe Sudoku::Parser do
board = Sudoku::Parser.parse(board_string, block_size: 3) board = Sudoku::Parser.parse(board_string, block_size: 3)
Sudoku::Solver.solve(board) Sudoku::Solver.solve(board)
true board.to_s.should eq \
# board.to_s.should eq \ "812753649\n" \
# "812753649\n" \ "943682175\n" \
# "943682175\n" \ "675491283\n" \
# "675491283\n" \ "154237896\n" \
# "154237896\n" \ "369845721\n" \
# "369845721\n" \ "287169534\n" \
# "287169534\n" \ "521974368\n" \
# "521974368\n" \ "438526917\n" \
# "438526917\n" \ "796318452"
# "796318452"
end end

View file

@ -71,7 +71,7 @@ module Sudoku
}.join("\n") }.join("\n")
end end
def solved? def complete?
@grid.flatten.none? { |i| i == nil } @grid.flatten.none? { |i| i == nil }
end end

View file

@ -2,6 +2,50 @@ module Sudoku
class Solver class Solver
def self.solve(board) 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
end end