doku 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Gemfile +32 -0
 - data/README.rdoc +15 -0
 - data/Rakefile +49 -0
 - data/VERSION +1 -0
 - data/lib/doku.rb +4 -0
 - data/lib/doku/dancing_links.rb +561 -0
 - data/lib/doku/grid.rb +241 -0
 - data/lib/doku/hexadoku.rb +56 -0
 - data/lib/doku/hexamurai.rb +95 -0
 - data/lib/doku/puzzle.rb +250 -0
 - data/lib/doku/solver.rb +103 -0
 - data/lib/doku/sudoku.rb +42 -0
 - data/spec/dancing_links_spec.rb +212 -0
 - data/spec/hexadoku_spec.rb +71 -0
 - data/spec/hexamurai_spec.rb +38 -0
 - data/spec/puzzle_spec.rb +147 -0
 - data/spec/solution_spec.rb +278 -0
 - data/spec/spec_helper.rb +19 -0
 - data/spec/sudoku_spec.rb +52 -0
 - data/spec/watch.rb +9 -0
 - metadata +198 -0
 
    
        data/spec/puzzle_spec.rb
    ADDED
    
    | 
         @@ -0,0 +1,147 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'backports' unless defined? require_relative
         
     | 
| 
      
 2 
     | 
    
         
            +
            require_relative 'spec_helper'
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            class TestPuzzle < Doku::Puzzle
         
     | 
| 
      
 5 
     | 
    
         
            +
              has_squares [1,2,3,4]
         
     | 
| 
      
 6 
     | 
    
         
            +
              has_glyphs  [true,false]
         
     | 
| 
      
 7 
     | 
    
         
            +
              
         
     | 
| 
      
 8 
     | 
    
         
            +
              define_group [1,2]
         
     | 
| 
      
 9 
     | 
    
         
            +
              define_group [2,3]
         
     | 
| 
      
 10 
     | 
    
         
            +
              define_group [3,4]
         
     | 
| 
      
 11 
     | 
    
         
            +
              define_group [4,1]
         
     | 
| 
      
 12 
     | 
    
         
            +
            end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
            describe "Puzzle instance" do
         
     | 
| 
      
 15 
     | 
    
         
            +
              before do
         
     | 
| 
      
 16 
     | 
    
         
            +
                @puzzle = TestPuzzle.new(1 => true)
         
     | 
| 
      
 17 
     | 
    
         
            +
              end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
              it "is basically a hash of square => glyph" do
         
     | 
| 
      
 20 
     | 
    
         
            +
                @puzzle[1].should == true
         
     | 
| 
      
 21 
     | 
    
         
            +
                @puzzle[4].should == nil
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                @puzzle[1] = false
         
     | 
| 
      
 24 
     | 
    
         
            +
                @puzzle[1].should == false
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                @puzzle[1] = nil
         
     | 
| 
      
 27 
     | 
    
         
            +
                @puzzle[1].should == nil
         
     | 
| 
      
 28 
     | 
    
         
            +
              end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
              it "has an each method that excludes nil values" do
         
     | 
| 
      
 31 
     | 
    
         
            +
                TestPuzzle.new(2 => nil).each { raise }
         
     | 
| 
      
 32 
     | 
    
         
            +
              end
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
              it "only allows squares as keys" do
         
     | 
| 
      
 35 
     | 
    
         
            +
                lambda { @puzzle[0] }.should raise_error IndexError
         
     | 
| 
      
 36 
     | 
    
         
            +
                lambda { @puzzle[0] = false }.should raise_error IndexError
         
     | 
| 
      
 37 
     | 
    
         
            +
              end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
              it "only allows glyphs or nil as values" do
         
     | 
| 
      
 40 
     | 
    
         
            +
                lambda { @puzzle[1] = 10 }.should raise_error ArgumentError
         
     | 
| 
      
 41 
     | 
    
         
            +
              end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
              it "can not be initialized with bad data" do
         
     | 
| 
      
 44 
     | 
    
         
            +
                lambda { TestPuzzle.new(:foo => true) }.should raise_error IndexError
         
     | 
| 
      
 45 
     | 
    
         
            +
                lambda { TestPuzzle.new(1 => :foo) }.should raise_error ArgumentError
         
     | 
| 
      
 46 
     | 
    
         
            +
              end
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
              describe "comparison operators say" do
         
     | 
| 
      
 49 
     | 
    
         
            +
                it "A == B iff the square=>glyph pairs in A and B are the same" do
         
     | 
| 
      
 50 
     | 
    
         
            +
                  @puzzle.should_not == TestPuzzle.new
         
     | 
| 
      
 51 
     | 
    
         
            +
                  TestPuzzle.new.should_not == @puzzle.should
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
                  puzzle2 = TestPuzzle.new(1 => true)
         
     | 
| 
      
 54 
     | 
    
         
            +
                  @puzzle.should == puzzle2
         
     | 
| 
      
 55 
     | 
    
         
            +
                  puzzle2.should == @puzzle
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                  (TestPuzzle.new(1 => true) != @puzzle).should == false
         
     | 
| 
      
 58 
     | 
    
         
            +
                end
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                it "A eql? B is the same as A == B" do
         
     | 
| 
      
 61 
     | 
    
         
            +
                  @puzzle.should_not eql TestPuzzle.new
         
     | 
| 
      
 62 
     | 
    
         
            +
                  TestPuzzle.new.should_not eql @puzzle.should
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
                  puzzle2 = TestPuzzle.new(1 => true)
         
     | 
| 
      
 65 
     | 
    
         
            +
                  @puzzle.should eql puzzle2
         
     | 
