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 100644 index 0000000000..3ca5956f65 --- /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_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 diff --git a/exercises/dominoes/example.rb b/exercises/dominoes/example.rb new file mode 100644 index 0000000000..c67f4d1864 --- /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[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 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..f7e83897af --- /dev/null +++ b/lib/dominoes_cases.rb @@ -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