diff --git a/bin/generate-run-length-encoding b/bin/generate-run-length-encoding new file mode 100755 index 0000000000..56dcca121c --- /dev/null +++ b/bin/generate-run-length-encoding @@ -0,0 +1,6 @@ +#!/usr/bin/env ruby + +require_relative '../lib/generator' +require_relative '../lib/run_length_encoding_cases' + +Generator.new('run-length-encoding', RunLengthEncodingCases).generate diff --git a/config.json b/config.json index b5d7cc2ad4..e0f9bc0348 100644 --- a/config.json +++ b/config.json @@ -19,6 +19,7 @@ "word-count", "bob", "food-chain", + "run-length-encoding", "sieve", "binary", "accumulate", diff --git a/exercises/run-length-encoding/.version b/exercises/run-length-encoding/.version new file mode 100644 index 0000000000..56a6051ca2 --- /dev/null +++ b/exercises/run-length-encoding/.version @@ -0,0 +1 @@ +1 \ No newline at end of file diff --git a/exercises/run-length-encoding/example.rb b/exercises/run-length-encoding/example.rb new file mode 100644 index 0000000000..bde2e01429 --- /dev/null +++ b/exercises/run-length-encoding/example.rb @@ -0,0 +1,32 @@ +class RunLengthEncoding + VERSION = 1 + + def self.encode(str) + str.chars.chunk { |char| char }.each_with_object('') do |chunk, out| + out << encoded(chunk) + end + end + + def self.decode(str) + str.scan(/(\d+)?(\D)/).each_with_object('') do |captures, out| + out << decoded(captures) + end + end + + # private + + def self.encoded(chunk) + char = chunk.first + times = chunk.last.count + return char if times == 1 + "#{times}#{char}" + end + private_class_method :encoded + + def self.decoded(captures) + times = (captures.first || 1).to_i + char = captures.last + char * times + end + private_class_method :decoded +end diff --git a/exercises/run-length-encoding/example.tt b/exercises/run-length-encoding/example.tt new file mode 100644 index 0000000000..62efd5e157 --- /dev/null +++ b/exercises/run-length-encoding/example.tt @@ -0,0 +1,29 @@ +#!/usr/bin/env ruby +# encoding: utf-8 +gem 'minitest', '>= 5.0.0' +require 'minitest/autorun' +require_relative 'run_length_encoding' + +# Test data version: +# <%= sha1 %> +class RunLengthEncodingTest < Minitest::Test<% test_cases.each do |test_case| %> + def <%= test_case.name %><% if test_case.skipped? %> + skip<% end %> + <%= test_case.assign_input %> + <%= test_case.assign_output %> + <%= test_case.assertion %> + end +<% 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 RunLengthEncoding. + # 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 <%= version+1 %>, RunLengthEncoding::VERSION + end +end diff --git a/exercises/run-length-encoding/run_length_encoding_test.rb b/exercises/run-length-encoding/run_length_encoding_test.rb new file mode 100755 index 0000000000..d798dd4e24 --- /dev/null +++ b/exercises/run-length-encoding/run_length_encoding_test.rb @@ -0,0 +1,72 @@ +#!/usr/bin/env ruby +# encoding: utf-8 +gem 'minitest', '>= 5.0.0' +require 'minitest/autorun' +require_relative 'run_length_encoding' + +# Test data version: +# eb8d142 Merge pull request #220 from IanWhitney/sieve_ordering + +class RunLengthEncodingTest < Minitest::Test + def test_encode_simple + input = 'AABBBCCCC' + output = '2A3B4C' + assert_equal output, RunLengthEncoding.encode(input) + end + + def test_decode_simple + skip + input = '2A3B4C' + output = 'AABBBCCCC' + assert_equal output, RunLengthEncoding.decode(input) + end + + def test_encode_with_single_values + skip + input = 'WWWWWWWWWWWWBWWWWWWWWWWWWBBBWWWWWWWWWWWWWWWWWWWWWWWWB' + output = '12WB12W3B24WB' + assert_equal output, RunLengthEncoding.encode(input) + end + + def test_decode_with_single_values + skip + input = '12WB12W3B24WB' + output = 'WWWWWWWWWWWWBWWWWWWWWWWWWBBBWWWWWWWWWWWWWWWWWWWWWWWWB' + assert_equal output, RunLengthEncoding.decode(input) + end + + def test_decode_encode_combination + skip + input = 'zzz ZZ zZ' + output = 'zzz ZZ zZ' + assert_equal output, + RunLengthEncoding.decode(RunLengthEncoding.encode(input)) + end + + def test_encode_unicode + skip + input = '⏰⚽⚽⚽⭐⭐⏰' + output = '⏰3⚽2⭐⏰' + assert_equal output, RunLengthEncoding.encode(input) + end + + def test_decode_unicode + skip + input = '⏰3⚽2⭐⏰' + output = '⏰⚽⚽⚽⭐⭐⏰' + assert_equal output, RunLengthEncoding.decode(input) + 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 RunLengthEncoding. + # 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, RunLengthEncoding::VERSION + end +end diff --git a/lib/run_length_encoding_cases.rb b/lib/run_length_encoding_cases.rb new file mode 100644 index 0000000000..32ce39d763 --- /dev/null +++ b/lib/run_length_encoding_cases.rb @@ -0,0 +1,41 @@ +class RunLengthEncodingCase < OpenStruct + def name + 'test_%s' % cleaned_description + end + + def assign_input + "input = '#{input}'" + end + + def assign_output + "output = '#{expected}'" + end + + def assertion + case description + when /decode.+encode/ + 'assert_equal output, + RunLengthEncoding.decode(RunLengthEncoding.encode(input))' + when /encode/ + 'assert_equal output, RunLengthEncoding.encode(input)' + when /decode/ + 'assert_equal output, RunLengthEncoding.decode(input)' + end + end + + def skipped? + index > 0 + end + + # internal + + def cleaned_description + description.gsub(/\W+/, '_').squeeze('_') + end +end + +RunLengthEncodingCases = proc do |data| + JSON.parse(data)['cases'].map.with_index do |row, i| + RunLengthEncodingCase.new(row.merge('index' => i)) + end +end