| 
      
 66 
     | 
    
         
            +
                  puzzle2.should eql @puzzle
         
     | 
| 
      
 67 
     | 
    
         
            +
                end
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
                it "A.subset?(B) iff square=>glyph pairs in A are a subset of those in B" do
         
     | 
| 
      
 70 
     | 
    
         
            +
                  @puzzle.should_not be_a_subset TestPuzzle.new
         
     | 
| 
      
 71 
     | 
    
         
            +
                  TestPuzzle.new.should be_a_subset @puzzle
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
      
 73 
     | 
    
         
            +
                  @puzzle.should be_a_subset @puzzle
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
                  @puzzle.should be_a_subset TestPuzzle.new(1 => true, 3 => false)
         
     | 
| 
      
 76 
     | 
    
         
            +
                end
         
     | 
| 
      
 77 
     | 
    
         
            +
              end
         
     | 
| 
      
 78 
     | 
    
         
            +
             
     | 
| 
      
 79 
     | 
    
         
            +
              describe "#filled?" do
         
     | 
| 
      
 80 
     | 
    
         
            +
                it "returns true iff all squares have been assigned a glyph" do
         
     | 
| 
      
 81 
     | 
    
         
            +
                  TestPuzzle.new(1 => true, 2=> true, 3 => false, 4 => false).should be_filled
         
     | 
| 
      
 82 
     | 
    
         
            +
                  @puzzle.should_not be_filled
         
     | 
| 
      
 83 
     | 
    
         
            +
                end
         
     | 
| 
      
 84 
     | 
    
         
            +
              end
         
     | 
| 
      
 85 
     | 
    
         
            +
             
     | 
| 
      
 86 
     | 
    
         
            +
              describe "#valid?" do
         
     | 
| 
      
 87 
     | 
    
         
            +
                it "returns false iff a group has the same glyph in it twice" do
         
     | 
| 
      
 88 
     | 
    
         
            +
                  @puzzle.should be_valid
         
     | 
| 
      
 89 
     | 
    
         
            +
                  TestPuzzle.new(1 => true, 2=> true).should_not be_valid      
         
     | 
| 
      
 90 
     | 
    
         
            +
                end
         
     | 
| 
      
 91 
     | 
    
         
            +
              end
         
     | 
| 
      
 92 
     | 
    
         
            +
             
     | 
| 
      
 93 
     | 
    
         
            +
              describe "#solution?" do
         
     | 
| 
      
 94 
     | 
    
         
            +
                it "returns true iff it is filled and valid" do
         
     | 
| 
      
 95 
     | 
    
         
            +
                  puzzle2 = TestPuzzle.new(1 => false, 2 => true, 3 => false, 4 => true)
         
     | 
| 
      
 96 
     | 
    
         
            +
                  puzzle2.should be_a_solution
         
     | 
| 
      
 97 
     | 
    
         
            +
             
     | 
| 
      
 98 
     | 
    
         
            +
                  puzzle3 = TestPuzzle.new(1 => true, 2=> true, 3 => false, 4 => false)
         
     | 
| 
      
 99 
     | 
    
         
            +
                  puzzle3.should_not be_a_solution
         
     | 
| 
      
 100 
     | 
    
         
            +
                end
         
     | 
| 
      
 101 
     | 
    
         
            +
              end
         
     | 
| 
      
 102 
     | 
    
         
            +
             
     | 
| 
      
 103 
     | 
    
         
            +
              describe "#solution_for?(puzzle)" do
         
     | 
| 
      
 104 
     | 
    
         
            +
                it "returns true if this puzzle is a solution and a super set of the other puzzle" do
         
     | 
| 
      
 105 
     | 
    
         
            +
                  puzzle4 = TestPuzzle.new(1 => false, 2 => true, 3 => false, 4 => true)
         
     | 
| 
      
 106 
     | 
    
         
            +
                  puzzle4.should_not be_a_solution_for @puzzle
         
     | 
| 
      
 107 
     | 
    
         
            +
             
     | 
| 
      
 108 
     | 
    
         
            +
                  puzzle5 = TestPuzzle.new(1 => true, 2 => false, 3 => true, 4 => false)
         
     | 
| 
      
 109 
     | 
    
         
            +
                  puzzle5.should be_a_solution_for @puzzle
         
     | 
| 
      
 110 
     | 
    
         
            +
                end
         
     | 
| 
      
 111 
     | 
    
         
            +
              end
         
     | 
| 
      
 112 
     | 
    
         
            +
             
     | 
| 
      
 113 
     | 
    
         
            +
              shared_examples_for "copied puzzle" do
         
     | 
| 
      
 114 
     | 
    
         
            +
                it "produces an equal puzzle" do
         
     | 
| 
      
 115 
     | 
    
         
            +
                  @copy.should == @puzzle
         
     | 
| 
      
 116 
     | 
    
         
            +
                  @copy.should_not equal @puzzle
         
     | 
| 
      
 117 
     | 
    
         
            +
                end
         
     | 
| 
      
 118 
     | 
    
         
            +
             
     | 
| 
      
 119 
     | 
    
         
            +
                it "produces a puzzle with a different glyph_state hash" do
         
     | 
| 
      
 120 
     | 
    
         
            +
                  @copy.glyph_state.should_not equal @puzzle.glyph_state
         
     | 
| 
      
 121 
     | 
    
         
            +
                end
         
     | 
| 
      
 122 
     | 
    
         
            +
              end
         
     | 
| 
      
 123 
     | 
    
         
            +
             
     | 
| 
      
 124 
     | 
    
         
            +
              describe "dup" do
         
     | 
