From 0073eb4b850a333ff767b41debb92e578ec18d5d Mon Sep 17 00:00:00 2001 From: Peter Tseng Date: Tue, 8 Nov 2016 01:45:21 -0800 Subject: [PATCH 01/14] dominoes: Add generator and example 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. --- config.json | 6 ++ exercises/dominoes/.version | 1 + exercises/dominoes/dominoes_test.rb | 138 ++++++++++++++++++++++++++++ exercises/dominoes/example.rb | 37 ++++++++ exercises/dominoes/example.tt | 46 ++++++++++ lib/dominoes_cases.rb | 23 +++++ 6 files changed, 251 insertions(+) create mode 100644 exercises/dominoes/.version create mode 100755 exercises/dominoes/dominoes_test.rb create mode 100644 exercises/dominoes/example.rb create mode 100644 exercises/dominoes/example.tt create mode 100644 lib/dominoes_cases.rb diff --git a/config.json b/config.json index 50df3e154f..f95243ff36 100644 --- a/config.json +++ b/config.json @@ -502,6 +502,12 @@ "difficulty": 1, "topics": [ ] + }, + { + "slug": "dominoes", + "difficulty": 1, + "topics": [ + ] } ], "deprecated": [ diff --git a/exercises/dominoes/.version b/exercises/dominoes/.version new file mode 100644 index 0000000000..56a6051ca2 --- /dev/null +++ b/exercises/dominoes/.version @@ -0,0 +1 @@ +1 \ No newline at end of file diff --git a/exercises/dominoes/dominoes_test.rb b/exercises/dominoes/dominoes_test.rb new file mode 100755 index 0000000000..ef04910ac8 --- /dev/null +++ b/exercises/dominoes/dominoes_test.rb @@ -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_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_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_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_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_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 diff --git a/exercises/dominoes/example.rb b/exercises/dominoes/example.rb new file mode 100644 index 0000000000..67b743f5e6 --- /dev/null +++ b/exercises/dominoes/example.rb @@ -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.take(i) + dominoes.drop(i + 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. + nil + end +end + +module BookKeeping + VERSION = 1 +end diff --git a/exercises/dominoes/example.tt b/exercises/dominoes/example.tt new file mode 100644 index 0000000000..8101b0eb83 --- /dev/null +++ b/exercises/dominoes/example.tt @@ -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 diff --git a/lib/dominoes_cases.rb b/lib/dominoes_cases.rb new file mode 100644 index 0000000000..3f9af3c1ae --- /dev/null +++ b/lib/dominoes_cases.rb @@ -0,0 +1,23 @@ +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_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 From 1ece0526b32297126bccf02a78f7392b00b7a969 Mon Sep 17 00:00:00 2001 From: Peter Tseng Date: Tue, 8 Nov 2016 09:40:24 -0800 Subject: [PATCH 02/14] dominoes: Explain why nil for "no chain possible" --- exercises/dominoes/example.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/exercises/dominoes/example.rb b/exercises/dominoes/example.rb index 67b743f5e6..2b7b64eb9f 100644 --- a/exercises/dominoes/example.rb +++ b/exercises/dominoes/example.rb @@ -28,6 +28,9 @@ def self.try_subchain(dominoes, chain_left, chain_right) } # Found no suitable chain. + # Note that for "no chain" we have to use nil instead of []. + # This is because [] is the valid answer for `Dominoes.chain([])`. + # If we used [] for "no chain", then the meaning of [] is ambiguous. nil end end From f27118725f82a7ff421b21ed41a36c07391b0fcc Mon Sep 17 00:00:00 2001 From: Peter Tseng Date: Tue, 8 Nov 2016 09:41:55 -0800 Subject: [PATCH 03/14] dominoes: use an actual x-common sha the old sha was on a personal branch and it would just confuse people if it were kept in this file. this one is on master --- exercises/dominoes/dominoes_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/dominoes/dominoes_test.rb b/exercises/dominoes/dominoes_test.rb index ef04910ac8..1b49c1d06a 100755 --- a/exercises/dominoes/dominoes_test.rb +++ b/exercises/dominoes/dominoes_test.rb @@ -4,7 +4,7 @@ require_relative 'dominoes' # Test data version: -# 018c48d +# 82eb00d class DominoesTest < Minitest::Test def assert_correct_chain(input_dominoes, output_chain) refute_nil output_chain, "There should be a chain for #{input_dominoes}" From bf41baa5628c372a96a2335de4a286590e8dc592 Mon Sep 17 00:00:00 2001 From: Peter Tseng Date: Tue, 8 Nov 2016 09:42:44 -0800 Subject: [PATCH 04/14] dominoes: Move assert_correct_chain to bottom --- exercises/dominoes/dominoes_test.rb | 50 ++++++++++++++--------------- exercises/dominoes/example.tt | 26 +++++++-------- 2 files changed, 38 insertions(+), 38 deletions(-) diff --git a/exercises/dominoes/dominoes_test.rb b/exercises/dominoes/dominoes_test.rb index 1b49c1d06a..c81da3ce16 100755 --- a/exercises/dominoes/dominoes_test.rb +++ b/exercises/dominoes/dominoes_test.rb @@ -6,31 +6,6 @@ # Test data version: # 82eb00d 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 = [] @@ -135,4 +110,29 @@ def test_bookkeeping skip assert_equal 1, BookKeeping::VERSION end + + 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 end diff --git a/exercises/dominoes/example.tt b/exercises/dominoes/example.tt index 8101b0eb83..0abd646188 100644 --- a/exercises/dominoes/example.tt +++ b/exercises/dominoes/example.tt @@ -6,6 +6,19 @@ require_relative 'dominoes' # Test data version: # <%= sha1 %> class DominoesTest < Minitest::Test +<% 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 + def assert_correct_chain(input_dominoes, output_chain) refute_nil output_chain, "There should be a chain for #{input_dominoes}" @@ -30,17 +43,4 @@ class DominoesTest < Minitest::Test 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 From 4d082ee6f40f36e7b0f464e9ba156dde62239d66 Mon Sep 17 00:00:00 2001 From: Peter Tseng Date: Tue, 8 Nov 2016 09:50:24 -0800 Subject: [PATCH 05/14] dominoes: Extract methods and give them names --- exercises/dominoes/dominoes_test.rb | 22 +++++++++++++++------- exercises/dominoes/example.tt | 22 +++++++++++++++------- 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/exercises/dominoes/dominoes_test.rb b/exercises/dominoes/dominoes_test.rb index c81da3ce16..d25341a819 100755 --- a/exercises/dominoes/dominoes_test.rb +++ b/exercises/dominoes/dominoes_test.rb @@ -113,26 +113,34 @@ def test_bookkeeping def assert_correct_chain(input_dominoes, output_chain) refute_nil output_chain, "There should be a chain for #{input_dominoes}" + assert_same_dominoes(input_dominoes, output_chain) + assert_consecutive_dominoes_match(output_chain) + return if output_chain.empty? + assert_dominoes_at_end_match(output_chain) + end + def assert_same_dominoes(input_dominoes, output_chain) 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' + end - output_chain.each_cons(2).with_index { |(d1, d2), i| + def assert_consecutive_dominoes_match(chain) + 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" + "In chain #{chain}, right end of domino #{i} (#{d1}) and left end of domino #{i + 1} (#{d2}) must match" } + end - return if output_chain.empty? - - first_domino = output_chain.first - last_domino = output_chain.last + def assert_dominoes_at_end_match(chain) + first_domino = chain.first + last_domino = 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" + "In chain #{chain}, left end of first domino (#{first_domino}) and right end of last domino (#{last_domino}) must match" end end diff --git a/exercises/dominoes/example.tt b/exercises/dominoes/example.tt index 0abd646188..fe62bb2107 100644 --- a/exercises/dominoes/example.tt +++ b/exercises/dominoes/example.tt @@ -21,26 +21,34 @@ class DominoesTest < Minitest::Test def assert_correct_chain(input_dominoes, output_chain) refute_nil output_chain, "There should be a chain for #{input_dominoes}" + assert_same_dominoes(input_dominoes, output_chain) + assert_consecutive_dominoes_match(output_chain) + return if output_chain.empty? + assert_dominoes_at_end_match(output_chain) + end + def assert_same_dominoes(input_dominoes, output_chain) 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' + end - output_chain.each_cons(2).with_index { |(d1, d2), i| + def assert_consecutive_dominoes_match(chain) + 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" + "In chain #{chain}, right end of domino #{i} (#{d1}) and left end of domino #{i + 1} (#{d2}) must match" } + end - return if output_chain.empty? - - first_domino = output_chain.first - last_domino = output_chain.last + def assert_dominoes_at_end_match(chain) + first_domino = chain.first + last_domino = 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" + "In chain #{chain}, left end of first domino (#{first_domino}) and right end of last domino (#{last_domino}) must match" end end From aed667c995a48f7a2b81c437dcc3fa67ad13c836 Mon Sep 17 00:00:00 2001 From: Peter Tseng Date: Tue, 8 Nov 2016 09:51:46 -0800 Subject: [PATCH 06/14] dominoes: short-circuit earlier --- exercises/dominoes/dominoes_test.rb | 2 +- exercises/dominoes/example.tt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/exercises/dominoes/dominoes_test.rb b/exercises/dominoes/dominoes_test.rb index d25341a819..96181fa77b 100755 --- a/exercises/dominoes/dominoes_test.rb +++ b/exercises/dominoes/dominoes_test.rb @@ -114,8 +114,8 @@ def test_bookkeeping def assert_correct_chain(input_dominoes, output_chain) refute_nil output_chain, "There should be a chain for #{input_dominoes}" assert_same_dominoes(input_dominoes, output_chain) - assert_consecutive_dominoes_match(output_chain) return if output_chain.empty? + assert_consecutive_dominoes_match(output_chain) assert_dominoes_at_end_match(output_chain) end diff --git a/exercises/dominoes/example.tt b/exercises/dominoes/example.tt index fe62bb2107..41b468a82d 100644 --- a/exercises/dominoes/example.tt +++ b/exercises/dominoes/example.tt @@ -22,8 +22,8 @@ class DominoesTest < Minitest::Test def assert_correct_chain(input_dominoes, output_chain) refute_nil output_chain, "There should be a chain for #{input_dominoes}" assert_same_dominoes(input_dominoes, output_chain) - assert_consecutive_dominoes_match(output_chain) return if output_chain.empty? + assert_consecutive_dominoes_match(output_chain) assert_dominoes_at_end_match(output_chain) end From 43aa6f8dad64960bec5428ded0d8edea7a82ac79 Mon Sep 17 00:00:00 2001 From: Peter Tseng Date: Tue, 8 Nov 2016 09:53:00 -0800 Subject: [PATCH 07/14] dominoes: Use first and last instead of destructure No need to worry about list lengths != 2. assert_same_dominoes has got it covered. --- exercises/dominoes/dominoes_test.rb | 8 ++------ exercises/dominoes/example.tt | 8 ++------ 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/exercises/dominoes/dominoes_test.rb b/exercises/dominoes/dominoes_test.rb index 96181fa77b..198c9a13a6 100755 --- a/exercises/dominoes/dominoes_test.rb +++ b/exercises/dominoes/dominoes_test.rb @@ -128,9 +128,7 @@ def assert_same_dominoes(input_dominoes, output_chain) def assert_consecutive_dominoes_match(chain) chain.each_cons(2).with_index { |(d1, d2), i| - _, d1_right = d1 - d2_left, _ = d2 - assert_equal d1_right, d2_left, + assert_equal d1.last, d2.first, "In chain #{chain}, right end of domino #{i} (#{d1}) and left end of domino #{i + 1} (#{d2}) must match" } end @@ -138,9 +136,7 @@ def assert_consecutive_dominoes_match(chain) def assert_dominoes_at_end_match(chain) first_domino = chain.first last_domino = chain.last - d1_left, _ = first_domino - _, d2_right = last_domino - assert_equal d1_left, d2_right, + assert_equal first_domino.first, last_domino.last, "In chain #{chain}, left end of first domino (#{first_domino}) and right end of last domino (#{last_domino}) must match" end end diff --git a/exercises/dominoes/example.tt b/exercises/dominoes/example.tt index 41b468a82d..b182361676 100644 --- a/exercises/dominoes/example.tt +++ b/exercises/dominoes/example.tt @@ -36,9 +36,7 @@ class DominoesTest < Minitest::Test def assert_consecutive_dominoes_match(chain) chain.each_cons(2).with_index { |(d1, d2), i| - _, d1_right = d1 - d2_left, _ = d2 - assert_equal d1_right, d2_left, + assert_equal d1.last, d2.first, "In chain #{chain}, right end of domino #{i} (#{d1}) and left end of domino #{i + 1} (#{d2}) must match" } end @@ -46,9 +44,7 @@ class DominoesTest < Minitest::Test def assert_dominoes_at_end_match(chain) first_domino = chain.first last_domino = chain.last - d1_left, _ = first_domino - _, d2_right = last_domino - assert_equal d1_left, d2_right, + assert_equal first_domino.first, last_domino.last, "In chain #{chain}, left end of first domino (#{first_domino}) and right end of last domino (#{last_domino}) must match" end end From 644efa57ee22bb67a36d8e3e7c00465d4cfa9ae8 Mon Sep 17 00:00:00 2001 From: Peter Tseng Date: Tue, 8 Nov 2016 10:02:12 -0800 Subject: [PATCH 08/14] dominoes: change can_t to cant --- exercises/dominoes/dominoes_test.rb | 4 ++-- lib/dominoes_cases.rb | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/exercises/dominoes/dominoes_test.rb b/exercises/dominoes/dominoes_test.rb index 198c9a13a6..16cec3242c 100755 --- a/exercises/dominoes/dominoes_test.rb +++ b/exercises/dominoes/dominoes_test.rb @@ -20,7 +20,7 @@ def test_singleton_input_singleton_output assert_correct_chain(input_dominoes, output_chain) end - def test_singleton_that_can_t_be_chained + def test_singleton_that_cant_be_chained skip input_dominoes = [[1, 2]] output_chain = Dominoes.chain(input_dominoes) @@ -41,7 +41,7 @@ def test_can_reverse_dominoes assert_correct_chain(input_dominoes, output_chain) end - def test_can_t_be_chained + def test_cant_be_chained skip input_dominoes = [[1, 2], [4, 1], [2, 3]] output_chain = Dominoes.chain(input_dominoes) diff --git a/lib/dominoes_cases.rb b/lib/dominoes_cases.rb index 3f9af3c1ae..2b47f4e28e 100644 --- a/lib/dominoes_cases.rb +++ b/lib/dominoes_cases.rb @@ -1,6 +1,6 @@ class DominoesCase < OpenStruct def test_name - 'test_%s' % description.gsub(/['= -]+/, '_') + 'test_%s' % description.tr("'", '').gsub(/[= -]+/, '_') end def workload From aa1307fca5c5183363025e3289c75d1b758f684a Mon Sep 17 00:00:00 2001 From: Peter Tseng Date: Tue, 8 Nov 2016 10:36:51 -0800 Subject: [PATCH 09/14] dominoes: can't -> can not --- lib/dominoes_cases.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/dominoes_cases.rb b/lib/dominoes_cases.rb index 2b47f4e28e..2e618f29ce 100644 --- a/lib/dominoes_cases.rb +++ b/lib/dominoes_cases.rb @@ -1,6 +1,6 @@ class DominoesCase < OpenStruct def test_name - 'test_%s' % description.tr("'", '').gsub(/[= -]+/, '_') + 'test_%s' % description.gsub("can't", 'can not').gsub(/[= -]+/, '_') end def workload From 4ad379ddb41a58c62abf027978ade62fb5351e90 Mon Sep 17 00:00:00 2001 From: Peter Tseng Date: Tue, 8 Nov 2016 10:37:18 -0800 Subject: [PATCH 10/14] dominoes: cant -> can_not --- exercises/dominoes/dominoes_test.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exercises/dominoes/dominoes_test.rb b/exercises/dominoes/dominoes_test.rb index 16cec3242c..79f27c014b 100755 --- a/exercises/dominoes/dominoes_test.rb +++ b/exercises/dominoes/dominoes_test.rb @@ -20,7 +20,7 @@ def test_singleton_input_singleton_output assert_correct_chain(input_dominoes, output_chain) end - def test_singleton_that_cant_be_chained + def test_singleton_that_can_not_be_chained skip input_dominoes = [[1, 2]] output_chain = Dominoes.chain(input_dominoes) @@ -41,7 +41,7 @@ def test_can_reverse_dominoes assert_correct_chain(input_dominoes, output_chain) end - def test_cant_be_chained + def test_can_not_be_chained skip input_dominoes = [[1, 2], [4, 1], [2, 3]] output_chain = Dominoes.chain(input_dominoes) From d5571b8566993bc5baffd531cf4f3483a92ce9d9 Mon Sep 17 00:00:00 2001 From: Peter Tseng Date: Tue, 8 Nov 2016 10:55:28 -0800 Subject: [PATCH 11/14] dominoes: refute_correct_chain --- exercises/dominoes/dominoes_test.rb | 14 +++++++++----- exercises/dominoes/example.tt | 4 ++++ lib/dominoes_cases.rb | 2 +- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/exercises/dominoes/dominoes_test.rb b/exercises/dominoes/dominoes_test.rb index 79f27c014b..705c617618 100755 --- a/exercises/dominoes/dominoes_test.rb +++ b/exercises/dominoes/dominoes_test.rb @@ -24,7 +24,7 @@ def test_singleton_that_can_not_be_chained skip input_dominoes = [[1, 2]] output_chain = Dominoes.chain(input_dominoes) - assert_nil output_chain, "There should be no chain for #{input_dominoes}" + refute_correct_chain(input_dominoes, output_chain) end def test_three_elements @@ -45,28 +45,28 @@ def test_can_not_be_chained skip input_dominoes = [[1, 2], [4, 1], [2, 3]] output_chain = Dominoes.chain(input_dominoes) - assert_nil output_chain, "There should be no chain for #{input_dominoes}" + refute_correct_chain(input_dominoes, output_chain) end def test_disconnected_simple skip input_dominoes = [[1, 1], [2, 2]] output_chain = Dominoes.chain(input_dominoes) - assert_nil output_chain, "There should be no chain for #{input_dominoes}" + refute_correct_chain(input_dominoes, output_chain) end def test_disconnected_double_loop skip input_dominoes = [[1, 2], [2, 1], [3, 4], [4, 3]] output_chain = Dominoes.chain(input_dominoes) - assert_nil output_chain, "There should be no chain for #{input_dominoes}" + refute_correct_chain(input_dominoes, output_chain) end def test_disconnected_single_isolated skip input_dominoes = [[1, 2], [2, 3], [3, 1], [4, 4]] output_chain = Dominoes.chain(input_dominoes) - assert_nil output_chain, "There should be no chain for #{input_dominoes}" + refute_correct_chain(input_dominoes, output_chain) end def test_need_backtrack @@ -139,4 +139,8 @@ def assert_dominoes_at_end_match(chain) assert_equal first_domino.first, last_domino.last, "In chain #{chain}, left end of first domino (#{first_domino}) and right end of last domino (#{last_domino}) must match" end + + def refute_correct_chain(input_dominoes, output_chain) + assert_nil output_chain, "There should be no chain for #{input_dominoes}" + end end diff --git a/exercises/dominoes/example.tt b/exercises/dominoes/example.tt index b182361676..2adc81df1c 100644 --- a/exercises/dominoes/example.tt +++ b/exercises/dominoes/example.tt @@ -47,4 +47,8 @@ class DominoesTest < Minitest::Test assert_equal first_domino.first, last_domino.last, "In chain #{chain}, left end of first domino (#{first_domino}) and right end of last domino (#{last_domino}) must match" end + + def refute_correct_chain(input_dominoes, output_chain) + assert_nil output_chain, "There should be no chain for #{input_dominoes}" + end end diff --git a/lib/dominoes_cases.rb b/lib/dominoes_cases.rb index 2e618f29ce..1bc60629e2 100644 --- a/lib/dominoes_cases.rb +++ b/lib/dominoes_cases.rb @@ -7,7 +7,7 @@ def workload <<-WL.chomp input_dominoes = #{input} output_chain = Dominoes.chain(input_dominoes) - #{can_chain ? 'assert_correct_chain(input_dominoes, output_chain)' : 'assert_nil output_chain, "There should be no chain for #{input_dominoes}"'} + #{can_chain ? 'assert' : 'refute' }_correct_chain(input_dominoes, output_chain) WL end From 9d2448ca6d2103bb6c6bae5ac8a980ccda10f77b Mon Sep 17 00:00:00 2001 From: Peter Tseng Date: Tue, 8 Nov 2016 10:57:27 -0800 Subject: [PATCH 12/14] dominoes: sha one line --- exercises/dominoes/dominoes_test.rb | 3 +-- exercises/dominoes/example.tt | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/exercises/dominoes/dominoes_test.rb b/exercises/dominoes/dominoes_test.rb index 705c617618..15a69e48b0 100755 --- a/exercises/dominoes/dominoes_test.rb +++ b/exercises/dominoes/dominoes_test.rb @@ -3,8 +3,7 @@ require 'minitest/autorun' require_relative 'dominoes' -# Test data version: -# 82eb00d +# Test data version: 82eb00d class DominoesTest < Minitest::Test def test_empty_input_empty_output # skip diff --git a/exercises/dominoes/example.tt b/exercises/dominoes/example.tt index 2adc81df1c..7d8cb25eef 100644 --- a/exercises/dominoes/example.tt +++ b/exercises/dominoes/example.tt @@ -3,8 +3,7 @@ gem 'minitest', '>= 5.0.0' require 'minitest/autorun' require_relative 'dominoes' -# Test data version: -# <%= sha1 %> +# Test data version: <%= sha1 %> class DominoesTest < Minitest::Test <% test_cases.each do |test_case| %> def <%= test_case.test_name %> From abd83dbf46885b7cc2908d4e33b7deea61255050 Mon Sep 17 00:00:00 2001 From: Peter Tseng Date: Tue, 8 Nov 2016 11:04:33 -0800 Subject: [PATCH 13/14] dominoes: de-duplicate example The candidates are the domino or the domino flipped. We can reverse to flip the domino! It's just a list! --- exercises/dominoes/example.rb | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/exercises/dominoes/example.rb b/exercises/dominoes/example.rb index 2b7b64eb9f..ac631a764c 100644 --- a/exercises/dominoes/example.rb +++ b/exercises/dominoes/example.rb @@ -13,18 +13,15 @@ def self.try_subchain(dominoes, chain_left, chain_right) dominoes.each_with_index { |domino, i| other_dominoes = dominoes.take(i) + dominoes.drop(i + 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 + # Try adding the domino either flipped or unflipped. + [domino, domino.reverse].each { |candidate| + domino_left, domino_right = candidate + if domino_left == chain_right + if (subchain = try_subchain(other_dominoes, chain_left, domino_right)) + return [candidate] + subchain + end 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. From eabc138eca45548fffcd547969b2b79777934c8f Mon Sep 17 00:00:00 2001 From: Peter Tseng Date: Tue, 8 Nov 2016 11:50:16 -0800 Subject: [PATCH 14/14] dominoes: Explain why we have the asserts, not examples --- exercises/dominoes/dominoes_test.rb | 4 ++++ exercises/dominoes/example.tt | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/exercises/dominoes/dominoes_test.rb b/exercises/dominoes/dominoes_test.rb index 15a69e48b0..fcb06def6e 100755 --- a/exercises/dominoes/dominoes_test.rb +++ b/exercises/dominoes/dominoes_test.rb @@ -110,6 +110,10 @@ def test_bookkeeping assert_equal 1, BookKeeping::VERSION end + # It's infeasible to use example-based tests for this exercise, + # because the list of acceptable answers for a given input can be quite large. + # Instead, we verify certain properties of a correct chain. + def assert_correct_chain(input_dominoes, output_chain) refute_nil output_chain, "There should be a chain for #{input_dominoes}" assert_same_dominoes(input_dominoes, output_chain) diff --git a/exercises/dominoes/example.tt b/exercises/dominoes/example.tt index 7d8cb25eef..086203bd6f 100644 --- a/exercises/dominoes/example.tt +++ b/exercises/dominoes/example.tt @@ -18,6 +18,10 @@ class DominoesTest < Minitest::Test assert_equal <%= version.next %>, BookKeeping::VERSION end + # It's infeasible to use example-based tests for this exercise, + # because the list of acceptable answers for a given input can be quite large. + # Instead, we verify certain properties of a correct chain. + def assert_correct_chain(input_dominoes, output_chain) refute_nil output_chain, "There should be a chain for #{input_dominoes}" assert_same_dominoes(input_dominoes, output_chain)