From b702c214d87608b709e1b68815910433f742f1cb Mon Sep 17 00:00:00 2001 From: Simon Jakobi Date: Tue, 18 Mar 2014 18:12:21 +0100 Subject: [PATCH 01/52] New exercise: python series --- EXERCISES.txt | 1 + series/example.py | 11 +++++++++++ series/series_test.py | 44 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+) create mode 100644 series/example.py create mode 100644 series/series_test.py diff --git a/EXERCISES.txt b/EXERCISES.txt index 2347f307651..8aa88af667a 100644 --- a/EXERCISES.txt +++ b/EXERCISES.txt @@ -5,6 +5,7 @@ word-count anagram beer-song nucleotide-count +series point-mutations phone-number grade-school diff --git a/series/example.py b/series/example.py new file mode 100644 index 00000000000..44ebaaabc61 --- /dev/null +++ b/series/example.py @@ -0,0 +1,11 @@ +class Series(object): + def __init__(self, digits): + self.digits = digits + self.numbers = [int(d) for d in digits] + + def slices(self, length): + if not 1 <= length <= len(self.numbers): + raise ValueError("Invalid slice length for this series: " + + str(length)) + return [self.numbers[i:i + length] + for i in range(len(self.numbers) - length + 1)] diff --git a/series/series_test.py b/series/series_test.py new file mode 100644 index 00000000000..e32696a0ba6 --- /dev/null +++ b/series/series_test.py @@ -0,0 +1,44 @@ +try: + from series import Series +except ImportError: + raise SystemExit('Could not find series.py. Does it exist?') + +import unittest + + +class SeriesTest(unittest.TestCase): + def test_slices_of_one(self): + self.assertEqual([[0], [1], [2], [3], [4]], + Series("01234").slices(1)) + + def test_slices_of_two(self): + self.assertEqual([[9, 7], [7, 8], [8, 6], [6, 7], + [7, 5], [5, 6], [6, 4]], + Series("97867564").slices(2)) + + def test_slices_of_three(self): + self.assertEqual([[9, 7, 8], [7, 8, 6], [8, 6, 7], + [6, 7, 5], [7, 5, 6], [5, 6, 4]], + Series("97867564").slices(3)) + + def test_slices_of_four(self): + self.assertEqual([[0, 1, 2, 3], [1, 2, 3, 4]], + Series("01234").slices(4)) + + def test_slices_of_five(self): + self.assertEqual([[0, 1, 2, 3, 4]], + Series("01234").slices(5)) + + def test_overly_long_slice(self): + self.assertRaisesRegexp(ValueError, + "^Invalid slice length for this series: 4$", + Series("012").slices, 4) + + def test_overly_short_slice(self): + self.assertRaisesRegexp(ValueError, + "^Invalid slice length for this series: 0$", + Series("01234").slices, 0) + + +if __name__ == '__main__': + unittest.main() From 36d2dd996bb35fcfe2ed8af6b5f3e181febee5fe Mon Sep 17 00:00:00 2001 From: Simon Jakobi Date: Tue, 18 Mar 2014 09:00:03 +0100 Subject: [PATCH 02/52] New exercise: python octal --- EXERCISES.txt | 1 + octal/example.py | 13 +++++++++++++ octal/octal_test.py | 45 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+) create mode 100644 octal/example.py create mode 100644 octal/octal_test.py diff --git a/EXERCISES.txt b/EXERCISES.txt index 8aa88af667a..7a9f8096191 100644 --- a/EXERCISES.txt +++ b/EXERCISES.txt @@ -6,6 +6,7 @@ anagram beer-song nucleotide-count series +octal point-mutations phone-number grade-school diff --git a/octal/example.py b/octal/example.py new file mode 100644 index 00000000000..b30b46b5c34 --- /dev/null +++ b/octal/example.py @@ -0,0 +1,13 @@ +class Octal(object): + def __init__(self, octal_string): + self.digits = self.__validate(octal_string) + + def __validate(self, s): + for char in s: + if not '0' <= char < '8': + raise ValueError("Invalid octal digit: " + char) + return s + + def to_decimal(self): + return sum(int(digit) * 8 ** i + for (i, digit) in enumerate(reversed(self.digits))) diff --git a/octal/octal_test.py b/octal/octal_test.py new file mode 100644 index 00000000000..1273a318f25 --- /dev/null +++ b/octal/octal_test.py @@ -0,0 +1,45 @@ +try: + from octal import Octal +except ImportError: + raise SystemExit('Could not find octal.py. Does it exist?') + +import unittest + + +class OctalTest(unittest.TestCase): + def test_octal_1_is_decimal_1(self): + self.assertEqual(1, Octal("1").to_decimal()) + + def test_octal_10_is_decimal_8(self): + self.assertEqual(8, Octal("10").to_decimal()) + + def test_octal_17_is_decimal_15(self): + self.assertEqual(15, Octal("17").to_decimal()) + + def test_octal_130_is_decimal_88(self): + self.assertEqual(88, Octal("130").to_decimal()) + + def test_octal_2047_is_decimal_1063(self): + self.assertEqual(1063, Octal("2047").to_decimal()) + + def test_octal_1234567_is_decimal_342391(self): + self.assertEqual(342391, Octal("1234567").to_decimal()) + + def test_8_is_seen_as_invalid(self): + self.assertRaisesRegexp(ValueError, "^Invalid octal digit: 8$", + Octal, "8") + + def test_invalid_octal_is_recognized(self): + self.assertRaisesRegexp(ValueError, "^Invalid octal digit: c$", + Octal, "carrot") + + def test_6789_is_seen_as_invalid(self): + self.assertRaisesRegexp(ValueError, "^Invalid octal digit: 8$", + Octal, "6789") + + def test_valid_octal_formatted_string_011_is_decimal_9(self): + self.assertEqual(9, Octal("011").to_decimal()) + + +if __name__ == '__main__': + unittest.main() From 4ad8c8a32a444e817bbe85eeb0fadc15257551a0 Mon Sep 17 00:00:00 2001 From: Simon Jakobi Date: Tue, 18 Mar 2014 20:46:15 +0100 Subject: [PATCH 03/52] New exercise: python difference-of-squares --- EXERCISES.txt | 1 + .../difference_of_squares.py | 11 +++++++ .../difference_of_squares_test.py | 31 +++++++++++++++++++ 3 files changed, 43 insertions(+) create mode 100644 difference-of-squares/difference_of_squares.py create mode 100644 difference-of-squares/difference_of_squares_test.py diff --git a/EXERCISES.txt b/EXERCISES.txt index 7a9f8096191..cecbe4169f3 100644 --- a/EXERCISES.txt +++ b/EXERCISES.txt @@ -7,6 +7,7 @@ beer-song nucleotide-count series octal +difference-of-squares point-mutations phone-number grade-school diff --git a/difference-of-squares/difference_of_squares.py b/difference-of-squares/difference_of_squares.py new file mode 100644 index 00000000000..b2944ecf111 --- /dev/null +++ b/difference-of-squares/difference_of_squares.py @@ -0,0 +1,11 @@ +def square_of_sum(n): + sum_ = n * (n + 1) / 2 + return sum_ * sum_ + + +def sum_of_squares(n): + return sum(m * m for m in range(n + 1)) + + +def difference(n): + return square_of_sum(n) - sum_of_squares(n) diff --git a/difference-of-squares/difference_of_squares_test.py b/difference-of-squares/difference_of_squares_test.py new file mode 100644 index 00000000000..728ff4c6f69 --- /dev/null +++ b/difference-of-squares/difference_of_squares_test.py @@ -0,0 +1,31 @@ +try: + from difference_of_squares import difference, square_of_sum, sum_of_squares +except ImportError: + raise SystemExit('Could not find difference_of_squares.py. Does it exist?') + +import unittest + + +class DifferenceOfSquaresTest(unittest.TestCase): + + def test_square_of_sum_5(self): + self.assertEqual(225, square_of_sum(5)) + + def test_sum_of_squares_5(self): + self.assertEqual(55, sum_of_squares(5)) + + def test_difference_5(self): + self.assertEqual(170, difference(5)) + + def test_square_of_sum_100(self): + self.assertEqual(25502500, square_of_sum(100)) + + def test_sum_of_squares_100(self): + self.assertEqual(338350, sum_of_squares(100)) + + def test_difference_100(self): + self.assertEqual(25164150, difference(100)) + + +if __name__ == '__main__': + unittest.main() From 319fb2a637748c4327b5430427850b37805fa2e9 Mon Sep 17 00:00:00 2001 From: Katrina Owen Date: Tue, 18 Mar 2014 18:44:48 -0400 Subject: [PATCH 04/52] Rename difference-of-squares example implementation --- difference-of-squares/{difference_of_squares.py => example.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename difference-of-squares/{difference_of_squares.py => example.py} (100%) diff --git a/difference-of-squares/difference_of_squares.py b/difference-of-squares/example.py similarity index 100% rename from difference-of-squares/difference_of_squares.py rename to difference-of-squares/example.py From 9617279674d40e2e57f85e2ab65f09511f5d7314 Mon Sep 17 00:00:00 2001 From: Simon Jakobi Date: Fri, 21 Mar 2014 21:08:28 +0100 Subject: [PATCH 05/52] New exercise: python luhn --- EXERCISES.txt | 1 + luhn/example.py | 20 ++++++++++++++++++++ luhn/luhn_test.py | 39 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 60 insertions(+) create mode 100644 luhn/example.py create mode 100644 luhn/luhn_test.py diff --git a/EXERCISES.txt b/EXERCISES.txt index 2347f307651..4a41d460bf7 100644 --- a/EXERCISES.txt +++ b/EXERCISES.txt @@ -18,6 +18,7 @@ gigasecond triangle scrabble-score sieve +luhn roman-numerals binary prime-factors diff --git a/luhn/example.py b/luhn/example.py new file mode 100644 index 00000000000..5d061b568d9 --- /dev/null +++ b/luhn/example.py @@ -0,0 +1,20 @@ +class Luhn(object): + def __init__(self, number): + self.number = number + + def addends(self): + old_digits = [int(d) for d in str(self.number)] + luhn_transform = lambda n: (2 * n - 9) if (n > 4) else (2 * n) + return [(luhn_transform(n) if (i % 2 == 0) else n) + for i, n in enumerate(old_digits, start=len(old_digits) % 2)] + + def checksum(self): + return sum(self.addends()) % 10 + + def is_valid(self): + return self.checksum() == 0 + + @staticmethod + def create(n): + diff = (10 - Luhn(n * 10).checksum()) % 10 + return 10 * n + diff diff --git a/luhn/luhn_test.py b/luhn/luhn_test.py new file mode 100644 index 00000000000..994ff1014b7 --- /dev/null +++ b/luhn/luhn_test.py @@ -0,0 +1,39 @@ +try: + from luhn import Luhn +except ImportError: + raise SystemExit('Could not find luhn.py. Does it exist?') + +import unittest + + +class LuhnTests(unittest.TestCase): + def test_addends(self): + self.assertEqual([1, 4, 1, 4, 1], Luhn(12121).addends()) + + def test_addends_large(self): + self.assertEqual([7, 6, 6, 1], Luhn(8631).addends()) + + def test_checksum1(self): + self.assertEqual(2, Luhn(4913).checksum()) + + def test_ckecksum2(self): + self.assertEqual(1, Luhn(201773).checksum()) + + def test_invalid_number(self): + self.assertFalse(Luhn(738).is_valid()) + + def test_valid_number(self): + self.assertTrue(Luhn(8739567).is_valid()) + + def test_create_valid_number1(self): + self.assertEqual(1230, Luhn.create(123)) + + def test_create_valid_number2(self): + self.assertEqual(8739567, Luhn.create(873956)) + + def test_create_valid_number3(self): + self.assertEqual(8372637564, Luhn.create(837263756)) + + +if __name__ == '__main__': + unittest.main() From 0f8b13dcf6188d77f78542a557308644a7cbe5ad Mon Sep 17 00:00:00 2001 From: Simon Jakobi Date: Sat, 22 Mar 2014 05:42:45 +0100 Subject: [PATCH 06/52] New exercise: python palindrome-products --- EXERCISES.txt | 1 + palindrome-products/example.py | 18 +++++++ .../palindrome_products_test.py | 50 +++++++++++++++++++ 3 files changed, 69 insertions(+) create mode 100644 palindrome-products/example.py create mode 100644 palindrome-products/palindrome_products_test.py diff --git a/EXERCISES.txt b/EXERCISES.txt index b00b09fc8dd..3c5f5923ea1 100644 --- a/EXERCISES.txt +++ b/EXERCISES.txt @@ -18,6 +18,7 @@ sum-of-multiples space-age grains gigasecond +palindrome-products triangle scrabble-score sieve diff --git a/palindrome-products/example.py b/palindrome-products/example.py new file mode 100644 index 00000000000..6d209b7f0fb --- /dev/null +++ b/palindrome-products/example.py @@ -0,0 +1,18 @@ +def largest_palindrome(max_factor, min_factor=0): + return max(palindromes(max_factor, min_factor), key=lambda tup: tup[0]) + + +def smallest_palindrome(max_factor, min_factor): + return min(palindromes(max_factor, min_factor), key=lambda tup: tup[0]) + + +def palindromes(max_factor, min_factor): + return ((a * b, (a, b)) + for a in range(min_factor, max_factor + 1) + for b in range(min_factor, a + 1) + if is_palindrome(a * b)) + + +def is_palindrome(n): + s = str(n) + return s == s[::-1] diff --git a/palindrome-products/palindrome_products_test.py b/palindrome-products/palindrome_products_test.py new file mode 100644 index 00000000000..9927e5323ee --- /dev/null +++ b/palindrome-products/palindrome_products_test.py @@ -0,0 +1,50 @@ +""" +Notes regarding the implementation of smallest_palindrome and +largest_palindrome: + +Both functions must take two keyword arguments: + max_factor -- int + min_factor -- int, default 0 + +Their return value must be a tuple (value, factors) where value is the +palindrome itself, and factors is an iterable containing both factors of the +palindrome in arbitrary order. +""" + +try: + from palindrome import smallest_palindrome, largest_palindrome +except ImportError: + raise SystemExit('Could not find palindrome.py. Does it exist?') + +import unittest + + +class PalindromesTests(unittest.TestCase): + def test_largest_palindrome_from_single_digit_factors(self): + value, factors = largest_palindrome(max_factor=9) + self.assertEqual(9, value) + self.assertIn(set(factors), [{1, 9}, {3, 3}]) + + def test_largest_palindrome_from_double_digit_factors(self): + value, factors = largest_palindrome(max_factor=99, min_factor=10) + self.assertEqual(9009, value) + self.assertEqual({91, 99}, set(factors)) + + def test_smallest_palindrome_from_double_digit_factors(self): + value, factors = smallest_palindrome(max_factor=99, min_factor=10) + self.assertEqual(121, value) + self.assertEqual({11}, set(factors)) + + def test_largest_palindrome_from_triple_digit_factors(self): + value, factors = largest_palindrome(max_factor=999, min_factor=100) + self.assertEqual(906609, value) + self.assertEqual({913, 993}, set(factors)) + + def test_smallest_palindrome_from_triple_digit_factors(self): + value, factors = smallest_palindrome(max_factor=999, min_factor=100) + self.assertEqual(10201, value) + self.assertEqual({101, 101}, set(factors)) + + +if __name__ == '__main__': + unittest.main() From b3a141753f9331306704818c1b7b806ae8836834 Mon Sep 17 00:00:00 2001 From: Simon Jakobi Date: Sat, 22 Mar 2014 06:58:39 +0100 Subject: [PATCH 07/52] New exercise: python nth-prime --- EXERCISES.txt | 1 + nth-prime/example.py | 31 +++++++++++++++++++++++++++++++ nth-prime/nth_prime_test.py | 25 +++++++++++++++++++++++++ 3 files changed, 57 insertions(+) create mode 100644 nth-prime/example.py create mode 100644 nth-prime/nth_prime_test.py diff --git a/EXERCISES.txt b/EXERCISES.txt index b00b09fc8dd..d7b2f3f76a6 100644 --- a/EXERCISES.txt +++ b/EXERCISES.txt @@ -21,6 +21,7 @@ gigasecond triangle scrabble-score sieve +nth-prime luhn roman-numerals binary diff --git a/nth-prime/example.py b/nth-prime/example.py new file mode 100644 index 00000000000..d997d8a29b1 --- /dev/null +++ b/nth-prime/example.py @@ -0,0 +1,31 @@ +from itertools import count +from math import sqrt + + +def nth_prime(n): + known = [] + candidates = prime_candidates() + + def is_prime(m): + sqrt_m = sqrt(m) + for k in known: + if k > sqrt_m: + return True + elif m % k == 0: + return False + return True + + while len(known) < n: + x = next(candidates) + if is_prime(x): + known.append(x) + + return known[n - 1] + + +def prime_candidates(): + yield 2 + yield 3 + for n in count(6, 6): + yield n - 1 + yield n + 1 diff --git a/nth-prime/nth_prime_test.py b/nth-prime/nth_prime_test.py new file mode 100644 index 00000000000..761e07ce105 --- /dev/null +++ b/nth-prime/nth_prime_test.py @@ -0,0 +1,25 @@ +try: + from prime import nth_prime +except ImportError: + raise SystemExit('Could not find prime.py. Does it exist?') + +import unittest + + +class NthPrimeTests(unittest.TestCase): + def test_first_prime(self): + self.assertEqual(2, nth_prime(1)) + + def test_sixth_prime(self): + self.assertEqual(13, nth_prime(6)) + + def test_first_twenty_primes(self): + self.assertEqual([2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71], + [nth_prime(n) for n in range(1, 21)]) + + def test_prime_no_10000(self): + self.assertEqual(104729, nth_prime(10000)) + + +if __name__ == '__main__': + unittest.main() From c51ea3f8ec1522fc8aab66345f2c114267658d2e Mon Sep 17 00:00:00 2001 From: Simon Jakobi Date: Sat, 22 Mar 2014 08:26:43 +0100 Subject: [PATCH 08/52] New exercise: python largest-series-product --- EXERCISES.txt | 1 + largest-series-product/example.py | 19 ++++++++ .../largest_series_product_test.py | 44 +++++++++++++++++++ 3 files changed, 64 insertions(+) create mode 100644 largest-series-product/example.py create mode 100644 largest-series-product/largest_series_product_test.py diff --git a/EXERCISES.txt b/EXERCISES.txt index 3cfb0fb25f5..8f78cf1ea2e 100644 --- a/EXERCISES.txt +++ b/EXERCISES.txt @@ -6,6 +6,7 @@ anagram beer-song nucleotide-count series +largest-series-product octal difference-of-squares point-mutations diff --git a/largest-series-product/example.py b/largest-series-product/example.py new file mode 100644 index 00000000000..daa4cde478f --- /dev/null +++ b/largest-series-product/example.py @@ -0,0 +1,19 @@ +from functools import reduce +from operator import mul + + +class Series(object): + def __init__(self, number_string): + self.digits = [int(d) for d in number_string] + + def slices(self, length): + if not 1 <= length <= len(self.digits): + raise ValueError("Invalid slice length for this series: " + + str(length)) + return [self.digits[i:i + length] + for i in range(len(self.digits) - length + 1)] + + def largest_product(self, length): + if length == 0: + return 1 + return max(reduce(mul, slc) for slc in self.slices(length)) diff --git a/largest-series-product/largest_series_product_test.py b/largest-series-product/largest_series_product_test.py new file mode 100644 index 00000000000..474dfea6d86 --- /dev/null +++ b/largest-series-product/largest_series_product_test.py @@ -0,0 +1,44 @@ +try: + from series import Series +except ImportError: + raise SystemExit('Could not find series.py. Does it exist?') + +import unittest + + +class SeriesTest(unittest.TestCase): + def test_slices_of_two(self): + self.assertEqual([[9, 7], [7, 8], [8, 6], [6, 7], + [7, 5], [5, 6], [6, 4]], + Series("97867564").slices(2)) + + def test_overly_long_slice(self): + self.assertRaisesRegexp(ValueError, + "^Invalid slice length for this series: 4$", + Series("012").slices, 4) + + def test_largest_product_of_2(self): + self.assertEqual(72, Series("0123456789").largest_product(2)) + + def test_tiny_number(self): + self.assertEqual(9, Series("19").largest_product(2)) + + def test_largest_product_of_3(self): + self.assertEqual(270, Series("1027839564").largest_product(3)) + + def test_big_number(self): + self.assertEqual(28350, + Series("52677741234314237566414902593461595376319419" + "139427").largest_product(6)) + + def test_identity(self): + self.assertEqual(1, Series("").largest_product(0)) + + def test_slices_bigger_than_number(self): + self.assertRaisesRegexp(ValueError, + "^Invalid slice length for this series: 4$", + Series("012").largest_product, 4) + + +if __name__ == '__main__': + unittest.main() From b3da077bd688d1846d683a91a252645e63d5dedb Mon Sep 17 00:00:00 2001 From: Simon Jakobi Date: Sun, 23 Mar 2014 09:17:40 +0100 Subject: [PATCH 09/52] New exercise: python twelve-days. --- EXERCISES.txt | 1 + twelve-days/example.py | 33 +++++++++++++++ twelve-days/twelve_days_test.py | 71 +++++++++++++++++++++++++++++++++ 3 files changed, 105 insertions(+) create mode 100644 twelve-days/example.py create mode 100644 twelve-days/twelve_days_test.py diff --git a/EXERCISES.txt b/EXERCISES.txt index 3c5f5923ea1..c27e911ee2b 100644 --- a/EXERCISES.txt +++ b/EXERCISES.txt @@ -2,6 +2,7 @@ bob rna-transcription matrix word-count +twelve-days anagram beer-song nucleotide-count diff --git a/twelve-days/example.py b/twelve-days/example.py new file mode 100644 index 00000000000..029a142750c --- /dev/null +++ b/twelve-days/example.py @@ -0,0 +1,33 @@ +GIFTS = ['twelve Drummers Drumming', + 'eleven Pipers Piping', + 'ten Lords-a-Leaping', + 'nine Ladies Dancing', + 'eight Maids-a-Milking', + 'seven Swans-a-Swimming', + 'six Geese-a-Laying', + 'five Gold Rings', + 'four Calling Birds', + 'three French Hens', + 'two Turtle Doves', + 'a Partridge in a Pear Tree'] + +ORDINAL = [None, 'first', 'second', 'third', 'fourth', 'fifth', 'sixth', + 'seventh', 'eighth', 'ninth', 'tenth', 'eleventh', 'twelfth'] + + +def verse(n): + gifts = GIFTS[-n:] + if len(gifts) > 1: + gifts[:-1] = [', '.join(gifts[:-1])] + gifts = ', and '.join(gifts) + return 'On the {} day of Christmas my true love gave to me, {}.\n'.format( + ORDINAL[n], gifts) + + +def verses(start, end): + return ''.join([verse(n) + '\n' + for n in range(start, end + 1)]) + + +def sing(): + return verses(1, 12) diff --git a/twelve-days/twelve_days_test.py b/twelve-days/twelve_days_test.py new file mode 100644 index 00000000000..d60ce28e936 --- /dev/null +++ b/twelve-days/twelve_days_test.py @@ -0,0 +1,71 @@ +try: + from twelve_days import sing, verse, verses +except ImportError: + raise SystemExit('Could not find twelve_days.py. Does it exist?') + +import unittest + + +class TwelveDaysTests(unittest.TestCase): + + def test_verse1(self): + expected = "On the first day of Christmas my true love gave to me, a Partridge in a Pear Tree.\n" + self.assertEqual(expected, verse(1)) + + def test_verse2(self): + expected = "On the second day of Christmas my true love gave to me, two Turtle Doves, and a Partridge in a Pear Tree.\n" + self.assertEqual(expected, verse(2)) + + def test_verse3(self): + expected = "On the third day of Christmas my true love gave to me, three French Hens, two Turtle Doves, and a Partridge in a Pear Tree.\n" + self.assertEqual(expected, verse(3)) + + def test_verse4(self): + expected = "On the fourth day of Christmas my true love gave to me, four Calling Birds, three French Hens, two Turtle Doves, and a Partridge in a Pear Tree.\n" + self.assertEqual(expected, verse(4)) + + def test_verse5(self): + expected = "On the fifth day of Christmas my true love gave to me, five Gold Rings, four Calling Birds, three French Hens, two Turtle Doves, and a Partridge in a Pear Tree.\n" + self.assertEqual(expected, verse(5)) + + def test_verse6(self): + expected = "On the sixth day of Christmas my true love gave to me, six Geese-a-Laying, five Gold Rings, four Calling Birds, three French Hens, two Turtle Doves, and a Partridge in a Pear Tree.\n" + self.assertEqual(expected, verse(6)) + + def test_verse7(self): + expected = "On the seventh day of Christmas my true love gave to me, seven Swans-a-Swimming, six Geese-a-Laying, five Gold Rings, four Calling Birds, three French Hens, two Turtle Doves, and a Partridge in a Pear Tree.\n" + self.assertEqual(expected, verse(7)) + + def test_verse8(self): + expected = "On the eighth day of Christmas my true love gave to me, eight Maids-a-Milking, seven Swans-a-Swimming, six Geese-a-Laying, five Gold Rings, four Calling Birds, three French Hens, two Turtle Doves, and a Partridge in a Pear Tree.\n" + self.assertEqual(expected, verse(8)) + + def test_verse9(self): + expected = "On the ninth day of Christmas my true love gave to me, nine Ladies Dancing, eight Maids-a-Milking, seven Swans-a-Swimming, six Geese-a-Laying, five Gold Rings, four Calling Birds, three French Hens, two Turtle Doves, and a Partridge in a Pear Tree.\n" + self.assertEqual(expected, verse(9)) + + def test_verse10(self): + expected = "On the tenth day of Christmas my true love gave to me, ten Lords-a-Leaping, nine Ladies Dancing, eight Maids-a-Milking, seven Swans-a-Swimming, six Geese-a-Laying, five Gold Rings, four Calling Birds, three French Hens, two Turtle Doves, and a Partridge in a Pear Tree.\n" + self.assertEqual(expected, verse(10)) + + def test_verse11(self): + expected = "On the eleventh day of Christmas my true love gave to me, eleven Pipers Piping, ten Lords-a-Leaping, nine Ladies Dancing, eight Maids-a-Milking, seven Swans-a-Swimming, six Geese-a-Laying, five Gold Rings, four Calling Birds, three French Hens, two Turtle Doves, and a Partridge in a Pear Tree.\n" + self.assertEqual(expected, verse(11)) + + def test_verse12(self): + expected = "On the twelfth day of Christmas my true love gave to me, twelve Drummers Drumming, eleven Pipers Piping, ten Lords-a-Leaping, nine Ladies Dancing, eight Maids-a-Milking, seven Swans-a-Swimming, six Geese-a-Laying, five Gold Rings, four Calling Birds, three French Hens, two Turtle Doves, and a Partridge in a Pear Tree.\n" + self.assertEqual(expected, verse(12)) + + def test_multiple_verses(self): + expected = ( + "On the first day of Christmas my true love gave to me, a Partridge in a Pear Tree.\n\n" + + "On the second day of Christmas my true love gave to me, two Turtle Doves, and a Partridge in a Pear Tree.\n\n" + + "On the third day of Christmas my true love gave to me, three French Hens, two Turtle Doves, and a Partridge in a Pear Tree.\n\n") + self.assertEqual(expected, verses(1, 3)) + + def test_the_whole_song(self): + self.assertEqual(verses(1, 12), sing()) + + +if __name__ == '__main__': + unittest.main() From a1e5258523d730f045266385e41d2dedfdeacd4c Mon Sep 17 00:00:00 2001 From: Simon Jakobi Date: Wed, 26 Mar 2014 14:12:41 +0100 Subject: [PATCH 10/52] New exercise: python allergies --- EXERCISES.txt | 1 + allergies/allergies_test.py | 43 +++++++++++++++++++++++++++++++++++++ allergies/example.py | 22 +++++++++++++++++++ 3 files changed, 66 insertions(+) create mode 100644 allergies/allergies_test.py create mode 100644 allergies/example.py diff --git a/EXERCISES.txt b/EXERCISES.txt index 2af8253f41a..82394ea421d 100644 --- a/EXERCISES.txt +++ b/EXERCISES.txt @@ -12,6 +12,7 @@ octal difference-of-squares point-mutations phone-number +allergies grade-school robot-name etl diff --git a/allergies/allergies_test.py b/allergies/allergies_test.py new file mode 100644 index 00000000000..c349e71d4ea --- /dev/null +++ b/allergies/allergies_test.py @@ -0,0 +1,43 @@ +try: + from allergies import Allergies +except ImportError: + raise SystemExit('Could not find allergies.py. Does it exist?') + +import unittest + + +class AllergiesTests(unittest.TestCase): + + def test_no_allergies_means_not_allergic(self): + allergies = Allergies(0) + self.assertFalse(allergies.is_allergic_to('peanuts')) + self.assertFalse(allergies.is_allergic_to('cats')) + self.assertFalse(allergies.is_allergic_to('strawberries')) + + def test_is_allergic_to_eggs(self): + self.assertTrue(Allergies(1).is_allergic_to('eggs')) + + def test_has_the_right_allergies(self): + allergies = Allergies(5) + self.assertTrue(allergies.is_allergic_to('eggs')) + self.assertTrue(allergies.is_allergic_to('shellfish')) + self.assertFalse(allergies.is_allergic_to('strawberries')) + + def test_no_allergies_at_all(self): + self.assertEqual([], Allergies(0).list) + + def test_allergic_to_just_peanuts(self): + self.assertEqual(['peanuts'], Allergies(2).list) + + def test_allergic_to_everything(self): + self.assertEqual( + ('eggs peanuts shellfish strawberries tomatoes ' + 'chocolate pollen cats').split(), + Allergies(255).list) + + def test_ignore_non_allergen_score_parts(self): + self.assertEqual(['eggs'], Allergies(257).list) + + +if __name__ == '__main__': + unittest.main() diff --git a/allergies/example.py b/allergies/example.py new file mode 100644 index 00000000000..37d779330e4 --- /dev/null +++ b/allergies/example.py @@ -0,0 +1,22 @@ +def powers_of_2(n): + """Return a list of the powers of 2 whose sum is the input number.""" + return [2 ** exponent + for exponent, bit in enumerate(bin(n)[:1:-1]) + if bit == '1'] + + +class Allergies(object): + + __allergens = {2 ** exponent: allergen + for exponent, allergen in enumerate( + ("eggs peanuts shellfish strawberries tomatoes " + "chocolate pollen cats").split())} + + def __init__(self, score): + self.score = score + self.list = [self.__allergens[p] + for p in powers_of_2(score) + if p in self.__allergens] + + def is_allergic_to(self, allergen): + return allergen in self.list From 78995b0a4509699659486dc4291f0277fbcf4770 Mon Sep 17 00:00:00 2001 From: Hermano Cabral Date: Thu, 27 Mar 2014 10:42:07 -0300 Subject: [PATCH 11/52] Add atbash-cipher exercise --- EXERCISES.txt | 1 + atbash-cipher/atbash_cipher_test.py | 45 +++++++++++++++++++++++++++++ atbash-cipher/example.py | 14 +++++++++ 3 files changed, 60 insertions(+) create mode 100644 atbash-cipher/atbash_cipher_test.py create mode 100644 atbash-cipher/example.py diff --git a/EXERCISES.txt b/EXERCISES.txt index 82394ea421d..e8df4e346b8 100644 --- a/EXERCISES.txt +++ b/EXERCISES.txt @@ -31,3 +31,4 @@ roman-numerals binary prime-factors raindrops +atbash-cipher diff --git a/atbash-cipher/atbash_cipher_test.py b/atbash-cipher/atbash_cipher_test.py new file mode 100644 index 00000000000..e42b3c90ac5 --- /dev/null +++ b/atbash-cipher/atbash_cipher_test.py @@ -0,0 +1,45 @@ +try: + from atbash_cipher import encode, decode +except ImportError: + raise SystemExit('Could not find atbash_cipher.py. Does it exist?') + +import unittest + + +class AtbashCipherTest(unittest.TestCase): + def test_encode_no(self): + self.assertEqual("ml", encode("no")) + + def test_encode_yes(self): + self.assertEqual("bvh", encode("yes")) + + def test_encode_OMG(self): + self.assertEqual("lnt", encode("OMG")) + + def test_encode_O_M_G(self): + self.assertEqual("lnt", encode("O M G")) + + def test_encode_long_word(self): + self.assertEqual("nrmwy oldrm tob", encode("mindblowingly")) + + def test_encode_numbers(self): + self.assertEqual("gvhgr mt123 gvhgr mt", + encode("Testing, 1 2 3, testing.")) + + def test_encode_sentence(self): + self.assertEqual("gifgs rhurx grlm", + encode("Truth is fiction.")) + + def test_encode_all_things(self): + plaintext = "The quick brown fox jumps over the lazy dog." + ciphertext = "gsvjf rxpyi ldmul cqfnk hlevi gsvoz abwlt" + self.assertEqual(ciphertext, encode(plaintext)) + + def test_decode_word(self): + self.assertEqual("exercism", decode("vcvix rhn")) + + def test_decode_sentence(self): + self.assertEqual("anobstacleisoftenasteppingstone", decode("zmlyh gzxov rhlug vmzhg vkkrm thglm v")) + +if __name__ == '__main__': + unittest.main() diff --git a/atbash-cipher/example.py b/atbash-cipher/example.py new file mode 100644 index 00000000000..021cd07d2bc --- /dev/null +++ b/atbash-cipher/example.py @@ -0,0 +1,14 @@ +from string import maketrans, lowercase, digits, punctuation, whitespace + +BLKSZ = 5 +trtbl = maketrans(lowercase+digits, "".join(reversed(lowercase))+digits) + +def base_trans(text): + return text.lower().translate(trtbl, punctuation+whitespace) + +def encode(plain): + cipher = base_trans(plain) + return " ".join([cipher[i:i+BLKSZ] for i in range(0,len(cipher),BLKSZ)]) + +def decode(ciphered): + return base_trans(ciphered) \ No newline at end of file From 6b37aff9b8ee4ba18cf4bcf5e182b9ebccefa9ea Mon Sep 17 00:00:00 2001 From: Hermano Cabral Date: Thu, 27 Mar 2014 16:31:41 -0300 Subject: [PATCH 12/52] Add wordy exercise --- EXERCISES.txt | 1 + wordy/example.py | 33 +++++++++++++++++ wordy/wordy_test.py | 90 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 124 insertions(+) create mode 100644 wordy/example.py create mode 100644 wordy/wordy_test.py diff --git a/EXERCISES.txt b/EXERCISES.txt index e8df4e346b8..1cadfb94899 100644 --- a/EXERCISES.txt +++ b/EXERCISES.txt @@ -32,3 +32,4 @@ binary prime-factors raindrops atbash-cipher +wordy diff --git a/wordy/example.py b/wordy/example.py new file mode 100644 index 00000000000..7e0fda310fb --- /dev/null +++ b/wordy/example.py @@ -0,0 +1,33 @@ +from operator import add, div, mul, sub + +VALID_OPERATIONS = {"plus": add, "minus": sub, "times": mul, + "multiplied by": mul, "divided by": div} + +def calculate(stmt): + if not (stmt.startswith("What is ") and stmt.endswith("?")): + raise ValueError("Ill-formed question") + l = stmt[8:-1].strip().lower().split() + if not l: + raise ValueError("Ill-formed question") + l.reverse() + try: + op1 = int(l.pop()) + except ValueError: + raise ValueError("Ill-formed question") + while l: + oprt = [l.pop()] + while l: + try: + next_tk = l.pop() + op2 = int(next_tk) + break + except ValueError: + oprt.append(next_tk) + else: + raise ValueError("Ill-formed question") + oprt = " ".join(oprt) + try: + op1 = VALID_OPERATIONS[oprt](op1, op2) + except KeyError: + raise ValueError("Ill-formed question") + return op1 \ No newline at end of file diff --git a/wordy/wordy_test.py b/wordy/wordy_test.py new file mode 100644 index 00000000000..0ddd0218d4e --- /dev/null +++ b/wordy/wordy_test.py @@ -0,0 +1,90 @@ +try: + from wordy import calculate +except ImportError: + raise SystemExit('Could not find wordy.py. Does it exist?') + +import unittest + +class WordyTest(unittest.TestCase): + def test_simple_add_1(self): + self.assertEqual(18, calculate("What is 5 plus 13?")) + + @unittest.skip("Not implemented yet") + def test_simple_add_2(self): + self.assertEqual(-8, calculate("What is 5 plus -13?")) + + @unittest.skip("Not implemented yet") + def test_simple_sub_1(self): + self.assertEqual(6, calculate("What is 103 minus 97?")) + + @unittest.skip("Not implemented yet") + def test_simple_sub_2(self): + self.assertEqual(-6, calculate("What is 97 minus 103?")) + + @unittest.skip("Not implemented yet") + def test_simple_mult(self): + self.assertEqual(21, calculate("What is 7 times 3?")) + + @unittest.skip("Not implemented yet") + def test_simple_div(self): + self.assertEqual(9, calculate("What is 45 divided by 5?")) + + @unittest.skip("Not implemented yet") + def test_add_negative_numbers(self): + self.assertEqual(-11, calculate("What is -1 plus -10?")) + + @unittest.skip("Not implemented yet") + def test_add_more_digits(self): + self.assertEqual(45801, calculate("What is 123 plus 45678?")) + + @unittest.skip("Not implemented yet") + def test_add_twice(self): + self.assertEqual(4, calculate("What is 1 plus 2 plus 1?")) + + @unittest.skip("Not implemented yet") + def test_add_then_subtract(self): + self.assertEqual(14, calculate("What is 1 plus 5 minus -8?")) + + @unittest.skip("Not implemented yet") + def test_subtract_twice(self): + self.assertEqual(-7, calculate("What is 20 minus 14 minus 13?")) + + @unittest.skip("Not implemented yet") + def test_multiply_twice(self): + self.assertEqual(-12, calculate("What is 2 multiplied by -2 multiplied by 3?")) + + @unittest.skip("Not implemented yet") + def test_add_then_multiply(self): + self.assertEqual(-8, calculate("What is -3 plus 7 multiplied by -2?")) + + @unittest.skip("Not implemented yet") + def test_divide_twice(self): + self.assertEqual(16, calculate("What is -12000 divided by 25 divided by -30?")) + + @unittest.skip("Not implemented yet") + def test_invalid_operation(self): + with self.assertRaises(ValueError) as context: + calculate("What is 4 xor 7?") + self.assertEqual(context.exception.message, 'Ill-formed question') + + @unittest.skip("Not implemented yet") + def test_missing_operation(self): + with self.assertRaises(ValueError) as context: + calculate("What is 2 2 minus 3?") + self.assertEqual(context.exception.message, 'Ill-formed question') + + @unittest.skip("Not implemented yet") + def test_missing_number(self): + with self.assertRaises(ValueError) as context: + calculate("What is 7 plus times -2?") + self.assertEqual(context.exception.message, 'Ill-formed question') + + @unittest.skip("Not implemented yet") + def test_irrelevant_question(self): + with self.assertRaises(ValueError) as context: + calculate("Which is greater, 3 or 2?") + self.assertEqual(context.exception.message, 'Ill-formed question') + + +if __name__ == '__main__': + unittest.main() From 55126db9e0d434873b766a63e0a01a1845a2776c Mon Sep 17 00:00:00 2001 From: Hermano Cabral Date: Fri, 28 Mar 2014 22:19:26 -0300 Subject: [PATCH 13/52] New exercise: minesweeper --- EXERCISES.txt | 1 + minesweeper/example.py | 36 +++++++++++++ minesweeper/minesweeper_test.py | 91 +++++++++++++++++++++++++++++++++ 3 files changed, 128 insertions(+) create mode 100644 minesweeper/example.py create mode 100644 minesweeper/minesweeper_test.py diff --git a/EXERCISES.txt b/EXERCISES.txt index 1cadfb94899..9c4d086c4c7 100644 --- a/EXERCISES.txt +++ b/EXERCISES.txt @@ -15,6 +15,7 @@ phone-number allergies grade-school robot-name +minesweeper etl leap sum-of-multiples diff --git a/minesweeper/example.py b/minesweeper/example.py new file mode 100644 index 00000000000..27f1559e2cb --- /dev/null +++ b/minesweeper/example.py @@ -0,0 +1,36 @@ +def board(inp): + verify_board(inp) + rowlen = len(inp[0]) + collen = len(inp) + b = [list(r) for r in inp] + for i1 in range(collen): + for i2 in range(rowlen): + if b[i1][i2] != ' ': + continue + cnt = inp[i1-1][i2-1:i2+2].count('*') + \ + inp[i1][i2-1:i2+2].count('*') + \ + inp[i1+1][i2-1:i2+2].count('*') + if cnt == 0: + continue + b[i1][i2] = str(cnt) + return ["".join(r) for r in b] + +def verify_board(inp): + # Null board or a null row + if not inp or not all(r for r in inp): + raise ValueError("Invalid board") + # Rows with different lengths + rowlen = len(inp[0]) + collen = len(inp) + if not all(len(r)==rowlen for r in inp): + raise ValueError("Invalid board") + # Unknown character in board + cset = set() + for r in inp: + cset.update(r) + if cset - set('+- *|'): + raise ValueError("Invalid board") + # Borders not as expected + if any(inp[i1] != '+'+'-'*(rowlen-2)+'+' for i1 in [0,-1]) or \ + any(inp[i1][i2] != '|' for i1 in range(1,collen-1) for i2 in [0,-1]): + raise ValueError("Invalid board") \ No newline at end of file diff --git a/minesweeper/minesweeper_test.py b/minesweeper/minesweeper_test.py new file mode 100644 index 00000000000..241ecffbde4 --- /dev/null +++ b/minesweeper/minesweeper_test.py @@ -0,0 +1,91 @@ +try: + from minesweeper import board +except ImportError: + raise SystemExit('Could not find minesweeper.py. Does it exist?') + +import unittest + +class MinesweeperTest(unittest.TestCase): + def test_board1(self): + inp = ["+------+", "| * * |", "| * |", "| * |", "| * *|", + "| * * |", "| |", "+------+"] + out = ["+------+", "|1*22*1|", "|12*322|", "| 123*2|", "|112*4*|", + "|1*22*2|", "|111111|", "+------+"] + self.assertEqual(out, board(inp)) + + @unittest.skip("Not implemented yet") + def test_board2(self): + inp = ["+-----+", "| * * |", "| |", "| * |", "| * *|", + "| * * |", "+-----+"] + out = ["+-----+", "|1*2*1|", "|11322|", "| 12*2|", "|12*4*|", + "|1*3*2|", "+-----+"] + self.assertEqual(out, board(inp)) + + @unittest.skip("Not implemented yet") + def test_board3(self): + inp = ["+-----+", "| * * |", "+-----+"] + out = ["+-----+", "|1*2*1|", "+-----+"] + self.assertEqual(out, board(inp)) + + @unittest.skip("Not implemented yet") + def test_board4(self): + inp = ["+-+", "|*|", "| |", "|*|", "| |", "| |", "+-+"] + out = ["+-+", "|*|", "|2|", "|*|", "|1|", "| |", "+-+"] + self.assertEqual(out, board(inp)) + + @unittest.skip("Not implemented yet") + def test_board5(self): + inp = ["+-+", "|*|", "+-+"] + out = ["+-+", "|*|", "+-+"] + self.assertEqual(out, board(inp)) + + @unittest.skip("Not implemented yet") + def test_board6(self): + inp = ["+--+", "|**|", "|**|", "+--+"] + out = ["+--+", "|**|", "|**|", "+--+"] + self.assertEqual(out, board(inp)) + + @unittest.skip("Not implemented yet") + def test_board7(self): + inp = ["+--+", "|**|", "|**|", "+--+"] + out = ["+--+", "|**|", "|**|", "+--+"] + self.assertEqual(out, board(inp)) + + @unittest.skip("Not implemented yet") + def test_board8(self): + inp = ["+---+", "|***|", "|* *|", "|***|", "+---+"] + out = ["+---+", "|***|", "|*8*|", "|***|", "+---+"] + self.assertEqual(out, board(inp)) + + @unittest.skip("Not implemented yet") + def test_board9(self): + inp = ["+-----+", "| |", "| * |", "| |", "| |", + "| * |", "+-----+"] + out = ["+-----+", "| 111|", "| 1*1|", "| 111|", "|111 |", + "|1*1 |", "+-----+"] + self.assertEqual(out, board(inp)) + + @unittest.skip("Not implemented yet") + def test_different_len(self): + inp = ["+-+", "| |", "|* |", "| |", "+-+"] + with self.assertRaises(ValueError) as context: + board(inp) + self.assertEqual(context.exception.message, 'Invalid board') + + @unittest.skip("Not implemented yet") + def test_faulty_border(self): + inp = ["+-----+", "* * |", "+-- --+"] + with self.assertRaises(ValueError) as context: + board(inp) + self.assertEqual(context.exception.message, 'Invalid board') + + @unittest.skip("Not implemented yet") + def test_invalid_char(self): + inp = ["+-----+", "|X * |", "+-----+"] + with self.assertRaises(ValueError) as context: + board(inp) + self.assertEqual(context.exception.message, 'Invalid board') + + +if __name__ == '__main__': + unittest.main() From 9e7b0980d9c55320d4a642d77a3c7d5e7ca21ea8 Mon Sep 17 00:00:00 2001 From: Hermano Cabral Date: Sat, 29 Mar 2014 12:59:34 -0300 Subject: [PATCH 14/52] New exercise: ocr-numbers --- EXERCISES.txt | 1 + ocr-numbers/example.py | 16 ++++++++++++ ocr-numbers/ocr_test.py | 56 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 73 insertions(+) create mode 100644 ocr-numbers/example.py create mode 100644 ocr-numbers/ocr_test.py diff --git a/EXERCISES.txt b/EXERCISES.txt index 9c4d086c4c7..24275add07b 100644 --- a/EXERCISES.txt +++ b/EXERCISES.txt @@ -9,6 +9,7 @@ nucleotide-count series largest-series-product octal +ocr-numbers difference-of-squares point-mutations phone-number diff --git a/ocr-numbers/example.py b/ocr-numbers/example.py new file mode 100644 index 00000000000..009f3030f88 --- /dev/null +++ b/ocr-numbers/example.py @@ -0,0 +1,16 @@ +def number(g): + if not g or len(g) < 4 or any(len(r)!=len(g[0]) for r in g): + raise ValueError('Ill-formed grid') + if g == [" _ ","| |","|_|"," "]: + return '0' + elif g == [" "," |"," |"," "]: + return '1' + else: + return '?' + +def grid(n): + if n == '0': + return [" _ ","| |","|_|"," "] + elif n == '1': + return [" "," |"," |"," "] + raise ValueError('Unknown digit') \ No newline at end of file diff --git a/ocr-numbers/ocr_test.py b/ocr-numbers/ocr_test.py new file mode 100644 index 00000000000..6134d3358e1 --- /dev/null +++ b/ocr-numbers/ocr_test.py @@ -0,0 +1,56 @@ +try: + from ocr import number, grid +except ImportError: + raise SystemExit('Could not find ocr.py. Does it exist?') + +import unittest + +class OcrTest(unittest.TestCase): + def test_0(self): + self.assertEqual('0', number([" _ ","| |","|_|"," "])) + + @unittest.skip("Not implemented yet") + def test_1(self): + self.assertEqual('1', number([" "," |"," |"," "])) + + @unittest.skip("Not implemented yet") + def test_garbage(self): + self.assertEqual('?', number([" _ "," _|"," |"," "])) + + @unittest.skip("Not implemented yet") + def test_last_line_nonblank(self): + self.assertEqual('?', number([" "," |"," |","| |"])) + + @unittest.skip("Not implemented yet") + def test_unknown_char(self): + self.assertEqual('?', number([" - "," _|"," X|"," "])) + + @unittest.skip("Not implemented yet") + def test_too_short_row(self): + with self.assertRaises(ValueError) as context: + number([" "," _|"," |"," "]) + self.assertEqual(context.exception.message, 'Ill-formed grid') + + @unittest.skip("Not implemented yet") + def test_insufficient_rows(self): + with self.assertRaises(ValueError) as context: + number([" "," _|"," X|"]) + self.assertEqual(context.exception.message, 'Ill-formed grid') + + @unittest.skip("Not implemented yet") + def test_grid0(self): + self.assertEqual([" _ ","| |","|_|"," "], grid('0')) + + @unittest.skip("Not implemented yet") + def test_grid1(self): + self.assertEqual([" "," |"," |"," "], grid('1')) + + @unittest.skip("Not implemented yet") + def test_invalid_digit(self): + with self.assertRaises(ValueError) as context: + grid('2') + self.assertEqual(context.exception.message, 'Unknown digit') + + +if __name__ == '__main__': + unittest.main() From c182801211c0ddbb780ec84a270069e6223f6a8f Mon Sep 17 00:00:00 2001 From: Hermano Cabral Date: Sun, 30 Mar 2014 11:38:57 -0300 Subject: [PATCH 15/52] New exercise: secret-handshake --- EXERCISES.txt | 1 + secret-handshake/example.py | 46 +++++++++++++++++++++++ secret-handshake/handshake_test.py | 60 ++++++++++++++++++++++++++++++ 3 files changed, 107 insertions(+) create mode 100644 secret-handshake/example.py create mode 100644 secret-handshake/handshake_test.py diff --git a/EXERCISES.txt b/EXERCISES.txt index 24275add07b..2b581575d2c 100644 --- a/EXERCISES.txt +++ b/EXERCISES.txt @@ -26,6 +26,7 @@ gigasecond palindrome-products triangle scrabble-score +secret-handshake sieve nth-prime luhn diff --git a/secret-handshake/example.py b/secret-handshake/example.py new file mode 100644 index 00000000000..efb165c57d8 --- /dev/null +++ b/secret-handshake/example.py @@ -0,0 +1,46 @@ +gestures = ['wink','double blink','close your eyes','jump'] + +def handshake(s): + s = list(sanitize(s)) + s.reverse() + seq = [] + lim = len(s) if len(s) <= len(gestures) else len(gestures) + for i1 in range(lim): + if s[i1] == '1': + seq.append(gestures[i1]) + if len(s) == 5: + seq.reverse() + return seq + +def code(seq): + if not seq or set(seq)-set(gestures): + return '0' + s = find_subseq(seq) + if not s: + s = ['1'] + find_subseq(reversed(seq)) + return "".join(s) + +def sanitize(s): + if not(isinstance(s, int) or isinstance(s,str)): + raise TypeError('Unknown type') + if isinstance(s,int): + if s < 0: + return "" + s = bin(s)[2:] + elif set(s)-set(['0','1']): + return "" + if len(s) > 5: + raise ValueError('Binary string too long') + return "0"*(len(gestures)-len(s)) + s + +def find_subseq(seq): + idx = 0 + s = [] + for g in seq: + if g not in gestures[idx:]: + return [] + newidx = gestures.index(g,idx) + 1 + s.extend(['0']*(newidx-idx-1)+['1']) + idx = newidx + s.reverse() + return s \ No newline at end of file diff --git a/secret-handshake/handshake_test.py b/secret-handshake/handshake_test.py new file mode 100644 index 00000000000..48d35a01794 --- /dev/null +++ b/secret-handshake/handshake_test.py @@ -0,0 +1,60 @@ +from handshake import handshake, code + +import unittest + +class HandshakeTest(unittest.TestCase): + def test_shake_int(self): + self.assertEqual(['wink','jump'], handshake(9)) + + @unittest.skip("Not implemented yet") + def test_shake_bin1(self): + self.assertEqual(['close your eyes','double blink'], handshake('10110')) + + @unittest.skip("Not implemented yet") + def test_shake_bin2(self): + self.assertEqual(['wink','close your eyes'], handshake('101')) + + @unittest.skip("Not implemented yet") + def test_shake_negative_int(self): + self.assertEqual([], handshake(-9)) + + @unittest.skip("Not implemented yet") + def test_shake_bin_invalid(self): + self.assertEqual([], handshake('121')) + + @unittest.skip("Not implemented yet") + def test_unknown_action(self): + self.assertEqual('0', code(['wink','sneeze'])) + + @unittest.skip("Not implemented yet") + def test_code1(self): + self.assertEqual('1100', code(['close your eyes','jump'])) + + @unittest.skip("Not implemented yet") + def test_code2(self): + self.assertEqual('11', code(['wink','double blink'])) + + @unittest.skip("Not implemented yet") + def test_code3(self): + self.assertEqual('11010', code(['jump','double blink'])) + + @unittest.skip("Not implemented yet") + def test_composition1(self): + self.assertEqual('11011', code(handshake(27))) + + @unittest.skip("Not implemented yet") + def test_composition2(self): + self.assertEqual('1', code(handshake(1))) + + @unittest.skip("Not implemented yet") + def test_composition3(self): + self.assertEqual('111', code(handshake('111'))) + + @unittest.skip("Not implemented yet") + def test_composition4(self): + inp = ['wink','double blink','jump'] + self.assertEqual(inp, handshake(code(inp))) + + +if __name__ == '__main__': + unittest.main() From a6dcaa41bb439472549f3e028ceaebf4bca4a919 Mon Sep 17 00:00:00 2001 From: Hermano Cabral Date: Tue, 1 Apr 2014 17:57:32 -0300 Subject: [PATCH 16/52] New exercise: pascals-triangle --- EXERCISES.txt | 1 + pascals-triangle/example.py | 12 +++++++ pascals-triangle/pascals_triangle_test.py | 43 +++++++++++++++++++++++ 3 files changed, 56 insertions(+) create mode 100644 pascals-triangle/example.py create mode 100644 pascals-triangle/pascals_triangle_test.py diff --git a/EXERCISES.txt b/EXERCISES.txt index 2b581575d2c..be7b5c7fe8e 100644 --- a/EXERCISES.txt +++ b/EXERCISES.txt @@ -13,6 +13,7 @@ ocr-numbers difference-of-squares point-mutations phone-number +pascals-triangle allergies grade-school robot-name diff --git a/pascals-triangle/example.py b/pascals-triangle/example.py new file mode 100644 index 00000000000..47c74842e2a --- /dev/null +++ b/pascals-triangle/example.py @@ -0,0 +1,12 @@ +def triangle(nth): + return [row(i) for i in xrange(nth+1)] + +def is_triangle(t): + new_t = triangle(len(t)-1) + return t == new_t + +def row(nth): + r = [1] + for i in xrange(1,nth+1): + r.append(r[-1]*(nth-i+1)/i) + return " ".join([str(i) for i in r]) \ No newline at end of file diff --git a/pascals-triangle/pascals_triangle_test.py b/pascals-triangle/pascals_triangle_test.py new file mode 100644 index 00000000000..035014b0545 --- /dev/null +++ b/pascals-triangle/pascals_triangle_test.py @@ -0,0 +1,43 @@ +from pascals_triangle import triangle, row, is_triangle + +import unittest + +class PascalsTriangleTest(unittest.TestCase): + def test_triangle1(self): + ans = ['1', '1 1', '1 2 1', '1 3 3 1', '1 4 6 4 1'] + self.assertEqual(ans, triangle(4)) + + @unittest.skip("Not implemented yet") + def test_triangle2(self): + ans = ['1', '1 1', '1 2 1', '1 3 3 1', '1 4 6 4 1', '1 5 10 10 5 1', + '1 6 15 20 15 6 1'] + self.assertEqual(ans, triangle(6)) + + @unittest.skip("Not implemented yet") + def test_is_triangle_true(self): + inp = ['1', '1 1', '1 2 1', '1 3 3 1', '1 4 6 4 1', '1 5 10 10 5 1'] + self.assertEqual(True, is_triangle(inp)) + + @unittest.skip("Not implemented yet") + def test_is_triangle_false(self): + inp = ['1', '1 1', '1 2 1', '1 4 4 1'] + self.assertEqual(False, is_triangle(inp)) + + @unittest.skip("Not implemented yet") + def test_row1(self): + ans = '1' + self.assertEqual(ans, row(0)) + + @unittest.skip("Not implemented yet") + def test_row2(self): + ans = '1 2 1' + self.assertEqual(ans, row(2)) + + @unittest.skip("Not implemented yet") + def test_row3(self): + ans = '1 7 21 35 35 21 7 1' + self.assertEqual(ans, row(7)) + + +if __name__ == '__main__': + unittest.main() From 4547a835eeecf24f3bc2423993f5246c113b67bc Mon Sep 17 00:00:00 2001 From: Hermano Cabral Date: Tue, 1 Apr 2014 23:49:52 -0300 Subject: [PATCH 17/52] New exercise: pythagorean triplet --- EXERCISES.txt | 1 + pythagorean-triplet/example.py | 72 ++++++++++++++ .../pythagorean_triplet_test.py | 93 +++++++++++++++++++ 3 files changed, 166 insertions(+) create mode 100644 pythagorean-triplet/example.py create mode 100644 pythagorean-triplet/pythagorean_triplet_test.py diff --git a/EXERCISES.txt b/EXERCISES.txt index be7b5c7fe8e..064cb5ac038 100644 --- a/EXERCISES.txt +++ b/EXERCISES.txt @@ -37,3 +37,4 @@ prime-factors raindrops atbash-cipher wordy +pythagorean-triplet diff --git a/pythagorean-triplet/example.py b/pythagorean-triplet/example.py new file mode 100644 index 00000000000..3910a4089c5 --- /dev/null +++ b/pythagorean-triplet/example.py @@ -0,0 +1,72 @@ +from itertools import product +from operator import mul +from math import sqrt + +def primitive_triplets(nbr): + if nbr % 4 != 0: + raise ValueError('Argument must be divisible by 4') + prime_factors,powers = factor(nbr/2) + args = [(1,prime_factors[i1]**powers[i1]) for i1 in range(len(powers))] + a = [reduce(mul, p) for p in product(*args)] + a.sort() + factors = [(m,n) for m,n in zip(reversed(a),a) if m>n] + ts = set() + for m,n in factors: + l = [nbr, m*m-n*n,m*m+n*n] + l.sort() + ts.update([tuple(l)]) + return ts + +def is_triplet(t): + t = list(t) + t.sort() + a,b,c = t + return c*c == a*a + b*b + +def triplets_in_range(m, n): + t = set() + for a in xrange(m,n+1): + for b in xrange(a+1,n+1): + c = int(sqrt(a*a + b*b)+0.5) + if c*c == a*a + b*b and c >= m and c <= n: + t.update([(a,b,c)]) + return t + +primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, + 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, + 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, + 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, + 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, + 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, + 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, + 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, + 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, + 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, + 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, + 941, 947, 953, 967, 971, 977, 983, 991, 997] + +def factor(n): + global primes + if n == 1: + return (1,),(0,) + factors = [] + powers = [] + idx = 0 + while n > 1: + prime = primes[idx] + idx += 1 + if n % prime != 0: + continue + factors.append(prime) + p = 0 + while n % prime == 0: + p += 1 + n /= prime + powers.append(p) + return factors,powers + +if __name__ == '__main__': + print primitive_triplets(4) + print primitive_triplets(84) + print primitive_triplets(288) + print triplets_in_range(50,100) \ No newline at end of file diff --git a/pythagorean-triplet/pythagorean_triplet_test.py b/pythagorean-triplet/pythagorean_triplet_test.py new file mode 100644 index 00000000000..5e09b9d0dc6 --- /dev/null +++ b/pythagorean-triplet/pythagorean_triplet_test.py @@ -0,0 +1,93 @@ +# +#============================================================================== +# The test cases below assume two functions are defined: +# +# - triplets_in_range(min, max) +# Compute all pythagorean triplets (a,b,c) with min <= a,b,c <= max +# +# - primitive_triplets(b) +# Find all primitive pythagorean triplets having b as one of their +# components +# +# Args: +# b - an integer divisible by 4 (see explanantion below) +# +# Note that in the latter function the components other than the argument can +# be quite large. +# +# A primitive pythagorean triplet has its 3 componentes coprime. So, (3,4,5) is +# a primitive pythagorean triplet since 3,4 and 5 don't have a common factor. +# On the other hand, (6,8,10), although a pythagorean triplet, is not primitive +# aince 2 divides all three components. +# +# A method for finding all primitive pythagorean triplet is given in wikipedia +# (http://en.wikipedia.org/wiki/Pythagorean_triple#Generating_a_triple). The +# triplet a=(m^2-n^2), b=2*m*n and c=(m^2+n^2), where m and n are coprime and +# m-n>0 is odd, generate a primitive triplet. Note that this implies that b has +# to be divisible by 4 and a and c are odd. Also note that we may have either +# a>b or b>a. +# +# The function primitive_triplets should then use the formula above with b set +# to its argument and find all possible pairs (m,n) such that m>n, m-n is odd, +# b=2*m*n and m and n are coprime. +# +#============================================================================== + +from pythagorean_triplet import primitive_triplets, triplets_in_range, is_triplet + +import unittest + +class PythagoreanTripletTest(unittest.TestCase): + def test_triplet1(self): + ans = set([(3,4,5)]) + self.assertEqual(ans, primitive_triplets(4)) + +# @unittest.skip("Not implemented yet") + def test_triplet2(self): + ans = set([(13, 84, 85), (84, 187, 205), (84, 437, 445), + (84, 1763, 1765)]) + self.assertEqual(ans, primitive_triplets(84)) + +# @unittest.skip("Not implemented yet") + def test_triplet3(self): + ans = set([(29, 420, 421), (341, 420, 541), (420, 851, 949), + (420, 1189, 1261), (420, 1739, 1789), (420, 4891, 4909), + (420, 11021, 11029), (420, 44099, 44101)]) + self.assertEqual(ans, primitive_triplets(420)) + +# @unittest.skip("Not implemented yet") + def test_triplet4(self): + ans = set([(175, 288, 337), (288, 20735, 20737)]) + self.assertEqual(ans, primitive_triplets(288)) + +# @unittest.skip("Not implemented yet") + def test_range1(self): + ans = set([(3,4,5),(6,8,10)]) + self.assertEqual(ans, triplets_in_range(1, 10)) + +# @unittest.skip("Not implemented yet") + def test_range2(self): + ans = set([(57,76,95),(60,63,87)]) + self.assertEqual(ans, triplets_in_range(56, 95)) + +# @unittest.skip("Not implemented yet") + def test_is_triplet1(self): + self.assertEqual(True, is_triplet((29,20,21))) + +# @unittest.skip("Not implemented yet") + def test_is_triplet2(self): + self.assertEqual(False, is_triplet((25,25,1225))) + +# @unittest.skip("Not implemented yet") + def test_is_triplet3(self): + self.assertEqual(True, is_triplet((924,43,925))) + +# @unittest.skip("Not implemented yet") + def test_odd_number(self): + with self.assertRaises(ValueError) as context: + primitive_triplets(5) + self.assertEqual(context.exception.message, 'Argument must be divisible by 4') + + +if __name__ == '__main__': + unittest.main() From b827705aa160623cdf4276b95dea0b82295b9f17 Mon Sep 17 00:00:00 2001 From: Simon Jakobi Date: Wed, 2 Apr 2014 18:11:17 +0200 Subject: [PATCH 18/52] Add meetup exercise --- EXERCISES.txt | 1 + meetup/example.py | 17 +++++++++++++++++ meetup/meetup_test.py | 41 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+) create mode 100644 meetup/example.py create mode 100644 meetup/meetup_test.py diff --git a/EXERCISES.txt b/EXERCISES.txt index 82394ea421d..9233de30922 100644 --- a/EXERCISES.txt +++ b/EXERCISES.txt @@ -22,6 +22,7 @@ space-age grains gigasecond palindrome-products +meetup triangle scrabble-score sieve diff --git a/meetup/example.py b/meetup/example.py new file mode 100644 index 00000000000..2abeb1d1308 --- /dev/null +++ b/meetup/example.py @@ -0,0 +1,17 @@ +from calendar import Calendar + + +def meetup_day(year, month, day_of_the_week, which): + candidates = [date + for date in Calendar().itermonthdates(year, month) + if date.month == month + if date.strftime('%A') == day_of_the_week] + return _choice(which)(candidates) + + +def _choice(which): + if which == 'teenth': + return lambda dates: next(d for d in dates if 13 <= d.day <= 19) + + ix = -1 if (which == 'last') else (int(which[0]) - 1) + return lambda dates: dates[ix] diff --git a/meetup/meetup_test.py b/meetup/meetup_test.py new file mode 100644 index 00000000000..54304b1a6eb --- /dev/null +++ b/meetup/meetup_test.py @@ -0,0 +1,41 @@ +from datetime import date +import unittest + +from meetup import meetup_day + + +class MeetupTest(unittest.TestCase): + def test_monteenth_of_may_2013(self): + self.assertEqual(date(2013, 5, 13), + meetup_day(2013, 5, 'Monday', 'teenth')) + + def test_saturteenth_of_february_2013(self): + self.assertEqual(date(2013, 2, 16), + meetup_day(2013, 2, 'Saturday', 'teenth')) + + def test_first_tuesday_of_may_2013(self): + self.assertEqual(date(2013, 5, 7), + meetup_day(2013, 5, 'Tuesday', '1st')) + + def test_second_monday_of_april_2013(self): + self.assertEqual(date(2013, 4, 8), + meetup_day(2013, 4, 'Monday', '2nd')) + + def test_third_thursday_of_september_2013(self): + self.assertEqual(date(2013, 9, 19), + meetup_day(2013, 9, 'Thursday', '3rd')) + + def test_fourth_sunday_of_march_2013(self): + self.assertEqual(date(2013, 3, 24), + meetup_day(2013, 3, 'Sunday', '4th')) + + def test_last_thursday_of_october_2013(self): + self.assertEqual(date(2013, 10, 31), + meetup_day(2013, 10, 'Thursday', 'last')) + + def test_last_wednesday_of_february_2012(self): + self.assertEqual(date(2012, 2, 29), + meetup_day(2012, 2, 'Wednesday', 'last')) + +if __name__ == '__main__': + unittest.main() From 3d1d0f19134553f5407b4e2f87b642320b1be081 Mon Sep 17 00:00:00 2001 From: Hermano Cabral Date: Wed, 2 Apr 2014 07:29:54 -0300 Subject: [PATCH 19/52] New exercise: saddle-points --- EXERCISES.txt | 1 + saddle-points/example.py | 10 ++++++++++ saddle-points/saddle_points_test.py | 27 +++++++++++++++++++++++++++ 3 files changed, 38 insertions(+) create mode 100644 saddle-points/example.py create mode 100644 saddle-points/saddle_points_test.py diff --git a/EXERCISES.txt b/EXERCISES.txt index 756fb4e1f2f..a93fb8305fd 100644 --- a/EXERCISES.txt +++ b/EXERCISES.txt @@ -7,6 +7,7 @@ anagram beer-song nucleotide-count series +saddle-points largest-series-product octal ocr-numbers diff --git a/saddle-points/example.py b/saddle-points/example.py new file mode 100644 index 00000000000..11ee4266dbd --- /dev/null +++ b/saddle-points/example.py @@ -0,0 +1,10 @@ +def saddle_points(m): + if not m: + return set() + if any(len(r)!=len(m[0]) for r in m): + raise ValueError('irregular matrix') + mmax = [max(r) for r in m] + mmin = [min(c) for c in zip(*m)] + points = [(i,j) for i in range(len(m)) for j in range(len(m[0])) if mmax[i] == mmin[j]] + + return set(points) diff --git a/saddle-points/saddle_points_test.py b/saddle-points/saddle_points_test.py new file mode 100644 index 00000000000..3daa1ff761a --- /dev/null +++ b/saddle-points/saddle_points_test.py @@ -0,0 +1,27 @@ +from saddle_points import saddle_points + +import unittest + +class SaddlePointTest(unittest.TestCase): + def test_one_saddle(self): + inp = [[9,8,7],[5,3,2],[6,6,7]] + self.assertEqual(set([(1,0)]), saddle_points(inp)) + + def test_no_saddle(self): + self.assertEqual(set(), saddle_points([[2,1],[1,2]])) + + def test_mult_saddle(self): + inp = [[5,3,5,4],[6,4,7,3],[5,1,5,3]] + ans = set([(0,0),(0,2),(2,0),(2,2)]) + self.assertEqual(ans, saddle_points(inp)) + + def test_empty_matrix(self): + self.assertEqual(set(), saddle_points([])) + + def test_irregular_matrix(self): + with self.assertRaisesRegexp(ValueError, 'irregular matrix'): + saddle_points([[1,2,3],[2,3],[3,2,1]]) + + +if __name__ == '__main__': + unittest.main() From c97aa1346d3bc7aca8f774821f39cd690260c169 Mon Sep 17 00:00:00 2001 From: Hermano Cabral Date: Mon, 7 Apr 2014 15:49:24 -0300 Subject: [PATCH 20/52] New exercise: trinary --- EXERCISES.txt | 1 + trinary/example.py | 9 +++++++++ trinary/trinary_test.py | 29 +++++++++++++++++++++++++++++ 3 files changed, 39 insertions(+) create mode 100644 trinary/example.py create mode 100644 trinary/trinary_test.py diff --git a/EXERCISES.txt b/EXERCISES.txt index a93fb8305fd..9d9d83d9a0d 100644 --- a/EXERCISES.txt +++ b/EXERCISES.txt @@ -28,6 +28,7 @@ gigasecond palindrome-products meetup triangle +trinary scrabble-score secret-handshake sieve diff --git a/trinary/example.py b/trinary/example.py new file mode 100644 index 00000000000..a1a18e0324f --- /dev/null +++ b/trinary/example.py @@ -0,0 +1,9 @@ +def trinary(s): + if set(s) - set('012'): + return 0 + return reduce(lambda x,y:x*3 + int(y), s, 0) + +if __name__ == '__main__': + print trinary('102101') + print trinary('22222') + print trinary('10000') diff --git a/trinary/trinary_test.py b/trinary/trinary_test.py new file mode 100644 index 00000000000..204c166d7a1 --- /dev/null +++ b/trinary/trinary_test.py @@ -0,0 +1,29 @@ +from trinary import trinary + +import unittest + +class TrinaryTest(unittest.TestCase): + def test_valid_trinary1(self): + self.assertEqual(0, trinary('0')) + + def test_valid_trinary2(self): + self.assertEqual(1, trinary('1')) + + def test_valid_trinary3(self): + self.assertEqual(3, trinary('10')) + + def test_valid_trinary4(self): + self.assertEqual(307, trinary('102101')) + + def test_valid_trinary5(self): + self.assertEqual(242, trinary('22222')) + + def test_valid_trinary6(self): + self.assertEqual(81, trinary('10000')) + + def test_invalid_trinary(self): + self.assertEqual(0, trinary('13201')) + + +if __name__ == '__main__': + unittest.main() From 8e361b669d461c02c7b5ea5396a1451f39447cf0 Mon Sep 17 00:00:00 2001 From: Hermano Cabral Date: Mon, 7 Apr 2014 22:32:31 -0300 Subject: [PATCH 21/52] New exercise: hamming --- EXERCISES.txt | 1 + hamming/example.py | 2 ++ hamming/hamming_test.py | 42 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 45 insertions(+) create mode 100644 hamming/example.py create mode 100644 hamming/hamming_test.py diff --git a/EXERCISES.txt b/EXERCISES.txt index 9d9d83d9a0d..e8eac0641cb 100644 --- a/EXERCISES.txt +++ b/EXERCISES.txt @@ -41,3 +41,4 @@ raindrops atbash-cipher wordy pythagorean-triplet +hamming diff --git a/hamming/example.py b/hamming/example.py new file mode 100644 index 00000000000..20026498987 --- /dev/null +++ b/hamming/example.py @@ -0,0 +1,2 @@ +def hamming(s1,s2): + return sum(1 for a,b in map(None,s1,s2) if a != b) diff --git a/hamming/hamming_test.py b/hamming/hamming_test.py new file mode 100644 index 00000000000..00c3d191400 --- /dev/null +++ b/hamming/hamming_test.py @@ -0,0 +1,42 @@ +from hamming import hamming + +import unittest + +# If the sequences have different lengths, assume the shorter one is extended +# with nucleotides in such a way to guarantee the extra nucleotides are all +# different between the two strands. + +class hammingdecimalTest(unittest.TestCase): + def test_hamming_empty(self): + self.assertEqual(0, hamming('','')) + + def test_hamming_onenucleotide_same(self): + self.assertEqual(0, hamming('A','A')) + + def test_hamming_onenucleotide_different(self): + self.assertEqual(1, hamming('A','G')) + + def test_hamming_short1(self): + self.assertEqual(1, hamming('AT','CT')) + + def test_hamming_short2(self): + self.assertEqual(2, hamming('AG','CT')) + + def test_hamming_large(self): + self.assertEqual(4, hamming('GGATCG','CCTGCG')) + + def test_hamming_small(self): + self.assertEqual(1, hamming('GGACGA','GGTCGA')) + + def test_hamming_very_long(self): + self.assertEqual(9, hamming('GGACGGATTCTG','AGGACGGATTCT')) + + def test_hamming_different_length1(self): + self.assertEqual(4, hamming('AAGCTAC','ACGTT')) + + def test_hamming_different_length2(self): + self.assertEqual(5, hamming('AAGCTAC','ACGTTACGTC')) + + +if __name__ == '__main__': + unittest.main() From a4e50bb4a7386f61a7c8b172b526e79b6a3eb309 Mon Sep 17 00:00:00 2001 From: Hermano Cabral Date: Tue, 8 Apr 2014 00:17:34 -0300 Subject: [PATCH 22/52] New exercise: simple-cipher --- EXERCISES.txt | 1 + simple-cipher/example.py | 34 ++++++++++++++++++ simple-cipher/simple_cipher_test.py | 55 +++++++++++++++++++++++++++++ 3 files changed, 90 insertions(+) create mode 100644 simple-cipher/example.py create mode 100644 simple-cipher/simple_cipher_test.py diff --git a/EXERCISES.txt b/EXERCISES.txt index e8eac0641cb..938463749ed 100644 --- a/EXERCISES.txt +++ b/EXERCISES.txt @@ -42,3 +42,4 @@ atbash-cipher wordy pythagorean-triplet hamming +simple-cipher diff --git a/simple-cipher/example.py b/simple-cipher/example.py new file mode 100644 index 00000000000..449331712be --- /dev/null +++ b/simple-cipher/example.py @@ -0,0 +1,34 @@ +from string import ascii_lowercase,punctuation,whitespace,digits +from time import time +import random + +class Cipher: + def __init__(self, k=None): + if k: + self.key = k.translate(None,punctuation+whitespace+digits).lower() + else: + random.seed(time()) + self.key = ''.join(random.choice(ascii_lowercase) for i in range(100)) + + def base_encode(self, s, shift): + xkey = self.key*(len(s)//len(self.key)+1) + return ''.join(shift(c,k) for c,k in zip(s,xkey)) + + def encode(self, s): + s = s.translate(None,punctuation+whitespace+digits).lower() + shift = lambda c,k: chr(((ord(c)+ord(k)-2*ord('a'))\ + % len(ascii_lowercase)) + ord('a')) + return self.base_encode(s, shift) + + def decode(self, s): + shift = lambda c,k: chr(((ord(c)-ord(k)+len(ascii_lowercase))\ + % len(ascii_lowercase)) + ord('a')) + return self.base_encode(s, shift) + +class Caesar(Cipher): + def __init__(self): + Cipher.__init__(self, 'd') + +if __name__ == '__main__': + print(Caesar().encode('venividivici')) + print(Caesar().encode('\'Twas the night before Christmas')) \ No newline at end of file diff --git a/simple-cipher/simple_cipher_test.py b/simple-cipher/simple_cipher_test.py new file mode 100644 index 00000000000..cb539b0e63a --- /dev/null +++ b/simple-cipher/simple_cipher_test.py @@ -0,0 +1,55 @@ +from cipher import Caesar, Cipher + +import unittest + +class CipherTest(unittest.TestCase): + def test_caesar_encode1(self): + self.assertEqual('lwlvdzhvrphsurjudpplqjlqsbwkrq', + Caesar().encode('itisawesomeprogramminginpython')) + + def test_caesar_encode2(self): + self.assertEqual('yhqlylglylfl', Caesar().encode('venividivici')) + + def test_caesar_encode3(self): + self.assertEqual('wzdvwkhqljkwehiruhfkulvwpdv', + Caesar().encode('\'Twas the night before Christmas')) + + def test_caesar_encode_with_numbers(self): + self.assertEqual('jr', Caesar().encode('1, 2, 3, Go!')) + + def test_caesar_decode(self): + self.assertEqual('venividivici', Caesar().decode('yhqlylglylfl')) + + def test_cipher_encode1(self): + c = Cipher('a') + self.assertEqual('itisawesomeprogramminginpython', + c.encode('itisawesomeprogramminginpython')) + + def test_cipher_encode2(self): + c = Cipher('aaaaaaaaaaaaaaaaaaaaaa') + self.assertEqual('itisawesomeprogramminginpython', + c.encode('itisawesomeprogramminginpython')) + + def test_cipher_encode3(self): + c = Cipher('dddddddddddddddddddddd') + self.assertEqual('yhqlylglylfl', c.encode('venividivici')) + + def test_cipher_encode4(self): + key = 'duxrceqyaimciuucnelkeoxjhdyduucpmrxmaivacmybmsdrzwqxvbxsygzsabdjmdjabeorttiwinfrpmpogvabiofqexnohrqu' + c = Cipher(key) + self.assertEqual('gccwkixcltycv', c.encode('diffiehellman')) + + def test_cipher_compositiion1(self): + key = 'duxrceqyaimciuucnelkeoxjhdyduucpmrxmaivacmybmsdrzwqxvbxsygzsabdjmdjabeorttiwinfrpmpogvabiofqexnohrqu' + plaintext = 'adaywithoutlaughterisadaywasted' + c = Cipher(key) + self.assertEqual(plaintext, c.decode(c.encode(plaintext))) + + def test_cipher_compositiion2(self): + plaintext = 'adaywithoutlaughterisadaywasted' + c = Cipher() + self.assertEqual(plaintext, c.decode(c.encode(plaintext))) + + +if __name__ == '__main__': + unittest.main() From dd624ca980a628a4666042fbbfd4d30f2746f92a Mon Sep 17 00:00:00 2001 From: Hermano Cabral Date: Mon, 7 Apr 2014 23:38:09 -0300 Subject: [PATCH 23/52] New exercise: hexadecimal --- EXERCISES.txt | 1 + hexadecimal/example.py | 12 ++++++++++ hexadecimal/hexadecimal_test.py | 42 +++++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+) create mode 100644 hexadecimal/example.py create mode 100644 hexadecimal/hexadecimal_test.py diff --git a/EXERCISES.txt b/EXERCISES.txt index 938463749ed..8ab901a75b3 100644 --- a/EXERCISES.txt +++ b/EXERCISES.txt @@ -43,3 +43,4 @@ wordy pythagorean-triplet hamming simple-cipher +hexadecimal diff --git a/hexadecimal/example.py b/hexadecimal/example.py new file mode 100644 index 00000000000..513c5e6dd09 --- /dev/null +++ b/hexadecimal/example.py @@ -0,0 +1,12 @@ +def hexa(s): + s = s.lower() + if set(s) - set('0123456789abcdef'): + raise ValueError('Invalid hexadecimal string') + l = [ord(c) - ord('a') + 10 if c in 'abcdef' else ord(c) - ord('0') + for c in s] + return reduce(lambda x,y:x*16 + y, l, 0) + +if __name__ == '__main__': + print hexa('19ACE') + print hexa('100') + print hexa('dead') \ No newline at end of file diff --git a/hexadecimal/hexadecimal_test.py b/hexadecimal/hexadecimal_test.py new file mode 100644 index 00000000000..08f13b1c1d2 --- /dev/null +++ b/hexadecimal/hexadecimal_test.py @@ -0,0 +1,42 @@ +# To avoid trivial solutions, try to solve this problem without the +# function int(s, base=16) + +from hexadecimal import hexa + +import unittest + +class HexadecimalTest(unittest.TestCase): + def test_valid_hexa1(self): + self.assertEqual(1, hexa('1')) + + def test_valid_hexa2(self): + self.assertEqual(12, hexa('c')) + + def test_valid_hexa3(self): + self.assertEqual(16, hexa('10')) + + def test_valid_hexa4(self): + self.assertEqual(175, hexa('af')) + + def test_valid_hexa5(self): + self.assertEqual(256, hexa('100')) + + def test_valid_hexa6(self): + self.assertEqual(105166, hexa('19ACE')) + + def test_valid_hexa7(self): + self.assertEqual(0, hexa('000000')) + + def test_valid_hexa8(self): + self.assertEqual(16776960, hexa('ffff00')) + + def test_valid_hexa9(self): + self.assertEqual(65520, hexa('00fff0')) + + def test_invalid_hexa(self): + with self.assertRaises(ValueError): + hexa('carrot') + + +if __name__ == '__main__': + unittest.main() From 9f47f55154ecab645331da7c73fee3f531cd5bb3 Mon Sep 17 00:00:00 2001 From: Hermano Cabral Date: Wed, 9 Apr 2014 14:20:52 -0300 Subject: [PATCH 24/52] New exercise: crypto-square --- EXERCISES.txt | 1 + crypto-square/crypto_square_test.py | 40 +++++++++++++++++++++++++++ crypto-square/example.py | 43 +++++++++++++++++++++++++++++ 3 files changed, 84 insertions(+) create mode 100644 crypto-square/crypto_square_test.py create mode 100644 crypto-square/example.py diff --git a/EXERCISES.txt b/EXERCISES.txt index 8ab901a75b3..cfb01c340e5 100644 --- a/EXERCISES.txt +++ b/EXERCISES.txt @@ -34,6 +34,7 @@ secret-handshake sieve nth-prime luhn +crypto-square roman-numerals binary prime-factors diff --git a/crypto-square/crypto_square_test.py b/crypto-square/crypto_square_test.py new file mode 100644 index 00000000000..91294ad5b4f --- /dev/null +++ b/crypto-square/crypto_square_test.py @@ -0,0 +1,40 @@ +from crypto_square import encode, decode + +import unittest + +class CryptoSquareTest(unittest.TestCase): + def test_empty_plain(self): + self.assertEqual('', encode('')) + + def test_perfect_square(self): + self.assertEqual('wliod drwe', encode('WorldWide')) + + def test_almost_perfect_square(self): + self.assertEqual('oasny selde', encode('One day less')) + + def test_punctuation(self): + msg = "1, 2, 3, Go! Go, for God's sake!" + ciph = '1gga2 ook3f degos ors' + self.assertEqual(ciph, encode(msg)) + + def test_long_string(self): + msg = "Be who you are and say what you feel, because those who mind "\ + "don't matter and those who matter don't mind." + ciph = 'betcw tttne ayahm htdwn ouoao ehdus mtsro sfeit edyae tnewo '\ + 'oyehd rhnuw lodao tahbs onmmr aeend ai' + self.assertEqual(ciph, encode(msg)) + + def test_decode(self): + ciph = 'woree iorhu ssmtp eefei aiafn ildjs ulenf eotse vdoor iecey '\ + 'nfima trott tenyu hhytd' + msg = 'wheneveryoufindyourselfonthesideofthemajorityitistimetopausea'\ + 'ndreflect' + self.assertEqual(msg, decode(ciph)) + + def test_encode_decode(self): + msg = 'tensioniswhoyouthinkyoushouldberelaxationiswhoyouare' + self.assertEqual(msg, decode(encode(msg))) + + +if __name__ == '__main__': + unittest.main() diff --git a/crypto-square/example.py b/crypto-square/example.py new file mode 100644 index 00000000000..768c08e8419 --- /dev/null +++ b/crypto-square/example.py @@ -0,0 +1,43 @@ +import math +from string import punctuation,whitespace + +def encode(msg): + msg = msg.strip().translate(None,punctuation+whitespace).lower() + sqrsz = int(math.sqrt(len(msg))) + if sqrsz*sqrsz < len(msg): + sqrsz += 1 + + cols = [msg[i1::sqrsz] for i1 in range(sqrsz)] + cols_str = ''.join(cols) + return ' '.join(cols_str[i1:i1+5] for i1 in range(0,len(cols_str),5)) + +def decode(ciph): + ciph = ciph.strip().translate(None,punctuation+whitespace).lower() + sqrsz = int(math.sqrt(len(ciph))) + if sqrsz*sqrsz < len(ciph): + sqrsz += 1 + colsz, nbr_full_cols = divmod(len(ciph),sqrsz) + + # The matrix produced by the plaintext is in general irregular, and the + # last row is usually shorter than the others. Extract this row first + full_cols_str = ciph[:(colsz+1)*nbr_full_cols] + partial_cols_str = ciph[(colsz+1)*nbr_full_cols:] + last_row = full_cols_str[colsz::colsz+1] + + # Compute the string of all concatenated columns of the colsz X sqrsz + # matrix consisting of the first colsz rows of the plaintext (irregular) + # matrix + trimmed_full_cols = [full_cols_str[i1:i1+colsz] + for i1 in range(0,len(full_cols_str),colsz+1)] + partial_cols = [partial_cols_str[i1:i1+colsz] + for i1 in range(0,len(partial_cols_str),colsz)] + uniform_cols_str = ''.join(trimmed_full_cols + partial_cols) + + other_rows = [uniform_cols_str[i1::colsz] for i1 in range(colsz)] + return ''.join(other_rows+[last_row]) + +if __name__ == '__main__': + msg = 'ifmanwasmeanttostayonthegroundgodwouldhavegivenusroots' + ciph = 'imtgd vsfea rwerm ayoog oanou uiont nnlvt wttdd esaoh ghnss eoau' + print(encode(msg)) + print(decode(ciph)) From 917831a293a2107aef25224e0f1210f8b66d683a Mon Sep 17 00:00:00 2001 From: Hermano Cabral Date: Thu, 10 Apr 2014 20:59:17 -0300 Subject: [PATCH 25/52] New exercise: strain --- EXERCISES.txt | 1 + strain/example.py | 14 +++++++++++++ strain/strain_test.py | 46 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+) create mode 100644 strain/example.py create mode 100644 strain/strain_test.py diff --git a/EXERCISES.txt b/EXERCISES.txt index cfb01c340e5..87847d025cc 100644 --- a/EXERCISES.txt +++ b/EXERCISES.txt @@ -23,6 +23,7 @@ etl leap sum-of-multiples space-age +strain grains gigasecond palindrome-products diff --git a/strain/example.py b/strain/example.py new file mode 100644 index 00000000000..6069fc751fd --- /dev/null +++ b/strain/example.py @@ -0,0 +1,14 @@ +def keep(seq, pred): + res = [] + for el in seq: + if pred(el): + res.append(el) + return res + + +def discard(seq, pred): + res = [] + for el in seq: + if not pred(el): + res.append(el) + return res diff --git a/strain/strain_test.py b/strain/strain_test.py new file mode 100644 index 00000000000..1ba9326b30d --- /dev/null +++ b/strain/strain_test.py @@ -0,0 +1,46 @@ +from strain import keep, discard + +import unittest + + +class StrainTest(unittest.TestCase): + def test_empty_sequence(self): + self.assertEqual([], keep([], lambda x: x % 2 == 0)) + + def test_empty_keep(self): + inp = [2, 4, 6, 8, 10] + out = [] + self.assertEqual(out, keep(inp, lambda x: x % 2 == 1)) + + def test_empty_discard(self): + inp = [2, 4, 6, 8, 10] + out = [] + self.assertEqual(out, discard(inp, lambda x: x % 2 == 0)) + + def test_keep_everything(self): + inp = [2, 4, 6, 8, 10] + self.assertEqual(inp, keep(inp, lambda x: x % 2 == 0)) + + def test_discard_endswith(self): + inp = ['dough', 'cash', 'plough', 'though', 'through', 'enough'] + out = ['cash'] + fn = lambda x: str.endswith(x, 'ough') + self.assertEqual(out, discard(inp, fn)) + + def test_keep_z(self): + inp = ['zebra', 'arizona', 'apple', 'google', 'mozilla'] + out = ['zebra', 'arizona', 'mozilla'] + self.assertEqual(out, keep(inp, lambda x: 'z' in x)) + + def test_keep_discard(self): + inp = ['1,2,3', 'one', 'almost!', 'love'] + self.assertEqual([], discard(keep(inp, str.isalpha), str.isalpha)) + + def test_keep_plus_discard(self): + inp = ['1,2,3', 'one', 'almost!', 'love'] + out = ['one', 'love', '1,2,3', 'almost!'] + self.assertEqual(out, keep(inp, str.isalpha)+discard(inp, str.isalpha)) + + +if __name__ == '__main__': + unittest.main() From 6321dd093e4f4f7acbe012e943e4a9e635c87fa9 Mon Sep 17 00:00:00 2001 From: Hermano Cabral Date: Thu, 10 Apr 2014 18:44:28 -0300 Subject: [PATCH 26/52] New exercise: accumulate --- EXERCISES.txt | 1 + accumulate/accumulate_test.py | 38 +++++++++++++++++++++++++++++++++++ accumulate/example.py | 8 ++++++++ 3 files changed, 47 insertions(+) create mode 100644 accumulate/accumulate_test.py create mode 100644 accumulate/example.py diff --git a/EXERCISES.txt b/EXERCISES.txt index 87847d025cc..e451b7839ad 100644 --- a/EXERCISES.txt +++ b/EXERCISES.txt @@ -3,6 +3,7 @@ rna-transcription matrix word-count twelve-days +accumulate anagram beer-song nucleotide-count diff --git a/accumulate/accumulate_test.py b/accumulate/accumulate_test.py new file mode 100644 index 00000000000..9edc6fca547 --- /dev/null +++ b/accumulate/accumulate_test.py @@ -0,0 +1,38 @@ +from accumulate import accumulate + +import unittest + + +class AccumulateTest(unittest.TestCase): + def test_empty_sequence(self): + self.assertEqual([], accumulate([], lambda x: x/2)) + + def test_pow(self): + self.assertEqual([1, 4, 9, 16, 25], accumulate([1, 2, 3, 4, 5], + lambda x: x*x)) + + def test_divmod(self): + inp = [10, 17, 23] + out = [(1, 3), (2, 3), (3, 2)] + self.assertEqual(out, accumulate(inp, lambda x: divmod(x, 7))) + + def test_composition(self): + inp = [10, 17, 23] + fn1 = lambda x: divmod(x, 7) + fn2 = lambda x: 7*x[0]+x[1] + self.assertEqual(inp, accumulate(accumulate(inp, fn1), fn2)) + + def test_capitalize(self): + inp = ['hello', 'world'] + out = ['HELLO', 'WORLD'] + self.assertEqual(out, accumulate(inp, str.upper)) + + def test_recursive(self): + inp = list('abc') + out = [['a1', 'a2', 'a3'], ['b1', 'b2', 'b3'], ['c1', 'c2', 'c3']] + fn = lambda x: accumulate(list('123'), lambda y: x+y) + self.assertEqual(out, accumulate(inp, fn)) + + +if __name__ == '__main__': + unittest.main() diff --git a/accumulate/example.py b/accumulate/example.py new file mode 100644 index 00000000000..ab9ca8054e8 --- /dev/null +++ b/accumulate/example.py @@ -0,0 +1,8 @@ +# [op(x) for x in seq] would be nice but trivial + + +def accumulate(seq,op): + res = [] + for el in seq: + res.append(op(el)) + return res From 39a6d408c561a4bc171c83f9a1f324deabb715e8 Mon Sep 17 00:00:00 2001 From: Hermano Cabral Date: Thu, 10 Apr 2014 22:10:22 -0300 Subject: [PATCH 27/52] New exercise: proverb --- EXERCISES.txt | 1 + proverb/example.py | 7 ++++++ proverb/proverb_test.py | 55 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 63 insertions(+) create mode 100644 proverb/example.py create mode 100644 proverb/proverb_test.py diff --git a/EXERCISES.txt b/EXERCISES.txt index e451b7839ad..ea50444cf4d 100644 --- a/EXERCISES.txt +++ b/EXERCISES.txt @@ -14,6 +14,7 @@ octal ocr-numbers difference-of-squares point-mutations +proverb phone-number pascals-triangle allergies diff --git a/proverb/example.py b/proverb/example.py new file mode 100644 index 00000000000..dba9c9df952 --- /dev/null +++ b/proverb/example.py @@ -0,0 +1,7 @@ +def proverb(itens, qualifier=''): + phrases = ['For want of a {0} the {1} was lost.'.format(el1, el2) + for el1, el2 in zip(itens, itens[1:])] + qualifier += ' ' if qualifier else '' + phrases.append('And all for the want of a {0}{1}.'.format(qualifier, + itens[0])) + return '\n'.join(phrases) diff --git a/proverb/proverb_test.py b/proverb/proverb_test.py new file mode 100644 index 00000000000..26ac938187e --- /dev/null +++ b/proverb/proverb_test.py @@ -0,0 +1,55 @@ +from proverb import proverb + +import unittest + + +class ProverbTest(unittest.TestCase): + def test_a_single_consequence(self): + expected = 'For want of a nail the shoe was lost.\n'\ + 'And all for the want of a nail.' + self.assertEqual(expected, proverb(['nail', 'shoe'])) + + def test_short_list(self): + expected = 'For want of a nail the shoe was lost.\n'\ + 'For want of a shoe the horse was lost.\n'\ + 'And all for the want of a nail.' + self.assertEqual(expected, proverb(['nail', 'shoe', 'horse'])) + + def test_long_list(self): + expected = 'For want of a nail the shoe was lost.\n'\ + 'For want of a shoe the horse was lost.\n'\ + 'For want of a horse the rider was lost.\n'\ + 'And all for the want of a nail.' + self.assertEqual(expected, proverb(['nail', 'shoe', 'horse', 'rider'])) + + def test_new_itens(self): + expected = 'For want of a key the value was lost.\n'\ + 'And all for the want of a key.' + self.assertEqual(expected, proverb(['key', 'value'])) + + def test_whole_proverb(self): + expected = 'For want of a nail the shoe was lost.\n'\ + 'For want of a shoe the horse was lost.\n'\ + 'For want of a horse the rider was lost.\n'\ + 'For want of a rider the message was lost.\n'\ + 'For want of a message the battle was lost.\n'\ + 'For want of a battle the kingdom was lost.\n'\ + 'And all for the want of a nail.' + self.assertEqual(expected, proverb(['nail', 'shoe', 'horse', 'rider', + 'message', 'battle', 'kingdom'])) + + def test_qualifier(self): + expected = 'For want of a nail the shoe was lost.\n'\ + 'For want of a shoe the horse was lost.\n'\ + 'For want of a horse the rider was lost.\n'\ + 'For want of a rider the message was lost.\n'\ + 'For want of a message the battle was lost.\n'\ + 'For want of a battle the kingdom was lost.\n'\ + 'And all for the want of a horseshoe nail.' + self.assertEqual(expected, proverb(['nail', 'shoe', 'horse', 'rider', + 'message', 'battle', 'kingdom'], + qualifier='horseshoe')) + + +if __name__ == '__main__': + unittest.main() From 482d96b668a2c914b25d57a83c0d983a11d5ca78 Mon Sep 17 00:00:00 2001 From: Simon Jakobi Date: Fri, 11 Apr 2014 04:17:34 +0200 Subject: [PATCH 28/52] Add kindergarten-garden exercise --- EXERCISES.txt | 1 + kindergarten-garden/example.py | 18 +++++++++ .../kindergarten_garden_test.py | 37 +++++++++++++++++++ 3 files changed, 56 insertions(+) create mode 100644 kindergarten-garden/example.py create mode 100644 kindergarten-garden/kindergarten_garden_test.py diff --git a/EXERCISES.txt b/EXERCISES.txt index e451b7839ad..e5f7b9ffd07 100644 --- a/EXERCISES.txt +++ b/EXERCISES.txt @@ -28,6 +28,7 @@ strain grains gigasecond palindrome-products +kindergarten-garden meetup triangle trinary diff --git a/kindergarten-garden/example.py b/kindergarten-garden/example.py new file mode 100644 index 00000000000..0544c440b1c --- /dev/null +++ b/kindergarten-garden/example.py @@ -0,0 +1,18 @@ +class Garden(object): + + __plant_names = {"C": "Clover", "G": "Grass", + "R": "Radishes", "V": "Violets"} + + def __init__(self, diagram, + students=("Alice Bob Charlie David " + "Eve Fred Ginny Harriet " + "Ileana Joseph Kincaid Larry").split()): + self.plant_rows = diagram.split() + self.students = sorted(students) + + def plants(self, student): + slot_start = self.students.index(student) * 2 + slot = slice(slot_start, slot_start + 2) + return [self.__plant_names[abbrev] + for abbrev in (self.plant_rows[0][slot] + + self.plant_rows[1][slot])] diff --git a/kindergarten-garden/kindergarten_garden_test.py b/kindergarten-garden/kindergarten_garden_test.py new file mode 100644 index 00000000000..04011a0a1b4 --- /dev/null +++ b/kindergarten-garden/kindergarten_garden_test.py @@ -0,0 +1,37 @@ +import unittest +from garden import Garden + + +class KindergartenGardenTests(unittest.TestCase): + + def test_alices_garden(self): + self.assertEqual("Radishes Clover Grass Grass".split(), + Garden("RC\nGG").plants("Alice")) + + def test_bob_and_charlies_gardens(self): + garden = Garden("VVCCGG\nVVCCGG") + self.assertEqual(["Clover"] * 4, garden.plants("Bob")) + self.assertEqual(["Grass"] * 4, garden.plants("Charlie")) + + def test_full_garden(self): + garden = Garden("VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV") + self.assertEqual("Violets Radishes Violets Radishes".split(), + garden.plants("Alice")) + self.assertEqual("Clover Grass Clover Clover".split(), + garden.plants("Bob")) + self.assertEqual("Grass Clover Clover Grass".split(), + garden.plants("Kincaid")) + self.assertEqual("Grass Violets Clover Violets".split(), + garden.plants("Larry")) + + def test_disordered_test(self): + garden = Garden("VCRRGVRG\nRVGCCGCV", + students="Samantha Patricia Xander Roger".split()) + self.assertEqual("Violets Clover Radishes Violets".split(), + garden.plants("Patricia")) + self.assertEqual("Radishes Grass Clover Violets".split(), + garden.plants("Xander")) + + +if __name__ == '__main__': + unittest.main() From 5b3572670b6531cfa080d745bd71d41ac614deda Mon Sep 17 00:00:00 2001 From: Hermano Cabral Date: Wed, 9 Apr 2014 07:34:19 -0300 Subject: [PATCH 29/52] New exercise: sublist --- EXERCISES.txt | 1 + sublist/example.py | 33 +++++++++++++++++ sublist/sublist_test.py | 79 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 113 insertions(+) create mode 100644 sublist/example.py create mode 100644 sublist/sublist_test.py diff --git a/EXERCISES.txt b/EXERCISES.txt index 2d570188343..4bd8150f1e6 100644 --- a/EXERCISES.txt +++ b/EXERCISES.txt @@ -22,6 +22,7 @@ grade-school robot-name minesweeper etl +sublist leap sum-of-multiples space-age diff --git a/sublist/example.py b/sublist/example.py new file mode 100644 index 00000000000..63470e27c0a --- /dev/null +++ b/sublist/example.py @@ -0,0 +1,33 @@ +SUBLIST = 0 +SUPERLIST = 1 +EQUAL = 2 +UNEQUAL = 3 + + +def check_lists(l1, l2): + if l1 == l2: + return EQUAL + if l1 == []: + return SUBLIST + if l2 == []: + return SUPERLIST + if is_sublist(l1, l2): + return SUBLIST + if is_sublist(l2, l1): + return SUPERLIST + return UNEQUAL + + +def is_sublist(l1, l2): + if len(l1) > len(l2): + return False + idx = -1 + while 1: + try: + idx = l2.index(l1[0], idx+1) + except ValueError: + return False + if len(l1) > len(l2) - idx: + return False + if all(el1 == el2 for el1, el2 in zip(l1, l2[idx:])): + return True diff --git a/sublist/sublist_test.py b/sublist/sublist_test.py new file mode 100644 index 00000000000..51d4cf3b01b --- /dev/null +++ b/sublist/sublist_test.py @@ -0,0 +1,79 @@ +from sublist import check_lists, SUBLIST, SUPERLIST, EQUAL, UNEQUAL + +import unittest + + +class SublistTest(unittest.TestCase): + def test_empty_lists(self): + self.assertEqual(EQUAL, check_lists([], [])) + + def test_empty_list_within(self): + self.assertEqual(SUBLIST, check_lists([], [1, 2, 3])) + + def test_within_empty_list(self): + self.assertEqual(SUPERLIST, check_lists([1], [])) + + def test_equal_lists(self): + l1 = [0, 1, 2] + l2 = [0, 1, 2] + self.assertEqual(EQUAL, check_lists(l1, l2)) + + def test_different_lists(self): + l1 = list(range(1000000)) + l2 = list(range(1, 1000001)) + self.assertEqual(UNEQUAL, check_lists(l1, l2)) + + def test_false_start(self): + l1 = [1, 2, 5] + l2 = [0, 1, 2, 3, 1, 2, 5, 6] + self.assertEqual(SUBLIST, check_lists(l1, l2)) + + def test_consecutive(self): + l1 = [1, 1, 2] + l2 = [0, 1, 1, 1, 2, 1, 2] + self.assertEqual(SUBLIST, check_lists(l1, l2)) + + def test_sublist_at_start(self): + l1 = [0, 1, 2] + l2 = [0, 1, 2, 3, 4, 5] + self.assertEqual(SUBLIST, check_lists(l1, l2)) + + def test_sublist_in_middle(self): + l1 = [2, 3, 4] + l2 = [0, 1, 2, 3, 4, 5] + self.assertEqual(SUBLIST, check_lists(l1, l2)) + + def test_sublist_at_end(self): + l1 = [3, 4, 5] + l2 = [0, 1, 2, 3, 4, 5] + self.assertEqual(SUBLIST, check_lists(l1, l2)) + + def test_at_start_of_superlist(self): + l1 = [0, 1, 2, 3, 4, 5] + l2 = [0, 1, 2] + self.assertEqual(SUPERLIST, check_lists(l1, l2)) + + def test_in_middle_of_superlist(self): + l1 = [0, 1, 2, 3, 4, 5] + l2 = [2, 3] + self.assertEqual(SUPERLIST, check_lists(l1, l2)) + + def test_at_end_of_superlist(self): + l1 = [0, 1, 2, 3, 4, 5] + l2 = [3, 4, 5] + self.assertEqual(SUPERLIST, check_lists(l1, l2)) + + def test_large_lists(self): + l1 = list(range(1000))*1000 + list(range(1000, 1100)) + l2 = list(range(900, 1050)) + self.assertEqual(SUPERLIST, check_lists(l1, l2)) + + def test_spread_sublist(self): + multiples_of_3 = list(range(3, 200, 3)) + multiples_of_15 = list(range(3, 200, 15)) + self.assertEqual(UNEQUAL, + check_lists(multiples_of_15, multiples_of_3)) + + +if __name__ == '__main__': + unittest.main() From ad51c095c7dc541de1d83a844f831d8729894746 Mon Sep 17 00:00:00 2001 From: Hermano Cabral Date: Tue, 8 Apr 2014 00:37:51 -0300 Subject: [PATCH 30/52] New exercise: queen-attack --- EXERCISES.txt | 1 + queen-attack/example.py | 27 ++++++++++++++ queen-attack/queen_attack_test.py | 61 +++++++++++++++++++++++++++++++ 3 files changed, 89 insertions(+) create mode 100644 queen-attack/example.py create mode 100644 queen-attack/queen_attack_test.py diff --git a/EXERCISES.txt b/EXERCISES.txt index 4bd8150f1e6..6fcaa9b0dc2 100644 --- a/EXERCISES.txt +++ b/EXERCISES.txt @@ -50,3 +50,4 @@ pythagorean-triplet hamming simple-cipher hexadecimal +queen-attack diff --git a/queen-attack/example.py b/queen-attack/example.py new file mode 100644 index 00000000000..f6dbf039e0d --- /dev/null +++ b/queen-attack/example.py @@ -0,0 +1,27 @@ +def board(pos1, pos2): + validate_position(pos1, pos2) + x1, y1 = pos1 + x2, y2 = pos2 + b = [['0']*8 for i in range(8)] + b[x1][y1] = 'W' + b[x2][y2] = 'B' + return [''.join(r) for r in b] + + +def can_attack(pos1, pos2): + validate_position(pos1, pos2) + x1, y1 = pos1 + x2, y2 = pos2 + dx = x1 - x2 if x1 >= x2 else x2 - x1 + dy = y1 - y2 if y1 >= y2 else y2 - y1 + if dx == dy or dx == 0 or dy == 0: + return True + return False + + +def validate_position(pos1, pos2): + if any(x < 0 or x > 7 for x in pos1 + pos2): + raise ValueError('Invalid queen position: queen out of the board') + if pos1 == pos2: + raise ValueError('Invalid queen position: both queens in the same ' + 'square: {0}'.format(pos1)) diff --git a/queen-attack/queen_attack_test.py b/queen-attack/queen_attack_test.py new file mode 100644 index 00000000000..2b43a2cc456 --- /dev/null +++ b/queen-attack/queen_attack_test.py @@ -0,0 +1,61 @@ +from queen_attack import board, can_attack + +import unittest + + +class QueenAttackTest(unittest.TestCase): + def test_board1(self): + ans = ['00000000', '00000000', '000W0000', '00000000', + '00000000', '000000B0', '00000000', '00000000'] + self.assertEqual(ans, board((2, 3), (5, 6))) + + def test_board2(self): + ans = ['000000W0', '0000000B', '00000000', '00000000', + '00000000', '00000000', '00000000', '00000000'] + self.assertEqual(ans, board((0, 6), (1, 7))) + + def test_attack_true1(self): + self.assertEqual(True, can_attack((2, 3), (5, 6))) + + def test_attack_true2(self): + self.assertEqual(True, can_attack((2, 6), (5, 3))) + + def test_attack_true3(self): + self.assertEqual(True, can_attack((2, 4), (2, 7))) + + def test_attack_true4(self): + self.assertEqual(True, can_attack((5, 4), (2, 4))) + + def test_attack_true5(self): + self.assertEqual(True, can_attack((1, 1), (6, 6))) + + def test_attack_true6(self): + self.assertEqual(True, can_attack((0, 6), (1, 7))) + + def test_attack_false1(self): + self.assertEqual(False, can_attack((4, 2), (0, 5))) + + def test_attack_false2(self): + self.assertEqual(False, can_attack((2, 3), (4, 7))) + + # If either board or can_attack are called with an invalid board position + # they should raise a ValueError with a meaningful error message. + def test_invalid_position_board(self): + with self.assertRaises(ValueError): + board((0, 0), (7, 8)) + + def test_invalid_position_can_attack(self): + with self.assertRaises(ValueError): + can_attack((0, 0), (7, 8)) + + def test_queens_same_position_board(self): + with self.assertRaises(ValueError): + board((2, 2), (2, 2)) + + def test_queens_same_position_can_attack(self): + with self.assertRaises(ValueError): + can_attack((2, 2), (2, 2)) + + +if __name__ == '__main__': + unittest.main() From 26dec9503797d96084286646c8fbbbf6b7b99b6e Mon Sep 17 00:00:00 2001 From: Hermano Cabral Date: Fri, 11 Apr 2014 00:37:19 -0300 Subject: [PATCH 31/52] New exercise: house --- EXERCISES.txt | 1 + house/example.py | 24 ++++++++++++ house/house_test.py | 92 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 117 insertions(+) create mode 100644 house/example.py create mode 100644 house/house_test.py diff --git a/EXERCISES.txt b/EXERCISES.txt index 4bd8150f1e6..33361b47cae 100644 --- a/EXERCISES.txt +++ b/EXERCISES.txt @@ -48,5 +48,6 @@ atbash-cipher wordy pythagorean-triplet hamming +house simple-cipher hexadecimal diff --git a/house/example.py b/house/example.py new file mode 100644 index 00000000000..de1d2e48625 --- /dev/null +++ b/house/example.py @@ -0,0 +1,24 @@ +parts = [('lay in', 'the house that Jack built'), + ('ate', 'the malt'), + ('killed', 'the rat'), + ('worried', 'the cat'), + ('tossed', 'the dog'), + ('milked', 'the cow with the crumpled horn'), + ('kissed', 'the maiden all forlorn'), + ('married', 'the man all tattered and torn'), + ('woke', 'the priest all shaven and shorn'), + ('kept', 'the rooster that crowed in the morn'), + ('belonged to', 'the farmer sowing his corn'), + ('', 'the horse and the hound and the horn')] + + +def _verse(n): + v = ['This is {}'.format(parts[n][1])] + v.extend(['that {0} {1}'.format(parts[i][0], parts[i][1]) + for i in range(n-1, -1, -1)]) + v[-1] += '.' + return '\n'.join(v) + + +def rhyme(): + return "\n\n".join(_verse(n) for n in range(len(parts))) diff --git a/house/house_test.py b/house/house_test.py new file mode 100644 index 00000000000..92b795be515 --- /dev/null +++ b/house/house_test.py @@ -0,0 +1,92 @@ +# Rhyme found in http://www.pitt.edu/~dash/type2035.html + +from house import rhyme + +import unittest + + +class VerseTest(unittest.TestCase): + def test_rhyme(self): + expected = 'This is the house that Jack built.\n\n'\ + 'This is the malt\n'\ + 'that lay in the house that Jack built.\n\n'\ + 'This is the rat\n'\ + 'that ate the malt\n'\ + 'that lay in the house that Jack built.\n\n'\ + 'This is the cat\n'\ + 'that killed the rat\n'\ + 'that ate the malt\n'\ + 'that lay in the house that Jack built.\n\n'\ + 'This is the dog\n'\ + 'that worried the cat\n'\ + 'that killed the rat\n'\ + 'that ate the malt\n'\ + 'that lay in the house that Jack built.\n\n'\ + 'This is the cow with the crumpled horn\n'\ + 'that tossed the dog\n'\ + 'that worried the cat\n'\ + 'that killed the rat\n'\ + 'that ate the malt\n'\ + 'that lay in the house that Jack built.\n\n'\ + 'This is the maiden all forlorn\n'\ + 'that milked the cow with the crumpled horn\n'\ + 'that tossed the dog\n'\ + 'that worried the cat\n'\ + 'that killed the rat\n'\ + 'that ate the malt\n'\ + 'that lay in the house that Jack built.\n\n'\ + 'This is the man all tattered and torn\n'\ + 'that kissed the maiden all forlorn\n'\ + 'that milked the cow with the crumpled horn\n'\ + 'that tossed the dog\n'\ + 'that worried the cat\n'\ + 'that killed the rat\n'\ + 'that ate the malt\n'\ + 'that lay in the house that Jack built.\n\n'\ + 'This is the priest all shaven and shorn\n'\ + 'that married the man all tattered and torn\n'\ + 'that kissed the maiden all forlorn\n'\ + 'that milked the cow with the crumpled horn\n'\ + 'that tossed the dog\n'\ + 'that worried the cat\n'\ + 'that killed the rat\n'\ + 'that ate the malt\n'\ + 'that lay in the house that Jack built.\n\n'\ + 'This is the rooster that crowed in the morn\n'\ + 'that woke the priest all shaven and shorn\n'\ + 'that married the man all tattered and torn\n'\ + 'that kissed the maiden all forlorn\n'\ + 'that milked the cow with the crumpled horn\n'\ + 'that tossed the dog\n'\ + 'that worried the cat\n'\ + 'that killed the rat\n'\ + 'that ate the malt\n'\ + 'that lay in the house that Jack built.\n\n'\ + 'This is the farmer sowing his corn\n'\ + 'that kept the rooster that crowed in the morn\n'\ + 'that woke the priest all shaven and shorn\n'\ + 'that married the man all tattered and torn\n'\ + 'that kissed the maiden all forlorn\n'\ + 'that milked the cow with the crumpled horn\n'\ + 'that tossed the dog\n'\ + 'that worried the cat\n'\ + 'that killed the rat\n'\ + 'that ate the malt\n'\ + 'that lay in the house that Jack built.\n\n'\ + 'This is the horse and the hound and the horn\n'\ + 'that belonged to the farmer sowing his corn\n'\ + 'that kept the rooster that crowed in the morn\n'\ + 'that woke the priest all shaven and shorn\n'\ + 'that married the man all tattered and torn\n'\ + 'that kissed the maiden all forlorn\n'\ + 'that milked the cow with the crumpled horn\n'\ + 'that tossed the dog\n'\ + 'that worried the cat\n'\ + 'that killed the rat\n'\ + 'that ate the malt\n'\ + 'that lay in the house that Jack built.' + self.assertEqual(expected, rhyme()) + + +if __name__ == '__main__': + unittest.main() From 27ef1d0a525c6fc1749721657fe403763ac945a2 Mon Sep 17 00:00:00 2001 From: Ben Lewis Date: Tue, 15 Apr 2014 22:27:49 -0600 Subject: [PATCH 32/52] fix minesweeper test to use assertRaisesRegex instead of broken context check --- minesweeper/minesweeper_test.py | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/minesweeper/minesweeper_test.py b/minesweeper/minesweeper_test.py index 241ecffbde4..4549eb4c2ac 100644 --- a/minesweeper/minesweeper_test.py +++ b/minesweeper/minesweeper_test.py @@ -68,24 +68,17 @@ def test_board9(self): @unittest.skip("Not implemented yet") def test_different_len(self): inp = ["+-+", "| |", "|* |", "| |", "+-+"] - with self.assertRaises(ValueError) as context: - board(inp) - self.assertEqual(context.exception.message, 'Invalid board') + self.assertRaisesRegex(ValueError, "^Invalid board", board, inp) @unittest.skip("Not implemented yet") def test_faulty_border(self): inp = ["+-----+", "* * |", "+-- --+"] - with self.assertRaises(ValueError) as context: - board(inp) - self.assertEqual(context.exception.message, 'Invalid board') + self.assertRaisesRegex(ValueError, "^Invalid board", board, inp) @unittest.skip("Not implemented yet") def test_invalid_char(self): inp = ["+-----+", "|X * |", "+-----+"] - with self.assertRaises(ValueError) as context: - board(inp) - self.assertEqual(context.exception.message, 'Invalid board') - + self.assertRaisesRegex(ValueError, "^Invalid board", board, inp) if __name__ == '__main__': unittest.main() From b722463821070844bd72fb2c864a3e1cab162f6d Mon Sep 17 00:00:00 2001 From: Simon Jakobi Date: Wed, 16 Apr 2014 14:47:57 +0200 Subject: [PATCH 33/52] Print assignment name for each test. --- test/check-exercises.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/check-exercises.py b/test/check-exercises.py index 27dd0af3798..c1307829517 100755 --- a/test/check-exercises.py +++ b/test/check-exercises.py @@ -56,8 +56,10 @@ def main(): failures = [] for test_file in glob.glob('./*/*_test.py'): name = assignment_name(test_file) + print('# ' + name) if check_assignment(name, test_file, modname_heuristic(test_file)): failures.append(name) + print('') if failures: print 'FAILURES: ' + ' '.join(failures) raise SystemExit(1) From 406b7e06e8fc2be1bdf8bed3e4ac2fbbd7905de9 Mon Sep 17 00:00:00 2001 From: Simon Jakobi Date: Wed, 16 Apr 2014 16:13:03 +0200 Subject: [PATCH 34/52] Let Travis also execute "skipped" tests Several test suites use the 'unittest.skip' decorator to allow students to progressively implement features in their solutions. So far, Travis, too, skipped the decorated test cases, with the result that these went entirely untested. This commit fixes this in two steps: 1. The Travis test script will now set the environment variable 'NO_SKIP'. 2. The unittest.skip decorator is replaced by the unittest.skipUnless decorator that will skip tests as usual, unless 'NO_SKIP' is set. --- pascals-triangle/pascals_triangle_test.py | 13 +++++++------ test/check-exercises.py | 1 + 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/pascals-triangle/pascals_triangle_test.py b/pascals-triangle/pascals_triangle_test.py index 035014b0545..7f420ac3c84 100644 --- a/pascals-triangle/pascals_triangle_test.py +++ b/pascals-triangle/pascals_triangle_test.py @@ -1,5 +1,6 @@ from pascals_triangle import triangle, row, is_triangle +import os import unittest class PascalsTriangleTest(unittest.TestCase): @@ -7,33 +8,33 @@ def test_triangle1(self): ans = ['1', '1 1', '1 2 1', '1 3 3 1', '1 4 6 4 1'] self.assertEqual(ans, triangle(4)) - @unittest.skip("Not implemented yet") + @unittest.skipUnless('NO_SKIP' in os.environ, "Not implemented yet") def test_triangle2(self): ans = ['1', '1 1', '1 2 1', '1 3 3 1', '1 4 6 4 1', '1 5 10 10 5 1', '1 6 15 20 15 6 1'] self.assertEqual(ans, triangle(6)) - @unittest.skip("Not implemented yet") + @unittest.skipUnless('NO_SKIP' in os.environ, "Not implemented yet") def test_is_triangle_true(self): inp = ['1', '1 1', '1 2 1', '1 3 3 1', '1 4 6 4 1', '1 5 10 10 5 1'] self.assertEqual(True, is_triangle(inp)) - @unittest.skip("Not implemented yet") + @unittest.skipUnless('NO_SKIP' in os.environ, "Not implemented yet") def test_is_triangle_false(self): inp = ['1', '1 1', '1 2 1', '1 4 4 1'] self.assertEqual(False, is_triangle(inp)) - @unittest.skip("Not implemented yet") + @unittest.skipUnless('NO_SKIP' in os.environ, "Not implemented yet") def test_row1(self): ans = '1' self.assertEqual(ans, row(0)) - @unittest.skip("Not implemented yet") + @unittest.skipUnless('NO_SKIP' in os.environ, "Not implemented yet") def test_row2(self): ans = '1 2 1' self.assertEqual(ans, row(2)) - @unittest.skip("Not implemented yet") + @unittest.skipUnless('NO_SKIP' in os.environ, "Not implemented yet") def test_row3(self): ans = '1 7 21 35 35 21 7 1' self.assertEqual(ans, row(7)) diff --git a/test/check-exercises.py b/test/check-exercises.py index c1307829517..6702bb3fdd6 100755 --- a/test/check-exercises.py +++ b/test/check-exercises.py @@ -53,6 +53,7 @@ def assignment_name(test_file): def main(): + os.environ['NO_SKIP'] = '1' # execute all tests including "@skipped" ones failures = [] for test_file in glob.glob('./*/*_test.py'): name = assignment_name(test_file) From 32ce5e97be7039eaf08f3a629976a26cc8e2bf39 Mon Sep 17 00:00:00 2001 From: Simon Jakobi Date: Thu, 17 Apr 2014 13:26:30 +0200 Subject: [PATCH 35/52] Replace assertRaisesRegex by assertRaises The unittest.assertRaisesRegexp function of Python2.7 has been renamed to assertRaisesRegex in current versions of Python3. To avoid this compatibility issue and in line with [this issue](https://github.com/exercism/xpython/issues/30) I replace these assertions with the assertRaises function. --- minesweeper/minesweeper_test.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/minesweeper/minesweeper_test.py b/minesweeper/minesweeper_test.py index 4549eb4c2ac..370df2dc8cf 100644 --- a/minesweeper/minesweeper_test.py +++ b/minesweeper/minesweeper_test.py @@ -1,3 +1,11 @@ +""" Tests for the minesweeper exercise + +Implementation note: +The board function must validate its input and raise a +ValueError with a meaningfull error message if the +input turns out to be malformed. +""" + try: from minesweeper import board except ImportError: @@ -68,17 +76,17 @@ def test_board9(self): @unittest.skip("Not implemented yet") def test_different_len(self): inp = ["+-+", "| |", "|* |", "| |", "+-+"] - self.assertRaisesRegex(ValueError, "^Invalid board", board, inp) + self.assertRaises(ValueError, board, inp) @unittest.skip("Not implemented yet") def test_faulty_border(self): inp = ["+-----+", "* * |", "+-- --+"] - self.assertRaisesRegex(ValueError, "^Invalid board", board, inp) + self.assertRaises(ValueError, board, inp) @unittest.skip("Not implemented yet") def test_invalid_char(self): inp = ["+-----+", "|X * |", "+-----+"] - self.assertRaisesRegex(ValueError, "^Invalid board", board, inp) + self.assertRaises(ValueError, board, inp) if __name__ == '__main__': unittest.main() From 43ed71f6d4c8c1b5619712be7180bd154e4a5033 Mon Sep 17 00:00:00 2001 From: Simon Jakobi Date: Thu, 17 Apr 2014 13:08:33 +0200 Subject: [PATCH 36/52] Let Travis also execute "skipped" tests This commit adapts the remaining test suites that make use of test skipping. --- minesweeper/minesweeper_test.py | 23 ++++++++++---------- ocr-numbers/ocr_test.py | 19 ++++++++-------- secret-handshake/handshake_test.py | 25 +++++++++++---------- wordy/wordy_test.py | 35 +++++++++++++++--------------- 4 files changed, 53 insertions(+), 49 deletions(-) diff --git a/minesweeper/minesweeper_test.py b/minesweeper/minesweeper_test.py index 4549eb4c2ac..29439ee4a65 100644 --- a/minesweeper/minesweeper_test.py +++ b/minesweeper/minesweeper_test.py @@ -3,6 +3,7 @@ except ImportError: raise SystemExit('Could not find minesweeper.py. Does it exist?') +import os import unittest class MinesweeperTest(unittest.TestCase): @@ -13,7 +14,7 @@ def test_board1(self): "|1*22*2|", "|111111|", "+------+"] self.assertEqual(out, board(inp)) - @unittest.skip("Not implemented yet") + @unittest.skipUnless('NO_SKIP' in os.environ, "Not implemented yet") def test_board2(self): inp = ["+-----+", "| * * |", "| |", "| * |", "| * *|", "| * * |", "+-----+"] @@ -21,43 +22,43 @@ def test_board2(self): "|1*3*2|", "+-----+"] self.assertEqual(out, board(inp)) - @unittest.skip("Not implemented yet") + @unittest.skipUnless('NO_SKIP' in os.environ, "Not implemented yet") def test_board3(self): inp = ["+-----+", "| * * |", "+-----+"] out = ["+-----+", "|1*2*1|", "+-----+"] self.assertEqual(out, board(inp)) - @unittest.skip("Not implemented yet") + @unittest.skipUnless('NO_SKIP' in os.environ, "Not implemented yet") def test_board4(self): inp = ["+-+", "|*|", "| |", "|*|", "| |", "| |", "+-+"] out = ["+-+", "|*|", "|2|", "|*|", "|1|", "| |", "+-+"] self.assertEqual(out, board(inp)) - @unittest.skip("Not implemented yet") + @unittest.skipUnless('NO_SKIP' in os.environ, "Not implemented yet") def test_board5(self): inp = ["+-+", "|*|", "+-+"] out = ["+-+", "|*|", "+-+"] self.assertEqual(out, board(inp)) - @unittest.skip("Not implemented yet") + @unittest.skipUnless('NO_SKIP' in os.environ, "Not implemented yet") def test_board6(self): inp = ["+--+", "|**|", "|**|", "+--+"] out = ["+--+", "|**|", "|**|", "+--+"] self.assertEqual(out, board(inp)) - @unittest.skip("Not implemented yet") + @unittest.skipUnless('NO_SKIP' in os.environ, "Not implemented yet") def test_board7(self): inp = ["+--+", "|**|", "|**|", "+--+"] out = ["+--+", "|**|", "|**|", "+--+"] self.assertEqual(out, board(inp)) - @unittest.skip("Not implemented yet") + @unittest.skipUnless('NO_SKIP' in os.environ, "Not implemented yet") def test_board8(self): inp = ["+---+", "|***|", "|* *|", "|***|", "+---+"] out = ["+---+", "|***|", "|*8*|", "|***|", "+---+"] self.assertEqual(out, board(inp)) - @unittest.skip("Not implemented yet") + @unittest.skipUnless('NO_SKIP' in os.environ, "Not implemented yet") def test_board9(self): inp = ["+-----+", "| |", "| * |", "| |", "| |", "| * |", "+-----+"] @@ -65,17 +66,17 @@ def test_board9(self): "|1*1 |", "+-----+"] self.assertEqual(out, board(inp)) - @unittest.skip("Not implemented yet") + @unittest.skipUnless('NO_SKIP' in os.environ, "Not implemented yet") def test_different_len(self): inp = ["+-+", "| |", "|* |", "| |", "+-+"] self.assertRaisesRegex(ValueError, "^Invalid board", board, inp) - @unittest.skip("Not implemented yet") + @unittest.skipUnless('NO_SKIP' in os.environ, "Not implemented yet") def test_faulty_border(self): inp = ["+-----+", "* * |", "+-- --+"] self.assertRaisesRegex(ValueError, "^Invalid board", board, inp) - @unittest.skip("Not implemented yet") + @unittest.skipUnless('NO_SKIP' in os.environ, "Not implemented yet") def test_invalid_char(self): inp = ["+-----+", "|X * |", "+-----+"] self.assertRaisesRegex(ValueError, "^Invalid board", board, inp) diff --git a/ocr-numbers/ocr_test.py b/ocr-numbers/ocr_test.py index 6134d3358e1..a84a2d587d5 100644 --- a/ocr-numbers/ocr_test.py +++ b/ocr-numbers/ocr_test.py @@ -3,49 +3,50 @@ except ImportError: raise SystemExit('Could not find ocr.py. Does it exist?') +import os import unittest class OcrTest(unittest.TestCase): def test_0(self): self.assertEqual('0', number([" _ ","| |","|_|"," "])) - @unittest.skip("Not implemented yet") + @unittest.skipUnless('NO_SKIP' in os.environ, "Not implemented yet") def test_1(self): self.assertEqual('1', number([" "," |"," |"," "])) - @unittest.skip("Not implemented yet") + @unittest.skipUnless('NO_SKIP' in os.environ, "Not implemented yet") def test_garbage(self): self.assertEqual('?', number([" _ "," _|"," |"," "])) - @unittest.skip("Not implemented yet") + @unittest.skipUnless('NO_SKIP' in os.environ, "Not implemented yet") def test_last_line_nonblank(self): self.assertEqual('?', number([" "," |"," |","| |"])) - @unittest.skip("Not implemented yet") + @unittest.skipUnless('NO_SKIP' in os.environ, "Not implemented yet") def test_unknown_char(self): self.assertEqual('?', number([" - "," _|"," X|"," "])) - @unittest.skip("Not implemented yet") + @unittest.skipUnless('NO_SKIP' in os.environ, "Not implemented yet") def test_too_short_row(self): with self.assertRaises(ValueError) as context: number([" "," _|"," |"," "]) self.assertEqual(context.exception.message, 'Ill-formed grid') - @unittest.skip("Not implemented yet") + @unittest.skipUnless('NO_SKIP' in os.environ, "Not implemented yet") def test_insufficient_rows(self): with self.assertRaises(ValueError) as context: number([" "," _|"," X|"]) self.assertEqual(context.exception.message, 'Ill-formed grid') - @unittest.skip("Not implemented yet") + @unittest.skipUnless('NO_SKIP' in os.environ, "Not implemented yet") def test_grid0(self): self.assertEqual([" _ ","| |","|_|"," "], grid('0')) - @unittest.skip("Not implemented yet") + @unittest.skipUnless('NO_SKIP' in os.environ, "Not implemented yet") def test_grid1(self): self.assertEqual([" "," |"," |"," "], grid('1')) - @unittest.skip("Not implemented yet") + @unittest.skipUnless('NO_SKIP' in os.environ, "Not implemented yet") def test_invalid_digit(self): with self.assertRaises(ValueError) as context: grid('2') diff --git a/secret-handshake/handshake_test.py b/secret-handshake/handshake_test.py index 48d35a01794..77d896dde2f 100644 --- a/secret-handshake/handshake_test.py +++ b/secret-handshake/handshake_test.py @@ -1,56 +1,57 @@ from handshake import handshake, code +import os import unittest class HandshakeTest(unittest.TestCase): def test_shake_int(self): self.assertEqual(['wink','jump'], handshake(9)) - @unittest.skip("Not implemented yet") + @unittest.skipUnless('NO_SKIP' in os.environ, "Not implemented yet") def test_shake_bin1(self): self.assertEqual(['close your eyes','double blink'], handshake('10110')) - @unittest.skip("Not implemented yet") + @unittest.skipUnless('NO_SKIP' in os.environ, "Not implemented yet") def test_shake_bin2(self): self.assertEqual(['wink','close your eyes'], handshake('101')) - @unittest.skip("Not implemented yet") + @unittest.skipUnless('NO_SKIP' in os.environ, "Not implemented yet") def test_shake_negative_int(self): self.assertEqual([], handshake(-9)) - @unittest.skip("Not implemented yet") + @unittest.skipUnless('NO_SKIP' in os.environ, "Not implemented yet") def test_shake_bin_invalid(self): self.assertEqual([], handshake('121')) - @unittest.skip("Not implemented yet") + @unittest.skipUnless('NO_SKIP' in os.environ, "Not implemented yet") def test_unknown_action(self): self.assertEqual('0', code(['wink','sneeze'])) - @unittest.skip("Not implemented yet") + @unittest.skipUnless('NO_SKIP' in os.environ, "Not implemented yet") def test_code1(self): self.assertEqual('1100', code(['close your eyes','jump'])) - @unittest.skip("Not implemented yet") + @unittest.skipUnless('NO_SKIP' in os.environ, "Not implemented yet") def test_code2(self): self.assertEqual('11', code(['wink','double blink'])) - @unittest.skip("Not implemented yet") + @unittest.skipUnless('NO_SKIP' in os.environ, "Not implemented yet") def test_code3(self): self.assertEqual('11010', code(['jump','double blink'])) - @unittest.skip("Not implemented yet") + @unittest.skipUnless('NO_SKIP' in os.environ, "Not implemented yet") def test_composition1(self): self.assertEqual('11011', code(handshake(27))) - @unittest.skip("Not implemented yet") + @unittest.skipUnless('NO_SKIP' in os.environ, "Not implemented yet") def test_composition2(self): self.assertEqual('1', code(handshake(1))) - @unittest.skip("Not implemented yet") + @unittest.skipUnless('NO_SKIP' in os.environ, "Not implemented yet") def test_composition3(self): self.assertEqual('111', code(handshake('111'))) - @unittest.skip("Not implemented yet") + @unittest.skipUnless('NO_SKIP' in os.environ, "Not implemented yet") def test_composition4(self): inp = ['wink','double blink','jump'] self.assertEqual(inp, handshake(code(inp))) diff --git a/wordy/wordy_test.py b/wordy/wordy_test.py index 0ddd0218d4e..b4947a29fc2 100644 --- a/wordy/wordy_test.py +++ b/wordy/wordy_test.py @@ -3,83 +3,84 @@ except ImportError: raise SystemExit('Could not find wordy.py. Does it exist?') +import os import unittest class WordyTest(unittest.TestCase): def test_simple_add_1(self): self.assertEqual(18, calculate("What is 5 plus 13?")) - @unittest.skip("Not implemented yet") + @unittest.skipUnless('NO_SKIP' in os.environ, "Not implemented yet") def test_simple_add_2(self): self.assertEqual(-8, calculate("What is 5 plus -13?")) - @unittest.skip("Not implemented yet") + @unittest.skipUnless('NO_SKIP' in os.environ, "Not implemented yet") def test_simple_sub_1(self): self.assertEqual(6, calculate("What is 103 minus 97?")) - @unittest.skip("Not implemented yet") + @unittest.skipUnless('NO_SKIP' in os.environ, "Not implemented yet") def test_simple_sub_2(self): self.assertEqual(-6, calculate("What is 97 minus 103?")) - @unittest.skip("Not implemented yet") + @unittest.skipUnless('NO_SKIP' in os.environ, "Not implemented yet") def test_simple_mult(self): self.assertEqual(21, calculate("What is 7 times 3?")) - @unittest.skip("Not implemented yet") + @unittest.skipUnless('NO_SKIP' in os.environ, "Not implemented yet") def test_simple_div(self): self.assertEqual(9, calculate("What is 45 divided by 5?")) - @unittest.skip("Not implemented yet") + @unittest.skipUnless('NO_SKIP' in os.environ, "Not implemented yet") def test_add_negative_numbers(self): self.assertEqual(-11, calculate("What is -1 plus -10?")) - @unittest.skip("Not implemented yet") + @unittest.skipUnless('NO_SKIP' in os.environ, "Not implemented yet") def test_add_more_digits(self): self.assertEqual(45801, calculate("What is 123 plus 45678?")) - @unittest.skip("Not implemented yet") + @unittest.skipUnless('NO_SKIP' in os.environ, "Not implemented yet") def test_add_twice(self): self.assertEqual(4, calculate("What is 1 plus 2 plus 1?")) - @unittest.skip("Not implemented yet") + @unittest.skipUnless('NO_SKIP' in os.environ, "Not implemented yet") def test_add_then_subtract(self): self.assertEqual(14, calculate("What is 1 plus 5 minus -8?")) - @unittest.skip("Not implemented yet") + @unittest.skipUnless('NO_SKIP' in os.environ, "Not implemented yet") def test_subtract_twice(self): self.assertEqual(-7, calculate("What is 20 minus 14 minus 13?")) - @unittest.skip("Not implemented yet") + @unittest.skipUnless('NO_SKIP' in os.environ, "Not implemented yet") def test_multiply_twice(self): self.assertEqual(-12, calculate("What is 2 multiplied by -2 multiplied by 3?")) - @unittest.skip("Not implemented yet") + @unittest.skipUnless('NO_SKIP' in os.environ, "Not implemented yet") def test_add_then_multiply(self): self.assertEqual(-8, calculate("What is -3 plus 7 multiplied by -2?")) - @unittest.skip("Not implemented yet") + @unittest.skipUnless('NO_SKIP' in os.environ, "Not implemented yet") def test_divide_twice(self): self.assertEqual(16, calculate("What is -12000 divided by 25 divided by -30?")) - @unittest.skip("Not implemented yet") + @unittest.skipUnless('NO_SKIP' in os.environ, "Not implemented yet") def test_invalid_operation(self): with self.assertRaises(ValueError) as context: calculate("What is 4 xor 7?") self.assertEqual(context.exception.message, 'Ill-formed question') - @unittest.skip("Not implemented yet") + @unittest.skipUnless('NO_SKIP' in os.environ, "Not implemented yet") def test_missing_operation(self): with self.assertRaises(ValueError) as context: calculate("What is 2 2 minus 3?") self.assertEqual(context.exception.message, 'Ill-formed question') - @unittest.skip("Not implemented yet") + @unittest.skipUnless('NO_SKIP' in os.environ, "Not implemented yet") def test_missing_number(self): with self.assertRaises(ValueError) as context: calculate("What is 7 plus times -2?") self.assertEqual(context.exception.message, 'Ill-formed question') - @unittest.skip("Not implemented yet") + @unittest.skipUnless('NO_SKIP' in os.environ, "Not implemented yet") def test_irrelevant_question(self): with self.assertRaises(ValueError) as context: calculate("Which is greater, 3 or 2?") From 8e2125078b71e9622abdf869b5f1065ce7780f06 Mon Sep 17 00:00:00 2001 From: Katrina Owen Date: Thu, 17 Apr 2014 09:39:31 -0600 Subject: [PATCH 37/52] Run against multiple environments on Travis CI --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index 44203e7e721..cdfd4f48b12 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,2 +1,5 @@ language: python +python: + - "2.7" + - "3.3" script: ./test/check-exercises.py From 7e13b3a83b5ce936d3692812589290fe48de5c00 Mon Sep 17 00:00:00 2001 From: Simon Jakobi Date: Thu, 17 Apr 2014 18:02:06 +0200 Subject: [PATCH 38/52] Adapt test script for py3k compatibility --- test/check-exercises.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/check-exercises.py b/test/check-exercises.py index 6702bb3fdd6..8aa30e381ab 100755 --- a/test/check-exercises.py +++ b/test/check-exercises.py @@ -62,10 +62,10 @@ def main(): failures.append(name) print('') if failures: - print 'FAILURES: ' + ' '.join(failures) + print('FAILURES: ' + ' '.join(failures)) raise SystemExit(1) else: - print 'SUCCESS!' + print('SUCCESS!') if __name__ == '__main__': main() From 327586a7a33487bed3b357d3e55b41cc41139c0b Mon Sep 17 00:00:00 2001 From: Simon Jakobi Date: Thu, 17 Apr 2014 18:29:24 +0200 Subject: [PATCH 39/52] Make hamming exercise compatible --- hamming/example.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/hamming/example.py b/hamming/example.py index 20026498987..8a5761560c1 100644 --- a/hamming/example.py +++ b/hamming/example.py @@ -1,2 +1,10 @@ -def hamming(s1,s2): - return sum(1 for a,b in map(None,s1,s2) if a != b) +import sys + +if sys.version_info[0] == 2: + from itertools import izip_longest as zip_longest +else: + from itertools import zip_longest + + +def hamming(s1, s2): + return sum(1 for a, b in zip_longest(s1, s2) if a != b) From 4537a8476631318a0192dd15ab1a5ff95a8b9126 Mon Sep 17 00:00:00 2001 From: Simon Jakobi Date: Thu, 17 Apr 2014 18:47:54 +0200 Subject: [PATCH 40/52] Make matrix exercise compatible with Python3 --- matrix/example.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/matrix/example.py b/matrix/example.py index 2fc6e2d0df0..9d228d0df34 100644 --- a/matrix/example.py +++ b/matrix/example.py @@ -5,4 +5,4 @@ def __init__(self, s): @property def columns(self): - return map(list, zip(*self.rows)) + return [list(tup) for tup in zip(*self.rows)] From 898e5e87b30847cc0ca47712e24072f96597726e Mon Sep 17 00:00:00 2001 From: Simon Jakobi Date: Thu, 17 Apr 2014 19:07:00 +0200 Subject: [PATCH 41/52] Make rna-transcription exercise compatible with Python3 --- rna-transcription/example.py | 7 ++++++- rna-transcription/rna_transcription_test.py | 6 +----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/rna-transcription/example.py b/rna-transcription/example.py index e6404104ab1..39bc5f177ce 100644 --- a/rna-transcription/example.py +++ b/rna-transcription/example.py @@ -1,4 +1,9 @@ -from string import maketrans +import sys + +if sys.version_info[0] == 2: + from string import maketrans +else: + maketrans = str.maketrans class DNA(object): diff --git a/rna-transcription/rna_transcription_test.py b/rna-transcription/rna_transcription_test.py index d9e6ce34ffa..daccce2de7a 100644 --- a/rna-transcription/rna_transcription_test.py +++ b/rna-transcription/rna_transcription_test.py @@ -1,9 +1,5 @@ -try: - import dna -except ImportError: - raise SystemExit('Could not find dna.py. Does it exist?') - import unittest +import dna class DNATests(unittest.TestCase): From 0213e01bfef0e15d582c392d72aaea9a86f5ff40 Mon Sep 17 00:00:00 2001 From: Simon Jakobi Date: Fri, 18 Apr 2014 10:14:34 +0200 Subject: [PATCH 42/52] Make crypto-square exercise compatible with Python3 The behavior of str.translate and str(ing).maketrans is very different in Python3. It turned out to be simpler to replace the method by a simple custom function (_cleanse). The other changes were introduced by a run of autopep8. --- crypto-square/example.py | 44 ++++++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/crypto-square/example.py b/crypto-square/example.py index 768c08e8419..79cef7f6c68 100644 --- a/crypto-square/example.py +++ b/crypto-square/example.py @@ -1,40 +1,48 @@ import math -from string import punctuation,whitespace + def encode(msg): - msg = msg.strip().translate(None,punctuation+whitespace).lower() + msg = _cleanse(msg) sqrsz = int(math.sqrt(len(msg))) - if sqrsz*sqrsz < len(msg): + if sqrsz * sqrsz < len(msg): sqrsz += 1 - + cols = [msg[i1::sqrsz] for i1 in range(sqrsz)] cols_str = ''.join(cols) - return ' '.join(cols_str[i1:i1+5] for i1 in range(0,len(cols_str),5)) - + return ' '.join(cols_str[i1:i1 + 5] for i1 in range(0, len(cols_str), 5)) + + def decode(ciph): - ciph = ciph.strip().translate(None,punctuation+whitespace).lower() + ciph = _cleanse(ciph) sqrsz = int(math.sqrt(len(ciph))) - if sqrsz*sqrsz < len(ciph): + if sqrsz * sqrsz < len(ciph): sqrsz += 1 - colsz, nbr_full_cols = divmod(len(ciph),sqrsz) + colsz, nbr_full_cols = divmod(len(ciph), sqrsz) # The matrix produced by the plaintext is in general irregular, and the # last row is usually shorter than the others. Extract this row first - full_cols_str = ciph[:(colsz+1)*nbr_full_cols] - partial_cols_str = ciph[(colsz+1)*nbr_full_cols:] - last_row = full_cols_str[colsz::colsz+1] + full_cols_str = ciph[:(colsz + 1) * nbr_full_cols] + partial_cols_str = ciph[(colsz + 1) * nbr_full_cols:] + last_row = full_cols_str[colsz::colsz + 1] # Compute the string of all concatenated columns of the colsz X sqrsz # matrix consisting of the first colsz rows of the plaintext (irregular) # matrix - trimmed_full_cols = [full_cols_str[i1:i1+colsz] - for i1 in range(0,len(full_cols_str),colsz+1)] - partial_cols = [partial_cols_str[i1:i1+colsz] - for i1 in range(0,len(partial_cols_str),colsz)] + trimmed_full_cols = [full_cols_str[i1:i1 + colsz] + for i1 in range(0, len(full_cols_str), colsz + 1)] + partial_cols = [partial_cols_str[i1:i1 + colsz] + for i1 in range(0, len(partial_cols_str), colsz)] uniform_cols_str = ''.join(trimmed_full_cols + partial_cols) - + other_rows = [uniform_cols_str[i1::colsz] for i1 in range(colsz)] - return ''.join(other_rows+[last_row]) + return ''.join(other_rows + [last_row]) + + +def _cleanse(s): + """Lowercase a string and remove punctuation and whitespace + """ + return ''.join([c for c in s if c.isalnum()]).lower() + if __name__ == '__main__': msg = 'ifmanwasmeanttostayonthegroundgodwouldhavegivenusroots' From e192944e2eb63c1328d2853ac0bb13683eb20b98 Mon Sep 17 00:00:00 2001 From: Simon Jakobi Date: Fri, 18 Apr 2014 11:13:43 +0200 Subject: [PATCH 43/52] Make ocr-numbers exercise compatible with Python3 --- ocr-numbers/example.py | 13 +++++++------ ocr-numbers/ocr_test.py | 41 +++++++++++++++++++++-------------------- 2 files changed, 28 insertions(+), 26 deletions(-) diff --git a/ocr-numbers/example.py b/ocr-numbers/example.py index 009f3030f88..32b8667ee70 100644 --- a/ocr-numbers/example.py +++ b/ocr-numbers/example.py @@ -1,16 +1,17 @@ def number(g): - if not g or len(g) < 4 or any(len(r)!=len(g[0]) for r in g): + if not g or len(g) < 4 or any(len(r) != len(g[0]) for r in g): raise ValueError('Ill-formed grid') - if g == [" _ ","| |","|_|"," "]: + if g == [" _ ", "| |", "|_|", " "]: return '0' - elif g == [" "," |"," |"," "]: + elif g == [" ", " |", " |", " "]: return '1' else: return '?' + def grid(n): if n == '0': - return [" _ ","| |","|_|"," "] + return [" _ ", "| |", "|_|", " "] elif n == '1': - return [" "," |"," |"," "] - raise ValueError('Unknown digit') \ No newline at end of file + return [" ", " |", " |", " "] + raise ValueError('Unknown digit') diff --git a/ocr-numbers/ocr_test.py b/ocr-numbers/ocr_test.py index a84a2d587d5..9806b389950 100644 --- a/ocr-numbers/ocr_test.py +++ b/ocr-numbers/ocr_test.py @@ -1,56 +1,57 @@ -try: - from ocr import number, grid -except ImportError: - raise SystemExit('Could not find ocr.py. Does it exist?') +"""Tests for the ocr-numbers exercise + +Implementation note: +Both ocr.grid and ocr.number should validate their input +and raise ValueErrors with meaningful error messages +if necessary. +""" import os import unittest +from ocr import grid, number + + class OcrTest(unittest.TestCase): + def test_0(self): - self.assertEqual('0', number([" _ ","| |","|_|"," "])) + self.assertEqual('0', number([" _ ", "| |", "|_|", " "])) @unittest.skipUnless('NO_SKIP' in os.environ, "Not implemented yet") def test_1(self): - self.assertEqual('1', number([" "," |"," |"," "])) + self.assertEqual('1', number([" ", " |", " |", " "])) @unittest.skipUnless('NO_SKIP' in os.environ, "Not implemented yet") def test_garbage(self): - self.assertEqual('?', number([" _ "," _|"," |"," "])) + self.assertEqual('?', number([" _ ", " _|", " |", " "])) @unittest.skipUnless('NO_SKIP' in os.environ, "Not implemented yet") def test_last_line_nonblank(self): - self.assertEqual('?', number([" "," |"," |","| |"])) + self.assertEqual('?', number([" ", " |", " |", "| |"])) @unittest.skipUnless('NO_SKIP' in os.environ, "Not implemented yet") def test_unknown_char(self): - self.assertEqual('?', number([" - "," _|"," X|"," "])) + self.assertEqual('?', number([" - ", " _|", " X|", " "])) @unittest.skipUnless('NO_SKIP' in os.environ, "Not implemented yet") def test_too_short_row(self): - with self.assertRaises(ValueError) as context: - number([" "," _|"," |"," "]) - self.assertEqual(context.exception.message, 'Ill-formed grid') + self.assertRaises(ValueError, number, [" ", " _|", " |", " "]) @unittest.skipUnless('NO_SKIP' in os.environ, "Not implemented yet") def test_insufficient_rows(self): - with self.assertRaises(ValueError) as context: - number([" "," _|"," X|"]) - self.assertEqual(context.exception.message, 'Ill-formed grid') + self.assertRaises(ValueError, number, [" ", " _|", " X|"]) @unittest.skipUnless('NO_SKIP' in os.environ, "Not implemented yet") def test_grid0(self): - self.assertEqual([" _ ","| |","|_|"," "], grid('0')) + self.assertEqual([" _ ", "| |", "|_|", " "], grid('0')) @unittest.skipUnless('NO_SKIP' in os.environ, "Not implemented yet") def test_grid1(self): - self.assertEqual([" "," |"," |"," "], grid('1')) + self.assertEqual([" ", " |", " |", " "], grid('1')) @unittest.skipUnless('NO_SKIP' in os.environ, "Not implemented yet") def test_invalid_digit(self): - with self.assertRaises(ValueError) as context: - grid('2') - self.assertEqual(context.exception.message, 'Unknown digit') + self.assertRaises(ValueError, grid, '2') if __name__ == '__main__': From 0bc09f206d99a191abb606e64d34a74a92576cd7 Mon Sep 17 00:00:00 2001 From: Simon Jakobi Date: Fri, 18 Apr 2014 11:21:41 +0200 Subject: [PATCH 44/52] Make hexadecimal exercise compatible with Python3 --- hexadecimal/example.py | 12 +++++------- hexadecimal/hexadecimal_test.py | 4 +++- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/hexadecimal/example.py b/hexadecimal/example.py index 513c5e6dd09..4e0388044b6 100644 --- a/hexadecimal/example.py +++ b/hexadecimal/example.py @@ -1,12 +1,10 @@ +from functools import reduce + + def hexa(s): s = s.lower() if set(s) - set('0123456789abcdef'): raise ValueError('Invalid hexadecimal string') l = [ord(c) - ord('a') + 10 if c in 'abcdef' else ord(c) - ord('0') - for c in s] - return reduce(lambda x,y:x*16 + y, l, 0) - -if __name__ == '__main__': - print hexa('19ACE') - print hexa('100') - print hexa('dead') \ No newline at end of file + for c in s] + return reduce(lambda x, y: x * 16 + y, l, 0) diff --git a/hexadecimal/hexadecimal_test.py b/hexadecimal/hexadecimal_test.py index 08f13b1c1d2..a7dbe20f5ba 100644 --- a/hexadecimal/hexadecimal_test.py +++ b/hexadecimal/hexadecimal_test.py @@ -1,11 +1,13 @@ -# To avoid trivial solutions, try to solve this problem without the +# To avoid trivial solutions, try to solve this problem without the # function int(s, base=16) from hexadecimal import hexa import unittest + class HexadecimalTest(unittest.TestCase): + def test_valid_hexa1(self): self.assertEqual(1, hexa('1')) From bc69602a97bb3244f3125d5fedec15ba87e2546f Mon Sep 17 00:00:00 2001 From: Simon Jakobi Date: Fri, 18 Apr 2014 10:32:41 +0200 Subject: [PATCH 45/52] Make wordy exercise compatible with Python3 --- wordy/example.py | 14 +++++++++++--- wordy/wordy_test.py | 31 ++++++++++++------------------- 2 files changed, 23 insertions(+), 22 deletions(-) diff --git a/wordy/example.py b/wordy/example.py index 7e0fda310fb..c1e152c7955 100644 --- a/wordy/example.py +++ b/wordy/example.py @@ -1,8 +1,16 @@ -from operator import add, div, mul, sub +import sys +from operator import add, mul, sub -VALID_OPERATIONS = {"plus": add, "minus": sub, "times": mul, +if sys.version_info[0] == 2: + from operator import div +else: + from operator import floordiv as div + + +VALID_OPERATIONS = {"plus": add, "minus": sub, "times": mul, "multiplied by": mul, "divided by": div} + def calculate(stmt): if not (stmt.startswith("What is ") and stmt.endswith("?")): raise ValueError("Ill-formed question") @@ -30,4 +38,4 @@ def calculate(stmt): op1 = VALID_OPERATIONS[oprt](op1, op2) except KeyError: raise ValueError("Ill-formed question") - return op1 \ No newline at end of file + return op1 diff --git a/wordy/wordy_test.py b/wordy/wordy_test.py index b4947a29fc2..bccbb62d087 100644 --- a/wordy/wordy_test.py +++ b/wordy/wordy_test.py @@ -1,12 +1,11 @@ -try: - from wordy import calculate -except ImportError: - raise SystemExit('Could not find wordy.py. Does it exist?') - import os import unittest +from wordy import calculate + + class WordyTest(unittest.TestCase): + def test_simple_add_1(self): self.assertEqual(18, calculate("What is 5 plus 13?")) @@ -52,7 +51,8 @@ def test_subtract_twice(self): @unittest.skipUnless('NO_SKIP' in os.environ, "Not implemented yet") def test_multiply_twice(self): - self.assertEqual(-12, calculate("What is 2 multiplied by -2 multiplied by 3?")) + self.assertEqual(-12, + calculate("What is 2 multiplied by -2 multiplied by 3?")) @unittest.skipUnless('NO_SKIP' in os.environ, "Not implemented yet") def test_add_then_multiply(self): @@ -60,31 +60,24 @@ def test_add_then_multiply(self): @unittest.skipUnless('NO_SKIP' in os.environ, "Not implemented yet") def test_divide_twice(self): - self.assertEqual(16, calculate("What is -12000 divided by 25 divided by -30?")) + self.assertEqual( + 16, calculate("What is -12000 divided by 25 divided by -30?")) @unittest.skipUnless('NO_SKIP' in os.environ, "Not implemented yet") def test_invalid_operation(self): - with self.assertRaises(ValueError) as context: - calculate("What is 4 xor 7?") - self.assertEqual(context.exception.message, 'Ill-formed question') + self.assertRaises(ValueError, calculate, "What is 4 xor 7?") @unittest.skipUnless('NO_SKIP' in os.environ, "Not implemented yet") def test_missing_operation(self): - with self.assertRaises(ValueError) as context: - calculate("What is 2 2 minus 3?") - self.assertEqual(context.exception.message, 'Ill-formed question') + self.assertRaises(ValueError, calculate, "What is 2 2 minus 3?") @unittest.skipUnless('NO_SKIP' in os.environ, "Not implemented yet") def test_missing_number(self): - with self.assertRaises(ValueError) as context: - calculate("What is 7 plus times -2?") - self.assertEqual(context.exception.message, 'Ill-formed question') + self.assertRaises(ValueError, calculate, "What is 7 plus times -2?") @unittest.skipUnless('NO_SKIP' in os.environ, "Not implemented yet") def test_irrelevant_question(self): - with self.assertRaises(ValueError) as context: - calculate("Which is greater, 3 or 2?") - self.assertEqual(context.exception.message, 'Ill-formed question') + self.assertRaises(ValueError, calculate, "Which is greater, 3 or 2?") if __name__ == '__main__': From 424027e538ac027d3c87444bfdbae02a15eb5990 Mon Sep 17 00:00:00 2001 From: Simon Jakobi Date: Sun, 20 Apr 2014 10:47:23 +0200 Subject: [PATCH 46/52] Make trinary exercise compatible with Python3 --- trinary/example.py | 10 ++++------ trinary/trinary_test.py | 1 + 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/trinary/example.py b/trinary/example.py index a1a18e0324f..96a6e6bdef8 100644 --- a/trinary/example.py +++ b/trinary/example.py @@ -1,9 +1,7 @@ +from functools import reduce + + def trinary(s): if set(s) - set('012'): return 0 - return reduce(lambda x,y:x*3 + int(y), s, 0) - -if __name__ == '__main__': - print trinary('102101') - print trinary('22222') - print trinary('10000') + return reduce(lambda x, y: x * 3 + int(y), s, 0) diff --git a/trinary/trinary_test.py b/trinary/trinary_test.py index 204c166d7a1..667922ae2a9 100644 --- a/trinary/trinary_test.py +++ b/trinary/trinary_test.py @@ -2,6 +2,7 @@ import unittest + class TrinaryTest(unittest.TestCase): def test_valid_trinary1(self): self.assertEqual(0, trinary('0')) From dcddecd74df810c9fecea3bbd2b24d26446e5692 Mon Sep 17 00:00:00 2001 From: Simon Jakobi Date: Sun, 20 Apr 2014 19:02:05 +0200 Subject: [PATCH 47/52] Make pythagorean-triplet exercise compatible with Python3 --- pythagorean-triplet/example.py | 39 +++++++++---------- .../pythagorean_triplet_test.py | 31 ++++++--------- 2 files changed, 30 insertions(+), 40 deletions(-) diff --git a/pythagorean-triplet/example.py b/pythagorean-triplet/example.py index 3910a4089c5..ea68045657c 100644 --- a/pythagorean-triplet/example.py +++ b/pythagorean-triplet/example.py @@ -1,35 +1,39 @@ from itertools import product +from functools import reduce from operator import mul from math import sqrt + def primitive_triplets(nbr): if nbr % 4 != 0: raise ValueError('Argument must be divisible by 4') - prime_factors,powers = factor(nbr/2) - args = [(1,prime_factors[i1]**powers[i1]) for i1 in range(len(powers))] + prime_factors, powers = factor(nbr / 2) + args = [(1, prime_factors[i1] ** powers[i1]) for i1 in range(len(powers))] a = [reduce(mul, p) for p in product(*args)] a.sort() - factors = [(m,n) for m,n in zip(reversed(a),a) if m>n] + factors = [(m, n) for m, n in zip(reversed(a), a) if m > n] ts = set() - for m,n in factors: - l = [nbr, m*m-n*n,m*m+n*n] + for m, n in factors: + l = [nbr, m * m - n * n, m * m + n * n] l.sort() ts.update([tuple(l)]) return ts + def is_triplet(t): t = list(t) t.sort() - a,b,c = t - return c*c == a*a + b*b + a, b, c = t + return c * c == a * a + b * b + def triplets_in_range(m, n): t = set() - for a in xrange(m,n+1): - for b in xrange(a+1,n+1): - c = int(sqrt(a*a + b*b)+0.5) - if c*c == a*a + b*b and c >= m and c <= n: - t.update([(a,b,c)]) + for a in range(m, n + 1): + for b in range(a + 1, n + 1): + c = int(sqrt(a * a + b * b) + 0.5) + if c * c == a * a + b * b and c >= m and c <= n: + t.update([(a, b, c)]) return t primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, @@ -45,10 +49,11 @@ def triplets_in_range(m, n): 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997] + def factor(n): global primes if n == 1: - return (1,),(0,) + return (1,), (0,) factors = [] powers = [] idx = 0 @@ -63,10 +68,4 @@ def factor(n): p += 1 n /= prime powers.append(p) - return factors,powers - -if __name__ == '__main__': - print primitive_triplets(4) - print primitive_triplets(84) - print primitive_triplets(288) - print triplets_in_range(50,100) \ No newline at end of file + return factors, powers diff --git a/pythagorean-triplet/pythagorean_triplet_test.py b/pythagorean-triplet/pythagorean_triplet_test.py index 5e09b9d0dc6..52d12d08ea7 100644 --- a/pythagorean-triplet/pythagorean_triplet_test.py +++ b/pythagorean-triplet/pythagorean_triplet_test.py @@ -18,7 +18,7 @@ # A primitive pythagorean triplet has its 3 componentes coprime. So, (3,4,5) is # a primitive pythagorean triplet since 3,4 and 5 don't have a common factor. # On the other hand, (6,8,10), although a pythagorean triplet, is not primitive -# aince 2 divides all three components. +# since 2 divides all three components. # # A method for finding all primitive pythagorean triplet is given in wikipedia # (http://en.wikipedia.org/wiki/Pythagorean_triple#Generating_a_triple). The @@ -29,7 +29,7 @@ # # The function primitive_triplets should then use the formula above with b set # to its argument and find all possible pairs (m,n) such that m>n, m-n is odd, -# b=2*m*n and m and n are coprime. +# b=2*m*n and m and n are coprime. # #============================================================================== @@ -37,56 +37,47 @@ import unittest + class PythagoreanTripletTest(unittest.TestCase): + def test_triplet1(self): - ans = set([(3,4,5)]) + ans = set([(3, 4, 5)]) self.assertEqual(ans, primitive_triplets(4)) -# @unittest.skip("Not implemented yet") def test_triplet2(self): ans = set([(13, 84, 85), (84, 187, 205), (84, 437, 445), (84, 1763, 1765)]) self.assertEqual(ans, primitive_triplets(84)) -# @unittest.skip("Not implemented yet") def test_triplet3(self): ans = set([(29, 420, 421), (341, 420, 541), (420, 851, 949), (420, 1189, 1261), (420, 1739, 1789), (420, 4891, 4909), (420, 11021, 11029), (420, 44099, 44101)]) self.assertEqual(ans, primitive_triplets(420)) -# @unittest.skip("Not implemented yet") def test_triplet4(self): ans = set([(175, 288, 337), (288, 20735, 20737)]) self.assertEqual(ans, primitive_triplets(288)) -# @unittest.skip("Not implemented yet") def test_range1(self): - ans = set([(3,4,5),(6,8,10)]) + ans = set([(3, 4, 5), (6, 8, 10)]) self.assertEqual(ans, triplets_in_range(1, 10)) -# @unittest.skip("Not implemented yet") def test_range2(self): - ans = set([(57,76,95),(60,63,87)]) + ans = set([(57, 76, 95), (60, 63, 87)]) self.assertEqual(ans, triplets_in_range(56, 95)) -# @unittest.skip("Not implemented yet") def test_is_triplet1(self): - self.assertEqual(True, is_triplet((29,20,21))) + self.assertEqual(True, is_triplet((29, 20, 21))) -# @unittest.skip("Not implemented yet") def test_is_triplet2(self): - self.assertEqual(False, is_triplet((25,25,1225))) + self.assertEqual(False, is_triplet((25, 25, 1225))) -# @unittest.skip("Not implemented yet") def test_is_triplet3(self): - self.assertEqual(True, is_triplet((924,43,925))) + self.assertEqual(True, is_triplet((924, 43, 925))) -# @unittest.skip("Not implemented yet") def test_odd_number(self): - with self.assertRaises(ValueError) as context: - primitive_triplets(5) - self.assertEqual(context.exception.message, 'Argument must be divisible by 4') + self.assertRaises(ValueError, primitive_triplets, 5) if __name__ == '__main__': From 5807fca7935429610801929c35813d0820d23180 Mon Sep 17 00:00:00 2001 From: Simon Jakobi Date: Sun, 20 Apr 2014 19:32:08 +0200 Subject: [PATCH 48/52] Make atbash-cipher exercise compatible with Python3 --- atbash-cipher/atbash_cipher_test.py | 26 +++++++++++++------------- atbash-cipher/example.py | 21 ++++++++++++++++----- 2 files changed, 29 insertions(+), 18 deletions(-) diff --git a/atbash-cipher/atbash_cipher_test.py b/atbash-cipher/atbash_cipher_test.py index e42b3c90ac5..1ae89199e9a 100644 --- a/atbash-cipher/atbash_cipher_test.py +++ b/atbash-cipher/atbash_cipher_test.py @@ -1,35 +1,33 @@ -try: - from atbash_cipher import encode, decode -except ImportError: - raise SystemExit('Could not find atbash_cipher.py. Does it exist?') - import unittest +from atbash_cipher import decode, encode + class AtbashCipherTest(unittest.TestCase): + def test_encode_no(self): self.assertEqual("ml", encode("no")) - + def test_encode_yes(self): self.assertEqual("bvh", encode("yes")) - + def test_encode_OMG(self): self.assertEqual("lnt", encode("OMG")) - + def test_encode_O_M_G(self): self.assertEqual("lnt", encode("O M G")) - + def test_encode_long_word(self): self.assertEqual("nrmwy oldrm tob", encode("mindblowingly")) - + def test_encode_numbers(self): self.assertEqual("gvhgr mt123 gvhgr mt", encode("Testing, 1 2 3, testing.")) - + def test_encode_sentence(self): self.assertEqual("gifgs rhurx grlm", encode("Truth is fiction.")) - + def test_encode_all_things(self): plaintext = "The quick brown fox jumps over the lazy dog." ciphertext = "gsvjf rxpyi ldmul cqfnk hlevi gsvoz abwlt" @@ -39,7 +37,9 @@ def test_decode_word(self): self.assertEqual("exercism", decode("vcvix rhn")) def test_decode_sentence(self): - self.assertEqual("anobstacleisoftenasteppingstone", decode("zmlyh gzxov rhlug vmzhg vkkrm thglm v")) + self.assertEqual("anobstacleisoftenasteppingstone", + decode("zmlyh gzxov rhlug vmzhg vkkrm thglm v")) + if __name__ == '__main__': unittest.main() diff --git a/atbash-cipher/example.py b/atbash-cipher/example.py index 021cd07d2bc..c40bf3d04aa 100644 --- a/atbash-cipher/example.py +++ b/atbash-cipher/example.py @@ -1,14 +1,25 @@ -from string import maketrans, lowercase, digits, punctuation, whitespace +from string import ascii_lowercase +import sys + +if sys.version_info[0] == 2: + from string import maketrans +else: + maketrans = str.maketrans + BLKSZ = 5 -trtbl = maketrans(lowercase+digits, "".join(reversed(lowercase))+digits) +trtbl = maketrans(ascii_lowercase, ascii_lowercase[::-1]) + def base_trans(text): - return text.lower().translate(trtbl, punctuation+whitespace) + return ''.join([c for c in text if c.isalnum()]).lower().translate(trtbl) + def encode(plain): cipher = base_trans(plain) - return " ".join([cipher[i:i+BLKSZ] for i in range(0,len(cipher),BLKSZ)]) + return " ".join([cipher[i:i + BLKSZ] + for i in range(0, len(cipher), BLKSZ)]) + def decode(ciphered): - return base_trans(ciphered) \ No newline at end of file + return base_trans(ciphered) From 34304d4985031a8ec2cdb0469c752f2e6443babe Mon Sep 17 00:00:00 2001 From: ikkebr Date: Sun, 20 Apr 2014 14:45:29 -0300 Subject: [PATCH 49/52] Updated example.py Changed xrange to range and added a conversion to int in line 11 --- pascals-triangle/example.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pascals-triangle/example.py b/pascals-triangle/example.py index 47c74842e2a..ed9fcdc3dfd 100644 --- a/pascals-triangle/example.py +++ b/pascals-triangle/example.py @@ -1,5 +1,5 @@ def triangle(nth): - return [row(i) for i in xrange(nth+1)] + return [row(i) for i in range(nth+1)] def is_triangle(t): new_t = triangle(len(t)-1) @@ -7,6 +7,6 @@ def is_triangle(t): def row(nth): r = [1] - for i in xrange(1,nth+1): - r.append(r[-1]*(nth-i+1)/i) - return " ".join([str(i) for i in r]) \ No newline at end of file + for i in range(1,nth+1): + r.append(int(r[-1]*(nth-i+1)/i)) + return " ".join([str(i) for i in r]) From ae12e202d2672a0b22e55529e6246997c1e17ed4 Mon Sep 17 00:00:00 2001 From: Simon Jakobi Date: Sun, 20 Apr 2014 19:43:22 +0200 Subject: [PATCH 50/52] Make simple-cipher exercise compatible with Python3 --- simple-cipher/example.py | 36 ++++++++++++++++++----------- simple-cipher/simple_cipher_test.py | 2 ++ 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/simple-cipher/example.py b/simple-cipher/example.py index 449331712be..467577bcb36 100644 --- a/simple-cipher/example.py +++ b/simple-cipher/example.py @@ -1,34 +1,44 @@ -from string import ascii_lowercase,punctuation,whitespace,digits +from string import ascii_lowercase from time import time import random + class Cipher: + def __init__(self, k=None): if k: - self.key = k.translate(None,punctuation+whitespace+digits).lower() + self.key = _normalize(k) else: random.seed(time()) - self.key = ''.join(random.choice(ascii_lowercase) for i in range(100)) - + self.key = ''.join(random.choice(ascii_lowercase) + for i in range(100)) + def base_encode(self, s, shift): - xkey = self.key*(len(s)//len(self.key)+1) - return ''.join(shift(c,k) for c,k in zip(s,xkey)) - + xkey = self.key * (len(s) // len(self.key) + 1) + return ''.join(shift(c, k) for c, k in zip(s, xkey)) + def encode(self, s): - s = s.translate(None,punctuation+whitespace+digits).lower() - shift = lambda c,k: chr(((ord(c)+ord(k)-2*ord('a'))\ - % len(ascii_lowercase)) + ord('a')) + s = _normalize(s) + shift = lambda c, k: chr(((ord(c) + ord(k) - 2 * ord('a')) + % len(ascii_lowercase)) + ord('a')) return self.base_encode(s, shift) def decode(self, s): - shift = lambda c,k: chr(((ord(c)-ord(k)+len(ascii_lowercase))\ - % len(ascii_lowercase)) + ord('a')) + shift = lambda c, k: chr(((ord(c) - ord(k) + len(ascii_lowercase)) + % len(ascii_lowercase)) + ord('a')) return self.base_encode(s, shift) + class Caesar(Cipher): + def __init__(self): Cipher.__init__(self, 'd') + +def _normalize(s): + return ''.join([c for c in s if c.isalpha()]).lower() + + if __name__ == '__main__': print(Caesar().encode('venividivici')) - print(Caesar().encode('\'Twas the night before Christmas')) \ No newline at end of file + print(Caesar().encode('\'Twas the night before Christmas')) diff --git a/simple-cipher/simple_cipher_test.py b/simple-cipher/simple_cipher_test.py index cb539b0e63a..c20ebc15106 100644 --- a/simple-cipher/simple_cipher_test.py +++ b/simple-cipher/simple_cipher_test.py @@ -2,7 +2,9 @@ import unittest + class CipherTest(unittest.TestCase): + def test_caesar_encode1(self): self.assertEqual('lwlvdzhvrphsurjudpplqjlqsbwkrq', Caesar().encode('itisawesomeprogramminginpython')) From ea4d2509873f897a52b67b1b06d9ea1baf42df5c Mon Sep 17 00:00:00 2001 From: Ben Lewis Date: Tue, 22 Apr 2014 21:58:09 -0600 Subject: [PATCH 51/52] Add missing leap tests --- leap/leap_test.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/leap/leap_test.py b/leap/leap_test.py index 6715f28e88b..5ffba4fb2bd 100644 --- a/leap/leap_test.py +++ b/leap/leap_test.py @@ -10,11 +10,18 @@ class YearTest(unittest.TestCase): def test_leap_year(self): self.assertTrue(Year(1996).is_leap_year()) - def test_any_old_year(self): + def test_non_leap_year(self): self.assertFalse(Year(1997).is_leap_year()) + def test_non_leap_even_year(self): + self.assertFalse(Year(1998).is_leap_year()) + + def test_century(self): + self.assertFalse(Year(1900).is_leap_year()) + def test_exceptional_century(self): self.assertTrue(Year(2400).is_leap_year()) if __name__ == '__main__': unittest.main() + From 7ff5e91bbcc55e479c4de977ae5d7111e9fce269 Mon Sep 17 00:00:00 2001 From: Simon Jakobi Date: Fri, 2 May 2014 00:39:34 +0200 Subject: [PATCH 52/52] Add Python3.4 to Travis build matrix --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index cdfd4f48b12..f0d2296ce95 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,4 +2,5 @@ language: python python: - "2.7" - "3.3" + - "3.4" script: ./test/check-exercises.py