RosettaCodeData/Task/Sudoku/Ruby/sudoku.rb

92 lines
2.2 KiB
Ruby
Raw Permalink Normal View History

2015-02-20 00:35:01 -05:00
def read_matrix(data)
2016-12-05 22:15:40 +01:00
lines = data.lines
2015-02-20 00:35:01 -05:00
9.times.collect { |i| 9.times.collect { |j| lines[i][j].to_i } }
2013-04-11 01:07:29 -07:00
end
def permissible(matrix, i, j)
2015-02-20 00:35:01 -05:00
ok = [nil, *1..9]
2016-12-05 22:15:40 +01:00
check = ->(x,y) { ok[matrix[x][y]] = nil if matrix[x][y].nonzero? }
2013-04-11 01:07:29 -07:00
# Same as another in the column isn't permissible...
2016-12-05 22:15:40 +01:00
9.times { |x| check[x, j] }
2013-04-11 01:07:29 -07:00
# Same as another in the row isn't permissible...
2016-12-05 22:15:40 +01:00
9.times { |y| check[i, y] }
2013-04-11 01:07:29 -07:00
# Same as another in the 3x3 block isn't permissible...
2016-12-05 22:15:40 +01:00
xary = [ *(x = (i / 3) * 3) .. x + 2 ] #=> [0,1,2], [3,4,5] or [6,7,8]
yary = [ *(y = (j / 3) * 3) .. y + 2 ]
xary.product(yary).each { |x, y| check[x, y] }
2015-02-20 00:35:01 -05:00
# Gathering only permitted one
ok.compact
2013-04-11 01:07:29 -07:00
end
def deep_copy_sudoku(matrix)
matrix.collect { |row| row.dup }
end
def solve_sudoku(matrix)
loop do
options = []
2015-02-20 00:35:01 -05:00
9.times do |i|
9.times do |j|
next if matrix[i][j].nonzero?
2013-04-11 01:07:29 -07:00
p = permissible(matrix, i, j)
# If nothing is permissible, there is no solution at this level.
2015-02-20 00:35:01 -05:00
return if p.empty? # return nil
options << [i, j, p]
end
end
2013-04-11 01:07:29 -07:00
# If the matrix is complete, we have a solution...
2015-02-20 00:35:01 -05:00
return matrix if options.empty?
2013-04-11 01:07:29 -07:00
2015-02-20 00:35:01 -05:00
i, j, permissible = options.min_by { |x| x.last.length }
2013-04-11 01:07:29 -07:00
# If there is an option with only one solution, set it and re-check permissibility
2015-02-20 00:35:01 -05:00
if permissible.length == 1
matrix[i][j] = permissible[0]
2013-04-11 01:07:29 -07:00
next
end
# We have two or more choices. We need to search both...
2015-02-20 00:35:01 -05:00
permissible.each do |v|
2013-04-11 01:07:29 -07:00
mtmp = deep_copy_sudoku(matrix)
2015-02-20 00:35:01 -05:00
mtmp[i][j] = v
2013-04-11 01:07:29 -07:00
ret = solve_sudoku(mtmp)
return ret if ret
2015-02-20 00:35:01 -05:00
end
2013-04-11 01:07:29 -07:00
# We did an exhaustive search on this branch and nothing worked out.
2015-02-20 00:35:01 -05:00
return
2013-04-11 01:07:29 -07:00
end
end
def print_matrix(matrix)
2015-02-20 00:35:01 -05:00
puts "Impossible" or return unless matrix
2013-04-11 01:07:29 -07:00
border = "+-----+-----+-----+"
2015-02-20 00:35:01 -05:00
9.times do |i|
2013-04-11 01:07:29 -07:00
puts border if i%3 == 0
2015-02-20 00:35:01 -05:00
9.times do |j|
print j%3 == 0 ? "|" : " "
print matrix[i][j] == 0 ? "." : matrix[i][j]
end
puts "|"
end
2013-04-11 01:07:29 -07:00
puts border
end
2015-02-20 00:35:01 -05:00
data = <<EOS
2013-04-11 01:07:29 -07:00
394__267_
___3__4__
5__69__2_
_45___9__
6_______7
__7___58_
_1__67__8
__9__8___
_264__735
2015-02-20 00:35:01 -05:00
EOS
matrix = read_matrix(data)
print_matrix(matrix)
puts
print_matrix(solve_sudoku(matrix))