| 
      
 125 
     | 
    
         
            +
                before do
         
     | 
| 
      
 126 
     | 
    
         
            +
                  @copy = @puzzle.dup
         
     | 
| 
      
 127 
     | 
    
         
            +
                end
         
     | 
| 
      
 128 
     | 
    
         
            +
             
     | 
| 
      
 129 
     | 
    
         
            +
                it_should_behave_like "copied puzzle"
         
     | 
| 
      
 130 
     | 
    
         
            +
              end
         
     | 
| 
      
 131 
     | 
    
         
            +
             
     | 
| 
      
 132 
     | 
    
         
            +
              describe "clone" do
         
     | 
| 
      
 133 
     | 
    
         
            +
                before do
         
     | 
| 
      
 134 
     | 
    
         
            +
                  @copy = @puzzle.clone
         
     | 
| 
      
 135 
     | 
    
         
            +
                end
         
     | 
| 
      
 136 
     | 
    
         
            +
             
     | 
| 
      
 137 
     | 
    
         
            +
                it_should_behave_like "copied puzzle"
         
     | 
| 
      
 138 
     | 
    
         
            +
              end
         
     | 
| 
      
 139 
     | 
    
         
            +
             
     | 
| 
      
 140 
     | 
    
         
            +
              it "equality and hash codes are not affected by nil values" do
         
     | 
| 
      
 141 
     | 
    
         
            +
                puzzle2 = TestPuzzle.new(1 => true, 3 => nil)
         
     | 
| 
      
 142 
     | 
    
         
            +
                @puzzle[2] = nil
         
     | 
| 
      
 143 
     | 
    
         
            +
                puzzle2.should == @puzzle
         
     | 
| 
      
 144 
     | 
    
         
            +
                puzzle2.hash.should == @puzzle.hash
         
     | 
| 
      
 145 
     | 
    
         
            +
              end
         
     | 
| 
      
 146 
     | 
    
         
            +
             
     | 
| 
      
 147 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,278 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'backports' unless defined? require_relative
         
     | 
| 
      
 2 
     | 
    
         
            +
            require_relative 'spec_helper'
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            describe "Puzzle#solve" do
         
     | 
| 
      
 5 
     | 
    
         
            +
              context 'given the sudoku puzzle' do
         
     | 
| 
      
 6 
     | 
    
         
            +
                before do
         
     | 
| 
      
 7 
     | 
    
         
            +
                  @puzzle = Doku::Sudoku.new <<END
         
     | 
| 
      
 8 
     | 
    
         
            +
            ...|..8|...
         
     | 
| 
      
 9 
     | 
    
         
            +
            ..7|.35|..9
         
     | 
| 
      
 10 
     | 
    
         
            +
            5..|4.6|8..
         
     | 
| 
      
 11 
     | 
    
         
            +
            ---+---+---
         
     | 
| 
      
 12 
     | 
    
         
            +
            ...|..4|2..
         
     | 
| 
      
 13 
     | 
    
         
            +
            4..|...|.37
         
     | 
| 
      
 14 
     | 
    
         
            +
            8..|...|5..
         
     | 
| 
      
 15 
     | 
    
         
            +
            ---+---+---
         
     | 
| 
      
 16 
     | 
    
         
            +
            .9.|.67|...
         
     | 
| 
      
 17 
     | 
    
         
            +
            ..3|...|1.5
         
     | 
| 
      
 18 
     | 
    
         
            +
            ...|...|..3
         
     | 
| 
      
 19 
     | 
    
         
            +
            END
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                  @solution = @puzzle.solve
         
     | 
| 
      
 22 
     | 
    
         
            +
                end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                it 'solves the puzzle' do
         
     | 
| 
      
 25 
     | 
    
         
            +
                  @solution.solution_for?(@puzzle).should == true
         
     | 
| 
      
 26 
     | 
    
         
            +
                end
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                it 'is the correct solution' do
         
     | 
| 
      
 29 
     | 
    
         
            +
                  @solution.to_grid_string.strip.should == <<END.strip
         
     | 
| 
      
 30 
     | 
    
         
            +
            964|278|351
         
     | 
| 
      
 31 
     | 
    
         
            +
            287|135|649
         
     | 
| 
      
 32 
     | 
    
         
            +
            531|496|872
         
     | 
| 
      
 33 
     | 
    
         
            +
            ---+---+---
         
     | 
| 
      
 34 
     | 
    
         
            +
            319|754|286
         
     | 
| 
      
 35 
     | 
    
         
            +
            452|681|937
         
     | 
| 
      
 36 
     | 
    
         
            +
            876|923|514
         
     | 
| 
      
 37 
     | 
    
         
            +
            ---+---+---
         
     | 
| 
      
 38 
     | 
    
         
            +
            195|367|428
         
     | 
| 
      
 39 
     | 
    
         
            +
            723|849|165
         
     | 
| 
      
 40 
     | 
    
         
            +
            648|512|793
         
     | 
| 
      
 41 
     | 
    
         
            +
            END
         
     | 
| 
      
 42 
     | 
    
         
            +
                end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                it "can also find the solution using Donald Knuth's recursive DLX" do
         
     | 
| 
      
 45 
     | 
    
         
            +
                  sm = @puzzle.to_link_matrix
         
     | 
| 
      
 46 
     | 
    
         
            +
                  exact_cover = sm.find_exact_cover_recursive
         
     | 
| 
      
 47 
     | 
    
         
            +
                  solution = @puzzle.exact_cover_to_solution exact_cover
         
     | 
| 
      
 48 
     | 
    
         
            +
                  solution.should == @solution
         
     | 
