Ruby sudoku solver

October 14th, 2011 by Anton Fagerberg Leave a reply »

I have written a new sudoku solver in Ruby. Just a simple command line tool as a proof on concept to teach myself Ruby and improve the back-tracking code a bit (the code is a bit better written than my Java version).

The code and more info is on my Github page:
github.com/KungFooAnton/Sudoku-Solver-Ruby

class SudokuSolver
	# Transforms each cell (0-80) to the corresponding top-left (0,3, ... ,57,60) of each 3x3 square.
	def fix_pos(cell)
		return 27*(cell/9/3)+3*(cell/3%3)
	end

	def get_valid(cell, matrix)
		valid = [1,2,3,4,5,6,7,8,9]

		# Look for duplicates in row / column.
		0.upto(8) do |n|
			row_element = matrix[cell%9+n*9]
			col_element = matrix[cell/9*9+n]

			valid[row_element-1] = 0 if row_element != 0
			valid[col_element-1] = 0 if col_element != 0
		end

		# Look for duplicates in "square".
		0.upto(2) do |n|
			[0,9,18].each do |m|
				value = matrix[fix_pos(cell) + n + m]
				valid[value-1] = 0 if value != 0
			end
		end

		return valid.delete_if { |x| x == 0 }
	end

	def solve(i = 0, matrix)
		return true if i == 81
		return solve(i + 1, matrix) if matrix[i] != 0 

		get_valid(i, matrix).each do |n|
			matrix[i] = n
			return true if solve(i+1, matrix)
			matrix[i] = 0
		end

		return false
	end

	# Very simple user-input mode.
	def new
		matrix = Array.new(81).fill(0)

		0.upto(80) do |n|
			matrix[n] = "*"
			puts ascii(matrix) + "\nPick a number 0-9 (Enter or 0 is blank):"
			matrix[n] = 0

			input = gets.chomp.to_i
			puts "\n\n\n==========================================\n\n\n"

			if !get_valid(n, matrix).push(0).include?(input)
				puts "* * * * * * * * * * * * *\n Warning: invalid choice! \n* * * * * * * * * * * * *"
				redo
			end

			matrix[n] = input
		end

		return matrix
	end

	# ASCII-representation of the Sudoku puzzle.
	def ascii(matrix)
		ascii = String.new

		0.upto(80) do |n|
			ascii = ascii + "\n+-------+-------+-------+" if n % 27 == 0
			ascii = ascii + "\n" if n % 9  == 0
			ascii = ascii + "| " if n % 3 == 0
			ascii = ascii + (matrix[n] != 0 ? matrix[n].to_s : "_") + " "
			ascii = ascii + "|" if n % 9 == 8
		end

		return ascii + "\n+-------+-------+-------+"
	end
end

# Short example of how to use it.
# User have to input the sudoku puzzle from commandline.
s = SudokuSolver::new
start_time = Time.now
s.solve(matrix = s.new)
puts "* * * * * * * * * * * * * *\nSolved it in #{Time.now - start_time}s!\n* * * * * * * * * * * * * * #{s.ascii(matrix)}"

Leave a Reply