This repository was archived by the owner on Jun 8, 2019. It is now read-only.
-
-
Notifications
You must be signed in to change notification settings - Fork 36
Solution to sudoku validator exercise by szymzet #12
Open
szymzet
wants to merge
6
commits into
thoughtbot:master
Choose a base branch
from
szymzet:master
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
aba22b3
Move example sudoku files to test dir
szymzet e587f7c
Sudoku file parser
szymzet 4b7a222
Sudoku row validator
szymzet e76c96e
Sudoku column validator
szymzet 9bd5ba8
Sudoku subgrid validator
szymzet 6918794
Final executable script with validator
szymzet File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| # A sample Gemfile | ||
| source "https://rubygems.org" | ||
|
|
||
| gem "mocha" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| GEM | ||
| remote: https://rubygems.org/ | ||
| specs: | ||
| metaclass (0.0.4) | ||
| mocha (1.0.0) | ||
| metaclass (~> 0.0.1) | ||
|
|
||
| PLATFORMS | ||
| ruby | ||
|
|
||
| DEPENDENCIES | ||
| mocha |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,93 @@ | ||
| class SudokuFileParser | ||
| attr_reader :grid | ||
|
|
||
| def initialize(filename) | ||
| contents = IO.read(filename) | ||
| stripped = strip_unnecessary(contents) | ||
| @grid = gridify(stripped) | ||
| end | ||
|
|
||
| private | ||
|
|
||
| def strip_unnecessary(contents) | ||
| contents.gsub(/[^\d\.]/, '') | ||
| end | ||
|
|
||
| def gridify(stripped) | ||
| stripped.chars.each_slice(9).to_a | ||
| end | ||
| end | ||
|
|
||
|
|
||
| class RowValidator | ||
| def initialize(grid) | ||
| @stripped_grid = strip_dots(grid) | ||
| end | ||
|
|
||
| def valid? | ||
| @stripped_grid.all? { |row| row.uniq.size == row.size } | ||
| end | ||
|
|
||
| private | ||
|
|
||
| def strip_dots(grid) | ||
| grid.map do |row| | ||
| row.reject { |e| e == '.' } | ||
| end | ||
| end | ||
| end | ||
|
|
||
|
|
||
| class ColumnValidator | ||
| def initialize(grid) | ||
| @validator = RowValidator.new(grid.transpose) | ||
| end | ||
|
|
||
| def valid? | ||
| @validator.valid? | ||
| end | ||
| end | ||
|
|
||
|
|
||
| class SubgridValidator | ||
| def initialize(grid) | ||
| modified_grid = subgrids_to_rows(grid) | ||
| @validator = RowValidator.new(modified_grid) | ||
| end | ||
|
|
||
| def valid? | ||
| @validator.valid? | ||
| end | ||
|
|
||
| private | ||
|
|
||
| def subgrids_to_rows(grid) | ||
| grid.each_slice(3).each_with_object([]) do |rows, new_grid| | ||
| new_grid.push(*row_of_subgrids(rows)) | ||
| end | ||
| end | ||
|
|
||
| def row_of_subgrids(rows) | ||
| rows.flatten | ||
| .each_slice(3) # in triples | ||
| .group_by.with_index { |_, i| i % 3 } # group every third | ||
| .map { |i, e| e.flatten } # form a row from subgrid elements | ||
| end | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Once I understood what |
||
| end | ||
|
|
||
|
|
||
| class SudokuValidator | ||
| def initialize(filename) | ||
| @grid = SudokuFileParser.new(filename).grid | ||
| @complete = !@grid.flatten.any? { |e| e == '.' } | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you use |
||
| end | ||
|
|
||
| def valid? | ||
| validators = [RowValidator, ColumnValidator, SubgridValidator] | ||
| validators.all? { |v| v.new(@grid).valid? } | ||
| end | ||
|
|
||
| def complete? | ||
| @complete | ||
| end | ||
| end | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| #!/usr/bin/env ruby | ||
|
|
||
| require_relative 'lib/sudoku_validator' | ||
|
|
||
| app = SudokuValidator.new(ARGV.first) | ||
|
|
||
| if app.valid? | ||
| ending = app.complete? ? '.' : ', but incomplete.' | ||
| puts "This sudoku is valid#{ending}" | ||
| else | ||
| puts 'This sudoku is invalid.' | ||
| end | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The repetition of "This sudoku is " makes your solution easier to follow than my solution was. |
||
File renamed without changes.
File renamed without changes.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,130 @@ | ||
| require 'minitest/autorun' | ||
| require 'minitest/pride' | ||
| require 'mocha/mini_test' | ||
|
|
||
| require_relative '../lib/sudoku_validator' | ||
|
|
||
| class SudokuFileParserTest < MiniTest::Unit::TestCase | ||
| def test_reads_file_into_9x9_2d_array | ||
| parser = SudokuFileParser.new('test/valid_complete.sudoku') | ||
|
|
||
| assert_equal(9, parser.grid.size) | ||
| parser.grid.each { |row| assert_equal(9, row.size) } | ||
| end | ||
|
|
||
| def test_parses_as_strings | ||
| expected = | ||
| [%w{8 5 . . . 2 4 . .}, | ||
| %w{7 2 . . . . . . 9}, | ||
| %w{. . 4 . . . . . .}, | ||
| %w{. . . 1 . 7 . . 2}, | ||
| %w{3 . 5 . . . 9 . .}, | ||
| %w{. 4 . . . . . . .}, | ||
| %w{. . . . 8 . . 7 .}, | ||
| %w{. 1 7 . . . . . .}, | ||
| %w{. . . . 3 6 . 4 .}] | ||
|
|
||
| parser = SudokuFileParser.new('test/valid_incomplete.sudoku') | ||
| assert_equal(expected, parser.grid) | ||
| end | ||
| end | ||
|
|
||
| class RowValidatorTest < MiniTest::Unit::TestCase | ||
| def test_valid_when_unique_in_row | ||
| grid = [%w{1 2 3}, %w{3 2 1}] | ||
|
|
||
| validator = RowValidator.new(grid) | ||
|
|
||
| assert(validator.valid?) | ||
| end | ||
|
|
||
| def test_invalid_when_duplicates_in_row | ||
| grid = [%w{1 2 3}, %w{3 2 3}] | ||
|
|
||
| validator = RowValidator.new(grid) | ||
|
|
||
| refute(validator.valid?) | ||
| end | ||
|
|
||
| def test_ignore_dots | ||
| assert(RowValidator.new([['.', '.'], ['.', '.']]).valid?) | ||
| assert(RowValidator.new([['.', '.'], ['1', '.']]).valid?) | ||
| refute(RowValidator.new([['.', '.', '2'], ['1', '.', '1']]).valid?) | ||
| end | ||
| end | ||
|
|
||
| class ColumnValidatorTest < MiniTest::Unit::TestCase | ||
| def test_is_implemented_in_terms_of_row_validator_for_transposed_grid | ||
| grid =[%w{1 2 3}, %w{4 5 6}] | ||
| row_validator = mock() | ||
| row_validator.expects(:valid?).returns(true) | ||
| RowValidator | ||
| .expects(:new) | ||
| .with([%w{1 4}, %w{2 5}, %w{3 6}]) | ||
| .returns(row_validator) | ||
|
|
||
| ColumnValidator.new(grid).valid? | ||
| end | ||
| end | ||
|
|
||
| class SubgridValidatorTest < MiniTest::Unit::TestCase | ||
| def test_is_implemented_in_terms_of_row_validator_for_modified_grid | ||
| grid = | ||
| [%w{8 5 . . . 2 4 . .}, | ||
| %w{7 2 . . . . . . 9}, | ||
| %w{. . 4 . . . . . .}, | ||
| %w{. . . 1 . 7 . . 2}, | ||
| %w{3 . 5 . . . 9 . .}, | ||
| %w{. 4 . . . . . . .}, | ||
| %w{. . . . 8 . . 7 .}, | ||
| %w{. 1 7 . . . . . .}, | ||
| %w{. . . . 3 6 . 4 .}] | ||
|
|
||
| modified_grid = | ||
| [%w{8 5 . 7 2 . . . 4}, | ||
| %w{. . 2 . . . . . .}, | ||
| %w{4 . . . . 9 . . .}, | ||
| %w{. . . 3 . 5 . 4 .}, | ||
| %w{1 . 7 . . . . . .}, | ||
| %w{. . 2 9 . . . . .}, | ||
| %w{. . . . 1 7 . . .}, | ||
| %w{. 8 . . . . . 3 6}, | ||
| %w{. 7 . . . . . 4 .}] | ||
|
|
||
| row_validator = mock() | ||
| row_validator.expects(:valid?).returns(true) | ||
| RowValidator | ||
| .expects(:new) | ||
| .with(modified_grid) | ||
| .returns(row_validator) | ||
|
|
||
| SubgridValidator.new(grid).valid? | ||
| end | ||
| end | ||
|
|
||
| class SudokuValidatorTest < MiniTest::Unit::TestCase | ||
| def test_valid_complete | ||
| app = SudokuValidator.new('test/valid_complete.sudoku') | ||
| assert(app.valid?) | ||
| assert(app.complete?) | ||
| end | ||
|
|
||
| def test_valid_incomplete | ||
| app = SudokuValidator.new('test/valid_incomplete.sudoku') | ||
| assert(app.valid?) | ||
| refute(app.complete?) | ||
| end | ||
|
|
||
| def test_invalid_complete | ||
| app = SudokuValidator.new('test/invalid_complete.sudoku') | ||
| refute(app.valid?) | ||
| assert(app.complete?) | ||
| end | ||
|
|
||
| def test_invalid_incomplete | ||
| app = SudokuValidator.new('test/invalid_incomplete.sudoku') | ||
| refute(app.valid?) | ||
| refute(app.complete?) | ||
| end | ||
| end | ||
|
|
File renamed without changes.
File renamed without changes.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If later you needed to allow differently sized grids (e.g., 16x16), hard-coding the grid size would make it more difficult. Whether a more-general solution is best would depend on how likely this prospect is, though.