| 
      
 49 
     | 
    
         
            +
                end
         
     | 
| 
      
 50 
     | 
    
         
            +
              end
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
              context 'given a sudoku puzzle with two solutions' do
         
     | 
| 
      
 53 
     | 
    
         
            +
                before do
         
     | 
| 
      
 54 
     | 
    
         
            +
                  @puzzle = Doku::Sudoku.new <<END
         
     | 
| 
      
 55 
     | 
    
         
            +
            ...|..8|...
         
     | 
| 
      
 56 
     | 
    
         
            +
            ..7|.3.|..9
         
     | 
| 
      
 57 
     | 
    
         
            +
            5..|4.6|8..
         
     | 
| 
      
 58 
     | 
    
         
            +
            ---+---+---
         
     | 
| 
      
 59 
     | 
    
         
            +
            ...|..4|2..
         
     | 
| 
      
 60 
     | 
    
         
            +
            4..|...|.37
         
     | 
| 
      
 61 
     | 
    
         
            +
            8..|...|5..
         
     | 
| 
      
 62 
     | 
    
         
            +
            ---+---+---
         
     | 
| 
      
 63 
     | 
    
         
            +
            .9.|.67|...
         
     | 
| 
      
 64 
     | 
    
         
            +
            ..3|...|1.5
         
     | 
| 
      
 65 
     | 
    
         
            +
            ...|...|..3
         
     | 
| 
      
 66 
     | 
    
         
            +
            END
         
     | 
| 
      
 67 
     | 
    
         
            +
                end
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
                it 'finds two solutions' do
         
     | 
| 
      
 70 
     | 
    
         
            +
                  solutions = @puzzle.solutions.to_a
         
     | 
| 
      
 71 
     | 
    
         
            +
                  solutions.size.should == 2
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
      
 73 
     | 
    
         
            +
                  solutions.should include Doku::Sudoku.new <<END
         
     | 
| 
      
 74 
     | 
    
         
            +
            964|278|351
         
     | 
| 
      
 75 
     | 
    
         
            +
            287|135|649
         
     | 
| 
      
 76 
     | 
    
         
            +
            531|496|872
         
     | 
| 
      
 77 
     | 
    
         
            +
            ---+---+---
         
     | 
| 
      
 78 
     | 
    
         
            +
            319|754|286
         
     | 
| 
      
 79 
     | 
    
         
            +
            452|681|937
         
     | 
| 
      
 80 
     | 
    
         
            +
            876|923|514
         
     | 
| 
      
 81 
     | 
    
         
            +
            ---+---+---
         
     | 
| 
      
 82 
     | 
    
         
            +
            195|367|428
         
     | 
| 
      
 83 
     | 
    
         
            +
            723|849|165
         
     | 
| 
      
 84 
     | 
    
         
            +
            648|512|793
         
     | 
| 
      
 85 
     | 
    
         
            +
            END
         
     | 
| 
      
 86 
     | 
    
         
            +
             
     | 
| 
      
 87 
     | 
    
         
            +
                  solutions.should include Doku::Sudoku.new <<END
         
     | 
| 
      
 88 
     | 
    
         
            +
            964|278|351
         
     | 
| 
      
 89 
     | 
    
         
            +
            287|531|649
         
     | 
| 
      
 90 
     | 
    
         
            +
            531|496|872
         
     | 
| 
      
 91 
     | 
    
         
            +
            ---+---+---
         
     | 
| 
      
 92 
     | 
    
         
            +
            359|714|286
         
     | 
| 
      
 93 
     | 
    
         
            +
            412|685|937
         
     | 
| 
      
 94 
     | 
    
         
            +
            876|923|514
         
     | 
| 
      
 95 
     | 
    
         
            +
            ---+---+---
         
     | 
| 
      
 96 
     | 
    
         
            +
            195|367|428
         
     | 
| 
      
 97 
     | 
    
         
            +
            723|849|165
         
     | 
| 
      
 98 
     | 
    
         
            +
            648|152|793
         
     | 
| 
      
 99 
     | 
    
         
            +
            END
         
     | 
| 
      
 100 
     | 
    
         
            +
             
     | 
| 
      
 101 
     | 
    
         
            +
                  solutions[0].should be_a_solution_for @puzzle
         
     | 
| 
      
 102 
     | 
    
         
            +
                  solutions[1].should be_a_solution_for @puzzle
         
     | 
| 
      
 103 
     | 
    
         
            +
                end
         
     | 
| 
      
 104 
     | 
    
         
            +
             
     | 
| 
      
 105 
     | 
    
         
            +
              end
         
     | 
| 
      
 106 
     | 
    
         
            +
             
     | 
| 
      
 107 
     | 
    
         
            +
              context 'given a sudoku puzzle with NO solutions' do
         
     | 
| 
      
 108 
     | 
    
         
            +
                before do
         
     | 
| 
      
 109 
     | 
    
         
            +
                  @puzzle = Doku::Sudoku.new <<END
         
     | 
| 
      
 110 
     | 
    
         
            +
            123|...|...
         
     | 
| 
      
 111 
     | 
    
         
            +
            456|...|...
         
     | 
| 
      
 112 
     | 
    
         
            +
            78.|...|...
         
     | 
| 
      
 113 
     | 
    
         
            +
            ---+---+---
         
     | 
| 
      
 114 
     | 
    
         
            +
            ..1|...|...
         
     | 
| 
      
 115 
     | 
    
         
            +
            ...|9..|...
         
     | 
| 
      
 116 
     | 
    
         
            +
            ..2|...|...
         
     | 
