Ruby Challenges - Sudoku Validator

This topic is for the Sudoku Validator exercise in the Ruby Challenges trail. Post any questions, corrections, or pointers you have to share with other Upcase subscribers.

Continuing the discussion from New trail: Ruby Challenges:

Hi Philip!

I think you might be misreading the expectation.

It looks like this:

 expect(result).to(
          eq("This sudoku is invalid."),
          "Expected #{fixture} to be invalid but it wasn't."
        )

The second argument to to is a string that provides a custom failure message when the expectation is not satisfied.

So, this code says ā€œresult should be equal to ā€˜This sudoku is invalid.ā€™, but if itā€™s not, output the string 'Expected #{fixture_name} to be invalid but it wasnt.ā€.

In other words, the Validator does not know, and should not care about the name of the file.

Does that make sense?

More info on custom error messages: https://www.relishapp.com/rspec/rspec-expectations/v/3-2/docs/customized-message

You are, of course, quite correct. Thank you for the help. :slight_smile:

1 Like

I know several ways to ā€œwrite a command line programā€ but none that could be constructed to fit this sample data:

$ sudoku-validator ./valid_complete.sudoku
  This sudoku is valid.

$ sudoku-validator ./valid_incomplete.sudoku
  This sudoku is valid, but incomplete.

$ sudoku-validator ./invalid_complete.sudoku
  This sudoku is invalid.

$ sudoku-validator ./invalid_incomplete.sudoku
  This sudoku is invalid.

This is what iā€™m seeing. Iā€™m in a directory we cloned called sudoku-validator:

git clone git@git.upcase.com:Midnightcoffee/sudoku-validator.git
cd sudoku-validator
bin/setup

Now from the output i should be able to run:

   $ sudoku-validator ./valid_complete.sudoku
      This sudoku is valid.

and see if the sudoku is valid or not. But sudoku-validator is the name of my directoryā€¦ whats more ā€œ./ā€ indicates an executable, which is odd sense valid_complete.sudoku is a .sudoku which from the directory contains the data about the sudokuā€¦

āžœ  sudoku-validator git:(master) āœ— cat spec/fixtures/invalid_due_to_column_dupe.sudoku 
8 5 9 |6 1 2 |4 3 7
7 2 3 |8 5 4 |1 6 9
1 6 4 |3 7 9 |5 2 8
------+------+------
9 8 6 |1 4 7 |3 5 2
3 7 5 |8 6 2 |9 1 4
2 4 1 |5 9 3 |7 8 6
------+------+------
4 3 2 |9 8 1 |6 7 5
6 1 7 |4 2 5 |8 9 3
5 9 8 |7 3 6 |2 4 1

So there are several things that seem off to me. I get the problem, but not what the excecution is supposed to be, not at least from the descriptionā€¦

Sorry about that! Thatā€™s a typo in the instructions.

They should look like this:

$ ./sudoku-validator valid_complete.sudoku

Iā€™ve updated them.

Thanks ben (is it ben?),

so inside the sudoku-validator folder i should create an executable sudoku-validator? Forgive me, iā€™m still not a full fledged rubyiest yet. In python i would expect something like this:

./main.py some_data_main_acts_open.txt

with main.py containing something like

if __name__ == __main__:
  file_name = args[1]
  do_something_to_the_file(file_name)

what type do you expect sudoku-validator to be? does it not matter?

This file appears to be valid spec/fixtures/invalid_due_to_column_dupe.sudoku?

8 5 9 |6 1 2 |4 3 7
7 2 3 |8 5 4 |1 6 9
1 6 4 |3 7 9 |5 2 8
------Ā±-----Ā±-----
9 8 6 |1 4 7 |3 5 2
3 7 5 |8 6 2 |9 1 4
2 4 1 |5 9 3 |7 8 6
------Ā±-----Ā±-----
4 3 2 |9 8 1 |6 7 5
6 1 7 |4 2 5 |8 9 3
5 9 8 |7 3 6 |2 4 1

Am I missing something?

Thanks

Yep, the fourth column has two 8s in it. :smile:

Woopsā€¦ (apparently your validator doesnā€™t like woopsā€¦)

Hi all! Iā€™m curious about the setup for this one. In particular, the approach to a class with one class method on it. The exercise started with the Validator class with a class method like interface that instantiate a new object of that class.

