class Variable
  def initialize(domain)
    @domain = domain
  end

  def fixed?
    if @domain.length == 1
      @domain[0]
    else
      false
    end
  end

  def include?(x)
    @domain.include?(x)
  end

  def fix(x)
    @domain = [x]
  end

  def eliminate(x)
    @domain.delete(x) unless fixed?
  end
end

def sudokuprint(grid, subsize, size = subsize * subsize)
  w = Math.log10(size).ceil

  bar = '┼ ┼' + (('─' * (subsize * (w + 1) + 1) + '┼') * subsize + ' ┼') * subsize

  puts bar
  puts bar
  (0...size).step(subsize).each do |rs|
    (rs...(rs + subsize)).each do |r|
      (1..size).step(subsize).each do |ds|
        print '│ │ '
        (0...size).step(subsize).each do |b|
          (b...(b + subsize)).each do |c|
            (ds...(ds + subsize)).each do |d|
              print (grid[r][c].include?(d) ? ("%#{w}d" % d) : (' ' * w)) + ' '
            end
            print '│ '
          end
          print '│ '
        end
        puts
      end
      puts bar
    end
    puts bar
  end
end

SUBSIZE = 3
SIZE = SUBSIZE * SUBSIZE

boxes = []
(0...SIZE).step(SUBSIZE).each do |r|
  (0...SIZE).step(SUBSIZE).each do |c|
    boxes.push([r, c])
  end
end

grid = Array.new(SIZE) {
  Array.new(SIZE) {
    Variable.new((1..SIZE).to_a)
  }
}

grid[0][3].fix(2)
grid[0][4].fix(9)
grid[0][5].fix(4)
grid[0][6].fix(3)
grid[0][8].fix(5)

grid[1][0].fix(9)
grid[1][1].fix(5)
grid[1][3].fix(6)
grid[1][5].fix(8)
grid[1][6].fix(7)
grid[1][8].fix(2)

grid[2][2].fix(3)
grid[2][7].fix(8)

grid[3][1].fix(8)
grid[3][8].fix(4)

grid[4][1].fix(6)
grid[4][3].fix(9)
grid[4][5].fix(1)
grid[4][7].fix(7)

grid[5][0].fix(4)
grid[5][7].fix(5)

grid[6][1].fix(1)
grid[6][6].fix(2)

grid[7][0].fix(2)
grid[7][2].fix(6)
grid[7][3].fix(5)
grid[7][5].fix(3)
grid[7][7].fix(9)
grid[7][8].fix(8)

grid[8][0].fix(3)
grid[8][2].fix(7)
grid[8][3].fix(1)
grid[8][4].fix(8)
grid[8][5].fix(2)

until grid.flatten.count(&:fixed?) == 81
  (0...SIZE).each do |i|
    row = grid[i]
    row.each do |cell|
      if v = cell.fixed?
        row.each do |c2|
          c2.eliminate(v)
        end
      end
    end
  end

  (0...SIZE).each do |i|
    col = grid.map { |row| row[i] }
    col.each do |cell|
      if v = cell.fixed?
        col.each do |c2|
          c2.eliminate(v)
        end
      end
    end
  end

  (0...SIZE).each do |i|
    rs, cs = boxes[i]
    box = []
    (rs...(rs + SUBSIZE)).each do |r|
      (cs...(cs + SUBSIZE)).each do |c|
        box.push(grid[r][c])
      end
    end

    box.each do |cell|
      if v = cell.fixed?
        box.each do |c2|
          c2.eliminate(v)
        end
      end
    end
  end
end

sudokuprint(grid, SUBSIZE, SIZE)