From a8091fb6ad5ad42e0dbdd109d710691db74dcb93 Mon Sep 17 00:00:00 2001 From: Eric Lubin Date: Sun, 20 Oct 2013 21:06:58 -0400 Subject: [PATCH 1/3] initial contest entry --- .gitignore | 1 + Gemfile | 7 ++ Gemfile.lock | 14 +++ SudokuValidator.rb | 7 ++ lib/board_reader.rb | 18 ++++ lib/board_validator.rb | 68 ++++++++++++++ sudoku_validator_spec.rb | 185 +++++++++++++++++++++++++++++++++++++++ 7 files changed, 300 insertions(+) create mode 100644 .gitignore create mode 100644 Gemfile create mode 100644 Gemfile.lock create mode 100644 SudokuValidator.rb create mode 100644 lib/board_reader.rb create mode 100644 lib/board_validator.rb create mode 100644 sudoku_validator_spec.rb diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..485dee6 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.idea diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..e0230a6 --- /dev/null +++ b/Gemfile @@ -0,0 +1,7 @@ +source 'https://rubygems.org' + +gem 'nokogiri', '~> 1.6.0' + +group :development, :test do + gem "minitest", "~> 5.0.7" +end diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..7881bcb --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,14 @@ +GEM + remote: https://rubygems.org/ + specs: + mini_portile (0.5.1) + minitest (5.0.8) + nokogiri (1.6.0) + mini_portile (~> 0.5.0) + +PLATFORMS + ruby + +DEPENDENCIES + minitest (~> 5.0.7) + nokogiri (~> 1.6.0) diff --git a/SudokuValidator.rb b/SudokuValidator.rb new file mode 100644 index 0000000..5feb552 --- /dev/null +++ b/SudokuValidator.rb @@ -0,0 +1,7 @@ +require_relative 'lib/board_reader' +require_relative 'lib/board_validator' + +raise 'Missing input file - SudokuValidator ' if ARGV.length != 1 +board = Sudoku::BoardValidator.new(Sudoku::BoardReader.new(ARGV[0]).read) +puts board.valid_board? + diff --git a/lib/board_reader.rb b/lib/board_reader.rb new file mode 100644 index 0000000..a76ab03 --- /dev/null +++ b/lib/board_reader.rb @@ -0,0 +1,18 @@ +module Sudoku + class BoardReader + + def initialize(file) + @file = file + end + + def read + board = [] + open(@file).each do |line| + board << line.chomp + # could get more involved checking specific lines here + raise 'Invalid board string' unless line =~ /[1-9].+-|/ + end + board + end + end +end \ No newline at end of file diff --git a/lib/board_validator.rb b/lib/board_validator.rb new file mode 100644 index 0000000..b06899e --- /dev/null +++ b/lib/board_validator.rb @@ -0,0 +1,68 @@ +module Sudoku + class BoardValidator + + def initialize( board_string ) + raise 'Invalid number of rows in board' if board_string.count != 11 + + row_index = 0 # can't use _with_index because of line rows + board_string.each do |one_row| + one_row_number_array = one_row.gsub('|','').gsub('+','').gsub('-','').split + if one_row_number_array.count == 9 + init_one_row( row_index, one_row_number_array ) + row_index += 1 + else + raise 'Invalid Board' if one_row_number_array.count != 0 + end + end + end + + def valid_board? + @rows.each {|row| return false if !array_unique?(row)} + @cols.each {|col| return false if !array_unique?(col)} + @grids.each {|grid| return false if !array_unique?(grid)} + true + end + + + + + + private + + def init_one_row( row_id, row_string ) + # store the row as a row + rows[row_id].concat(row_string) + # store each element of the array into the cols arrays + row_string.each_with_index {|x,i| cols[i] << x} + # pull off 3 at a top and store in the grids array + in_groups_of(row_string,3).each_with_index do |triplet,i| + grids[((row_id/3)*3)+i].concat(triplet) + end + end + + def array_unique?(arr) + # clone the tested array because delete is destructive + _arr = arr.clone + _arr.delete('.') + _arr.uniq.count == _arr.count + end + + def rows + @rows ||= [[],[],[],[],[],[],[],[],[]] + end + + def cols + @cols ||= [[],[],[],[],[],[],[],[],[]] + end + + def grids + @grids ||= [[],[],[],[],[],[],[],[],[]] + end + + def in_groups_of(array,number) + groups = [] + array.each_slice(number) {|group| groups << group} + groups + end + end +end \ No newline at end of file diff --git a/sudoku_validator_spec.rb b/sudoku_validator_spec.rb new file mode 100644 index 0000000..85a6b48 --- /dev/null +++ b/sudoku_validator_spec.rb @@ -0,0 +1,185 @@ +require 'minitest' +require 'minitest/autorun' +require_relative 'lib/board_reader' +require_relative 'lib/board_validator' + + +require 'minitest/autorun' + +module MiniTest::Assertions + def assert_contains(string, substring) + assert string.include?(substring), "Expected #{string} to contain #{substring}" + end +end +String.infect_an_assertion :assert_contains, :must_contain, :only_one_argument + + + +class TestMacbethAnalyzer < MiniTest::Test + + describe '#initialize' do + def setup + @test_board = [ + "1 1 1 |1 1 1 |1 1 1", + "2 2 2 |2 2 2 |2 2 2", + "3 3 3 |3 3 3 |3 3 3", + "------+------+------", + "4 4 4 |4 4 4 |4 4 4", + "5 5 5 |5 5 5 |5 5 5", + "6 6 6 |6 6 6 |6 6 6", + "------+------+------", + "7 7 7 |7 7 7 |7 7 7", + "8 8 8 |8 8 8 |8 8 8", + "9 9 9 |9 9 9 |9 9 9" + ] + @exception_board = [ + " |1 1 1 |1 1 1", + "2 2 2 |2 2 2 |2 2 2", + "3 3 3 |3 3 3 |3 3 3", + "------+------+------", + "4 4 4 |4 4 4 |4 4 4", + "5 5 5 |5 5 5 |5 5 5", + "6 6 6 |6 6 6 |6 6 6", + "------+------+------", + "7 7 7 |7 7 7 |7 7 7", + "8 8 8 |8 8 8 |8 8 8", + "9 9 9 |9 9 9 |9 9 9" + ] + @board_not_complete = [ + "1 1 1 |1 1 1 |1 1 1", + "2 2 2 |2 2 2 |2 2 2", + "3 3 3 |3 3 3 |3 3 3", + "------+------+------", + ] + + end + def test_valid_validator_input + board = Sudoku::BoardValidator.new @test_board + board.inspect.to_s.must_contain( + 'rows=[["1", "1", "1", "1", "1", "1", "1", "1", "1"], '+ + '["2", "2", "2", "2", "2", "2", "2", "2", "2"], '+ + '["3", "3", "3", "3", "3", "3", "3", "3", "3"], '+ + '["4", "4", "4", "4", "4", "4", "4", "4", "4"], '+ + '["5", "5", "5", "5", "5", "5", "5", "5", "5"], '+ + '["6", "6", "6", "6", "6", "6", "6", "6", "6"], '+ + '["7", "7", "7", "7", "7", "7", "7", "7", "7"], '+ + '["8", "8", "8", "8", "8", "8", "8", "8", "8"], '+ + '["9", "9", "9", "9", "9", "9", "9", "9", "9"]]' + ) + + board.inspect.to_s.must_contain( + 'cols=[["1", "2", "3", "4", "5", "6", "7", "8", "9"], '+ + '["1", "2", "3", "4", "5", "6", "7", "8", "9"], '+ + '["1", "2", "3", "4", "5", "6", "7", "8", "9"], '+ + '["1", "2", "3", "4", "5", "6", "7", "8", "9"], '+ + '["1", "2", "3", "4", "5", "6", "7", "8", "9"], '+ + '["1", "2", "3", "4", "5", "6", "7", "8", "9"], '+ + '["1", "2", "3", "4", "5", "6", "7", "8", "9"], '+ + '["1", "2", "3", "4", "5", "6", "7", "8", "9"], '+ + '["1", "2", "3", "4", "5", "6", "7", "8", "9"]]' + ) + board.inspect.to_s.must_contain( + 'grids=[["1", "1", "1", "2", "2", "2", "3", "3", "3"], '+ + '["1", "1", "1", "2", "2", "2", "3", "3", "3"], '+ + '["1", "1", "1", "2", "2", "2", "3", "3", "3"], '+ + '["4", "4", "4", "5", "5", "5", "6", "6", "6"], '+ + '["4", "4", "4", "5", "5", "5", "6", "6", "6"], '+ + '["4", "4", "4", "5", "5", "5", "6", "6", "6"], '+ + '["7", "7", "7", "8", "8", "8", "9", "9", "9"], '+ + '["7", "7", "7", "8", "8", "8", "9", "9", "9"], '+ + '["7", "7", "7", "8", "8", "8", "9", "9", "9"]]' + ) + end + def test_incomplete_board + assert_raises(RuntimeError) { Sudoku::BoardValidator.new @board_not_complete } + end + def test_invalid_entries_board + assert_raises(RuntimeError) { Sudoku::BoardValidator.new @exception_board } + end + end + describe '#valid_board?' do + def setup + @valid_board = [ + "4 5 2 |6 1 8 |9 7 3", + "3 1 8 |2 7 9 |5 4 6", + "7 9 6 |4 5 3 |8 1 2", + "------+------+------", + "2 4 7 |8 9 6 |1 3 5", + "9 3 1 |5 4 7 |2 6 8", + "8 6 5 |1 3 2 |7 9 4", + "------+------+------", + "5 2 9 |7 6 4 |3 8 1", + "6 8 3 |9 2 1 |4 5 7", + "1 7 4 |3 8 5 |6 2 9", + ] + @blank_board = [ + ". . . |. . . |. . .", + ". . . |. . . |. . .", + ". . . |. . . |. . .", + "------+------+------", + ". . . |. . . |. . .", + ". . . |. . . |. . .", + ". . . |. . . |. . .", + "------+------+------", + ". . . |. . . |. . .", + ". . . |. . . |. . .", + ". . . |. . . |. . .", + ] + @invalid_row = [ + "1 . . |1 . . |. . .", + ". . . |. . . |. . .", + ". . . |. . . |. . .", + "------+------+------", + ". . . |. . . |. . .", + ". . . |. . . |. . .", + ". . . |. . . |. . .", + "------+------+------", + ". . . |. . . |. . .", + ". . . |. . . |. . .", + ". . . |. . . |. . .", + ] + @invalid_col = [ + "1 . . |. . . |. . .", + ". . . |. . . |. . .", + ". . . |. . . |. . .", + "------+------+------", + ". . . |. . . |. . .", + "1 . . |. . . |. . .", + ". . . |. . . |. . .", + "------+------+------", + ". . . |. . . |. . .", + ". . . |. . . |. . .", + ". . . |. . . |. . .", + ] + @invalid_grid = [ + "1 . . |. . . |. . .", + ". . . |. . . |. . .", + ". . 1 |. . . |. . .", + "------+------+------", + ". . . |. . . |. . .", + ". . . |. . . |. . .", + ". . . |. . . |. . .", + "------+------+------", + ". . . |. . . |. . .", + ". . . |. . . |. . .", + ". . . |. . . |. . .", + ] + end + def test_valid_board + assert_equal(Sudoku::BoardValidator.new(@valid_board).valid_board?, true ) + end + def test_valid_empty_board + assert_equal(Sudoku::BoardValidator.new(@blank_board).valid_board?, true ) + end + def test_invalid_row + assert_equal(Sudoku::BoardValidator.new(@invalid_row).valid_board?, false ) + end + def test_invalid_col + assert_equal(Sudoku::BoardValidator.new(@invalid_col).valid_board?, false ) + end + def test_invalid_grid + assert_equal(Sudoku::BoardValidator.new(@invalid_grid).valid_board?, false ) + end + end +end + From 8505640049bb8d9aeb574bda60c62d74a83bf727 Mon Sep 17 00:00:00 2001 From: Eric Lubin Date: Tue, 22 Oct 2013 14:29:28 -0400 Subject: [PATCH 2/3] updating tests --- lib/board_reader.rb | 8 +++---- specs/invalid_error_board.sudoku | 11 +++++++++ specs/sudoku_reader_spec.rb | 24 +++++++++++++++++++ .../sudoku_validator_spec.rb | 5 +--- specs/valid_test_board.sudoku | 11 +++++++++ valid_complete.sudoku | 2 +- 6 files changed, 52 insertions(+), 9 deletions(-) create mode 100644 specs/invalid_error_board.sudoku create mode 100644 specs/sudoku_reader_spec.rb rename sudoku_validator_spec.rb => specs/sudoku_validator_spec.rb (98%) create mode 100644 specs/valid_test_board.sudoku diff --git a/lib/board_reader.rb b/lib/board_reader.rb index a76ab03..e663f4d 100644 --- a/lib/board_reader.rb +++ b/lib/board_reader.rb @@ -7,10 +7,10 @@ def initialize(file) def read board = [] - open(@file).each do |line| - board << line.chomp - # could get more involved checking specific lines here - raise 'Invalid board string' unless line =~ /[1-9].+-|/ + open(@file).each_with_index do |l,index| + board << l.chomp! + # could get more involved checking specific lines here like ---+---+--- + raise "Invalid board string [#{board[index]}]" if board[index].match(/^[1-9|+.\-\s]+$/) == nil end board end diff --git a/specs/invalid_error_board.sudoku b/specs/invalid_error_board.sudoku new file mode 100644 index 0000000..32b9544 --- /dev/null +++ b/specs/invalid_error_board.sudoku @@ -0,0 +1,11 @@ +A B C |D E F |G H I +7 2 3 |8 5 4 |1 6 9 +1 6 4 |3 7 9 |5 2 8 +------+------+------ +. . . |. . . |. . . +. . . |. . . |. . . +. . . |. . . |. . . +------+------+------ +. . . |. . . |. . . +. . . |. . . |. . . +. . . |. . . |. . . diff --git a/specs/sudoku_reader_spec.rb b/specs/sudoku_reader_spec.rb new file mode 100644 index 0000000..47caf72 --- /dev/null +++ b/specs/sudoku_reader_spec.rb @@ -0,0 +1,24 @@ +require 'minitest' +require 'minitest/autorun' +require_relative '../lib/board_reader' + + +class TestMacbethAnalyzer < MiniTest::Test + + describe '#read' do + def test_read_valid_board_returns_11_rows + array = Sudoku::BoardReader.new('./specs/valid_test_board.sudoku').read + assert_equal( array.count, 11 ) + end + def test_read_valid_board_returns_correct_row + array = Sudoku::BoardReader.new('./specs/valid_test_board.sudoku').read + assert_equal( "8 5 9 |6 1 2 |4 3 7", array[0] ) + end + def test_read_error_board + board_reader = Sudoku::BoardReader.new('./specs/invalid_error_board.sudoku') + #puts board_reader.read + assert_raises(RuntimeError) { board_reader.read } + end + end +end + diff --git a/sudoku_validator_spec.rb b/specs/sudoku_validator_spec.rb similarity index 98% rename from sudoku_validator_spec.rb rename to specs/sudoku_validator_spec.rb index 85a6b48..ebafad4 100644 --- a/sudoku_validator_spec.rb +++ b/specs/sudoku_validator_spec.rb @@ -1,9 +1,6 @@ require 'minitest' require 'minitest/autorun' -require_relative 'lib/board_reader' -require_relative 'lib/board_validator' - - +require_relative '../lib/board_validator' require 'minitest/autorun' module MiniTest::Assertions diff --git a/specs/valid_test_board.sudoku b/specs/valid_test_board.sudoku new file mode 100644 index 0000000..ad73252 --- /dev/null +++ b/specs/valid_test_board.sudoku @@ -0,0 +1,11 @@ +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 +------+------+------ +. . . |. . . |. . . +. . . |. . . |. . . +. . . |. . . |. . . +------+------+------ +. . . |. . . |. . . +. . . |. . . |. . . +. . . |. . . |. . . diff --git a/valid_complete.sudoku b/valid_complete.sudoku index ec5075c..bef95c9 100644 --- a/valid_complete.sudoku +++ b/valid_complete.sudoku @@ -1,4 +1,4 @@ -8 5 9 |6 1 2 |4 3 7 +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 ------+------+------ From 7e21e61ef6637f005143244c5182835dacbc3ba2 Mon Sep 17 00:00:00 2001 From: Eric Lubin Date: Thu, 24 Oct 2013 09:55:41 -0400 Subject: [PATCH 3/3] spec cleanup --- lib/board_reader.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/board_reader.rb b/lib/board_reader.rb index e663f4d..deb63df 100644 --- a/lib/board_reader.rb +++ b/lib/board_reader.rb @@ -10,9 +10,14 @@ def read open(@file).each_with_index do |l,index| board << l.chomp! # could get more involved checking specific lines here like ---+---+--- - raise "Invalid board string [#{board[index]}]" if board[index].match(/^[1-9|+.\-\s]+$/) == nil + raise "Invalid board string [#{board[index]}]" if board[index].match(valid_chars) == nil end board end + + private + def valid_chars + /^[1-9|+.\-\s]+$/ + end end end \ No newline at end of file