Search code examples
rubyrspecsudokuenumerator

Rspec testing a method using enumerator in Ruby. Method only runs once the fails subsequently


I am currently completing a Sudoku solver in ruby. Using enumerator I have create a method that creates 9 arrays based on my 9x9 grid each of which refers to a 3x3 box in the sudoku game. Whilst testing this in Rspec I am finding that it only works in one test. When i create a second test using the same method it will always fail. When I muddle around they tests they work individually but not when the method is being called in a second test. I ideally want to add my method into my initialize method but this fails all tests apart from one. The error I get is 'StopIteration: iteration reached an end'. WHich I understand but why cant it just restart at each test? Any ideas?

class Grid

BoxOfIndex = [
  0,0,0,1,1,1,2,2,2,0,0,0,1,1,1,2,2,2,0,0,0,1,1,1,2,2,2,
  3,3,3,4,4,4,5,5,5,3,3,3,4,4,4,5,5,5,3,3,3,4,4,4,5,5,5,
  6,6,6,7,7,7,8,8,8,6,6,6,7,7,7,8,8,8,6,6,6,7,7,7,8,8,8
].each

attr_accessor :cells, :rows, :columns, :boxes

def initialize(puzzle)
    @cells = puzzle.split('').map {|v| Cell.new(v) }
    create_boxes
end

def create_rows
    @rows = cells.each_slice(9).to_a
end

def create_columns
    @columns = create_rows.transpose
end

def create_boxes
    @boxes = []
9.times { @boxes << Array.new}
@cells.each{|cell| @boxes[BoxOfIndex.next].concat([cell])}
end

....................Tests below(second test fails)

it "should be able to create boxes with a cell value" do
        grid.create_boxes
        expect(grid.boxes[0][2].value).to eq(5)
    end


    it "should be able to find neighbours of a cell" do
        grid.create_boxes
    end

Solution

  • I think the problem is that your BoxOfIndex constant holds an iterator. In your create_boxes method you iterate to the last element. Later specs cannot call next again, because you already reached the end.

    Change the constant to just hold the array:

    BOX_OF_INDEX = [
      0,0,0,1,1,1,2,2,2,0,0,0,1,1,1,2,2,2,0,0,0,1,1,1,2,2,2,
      3,3,3,4,4,4,5,5,5,3,3,3,4,4,4,5,5,5,3,3,3,4,4,4,5,5,5,
      6,6,6,7,7,7,8,8,8,6,6,6,7,7,7,8,8,8,6,6,6,7,7,7,8,8,8
    ]
    

    And change the create_boxes method to use a new iterator each time:

    def create_boxes
      @boxes = []
      iterator = BOX_OF_INDEX.each
      9.times { @boxes << Array.new }
      @cells.each { |cell| @boxes[iterator.next].concat([cell]) }
    end