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
52 changes: 52 additions & 0 deletions lib/sudoku_board.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
require 'matrix'

class SudokuBoard
attr_reader :board, :rows
def initialize(file)
@rows = []

File.readlines(file).each do |line|
row = get_row_from line
@rows << row unless row.empty?
end
@board = Matrix.rows(@rows)
end

def point(row, col)
board.[](row - 1, col - 1)
end

def row(n)
board.row(n-1).to_a
end

def column(n)
board.column(n-1).to_a
end

def subgrid(starting_row, starting_col)
board.minor(starting_row - 1, 3, starting_col - 1, 3).to_a.flatten!
end

def columns
columns = (1..9).collect do |n|
column(n)
end
end

def subgrids
subgrids = []
[1,4,7].each do |n|
[1,4,7].each do |x|

Choose a reason for hiding this comment

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

You can avoid the nested loops by using repeated_permutation, which will give you exactly the same combinations of n and x as nesting each.

[1, 4, 7].repeated_permutation(2).each do |n, x|
  subgrids << subgrid(n, x)
end

If you combine that with map you can also remove both the first and the last lines of the method.

[1, 4, 7].repeated_permutation(2).map do |n, x|
  subgrid(n, x)
end

subgrids << subgrid(n, x)
end
end
subgrids
end

private
def get_row_from(line)
line.chomp!.delete!('-+| ').tr!('.', '0')
row = line.each_char.collect(&:to_i)
end
end
58 changes: 58 additions & 0 deletions lib/sudoku_validator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
require_relative './sudoku_board'

class SudokuValidator
attr_reader :board

def initialize(board)
@board = board
end

def validate
if valid_rows_columns_grids && contains_empty_points
'valid but incomplete sudoku'
elsif valid_rows_columns_grids && !contains_empty_points
'valid and complete sudoku'
elsif !valid_rows_columns_grids && contains_empty_points
'incomplete and invalid sudoku'
elsif !valid_rows_columns_grids && !contains_empty_points
'complete but invalid sudoku'
end
end

private
def valid_rows_columns_grids
check_rows && check_columns && check_subgrids
end

def check_rows
board.rows.none? do |row|
check_for_duplicates(row)
end
end

def check_columns
board.columns.none? do |column|
check_for_duplicates(column)
end
end

def check_subgrids
board.subgrids.none? do |subgrid|
check_for_duplicates(subgrid)
end
end

def check_for_duplicates(elements)

Choose a reason for hiding this comment

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

I like that you've made this method generic since the logic is the same for rows, columns, and subgrids. I think you could push a bit further in removing the duplication though, since the check_rows, check_columns, and check_subgrids methods are all essentially the same. Perhaps valid_rows_columns_grids could be changed to something like the below:

def valid_rows_columns_grids
  [board.rows, board.columns, board.subgrids].none? { |set|
    set.none? { |elements| check_for_duplicates(elements) }
  }
end

The inner none? block could probably be extracted to a small private method as well afterwards.

elements.any? { |n| elements.count(n) > 1 unless n == 0 }
end

def contains_empty_points
board.rows.any? do |row|
any_zeros?(row)
end
end

def any_zeros?(element)
element.count(0) >= 1
end
end
60 changes: 60 additions & 0 deletions spec/sudoku_board_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
require_relative '../lib/sudoku_board'

describe 'SudokuBoard' do
before do
@incomplete_board = SudokuBoard.new('valid_incomplete.sudoku')
@complete_board = SudokuBoard.new('valid_complete.sudoku')
end

describe '#row' do
it 'returns the row when passed an index' do
expect(@complete_board.row(2)).to eq [7,2,3,8,5,4,1,6,9]
end

it 'reurns the row with zeros if the board is incomplete' do
expect(@incomplete_board.row(3)).to eq [0,0,4,0,0,0,0,0,0]
end
end

describe '#column' do
it 'returns the expected column for the valid and complete file' do
expect(@complete_board.column(1)).to eq [8,7,1,9,3,2,4,6,5]
end

it 'returns the expected column for the valid and incomplete file' do
expect(@incomplete_board.column(4)).to eq [0,0,0,1,0,0,0,0,0]
end
end

describe '#subgrid' do
it 'returns a given subgrid' do
expect(@complete_board.subgrid(4, 4)).to eq [1,4,7,2,6,8,5,9,3]
end

it 'returns a given subgrid for valid incomplete sudoku' do
expect(@incomplete_board.subgrid(7, 7)).to eq [0,7,0,0,0,0,0,4,0]
end
end

describe '#point' do
it 'returns the value at a given point' do
expect(@complete_board.point(3,5)).to eq 7
end
end

describe '#columns' do
it 'returns all the columns from the board' do
expect(@complete_board.columns).to eq [[8,7,1,9,3,2,4,6,5],[5,2,6,8,7,4,3,1,9],
[9,3,4,6,5,1,2,7,8],[6,8,3,1,2,5,9,4,7],[1,5,7,4,6,9,8,2,3],[2,4,9,7,8,3,1,5,6],
[4,1,5,3,9,7,6,8,2], [3,6,2,5,1,8,7,9,4], [7,9,8,2,4,6,5,3,1]]
end
end

describe '#subgrids' do
it 'returns all the subgrids frim the board' do
expect(@incomplete_board.subgrids).to eq [[8,5,0,7,2,0,0,0,4],
[0,0,2,0,0,0,0,0,0], [4,0,0,0,0,9,0,0,0], [0,0,0,3,0,5,0,4,0],[1,0,7,0,0,0,0,0,0],
[0,0,2,9,0,0,0,0,0], [0,0,0,0,1,7,0,0,0],[0,8,0,0,0,0,0,3,6], [0,7,0,0,0,0,0,4,0]]
end
end
end
30 changes: 30 additions & 0 deletions spec/sudoku_validator_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
require_relative '../lib/sudoku_validator'
require_relative '../lib/sudoku_board'

describe 'SudokuValidator' do

describe 'valid game' do
it 'points out valid sudoku when completed' do
board = SudokuBoard.new('valid_complete.sudoku')
expect(SudokuValidator.new(board).validate).to eq 'valid and complete sudoku'
end

it 'points out valid sudoku when incomplete' do
board = SudokuBoard.new('valid_incomplete.sudoku')
expect(SudokuValidator.new(board).validate).to eq 'valid but incomplete sudoku'
end
end

describe 'invalid game' do
it 'points out invalid but complete sudoku' do
board = SudokuBoard.new('invalid_complete.sudoku')
expect(SudokuValidator.new(board).validate).to eq 'complete but invalid sudoku'
end

it 'points out invalid and incomplete sudoku' do
board = SudokuBoard.new('invalid_incomplete.sudoku')
expect(SudokuValidator.new(board).validate).to eq 'incomplete and invalid sudoku'
end
end

end