| 
      
 117 
     | 
    
         
            +
            ---+---+---
         
     | 
| 
      
 118 
     | 
    
         
            +
            ..4|...|...
         
     | 
| 
      
 119 
     | 
    
         
            +
            ...|9..|...
         
     | 
| 
      
 120 
     | 
    
         
            +
            ..5|...|...
         
     | 
| 
      
 121 
     | 
    
         
            +
            END
         
     | 
| 
      
 122 
     | 
    
         
            +
                end
         
     | 
| 
      
 123 
     | 
    
         
            +
             
     | 
| 
      
 124 
     | 
    
         
            +
                it 'finds no solutions' do
         
     | 
| 
      
 125 
     | 
    
         
            +
                  @puzzle.solve.should == nil
         
     | 
| 
      
 126 
     | 
    
         
            +
                end
         
     | 
| 
      
 127 
     | 
    
         
            +
             
     | 
| 
      
 128 
     | 
    
         
            +
                it 'can not tell instantly there is no solution, in this case' do
         
     | 
| 
      
 129 
     | 
    
         
            +
                  sm = @puzzle.to_link_matrix
         
     | 
| 
      
 130 
     | 
    
         
            +
                  sc = sm.columns.min_by(&:size)
         
     | 
| 
      
 131 
     | 
    
         
            +
                  sc.size.should > 0
         
     | 
| 
      
 132 
     | 
    
         
            +
                end
         
     | 
| 
      
 133 
     | 
    
         
            +
             
     | 
| 
      
 134 
     | 
    
         
            +
                it 'finds no solutions using the recursive algorithm' do
         
     | 
| 
      
 135 
     | 
    
         
            +
                  sm = @puzzle.to_link_matrix
         
     | 
| 
      
 136 
     | 
    
         
            +
                  sm.find_exact_cover_recursive.should == nil
         
     | 
| 
      
 137 
     | 
    
         
            +
                end
         
     | 
| 
      
 138 
     | 
    
         
            +
              end
         
     | 
| 
      
 139 
     | 
    
         
            +
             
     | 
| 
      
 140 
     | 
    
         
            +
              context 'given a hexadoku puzzle' do
         
     | 
| 
      
 141 
     | 
    
         
            +
                before do
         
     | 
| 
      
 142 
     | 
    
         
            +
                  # Elektor Hexadoku 2011
         
     | 
| 
      
 143 
     | 
    
         
            +
                  @puzzle = Doku::Hexadoku.new <<END
         
     | 
| 
      
 144 
     | 
    
         
            +
            2A.7|.C..|9D64|8...
         
     | 
| 
      
 145 
     | 
    
         
            +
            ..3.|A..D|7...|.2F.
         
     | 
| 
      
 146 
     | 
    
         
            +
            .1..|..0.|8...|.4AB
         
     | 
| 
      
 147 
     | 
    
         
            +
            ....|.7.2|..BC|.0.3
         
     | 
| 
      
 148 
     | 
    
         
            +
            ----+----+----+----
         
     | 
| 
      
 149 
     | 
    
         
            +
            C2.8|.D3.|..4E|....
         
     | 
| 
      
 150 
     | 
    
         
            +
            ..FA|7.2.|B.3.|1C04
         
     | 
| 
      
 151 
     | 
    
         
            +
            ....|4..F|..1.|...E
         
     | 
| 
      
 152 
     | 
    
         
            +
            9..B|1...|....|23..
         
     | 
| 
      
 153 
     | 
    
         
            +
            ----+----+----+----
         
     | 
| 
      
 154 
     | 
    
         
            +
            ..8C|....|...0|3..D
         
     | 
| 
      
 155 
     | 
    
         
            +
            6...|.F..|1..A|....
         
     | 
| 
      
 156 
     | 
    
         
            +
            D37E|.0.1|.9.8|AF..
         
     | 
| 
      
 157 
     | 
    
         
            +
            ....|3B..|.2D.|C.80
         
     | 
| 
      
 158 
     | 
    
         
            +
            ----+----+----+----
         
     | 
| 
      
 159 
     | 
    
         
            +
            F.B.|51..|2.A.|....
         
     | 
| 
      
 160 
     | 
    
         
            +
            3CA.|...7|.E..|..6.
         
     | 
| 
      
 161 
     | 
    
         
            +
            .E4.|...9|3..5|.D..
         
     | 
| 
      
 162 
     | 
    
         
            +
            ...1|F3A4|..9.|5.E2
         
     | 
| 
      
 163 
     | 
    
         
            +
            END
         
     | 
| 
      
 164 
     | 
    
         
            +
             
     | 
| 
      
 165 
     | 
    
         
            +
                  @solution = @puzzle.solve
         
     | 
| 
      
 166 
     | 
    
         
            +
                end
         
     | 
| 
      
 167 
     | 
    
         
            +
             
     | 
| 
      
 168 
     | 
    
         
            +
                it 'finds the correct solution' do
         
     | 
| 
      
 169 
     | 
    
         
            +
                  @solution.to_grid_string.should == <<END.strip
         
     | 
| 
      
 170 
     | 
    
         
            +
            2A07|BCF3|9D64|8E15
         
     | 
| 
      
 171 
     | 
    
         
            +
            B839|A54D|70E1|62FC
         
     | 
| 
      
 172 
     | 
    
         
            +
            51CD|E906|8F23|74AB
         
     | 
| 
      
 173 
     | 
    
         
            +
            46EF|8712|A5BC|D093
         
     | 
| 
      
 174 
     | 
    
         
            +
            ----+----+----+----
         
     | 
| 
      
 175 
     | 
    
         
            +
            C218|6D3B|074E|9A5F
         
     | 
