Skip to content
This repository was archived by the owner on Jun 8, 2019. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .rspec
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
--color
--format progress
3 changes: 3 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
source 'https://rubygems.org'

gem 'rspec'
18 changes: 18 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
GEM
remote: https://rubygems.org/
specs:
diff-lcs (1.2.4)
rspec (2.14.1)
rspec-core (~> 2.14.0)
rspec-expectations (~> 2.14.0)
rspec-mocks (~> 2.14.0)
rspec-core (2.14.5)
rspec-expectations (2.14.3)
diff-lcs (>= 1.1.3, < 2.0)
rspec-mocks (2.14.3)

PLATFORMS
ruby

DEPENDENCIES
rspec
77 changes: 77 additions & 0 deletions lib/sudoku_validator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
require 'Matrix'

class SudokuValidator
def initialize(file=nil)
return if file.nil?

puzzle = []
open(file).each do |line|
row = line.gsub(/[|\-+]/, "")
unless row.strip.empty?
puzzle.push(row.split(' ').map{|x| x == '.' ? nil : x.to_i})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

String#split can accept a regex.

line.gsub(/[|\-+]/, "").strip.split(' ')

# is equivalent to

line.split(/[^\d\.]+/)

end
end

@puzzle = Matrix.rows(puzzle)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 for using Matrix

end

def valid?
return false if @puzzle.nil?
return validate(@puzzle)
end

def complete?
@puzzle.row_vectors.each do |row|
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could say:

def complete?
  @puzzle.row_vectors.all?(&:all?)
end

all? is a method from Enumerable which returns true if the given block returns true for all items in the collection. If called without arguments, all? returns true if all items are not nil

The line above checks that all the vectors have all non-nil values.

return false if row.include? nil
end
true
end

def validate(puzzle)
valid_puzzle?(puzzle) && valid_rows?(puzzle) && valid_columns?(puzzle) && valid_submatricies?(puzzle)
end

def valid_puzzle?(puzzle)
puzzle.square? && (puzzle.row_count % 3) == 0
end

def valid_rows?(puzzle)
puzzle.row_vectors.each do |row|
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You probably want to use all? here.

def valid_rows(puzzle)
  puzzle.row_vectors.all? do |row|
    grouping_valid?(row)
  end
end

This checks that all rows have a valid grouping.

return false unless grouping_valid?(row)
end

true
end

def valid_columns?(puzzle)
puzzle.column_vectors.each do |column|
return false unless grouping_valid?(column)
end

true
end

def valid_submatricies?(puzzle)
(puzzle.row_count / 3).times do |row|
(puzzle.column_count / 3).times do |column|
subpuzzle = puzzle.minor(3*row..(3*row)+2, 3*column..(3*column)+2)
flattened = []
subpuzzle.row_vectors.each do |subrow|
flattened += subrow.to_a
end
return false unless grouping_valid?(flattened)
end
end

true
end

def grouping_valid?(set)
values = Hash.new(0)
set.each do |value|
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are looking to see there are any duplicates in a set I think the simplest way to do this would be:

def grouping_valid?(set)
  set.to_a.length == set.to_a.uniq.length
end

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! I really like this way of doing it -- very clean.

values[value] += 1 unless value == nil
end

values.find{|k, v| v > 1 } == nil
end
end
17 changes: 17 additions & 0 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# This file was generated by the `rspec --init` command. Conventionally, all
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
# Require this file using `require "spec_helper"` to ensure that it is only
# loaded once.
#
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
RSpec.configure do |config|
config.treat_symbols_as_metadata_keys_with_true_values = true
config.run_all_when_everything_filtered = true
config.filter_run :focus

# Run specs in random order to surface order dependencies. If you find an
# order dependency and want to debug it, you can fix the order by providing
# the seed, which is printed after each run.
# --seed 1234
config.order = 'random'
end
49 changes: 49 additions & 0 deletions spec/sudoku_validator_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
require 'sudoku_validator'

describe SudokuValidator do
before (:each) do
@validator = SudokuValidator.new
end

it "has no duplicates in a row" do
validPuzzle = Matrix[[1,2,3],[nil,nil,nil],[nil,nil,nil]]
@validator.validate(validPuzzle).should be_true

invalidPuzzle = Matrix[[1,1,2],[nil,nil,nil],[nil,nil,nil]]
@validator.validate(invalidPuzzle).should be_false
end

it "has no duplicates in a column" do
validPuzzle = Matrix[[1,nil,nil],[2,nil,nil],[3,nil,nil]]
@validator.validate(validPuzzle).should be_true

invalidPuzzle = Matrix[[1,nil,nil], [1,nil,nil], [2,nil,nil]]
@validator.validate(invalidPuzzle).should be_false
end

it "has no duplicates in a sub-grid" do
validPuzzle = Matrix[[1,2,3],[4,5,6],[7,8,9]]
@validator.validate(validPuzzle).should be_true

invalidPuzzle = Matrix[[1,2,3],[4,5,6],[7,8,1]]
@validator.validate(invalidPuzzle).should be_false
end

it "validates files" do
vc = SudokuValidator.new("valid_complete.sudoku")
vc.valid?.should be_true
vc.complete?.should be_true

vi = SudokuValidator.new("valid_incomplete.sudoku")
vi.valid?.should be_true
vi.complete?.should be_false

ic = SudokuValidator.new("invalid_complete.sudoku")
ic.valid?.should be_false
ic.complete?.should be_true

ii = SudokuValidator.new("invalid_incomplete.sudoku")
ii.valid?.should be_false
ii.complete?.should be_false
end
end
5 changes: 5 additions & 0 deletions sudoku-validator
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/usr/bin/env ruby
require_relative 'lib/sudoku_validator'

validator = SudokuValidator.new(ARGV[0])
puts "This sudoku is #{!validator.valid? ? "in" : ""}valid#{validator.valid? && !validator.complete? ? ", but incomplete" : ""}."