diff --git a/.gitignore b/.gitignore index edce8452bfc..6f97f3570a0 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,5 @@ bin/configlet bin/configlet.exe .idea/ .cache +.pytest_cache __pycache__ diff --git a/LICENSE b/LICENSE index eee993a55dc..a7527f968fb 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2017 Exercism, Inc +Copyright (c) 2019 Exercism Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/bin/check-test-version.py b/bin/check-test-version.py index 723bca0ba2c..6105a3e2653 100755 --- a/bin/check-test-version.py +++ b/bin/check-test-version.py @@ -323,5 +323,9 @@ def check_test_version( if opts.ignore and exercise in opts.ignore: continue if os.path.isdir(os.path.join('exercises', exercise)): - result = check_test_version(exercise, **kwargs) and result + try: + result = check_test_version(exercise, **kwargs) and result + except FileNotFoundError as e: + print(str(e)) + result = False sys.exit(0 if result else 1) diff --git a/config.json b/config.json index f80b140e3fd..36d3cfe5e1c 100644 --- a/config.json +++ b/config.json @@ -798,7 +798,7 @@ ] }, { - "slug": "bracket-push", + "slug": "matching-brackets", "uuid": "45229a7c-6703-4240-8287-16645881a043", "core": false, "unlocked_by": "isogram", @@ -1446,6 +1446,38 @@ "math" ] }, + { + "slug": "resistor-color", + "uuid": "d17bee9c-e803-4745-85ea-864f255fb04e", + "core": false, + "unlocked_by": null, + "difficulty": 1, + "topics": [ + "arrays" + ] + }, + { + "slug": "resistor-color-duo", + "uuid": "089f06a6-0759-479c-8c00-d699525a1e22", + "core": false, + "unlocked_by": null, + "difficulty": 1, + "topics": [ + "arrays" + ] + }, + { + "slug": "knapsack", + "uuid": "b0301d0b-d97a-4043-bd82-ba1edf8c1b16", + "core": false, + "unlocked_by": "clock", + "difficulty": 5, + "topics": [ + "algorithms", + "lists", + "loops" + ] + }, { "slug": "accumulate", "uuid": "e7351e8e-d3ff-4621-b818-cd55cf05bffd", @@ -1544,26 +1576,6 @@ "difficulty": 0, "topics": null, "deprecated": true - }, - { - "slug": "resistor-color", - "uuid": "d17bee9c-e803-4745-85ea-864f255fb04e", - "core": false, - "unlocked_by": null, - "difficulty": 1, - "topics": [ - "arrays" - ] - }, - { - "slug": "resistor-colors", - "uuid": "089f06a6-0759-479c-8c00-d699525a1e22", - "core": false, - "unlocked_by": null, - "difficulty": 1, - "topics": [ - "arrays" - ] } ] } diff --git a/config/maintainers.json b/config/maintainers.json index 8166c807e11..db4764212f6 100644 --- a/config/maintainers.json +++ b/config/maintainers.json @@ -21,16 +21,6 @@ "avatar_url": null, "bio": "I can not only fetch JSON, but parse it too." }, - { - "github_username": "N-Parsons", - "alumnus": false, - "show_on_website": true, - "name": "Nathan Parsons", - "link_text": null, - "link_url": null, - "avatar_url": null, - "bio": "Python fits with how I think and allows me to quickly write powerful, maintainable, and extensible code." - }, { "github_username": "m-a-ge", "alumnus": false, diff --git a/exercises/allergies/allergies.py b/exercises/allergies/allergies.py index f1eb64b0f06..8299b31e552 100644 --- a/exercises/allergies/allergies.py +++ b/exercises/allergies/allergies.py @@ -3,7 +3,7 @@ class Allergies(object): def __init__(self, score): pass - def is_allergic_to(self, item): + def allergic_to(self, item): pass @property diff --git a/exercises/allergies/allergies_test.py b/exercises/allergies/allergies_test.py index ad371cf3f5a..ad67eae3c52 100644 --- a/exercises/allergies/allergies_test.py +++ b/exercises/allergies/allergies_test.py @@ -7,62 +7,161 @@ unittest.TestCase.assertCountEqual = unittest.TestCase.assertItemsEqual -# Tests adapted from `problem-specifications//canonical-data.json` @ v1.2.0 +# Tests adapted from `problem-specifications//canonical-data.json` @ v2.0.0 class AllergiesTest(unittest.TestCase): - def test_no_allergies_means_not_allergic(self): - allergies = Allergies(0) - self.assertIs(allergies.is_allergic_to('peanuts'), False) - self.assertIs(allergies.is_allergic_to('cats'), False) - self.assertIs(allergies.is_allergic_to('strawberries'), False) - - def test_is_allergic_to_eggs(self): - self.assertIs(Allergies(1).is_allergic_to('eggs'), True) - - def test_allergic_to_eggs_in_addition_to_other_stuff(self): - allergies = Allergies(5) - self.assertIs(allergies.is_allergic_to('eggs'), True) - self.assertIs(allergies.is_allergic_to('shellfish'), True) - self.assertIs(allergies.is_allergic_to('strawberries'), False) - - def test_allergic_to_strawberries_but_not_peanuts(self): - allergies = Allergies(9) - self.assertIs(allergies.is_allergic_to('eggs'), True) - self.assertIs(allergies.is_allergic_to('peanuts'), False) - self.assertIs(allergies.is_allergic_to('shellfish'), False) - self.assertIs(allergies.is_allergic_to('strawberries'), True) - - def test_no_allergies_at_all(self): + + def test_eggs_not_allergic_to_anything(self): + self.assertIs(Allergies(0).allergic_to("eggs"), False) + + def test_allergic_only_to_eggs(self): + self.assertIs(Allergies(1).allergic_to("eggs"), True) + + def test_allergic_to_eggs_and_something_else(self): + self.assertIs(Allergies(3).allergic_to("eggs"), True) + + def test_allergic_to_something_but_not_eggs(self): + self.assertIs(Allergies(2).allergic_to("eggs"), False) + + def test_eggs_allergic_to_everything(self): + self.assertIs(Allergies(255).allergic_to("eggs"), True) + + def test_peanuts_not_allergic_to_anything(self): + self.assertIs(Allergies(0).allergic_to("peanuts"), False) + + def test_allergic_only_to_peanuts(self): + self.assertIs(Allergies(2).allergic_to("peanuts"), True) + + def test_allergic_to_peanuts_and_something_else(self): + self.assertIs(Allergies(7).allergic_to("peanuts"), True) + + def test_allergic_to_something_but_not_peanuts(self): + self.assertIs(Allergies(5).allergic_to("peanuts"), False) + + def test_peanuts_allergic_to_everything(self): + self.assertIs(Allergies(255).allergic_to("peanuts"), True) + + def test_shellfish_not_allergic_to_anything(self): + self.assertIs(Allergies(0).allergic_to("shellfish"), False) + + def test_allergic_only_to_shellfish(self): + self.assertIs(Allergies(4).allergic_to("shellfish"), True) + + def test_allergic_to_shellfish_and_something_else(self): + self.assertIs(Allergies(14).allergic_to("shellfish"), True) + + def test_allergic_to_something_but_not_shellfish(self): + self.assertIs(Allergies(10).allergic_to("shellfish"), False) + + def test_shellfish_allergic_to_everything(self): + self.assertIs(Allergies(255).allergic_to("shellfish"), True) + + def test_strawberries_not_allergic_to_anything(self): + self.assertIs(Allergies(0).allergic_to("strawberries"), False) + + def test_allergic_only_to_strawberries(self): + self.assertIs(Allergies(8).allergic_to("strawberries"), True) + + def test_allergic_to_strawberries_and_something_else(self): + self.assertIs(Allergies(28).allergic_to("strawberries"), True) + + def test_allergic_to_something_but_not_strawberries(self): + self.assertIs(Allergies(20).allergic_to("strawberries"), False) + + def test_strawberries_allergic_to_everything(self): + self.assertIs(Allergies(255).allergic_to("strawberries"), True) + + def test_tomatoes_not_allergic_to_anything(self): + self.assertIs(Allergies(0).allergic_to("tomatoes"), False) + + def test_allergic_only_to_tomatoes(self): + self.assertIs(Allergies(16).allergic_to("tomatoes"), True) + + def test_allergic_to_tomatoes_and_something_else(self): + self.assertIs(Allergies(56).allergic_to("tomatoes"), True) + + def test_allergic_to_something_but_not_tomatoes(self): + self.assertIs(Allergies(40).allergic_to("tomatoes"), False) + + def test_tomatoes_allergic_to_everything(self): + self.assertIs(Allergies(255).allergic_to("tomatoes"), True) + + def test_chocolate_not_allergic_to_anything(self): + self.assertIs(Allergies(0).allergic_to("chocolate"), False) + + def test_allergic_only_to_chocolate(self): + self.assertIs(Allergies(32).allergic_to("chocolate"), True) + + def test_allergic_to_chocolate_and_something_else(self): + self.assertIs(Allergies(112).allergic_to("chocolate"), True) + + def test_allergic_to_something_but_not_chocolate(self): + self.assertIs(Allergies(80).allergic_to("chocolate"), False) + + def test_chocolate_alergic_to_everything(self): + self.assertIs(Allergies(255).allergic_to("chocolate"), True) + + def test_pollen_not_alergic_to_anything(self): + self.assertIs(Allergies(0).allergic_to("pollen"), False) + + def test_allergic_only_to_pollen(self): + self.assertIs(Allergies(64).allergic_to("pollen"), True) + + def test_allergic_to_pollen_and_something_else(self): + self.assertIs(Allergies(224).allergic_to("pollen"), True) + + def test_allergic_to_something_but_not_pollen(self): + self.assertIs(Allergies(160).allergic_to("pollen"), False) + + def test_pollen_allergic_to_everything(self): + self.assertIs(Allergies(255).allergic_to("pollen"), True) + + def test_cats_not_allergic_to_anything(self): + self.assertIs(Allergies(0).allergic_to("cats"), False) + + def test_allergic_only_to_cats(self): + self.assertIs(Allergies(128).allergic_to("cats"), True) + + def test_allergic_to_cats_and_something_else(self): + self.assertIs(Allergies(192).allergic_to("cats"), True) + + def test_allergic_to_something_but_not_cats(self): + self.assertIs(Allergies(64).allergic_to("cats"), False) + + def test_cats_allergic_to_everything(self): + self.assertIs(Allergies(255).allergic_to('cats'), True) + + def test_no_allergies(self): self.assertEqual(Allergies(0).lst, []) - def test_allergic_to_just_eggs(self): + def test_just_eggs(self): self.assertEqual(Allergies(1).lst, ['eggs']) - def test_allergic_to_just_peanuts(self): + def test_just_peanuts(self): self.assertEqual(Allergies(2).lst, ['peanuts']) - def test_allergic_to_just_strawberries(self): + def test_just_strawberries(self): self.assertEqual(Allergies(8).lst, ['strawberries']) - def test_allergic_to_eggs_and_peanuts(self): + def test_eggs_and_peanuts(self): self.assertCountEqual(Allergies(3).lst, ['eggs', 'peanuts']) - def test_allergic_to_more_than_eggs_but_not_peanuts(self): + def test_more_than_eggs_but_not_peanuts(self): self.assertCountEqual(Allergies(5).lst, ['eggs', 'shellfish']) - def test_allergic_to_lots_of_stuff(self): + def test_lots_of_stuff(self): self.assertCountEqual( Allergies(248).lst, ['strawberries', 'tomatoes', 'chocolate', 'pollen', 'cats']) - def test_allergic_to_everything(self): + def test_everything(self): self.assertCountEqual( Allergies(255).lst, [ 'eggs', 'peanuts', 'shellfish', 'strawberries', 'tomatoes', 'chocolate', 'pollen', 'cats' ]) - def test_ignore_non_allergen_score_parts(self): + def test_no_allergen_score_parts(self): self.assertCountEqual( Allergies(509).lst, [ 'eggs', 'shellfish', 'strawberries', 'tomatoes', 'chocolate', diff --git a/exercises/allergies/example.py b/exercises/allergies/example.py index a88674ca97d..9e534667f36 100644 --- a/exercises/allergies/example.py +++ b/exercises/allergies/example.py @@ -14,10 +14,10 @@ class Allergies(object): def __init__(self, score): self.score = score - def is_allergic_to(self, allergy): + def allergic_to(self, allergy): return bool(self.score & 1 << self._allergies.index(allergy)) @property def lst(self): return [allergy for allergy in self._allergies - if self.is_allergic_to(allergy)] + if self.allergic_to(allergy)] diff --git a/exercises/alphametics/alphametics_test.py b/exercises/alphametics/alphametics_test.py index daa47cbd8bc..01f7d9130a6 100644 --- a/exercises/alphametics/alphametics_test.py +++ b/exercises/alphametics/alphametics_test.py @@ -10,10 +10,10 @@ def test_puzzle_with_three_letters(self): self.assertEqual(solve("I + BB == ILL"), {"I": 1, "B": 9, "L": 0}) def test_solution_must_have_unique_value_for_each_letter(self): - self.assertEqual(solve("A == B"), {}) + self.assertEqual(solve("A == B"), None) def test_leading_zero_solution_is_invalid(self): - self.assertEqual(solve("ACA + DD == BD"), {}) + self.assertEqual(solve("ACA + DD == BD"), None) def test_puzzle_with_two_digits_final_carry(self): self.assertEqual( diff --git a/exercises/alphametics/example.py b/exercises/alphametics/example.py index 7543bebabb3..6a7a869961b 100644 --- a/exercises/alphametics/example.py +++ b/exercises/alphametics/example.py @@ -108,11 +108,11 @@ def check_rec(eqparams, tracecombo=(dict(), 0, set(range(10))), p=0): # if the recursive call returned a non-empty dictionary # this means the recursion has found a solution # otherwise, proceed to the new permutation - if len(rectest) > 0: + if rectest and len(rectest) > 0: return rectest # if no permutations are avaialble or no - # permutation gave the result return the empty dictionary - return dict() + # permutation gave the result return None + return None def solve(an): diff --git a/exercises/armstrong-numbers/armstrong_numbers.py b/exercises/armstrong-numbers/armstrong_numbers.py index d046e348db3..b8829fac575 100644 --- a/exercises/armstrong-numbers/armstrong_numbers.py +++ b/exercises/armstrong-numbers/armstrong_numbers.py @@ -1,2 +1,2 @@ -def is_armstrong(number): +def is_armstrong_number(number): pass diff --git a/exercises/armstrong-numbers/armstrong_numbers_test.py b/exercises/armstrong-numbers/armstrong_numbers_test.py index ec52be9a76d..46b7fc5dd01 100644 --- a/exercises/armstrong-numbers/armstrong_numbers_test.py +++ b/exercises/armstrong-numbers/armstrong_numbers_test.py @@ -1,35 +1,38 @@ import unittest -from armstrong_numbers import is_armstrong +from armstrong_numbers import is_armstrong_number -# Tests adapted from `problem-specifications//canonical-data.json` @ v1.0.0 +# 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(5), True) + self.assertIs(is_armstrong_number(5), True) def test_there_are_no_two_digit_armstrong_numbers(self): - self.assertIs(is_armstrong(10), False) + self.assertIs(is_armstrong_number(10), False) def test_three_digit_number_that_is_an_armstrong_number(self): - self.assertIs(is_armstrong(153), True) + self.assertIs(is_armstrong_number(153), True) def test_three_digit_number_that_is_not_an_armstrong_number(self): - self.assertIs(is_armstrong(100), False) + self.assertIs(is_armstrong_number(100), False) def test_four_digit_number_that_is_an_armstrong_number(self): - self.assertIs(is_armstrong(9474), True) + self.assertIs(is_armstrong_number(9474), True) def test_four_digit_number_that_is_not_an_armstrong_number(self): - self.assertIs(is_armstrong(9475), False) + self.assertIs(is_armstrong_number(9475), False) def test_seven_digit_number_that_is_an_armstrong_number(self): - self.assertIs(is_armstrong(9926315), True) + self.assertIs(is_armstrong_number(9926315), True) def test_seven_digit_number_that_is_not_an_armstrong_number(self): - self.assertIs(is_armstrong(9926314), False) + self.assertIs(is_armstrong_number(9926314), False) if __name__ == '__main__': diff --git a/exercises/armstrong-numbers/example.py b/exercises/armstrong-numbers/example.py index 4dc0a3eef6e..9bc1a3a5918 100644 --- a/exercises/armstrong-numbers/example.py +++ b/exercises/armstrong-numbers/example.py @@ -1,2 +1,2 @@ -def is_armstrong(number): +def is_armstrong_number(number): return sum(pow(int(d), len(str(number))) for d in str(number)) == number diff --git a/exercises/darts/darts_test.py b/exercises/darts/darts_test.py index 1b359709095..40882f15dbe 100644 --- a/exercises/darts/darts_test.py +++ b/exercises/darts/darts_test.py @@ -2,7 +2,7 @@ from darts import score -# Tests adapted from `problem-specifications//canonical-data.json` @ v1.1.0 +# Tests adapted from `problem-specifications//canonical-data.json` @ v2.0.0 class darts_test(unittest.TestCase): def test_dart_lands_outside_target(self): @@ -26,6 +26,15 @@ def test_dart_lands_border_betweeen_middle_inner(self): def test_dart_lands_inner_circle(self): self.assertEqual(score(-0.1, -0.1), 10) + def test_dart_coord_sum_more_than_1_radius_less_than_1_is_in_inner(self): + self.assertEqual(score(0.4, 0.8), 10) + + def test_dart_coord_sum_more_than_5_radius_less_than_5_is_in_middle(self): + self.assertEqual(score(2, 4), 5) + + def test_dart_coord_sum_more_than_10_radius_less_than_10_is_in_outer(self): + self.assertEqual(score(4, 8), 1) + if __name__ == '__main__': unittest.main() diff --git a/exercises/high-scores/.meta/HINTS.md b/exercises/high-scores/.meta/HINTS.md new file mode 100644 index 00000000000..b1de85b1839 --- /dev/null +++ b/exercises/high-scores/.meta/HINTS.md @@ -0,0 +1,5 @@ +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) diff --git a/exercises/high-scores/README.md b/exercises/high-scores/README.md index 802ad5ac1ee..fa7934d4d37 100644 --- a/exercises/high-scores/README.md +++ b/exercises/high-scores/README.md @@ -4,19 +4,59 @@ 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. -## Submitting Exercises +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 -Note that, when trying to submit an exercise, make sure the solution is in the `exercism/python/` directory. +To run the tests, run the appropriate command below ([why they are different](https://github.com/pytest-dev/pytest/issues/1629#issue-161422224)): -For example, if you're submitting `bob.py` for the Bob exercise, the submit command would be something like `exercism submit /python/bob/bob.py`. +- Python 2.7: `py.test high_scores_test.py` +- Python 3.4+: `pytest high_scores_test.py` +Alternatively, you can tell Python to run the pytest module (allowing the same command to be used regardless of Python version): +`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 the [help page](http://exercism.io/languages/python). +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/exercises/high-scores/example.py b/exercises/high-scores/example.py index 0c77d73af86..fd168ffc526 100644 --- a/exercises/high-scores/example.py +++ b/exercises/high-scores/example.py @@ -1,12 +1,10 @@ -class HighScores(object): - def __init__(self, scores): - self.scores = scores +def latest(scores): + return scores[-1] - def latest(self): - return self.scores[-1] - def personal_best(self): - return max(self.scores) +def personal_best(scores): + return max(scores) - def personal_top_three(self): - return sorted(self.scores, reverse=True)[:3] + +def personal_top_three(scores): + return sorted(scores, reverse=True)[:3] diff --git a/exercises/high-scores/high_scores.py b/exercises/high-scores/high_scores.py index 3bfba4d0b57..0d24cdb1c1d 100644 --- a/exercises/high-scores/high_scores.py +++ b/exercises/high-scores/high_scores.py @@ -1,3 +1,10 @@ -class HighScores(object): - def __init__(self, scores): - pass +def latest(scores): + pass + + +def personal_best(scores): + pass + + +def personal_top_three(scores): + pass diff --git a/exercises/high-scores/high_scores_test.py b/exercises/high-scores/high_scores_test.py index 17d69c0e15d..40bd76c5ed1 100644 --- a/exercises/high-scores/high_scores_test.py +++ b/exercises/high-scores/high_scores_test.py @@ -1,51 +1,47 @@ import unittest -from high_scores import HighScores +from high_scores import latest, personal_best, personal_top_three # Tests adapted from `problem-specifications//canonical-data.json` @ v4.0.0 class HighScoreTest(unittest.TestCase): - def test_list_of_scores(self): - scores = [30, 50, 20, 70] - expected = [30, 50, 20, 70] - self.assertEqual(HighScores(scores).scores, expected) def test_latest_score(self): scores = [100, 0, 90, 30] expected = 30 - self.assertEqual(HighScores(scores).latest(), expected) + self.assertEqual(latest(scores), expected) def test_personal_best(self): scores = [40, 100, 70] expected = 100 - self.assertEqual(HighScores(scores).personal_best(), expected) + self.assertEqual(personal_best(scores), expected) def test_personal_top_three_from_a_long_list(self): scores = [10, 30, 90, 30, 100, 20, 10, 0, 30, 40, 40, 70, 70] expected = [100, 90, 70] - self.assertEqual(HighScores(scores).personal_top_three(), expected) + self.assertEqual(personal_top_three(scores), expected) def test_personal_top_three_highest_to_lowest(self): scores = [20, 10, 30] expected = [30, 20, 10] - self.assertEqual(HighScores(scores).personal_top_three(), expected) + self.assertEqual(personal_top_three(scores), expected) def test_personal_top_three_when_there_is_a_tie(self): scores = [40, 20, 40, 30] expected = [40, 40, 30] - self.assertEqual(HighScores(scores).personal_top_three(), expected) + self.assertEqual(personal_top_three(scores), expected) def test_personal_top_three_when_there_are_less_than_3(self): scores = [30, 70] expected = [70, 30] - self.assertEqual(HighScores(scores).personal_top_three(), expected) + self.assertEqual(personal_top_three(scores), expected) def test_personal_top_three_when_there_is_only_one(self): scores = [40] expected = [40] - self.assertEqual(HighScores(scores).personal_top_three(), expected) + self.assertEqual(personal_top_three(scores), expected) if __name__ == "__main__": diff --git a/exercises/knapsack/README.md b/exercises/knapsack/README.md new file mode 100644 index 00000000000..0a21d980a5c --- /dev/null +++ b/exercises/knapsack/README.md @@ -0,0 +1,87 @@ +# Knapsack + +In this exercise, let's try to solve a classic problem. + +Bob is a thief. After months of careful planning, he finally manages to +crack the security systems of a high-class apartment. + +In front of him are many items, each with a value (v) and weight (w). Bob, +of course, wants to maximize the total value he can get; he would gladly +take all of the items if he could. However, to his horror, he realizes that +the knapsack he carries with him can only hold so much weight (W). + +Given a knapsack with a specific carrying capacity (W), help Bob determine +the maximum value he can get from the items in the house. Note that Bob can +take only one of each item. + +All values given will be strictly positive. Items will be represented as a +list of pairs, `wi` and `vi`, where the first element `wi` is the weight of +the *i*th item and `vi` is the value for that item. + +For example: + +Items: [ + { "weight": 5, "value": 10 }, + { "weight": 4, "value": 40 }, + { "weight": 6, "value": 30 }, + { "weight": 4, "value": 50 } +] + +Knapsack Limit: 10 + +For the above, the first item has weight 5 and value 10, the second item has +weight 4 and value 40, and so on. + +In this example, Bob should take the second and fourth item to maximize his +value, which, in this case, is 90. He cannot get more than 90 as his +knapsack has a weight limit of 10. + + +## 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 the appropriate command below ([why they are different](https://github.com/pytest-dev/pytest/issues/1629#issue-161422224)): + +- Python 2.7: `py.test knapsack_test.py` +- Python 3.4+: `pytest knapsack_test.py` + +Alternatively, you can tell Python to run the pytest module (allowing the same command to be used regardless of Python version): +`python -m pytest knapsack_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/knapsack` 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/Knapsack_problem](https://en.wikipedia.org/wiki/Knapsack_problem) + +## Submitting Incomplete Solutions + +It's possible to submit an incomplete solution so you can see how others have completed the exercise. diff --git a/exercises/knapsack/example.py b/exercises/knapsack/example.py new file mode 100644 index 00000000000..f19d066e951 --- /dev/null +++ b/exercises/knapsack/example.py @@ -0,0 +1,11 @@ +def solve_knapsack(max_weight, items): + t = [[0 for _ in range(len(items) + 1)] for _ in range(max_weight + 1)] + for weight in range(1, max_weight + 1): + for i, item in enumerate(items, 1): + if item["weight"] <= weight: + value = item["value"] + t[weight - item["weight"]][i - 1] + value_without_item = t[weight][i - 1] + t[weight][i] = max(value, value_without_item) + else: + t[weight][i] = t[weight][i - 1] + return t[max_weight][len(items)] diff --git a/exercises/knapsack/knapsack.py b/exercises/knapsack/knapsack.py new file mode 100644 index 00000000000..9458bb3c2af --- /dev/null +++ b/exercises/knapsack/knapsack.py @@ -0,0 +1,2 @@ +def solve_knapsack(max_weight, items): + pass diff --git a/exercises/knapsack/knapsack_test.py b/exercises/knapsack/knapsack_test.py new file mode 100644 index 00000000000..14be85f6ad6 --- /dev/null +++ b/exercises/knapsack/knapsack_test.py @@ -0,0 +1,66 @@ +import unittest + +from knapsack import solve_knapsack + + +# Tests adapted from `problem-specifications//canonical-data.json` @ v1.0.0 + +class ChangeTest(unittest.TestCase): + def test_no_items(self): + self.assertEqual(solve_knapsack(100, []), 0) + + def test_one_item_too_heavy(self): + self.assertEqual(solve_knapsack(10, [{"weight": 100, "value": 1}]), 0) + + def test_cannot_be_greedy_by_weight(self): + self.assertEqual(solve_knapsack(10, + [{"weight": 2, "value": 5}, + {"weight": 2, "value": 5}, + {"weight": 2, "value": 5}, + {"weight": 2, "value": 5}, + {"weight": 10, "value": 21}]), 21) + + def test_cannot_be_greedy_by_value(self): + self.assertEqual(solve_knapsack(10, [{"weight": 2, "value": 20}, + {"weight": 2, "value": 20}, + {"weight": 2, "value": 20}, + {"weight": 2, "value": 20}, + {"weight": 10, "value": 50}]), 80) + + def test_example_knapsack(self): + self.assertEqual(solve_knapsack(10, [{"weight": 5, "value": 10}, + {"weight": 4, "value": 40}, + {"weight": 6, "value": 30}, + {"weight": 4, "value": 50}]), 90) + + def test_eight_items(self): + self.assertEqual(solve_knapsack(104, [{"weight": 25, "value": 350}, + {"weight": 35, "value": 400}, + {"weight": 45, "value": 450}, + {"weight": 5, "value": 20}, + {"weight": 25, "value": 70}, + {"weight": 3, "value": 8}, + {"weight": 2, "value": 5}, + {"weight": 2, "value": 5}]), 900) + + def test_fifteen_items(self): + self.assertEqual(solve_knapsack(750, + [{"weight": 70, "value": 135}, + {"weight": 73, "value": 139}, + {"weight": 77, "value": 149}, + {"weight": 80, "value": 150}, + {"weight": 82, "value": 156}, + {"weight": 87, "value": 163}, + {"weight": 90, "value": 173}, + {"weight": 94, "value": 184}, + {"weight": 98, "value": 192}, + {"weight": 106, "value": 201}, + {"weight": 110, "value": 210}, + {"weight": 113, "value": 214}, + {"weight": 115, "value": 221}, + {"weight": 118, "value": 229}, + {"weight": 120, "value": 240}]), 1458) + + +if __name__ == "__main__": + unittest.main() diff --git a/exercises/luhn/luhn_test.py b/exercises/luhn/luhn_test.py index c82e08b9842..7457a5a4a33 100644 --- a/exercises/luhn/luhn_test.py +++ b/exercises/luhn/luhn_test.py @@ -5,7 +5,7 @@ from luhn import Luhn -# Tests adapted from `problem-specifications//canonical-data.json` @ v1.4.0 +# Tests adapted from `problem-specifications//canonical-data.json` @ v1.5.0 class LuhnTest(unittest.TestCase): def test_single_digit_strings_can_not_be_valid(self): @@ -32,9 +32,6 @@ def test_invalid_credit_card(self): def test_valid_number_with_an_even_number_of_digits(self): self.assertIs(Luhn("095 245 88").is_valid(), True) - def test_valid_strings_with_a_non_digit_included_become_invalid(self): - self.assertIs(Luhn("055a 444 285").is_valid(), False) - def test_valid_strings_with_non_digit_added_at_end_become_invalid(self): self.assertIs(Luhn("059a").is_valid(), False) @@ -53,7 +50,10 @@ def test_more_than_a_single_zero_is_valid(self): def test_input_digit_9_is_correctly_converted_to_output_digit_9(self): self.assertIs(Luhn("091").is_valid(), True) - def test_strings_with_non_digits_is_invalid(self): + def test_using_ascii_value_for_non_doubled_non_digit_isnot_allowed(self): + self.assertIs(Luhn("055b 444 285").is_valid(), False) + + def test_using_ascii_value_for_doubled_non_digit_isnot_allowed(self): self.assertIs(Luhn(":9").is_valid(), False) def test_is_valid_can_be_called_repeatedly(self): diff --git a/exercises/bracket-push/README.md b/exercises/matching-brackets/README.md similarity index 88% rename from exercises/bracket-push/README.md rename to exercises/matching-brackets/README.md index 7ee2aa2e3ad..bbd62d63ee8 100644 --- a/exercises/bracket-push/README.md +++ b/exercises/matching-brackets/README.md @@ -1,9 +1,10 @@ -# Bracket Push +# Matching Brackets Given a string containing brackets `[]`, braces `{}`, parentheses `()`, or any combination thereof, verify that any and all pairs are matched and nested correctly. + ## Exception messages Sometimes it is necessary to raise an exception. When you do this, you should include a meaningful error message to @@ -22,11 +23,11 @@ raise Exception("Meaningful message indicating the source of the error") To run the tests, run the appropriate command below ([why they are different](https://github.com/pytest-dev/pytest/issues/1629#issue-161422224)): -- Python 2.7: `py.test bracket_push_test.py` -- Python 3.4+: `pytest bracket_push_test.py` +- Python 2.7: `py.test matching_brackets_test.py` +- Python 3.4+: `pytest matching_brackets_test.py` Alternatively, you can tell Python to run the pytest module (allowing the same command to be used regardless of Python version): -`python -m pytest bracket_push_test.py` +`python -m pytest matching_brackets_test.py` ### Common `pytest` options @@ -38,7 +39,7 @@ 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/bracket-push` directory. +Note that, when trying to submit an exercise, make sure the solution is in the `$EXERCISM_WORKSPACE/python/matching-brackets` directory. You can find your Exercism workspace by running `exercism debug` and looking for the line that starts with `Workspace`. diff --git a/exercises/bracket-push/example.py b/exercises/matching-brackets/example.py similarity index 100% rename from exercises/bracket-push/example.py rename to exercises/matching-brackets/example.py diff --git a/exercises/bracket-push/bracket_push.py b/exercises/matching-brackets/matching_brackets.py similarity index 100% rename from exercises/bracket-push/bracket_push.py rename to exercises/matching-brackets/matching_brackets.py diff --git a/exercises/bracket-push/bracket_push_test.py b/exercises/matching-brackets/matching_brackets_test.py similarity index 95% rename from exercises/bracket-push/bracket_push_test.py rename to exercises/matching-brackets/matching_brackets_test.py index 9dfd06489bf..3ed117f18de 100644 --- a/exercises/bracket-push/bracket_push_test.py +++ b/exercises/matching-brackets/matching_brackets_test.py @@ -1,11 +1,11 @@ import unittest -from bracket_push import is_paired +from matching_brackets import is_paired -# Tests adapted from `problem-specifications//canonical-data.json` @ v1.5.0 +# Tests adapted from `problem-specifications//canonical-data.json` @ v2.0.0 -class BracketPushTest(unittest.TestCase): +class MatchingBracketsTest(unittest.TestCase): def test_paired_square_brackets(self): self.assertEqual(is_paired("[]"), True) diff --git a/exercises/matrix/.meta/HINTS.md b/exercises/matrix/.meta/HINTS.md new file mode 100644 index 00000000000..fe997c63097 --- /dev/null +++ b/exercises/matrix/.meta/HINTS.md @@ -0,0 +1,5 @@ +In this exercise you're going to create a **class**. _Don't worry, it's not as complicated as you think!_ + +- [**A First Look at Classes**](https://docs.python.org/3/tutorial/classes.html#a-first-look-at-classes) from the Python 3 documentation. +- [**How to Define a Class in Python**](https://realpython.com/python3-object-oriented-programming/#how-to-define-a-class-in-python) from the Real Python website. +- [**Data Structures in Python**](https://docs.python.org/3/tutorial/datastructures.html) from the Python 3 documentation. diff --git a/exercises/matrix/README.md b/exercises/matrix/README.md index 2165e6fe802..285e5b9d515 100644 --- a/exercises/matrix/README.md +++ b/exercises/matrix/README.md @@ -40,6 +40,14 @@ And its columns: - 8, 3, 6 - 7, 2, 7 +In this exercise you're going to create a **class**. _Don't worry, it's not as complicated as you think!_ + +- [**A First Look at Classes**](https://docs.python.org/3/tutorial/classes.html#a-first-look-at-classes) from the Python 3 documentation. +- [**How to Define a Class in Python**](https://realpython.com/python3-object-oriented-programming/#how-to-define-a-class-in-python) from the Real Python website. +- [**Data Structures in Python**](https://docs.python.org/3/tutorial/datastructures.html) from the Python 3 documentation. + + + ## Exception messages Sometimes it is necessary to raise an exception. When you do this, you should include a meaningful error message to diff --git a/exercises/queen-attack/queen_attack_test.py b/exercises/queen-attack/queen_attack_test.py index f4212093b11..11868b35d89 100644 --- a/exercises/queen-attack/queen_attack_test.py +++ b/exercises/queen-attack/queen_attack_test.py @@ -3,7 +3,7 @@ from queen_attack import Queen -# Tests adapted from `problem-specifications//canonical-data.json` @ v2.2.0 +# Tests adapted from `problem-specifications//canonical-data.json` @ v2.3.0 class QueenAttackTest(unittest.TestCase): @@ -50,7 +50,7 @@ def test_attack_diagonal3(self): self.assertIs(Queen(2, 2).can_attack(Queen(1, 1)), True) def test_attack_diagonal4(self): - self.assertIs(Queen(2, 2).can_attack(Queen(5, 5)), True) + self.assertIs(Queen(1, 7).can_attack(Queen(0, 6)), True) # Track-specific tests def test_queens_same_position_can_attack(self): diff --git a/exercises/resistor-colors/README.md b/exercises/resistor-color-duo/README.md similarity index 91% rename from exercises/resistor-colors/README.md rename to exercises/resistor-color-duo/README.md index aa49ca44cb0..9ba682dc10b 100644 --- a/exercises/resistor-colors/README.md +++ b/exercises/resistor-color-duo/README.md @@ -1,4 +1,4 @@ -# Resistor Colors +# Resistor Color Duo If you want to build something using a Raspberry Pi, you'll probably use _resistors_. For this exercise, you need to know two things about them: @@ -40,11 +40,11 @@ raise Exception("Meaningful message indicating the source of the error") To run the tests, run the appropriate command below ([why they are different](https://github.com/pytest-dev/pytest/issues/1629#issue-161422224)): -- Python 2.7: `py.test resistor_colors_test.py` -- Python 3.4+: `pytest resistor_colors_test.py` +- Python 2.7: `py.test resistor_color_duo_test.py` +- Python 3.4+: `pytest resistor_color_duo_test.py` Alternatively, you can tell Python to run the pytest module (allowing the same command to be used regardless of Python version): -`python -m pytest resistor_colors_test.py` +`python -m pytest resistor_color_duo_test.py` ### Common `pytest` options @@ -56,7 +56,7 @@ 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/resistor-colors` directory. +Note that, when trying to submit an exercise, make sure the solution is in the `$EXERCISM_WORKSPACE/python/resistor-color-duo` directory. You can find your Exercism workspace by running `exercism debug` and looking for the line that starts with `Workspace`. diff --git a/exercises/resistor-colors/example.py b/exercises/resistor-color-duo/example.py similarity index 100% rename from exercises/resistor-colors/example.py rename to exercises/resistor-color-duo/example.py diff --git a/exercises/resistor-colors/resistor_colors.py b/exercises/resistor-color-duo/resistor_color_duo.py similarity index 100% rename from exercises/resistor-colors/resistor_colors.py rename to exercises/resistor-color-duo/resistor_color_duo.py diff --git a/exercises/resistor-colors/resistor_colors_test.py b/exercises/resistor-color-duo/resistor_color_duo_test.py similarity index 84% rename from exercises/resistor-colors/resistor_colors_test.py rename to exercises/resistor-color-duo/resistor_color_duo_test.py index f3681dad4c4..6c9384407bb 100644 --- a/exercises/resistor-colors/resistor_colors_test.py +++ b/exercises/resistor-color-duo/resistor_color_duo_test.py @@ -1,11 +1,11 @@ import unittest -from resistor_colors import value +from resistor_color_duo import value -# Tests adapted from `problem-specifications//canonical-data.json` @ v1.0.0 +# Tests adapted from `problem-specifications//canonical-data.json` @ v2.0.0 -class ResistorColorsTest(unittest.TestCase): +class ResistorColorDuoTest(unittest.TestCase): def test_brown_and_black(self): self.assertEqual(value(['brown', 'black']), 10) diff --git a/exercises/sgf-parsing/README.md b/exercises/sgf-parsing/README.md index 597520c4cf7..6db23421ff7 100644 --- a/exercises/sgf-parsing/README.md +++ b/exercises/sgf-parsing/README.md @@ -18,9 +18,10 @@ An SGF file may look like this: This is a tree with three nodes: -- The top level node has two properties: FF\[4\] (key = "FF", value = - "4") and C\[root\](key = "C", value = "root"). (FF indicates the - version of SGF and C is a comment.) +- The top level node has three properties: FF\[4\] (key = "FF", value + = "4"), C\[root\](key = "C", value = "root") and SZ\[19\] (key = + "SZ", value = "19"). (FF indicates the version of SGF, C is a + comment and SZ is the size of the board.) - The top level node has a single child which has a single property: B\[aa\]. (Black plays on the point encoded as "aa", which is the 1-1 point (which is a stupid place to play)). @@ -64,6 +65,7 @@ structure of properties. You do not need to encode knowledge about the data types of properties, just use the rules for the [text](http://www.red-bean.com/sgf/sgf4.html#text) type everywhere. + ## Exception messages Sometimes it is necessary to raise an exception. When you do this, you should include a meaningful error message to diff --git a/exercises/tournament/example.py b/exercises/tournament/example.py index e7b62874cd9..88494049440 100644 --- a/exercises/tournament/example.py +++ b/exercises/tournament/example.py @@ -31,14 +31,13 @@ def format_table(results): team_fmt = '{0:30} | {1:2} | {3:2} | {4:2} | {5:2} | {2:2}' table.append( team_fmt.format(team, sum(games), calculate_points(games), *games)) - - return '\n'.join(table) + return table def tally(data): table = defaultdict(lambda: [0, 0, 0]) - for line in data.split('\n'): + for line in data: for team, result in parse_game(line): table[team][result] += 1 diff --git a/exercises/tournament/tournament_test.py b/exercises/tournament/tournament_test.py index fa3791db4bd..d9f19168a7a 100644 --- a/exercises/tournament/tournament_test.py +++ b/exercises/tournament/tournament_test.py @@ -8,107 +8,107 @@ class TournamentTest(unittest.TestCase): def test_just_the_header_if_no_input(self): self.assertEqual( - tally(''), - 'Team | MP | W | D | L | P' + tally([]), + ['Team | MP | W | D | L | P'] ) def test_a_win_is_three_points_and_a_loss_is_zero_points(self): - results = 'Allegoric Alaskans;Blithering Badgers;win' - table = ('Team | MP | W | D | L | P\n' - 'Allegoric Alaskans | 1 | 1 | 0 | 0 | 3\n' - 'Blithering Badgers | 1 | 0 | 0 | 1 | 0') + results = ['Allegoric Alaskans;Blithering Badgers;win'] + table = ['Team | MP | W | D | L | P', + 'Allegoric Alaskans | 1 | 1 | 0 | 0 | 3', + 'Blithering Badgers | 1 | 0 | 0 | 1 | 0'] self.assertEqual(tally(results), table) def test_a_win_can_also_be_expressed_as_a_loss(self): - results = 'Blithering Badgers;Allegoric Alaskans;loss' - table = ('Team | MP | W | D | L | P\n' - 'Allegoric Alaskans | 1 | 1 | 0 | 0 | 3\n' - 'Blithering Badgers | 1 | 0 | 0 | 1 | 0') + results = ['Blithering Badgers;Allegoric Alaskans;loss'] + table = ['Team | MP | W | D | L | P', + 'Allegoric Alaskans | 1 | 1 | 0 | 0 | 3', + 'Blithering Badgers | 1 | 0 | 0 | 1 | 0'] self.assertEqual(tally(results), table) def test_a_different_team_can_win(self): - results = 'Blithering Badgers;Allegoric Alaskans;win' - table = ('Team | MP | W | D | L | P\n' - 'Blithering Badgers | 1 | 1 | 0 | 0 | 3\n' - 'Allegoric Alaskans | 1 | 0 | 0 | 1 | 0') + results = ['Blithering Badgers;Allegoric Alaskans;win'] + table = ['Team | MP | W | D | L | P', + 'Blithering Badgers | 1 | 1 | 0 | 0 | 3', + 'Allegoric Alaskans | 1 | 0 | 0 | 1 | 0'] self.assertEqual(tally(results), table) def test_a_draw_is_one_point_each(self): - results = 'Allegoric Alaskans;Blithering Badgers;draw' - table = ('Team | MP | W | D | L | P\n' - 'Allegoric Alaskans | 1 | 0 | 1 | 0 | 1\n' - 'Blithering Badgers | 1 | 0 | 1 | 0 | 1') + results = ['Allegoric Alaskans;Blithering Badgers;draw'] + table = ['Team | MP | W | D | L | P', + 'Allegoric Alaskans | 1 | 0 | 1 | 0 | 1', + 'Blithering Badgers | 1 | 0 | 1 | 0 | 1'] self.assertEqual(tally(results), table) def test_there_can_be_more_than_one_match(self): - results = ('Allegoric Alaskans;Blithering Badgers;win\n' - 'Allegoric Alaskans;Blithering Badgers;win') - table = ('Team | MP | W | D | L | P\n' - 'Allegoric Alaskans | 2 | 2 | 0 | 0 | 6\n' - 'Blithering Badgers | 2 | 0 | 0 | 2 | 0') + results = ['Allegoric Alaskans;Blithering Badgers;win', + 'Allegoric Alaskans;Blithering Badgers;win'] + table = ['Team | MP | W | D | L | P', + 'Allegoric Alaskans | 2 | 2 | 0 | 0 | 6', + 'Blithering Badgers | 2 | 0 | 0 | 2 | 0'] self.assertEqual(tally(results), table) def test_there_can_be_more_than_one_winner(self): - results = ('Allegoric Alaskans;Blithering Badgers;loss\n' - 'Allegoric Alaskans;Blithering Badgers;win') - table = ('Team | MP | W | D | L | P\n' - 'Allegoric Alaskans | 2 | 1 | 0 | 1 | 3\n' - 'Blithering Badgers | 2 | 1 | 0 | 1 | 3') + results = ['Allegoric Alaskans;Blithering Badgers;loss', + 'Allegoric Alaskans;Blithering Badgers;win'] + table = ['Team | MP | W | D | L | P', + 'Allegoric Alaskans | 2 | 1 | 0 | 1 | 3', + 'Blithering Badgers | 2 | 1 | 0 | 1 | 3'] self.assertEqual(tally(results), table) def test_there_can_be_more_than_two_teams(self): - results = ('Allegoric Alaskans;Blithering Badgers;win\n' - 'Blithering Badgers;Courageous Californians;win\n' - 'Courageous Californians;Allegoric Alaskans;loss') - table = ('Team | MP | W | D | L | P\n' - 'Allegoric Alaskans | 2 | 2 | 0 | 0 | 6\n' - 'Blithering Badgers | 2 | 1 | 0 | 1 | 3\n' - 'Courageous Californians | 2 | 0 | 0 | 2 | 0') + results = ['Allegoric Alaskans;Blithering Badgers;win', + 'Blithering Badgers;Courageous Californians;win', + 'Courageous Californians;Allegoric Alaskans;loss'] + table = ['Team | MP | W | D | L | P', + 'Allegoric Alaskans | 2 | 2 | 0 | 0 | 6', + 'Blithering Badgers | 2 | 1 | 0 | 1 | 3', + 'Courageous Californians | 2 | 0 | 0 | 2 | 0'] self.assertEqual(tally(results), table) def test_typical_input(self): - results = ('Allegoric Alaskans;Blithering Badgers;win\n' - 'Devastating Donkeys;Courageous Californians;draw\n' - 'Devastating Donkeys;Allegoric Alaskans;win\n' - 'Courageous Californians;Blithering Badgers;loss\n' - 'Blithering Badgers;Devastating Donkeys;loss\n' - 'Allegoric Alaskans;Courageous Californians;win') - - table = ('Team | MP | W | D | L | P\n' - 'Devastating Donkeys | 3 | 2 | 1 | 0 | 7\n' - 'Allegoric Alaskans | 3 | 2 | 0 | 1 | 6\n' - 'Blithering Badgers | 3 | 1 | 0 | 2 | 3\n' - 'Courageous Californians | 3 | 0 | 1 | 2 | 1') + results = ['Allegoric Alaskans;Blithering Badgers;win', + 'Devastating Donkeys;Courageous Californians;draw', + 'Devastating Donkeys;Allegoric Alaskans;win', + 'Courageous Californians;Blithering Badgers;loss', + 'Blithering Badgers;Devastating Donkeys;loss', + 'Allegoric Alaskans;Courageous Californians;win'] + + table = ['Team | MP | W | D | L | P', + 'Devastating Donkeys | 3 | 2 | 1 | 0 | 7', + 'Allegoric Alaskans | 3 | 2 | 0 | 1 | 6', + 'Blithering Badgers | 3 | 1 | 0 | 2 | 3', + 'Courageous Californians | 3 | 0 | 1 | 2 | 1'] self.assertEqual(tally(results), table) def test_incomplete_competitionnot_not_all_pairs_have_played(self): - results = ('Allegoric Alaskans;Blithering Badgers;loss\n' - 'Devastating Donkeys;Allegoric Alaskans;loss\n' - 'Courageous Californians;Blithering Badgers;draw\n' - 'Allegoric Alaskans;Courageous Californians;win') + results = ['Allegoric Alaskans;Blithering Badgers;loss', + 'Devastating Donkeys;Allegoric Alaskans;loss', + 'Courageous Californians;Blithering Badgers;draw', + 'Allegoric Alaskans;Courageous Californians;win'] - table = ('Team | MP | W | D | L | P\n' - 'Allegoric Alaskans | 3 | 2 | 0 | 1 | 6\n' - 'Blithering Badgers | 2 | 1 | 1 | 0 | 4\n' - 'Courageous Californians | 2 | 0 | 1 | 1 | 1\n' - 'Devastating Donkeys | 1 | 0 | 0 | 1 | 0') + table = ['Team | MP | W | D | L | P', + 'Allegoric Alaskans | 3 | 2 | 0 | 1 | 6', + 'Blithering Badgers | 2 | 1 | 1 | 0 | 4', + 'Courageous Californians | 2 | 0 | 1 | 1 | 1', + 'Devastating Donkeys | 1 | 0 | 0 | 1 | 0'] self.assertEqual(tally(results), table) def test_ties_broken_alphabetically(self): - results = ('Courageous Californians;Devastating Donkeys;win\n' - 'Allegoric Alaskans;Blithering Badgers;win\n' - 'Devastating Donkeys;Allegoric Alaskans;loss\n' - 'Courageous Californians;Blithering Badgers;win\n' - 'Blithering Badgers;Devastating Donkeys;draw\n' - 'Allegoric Alaskans;Courageous Californians;draw') - - table = ('Team | MP | W | D | L | P\n' - 'Allegoric Alaskans | 3 | 2 | 1 | 0 | 7\n' - 'Courageous Californians | 3 | 2 | 1 | 0 | 7\n' - 'Blithering Badgers | 3 | 0 | 1 | 2 | 1\n' - 'Devastating Donkeys | 3 | 0 | 1 | 2 | 1') + results = ['Courageous Californians;Devastating Donkeys;win', + 'Allegoric Alaskans;Blithering Badgers;win', + 'Devastating Donkeys;Allegoric Alaskans;loss', + 'Courageous Californians;Blithering Badgers;win', + 'Blithering Badgers;Devastating Donkeys;draw', + 'Allegoric Alaskans;Courageous Californians;draw'] + + table = ['Team | MP | W | D | L | P', + 'Allegoric Alaskans | 3 | 2 | 1 | 0 | 7', + 'Courageous Californians | 3 | 2 | 1 | 0 | 7', + 'Blithering Badgers | 3 | 0 | 1 | 2 | 1', + 'Devastating Donkeys | 3 | 0 | 1 | 2 | 1'] self.assertEqual(tally(results), table) diff --git a/exercises/triangle/triangle_test.py b/exercises/triangle/triangle_test.py index 88f97e53fb0..1a61dd1dd4e 100644 --- a/exercises/triangle/triangle_test.py +++ b/exercises/triangle/triangle_test.py @@ -3,19 +3,19 @@ from triangle import is_equilateral, is_isosceles, is_scalene -# Tests adapted from `problem-specifications//canonical-data.json` @ v1.2.0 +# Tests adapted from `problem-specifications//canonical-data.json` @ v1.2.1 class is_equilateralTests(unittest.TestCase): - def test_true_if_all_sides_are_equal(self): + def test_all_sides_are_equal(self): self.assertIs(is_equilateral([2, 2, 2]), True) - def test_false_if_any_side_is_unequal(self): + def test_any_side_is_unequal(self): self.assertIs(is_equilateral([2, 3, 2]), False) - def test_false_if_no_sides_are_equal(self): + def test_no_sides_are_equal(self): self.assertIs(is_equilateral([5, 4, 6]), False) - def test_false_if_all_sides_are_zero(self): + def test_all_zero_sides_is_not_a_triangle(self): self.assertIs(is_equilateral([0, 0, 0]), False) def test_sides_may_be_floats(self): @@ -23,28 +23,28 @@ def test_sides_may_be_floats(self): class is_isoscelesTests(unittest.TestCase): - def test_true_if_last_two_sides_are_equal(self): + def test_last_two_sides_are_equal(self): self.assertIs(is_isosceles([3, 4, 4]), True) - def test_true_if_first_two_sides_are_equal(self): + def test_first_two_sides_are_equal(self): self.assertIs(is_isosceles([4, 4, 3]), True) - def test_true_if_first_and_last_sides_are_equal(self): + def test_first_and_last_sides_are_equal(self): self.assertIs(is_isosceles([4, 3, 4]), True) - def test_is_equilateral_triangles_are_also_is_isosceles(self): + def test_equilateral_triangles_are_also_isosceles(self): self.assertIs(is_isosceles([4, 4, 4]), True) - def test_false_if_no_sides_are_equal(self): + def test_no_sides_are_equal(self): self.assertIs(is_isosceles([2, 3, 4]), False) - def test_violation_of_triangle_inequality_is_not_isosceles_1(self): + def test_first_triangle_inequality_violation(self): self.assertIs(is_isosceles([1, 1, 3]), False) - def test_violation_of_triangle_inequality_is_not_isosceles_2(self): + def test_second_triangle_inequality_violation(self): self.assertIs(is_isosceles([1, 3, 1]), False) - def test_violation_of_triangle_inequality_is_not_isosceles_3(self): + def test_third_triangle_inequality_violation(self): self.assertIs(is_isosceles([3, 1, 1]), False) def test_sides_may_be_floats(self): @@ -52,16 +52,16 @@ def test_sides_may_be_floats(self): class is_scaleneTests(unittest.TestCase): - def test_true_if_no_sides_are_equal(self): + def test_no_sides_are_equal(self): self.assertIs(is_scalene([5, 4, 6]), True) - def test_false_if_all_sides_are_equal(self): + def test_all_sides_are_equal(self): self.assertIs(is_scalene([4, 4, 4]), False) - def test_false_if_two_sides_are_equal(self): + def test_two_sides_are_equal(self): self.assertIs(is_scalene([4, 4, 3]), False) - def test_violation_of_triangle_inequality_not_is_scalene(self): + def test_may_not_violate_triangle_inequality(self): self.assertIs(is_scalene([7, 3, 2]), False) def test_sides_may_be_floats(self):