| 
      
 176 
     | 
    
         
            +
            EDFA|7825|B639|1C04
         
     | 
| 
      
 177 
     | 
    
         
            +
            0753|4A9F|C812|B6DE
         
     | 
| 
      
 178 
     | 
    
         
            +
            946B|1EC0|DA5F|2378
         
     | 
| 
      
 179 
     | 
    
         
            +
            ----+----+----+----
         
     | 
| 
      
 180 
     | 
    
         
            +
            158C|946A|EBF0|372D
         
     | 
| 
      
 181 
     | 
    
         
            +
            6B20|CFD8|137A|E549
         
     | 
| 
      
 182 
     | 
    
         
            +
            D37E|2051|49C8|AFB6
         
     | 
| 
      
 183 
     | 
    
         
            +
            AF94|3B7E|52D6|C180
         
     | 
| 
      
 184 
     | 
    
         
            +
            ----+----+----+----
         
     | 
| 
      
 185 
     | 
    
         
            +
            F9B6|51EC|24AD|0837
         
     | 
| 
      
 186 
     | 
    
         
            +
            3CA5|D287|FE0B|4961
         
     | 
| 
      
 187 
     | 
    
         
            +
            7E42|06B9|3185|FDCA
         
     | 
| 
      
 188 
     | 
    
         
            +
            80D1|F3A4|6C97|5BE2
         
     | 
| 
      
 189 
     | 
    
         
            +
            END
         
     | 
| 
      
 190 
     | 
    
         
            +
                end
         
     | 
| 
      
 191 
     | 
    
         
            +
              end
         
     | 
| 
      
 192 
     | 
    
         
            +
             
     | 
| 
      
 193 
     | 
    
         
            +
              context "given a Hexamurai puzzle" do
         
     | 
| 
      
 194 
     | 
    
         
            +
                before do
         
     | 
| 
      
 195 
     | 
    
         
            +
                  # From Elektor 2011-07 (with extra hints so it can be solved quickly).
         
     | 
| 
      
 196 
     | 
    
         
            +
                  @puzzle = Doku::Hexamurai.new <<END
         
     | 
| 
      
 197 
     | 
    
         
            +
                    |39DEA62B|80C1745F|
         
     | 
| 
      
 198 
     | 
    
         
            +
                    |..213...|5.B4....|
         
     | 
| 
      
 199 
     | 
    
         
            +
                    |..0.....|......2.|
         
     | 
| 
      
 200 
     | 
    
         
            +
                    |.....4.5|..9A1.E.|
         
     | 
| 
      
 201 
     | 
    
         
            +
                    |..3C....|.1654.0.|
         
     | 
| 
      
 202 
     | 
    
         
            +
                    |.1..2...|3......6|
         
     | 
| 
      
 203 
     | 
    
         
            +
                    |...90...|BD4.25..|
         
     | 
| 
      
 204 
     | 
    
         
            +
                    |2.57....|ACE03.D1|
         
     | 
| 
      
 205 
     | 
    
         
            +
            --------+--------+--------+--------
         
     | 
| 
      
 206 
     | 
    
         
            +
            52.0..3.|A.....B.|0.....3.|78A....6
         
     | 
| 
      
 207 
     | 
    
         
            +
            4.8.1...|..9.....|...E..4.|2.063...
         
     | 
| 
      
 208 
     | 
    
         
            +
            .7......|.4F.50.9|.7.2..8E|.1..4...
         
     | 
| 
      
 209 
     | 
    
         
            +
            ..6...0.|.....C3.|...6.7.0|5..3..21
         
     | 
| 
      
 210 
     | 
    
         
            +
            A1D24E6B|9083C.5F|E62BAD14|.....8..
         
     | 
| 
      
 211 
     | 
    
         
            +
            E0347.28|CFA5BD96|14830E72|.9....5A
         
     | 
| 
      
 212 
     | 
    
         
            +
            8B96FC50|ED.2134A|950CB6F8|.2.1.4.3
         
     | 
| 
      
 213 
     | 
    
         
            +
            7.FC3A9D|1B64E2.0|FA7..9C3|0..E.2..
         
     | 
| 
      
 214 
     | 
    
         
            +
            --------+--------+--------+--------
         
     | 
| 
      
 215 
     | 
    
         
            +
            .3.1..7.|B.5F2.E4|31......|B.6....4
         
     | 
| 
      
 216 
     | 
    
         
            +
            C.......|4..7..0D|..6...B.|9.40.178
         
     | 
| 
      
 217 
     | 
    
         
            +
            ..28....|.C.A..73|...49..1|..3.5.0.
         
     | 
| 
      
 218 
     | 
    
         
            +
            047.5...|.6.1....|...045..|1....3..
         
     | 
| 
      
 219 
     | 
    
         
            +
            ..036.4.|.1.C.EA.|60473..9|........
         
     | 
| 
      
 220 
     | 
    
         
            +
            1......2|8.4D.96.|A3EF..2.|4.5.0...
         
     | 
| 
      
 221 
     | 
    
         
            +
            ..4..51.|.7.E3...|B291..5.|....C637
         
     | 
| 
      
 222 
     | 
    
         
            +
            6.5...8.|...9...1|8DC5..0.|........
         
     | 
| 
      
 223 
     | 
    
         
            +
            --------+--------+--------+--------
         
     | 
| 
      
 224 
     | 
    
         
            +
                    |...B..39|..0..2..|
         
     | 
| 
      
 225 
     | 
    
         
            +
                    |5.804..2|.6.3.E7.|
         
     | 
