Here is my solution, just uses backtracking and simple algorithm, no
complex strategics.

class Sodoku
  
  def initialize( input )
    @ary = Array.new(9) { Array.new(9) }  # 9 rows x 9 columns
    index = 0
    input.to_s.scan(/./) do |c|
      case c
        when '1'..'9' : @ary[index / 9][index % 9] = c.to_i
        when '_'      : # skip
        else next
      end
      break if (index += 1) == 9 * 9
    end
    # raise "Input data incomplete" if index != 9 * 9
  end

  def solve
    9.times do |row|
      9.times do |col|
        next if @ary[row][col]
        
        # find next possible value for @ary[row][col]
        1.upto(9) do |v|          
          # check on same row/col, no any col/row already used it
          next unless 9.times { |i| break if @ary[i][col] == v ||
@ary[row][i] ==v }
          
          # check on 3x3 box, no other cell already used it
          next unless 3.times do |i|
            break unless 3.times do |j|
              break if @ary[i + row / 3 * 3][j + col / 3 * 3] == v
            end
          end
          
          @ary[row][col] = v

          if solve
            return true
          else
            @ary[row][col] = nil  # backtracking
          end          
        end
        
        return false
      end
    end
    true
  end
  
  def to_s
    s = "+-------+-------+-------+\n"
    @ary.each_with_index do |row, index|
      row = row.map { |e| e ||= '_' } * ' '
      row[6,0] = row[12,0] = '| '
      s << '| ' << row << " |\n"
      s << "+-------+-------+-------+\n" if [2, 5, 8].include?(index)
    end
    s
  end
end

if __FILE__ == $0
  input = ''
  while line = gets
    input << line
  end

  sodoku = Sodoku.new(input)
  puts sodoku.solve ? sodoku : "No solution"
end