-
-
Notifications
You must be signed in to change notification settings - Fork 520
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Note that this exercise appears to differ from other xruby exercises in the following significant manner: Verification that the student function under test has produced a correct answer is NOT done by comparing the observed output to an expected output (or even a list of expected outputs), because the set of acceptable outputs is quite large even for a small input set. Thus, verification is done by running the observed output against a function that verifies various properties about the output. https://github.com/exercism/x-common/blob/master/exercises/dominoes/canonical-data.json explains the approach taken.
- Loading branch information
1 parent
9e9977a
commit add7611
Showing
6 changed files
with
252 additions
and
0 deletions.
There are no files selected for viewing
This file contains 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
This file contains 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 @@ | ||
1 |
This file contains 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,138 @@ | ||
#!/usr/bin/env ruby | ||
gem 'minitest', '>= 5.0.0' | ||
require 'minitest/autorun' | ||
require_relative 'dominoes' | ||
|
||
# Test data version: | ||
# 018c48d | ||
class DominoesTest < Minitest::Test | ||
def assert_correct_chain(input_dominoes, output_chain) | ||
refute_nil output_chain, "There should be a chain for #{input_dominoes}" | ||
|
||
input_normal = input_dominoes.map(&:sort).sort | ||
output_normal = output_chain.map(&:sort).sort | ||
assert_equal input_normal, output_normal, | ||
'Dominoes used in the output must be the same as the ones given in the input' | ||
|
||
output_chain.each_cons(2).with_index { |(d1, d2), i| | ||
_, d1_right = d1 | ||
d2_left, _ = d2 | ||
assert_equal d1_right, d2_left, | ||
"In chain #{output_chain}, right end of domino #{i} (#{d1}) and left end of domino #{i + 1} (#{d2}) must match" | ||
} | ||
|
||
return if output_chain.empty? | ||
|
||
first_domino = output_chain.first | ||
last_domino = output_chain.last | ||
d1_left, _ = first_domino | ||
_, d2_right = last_domino | ||
assert_equal d1_left, d2_right, | ||
"In chain #{output_chain}, left end of first domino (#{first_domino}) and right end of last domino (#{last_domino}) must match" | ||
end | ||
|
||
def test_empty_input_empty_output | ||
# skip | ||
input_dominoes = [] | ||
output_chain = Dominoes.chain(input_dominoes) | ||
assert_correct_chain(input_dominoes, output_chain) | ||
end | ||
|
||
def test_singleton_input_singleton_output | ||
skip | ||
input_dominoes = [[1, 1]] | ||
output_chain = Dominoes.chain(input_dominoes) | ||
assert_correct_chain(input_dominoes, output_chain) | ||
end | ||
|
||
def test_singleton_that_can_t_be_chained | ||
skip | ||
input_dominoes = [[1, 2]] | ||
output_chain = Dominoes.chain(input_dominoes) | ||
assert_equal nil, output_chain, "There should be no chain for #{input_dominoes}" | ||
end | ||
|
||
def test_three_elements | ||
skip | ||
input_dominoes = [[1, 2], [3, 1], [2, 3]] | ||
output_chain = Dominoes.chain(input_dominoes) | ||
assert_correct_chain(input_dominoes, output_chain) | ||
end | ||
|
||
def test_can_reverse_dominoes | ||
skip | ||
input_dominoes = [[1, 2], [1, 3], [2, 3]] | ||
output_chain = Dominoes.chain(input_dominoes) | ||
assert_correct_chain(input_dominoes, output_chain) | ||
end | ||
|
||
def test_can_t_be_chained | ||
skip | ||
input_dominoes = [[1, 2], [4, 1], [2, 3]] | ||
output_chain = Dominoes.chain(input_dominoes) | ||
assert_equal nil, output_chain, "There should be no chain for #{input_dominoes}" | ||
end | ||
|
||
def test_disconnected_simple | ||
skip | ||
input_dominoes = [[1, 1], [2, 2]] | ||
output_chain = Dominoes.chain(input_dominoes) | ||
assert_equal nil, output_chain, "There should be no chain for #{input_dominoes}" | ||
end | ||
|
||
def test_disconnected_double_loop | ||
skip | ||
input_dominoes = [[1, 2], [2, 1], [3, 4], [4, 3]] | ||
output_chain = Dominoes.chain(input_dominoes) | ||
assert_equal nil, output_chain, "There should be no chain for #{input_dominoes}" | ||
end | ||
|
||
def test_disconnected_single_isolated | ||
skip | ||
input_dominoes = [[1, 2], [2, 3], [3, 1], [4, 4]] | ||
output_chain = Dominoes.chain(input_dominoes) | ||
assert_equal nil, output_chain, "There should be no chain for #{input_dominoes}" | ||
end | ||
|
||
def test_need_backtrack | ||
skip | ||
input_dominoes = [[1, 2], [2, 3], [3, 1], [2, 4], [2, 4]] | ||
output_chain = Dominoes.chain(input_dominoes) | ||
assert_correct_chain(input_dominoes, output_chain) | ||
end | ||
|
||
def test_separate_loops | ||
skip | ||
input_dominoes = [[1, 2], [2, 3], [3, 1], [1, 1], [2, 2], [3, 3]] | ||
output_chain = Dominoes.chain(input_dominoes) | ||
assert_correct_chain(input_dominoes, output_chain) | ||
end | ||
|
||
def test_ten_elements | ||
skip | ||
input_dominoes = [[1, 2], [5, 3], [3, 1], [1, 2], [2, 4], [1, 6], [2, 3], [3, 4], [5, 6]] | ||
output_chain = Dominoes.chain(input_dominoes) | ||
assert_correct_chain(input_dominoes, output_chain) | ||
end | ||
|
||
# Problems in exercism evolve over time, as we find better ways to ask | ||
# questions. | ||
# The version number refers to the version of the problem you solved, | ||
# not your solution. | ||
# | ||
# Define a constant named VERSION inside of the top level BookKeeping | ||
# module, which may be placed near the end of your file. | ||
# | ||
# In your file, it will look like this: | ||
# | ||
# module BookKeeping | ||
# VERSION = 1 # Where the version number matches the one in the test. | ||
# end | ||
# | ||
# If you are curious, read more about constants on RubyDoc: | ||
# http://ruby-doc.org/docs/ruby-doc-bundle/UsersGuide/rg/constants.html | ||
def test_bookkeeping | ||
skip | ||
assert_equal 1, BookKeeping::VERSION | ||
end | ||
end |
This file contains 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,37 @@ | ||
module Dominoes | ||
def self.chain(dominoes) | ||
return dominoes if dominoes.empty? | ||
|
||
first = dominoes.first | ||
|
||
subchain = try_subchain(dominoes.drop(1), *first) | ||
subchain && [first] + subchain | ||
end | ||
|
||
def self.try_subchain(dominoes, chain_left, chain_right) | ||
return chain_left == chain_right ? [] : nil if dominoes.empty? | ||
|
||
dominoes.each_with_index { |domino, i| | ||
other_dominoes = dominoes[0...i] + dominoes[(i + 1)..-1] | ||
domino_left, domino_right = domino | ||
if domino_left == chain_right | ||
# Try to add domino (unflipped) to chain | ||
if (subchain = try_subchain(other_dominoes, chain_left, domino_right)) | ||
return [domino] + subchain | ||
end | ||
elsif domino_right == chain_right | ||
# Try to add domino (flipped) to chain | ||
if (subchain = try_subchain(other_dominoes, chain_left, domino_left)) | ||
return [[domino_right, domino_left]] + subchain | ||
end | ||
end | ||
} | ||
|
||
# Found no suitable chain. | ||
return nil | ||
end | ||
end | ||
|
||
module BookKeeping | ||
VERSION = 1 | ||
end |
This file contains 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,46 @@ | ||
#!/usr/bin/env ruby | ||
gem 'minitest', '>= 5.0.0' | ||
require 'minitest/autorun' | ||
require_relative 'dominoes' | ||
|
||
# Test data version: | ||
# <%= sha1 %> | ||
class DominoesTest < Minitest::Test | ||
def assert_correct_chain(input_dominoes, output_chain) | ||
refute_nil output_chain, "There should be a chain for #{input_dominoes}" | ||
|
||
input_normal = input_dominoes.map(&:sort).sort | ||
output_normal = output_chain.map(&:sort).sort | ||
assert_equal input_normal, output_normal, | ||
'Dominoes used in the output must be the same as the ones given in the input' | ||
|
||
output_chain.each_cons(2).with_index { |(d1, d2), i| | ||
_, d1_right = d1 | ||
d2_left, _ = d2 | ||
assert_equal d1_right, d2_left, | ||
"In chain #{output_chain}, right end of domino #{i} (#{d1}) and left end of domino #{i + 1} (#{d2}) must match" | ||
} | ||
|
||
return if output_chain.empty? | ||
|
||
first_domino = output_chain.first | ||
last_domino = output_chain.last | ||
d1_left, _ = first_domino | ||
_, d2_right = last_domino | ||
assert_equal d1_left, d2_right, | ||
"In chain #{output_chain}, left end of first domino (#{first_domino}) and right end of last domino (#{last_domino}) must match" | ||
end | ||
|
||
<% test_cases.each do |test_case| %> | ||
def <%= test_case.test_name %> | ||
<%= test_case.skipped %> | ||
<%= test_case.workload %> | ||
end | ||
<% end %> | ||
<%= IO.read(XRUBY_LIB + '/bookkeeping.md') %> | ||
def test_bookkeeping | ||
skip | ||
assert_equal <%= version.next %>, BookKeeping::VERSION | ||
end | ||
end |
This file contains 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,24 @@ | ||
class DominoesCase < OpenStruct | ||
def test_name | ||
'test_%s' % description.gsub(/['= -]+/, '_') | ||
end | ||
|
||
def workload | ||
|
||
<<-WL.chomp | ||
input_dominoes = #{input} | ||
output_chain = Dominoes.chain(input_dominoes) | ||
#{can_chain ? 'assert_correct_chain(input_dominoes, output_chain)' : 'assert_equal nil, output_chain, "There should be no chain for #{input_dominoes}"'} | ||
WL | ||
end | ||
|
||
def skipped | ||
index.zero? ? '# skip' : 'skip' | ||
end | ||
end | ||
|
||
DominoesCases = proc do |data| | ||
JSON.parse(data)['cases'].map.with_index do |row, i| | ||
DominoesCase.new(row.merge('index' => i)) | ||
end | ||
end |