| 
      
 226 
     | 
    
         
            +
                    |...6....|....5.3.|
         
     | 
| 
      
 227 
     | 
    
         
            +
                    |...2.7..|C4.....0|
         
     | 
| 
      
 228 
     | 
    
         
            +
                    |..685340|.729A1..|
         
     | 
| 
      
 229 
     | 
    
         
            +
                    |7..51...|.....6.3|
         
     | 
| 
      
 230 
     | 
    
         
            +
                    |...4....|.B..0...|
         
     | 
| 
      
 231 
     | 
    
         
            +
                    |...3....|0......2|
         
     | 
| 
      
 232 
     | 
    
         
            +
            END
         
     | 
| 
      
 233 
     | 
    
         
            +
                end
         
     | 
| 
      
 234 
     | 
    
         
            +
             
     | 
| 
      
 235 
     | 
    
         
            +
                it "can be solved correctly" do
         
     | 
| 
      
 236 
     | 
    
         
            +
                  solution = @puzzle.solve
         
     | 
| 
      
 237 
     | 
    
         
            +
                  solution.should be_solution_for @puzzle
         
     | 
| 
      
 238 
     | 
    
         
            +
                  solution.to_s.should == <<END.chomp
         
     | 
| 
      
 239 
     | 
    
         
            +
                    |39DEA62B|80C1745F|
         
     | 
| 
      
 240 
     | 
    
         
            +
                    |87213FDC|5EB4906A|
         
     | 
| 
      
 241 
     | 
    
         
            +
                    |450A79E1|63DF8B2C|
         
     | 
| 
      
 242 
     | 
    
         
            +
                    |6CBF8405|729A13ED|
         
     | 
| 
      
 243 
     | 
    
         
            +
                    |BA3CFE7D|21654809|
         
     | 
| 
      
 244 
     | 
    
         
            +
                    |014D25A8|39F7ECB6|
         
     | 
| 
      
 245 
     | 
    
         
            +
                    |F6E901C3|BD4825A7|
         
     | 
| 
      
 246 
     | 
    
         
            +
                    |28579B64|ACE03FD1|
         
     | 
| 
      
 247 
     | 
    
         
            +
            --------+--------+--------+--------
         
     | 
| 
      
 248 
     | 
    
         
            +
            52109D3F|AEC648B7|0F19D235|78A4BEC6
         
     | 
| 
      
 249 
     | 
    
         
            +
            4E8D1BC5|73906AF2|D85EC14B|2F0637A9
         
     | 
| 
      
 250 
     | 
    
         
            +
            37CA82E6|D4FB5019|C7326A8E|D19B40F5
         
     | 
| 
      
 251 
     | 
    
         
            +
            B96FA704|5218DC3E|4BA6F790|5CE38D21
         
     | 
| 
      
 252 
     | 
    
         
            +
            A1D24E6B|9083C75F|E62BAD14|C3F57890
         
     | 
| 
      
 253 
     | 
    
         
            +
            E0347128|CFA5BD96|14830E72|69BDFC5A
         
     | 
| 
      
 254 
     | 
    
         
            +
            8B96FC50|ED72134A|950CB6F8|A271E4D3
         
     | 
| 
      
 255 
     | 
    
         
            +
            75FC3A9D|1B64E280|FA7D59C3|048E126B
         
     | 
| 
      
 256 
     | 
    
         
            +
            --------+--------+--------+--------
         
     | 
| 
      
 257 
     | 
    
         
            +
            D3A1087C|B95F26E4|31D870AC|B5629FE4
         
     | 
| 
      
 258 
     | 
    
         
            +
            CFB526A3|48E7910D|5C6A23BF|9E40D178
         
     | 
| 
      
 259 
     | 
    
         
            +
            9628E4B1|0CDAF573|2EB49861|FD375A0C
         
     | 
| 
      
 260 
     | 
    
         
            +
            047E5FD9|3621ABC8|79F045ED|1AC863B2
         
     | 
| 
      
 261 
     | 
    
         
            +
            FD036947|21BC8EA5|60473FD9|8B2CA51E
         
     | 
| 
      
 262 
     | 
    
         
            +
            1AE7C3F2|854D096B|A3EF1C27|46590B8D
         
     | 
| 
      
 263 
     | 
    
         
            +
            2849B51A|670E3FDC|B291845A|E0DFC637
         
     | 
| 
      
 264 
     | 
    
         
            +
            6C5BD08E|FA397421|8DC5EB06|371A294F
         
     | 
| 
      
 265 
     | 
    
         
            +
            --------+--------+--------+--------
         
     | 
| 
      
 266 
     | 
    
         
            +
                    |AF7BC839|E50D6214|
         
     | 
| 
      
 267 
     | 
    
         
            +
                    |5D804AF2|9613CE7B|
         
     | 
| 
      
 268 
     | 
    
         
            +
                    |94C6B01E|FA725D38|
         
     | 
| 
      
 269 
     | 
    
         
            +
                    |E312D756|C48BFA90|
         
     | 
| 
      
 270 
     | 
    
         
            +
                    |CB685340|D729A1FE|
         
     | 
| 
      
 271 
     | 
    
         
            +
                    |709512BF|48AED6C3|
         
     | 
| 
      
 272 
     | 
    
         
            +
                    |D2F4EC9A|1B360785|
         
     | 
| 
      
 273 
     | 
    
         
            +
                    |1EA36D87|0F5CB942|
         
     | 
| 
      
 274 
     | 
    
         
            +
            END
         
     | 
| 
      
 275 
     | 
    
         
            +
                end
         
     | 
| 
      
 276 
     | 
    
         
            +
              end
         
     | 