class Validator
  def initialize(puzzle_string)
    @puzzle_string = puzzle_string
  end

  def self.validate(puzzle_string)
    new(puzzle_string).validate
  end

  def validate
    #code goes here 
  end
end

Is this pattern the common approach to the case where you need a single method interface? Iā€™m familiar with the replace method with method object pattern, but why create an object? Why would you prefer this over something likeā€¦

class Validator
  def self.validate(puzzle_string)
    #code goes here
  end
end

I had a lot of fun with the exercise. Looking forward to chatting more about the approach!

Edit:

Interestingly, the exercise that follows this one, Ranking Poker Hands, uses the latter approach.

class HandEvaluator
  def return_stronger_hand(left, right)
  end
end

Hi, @timlombardo

I think you mean ā€˜class methodā€™ rather than ā€˜singleton methodā€™? (See RubyMonk - Ruby Primer: Ascent - Singleton methods and metaclasses)

Regarding creating a new instance, the reason I usually do that is so that the object can have state, which makes it easier to simplify and refactor by extracting methods.

This post from Code Climate explains it well: Why Ruby Class Methods Resist Refactoring | Code Climate

Hey @andyw8 thanks for the response.

Yep, youā€™re correct! I meant class method, which I edited to prevent any more confusion later on.

Thanks for the insight and sharing the post. Sometimes with exercises the scope is narrow and fixed so I feel a bit of the YAGNI objection creeps in.

Iā€™m really liking the object instance with convenience method pattern in this case.

@timlombardo you may also enjoy Meditations on a Class Method which talks about this exact pattern (class methods as syntactic sugar for creating an instance and calling a method on it)

Hi all! I have a design question regarding this exercise. Specifically, I would love to hear peopleā€™s thoughts on if creating a SudokuPuzzle class, should an instance of that class have the ability to report on whether it is valid? and complete?

What Iā€™m trying to get at is whether it is appropriate for a puzzle instance to be able to answer the question of whether it is valid or not, or complete or not. Or should that logic be better in a class that is specifically just for validating a puzzle?

If this is not clear, please let me know I can try to expand upon it.

Thank you!
Jonathan

I would say it depends on what other behaviour youā€™re planning to have in that SudukoPuzzle class. Following the Single Responsibility Principle should help here.

I think there is a bug in the way the tests are set up, Iā€™m getting

     Failure/Error: result = Validator.validate(file)
     Errno::ENOENT:
       No such file or directory @ rb_sysopen - 8 5 9 |6 1 2 |4 3 7
       7 2 3 |8 5 4 |1 6 9
       1 6 4 |3 7 9 |5 2 8
       ------+------+------
       9 8 6 |1 4 7 |3 5 2
       3 7 5 |2 6 8 |9 1 4
       2 4 1 |5 9 3 |7 8 6
       ------+------+------
       4 3 2 |9 8 1 |6 7 5
       6 1 7 |4 2 5 |8 9 3
       5 9 8 |7 3 6 |2 4 1
     # ./lib/validator.rb:3:in `initialize'
     # ./lib/validator.rb:3:in `open'
     # ./lib/validator.rb:3:in `initialize'
     # ./lib/validator.rb:8:in `new'
     # ./lib/validator.rb:8:in `validate'
     # ./spec/end_to_end_integration_spec.rb:12:in `block (4 levels) in <top (required)>'

@askl56 I donā€™t think thereā€™s a bug in the spec, but the naming of the variables is confusing:

file = File.read("spec/fixtures/valid_complete.sudoku")
result = Validator.validate(file)

File.read returns a string rather than a File instance, so file should really be called something like file_contents.

From the error output, it looks like the spec is passing a sudoku string to your implementation, but the implementation is expecting a file path.

I had a similar confusion, but managed to get the implementation requested in the challenge working properly using:

  • a shebang in my ./sudoku-validator file to instruct the terminal to execute the contents of the file in ruby (i.e. #!/usr/bin/env ruby at the top of the file)
  • rubyā€™s ARGV array in the ./sudoku-validator file. This is an array of any command line arguments given to a programme on execution. The string I pass to Validator.validate is therefore file = File.read(ARGV[0])
  • puts to get the string my validate method returns onto the command line
    I was also careful to require the correct file(s) in the ./sudoku-validator file, and to use a valid path to a sudoku file when executing the programme (e.g. ./spec/fixtures/valid_complete.sudoku)
    Hope this helps!