Permalink
Browse files

Refactoring and commenting

  • Loading branch information...
1 parent 384b1b4 commit e08572827119e24bbf8dec2a7950bef2da91ad86 Dave Hoover committed Jan 2, 2009
Showing with 57 additions and 23 deletions.
  1. +22 −12 sudoku/brain.rb
  2. +20 −2 sudoku/brain_cell.rb
  3. +3 −1 sudoku/grids.rb
  4. +5 −3 sudoku/solver.rb
  5. +7 −5 sudoku/solvers.rb
View
@@ -1,50 +1,60 @@
+# The Brain is basically a collection of BrainCells
class Brain < Array
- def initialize
- 9.times { |i| self[i] = (1..9).map { BrainCell.new([*1..9]) } }
+ def initialize(board)
+ board.each_with_index do |row, y|
+ self[y] = []
+ board.each_with_index do |cell, x|
+ self[y][x] = BrainCell.new([*1..9], [x, y])
+ end
+ end
end
def narrow(blanks, board, grids)
blanks.each do |x, y|
brain_cell = self[y][x]
- brain_cell.narrow(x, y, board, grids)
+ brain_cell.narrow(board, grids)
end
end
+ # Solvers are a collection of BrainCells that have only one possibility
def solvers(blanks)
solvers = Solvers.new
blanks.each do |x, y|
brain_cell = self[y][x]
if brain_cell.solved?
- solvers << OpenStruct.new(:x => x, :y => y, :value => brain_cell.solved_value)
+ solvers << brain_cell
end
end
solvers
end
+ # Sometimes in Sudoku, you have to guess in order to move forward
def guess(board, blanks, state)
- brain_cells = sorted_cells_with_coords_from(blanks)
- brain_cells.each do |brain_cell, coords|
- brain_cell.size.times do |i|
- board[coords.last][coords.first] = brain_cell[i]
+ # I sort these as an optimization, it makes more sense to guess
+ # with the BrainCells with less possibilities first.
+ sorted_cells_from(blanks).each do |brain_cell|
+ brain_cell.each do |possibility|
if state.finished
return
else
+ # I made this concurrent as an optimization for evil puzzles.
Thread.new do
+ # Here is where I "guess", by setting the spot on the board.
+ board.set(brain_cell.x, brain_cell.y, possibility)
think(board, self, state)
end
end
end
- board[coords.last][coords.first] = 0
end
end
private
- def sorted_cells_with_coords_from(blanks)
+ def sorted_cells_from(blanks)
blanks.map { |x, y|
- [self[y][x], [x, y]]
- }.sort_by { |brain_cell, coords|
+ self[y][x]
+ }.sort_by { |brain_cell|
brain_cell.size
}
end
View
@@ -1,8 +1,14 @@
-# Would be nice to have coords in the BrainCell
+# A brain cell holds the current possibilities for a specific spot on the board
class BrainCell < Array
class EmptyException < Exception; end
- def narrow(x, y, board, grids)
+ def initialize(possibilities, coords)
+ super(possibilities)
+ @coords = coords
+ end
+
+ # This is where we narrow down the possibilities
+ def narrow(board, grids)
row = board[y]
col = board.map { |r| r[x] }
reject! { |n| row.include?(n) || col.include?(n) }
@@ -21,4 +27,16 @@ def solved_value
raise "Not solved: #{self.join(", ")}" unless solved?
first
end
+
+ def x
+ @coords.first
+ end
+
+ def y
+ @coords.last
+ end
+
+ def ==(other)
+ self.x == other.x && self.y == other.y
+ end
end
View
@@ -1,5 +1,7 @@
class Grids
- SETUP = [[0..2, 0..2, []],[3..5, 0..2, []], [6..8, 0..2, []], [0..2, 3..5, []],[3..5, 3..5, []], [6..8, 3..5, []], [0..2, 6..8, []],[3..5, 6..8, []], [6..8, 6..8, []]]
+ SETUP = [[0..2, 0..2, []],[3..5, 0..2, []], [6..8, 0..2, []],
+ [0..2, 3..5, []],[3..5, 3..5, []], [6..8, 3..5, []],
+ [0..2, 6..8, []],[3..5, 6..8, []], [6..8, 6..8, []]]
def initialize(board)
@grids = SETUP.deep_copy
View
@@ -10,18 +10,20 @@ def solve(start)
board = Board.new(start)
fail("Bad board") unless board.size == 9 && board[4].size == 9
- brain = Brain.new
+ brain = Brain.new(board)
fail("Bad brain: rows=#{brain.size}, cols=#{brain[7].size}") unless brain.size == 9 && brain[7].size == 9 && brain[3][1].size == 9
state = OpenStruct.new
think(board, brain, state)
-
return state.finished.to_s
end
def think(board, brain, state)
+ # I make copies of the board and brain because I need to make guesses
+ # that I don't want to have to clean up
board = board.deep_copy
brain = brain.deep_copy
+
grids = Grids.new(board)
blanks = board.blanks
@@ -46,7 +48,7 @@ def think(board, brain, state)
begin
solvers.check_for_conflicts(grids)
solvers.solve(board)
- rescue Solver::ConflictException
+ rescue Solvers::ConflictException
return
end
end
View
@@ -1,3 +1,5 @@
+# A collection of BrainCells that have just one possibility left
+# and therefore are "Solvers"
class Solvers < Array
class ConflictException < Exception; end
@@ -6,17 +8,17 @@ def check_for_conflicts(grids)
grid1 = grids.get(s1.x, s1.y)
each do |s2|
- next if s1.x == s2.x && s1.y == s2.y
+ next if s1 == s2
- if s1.x == s2.x && s1.value == s2.value
+ if s1.x == s2.x && s1.solved_value == s2.solved_value
raise ConflictException
end
- if s1.y == s2.y && s1.value == s2.value
+ if s1.y == s2.y && s1.solved_value == s2.solved_value
raise ConflictException
end
grid2 = grids.get(s2.x, s2.y)
- if grid1 == grid2 && s1.value == s2.value
+ if grid1 == grid2 && s1.solved_value == s2.solved_value
raise ConflictException
end
end
@@ -25,7 +27,7 @@ def check_for_conflicts(grids)
def solve(board)
each do |s|
- board.set(s.x, s.y, s.value)
+ board.set(s.x, s.y, s.solved_value)
end
end
end

0 comments on commit e085728

Please sign in to comment.