| 
      
 277 
     | 
    
         
            +
            end
         
     | 
| 
      
 278 
     | 
    
         
            +
             
     | 
    
        data/spec/spec_helper.rb
    ADDED
    
    | 
         @@ -0,0 +1,19 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            $LOAD_PATH.unshift File.join File.dirname(__FILE__), '..', 'lib'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'rspec'
         
     | 
| 
      
 3 
     | 
    
         
            +
            require 'doku'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            # Change this to true if a test is failing and you want more clues about why.
         
     | 
| 
      
 6 
     | 
    
         
            +
            EXTRA_ASSERTS = false
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            if EXTRA_ASSERTS
         
     | 
| 
      
 9 
     | 
    
         
            +
              # Add any patches here that help check the validity of the algorithm but are
         
     | 
| 
      
 10 
     | 
    
         
            +
              # not necessary to have in the production code.
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
              class Doku::DancingLinks::LinkMatrix::Column
         
     | 
| 
      
 13 
     | 
    
         
            +
                def size=(num)
         
     | 
| 
      
 14 
     | 
    
         
            +
                  raise "bad class" unless Fixnum === num
         
     | 
| 
      
 15 
     | 
    
         
            +
                  raise "negative value #{num}" if num < 0
         
     | 
| 
      
 16 
     | 
    
         
            +
                  @size = num
         
     | 
| 
      
 17 
     | 
    
         
            +
                end
         
     | 
| 
      
 18 
     | 
    
         
            +
              end
         
     | 
| 
      
 19 
     | 
    
         
            +
            end
         
     | 
    
        data/spec/sudoku_spec.rb
    ADDED
    
    | 
         @@ -0,0 +1,52 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'backports' unless defined? require_relative
         
     | 
| 
      
 2 
     | 
    
         
            +
            require_relative 'spec_helper'
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            describe Doku::Sudoku do
         
     | 
| 
      
 5 
     | 
    
         
            +
              before do
         
     | 
| 
      
 6 
     | 
    
         
            +
                @puzzle = Doku::Sudoku.new
         
     | 
| 
      
 7 
     | 
    
         
            +
              end
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
              it 'has 81 squares' do
         
     | 
| 
      
 10 
     | 
    
         
            +
                Doku::Sudoku.squares.size.should == 81
         
     | 
| 
      
 11 
     | 
    
         
            +
              end
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
              it 'has 9 squares in the first row' do
         
     | 
| 
      
 14 
     | 
    
         
            +
                first_row = Doku::Sudoku.squares.select { |s| s.matches?(:y => 0) }
         
     | 
| 
      
 15 
     | 
    
         
            +
                first_row.size.should == 9
         
     | 
| 
      
 16 
     | 
    
         
            +
              end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
              it 'has the right number of groups' do
         
     | 
| 
      
 19 
     | 
    
         
            +
                Doku::Sudoku.groups.size.should == 3*9
         
     | 
| 
      
 20 
     | 
    
         
            +
              end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
              it 'has methods for getting and setting glyphs by coordinates' do
         
     | 
| 
      
 23 
     | 
    
         
            +
                # (just like any puzzle class that includes the PuzzleOnGrid module)
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                @puzzle.get(0, 1).should == nil
         
     | 
| 
      
 26 
     | 
    
         
            +
                @puzzle.set(0, 1, 4)
         
     | 
| 
      
 27 
     | 
    
         
            +
                @puzzle.set(5, 7, 9)
         
     | 
| 
      
 28 
     | 
    
         
            +
                @puzzle.set(6, 6, 1)
         
     | 
| 
      
 29 
     | 
    
         
            +
                @puzzle.set(6, 6, nil)
         
     | 
| 
      
 30 
     | 
    
         
            +
                @puzzle.get(0, 1).should == 4
         
     | 
| 
      
 31 
     | 
    
         
            +
                @puzzle.to_s.should == <<END.strip
         
     | 
| 
      
 32 
     | 
    
         
            +
            ...|...|...
         
     | 
| 
      
 33 
     | 
    
         
            +
            4..|...|...
         
     | 
| 
      
 34 
     | 
    
         
            +
            ...|...|...
         
     | 
| 
      
 35 
     | 
    
         
            +
            ---+---+---
         
     | 
| 
      
 36 
     | 
    
         
            +
            ...|...|...
         
     | 
| 
      
 37 
     | 
    
         
            +
            ...|...|...
         
     | 
| 
      
 38 
     | 
    
         
            +
            ...|...|...
         
     | 
| 
      
 39 
     | 
    
         
            +
            ---+---+---
         
     | 
| 
      
 40 
     | 
    
         
            +
            ...|...|...
         
     | 
| 
      
 41 
     | 
    
         
            +
            ...|..9|...
         
     | 
| 
      
 42 
     | 
    
         
            +
            ...|...|...
         
     | 
| 
      
 43 
     | 
    
         
            +
            END
         
     | 
| 
      
 44 
     | 
    
         
            +
              end
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
              it "the get method has a good error message" do
         
     | 
| 
      
 47 
     | 
    
         
            +
                msg = "Square not found in Doku::Sudoku: Square(19, david)."
         
     | 
| 
      
 48 
     | 
    
         
            +
                lambda { @puzzle.get(19, 'david') }.should raise_error IndexError, msg
         
     | 
| 
      
 49 
     | 
    
         
            +
                lambda { @puzzle.set(19, 'david', 10) }.should raise_error IndexError, msg
         
     | 
| 
      
 50 
     | 
    
         
            +
              end
         
     | 
| 
      
 51 
     | 
    
         
            +
            end
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     |