From 96db0939c1756bcdea186e4d09624b8e861a8795 Mon Sep 17 00:00:00 2001 From: "Bradley M. Small" <bradley_small@hotmail.com> Date: Thu, 27 Feb 2020 20:49:36 -0500 Subject: [PATCH] Adding python work --- python/armstrong-numbers/README.md | 59 ++++++++ python/armstrong-numbers/armstrong_numbers.py | 5 + .../armstrong_numbers_test.py | 38 ++++++ python/hello-world/README.md | 62 +++++++++ python/hello-world/hello_world.py | 2 + python/hello-world/hello_world_test.py | 14 ++ python/high-scores/README.md | 58 ++++++++ python/high-scores/high_scores.py | 10 ++ python/high-scores/high_scores_test.py | 46 +++++++ python/pangram/README.md | 56 ++++++++ python/pangram/pangram.py | 3 + python/pangram/pangram_test.py | 46 +++++++ python/perfect-numbers/README.md | 65 +++++++++ python/perfect-numbers/fact.py | 6 + python/perfect-numbers/fact_test.py | 72 ++++++++++ python/perfect-numbers/perfect_numbers.py | 16 +++ .../perfect-numbers/perfect_numbers_test.py | 62 +++++++++ python/protein-translation/README.md | 89 +++++++++++++ .../protein_translation.py | 34 +++++ .../protein_translation_test.py | 126 ++++++++++++++++++ python/raindrops/README.md | 63 +++++++++ python/raindrops/raindrops.py | 9 ++ python/raindrops/raindrops_test.py | 67 ++++++++++ python/robot-name/README.md | 63 +++++++++ python/robot-name/robot_name.py | 22 +++ python/robot-name/robot_name_test.py | 51 +++++++ python/two-fer/README.md | 72 ++++++++++ python/two-fer/two_fer.py | 2 + python/two-fer/two_fer_test.py | 20 +++ 29 files changed, 1238 insertions(+) create mode 100644 python/armstrong-numbers/README.md create mode 100644 python/armstrong-numbers/armstrong_numbers.py create mode 100644 python/armstrong-numbers/armstrong_numbers_test.py create mode 100644 python/hello-world/README.md create mode 100644 python/hello-world/hello_world.py create mode 100644 python/hello-world/hello_world_test.py create mode 100644 python/high-scores/README.md create mode 100644 python/high-scores/high_scores.py create mode 100644 python/high-scores/high_scores_test.py create mode 100644 python/pangram/README.md create mode 100644 python/pangram/pangram.py create mode 100644 python/pangram/pangram_test.py create mode 100644 python/perfect-numbers/README.md create mode 100644 python/perfect-numbers/fact.py create mode 100644 python/perfect-numbers/fact_test.py create mode 100644 python/perfect-numbers/perfect_numbers.py create mode 100644 python/perfect-numbers/perfect_numbers_test.py create mode 100644 python/protein-translation/README.md create mode 100644 python/protein-translation/protein_translation.py create mode 100644 python/protein-translation/protein_translation_test.py create mode 100644 python/raindrops/README.md create mode 100644 python/raindrops/raindrops.py create mode 100644 python/raindrops/raindrops_test.py create mode 100644 python/robot-name/README.md create mode 100644 python/robot-name/robot_name.py create mode 100644 python/robot-name/robot_name_test.py create mode 100644 python/two-fer/README.md create mode 100644 python/two-fer/two_fer.py create mode 100644 python/two-fer/two_fer_test.py diff --git a/python/armstrong-numbers/README.md b/python/armstrong-numbers/README.md new file mode 100644 index 0000000..a2402fa --- /dev/null +++ b/python/armstrong-numbers/README.md @@ -0,0 +1,59 @@ +# Armstrong Numbers + +An [Armstrong number](https://en.wikipedia.org/wiki/Narcissistic_number) is a number that is the sum of its own digits each raised to the power of the number of digits. + +For example: + +- 9 is an Armstrong number, because `9 = 9^1 = 9` +- 10 is *not* an Armstrong number, because `10 != 1^2 + 0^2 = 1` +- 153 is an Armstrong number, because: `153 = 1^3 + 5^3 + 3^3 = 1 + 125 + 27 = 153` +- 154 is *not* an Armstrong number, because: `154 != 1^3 + 5^3 + 4^3 = 1 + 125 + 64 = 190` + +Write some code to determine whether a number is an Armstrong number. + + +## Exception messages + +Sometimes it is necessary to raise an exception. When you do this, you should include a meaningful error message to +indicate what the source of the error is. This makes your code more readable and helps significantly with debugging. Not +every exercise will require you to raise an exception, but for those that do, the tests will only pass if you include +a message. + +To raise a message with an exception, just write it as an argument to the exception type. For example, instead of +`raise Exception`, you should write: + +```python +raise Exception("Meaningful message indicating the source of the error") +``` + +## Running the tests + +To run the tests, run `pytest armstrong_numbers_test.py` + +Alternatively, you can tell Python to run the pytest module: +`python -m pytest armstrong_numbers_test.py` + +### Common `pytest` options + +- `-v` : enable verbose output +- `-x` : stop running tests on first failure +- `--ff` : run failures from previous test before running other test cases + +For other options, see `python -m pytest -h` + +## Submitting Exercises + +Note that, when trying to submit an exercise, make sure the solution is in the `$EXERCISM_WORKSPACE/python/armstrong-numbers` directory. + +You can find your Exercism workspace by running `exercism debug` and looking for the line that starts with `Workspace`. + +For more detailed information about running tests, code style and linting, +please see [Running the Tests](http://exercism.io/tracks/python/tests). + +## Source + +Wikipedia [https://en.wikipedia.org/wiki/Narcissistic_number](https://en.wikipedia.org/wiki/Narcissistic_number) + +## Submitting Incomplete Solutions + +It's possible to submit an incomplete solution so you can see how others have completed the exercise. diff --git a/python/armstrong-numbers/armstrong_numbers.py b/python/armstrong-numbers/armstrong_numbers.py new file mode 100644 index 0000000..8daf120 --- /dev/null +++ b/python/armstrong-numbers/armstrong_numbers.py @@ -0,0 +1,5 @@ +def is_armstrong_number(number): + digits = list(map(int, str(number))) + count = len(digits) + sum_raised_digits = sum([d**count for d in digits]) + return sum_raised_digits == number diff --git a/python/armstrong-numbers/armstrong_numbers_test.py b/python/armstrong-numbers/armstrong_numbers_test.py new file mode 100644 index 0000000..e36d220 --- /dev/null +++ b/python/armstrong-numbers/armstrong_numbers_test.py @@ -0,0 +1,38 @@ +import unittest + +from armstrong_numbers import is_armstrong_number + +# Tests adapted from `problem-specifications//canonical-data.json` @ v1.1.0 + + +class ArmstrongNumbersTest(unittest.TestCase): + def test_zero_is_an_armstrong_number(self): + self.assertIs(is_armstrong_number(0), True) + + def test_single_digit_numbers_are_armstrong_numbers(self): + self.assertIs(is_armstrong_number(5), True) + + def test_there_are_no_2_digit_armstrong_numbers(self): + self.assertIs(is_armstrong_number(10), False) + + def test_three_digit_number_that_is_an_armstrong_number(self): + self.assertIs(is_armstrong_number(153), True) + + def test_three_digit_number_that_is_not_an_armstrong_number(self): + self.assertIs(is_armstrong_number(100), False) + + def test_four_digit_number_that_is_an_armstrong_number(self): + self.assertIs(is_armstrong_number(9474), True) + + def test_four_digit_number_that_is_not_an_armstrong_number(self): + self.assertIs(is_armstrong_number(9475), False) + + def test_seven_digit_number_that_is_an_armstrong_number(self): + self.assertIs(is_armstrong_number(9926315), True) + + def test_seven_digit_number_that_is_not_an_armstrong_number(self): + self.assertIs(is_armstrong_number(9926314), False) + + +if __name__ == "__main__": + unittest.main() diff --git a/python/hello-world/README.md b/python/hello-world/README.md new file mode 100644 index 0000000..d5acfab --- /dev/null +++ b/python/hello-world/README.md @@ -0,0 +1,62 @@ +# Hello World + +The classical introductory exercise. Just say "Hello, World!". + +["Hello, World!"](http://en.wikipedia.org/wiki/%22Hello,_world!%22_program) is +the traditional first program for beginning programming in a new language +or environment. + +The objectives are simple: + +- Write a function that returns the string "Hello, World!". +- Run the test suite and make sure that it succeeds. +- Submit your solution and check it at the website. + +If everything goes well, you will be ready to fetch your first real exercise. + + +## Exception messages + +Sometimes it is necessary to raise an exception. When you do this, you should include a meaningful error message to +indicate what the source of the error is. This makes your code more readable and helps significantly with debugging. Not +every exercise will require you to raise an exception, but for those that do, the tests will only pass if you include +a message. + +To raise a message with an exception, just write it as an argument to the exception type. For example, instead of +`raise Exception`, you should write: + +```python +raise Exception("Meaningful message indicating the source of the error") +``` + +## Running the tests + +To run the tests, run `pytest hello_world_test.py` + +Alternatively, you can tell Python to run the pytest module: +`python -m pytest hello_world_test.py` + +### Common `pytest` options + +- `-v` : enable verbose output +- `-x` : stop running tests on first failure +- `--ff` : run failures from previous test before running other test cases + +For other options, see `python -m pytest -h` + +## Submitting Exercises + +Note that, when trying to submit an exercise, make sure the solution is in the `$EXERCISM_WORKSPACE/python/hello-world` directory. + +You can find your Exercism workspace by running `exercism debug` and looking for the line that starts with `Workspace`. + +For more detailed information about running tests, code style and linting, +please see [Running the Tests](http://exercism.io/tracks/python/tests). + +## Source + +This is an exercise to introduce users to using Exercism [http://en.wikipedia.org/wiki/%22Hello,_world!%22_program](http://en.wikipedia.org/wiki/%22Hello,_world!%22_program) + +## Submitting Incomplete Solutions + +It's possible to submit an incomplete solution so you can see how others have completed the exercise. diff --git a/python/hello-world/hello_world.py b/python/hello-world/hello_world.py new file mode 100644 index 0000000..d695ea1 --- /dev/null +++ b/python/hello-world/hello_world.py @@ -0,0 +1,2 @@ +def hello(): + return 'Hello, World!' diff --git a/python/hello-world/hello_world_test.py b/python/hello-world/hello_world_test.py new file mode 100644 index 0000000..a6b332d --- /dev/null +++ b/python/hello-world/hello_world_test.py @@ -0,0 +1,14 @@ +import unittest + +from hello_world import hello + +# Tests adapted from `problem-specifications//canonical-data.json` @ v1.1.0 + + +class HelloWorldTest(unittest.TestCase): + def test_say_hi(self): + self.assertEqual(hello(), "Hello, World!") + + +if __name__ == "__main__": + unittest.main() diff --git a/python/high-scores/README.md b/python/high-scores/README.md new file mode 100644 index 0000000..70e72d8 --- /dev/null +++ b/python/high-scores/README.md @@ -0,0 +1,58 @@ +# High Scores + +Manage a game player's High Score list. + +Your task is to build a high-score component of the classic Frogger game, one of the highest selling and addictive games of all time, and a classic of the arcade era. Your task is to write methods that return the highest score from the list, the last added score and the three highest scores. + +In this exercise, you're going to use and manipulate lists. Python lists are very versatile, and you'll find yourself using them again and again in problems both simple and complex. + +- [**Data Structures (Python 3 Documentation Tutorial)**](https://docs.python.org/3/tutorial/datastructures.html) +- [**Lists and Tuples in Python (Real Python)**](https://realpython.com/python-lists-tuples/) +- [**Python Lists (Google for Education)**](https://developers.google.com/edu/python/lists) + + +## Exception messages + +Sometimes it is necessary to raise an exception. When you do this, you should include a meaningful error message to +indicate what the source of the error is. This makes your code more readable and helps significantly with debugging. Not +every exercise will require you to raise an exception, but for those that do, the tests will only pass if you include +a message. + +To raise a message with an exception, just write it as an argument to the exception type. For example, instead of +`raise Exception`, you should write: + +```python +raise Exception("Meaningful message indicating the source of the error") +``` + +## Running the tests + +To run the tests, run `pytest high_scores_test.py` + +Alternatively, you can tell Python to run the pytest module: +`python -m pytest high_scores_test.py` + +### Common `pytest` options + +- `-v` : enable verbose output +- `-x` : stop running tests on first failure +- `--ff` : run failures from previous test before running other test cases + +For other options, see `python -m pytest -h` + +## Submitting Exercises + +Note that, when trying to submit an exercise, make sure the solution is in the `$EXERCISM_WORKSPACE/python/high-scores` directory. + +You can find your Exercism workspace by running `exercism debug` and looking for the line that starts with `Workspace`. + +For more detailed information about running tests, code style and linting, +please see [Running the Tests](http://exercism.io/tracks/python/tests). + +## Source + +Tribute to the eighties' arcade game Frogger + +## Submitting Incomplete Solutions + +It's possible to submit an incomplete solution so you can see how others have completed the exercise. diff --git a/python/high-scores/high_scores.py b/python/high-scores/high_scores.py new file mode 100644 index 0000000..d5f1f37 --- /dev/null +++ b/python/high-scores/high_scores.py @@ -0,0 +1,10 @@ +def latest(scores): + return scores[-1] + + +def personal_best(scores): + return max(scores) + + +def personal_top_three(scores): + return sorted(scores,reverse=True)[:3] diff --git a/python/high-scores/high_scores_test.py b/python/high-scores/high_scores_test.py new file mode 100644 index 0000000..9a35c6d --- /dev/null +++ b/python/high-scores/high_scores_test.py @@ -0,0 +1,46 @@ +import unittest + +from high_scores import latest, personal_best, personal_top_three + +# Tests adapted from `problem-specifications//canonical-data.json` @ v4.0.0 + + +class HighScoresTest(unittest.TestCase): + def test_latest_score(self): + scores = [100, 0, 90, 30] + expected = 30 + self.assertEqual(latest(scores), expected) + + def test_personal_best(self): + scores = [40, 100, 70] + expected = 100 + self.assertEqual(personal_best(scores), expected) + + def test_personal_top_three_from_a_list_of_scores(self): + scores = [10, 30, 90, 30, 100, 20, 10, 0, 30, 40, 40, 70, 70] + expected = [100, 90, 70] + self.assertEqual(personal_top_three(scores), expected) + + def test_personal_top_highest_to_lowest(self): + scores = [20, 10, 30] + expected = [30, 20, 10] + self.assertEqual(personal_top_three(scores), expected) + + def test_personal_top_when_there_is_a_tie(self): + scores = [40, 20, 40, 30] + expected = [40, 40, 30] + self.assertEqual(personal_top_three(scores), expected) + + def test_personal_top_when_there_are_less_than_3(self): + scores = [30, 70] + expected = [70, 30] + self.assertEqual(personal_top_three(scores), expected) + + def test_personal_top_when_there_is_only_one(self): + scores = [40] + expected = [40] + self.assertEqual(personal_top_three(scores), expected) + + +if __name__ == "__main__": + unittest.main() diff --git a/python/pangram/README.md b/python/pangram/README.md new file mode 100644 index 0000000..b81d824 --- /dev/null +++ b/python/pangram/README.md @@ -0,0 +1,56 @@ +# Pangram + +Determine if a sentence is a pangram. A pangram (Greek: παν γÏάμμα, pan gramma, +"every letter") is a sentence using every letter of the alphabet at least once. +The best known English pangram is: +> The quick brown fox jumps over the lazy dog. + +The alphabet used consists of ASCII letters `a` to `z`, inclusive, and is case +insensitive. Input will not contain non-ASCII symbols. + + +## Exception messages + +Sometimes it is necessary to raise an exception. When you do this, you should include a meaningful error message to +indicate what the source of the error is. This makes your code more readable and helps significantly with debugging. Not +every exercise will require you to raise an exception, but for those that do, the tests will only pass if you include +a message. + +To raise a message with an exception, just write it as an argument to the exception type. For example, instead of +`raise Exception`, you should write: + +```python +raise Exception("Meaningful message indicating the source of the error") +``` + +## Running the tests + +To run the tests, run `pytest pangram_test.py` + +Alternatively, you can tell Python to run the pytest module: +`python -m pytest pangram_test.py` + +### Common `pytest` options + +- `-v` : enable verbose output +- `-x` : stop running tests on first failure +- `--ff` : run failures from previous test before running other test cases + +For other options, see `python -m pytest -h` + +## Submitting Exercises + +Note that, when trying to submit an exercise, make sure the solution is in the `$EXERCISM_WORKSPACE/python/pangram` directory. + +You can find your Exercism workspace by running `exercism debug` and looking for the line that starts with `Workspace`. + +For more detailed information about running tests, code style and linting, +please see [Running the Tests](http://exercism.io/tracks/python/tests). + +## Source + +Wikipedia [https://en.wikipedia.org/wiki/Pangram](https://en.wikipedia.org/wiki/Pangram) + +## Submitting Incomplete Solutions + +It's possible to submit an incomplete solution so you can see how others have completed the exercise. diff --git a/python/pangram/pangram.py b/python/pangram/pangram.py new file mode 100644 index 0000000..33b97fd --- /dev/null +++ b/python/pangram/pangram.py @@ -0,0 +1,3 @@ +def is_pangram(sentence): + return {letter.lower() for letter in sentence + if letter.isalpha()}.__len__() == 26 diff --git a/python/pangram/pangram_test.py b/python/pangram/pangram_test.py new file mode 100644 index 0000000..4284849 --- /dev/null +++ b/python/pangram/pangram_test.py @@ -0,0 +1,46 @@ +import unittest + +from pangram import is_pangram + +# Tests adapted from `problem-specifications//canonical-data.json` @ v2.0.0 + + +class PangramTest(unittest.TestCase): + def test_empty_sentence(self): + self.assertIs(is_pangram(""), False) + + def test_perfect_lower_case(self): + self.assertIs(is_pangram("abcdefghijklmnopqrstuvwxyz"), True) + + def test_only_lower_case(self): + self.assertIs(is_pangram("the quick brown fox jumps over the lazy dog"), True) + + def test_missing_the_letter_x(self): + self.assertIs( + is_pangram("a quick movement of the enemy will jeopardize five gunboats"), + False, + ) + + def test_missing_the_letter_h(self): + self.assertIs(is_pangram("five boxing wizards jump quickly at it"), False) + + def test_with_underscores(self): + self.assertIs(is_pangram("the_quick_brown_fox_jumps_over_the_lazy_dog"), True) + + def test_with_numbers(self): + self.assertIs( + is_pangram("the 1 quick brown fox jumps over the 2 lazy dogs"), True + ) + + def test_missing_letters_replaced_by_numbers(self): + self.assertIs(is_pangram("7h3 qu1ck brown fox jumps ov3r 7h3 lazy dog"), False) + + def test_mixed_case_and_punctuation(self): + self.assertIs(is_pangram('"Five quacking Zephyrs jolt my wax bed."'), True) + + def test_case_insensitive(self): + self.assertIs(is_pangram("the quick brown fox jumps over with lazy FX"), False) + + +if __name__ == "__main__": + unittest.main() diff --git a/python/perfect-numbers/README.md b/python/perfect-numbers/README.md new file mode 100644 index 0000000..7759f9e --- /dev/null +++ b/python/perfect-numbers/README.md @@ -0,0 +1,65 @@ +# Perfect Numbers + +Determine if a number is perfect, abundant, or deficient based on +Nicomachus' (60 - 120 CE) classification scheme for natural numbers. + +The Greek mathematician [Nicomachus](https://en.wikipedia.org/wiki/Nicomachus) devised a classification scheme for natural numbers, identifying each as belonging uniquely to the categories of **perfect**, **abundant**, or **deficient** based on their [aliquot sum](https://en.wikipedia.org/wiki/Aliquot_sum). The aliquot sum is defined as the sum of the factors of a number not including the number itself. For example, the aliquot sum of 15 is (1 + 3 + 5) = 9 + +- **Perfect**: aliquot sum = number + - 6 is a perfect number because (1 + 2 + 3) = 6 + - 28 is a perfect number because (1 + 2 + 4 + 7 + 14) = 28 +- **Abundant**: aliquot sum > number + - 12 is an abundant number because (1 + 2 + 3 + 4 + 6) = 16 + - 24 is an abundant number because (1 + 2 + 3 + 4 + 6 + 8 + 12) = 36 +- **Deficient**: aliquot sum < number + - 8 is a deficient number because (1 + 2 + 4) = 7 + - Prime numbers are deficient + +Implement a way to determine whether a given number is **perfect**. Depending on your language track, you may also need to implement a way to determine whether a given number is **abundant** or **deficient**. + + +## Exception messages + +Sometimes it is necessary to raise an exception. When you do this, you should include a meaningful error message to +indicate what the source of the error is. This makes your code more readable and helps significantly with debugging. Not +every exercise will require you to raise an exception, but for those that do, the tests will only pass if you include +a message. + +To raise a message with an exception, just write it as an argument to the exception type. For example, instead of +`raise Exception`, you should write: + +```python +raise Exception("Meaningful message indicating the source of the error") +``` + +## Running the tests + +To run the tests, run `pytest perfect_numbers_test.py` + +Alternatively, you can tell Python to run the pytest module: +`python -m pytest perfect_numbers_test.py` + +### Common `pytest` options + +- `-v` : enable verbose output +- `-x` : stop running tests on first failure +- `--ff` : run failures from previous test before running other test cases + +For other options, see `python -m pytest -h` + +## Submitting Exercises + +Note that, when trying to submit an exercise, make sure the solution is in the `$EXERCISM_WORKSPACE/python/perfect-numbers` directory. + +You can find your Exercism workspace by running `exercism debug` and looking for the line that starts with `Workspace`. + +For more detailed information about running tests, code style and linting, +please see [Running the Tests](http://exercism.io/tracks/python/tests). + +## Source + +Taken from Chapter 2 of Functional Thinking by Neal Ford. [http://shop.oreilly.com/product/0636920029687.do](http://shop.oreilly.com/product/0636920029687.do) + +## Submitting Incomplete Solutions + +It's possible to submit an incomplete solution so you can see how others have completed the exercise. diff --git a/python/perfect-numbers/fact.py b/python/perfect-numbers/fact.py new file mode 100644 index 0000000..7307e24 --- /dev/null +++ b/python/perfect-numbers/fact.py @@ -0,0 +1,6 @@ +"""Fact module.""" + + +def fact(num): + return [] if not num else sorted( + list((x for x in range(1, num+1) if num % x == 0))) diff --git a/python/perfect-numbers/fact_test.py b/python/perfect-numbers/fact_test.py new file mode 100644 index 0000000..bdd5ab4 --- /dev/null +++ b/python/perfect-numbers/fact_test.py @@ -0,0 +1,72 @@ +"""Unit tests for fact module.""" + + +import fact + + +def test_fact_0(): + assert fact.fact(0) == [] + + +def test_fact_1(): + assert fact.fact(1) == [1] + + +def test_fact_2(): + assert fact.fact(2) == [1, 2] + + +def test_fact_3(): + assert fact.fact(3) == [1, 3] + + +def test_fact_4(): + assert fact.fact(4) == [1, 2, 4] + + +def test_fact_5(): + assert fact.fact(5) == [1, 5] + + +def test_fact_6(): + assert fact.fact(6) == [1, 2, 3, 6] + + +def test_fact_12(): + assert fact.fact(12) == [1, 2, 3, 4, 6, 12] + + +def test_fact_36(): + assert fact.fact(36) == [1, 2, 3, 4, 6, 9, 12, 18, 36] + + +def test_fact_big(): + assert fact.fact(33550336) == [ + 1, + 2, + 4, + 8, + 16, + 32, + 64, + 128, + 256, + 512, + 1024, + 2048, + 4096, + 8191, + 16382, + 32764, + 65528, + 131056, + 262112, + 524224, + 1048448, + 2096896, + 4193792, + 8387584, + 16775168, + 33550336 + ] + diff --git a/python/perfect-numbers/perfect_numbers.py b/python/perfect-numbers/perfect_numbers.py new file mode 100644 index 0000000..1efa1c8 --- /dev/null +++ b/python/perfect-numbers/perfect_numbers.py @@ -0,0 +1,16 @@ +import fact + + +def classify(number): + if number < 1: + raise ValueError('Must be > 0') + if not isinstance(number, int): + raise ValueError('Must be natural number') + + aliquot = sum(fact.fact(number)[:-1]) + if aliquot == number: + return 'perfect' + if aliquot > number: + return 'abundant' + if aliquot < number: + return 'deficient' diff --git a/python/perfect-numbers/perfect_numbers_test.py b/python/perfect-numbers/perfect_numbers_test.py new file mode 100644 index 0000000..2ccb268 --- /dev/null +++ b/python/perfect-numbers/perfect_numbers_test.py @@ -0,0 +1,62 @@ +import unittest + +from perfect_numbers import classify + +# Tests adapted from `problem-specifications//canonical-data.json` @ v1.1.0 + + +class PerfectNumbersTest(unittest.TestCase): + def test_smallest_perfect_number_is_classified_correctly(self): + self.assertIs(classify(6), "perfect") + + def test_medium_perfect_number_is_classified_correctly(self): + self.assertIs(classify(28), "perfect") + + def test_large_perfect_number_is_classified_correctly(self): + self.assertIs(classify(33550336), "perfect") + + +class AbundantNumbersTest(unittest.TestCase): + def test_smallest_abundant_number_is_classified_correctly(self): + self.assertIs(classify(12), "abundant") + + def test_medium_abundant_number_is_classified_correctly(self): + self.assertIs(classify(30), "abundant") + + def test_large_abundant_number_is_classified_correctly(self): + self.assertIs(classify(33550335), "abundant") + + +class DeficientNumbersTest(unittest.TestCase): + def test_smallest_prime_deficient_number_is_classified_correctly(self): + self.assertIs(classify(2), "deficient") + + def test_smallest_non_prime_deficient_number_is_classified_correctly(self): + self.assertIs(classify(4), "deficient") + + def test_medium_deficient_number_is_classified_correctly(self): + self.assertIs(classify(32), "deficient") + + def test_large_deficient_number_is_classified_correctly(self): + self.assertIs(classify(33550337), "deficient") + + def test_edge_case_no_factors_other_than_itself_is_classified_correctly(self): + self.assertIs(classify(1), "deficient") + + +class InvalidInputsTest(unittest.TestCase): + def test_zero_is_rejected_not_a_natural_number(self): + with self.assertRaisesWithMessage(ValueError): + classify(0) + + def test_negative_integer_is_rejected_not_a_natural_number(self): + with self.assertRaisesWithMessage(ValueError): + classify(-1) + + # Utility functions + def assertRaisesWithMessage(self, exception): + return self.assertRaisesRegex(exception, r".+") + + +if __name__ == "__main__": + unittest.main() diff --git a/python/protein-translation/README.md b/python/protein-translation/README.md new file mode 100644 index 0000000..24da4be --- /dev/null +++ b/python/protein-translation/README.md @@ -0,0 +1,89 @@ +# Protein Translation + +Translate RNA sequences into proteins. + +RNA can be broken into three nucleotide sequences called codons, and then translated to a polypeptide like so: + +RNA: `"AUGUUUUCU"` => translates to + +Codons: `"AUG", "UUU", "UCU"` +=> which become a polypeptide with the following sequence => + +Protein: `"Methionine", "Phenylalanine", "Serine"` + +There are 64 codons which in turn correspond to 20 amino acids; however, all of the codon sequences and resulting amino acids are not important in this exercise. If it works for one codon, the program should work for all of them. +However, feel free to expand the list in the test suite to include them all. + +There are also three terminating codons (also known as 'STOP' codons); if any of these codons are encountered (by the ribosome), all translation ends and the protein is terminated. + +All subsequent codons after are ignored, like this: + +RNA: `"AUGUUUUCUUAAAUG"` => + +Codons: `"AUG", "UUU", "UCU", "UAA", "AUG"` => + +Protein: `"Methionine", "Phenylalanine", "Serine"` + +Note the stop codon `"UAA"` terminates the translation and the final methionine is not translated into the protein sequence. + +Below are the codons and resulting Amino Acids needed for the exercise. + +Codon | Protein +:--- | :--- +AUG | Methionine +UUU, UUC | Phenylalanine +UUA, UUG | Leucine +UCU, UCC, UCA, UCG | Serine +UAU, UAC | Tyrosine +UGU, UGC | Cysteine +UGG | Tryptophan +UAA, UAG, UGA | STOP + +Learn more about [protein translation on Wikipedia](http://en.wikipedia.org/wiki/Translation_(biology)) + + +## Exception messages + +Sometimes it is necessary to raise an exception. When you do this, you should include a meaningful error message to +indicate what the source of the error is. This makes your code more readable and helps significantly with debugging. Not +every exercise will require you to raise an exception, but for those that do, the tests will only pass if you include +a message. + +To raise a message with an exception, just write it as an argument to the exception type. For example, instead of +`raise Exception`, you should write: + +```python +raise Exception("Meaningful message indicating the source of the error") +``` + +## Running the tests + +To run the tests, run `pytest protein_translation_test.py` + +Alternatively, you can tell Python to run the pytest module: +`python -m pytest protein_translation_test.py` + +### Common `pytest` options + +- `-v` : enable verbose output +- `-x` : stop running tests on first failure +- `--ff` : run failures from previous test before running other test cases + +For other options, see `python -m pytest -h` + +## Submitting Exercises + +Note that, when trying to submit an exercise, make sure the solution is in the `$EXERCISM_WORKSPACE/python/protein-translation` directory. + +You can find your Exercism workspace by running `exercism debug` and looking for the line that starts with `Workspace`. + +For more detailed information about running tests, code style and linting, +please see [Running the Tests](http://exercism.io/tracks/python/tests). + +## Source + +Tyler Long + +## Submitting Incomplete Solutions + +It's possible to submit an incomplete solution so you can see how others have completed the exercise. diff --git a/python/protein-translation/protein_translation.py b/python/protein-translation/protein_translation.py new file mode 100644 index 0000000..96e9cc3 --- /dev/null +++ b/python/protein-translation/protein_translation.py @@ -0,0 +1,34 @@ +protein_dict = { + "AUG": "Methionine", + "UUU": "Phenylalanine", + "UUC": "Phenylalanine", + "UUA": "Leucine", + "UUG": "Leucine", + "UCU": "Serine", + "UCC": "Serine", + "UCA": "Serine", + "UCG": "Serine", + "UAU": "Tyrosine", + "UAC": "Tyrosine", + "UGU": "Cysteine", + "UGC": "Cysteine", + "UGG": "Tryptophan", + "UAA": "Stop", + "UAG": "Stop", + "UGA": "Stop", +} + + +def proteins(strand): + protein_list = [] + codon_list = [] + for triplet in range(0, len(strand), 3): + codon_list.append(strand[triplet:triplet+3]) + + for codon in codon_list: + protein = protein_dict[codon] + if protein == 'Stop': + break + protein_list.append(protein) + + return protein_list diff --git a/python/protein-translation/protein_translation_test.py b/python/protein-translation/protein_translation_test.py new file mode 100644 index 0000000..c14e93d --- /dev/null +++ b/python/protein-translation/protein_translation_test.py @@ -0,0 +1,126 @@ +import unittest + +from protein_translation import proteins + +# Tests adapted from `problem-specifications//canonical-data.json` @ v1.1.1 + + +class ProteinTranslationTest(unittest.TestCase): + def test_methionine_rna_sequence(self): + value = "AUG" + expected = ["Methionine"] + self.assertEqual(proteins(value), expected) + + def test_phenylalanine_rna_sequence_1(self): + value = "UUU" + expected = ["Phenylalanine"] + self.assertEqual(proteins(value), expected) + + def test_phenylalanine_rna_sequence_2(self): + value = "UUC" + expected = ["Phenylalanine"] + self.assertEqual(proteins(value), expected) + + def test_leucine_rna_sequence_1(self): + value = "UUA" + expected = ["Leucine"] + self.assertEqual(proteins(value), expected) + + def test_leucine_rna_sequence_2(self): + value = "UUG" + expected = ["Leucine"] + self.assertEqual(proteins(value), expected) + + def test_serine_rna_sequence_1(self): + value = "UCU" + expected = ["Serine"] + self.assertEqual(proteins(value), expected) + + def test_serine_rna_sequence_2(self): + value = "UCC" + expected = ["Serine"] + self.assertEqual(proteins(value), expected) + + def test_serine_rna_sequence_3(self): + value = "UCA" + expected = ["Serine"] + self.assertEqual(proteins(value), expected) + + def test_serine_rna_sequence_4(self): + value = "UCG" + expected = ["Serine"] + self.assertEqual(proteins(value), expected) + + def test_tyrosine_rna_sequence_1(self): + value = "UAU" + expected = ["Tyrosine"] + self.assertEqual(proteins(value), expected) + + def test_tyrosine_rna_sequence_2(self): + value = "UAC" + expected = ["Tyrosine"] + self.assertEqual(proteins(value), expected) + + def test_cysteine_rna_sequence_1(self): + value = "UGU" + expected = ["Cysteine"] + self.assertEqual(proteins(value), expected) + + def test_cysteine_rna_sequence_2(self): + value = "UGC" + expected = ["Cysteine"] + self.assertEqual(proteins(value), expected) + + def test_tryptophan_rna_sequence(self): + value = "UGG" + expected = ["Tryptophan"] + self.assertEqual(proteins(value), expected) + + def test_stop_codon_rna_sequence_1(self): + value = "UAA" + expected = [] + self.assertEqual(proteins(value), expected) + + def test_stop_codon_rna_sequence_2(self): + value = "UAG" + expected = [] + self.assertEqual(proteins(value), expected) + + def test_stop_codon_rna_sequence_3(self): + value = "UGA" + expected = [] + self.assertEqual(proteins(value), expected) + + def test_translate_rna_strand_into_correct_protein_list(self): + value = "AUGUUUUGG" + expected = ["Methionine", "Phenylalanine", "Tryptophan"] + self.assertEqual(proteins(value), expected) + + def test_translation_stops_if_stop_codon_at_beginning_of_sequence(self): + value = "UAGUGG" + expected = [] + self.assertEqual(proteins(value), expected) + + def test_translation_stops_if_stop_codon_at_end_of_two_codon_sequence(self): + value = "UGGUAG" + expected = ["Tryptophan"] + self.assertEqual(proteins(value), expected) + + def test_translation_stops_if_stop_codon_at_end_of_three_codon_sequence(self): + value = "AUGUUUUAA" + expected = ["Methionine", "Phenylalanine"] + self.assertEqual(proteins(value), expected) + + def test_translation_stops_if_stop_codon_in_middle_of_three_codon_sequence(self): + value = "UGGUAGUGG" + expected = ["Tryptophan"] + self.assertEqual(proteins(value), expected) + + def test_translation_stops_if_stop_codon_in_middle_of_six_codon_sequence(self): + value = "UGGUGUUAUUAAUGGUUU" + expected = ["Tryptophan", "Cysteine", "Tyrosine"] + self.assertEqual(proteins(value), expected) + + +if __name__ == "__main__": + unittest.main() diff --git a/python/raindrops/README.md b/python/raindrops/README.md new file mode 100644 index 0000000..acfac81 --- /dev/null +++ b/python/raindrops/README.md @@ -0,0 +1,63 @@ +# Raindrops + +Your task is to convert a number into a string that contains raindrop sounds corresponding to certain potential factors. A factor is a number that evenly divides into another number, leaving no remainder. The simplest way to test if a one number is a factor of another is to use the [modulo operation](https://en.wikipedia.org/wiki/Modulo_operation). + +The rules of `raindrops` are that if a given number: + +- has 3 as a factor, add 'Pling' to the result. +- has 5 as a factor, add 'Plang' to the result. +- has 7 as a factor, add 'Plong' to the result. +- _does not_ have any of 3, 5, or 7 as a factor, the result should be the digits of the number. + +## Examples + +- 28 has 7 as a factor, but not 3 or 5, so the result would be "Plong". +- 30 has both 3 and 5 as factors, but not 7, so the result would be "PlingPlang". +- 34 is not factored by 3, 5, or 7, so the result would be "34". + + +## Exception messages + +Sometimes it is necessary to raise an exception. When you do this, you should include a meaningful error message to +indicate what the source of the error is. This makes your code more readable and helps significantly with debugging. Not +every exercise will require you to raise an exception, but for those that do, the tests will only pass if you include +a message. + +To raise a message with an exception, just write it as an argument to the exception type. For example, instead of +`raise Exception`, you should write: + +```python +raise Exception("Meaningful message indicating the source of the error") +``` + +## Running the tests + +To run the tests, run `pytest raindrops_test.py` + +Alternatively, you can tell Python to run the pytest module: +`python -m pytest raindrops_test.py` + +### Common `pytest` options + +- `-v` : enable verbose output +- `-x` : stop running tests on first failure +- `--ff` : run failures from previous test before running other test cases + +For other options, see `python -m pytest -h` + +## Submitting Exercises + +Note that, when trying to submit an exercise, make sure the solution is in the `$EXERCISM_WORKSPACE/python/raindrops` directory. + +You can find your Exercism workspace by running `exercism debug` and looking for the line that starts with `Workspace`. + +For more detailed information about running tests, code style and linting, +please see [Running the Tests](http://exercism.io/tracks/python/tests). + +## Source + +A variation on FizzBuzz, a famous technical interview question that is intended to weed out potential candidates. That question is itself derived from Fizz Buzz, a popular children's game for teaching division. [https://en.wikipedia.org/wiki/Fizz_buzz](https://en.wikipedia.org/wiki/Fizz_buzz) + +## Submitting Incomplete Solutions + +It's possible to submit an incomplete solution so you can see how others have completed the exercise. diff --git a/python/raindrops/raindrops.py b/python/raindrops/raindrops.py new file mode 100644 index 0000000..50a7f87 --- /dev/null +++ b/python/raindrops/raindrops.py @@ -0,0 +1,9 @@ +def convert(number): + conc_dict = {3: "Pling", 5: "Plang", 7: "Plong"} + + hold = "" + for fact, name in conc_dict.items(): + if number % fact == 0: + hold += name + + return hold or str(number) diff --git a/python/raindrops/raindrops_test.py b/python/raindrops/raindrops_test.py new file mode 100644 index 0000000..169015f --- /dev/null +++ b/python/raindrops/raindrops_test.py @@ -0,0 +1,67 @@ +import unittest + +from raindrops import convert + +# Tests adapted from `problem-specifications//canonical-data.json` @ v1.1.0 + + +class RaindropsTest(unittest.TestCase): + def test_the_sound_for_1_is_1(self): + self.assertEqual(convert(1), "1") + + def test_the_sound_for_3_is_pling(self): + self.assertEqual(convert(3), "Pling") + + def test_the_sound_for_5_is_plang(self): + self.assertEqual(convert(5), "Plang") + + def test_the_sound_for_7_is_plong(self): + self.assertEqual(convert(7), "Plong") + + def test_the_sound_for_6_is_pling_as_it_has_a_factor_3(self): + self.assertEqual(convert(6), "Pling") + + def test_2_to_the_power_3_does_not_make_a_raindrop_sound_as_3_is_the_exponent_not_the_base( + self + ): + self.assertEqual(convert(8), "8") + + def test_the_sound_for_9_is_pling_as_it_has_a_factor_3(self): + self.assertEqual(convert(9), "Pling") + + def test_the_sound_for_10_is_plang_as_it_has_a_factor_5(self): + self.assertEqual(convert(10), "Plang") + + def test_the_sound_for_14_is_plong_as_it_has_a_factor_of_7(self): + self.assertEqual(convert(14), "Plong") + + def test_the_sound_for_15_is_pling_plang_as_it_has_factors_3_and_5(self): + self.assertEqual(convert(15), "PlingPlang") + + def test_the_sound_for_21_is_pling_plong_as_it_has_factors_3_and_7(self): + self.assertEqual(convert(21), "PlingPlong") + + def test_the_sound_for_25_is_plang_as_it_has_a_factor_5(self): + self.assertEqual(convert(25), "Plang") + + def test_the_sound_for_27_is_pling_as_it_has_a_factor_3(self): + self.assertEqual(convert(27), "Pling") + + def test_the_sound_for_35_is_plang_plong_as_it_has_factors_5_and_7(self): + self.assertEqual(convert(35), "PlangPlong") + + def test_the_sound_for_49_is_plong_as_it_has_a_factor_7(self): + self.assertEqual(convert(49), "Plong") + + def test_the_sound_for_52_is_52(self): + self.assertEqual(convert(52), "52") + + def test_the_sound_for_105_is_pling_plang_plong_as_it_has_factors_3_5_and_7(self): + self.assertEqual(convert(105), "PlingPlangPlong") + + def test_the_sound_for_3125_is_plang_as_it_has_a_factor_5(self): + self.assertEqual(convert(3125), "Plang") + + +if __name__ == "__main__": + unittest.main() diff --git a/python/robot-name/README.md b/python/robot-name/README.md new file mode 100644 index 0000000..e8e1c95 --- /dev/null +++ b/python/robot-name/README.md @@ -0,0 +1,63 @@ +# Robot Name + +Manage robot factory settings. + +When robots come off the factory floor, they have no name. + +The first time you boot them up, a random name is generated in the format +of two uppercase letters followed by three digits, such as RX837 or BC811. + +Every once in a while we need to reset a robot to its factory settings, +which means that their name gets wiped. The next time you ask, it will +respond with a new random name. + +The names must be random: they should not follow a predictable sequence. +Random names means a risk of collisions. Your solution must ensure that +every existing robot has a unique name. + + +## Exception messages + +Sometimes it is necessary to raise an exception. When you do this, you should include a meaningful error message to +indicate what the source of the error is. This makes your code more readable and helps significantly with debugging. Not +every exercise will require you to raise an exception, but for those that do, the tests will only pass if you include +a message. + +To raise a message with an exception, just write it as an argument to the exception type. For example, instead of +`raise Exception`, you should write: + +```python +raise Exception("Meaningful message indicating the source of the error") +``` + +## Running the tests + +To run the tests, run `pytest robot_name_test.py` + +Alternatively, you can tell Python to run the pytest module: +`python -m pytest robot_name_test.py` + +### Common `pytest` options + +- `-v` : enable verbose output +- `-x` : stop running tests on first failure +- `--ff` : run failures from previous test before running other test cases + +For other options, see `python -m pytest -h` + +## Submitting Exercises + +Note that, when trying to submit an exercise, make sure the solution is in the `$EXERCISM_WORKSPACE/python/robot-name` directory. + +You can find your Exercism workspace by running `exercism debug` and looking for the line that starts with `Workspace`. + +For more detailed information about running tests, code style and linting, +please see [Running the Tests](http://exercism.io/tracks/python/tests). + +## Source + +A debugging session with Paul Blackwell at gSchool. [http://gschool.it](http://gschool.it) + +## Submitting Incomplete Solutions + +It's possible to submit an incomplete solution so you can see how others have completed the exercise. diff --git a/python/robot-name/robot_name.py b/python/robot-name/robot_name.py new file mode 100644 index 0000000..82ec001 --- /dev/null +++ b/python/robot-name/robot_name.py @@ -0,0 +1,22 @@ +import random +import string + + +class Robot: + Names = set() + + def __init__(self): + self.name = '' + self._gen_name() + + def reset(self): + self.name = '' + self._gen_name() + + def _gen_name(self): + while not self.name or self.name in Robot.Names: + self.name = ''.join( + random.choices(string.ascii_uppercase, k=2) + + random.choices(string.digits, k=3) + ) + Robot.Names.add(self.name) diff --git a/python/robot-name/robot_name_test.py b/python/robot-name/robot_name_test.py new file mode 100644 index 0000000..f023b05 --- /dev/null +++ b/python/robot-name/robot_name_test.py @@ -0,0 +1,51 @@ +import unittest +import random + +from robot_name import Robot + + +class RobotNameTest(unittest.TestCase): + # assertRegex() alias to adress DeprecationWarning + # assertRegexpMatches got renamed in version 3.2 + if not hasattr(unittest.TestCase, "assertRegex"): + assertRegex = unittest.TestCase.assertRegexpMatches + + name_re = r'^[A-Z]{2}\d{3}$' + + def test_has_name(self): + self.assertRegex(Robot().name, self.name_re) + + def test_name_sticks(self): + robot = Robot() + robot.name + self.assertEqual(robot.name, robot.name) + + def test_different_robots_have_different_names(self): + self.assertNotEqual( + Robot().name, + Robot().name + ) + + def test_reset_name(self): + # Set a seed + seed = "Totally random." + + # Initialize RNG using the seed + random.seed(seed) + + # Call the generator + robot = Robot() + name = robot.name + + # Reinitialize RNG using seed + random.seed(seed) + + # Call the generator again + robot.reset() + name2 = robot.name + self.assertNotEqual(name, name2) + self.assertRegex(name2, self.name_re) + + +if __name__ == '__main__': + unittest.main() diff --git a/python/two-fer/README.md b/python/two-fer/README.md new file mode 100644 index 0000000..5528964 --- /dev/null +++ b/python/two-fer/README.md @@ -0,0 +1,72 @@ +# Two Fer + +`Two-fer` or `2-fer` is short for two for one. One for you and one for me. + +Given a name, return a string with the message: + +```text +One for X, one for me. +``` + +Where X is the given name. + +However, if the name is missing, return the string: + +```text +One for you, one for me. +``` + +Here are some examples: + +|Name |String to return +|:-------|:------------------ +|Alice |One for Alice, one for me. +|Bob |One for Bob, one for me. +| |One for you, one for me. +|Zaphod |One for Zaphod, one for me. + +## Exception messages + +Sometimes it is necessary to raise an exception. When you do this, you should include a meaningful error message to +indicate what the source of the error is. This makes your code more readable and helps significantly with debugging. Not +every exercise will require you to raise an exception, but for those that do, the tests will only pass if you include +a message. + +To raise a message with an exception, just write it as an argument to the exception type. For example, instead of +`raise Exception`, you should write: + +```python +raise Exception("Meaningful message indicating the source of the error") +``` + +## Running the tests + +To run the tests, run `pytest two_fer_test.py` + +Alternatively, you can tell Python to run the pytest module: +`python -m pytest two_fer_test.py` + +### Common `pytest` options + +- `-v` : enable verbose output +- `-x` : stop running tests on first failure +- `--ff` : run failures from previous test before running other test cases + +For other options, see `python -m pytest -h` + +## Submitting Exercises + +Note that, when trying to submit an exercise, make sure the solution is in the `$EXERCISM_WORKSPACE/python/two-fer` directory. + +You can find your Exercism workspace by running `exercism debug` and looking for the line that starts with `Workspace`. + +For more detailed information about running tests, code style and linting, +please see [Running the Tests](http://exercism.io/tracks/python/tests). + +## Source + +[https://github.com/exercism/problem-specifications/issues/757](https://github.com/exercism/problem-specifications/issues/757) + +## Submitting Incomplete Solutions + +It's possible to submit an incomplete solution so you can see how others have completed the exercise. diff --git a/python/two-fer/two_fer.py b/python/two-fer/two_fer.py new file mode 100644 index 0000000..425d236 --- /dev/null +++ b/python/two-fer/two_fer.py @@ -0,0 +1,2 @@ +def two_fer(name='you'): + return f'One for {name}, one for me.' diff --git a/python/two-fer/two_fer_test.py b/python/two-fer/two_fer_test.py new file mode 100644 index 0000000..58f7386 --- /dev/null +++ b/python/two-fer/two_fer_test.py @@ -0,0 +1,20 @@ +import unittest + +from two_fer import two_fer + +# Tests adapted from `problem-specifications//canonical-data.json` @ v1.2.0 + + +class TwoFerTest(unittest.TestCase): + def test_no_name_given(self): + self.assertEqual(two_fer(), "One for you, one for me.") + + def test_a_name_given(self): + self.assertEqual(two_fer("Alice"), "One for Alice, one for me.") + + def test_another_name_given(self): + self.assertEqual(two_fer("Bob"), "One for Bob, one for me.") + + +if __name__ == "__main__": + unittest.main() -- GitLab