From b916bdb7506611669dde96db6b230108db271420 Mon Sep 17 00:00:00 2001 From: Fabio Regis Date: Tue, 10 Jan 2023 10:29:26 +0100 Subject: [PATCH 01/55] Completed lasagna exercise. --- .../guidos-gorgeous-lasagna/lasagna.py | 31 ++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/exercises/concept/guidos-gorgeous-lasagna/lasagna.py b/exercises/concept/guidos-gorgeous-lasagna/lasagna.py index b08e1a7149b..84e9929a631 100644 --- a/exercises/concept/guidos-gorgeous-lasagna/lasagna.py +++ b/exercises/concept/guidos-gorgeous-lasagna/lasagna.py @@ -7,9 +7,11 @@ # TODO: consider defining the 'PREPARATION_TIME' constant # equal to the time it takes to prepare a single layer +EXPECTED_BAKE_TIME = 40 +PREPARATION_TIME = 2 # TODO: define the 'bake_time_remaining()' function -def bake_time_remaining(): +def bake_time_remaining(elapsed_bake_time: int): """Calculate the bake time remaining. :param elapsed_bake_time: int - baking time already elapsed. @@ -20,11 +22,38 @@ def bake_time_remaining(): based on the `EXPECTED_BAKE_TIME`. """ + return EXPECTED_BAKE_TIME - elapsed_bake_time + pass # TODO: define the 'preparation_time_in_minutes()' function # and consider using 'PREPARATION_TIME' here +def preparation_time_in_minutes(number_of_layers: int): + """Calculate the preparation time in minutes. + + :param number_of_layers: int - The number of layers of the lasagna. + :return: int - preparation time, derived from 'PREPARATION_TIME'. + + Function that takes the number of layers of the desired lasagna as + an argument and returns how many minutes it will take to prepare the lasagna. + Calculated by number_of_layers * PREPARATION_TIME. + """ + return number_of_layers * PREPARATION_TIME # TODO: define the 'elapsed_time_in_minutes()' function + +def elapsed_time_in_minutes(number_of_layers: int, elapsed_bake_time: int): + """Calculate the time that has been spent preparing and cooking the lasagna. + + :param number_of_layers: int - The number of layers of the lasagna. + :param elapsed_bake_time: int - The number of minutes the lasagna has been cooking in the oven. + :return: int - The number of minutes that have been spent preparing and cooking the lasagna. + + Function that takes the number of layers of the lasagna and the time that + the lasagna has been cooking in the oven and returns the total number of + minutes that have been spent preparing and cooking the lasagna. + Calculated by preparation_time_in_minutes(number_of_layers) + elapsed_bake_time. + """ + return preparation_time_in_minutes(number_of_layers) + elapsed_bake_time \ No newline at end of file From 628fe3fea1060a2db0cf1189cc54c27fd78cf06d Mon Sep 17 00:00:00 2001 From: Fabio Regis Date: Tue, 10 Jan 2023 10:31:10 +0100 Subject: [PATCH 02/55] Add un-edited lasagna python file. --- .../lasagna-original.py | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 exercises/concept/guidos-gorgeous-lasagna/lasagna-original.py diff --git a/exercises/concept/guidos-gorgeous-lasagna/lasagna-original.py b/exercises/concept/guidos-gorgeous-lasagna/lasagna-original.py new file mode 100644 index 00000000000..b08e1a7149b --- /dev/null +++ b/exercises/concept/guidos-gorgeous-lasagna/lasagna-original.py @@ -0,0 +1,30 @@ +"""Functions used in preparing Guido's gorgeous lasagna. + +Learn about Guido, the creator of the Python language: https://en.wikipedia.org/wiki/Guido_van_Rossum +""" + +# TODO: define the 'EXPECTED_BAKE_TIME' constant +# TODO: consider defining the 'PREPARATION_TIME' constant +# equal to the time it takes to prepare a single layer + + +# TODO: define the 'bake_time_remaining()' function +def bake_time_remaining(): + """Calculate the bake time remaining. + + :param elapsed_bake_time: int - baking time already elapsed. + :return: int - remaining bake time (in minutes) derived from 'EXPECTED_BAKE_TIME'. + + Function that takes the actual minutes the lasagna has been in the oven as + an argument and returns how many minutes the lasagna still needs to bake + based on the `EXPECTED_BAKE_TIME`. + """ + + pass + + +# TODO: define the 'preparation_time_in_minutes()' function +# and consider using 'PREPARATION_TIME' here + + +# TODO: define the 'elapsed_time_in_minutes()' function From 381ce1987a47e0b4745c9f33e897be60322b8f92 Mon Sep 17 00:00:00 2001 From: Fabio Regis Date: Tue, 10 Jan 2023 10:36:48 +0100 Subject: [PATCH 03/55] Removed TODO comments and pass. --- .../concept/guidos-gorgeous-lasagna/lasagna.py | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/exercises/concept/guidos-gorgeous-lasagna/lasagna.py b/exercises/concept/guidos-gorgeous-lasagna/lasagna.py index 84e9929a631..6fd8d13bb44 100644 --- a/exercises/concept/guidos-gorgeous-lasagna/lasagna.py +++ b/exercises/concept/guidos-gorgeous-lasagna/lasagna.py @@ -3,14 +3,9 @@ Learn about Guido, the creator of the Python language: https://en.wikipedia.org/wiki/Guido_van_Rossum """ -# TODO: define the 'EXPECTED_BAKE_TIME' constant -# TODO: consider defining the 'PREPARATION_TIME' constant -# equal to the time it takes to prepare a single layer - EXPECTED_BAKE_TIME = 40 PREPARATION_TIME = 2 -# TODO: define the 'bake_time_remaining()' function def bake_time_remaining(elapsed_bake_time: int): """Calculate the bake time remaining. @@ -24,12 +19,6 @@ def bake_time_remaining(elapsed_bake_time: int): return EXPECTED_BAKE_TIME - elapsed_bake_time - pass - - -# TODO: define the 'preparation_time_in_minutes()' function -# and consider using 'PREPARATION_TIME' here - def preparation_time_in_minutes(number_of_layers: int): """Calculate the preparation time in minutes. @@ -42,7 +31,6 @@ def preparation_time_in_minutes(number_of_layers: int): """ return number_of_layers * PREPARATION_TIME -# TODO: define the 'elapsed_time_in_minutes()' function def elapsed_time_in_minutes(number_of_layers: int, elapsed_bake_time: int): """Calculate the time that has been spent preparing and cooking the lasagna. @@ -56,4 +44,5 @@ def elapsed_time_in_minutes(number_of_layers: int, elapsed_bake_time: int): minutes that have been spent preparing and cooking the lasagna. Calculated by preparation_time_in_minutes(number_of_layers) + elapsed_bake_time. """ - return preparation_time_in_minutes(number_of_layers) + elapsed_bake_time \ No newline at end of file + return preparation_time_in_minutes(number_of_layers) + elapsed_bake_time + \ No newline at end of file From ca4f793184554c75d401ea1f62e36400b4ff6fc6 Mon Sep 17 00:00:00 2001 From: Fabio Regis Date: Tue, 10 Jan 2023 11:11:09 +0100 Subject: [PATCH 04/55] Complete currency-exchange exercise. --- .../currency-exchange/exchange-original.py | 66 +++++++++++++++++++ .../concept/currency-exchange/exchange.py | 16 +++-- 2 files changed, 76 insertions(+), 6 deletions(-) create mode 100644 exercises/concept/currency-exchange/exchange-original.py diff --git a/exercises/concept/currency-exchange/exchange-original.py b/exercises/concept/currency-exchange/exchange-original.py new file mode 100644 index 00000000000..5b54fea4aad --- /dev/null +++ b/exercises/concept/currency-exchange/exchange-original.py @@ -0,0 +1,66 @@ +def exchange_money(budget, exchange_rate): + """ + + :param budget: float - amount of money you are planning to exchange. + :param exchange_rate: float - unit value of the foreign currency. + :return: float - exchanged value of the foreign currency you can receive. + """ + + pass + + +def get_change(budget, exchanging_value): + """ + + :param budget: float - amount of money you own. + :param exchanging_value: float - amount of your money you want to exchange now. + :return: float - amount left of your starting currency after exchanging. + """ + + pass + + +def get_value_of_bills(denomination, number_of_bills): + """ + + :param denomination: int - the value of a bill. + :param number_of_bills: int - amount of bills you received. + :return: int - total value of bills you now have. + """ + + pass + + +def get_number_of_bills(budget, denomination): + """ + + :param budget: float - the amount of money you are planning to exchange. + :param denomination: int - the value of a single bill. + :return: int - number of bills after exchanging all your money. + """ + + pass + + +def get_leftover_of_bills(budget, denomination): + """ + + :param budget: float - the amount of money you are planning to exchange. + :param denomination: int - the value of a single bill. + :return: float - the leftover amount that cannot be exchanged given the current denomination. + """ + + pass + + +def exchangeable_value(budget, exchange_rate, spread, denomination): + """ + + :param budget: float - the amount of your money you are planning to exchange. + :param exchange_rate: float - the unit value of the foreign currency. + :param spread: int - percentage that is taken as an exchange fee. + :param denomination: int - the value of a single bill. + :return: int - maximum value you can get. + """ + + pass diff --git a/exercises/concept/currency-exchange/exchange.py b/exercises/concept/currency-exchange/exchange.py index 5b54fea4aad..30d3047a6c3 100644 --- a/exercises/concept/currency-exchange/exchange.py +++ b/exercises/concept/currency-exchange/exchange.py @@ -1,3 +1,5 @@ +import math + def exchange_money(budget, exchange_rate): """ @@ -6,7 +8,7 @@ def exchange_money(budget, exchange_rate): :return: float - exchanged value of the foreign currency you can receive. """ - pass + return budget / exchange_rate def get_change(budget, exchanging_value): @@ -17,7 +19,7 @@ def get_change(budget, exchanging_value): :return: float - amount left of your starting currency after exchanging. """ - pass + return budget - exchanging_value def get_value_of_bills(denomination, number_of_bills): @@ -28,7 +30,7 @@ def get_value_of_bills(denomination, number_of_bills): :return: int - total value of bills you now have. """ - pass + return denomination * number_of_bills def get_number_of_bills(budget, denomination): @@ -39,7 +41,7 @@ def get_number_of_bills(budget, denomination): :return: int - number of bills after exchanging all your money. """ - pass + return math.floor( budget / denomination ) def get_leftover_of_bills(budget, denomination): @@ -50,7 +52,7 @@ def get_leftover_of_bills(budget, denomination): :return: float - the leftover amount that cannot be exchanged given the current denomination. """ - pass + return budget % denomination def exchangeable_value(budget, exchange_rate, spread, denomination): @@ -63,4 +65,6 @@ def exchangeable_value(budget, exchange_rate, spread, denomination): :return: int - maximum value you can get. """ - pass + new_rate = exchange_rate * ( 1 + spread/100 ) + total_new_currency = exchange_money( budget, new_rate ) + return denomination * get_number_of_bills( total_new_currency, denomination ) From 30fbbc824fd5527535cfafe3c761dbc090f15ee4 Mon Sep 17 00:00:00 2001 From: Fabio Regis Date: Tue, 10 Jan 2023 11:49:31 +0100 Subject: [PATCH 05/55] Completed grains exercise. --- exercises/practice/grains/grains-original.py | 6 ++++++ exercises/practice/grains/grains.py | 17 +++++++++++++++-- 2 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 exercises/practice/grains/grains-original.py diff --git a/exercises/practice/grains/grains-original.py b/exercises/practice/grains/grains-original.py new file mode 100644 index 00000000000..16854a796e1 --- /dev/null +++ b/exercises/practice/grains/grains-original.py @@ -0,0 +1,6 @@ +def square(number): + pass + + +def total(): + pass diff --git a/exercises/practice/grains/grains.py b/exercises/practice/grains/grains.py index 16854a796e1..122013c36a6 100644 --- a/exercises/practice/grains/grains.py +++ b/exercises/practice/grains/grains.py @@ -1,6 +1,19 @@ def square(number): - pass + # when the square value is not in the acceptable range + if(number < 1 or number > 64): + raise ValueError("square must be between 1 and 64") + + # With bit-shift operator + # return 1 << (number - 1) + return 2 ** (number - 1) def total(): - pass + total_grains = 0 + + for cell in range(1,65): + total_grains += square(cell) + + # With bit-shift operator + # return ( 1 << 64 ) - 1 + return total_grains From 2be871b87c988b79a0ddde526b195b2f14f54feb Mon Sep 17 00:00:00 2001 From: Fabio Regis Date: Tue, 10 Jan 2023 11:58:43 +0100 Subject: [PATCH 06/55] Completed ghost exercise. --- .../arcade_game-original.py | 46 +++++++++++++++++++ .../ghost-gobble-arcade-game/arcade_game.py | 8 ++-- 2 files changed, 50 insertions(+), 4 deletions(-) create mode 100644 exercises/concept/ghost-gobble-arcade-game/arcade_game-original.py diff --git a/exercises/concept/ghost-gobble-arcade-game/arcade_game-original.py b/exercises/concept/ghost-gobble-arcade-game/arcade_game-original.py new file mode 100644 index 00000000000..c9807d23207 --- /dev/null +++ b/exercises/concept/ghost-gobble-arcade-game/arcade_game-original.py @@ -0,0 +1,46 @@ +"""Functions for implementing the rules of the classic arcade game Pac-Man.""" + + +def eat_ghost(power_pellet_active, touching_ghost): + """Verify that Pac-Man can eat a ghost if he is empowered by a power pellet. + + :param power_pellet_active: bool - does the player have an active power pellet? + :param touching_ghost: bool - is the player touching a ghost? + :return: bool - can the ghost be eaten? + """ + + pass + + +def score(touching_power_pellet, touching_dot): + """Verify that Pac-Man has scored when a power pellet or dot has been eaten. + + :param touching_power_pellet: bool - is the player touching a power pellet? + :param touching_dot: bool - is the player touching a dot? + :return: bool - has the player scored or not? + """ + + pass + + +def lose(power_pellet_active, touching_ghost): + """Trigger the game loop to end (GAME OVER) when Pac-Man touches a ghost without his power pellet. + + :param power_pellet_active: bool - does the player have an active power pellet? + :param touching_ghost: bool - is the player touching a ghost? + :return: bool - has the player lost the game? + """ + + pass + + +def win(has_eaten_all_dots, power_pellet_active, touching_ghost): + """Trigger the victory event when all dots have been eaten. + + :param has_eaten_all_dots: bool - has the player "eaten" all the dots? + :param power_pellet_active: bool - does the player have an active power pellet? + :param touching_ghost: bool - is the player touching a ghost? + :return: bool - has the player won the game? + """ + + pass diff --git a/exercises/concept/ghost-gobble-arcade-game/arcade_game.py b/exercises/concept/ghost-gobble-arcade-game/arcade_game.py index c9807d23207..86cf31ed6f2 100644 --- a/exercises/concept/ghost-gobble-arcade-game/arcade_game.py +++ b/exercises/concept/ghost-gobble-arcade-game/arcade_game.py @@ -9,7 +9,7 @@ def eat_ghost(power_pellet_active, touching_ghost): :return: bool - can the ghost be eaten? """ - pass + return power_pellet_active and touching_ghost def score(touching_power_pellet, touching_dot): @@ -20,7 +20,7 @@ def score(touching_power_pellet, touching_dot): :return: bool - has the player scored or not? """ - pass + return touching_power_pellet or touching_dot def lose(power_pellet_active, touching_ghost): @@ -31,7 +31,7 @@ def lose(power_pellet_active, touching_ghost): :return: bool - has the player lost the game? """ - pass + return touching_ghost and not power_pellet_active def win(has_eaten_all_dots, power_pellet_active, touching_ghost): @@ -43,4 +43,4 @@ def win(has_eaten_all_dots, power_pellet_active, touching_ghost): :return: bool - has the player won the game? """ - pass + return has_eaten_all_dots and not lose(power_pellet_active, touching_ghost) From 445e8d063b9c82339e6398a6f72ed3e602592ee1 Mon Sep 17 00:00:00 2001 From: Fabio Regis Date: Tue, 10 Jan 2023 12:06:06 +0100 Subject: [PATCH 07/55] Completed leap year exercise. --- exercises/practice/leap/leap-original.py | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 exercises/practice/leap/leap-original.py diff --git a/exercises/practice/leap/leap-original.py b/exercises/practice/leap/leap-original.py new file mode 100644 index 00000000000..d3abe5a09b0 --- /dev/null +++ b/exercises/practice/leap/leap-original.py @@ -0,0 +1,9 @@ +def leap_year(year): + if year % 400 == 0 : + return True + elif year % 100 == 0 : + return False + elif year % 4 == 0 : + return True + else: + return False From 4a22fd7e806fc0f36e274bdaca700e361d27b8be Mon Sep 17 00:00:00 2001 From: Fabio Regis Date: Tue, 10 Jan 2023 12:07:11 +0100 Subject: [PATCH 08/55] Completed ghost exercise. --- exercises/practice/leap/leap-original.py | 9 +-------- exercises/practice/leap/leap.py | 9 ++++++++- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/exercises/practice/leap/leap-original.py b/exercises/practice/leap/leap-original.py index d3abe5a09b0..50fd034ff2d 100644 --- a/exercises/practice/leap/leap-original.py +++ b/exercises/practice/leap/leap-original.py @@ -1,9 +1,2 @@ def leap_year(year): - if year % 400 == 0 : - return True - elif year % 100 == 0 : - return False - elif year % 4 == 0 : - return True - else: - return False + pass diff --git a/exercises/practice/leap/leap.py b/exercises/practice/leap/leap.py index 50fd034ff2d..d3abe5a09b0 100644 --- a/exercises/practice/leap/leap.py +++ b/exercises/practice/leap/leap.py @@ -1,2 +1,9 @@ def leap_year(year): - pass + if year % 400 == 0 : + return True + elif year % 100 == 0 : + return False + elif year % 4 == 0 : + return True + else: + return False From ea4bf19b4ddbdfa9ea0a32205c30d623467f3ccb Mon Sep 17 00:00:00 2001 From: Fabio Regis Date: Tue, 10 Jan 2023 12:23:17 +0100 Subject: [PATCH 09/55] Complete meltdown exercise. --- .../conditionals-original.py | 56 +++++++++++++++++++ .../meltdown-mitigation/conditionals.py | 20 ++++++- 2 files changed, 73 insertions(+), 3 deletions(-) create mode 100644 exercises/concept/meltdown-mitigation/conditionals-original.py diff --git a/exercises/concept/meltdown-mitigation/conditionals-original.py b/exercises/concept/meltdown-mitigation/conditionals-original.py new file mode 100644 index 00000000000..1eb0a571ff5 --- /dev/null +++ b/exercises/concept/meltdown-mitigation/conditionals-original.py @@ -0,0 +1,56 @@ +"""Functions to prevent a nuclear meltdown.""" + + +def is_criticality_balanced(temperature, neutrons_emitted): + """Verify criticality is balanced. + + :param temperature: int or float - temperature value in kelvin. + :param neutrons_emitted: int or float - number of neutrons emitted per second. + :return: bool - is criticality balanced? + + A reactor is said to be critical if it satisfies the following conditions: + - The temperature is less than 800 K. + - The number of neutrons emitted per second is greater than 500. + - The product of temperature and neutrons emitted per second is less than 500000. + """ + + pass + + +def reactor_efficiency(voltage, current, theoretical_max_power): + """Assess reactor efficiency zone. + + :param voltage: int or float - voltage value. + :param current: int or float - current value. + :param theoretical_max_power: int or float - power that corresponds to a 100% efficiency. + :return: str - one of ('green', 'orange', 'red', or 'black'). + + Efficiency can be grouped into 4 bands: + + 1. green -> efficiency of 80% or more, + 2. orange -> efficiency of less than 80% but at least 60%, + 3. red -> efficiency below 60%, but still 30% or more, + 4. black -> less than 30% efficient. + + The percentage value is calculated as + (generated power/ theoretical max power)*100 + where generated power = voltage * current + """ + + pass + + +def fail_safe(temperature, neutrons_produced_per_second, threshold): + """Assess and return status code for the reactor. + + :param temperature: int or float - value of the temperature in kelvin. + :param neutrons_produced_per_second: int or float - neutron flux. + :param threshold: int or float - threshold for category. + :return: str - one of ('LOW', 'NORMAL', 'DANGER'). + + 1. 'LOW' -> `temperature * neutrons per second` < 90% of `threshold` + 2. 'NORMAL' -> `temperature * neutrons per second` +/- 10% of `threshold` + 3. 'DANGER' -> `temperature * neutrons per second` is not in the above-stated ranges + """ + + pass diff --git a/exercises/concept/meltdown-mitigation/conditionals.py b/exercises/concept/meltdown-mitigation/conditionals.py index 1eb0a571ff5..bf9dc381dad 100644 --- a/exercises/concept/meltdown-mitigation/conditionals.py +++ b/exercises/concept/meltdown-mitigation/conditionals.py @@ -14,7 +14,7 @@ def is_criticality_balanced(temperature, neutrons_emitted): - The product of temperature and neutrons emitted per second is less than 500000. """ - pass + return temperature < 800 and neutrons_emitted > 500 and temperature * neutrons_emitted < 500000 def reactor_efficiency(voltage, current, theoretical_max_power): @@ -36,8 +36,16 @@ def reactor_efficiency(voltage, current, theoretical_max_power): (generated power/ theoretical max power)*100 where generated power = voltage * current """ + generated_power = voltage * current + efficiency = ( generated_power / theoretical_max_power ) * 100 - pass + if efficiency >= 80 : + return 'green' + if efficiency >= 60 : + return 'orange' + if efficiency >= 30 : + return 'red' + return 'black' def fail_safe(temperature, neutrons_produced_per_second, threshold): @@ -53,4 +61,10 @@ def fail_safe(temperature, neutrons_produced_per_second, threshold): 3. 'DANGER' -> `temperature * neutrons per second` is not in the above-stated ranges """ - pass + percent_of_threshold = temperature * neutrons_produced_per_second * 100 / threshold + + if percent_of_threshold < 90 : + return 'LOW' + if 90 <= percent_of_threshold <= 110 : + return 'NORMAL' + return 'DANGER' From 0a60fae94a159fbe1af95b3eac9bcc1f49ba3ac0 Mon Sep 17 00:00:00 2001 From: Fabio Regis Date: Wed, 11 Jan 2023 08:34:36 +0100 Subject: [PATCH 10/55] Complete traingle exercise. --- .../practice/triangle/triangle-original.py | 10 ++++++ exercises/practice/triangle/triangle.py | 33 +++++++++++++++++-- 2 files changed, 40 insertions(+), 3 deletions(-) create mode 100644 exercises/practice/triangle/triangle-original.py diff --git a/exercises/practice/triangle/triangle-original.py b/exercises/practice/triangle/triangle-original.py new file mode 100644 index 00000000000..3d088c774e8 --- /dev/null +++ b/exercises/practice/triangle/triangle-original.py @@ -0,0 +1,10 @@ +def equilateral(sides): + pass + + +def isosceles(sides): + pass + + +def scalene(sides): + pass diff --git a/exercises/practice/triangle/triangle.py b/exercises/practice/triangle/triangle.py index 3d088c774e8..de97b6c99fe 100644 --- a/exercises/practice/triangle/triangle.py +++ b/exercises/practice/triangle/triangle.py @@ -1,10 +1,37 @@ +def is_triangle(sides): + sides.sort() + if 0 > len(sides) > 3 : + raise ValueError("Sides must be an array containing exactly 3 elements.") + return False + if sides[0] + sides[1] <= sides[2] : + return False + return True + def equilateral(sides): - pass + if not is_triangle(sides) : + return False + if sides[0] == sides[1] == sides[2] : + return True + return False def isosceles(sides): - pass + if not is_triangle(sides) : + return False + if equilateral(sides) : + return True + if sides[0] == sides[1] != sides[2] : + return True + if sides[1] == sides[2] != sides[0] : + return True + if sides[0] == sides[2] != sides[1] : + return True + return False def scalene(sides): - pass + if not is_triangle(sides) : + return False + if sides[0] != sides[1] != sides[2] : + return True + return False From b80f3e225161ea47a56c8c744b1c95abcf557bdc Mon Sep 17 00:00:00 2001 From: Fabio Regis Date: Wed, 11 Jan 2023 09:00:57 +0100 Subject: [PATCH 11/55] Added decorator pattern solution. --- .../practice/triangle/triangle-decorator.py | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 exercises/practice/triangle/triangle-decorator.py diff --git a/exercises/practice/triangle/triangle-decorator.py b/exercises/practice/triangle/triangle-decorator.py new file mode 100644 index 00000000000..013e38a3e19 --- /dev/null +++ b/exercises/practice/triangle/triangle-decorator.py @@ -0,0 +1,27 @@ +def is_triangle(fn): + def inner(sides): + has_three_sides = len(sides) == 3 + return sum(sides) > 2 * max(sides) and has_three_sides and fn(sides) + return inner + +# The @ decorator passes the euilateral function defined just below as the fn argument +# into the is_triangle function and assigns the output of is_triangle (the inner function) +# to equilateral. +@is_triangle +def equilateral(sides): + # A set only holds unique elements. + # When the elements of the sides array are all equal the set reduces + # to a single unique element making its length == 1. + return len(set(sides)) == 1 + +@is_triangle +def isosceles(sides): + # When there are at least two equal elements of the sides array + # it means that the triangle is either + # isosceles [len(set(sides)) == 2] or + # equilateral [len(set(sides)) == 1]. + return len(set(sides)) < 3 + +@is_triangle +def scalene(sides): + return len(set(sides)) == 3 From cdb3ba2fd0ffabc3282e3922fc6fbb55644da8e7 Mon Sep 17 00:00:00 2001 From: Fabio Regis Date: Wed, 11 Jan 2023 09:29:13 +0100 Subject: [PATCH 12/55] Completed Armstrong numbers exercise. --- .../armstrong_numbers-original.py | 2 ++ .../armstrong-numbers/armstrong_numbers.py | 24 ++++++++++++++++++- 2 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 exercises/practice/armstrong-numbers/armstrong_numbers-original.py diff --git a/exercises/practice/armstrong-numbers/armstrong_numbers-original.py b/exercises/practice/armstrong-numbers/armstrong_numbers-original.py new file mode 100644 index 00000000000..b8829fac575 --- /dev/null +++ b/exercises/practice/armstrong-numbers/armstrong_numbers-original.py @@ -0,0 +1,2 @@ +def is_armstrong_number(number): + pass diff --git a/exercises/practice/armstrong-numbers/armstrong_numbers.py b/exercises/practice/armstrong-numbers/armstrong_numbers.py index b8829fac575..6919dcbac4b 100644 --- a/exercises/practice/armstrong-numbers/armstrong_numbers.py +++ b/exercises/practice/armstrong-numbers/armstrong_numbers.py @@ -1,2 +1,24 @@ +# An Armstrong 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 + def is_armstrong_number(number): - pass + # Exclude negative and decimal numbers since they cannot be Armstrong numbers. + if number < 0 or not isinstance(number, int): + raise ValueError("is_armstrong_number function only accepts positive integers.") + # Get the number of digits by casting number as a string and counting the characters. + # Math.log10 doesn't work because it fails with 0 - ValueError: math domain error. + number_of_digits = len(str(number)) + # Extract the single digits into an array + # by treating "number" as a string, looping over each character, + # and casting it back as an integer. + list_of_digits = [int(i) for i in str(number)] + # Execute i**number_of_digits on each element of the list_of_digits + # array using list comprehension and sum the results. + calculated = sum(i ** number_of_digits for i in list_of_digits) + return calculated == number From 813939299116d11bfb057f8babd4dd779c5c5125 Mon Sep 17 00:00:00 2001 From: Fabio Regis Date: Wed, 11 Jan 2023 10:40:59 +0100 Subject: [PATCH 13/55] Completed black-jack exercise. --- .../concept/black-jack/black_jack-original.py | 81 +++++++++++++++++++ exercises/concept/black-jack/black_jack.py | 40 +++++---- 2 files changed, 107 insertions(+), 14 deletions(-) create mode 100644 exercises/concept/black-jack/black_jack-original.py diff --git a/exercises/concept/black-jack/black_jack-original.py b/exercises/concept/black-jack/black_jack-original.py new file mode 100644 index 00000000000..9ce6ca5ba4d --- /dev/null +++ b/exercises/concept/black-jack/black_jack-original.py @@ -0,0 +1,81 @@ +"""Functions to help play and score a game of blackjack. + +How to play blackjack: https://bicyclecards.com/how-to-play/blackjack/ +"Standard" playing cards: https://en.wikipedia.org/wiki/Standard_52-card_deck +""" + + +def value_of_card(card): + """Determine the scoring value of a card. + + :param card: str - given card. + :return: int - value of a given card. See below for values. + + 1. 'J', 'Q', or 'K' (otherwise known as "face cards") = 10 + 2. 'A' (ace card) = 1 + 3. '2' - '10' = numerical value. + """ + + pass + + +def higher_card(card_one, card_two): + """Determine which card has a higher value in the hand. + + :param card_one, card_two: str - cards dealt in hand. See below for values. + :return: str or tuple - resulting Tuple contains both cards if they are of equal value. + + 1. 'J', 'Q', or 'K' (otherwise known as "face cards") = 10 + 2. 'A' (ace card) = 1 + 3. '2' - '10' = numerical value. + """ + + pass + + +def value_of_ace(card_one, card_two): + """Calculate the most advantageous value for the ace card. + + :param card_one, card_two: str - card dealt. See below for values. + :return: int - either 1 or 11 value of the upcoming ace card. + + 1. 'J', 'Q', or 'K' (otherwise known as "face cards") = 10 + 2. 'A' (ace card) = 11 (if already in hand) + 3. '2' - '10' = numerical value. + """ + + pass + + +def is_blackjack(card_one, card_two): + """Determine if the hand is a 'natural' or 'blackjack'. + + :param card_one, card_two: str - card dealt. See below for values. + :return: bool - is the hand is a blackjack (two cards worth 21). + + 1. 'J', 'Q', or 'K' (otherwise known as "face cards") = 10 + 2. 'A' (ace card) = 11 (if already in hand) + 3. '2' - '10' = numerical value. + """ + + pass + + +def can_split_pairs(card_one, card_two): + """Determine if a player can split their hand into two hands. + + :param card_one, card_two: str - cards dealt. + :return: bool - can the hand be split into two pairs? (i.e. cards are of the same value). + """ + + pass + + +def can_double_down(card_one, card_two): + """Determine if a blackjack player can place a double down bet. + + :param card_one, card_two: str - first and second cards in hand. + :return: bool - can the hand can be doubled down? (i.e. totals 9, 10 or 11 points). + """ + + pass diff --git a/exercises/concept/black-jack/black_jack.py b/exercises/concept/black-jack/black_jack.py index 9ce6ca5ba4d..049ea76cb7f 100644 --- a/exercises/concept/black-jack/black_jack.py +++ b/exercises/concept/black-jack/black_jack.py @@ -3,7 +3,11 @@ How to play blackjack: https://bicyclecards.com/how-to-play/blackjack/ "Standard" playing cards: https://en.wikipedia.org/wiki/Standard_52-card_deck """ - +CARD_VALUES = { + 'J': 10, 'Q': 10, 'K': 10, + 'A': 1, + '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9, '10': 10 +} def value_of_card(card): """Determine the scoring value of a card. @@ -15,8 +19,9 @@ def value_of_card(card): 2. 'A' (ace card) = 1 3. '2' - '10' = numerical value. """ - - pass + if not card in CARD_VALUES: + raise ValueError(str(card) + ' is not a valid card.') + return CARD_VALUES[str(card)] def higher_card(card_one, card_two): @@ -29,8 +34,11 @@ def higher_card(card_one, card_two): 2. 'A' (ace card) = 1 3. '2' - '10' = numerical value. """ - - pass + if value_of_card(card_one) == value_of_card(card_two): + return (card_one, card_two) + if value_of_card(card_one) > value_of_card(card_two): + return card_one + return card_two def value_of_ace(card_one, card_two): @@ -43,8 +51,12 @@ def value_of_ace(card_one, card_two): 2. 'A' (ace card) = 11 (if already in hand) 3. '2' - '10' = numerical value. """ - - pass + value_of_card_one = value_of_card(card_one) if value_of_card(card_one) > 1 else 11 + value_of_card_two = value_of_card(card_two) if value_of_card(card_two) > 1 else 11 + value_of_hand = value_of_card_one + value_of_card_two + if value_of_hand <= 10: + return 11 + return 1 def is_blackjack(card_one, card_two): @@ -57,9 +69,11 @@ def is_blackjack(card_one, card_two): 2. 'A' (ace card) = 11 (if already in hand) 3. '2' - '10' = numerical value. """ - - pass - + if card_one != card_two and card_one == 'A' and card_two in ('J', 'Q', 'K', '10'): + return True + if card_one != card_two and card_two == 'A' and card_one in ('J', 'Q', 'K', '10'): + return True + return False def can_split_pairs(card_one, card_two): """Determine if a player can split their hand into two hands. @@ -67,8 +81,7 @@ def can_split_pairs(card_one, card_two): :param card_one, card_two: str - cards dealt. :return: bool - can the hand be split into two pairs? (i.e. cards are of the same value). """ - - pass + return value_of_card(card_one) == value_of_card(card_two) def can_double_down(card_one, card_two): @@ -77,5 +90,4 @@ def can_double_down(card_one, card_two): :param card_one, card_two: str - first and second cards in hand. :return: bool - can the hand can be doubled down? (i.e. totals 9, 10 or 11 points). """ - - pass + return value_of_card(card_one) + value_of_card(card_two) in (9, 10, 11) From 606fde2548bda0d53578a619f7fb79d5a4c1f7db Mon Sep 17 00:00:00 2001 From: Fabio Regis Date: Wed, 11 Jan 2023 10:50:17 +0100 Subject: [PATCH 14/55] Completed exercise difference of squares. --- .../difference_of_squares-original copy.py | 10 ++++++++++ .../difference-of-squares/difference_of_squares.py | 6 +++--- 2 files changed, 13 insertions(+), 3 deletions(-) create mode 100644 exercises/practice/difference-of-squares/difference_of_squares-original copy.py diff --git a/exercises/practice/difference-of-squares/difference_of_squares-original copy.py b/exercises/practice/difference-of-squares/difference_of_squares-original copy.py new file mode 100644 index 00000000000..2b2ec944808 --- /dev/null +++ b/exercises/practice/difference-of-squares/difference_of_squares-original copy.py @@ -0,0 +1,10 @@ +def square_of_sum(number): + pass + + +def sum_of_squares(number): + pass + + +def difference_of_squares(number): + pass diff --git a/exercises/practice/difference-of-squares/difference_of_squares.py b/exercises/practice/difference-of-squares/difference_of_squares.py index 2b2ec944808..d4dc8fff1be 100644 --- a/exercises/practice/difference-of-squares/difference_of_squares.py +++ b/exercises/practice/difference-of-squares/difference_of_squares.py @@ -1,10 +1,10 @@ def square_of_sum(number): - pass + return sum(range(1,number+1))**2 def sum_of_squares(number): - pass + return sum(i**2 for i in range(1,number+1)) def difference_of_squares(number): - pass + return square_of_sum(number) - sum_of_squares(number) From 605a4c862f68695ebd53a1f961696c7bcaa21e95 Mon Sep 17 00:00:00 2001 From: Fabio Regis Date: Wed, 11 Jan 2023 11:09:04 +0100 Subject: [PATCH 15/55] Completed Collatz Conjecture exercise. --- .../collatz_conjecture-original.py | 2 ++ .../collatz-conjecture/collatz_conjecture.py | 15 +++++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) create mode 100644 exercises/practice/collatz-conjecture/collatz_conjecture-original.py diff --git a/exercises/practice/collatz-conjecture/collatz_conjecture-original.py b/exercises/practice/collatz-conjecture/collatz_conjecture-original.py new file mode 100644 index 00000000000..17404248f60 --- /dev/null +++ b/exercises/practice/collatz-conjecture/collatz_conjecture-original.py @@ -0,0 +1,2 @@ +def steps(number): + pass diff --git a/exercises/practice/collatz-conjecture/collatz_conjecture.py b/exercises/practice/collatz-conjecture/collatz_conjecture.py index 17404248f60..c901d1f9b6d 100644 --- a/exercises/practice/collatz-conjecture/collatz_conjecture.py +++ b/exercises/practice/collatz-conjecture/collatz_conjecture.py @@ -1,2 +1,13 @@ -def steps(number): - pass +def steps(number: int) -> int: + if not ( isinstance(number, int) and number > 0 ): + raise ValueError('Only positive integers are allowed') + steps = 0 + while number != 1: + number = do_step(number) + steps += 1 + return steps + +def do_step(number: int) -> int: + if number % 2 == 0: + return number / 2 + return 3 * number + 1 \ No newline at end of file From 4259f970047e5ac68bba881a801514dfcb032c4c Mon Sep 17 00:00:00 2001 From: Fabio Regis Date: Wed, 11 Jan 2023 11:52:47 +0100 Subject: [PATCH 16/55] Completed diffie-hellman exercise. --- exercises/practice/diffie-hellman/diffie_hellman.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/exercises/practice/diffie-hellman/diffie_hellman.py b/exercises/practice/diffie-hellman/diffie_hellman.py index a12455249fd..9b2b485fa3b 100644 --- a/exercises/practice/diffie-hellman/diffie_hellman.py +++ b/exercises/practice/diffie-hellman/diffie_hellman.py @@ -1,10 +1,15 @@ +import secrets + def private_key(p): - pass + # secrets.randbelow(N) returns a random integer between 0 and N exclusive. + return secrets.randbelow(p - 2) + 2 def public_key(p, g, private): - pass + # pow -> (g ** private) % p + return pow(g, private, p) def secret(p, public, private): - pass + # pow -> (public ** private) % p + return pow(public, private, p) From a737e9012469ebf30c47985d88488bc7ee33157b Mon Sep 17 00:00:00 2001 From: Fabio Regis Date: Wed, 11 Jan 2023 13:49:14 +0100 Subject: [PATCH 17/55] Completed yacht exercise. --- .../collatz-conjecture/collatz_conjecture.py | 2 +- exercises/practice/yacht/yacht-original.py | 18 +++++ exercises/practice/yacht/yacht.py | 73 +++++++++++++++---- 3 files changed, 79 insertions(+), 14 deletions(-) create mode 100644 exercises/practice/yacht/yacht-original.py diff --git a/exercises/practice/collatz-conjecture/collatz_conjecture.py b/exercises/practice/collatz-conjecture/collatz_conjecture.py index c901d1f9b6d..d9d6b2fb3a1 100644 --- a/exercises/practice/collatz-conjecture/collatz_conjecture.py +++ b/exercises/practice/collatz-conjecture/collatz_conjecture.py @@ -10,4 +10,4 @@ def steps(number: int) -> int: def do_step(number: int) -> int: if number % 2 == 0: return number / 2 - return 3 * number + 1 \ No newline at end of file + return 3 * number + 1 diff --git a/exercises/practice/yacht/yacht-original.py b/exercises/practice/yacht/yacht-original.py new file mode 100644 index 00000000000..d64452f96ef --- /dev/null +++ b/exercises/practice/yacht/yacht-original.py @@ -0,0 +1,18 @@ +# Score categories. +# Change the values as you see fit. +YACHT = None +ONES = None +TWOS = None +THREES = None +FOURS = None +FIVES = None +SIXES = None +FULL_HOUSE = None +FOUR_OF_A_KIND = None +LITTLE_STRAIGHT = None +BIG_STRAIGHT = None +CHOICE = None + + +def score(dice, category): + pass diff --git a/exercises/practice/yacht/yacht.py b/exercises/practice/yacht/yacht.py index d64452f96ef..d4306c530ac 100644 --- a/exercises/practice/yacht/yacht.py +++ b/exercises/practice/yacht/yacht.py @@ -1,18 +1,65 @@ # Score categories. # Change the values as you see fit. -YACHT = None -ONES = None -TWOS = None -THREES = None -FOURS = None -FIVES = None -SIXES = None -FULL_HOUSE = None -FOUR_OF_A_KIND = None -LITTLE_STRAIGHT = None -BIG_STRAIGHT = None -CHOICE = None +YACHT = 1 +ONES = 2 +TWOS = 3 +THREES = 4 +FOURS = 5 +FIVES = 6 +SIXES = 7 +FULL_HOUSE = 8 +FOUR_OF_A_KIND = 9 +LITTLE_STRAIGHT = 10 +BIG_STRAIGHT = 11 +CHOICE = 12 def score(dice, category): - pass + + if category == YACHT: + return 50 if len(set(dice)) == 1 else 0 + + if category == ONES: + return dice.count(1) + + if category == TWOS: + return dice.count(2) * 2 + + if category == THREES: + return dice.count(3) * 3 + + if category == FOURS: + return dice.count(4) * 4 + + if category == FIVES: + return dice.count(5) * 5 + + if category == SIXES: + return dice.count(6) * 6 + + if category == FULL_HOUSE: + if len(set(dice)) == 2 and ( dice.count(dice[0]) == 2 or dice.count(dice[0]) == 3): + return sum(dice) + else: + return 0 + + if category == FOUR_OF_A_KIND: + if dice.count( max(set(dice), key = dice.count) ) >= 4: + return max(set(dice), key = dice.count) * 4 + else: + return 0 + + if category == LITTLE_STRAIGHT: + if sorted(dice) == [1,2,3,4,5]: + return 30 + else: + return 0 + + if category == BIG_STRAIGHT: + if sorted(dice) == [2,3,4,5,6]: + return 30 + else: + return 0 + + if category == CHOICE: + return sum(dice) From 1ff90d9b8602fed9c2625c2affcef4ea56ba7cab Mon Sep 17 00:00:00 2001 From: Fabio Regis Date: Thu, 12 Jan 2023 09:10:51 +0100 Subject: [PATCH 18/55] Completed bob exercise. --- exercises/practice/bob/bob-original.py | 2 ++ exercises/practice/bob/bob.py | 37 ++++++++++++++++++++++++-- 2 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 exercises/practice/bob/bob-original.py diff --git a/exercises/practice/bob/bob-original.py b/exercises/practice/bob/bob-original.py new file mode 100644 index 00000000000..94a7315112e --- /dev/null +++ b/exercises/practice/bob/bob-original.py @@ -0,0 +1,2 @@ +def response(hey_bob): + pass diff --git a/exercises/practice/bob/bob.py b/exercises/practice/bob/bob.py index 94a7315112e..8d91bbfa8e2 100644 --- a/exercises/practice/bob/bob.py +++ b/exercises/practice/bob/bob.py @@ -1,2 +1,35 @@ -def response(hey_bob): - pass +# Bob is a lackadaisical teenager. In conversation, his responses are very limited. +# Bob answers 'Sure.' if you ask him a question, such as "How are you?". +# He answers 'Whoa, chill out!' if you YELL AT HIM (in all capitals). +# He answers 'Calm down, I know what I'm doing!' if you yell a question at him. +# He says 'Fine. Be that way!' if you address him without actually saying anything. +# He answers 'Whatever.' to anything else. +# Bob's conversational partner is a purist when it comes to written communication and +# always follows normal rules regarding sentence punctuation in English. + +def is_question(message: str) -> bool: + return message.endswith('?') + +def is_yell(message: str) -> bool: + return message.isupper() + +def is_empty(message: str) -> bool: + return message == '' + +def response(hey_bob: str) -> str: + + hey_bob = hey_bob.strip() + + if is_question(hey_bob): + if is_yell(hey_bob): + return 'Calm down, I know what I\'m doing!' + else: + return 'Sure.' + + if is_yell(hey_bob): + return 'Whoa, chill out!' + + if is_empty(hey_bob): + return 'Fine. Be that way!' + + return 'Whatever.' \ No newline at end of file From 29d1ea6af97d428860a46ad92b2d57909822467b Mon Sep 17 00:00:00 2001 From: Fabio Regis Date: Thu, 12 Jan 2023 09:19:46 +0100 Subject: [PATCH 19/55] Completed raindrops exercise. --- .../practice/raindrops/raindrops-original.py | 2 ++ exercises/practice/raindrops/raindrops.py | 20 +++++++++++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) create mode 100644 exercises/practice/raindrops/raindrops-original.py diff --git a/exercises/practice/raindrops/raindrops-original.py b/exercises/practice/raindrops/raindrops-original.py new file mode 100644 index 00000000000..f38f3b611d4 --- /dev/null +++ b/exercises/practice/raindrops/raindrops-original.py @@ -0,0 +1,2 @@ +def convert(number): + pass diff --git a/exercises/practice/raindrops/raindrops.py b/exercises/practice/raindrops/raindrops.py index f38f3b611d4..4cb4bdffc32 100644 --- a/exercises/practice/raindrops/raindrops.py +++ b/exercises/practice/raindrops/raindrops.py @@ -1,2 +1,18 @@ -def convert(number): - pass +def convert(number: int) -> str: + + factor_by_3 = number % 3 == 0 + factor_by_5 = number % 5 == 0 + factor_by_7 = number % 7 == 0 + + output = '' + + if factor_by_3: + output += 'Pling' + if factor_by_5: + output += 'Plang' + if factor_by_7: + output += 'Plong' + if not (factor_by_3 or factor_by_5 or factor_by_7): + output = number + + return str(output) \ No newline at end of file From b96795fa4620e3440b4d1c8f0eb9220023168ddd Mon Sep 17 00:00:00 2001 From: phets Date: Sun, 26 Feb 2023 08:14:02 +0100 Subject: [PATCH 20/55] Completed little-sisters-vocab exercise. --- .../concept/little-sisters-vocab/strings.py | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/exercises/concept/little-sisters-vocab/strings.py b/exercises/concept/little-sisters-vocab/strings.py index 8c4fff0b891..3cb4d8891a1 100644 --- a/exercises/concept/little-sisters-vocab/strings.py +++ b/exercises/concept/little-sisters-vocab/strings.py @@ -1,4 +1,5 @@ """Functions for creating, transforming, and adding prefixes to strings.""" +import string def add_prefix_un(word): @@ -7,12 +8,12 @@ def add_prefix_un(word): :param word: str - containing the root word. :return: str - of root word prepended with 'un'. """ - - pass + return 'un' + word def make_word_groups(vocab_words): - """Transform a list containing a prefix and words into a string with the prefix followed by the words with prefix prepended. + """Transform a list containing a prefix and words into a string with the prefix + followed by the words with prefix prepended. :param vocab_words: list - of vocabulary words with prefix in first index. :return: str - of prefix followed by vocabulary words with @@ -25,8 +26,11 @@ def make_word_groups(vocab_words): For example: list('en', 'close', 'joy', 'lighten'), produces the following string: 'en :: enclose :: enjoy :: enlighten'. """ - - pass + prefix = vocab_words[0] + result = prefix + for word in vocab_words[1:]: + result += ' :: ' + prefix + word + return result def remove_suffix_ness(word): @@ -37,8 +41,10 @@ def remove_suffix_ness(word): For example: "heaviness" becomes "heavy", but "sadness" becomes "sad". """ - - pass + result = word.replace('ness', '') + if result[-1] == 'i': + return result[0:-1] + 'y' + return result def adjective_to_verb(sentence, index): @@ -50,5 +56,5 @@ def adjective_to_verb(sentence, index): For example, ("It got dark as the sun set", 2) becomes "darken". """ - - pass + no_punctuation = sentence.translate(str.maketrans('', '', string.punctuation)) + return no_punctuation.split(' ')[index] + 'en' From cd906e88b60e3a9e011eedb1a6efa4eb3301cf3c Mon Sep 17 00:00:00 2001 From: phets Date: Sun, 26 Feb 2023 08:21:55 +0100 Subject: [PATCH 21/55] Completed little-sisters-essay exercise. --- .../concept/little-sisters-essay/string_methods.py | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/exercises/concept/little-sisters-essay/string_methods.py b/exercises/concept/little-sisters-essay/string_methods.py index 5c5b9ce66dd..94f7395f78e 100644 --- a/exercises/concept/little-sisters-essay/string_methods.py +++ b/exercises/concept/little-sisters-essay/string_methods.py @@ -1,5 +1,5 @@ """Functions to help edit essay homework using string manipulation.""" - +import string def capitalize_title(title): """Convert the first letter of each word in the title to uppercase if needed. @@ -7,8 +7,7 @@ def capitalize_title(title): :param title: str - title string that needs title casing. :return: str - title string in title case (first letters capitalized). """ - - pass + return string.capwords(title, ' ') def check_sentence_ending(sentence): @@ -17,8 +16,7 @@ def check_sentence_ending(sentence): :param sentence: str - a sentence to check. :return: bool - return True if punctuated correctly with period, False otherwise. """ - - pass + return sentence[-1] == '.' def clean_up_spacing(sentence): @@ -27,8 +25,7 @@ def clean_up_spacing(sentence): :param sentence: str - a sentence to clean of leading and trailing space characters. :return: str - a sentence that has been cleaned of leading and trailing space characters. """ - - pass + return sentence.strip() def replace_word_choice(sentence, old_word, new_word): @@ -39,5 +36,4 @@ def replace_word_choice(sentence, old_word, new_word): :param new_word: str - replacement word. :return: str - input sentence with new words in place of old words. """ - - pass + return sentence.replace(old_word, new_word) From b8e6ea28ba30ff937f455de1e572b23ce0e87484 Mon Sep 17 00:00:00 2001 From: phets Date: Sun, 26 Feb 2023 08:39:27 +0100 Subject: [PATCH 22/55] Completed card-games exercise. --- exercises/concept/card-games/lists.py | 28 +++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/exercises/concept/card-games/lists.py b/exercises/concept/card-games/lists.py index 11dff666de6..8809e8dd5b8 100644 --- a/exercises/concept/card-games/lists.py +++ b/exercises/concept/card-games/lists.py @@ -10,8 +10,7 @@ def get_rounds(number): :param number: int - current round number. :return: list - current round and the two that follow. """ - - pass + return [number, number+1, number+2] def concatenate_rounds(rounds_1, rounds_2): @@ -21,8 +20,7 @@ def concatenate_rounds(rounds_1, rounds_2): :param rounds_2: list - second set of rounds played. :return: list - all rounds played. """ - - pass + return rounds_1 + rounds_2 def list_contains_round(rounds, number): @@ -32,8 +30,7 @@ def list_contains_round(rounds, number): :param number: int - round number. :return: bool - was the round played? """ - - pass + return number in rounds def card_average(hand): @@ -42,8 +39,7 @@ def card_average(hand): :param hand: list - cards in hand. :return: float - average value of the cards in the hand. """ - - pass + return sum(hand) / len(hand) def approx_average_is_average(hand): @@ -52,8 +48,10 @@ def approx_average_is_average(hand): :param hand: list - cards in hand. :return: bool - does one of the approximate averages equal the `true average`? """ - - pass + median = hand[len(hand)//2] + approx = ( hand[0] + hand[-1] ) / 2 + average = card_average(hand) + return average in (median, approx) def average_even_is_average_odd(hand): @@ -62,8 +60,9 @@ def average_even_is_average_odd(hand): :param hand: list - cards in hand. :return: bool - are even and odd averages equal? """ - - pass + even = hand[::2] + odd = hand[1::2] + return card_average(even) == card_average(odd) def maybe_double_last(hand): @@ -72,5 +71,6 @@ def maybe_double_last(hand): :param hand: list - cards in hand. :return: list - hand with Jacks (if present) value doubled. """ - - pass + if hand[-1] == 11: + hand[-1] = 22 + return hand From 4f669723535363772a20c0c74d360ce22523b137 Mon Sep 17 00:00:00 2001 From: phets Date: Sun, 26 Feb 2023 09:06:52 +0100 Subject: [PATCH 23/55] Completed chaitanas-colossal-coaster exercise. --- .../list_methods.py | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/exercises/concept/chaitanas-colossal-coaster/list_methods.py b/exercises/concept/chaitanas-colossal-coaster/list_methods.py index 6603b5adf67..68a479f7a01 100644 --- a/exercises/concept/chaitanas-colossal-coaster/list_methods.py +++ b/exercises/concept/chaitanas-colossal-coaster/list_methods.py @@ -10,8 +10,11 @@ def add_me_to_the_queue(express_queue, normal_queue, ticket_type, person_name): :param person_name: str - name of person to add to a queue. :return: list - the (updated) queue the name was added to. """ - - pass + if ticket_type == 1: + express_queue.append(person_name) + return express_queue + normal_queue.append(person_name) + return normal_queue def find_my_friend(queue, friend_name): @@ -21,8 +24,7 @@ def find_my_friend(queue, friend_name): :param friend_name: str - name of friend to find. :return: int - index at which the friends name was found. """ - - pass + return queue.index(friend_name) def add_me_with_my_friends(queue, index, person_name): @@ -33,8 +35,8 @@ def add_me_with_my_friends(queue, index, person_name): :param person_name: str - the name to add. :return: list - queue updated with new name. """ - - pass + queue.insert(index, person_name) + return queue def remove_the_mean_person(queue, person_name): @@ -44,8 +46,8 @@ def remove_the_mean_person(queue, person_name): :param person_name: str - name of mean person. :return: list - queue update with the mean persons name removed. """ - - pass + queue.remove(person_name) + return queue def how_many_namefellows(queue, person_name): @@ -55,8 +57,7 @@ def how_many_namefellows(queue, person_name): :param person_name: str - name you wish to count or track. :return: int - the number of times the name appears in the queue. """ - - pass + return queue.count(person_name) def remove_the_last_person(queue): @@ -65,8 +66,7 @@ def remove_the_last_person(queue): :param queue: list - names in the queue. :return: str - name that has been removed from the end of the queue. """ - - pass + return queue.pop(-1) def sorted_names(queue): @@ -75,5 +75,4 @@ def sorted_names(queue): :param queue: list - names in the queue. :return: list - copy of the queue in alphabetical order. """ - - pass + return sorted(queue) From 9ec47df051fb9e7cae14910185a26d20b19cf35d Mon Sep 17 00:00:00 2001 From: phets Date: Sun, 26 Feb 2023 18:34:07 +0100 Subject: [PATCH 24/55] Completed making-the-grade exercise. --- exercises/concept/making-the-grade/loops.py | 24 ++++++++++----------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/exercises/concept/making-the-grade/loops.py b/exercises/concept/making-the-grade/loops.py index f1071b23b52..e1c8f552fb9 100644 --- a/exercises/concept/making-the-grade/loops.py +++ b/exercises/concept/making-the-grade/loops.py @@ -7,8 +7,7 @@ def round_scores(student_scores): :param student_scores: list - float or int of student exam scores. :return: list - student scores *rounded* to nearest integer value. """ - - pass + return list(map(round,student_scores)) def count_failed_students(student_scores): @@ -17,8 +16,7 @@ def count_failed_students(student_scores): :param student_scores: list - containing int student scores. :return: int - count of student scores at or below 40. """ - - pass + return sum(map(lambda x : x<=40,student_scores)) def above_threshold(student_scores, threshold): @@ -28,8 +26,7 @@ def above_threshold(student_scores, threshold): :param threshold: int - threshold to cross to be the "best" score. :return: list - of integer scores that are at or above the "best" threshold. """ - - pass + return [score for score in student_scores if score >= threshold] def letter_grades(highest): @@ -45,8 +42,8 @@ def letter_grades(highest): 71 <= "B" <= 85 86 <= "A" <= 100 """ - - pass + interval = round(( highest - 40 ) / 4) + return [41 + index*interval for index in range(4)] def student_ranking(student_scores, student_names): @@ -56,8 +53,10 @@ def student_ranking(student_scores, student_names): :param student_names: list - of string names by exam score in descending order. :return: list - of strings in format [". : "]. """ - - pass + output = [] + for index, (score, name) in enumerate(zip(student_scores,student_names)): + output.append(f'{index+1}. {name}: {score}') + return output def perfect_score(student_info): @@ -66,5 +65,6 @@ def perfect_score(student_info): :param student_info: list - of [, ] lists. :return: list - first `[, 100]` or `[]` if no student score of 100 is found. """ - - pass + for name, score in student_info: + if score == 100: return [name,score] + return [] From c97430e67479be5d7d3e9d733a481bd78d3494e2 Mon Sep 17 00:00:00 2001 From: phets Date: Sun, 26 Feb 2023 19:00:39 +0100 Subject: [PATCH 25/55] Completed tisbury-treasure-hunt exercise. --- .../concept/tisbury-treasure-hunt/tuples.py | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/exercises/concept/tisbury-treasure-hunt/tuples.py b/exercises/concept/tisbury-treasure-hunt/tuples.py index 92336d88ec1..a5ec5083c5a 100644 --- a/exercises/concept/tisbury-treasure-hunt/tuples.py +++ b/exercises/concept/tisbury-treasure-hunt/tuples.py @@ -7,8 +7,7 @@ def get_coordinate(record): :param record: tuple - with a (treasure, coordinate) pair. :return: str - the extracted map coordinate. """ - - pass + return record[1] def convert_coordinate(coordinate): @@ -17,8 +16,7 @@ def convert_coordinate(coordinate): :param coordinate: str - a string map coordinate :return: tuple - the string coordinate split into its individual components. """ - - pass + return (coordinate[0],coordinate[1]) def compare_records(azara_record, rui_record): @@ -28,8 +26,7 @@ def compare_records(azara_record, rui_record): :param rui_record: tuple - a (location, tuple(coordinate_1, coordinate_2), quadrant) trio. :return: bool - do the coordinates match? """ - - pass + return convert_coordinate(get_coordinate(azara_record)) == rui_record[1] def create_record(azara_record, rui_record): @@ -39,8 +36,9 @@ def create_record(azara_record, rui_record): :param rui_record: tuple - a (location, coordinate, quadrant) trio. :return: tuple or str - the combined record (if compatible), or the string "not a match" (if incompatible). """ - - pass + if compare_records(azara_record, rui_record): + return ( azara_record[0], azara_record[1], rui_record[0], rui_record[1], rui_record[2]) + return 'not a match' def clean_up(combined_record_group): @@ -53,5 +51,7 @@ def clean_up(combined_record_group): (see HINTS.md for an example). """ - - pass + out = '' + for record in combined_record_group: + out += f"('{record[0]}', '{record[2]}', {record[3]}, '{record[4]}')\n" + return out From cd62058c05794769d0b3229481fb811cb9c9c13d Mon Sep 17 00:00:00 2001 From: phets Date: Sun, 26 Feb 2023 19:30:42 +0100 Subject: [PATCH 26/55] Completed inventory-management exercise. --- .../concept/inventory-management/dicts.py | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/exercises/concept/inventory-management/dicts.py b/exercises/concept/inventory-management/dicts.py index d8f0ea2e81d..1cd4c950b58 100644 --- a/exercises/concept/inventory-management/dicts.py +++ b/exercises/concept/inventory-management/dicts.py @@ -7,8 +7,10 @@ def create_inventory(items): :param items: list - list of items to create an inventory from. :return: dict - the inventory dictionary. """ - - pass + inventory = {} + for item in items: + inventory[item] = inventory.get(item, 0) + 1 + return inventory def add_items(inventory, items): @@ -18,8 +20,9 @@ def add_items(inventory, items): :param items: list - list of items to update the inventory with. :return: dict - the inventory updated with the new items. """ - - pass + for item in items: + inventory[item] = inventory.get(item, 0) + 1 + return inventory def decrement_items(inventory, items): @@ -29,8 +32,12 @@ def decrement_items(inventory, items): :param items: list - list of items to decrement from the inventory. :return: dict - updated inventory with items decremented. """ - - pass + for item in items: + if inventory[item] > 0: + inventory[item] -= 1 + else: + inventory[item] = inventory.get(item, 0) + return inventory def remove_item(inventory, item): @@ -40,8 +47,8 @@ def remove_item(inventory, item): :param item: str - item to remove from the inventory. :return: dict - updated inventory with item removed. Current inventory if item does not match. """ - - pass + inventory.pop(item, None) + return inventory def list_inventory(inventory): @@ -50,5 +57,4 @@ def list_inventory(inventory): :param inventory: dict - an inventory dictionary. :return: list of tuples - list of key, value pairs from the inventory dictionary. """ - - pass + return [(name, count) for name,count in inventory.items() if count > 0] From 6d5008ac1cd25b42a7a65066fa4e7b911ea00204 Mon Sep 17 00:00:00 2001 From: phets Date: Sun, 26 Feb 2023 20:12:04 +0100 Subject: [PATCH 27/55] WIP cater-waiter. --- exercises/concept/cater-waiter/sets.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/exercises/concept/cater-waiter/sets.py b/exercises/concept/cater-waiter/sets.py index b0202e6a5fb..08f284e7df4 100644 --- a/exercises/concept/cater-waiter/sets.py +++ b/exercises/concept/cater-waiter/sets.py @@ -20,8 +20,7 @@ def clean_ingredients(dish_name, dish_ingredients): This function should return a `tuple` with the name of the dish as the first item, followed by the de-duped `set` of ingredients as the second item. """ - - pass + return (dish_name,set(dish_ingredients)) def check_drinks(drink_name, drink_ingredients): @@ -35,8 +34,10 @@ def check_drinks(drink_name, drink_ingredients): name followed by "Cocktail" (includes alcohol). """ + if any(ingredient in ALCOHOLS for ingredient in drink_ingredients): + return f'{drink_name} Cocktail' + return f'{drink_name} Mocktail' - pass def categorize_dish(dish_name, dish_ingredients): @@ -51,8 +52,12 @@ def categorize_dish(dish_name, dish_ingredients): All dishes will "fit" into one of the categories imported from `sets_categories_data.py` """ - - pass + category_strings = ['VEGAN', 'VEGETARIAN', 'PALEO', 'KETO', 'OMNIVORE'] + categories = [VEGAN, VEGETARIAN, PALEO, KETO, OMNIVORE] + for category_string, category in zip(category_strings,categories): + if set(dish_ingredients).issubset(category): + return f'{dish_name}: {category_string}' + return f'{dish_name}: NO CATEGORY' def tag_special_ingredients(dish): @@ -65,8 +70,7 @@ def tag_special_ingredients(dish): For the purposes of this exercise, all allergens or special ingredients that need to be tracked are in the SPECIAL_INGREDIENTS constant imported from `sets_categories_data.py`. """ - - pass + return (dish[0],SPECIAL_INGREDIENTS.intersection(dish[1])) def compile_ingredients(dishes): @@ -77,8 +81,7 @@ def compile_ingredients(dishes): This function should return a `set` of all ingredients from all listed dishes. """ - - pass + return set.union(*dishes) def separate_appetizers(dishes, appetizers): From 730adfaa5a14e10b2545c3ba1c64c71053634a04 Mon Sep 17 00:00:00 2001 From: phets Date: Mon, 27 Feb 2023 07:52:12 +0100 Subject: [PATCH 28/55] Completed cater-waiter exercise. --- exercises/concept/cater-waiter/sets.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/exercises/concept/cater-waiter/sets.py b/exercises/concept/cater-waiter/sets.py index 08f284e7df4..4b8e206a4de 100644 --- a/exercises/concept/cater-waiter/sets.py +++ b/exercises/concept/cater-waiter/sets.py @@ -94,8 +94,7 @@ def separate_appetizers(dishes, appetizers): The function should return the list of dish names with appetizer names removed. Either list could contain duplicates and may require de-duping. """ - - pass + return list( set(dishes) - set(appetizers) ) def singleton_ingredients(dishes, intersection): @@ -112,5 +111,5 @@ def singleton_ingredients(dishes, intersection): The function should return a `set` of ingredients that only appear in a single dish. """ - - pass + all_ingredients_set = compile_ingredients(dishes) + return all_ingredients_set - intersection From 8c031ef05df711abd315bfbc8e26aa5779f3197b Mon Sep 17 00:00:00 2001 From: phets Date: Mon, 27 Feb 2023 09:36:58 +0100 Subject: [PATCH 29/55] Completed locomotive-engineer exercise. --- .../locomotive-engineer/locomotive_engineer.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/exercises/concept/locomotive-engineer/locomotive_engineer.py b/exercises/concept/locomotive-engineer/locomotive_engineer.py index d9291f65a32..31e7749b999 100644 --- a/exercises/concept/locomotive-engineer/locomotive_engineer.py +++ b/exercises/concept/locomotive-engineer/locomotive_engineer.py @@ -2,13 +2,13 @@ # TODO: define the 'get_list_of_wagons' function -def get_list_of_wagons(): +def get_list_of_wagons(*ids): """Return a list of wagons. :param: arbitrary number of wagons. :return: list - list of wagons. """ - pass + return list(ids) # TODO: define the 'fixListOfWagons()' function @@ -19,19 +19,19 @@ def fix_list_of_wagons(each_wagons_id, missing_wagons): :parm missing_wagons: list - the list of missing wagons. :return: list - list of wagons. """ - pass + wagon1, wagon2, locomotive, *other_wagons = each_wagons_id + return [locomotive, *missing_wagons, *other_wagons, wagon1, wagon2] # TODO: define the 'add_missing_stops()' function -def add_missing_stops(): +def add_missing_stops(route, **stops): """Add missing stops to route dict. :param route: dict - the dict of routing information. :param: arbitrary number of stops. :return: dict - updated route dictionary. """ - pass - + return route | {'stops': list(stops.values())} # TODO: define the 'extend_route_information()' function def extend_route_information(route, more_route_information): @@ -41,7 +41,7 @@ def extend_route_information(route, more_route_information): :param more_route_information: dict - extra route information. :return: dict - extended route information. """ - pass + return route | more_route_information # TODO: define the 'fix_wagon_depot()' function @@ -51,4 +51,4 @@ def fix_wagon_depot(wagons_rows): :param wagons_rows: list[list[tuple]] - the list of rows of wagons. :return: list[list[tuple]] - list of rows of wagons. """ - pass + return list(map(list,zip(*wagons_rows))) From 86fda2b93c56e094aea13261561cc9cb2d994a35 Mon Sep 17 00:00:00 2001 From: phets Date: Mon, 27 Feb 2023 10:47:25 +0100 Subject: [PATCH 30/55] Completed ellens-alien-game exercise. --- .../concept/ellens-alien-game/classes.py | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/exercises/concept/ellens-alien-game/classes.py b/exercises/concept/ellens-alien-game/classes.py index 4e45b96ac7d..3b5dc924bc0 100644 --- a/exercises/concept/ellens-alien-game/classes.py +++ b/exercises/concept/ellens-alien-game/classes.py @@ -19,7 +19,32 @@ class Alien: collision_detection(other): Implementation TBD. """ - pass + total_aliens_created = 0 + + + def __init__(self, x_coordinate, y_coordinate): + self.x_coordinate = x_coordinate + self.y_coordinate = y_coordinate + self.health = 3 + Alien.total_aliens_created += 1 + + + def hit(self): + self.health -= 1 + + + def is_alive(self): + return self.health > 0 + + + def teleport(self, x_coordinate, y_coordinate): + self.x_coordinate = x_coordinate + self.y_coordinate = y_coordinate + + def collision_detection(self,other_object): + pass #TODO: create the new_aliens_collection() function below to call your Alien class with a list of coordinates. +def new_aliens_collection(positions): + return [Alien(*position) for position in positions] From f168437827b7d4816b08e8f5f8cecca618ae506f Mon Sep 17 00:00:00 2001 From: phets Date: Tue, 28 Feb 2023 12:20:29 +0100 Subject: [PATCH 31/55] palindrome-products WIP --- .../palindrome_products.py | 53 +++++++++++++++++-- 1 file changed, 50 insertions(+), 3 deletions(-) diff --git a/exercises/practice/palindrome-products/palindrome_products.py b/exercises/practice/palindrome-products/palindrome_products.py index d4c26684e15..70e667ce84c 100644 --- a/exercises/practice/palindrome-products/palindrome_products.py +++ b/exercises/practice/palindrome-products/palindrome_products.py @@ -1,4 +1,4 @@ -def largest(min_factor, max_factor): +def largest(min_factor: int=0, max_factor: int=0) -> tuple: """Given a range of numbers, find the largest palindromes which are products of two numbers within that range. @@ -7,8 +7,24 @@ def largest(min_factor, max_factor): :return: tuple of (palindrome, iterable). Iterable should contain both factors of the palindrome in an arbitrary order. """ + if min_factor > max_factor: + # if the max_factor is less than the min_factor + raise ValueError("min must be <= max") - pass + largest_palindrome = 0 + largest_palindrome_factors = [] + for product in range (min_factor ** 2, max_factor ** 2 + 1): + if is_palindrome(product) and product > largest_palindrome: + factors = find_factors(product, min_factor, max_factor) + if not factors: + continue + else: + largest_palindrome = product + largest_palindrome_factors = factors + if not largest_palindrome_factors: + return None + else: + return (largest_palindrome, largest_palindrome_factors) def smallest(min_factor, max_factor): @@ -20,5 +36,36 @@ def smallest(min_factor, max_factor): :return: tuple of (palindrome, iterable). Iterable should contain both factors of the palindrome in an arbitrary order. """ + if min_factor > max_factor: + # if the max_factor is less than the min_factor + raise ValueError("min must be <= max") - pass + smallest_palindrome = max_factor ** 2 + smallest_palindrome_factors = [] + for product in range (min_factor ** 2, max_factor ** 2 + 1): + if is_palindrome(product) and product < smallest_palindrome: + factors = find_factors(product, min_factor, max_factor) + if not factors: + continue + else: + smallest_palindrome = product + smallest_palindrome_factors = factors + if not smallest_palindrome_factors: + return None + else: + return (smallest_palindrome, smallest_palindrome_factors) + + +def is_palindrome(number): + return str(number) == str(number)[::-1] + +def find_factors(number: int, min_factor: int, max_factor: int) -> list: + output = [] + for factor_1 in range(min_factor,max_factor+1,1): + for factor_2 in range(min_factor,max_factor+1,1): + if factor_1 * factor_2 == number: + output.append([factor_1, factor_2]) + return output + +print(largest(15,15)) +print(smallest(1002,1003)) \ No newline at end of file From 93685336d64ee43c904944c48d0c26b85f782202 Mon Sep 17 00:00:00 2001 From: phets Date: Tue, 28 Feb 2023 21:31:17 +0100 Subject: [PATCH 32/55] palindrome products WIP --- .../palindrome-products/palindrome_products.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/exercises/practice/palindrome-products/palindrome_products.py b/exercises/practice/palindrome-products/palindrome_products.py index 70e667ce84c..183d0e8ffcf 100644 --- a/exercises/practice/palindrome-products/palindrome_products.py +++ b/exercises/practice/palindrome-products/palindrome_products.py @@ -7,9 +7,7 @@ def largest(min_factor: int=0, max_factor: int=0) -> tuple: :return: tuple of (palindrome, iterable). Iterable should contain both factors of the palindrome in an arbitrary order. """ - if min_factor > max_factor: - # if the max_factor is less than the min_factor - raise ValueError("min must be <= max") + validate(min_factor, max_factor) largest_palindrome = 0 largest_palindrome_factors = [] @@ -22,7 +20,7 @@ def largest(min_factor: int=0, max_factor: int=0) -> tuple: largest_palindrome = product largest_palindrome_factors = factors if not largest_palindrome_factors: - return None + return (None,[]) else: return (largest_palindrome, largest_palindrome_factors) @@ -36,9 +34,7 @@ def smallest(min_factor, max_factor): :return: tuple of (palindrome, iterable). Iterable should contain both factors of the palindrome in an arbitrary order. """ - if min_factor > max_factor: - # if the max_factor is less than the min_factor - raise ValueError("min must be <= max") + validate(min_factor, max_factor) smallest_palindrome = max_factor ** 2 smallest_palindrome_factors = [] @@ -51,7 +47,7 @@ def smallest(min_factor, max_factor): smallest_palindrome = product smallest_palindrome_factors = factors if not smallest_palindrome_factors: - return None + return (None,[]) else: return (smallest_palindrome, smallest_palindrome_factors) @@ -59,6 +55,9 @@ def smallest(min_factor, max_factor): def is_palindrome(number): return str(number) == str(number)[::-1] +def validate(min_factor: int, max_factor: int) -> None: + if min_factor < max_factor: raise ValueError("min must be <= max") + def find_factors(number: int, min_factor: int, max_factor: int) -> list: output = [] for factor_1 in range(min_factor,max_factor+1,1): From db5f18852d7a62ba2a02bf5150dac5d6376403d8 Mon Sep 17 00:00:00 2001 From: phets Date: Wed, 1 Mar 2023 14:14:33 +0100 Subject: [PATCH 33/55] Completed palindrome-factors exercise. --- .../palindrome_products.py | 44 +++++++++++-------- 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/exercises/practice/palindrome-products/palindrome_products.py b/exercises/practice/palindrome-products/palindrome_products.py index 183d0e8ffcf..557ab8c7707 100644 --- a/exercises/practice/palindrome-products/palindrome_products.py +++ b/exercises/practice/palindrome-products/palindrome_products.py @@ -1,3 +1,5 @@ +import time + def largest(min_factor: int=0, max_factor: int=0) -> tuple: """Given a range of numbers, find the largest palindromes which are products of two numbers within that range. @@ -11,14 +13,15 @@ def largest(min_factor: int=0, max_factor: int=0) -> tuple: largest_palindrome = 0 largest_palindrome_factors = [] - for product in range (min_factor ** 2, max_factor ** 2 + 1): - if is_palindrome(product) and product > largest_palindrome: - factors = find_factors(product, min_factor, max_factor) - if not factors: - continue - else: - largest_palindrome = product - largest_palindrome_factors = factors + for product in range(max_factor**2, min_factor**2-1,-1): + if is_palindrome(product): + factors = find_factors(product, min_factor, max_factor) + if not factors: + continue + else: + largest_palindrome = product + largest_palindrome_factors = factors + break if not largest_palindrome_factors: return (None,[]) else: @@ -36,16 +39,19 @@ def smallest(min_factor, max_factor): """ validate(min_factor, max_factor) - smallest_palindrome = max_factor ** 2 smallest_palindrome_factors = [] - for product in range (min_factor ** 2, max_factor ** 2 + 1): - if is_palindrome(product) and product < smallest_palindrome: + for product in range(min_factor**2, max_factor**2+1): + if is_palindrome(product): + start = time.time() factors = find_factors(product, min_factor, max_factor) + end = time.time() + print(f'find_factors({product},{min_factor},{max_factor}) took {end-start}') if not factors: continue else: smallest_palindrome = product smallest_palindrome_factors = factors + break if not smallest_palindrome_factors: return (None,[]) else: @@ -56,15 +62,17 @@ def is_palindrome(number): return str(number) == str(number)[::-1] def validate(min_factor: int, max_factor: int) -> None: - if min_factor < max_factor: raise ValueError("min must be <= max") + if min_factor > max_factor: raise ValueError("min must be <= max") def find_factors(number: int, min_factor: int, max_factor: int) -> list: output = [] - for factor_1 in range(min_factor,max_factor+1,1): - for factor_2 in range(min_factor,max_factor+1,1): - if factor_1 * factor_2 == number: - output.append([factor_1, factor_2]) + factor_1 = min_factor + while factor_1 <= max_factor: + factor_2, rem = divmod(number,factor_1) + if rem == 0 and min_factor <= factor_2 <= max_factor: + output.append([factor_1, factor_2]) + factor_1 += 1 return output -print(largest(15,15)) -print(smallest(1002,1003)) \ No newline at end of file +print(largest(min_factor=1000, max_factor=9999)) +print(smallest(min_factor=1000, max_factor=9999)) \ No newline at end of file From f258a3b430151ff13c2ec7d6815c8a94f71aa463 Mon Sep 17 00:00:00 2001 From: phets Date: Wed, 1 Mar 2023 22:45:02 +0100 Subject: [PATCH 34/55] Completed reverse-string exercise. --- exercises/practice/reverse-string/reverse_string.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exercises/practice/reverse-string/reverse_string.py b/exercises/practice/reverse-string/reverse_string.py index 4690cc7c834..7685bd0ad7f 100644 --- a/exercises/practice/reverse-string/reverse_string.py +++ b/exercises/practice/reverse-string/reverse_string.py @@ -1,2 +1,2 @@ -def reverse(text): - pass +def reverse(text: str) -> str: + return text[::-1] From d60d3857dfce552994e39591003c194835206d2f Mon Sep 17 00:00:00 2001 From: phets Date: Thu, 2 Mar 2023 03:23:34 +0100 Subject: [PATCH 35/55] Completed resistor-color exercise. --- .../practice/resistor-color/resistor_color.py | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/exercises/practice/resistor-color/resistor_color.py b/exercises/practice/resistor-color/resistor_color.py index 34915958e8d..0d99d63e723 100644 --- a/exercises/practice/resistor-color/resistor_color.py +++ b/exercises/practice/resistor-color/resistor_color.py @@ -1,6 +1,21 @@ -def color_code(color): - pass +from enum import Enum + +class ResistorColors(Enum): + BLACK = 0 + BROWN = 1 + RED = 2 + ORANGE = 3 + YELLOW = 4 + GREEN = 5 + BLUE = 6 + VIOLET = 7 + GREY = 8 + WHITE = 9 + + +def color_code(color: str) -> int: + return ResistorColors[color.strip().upper()].value def colors(): - pass + return [color.name.lower() for color in ResistorColors] From 443df1ecf0b9acb4428f4ea5d2c34d8851bdb83c Mon Sep 17 00:00:00 2001 From: phets Date: Thu, 2 Mar 2023 03:52:31 +0100 Subject: [PATCH 36/55] Completed two-fer exercise. --- exercises/practice/two-fer/two_fer.py | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/exercises/practice/two-fer/two_fer.py b/exercises/practice/two-fer/two_fer.py index 680c26b277a..858befb6c05 100644 --- a/exercises/practice/two-fer/two_fer.py +++ b/exercises/practice/two-fer/two_fer.py @@ -1,2 +1,20 @@ -def two_fer(name): - pass +""" +This module solves the two_fer exercise from exercism.org. + +Functions: + + two_fer(str) -> str +""" + +def two_fer(name: str='you') -> str: + """ + This function creates and returns a string like: + One for {name}, one for me. + + Args: + name (str, optional): The name to insert in the string. Defaults to 'you'. + + Returns: + str: One for {name}, one for me. + """ + return f'One for {name}, one for me.' From f12827dd7db32017e2a6a4cf926abd7ebfb276f3 Mon Sep 17 00:00:00 2001 From: phets Date: Thu, 2 Mar 2023 04:07:39 +0100 Subject: [PATCH 37/55] Completed resistor-color-duo exercise. --- .../resistor-color-duo/resistor_color_duo.py | 44 ++++++++++++++++++- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/exercises/practice/resistor-color-duo/resistor_color_duo.py b/exercises/practice/resistor-color-duo/resistor_color_duo.py index f83a1c237cb..46ef5c6af61 100644 --- a/exercises/practice/resistor-color-duo/resistor_color_duo.py +++ b/exercises/practice/resistor-color-duo/resistor_color_duo.py @@ -1,2 +1,42 @@ -def value(colors): - pass +""" +This module solves the resistor_color_duo python exercise from exercism.org +The exercise focuses on converting resistance color codes to resistance values. + +Classes: + ResistorColors + +Functions: + value(list) -> int +""" + +from enum import Enum + +class ResistorColors(Enum): + """ + An Enum that maps color names to resistance values. + """ + BLACK = 0 + BROWN = 1 + RED = 2 + ORANGE = 3 + YELLOW = 4 + GREEN = 5 + BLUE = 6 + VIOLET = 7 + GREY = 8 + WHITE = 9 + + +def value(colors: list) -> int: + """ + A function to transform a list of colors into a two digit resistance value. + Only the first two colors are considered, the rest are ignored. + + Args: + colors (list): A list of with two or more colors. + + Returns: + int: An integer between 1 and 99 representing the resistance value. + """ + color1, color2, *other_colors = colors + return int(str(ResistorColors[color1.strip().upper()].value) + str(ResistorColors[color2.strip().upper()].value)) From fc3289e4ac246bccfeb5f15fb35ac39f893802d1 Mon Sep 17 00:00:00 2001 From: phets Date: Thu, 2 Mar 2023 11:45:30 +0100 Subject: [PATCH 38/55] Completed pangram exercise. --- exercises/practice/pangram/pangram.py | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/exercises/practice/pangram/pangram.py b/exercises/practice/pangram/pangram.py index 5377191ef56..a0d7c6b3339 100644 --- a/exercises/practice/pangram/pangram.py +++ b/exercises/practice/pangram/pangram.py @@ -1,2 +1,22 @@ -def is_pangram(sentence): - pass +""" +Module to find whether a string is a pangram. +""" + +import string + +def is_pangram(sentence: str) -> bool: + """ + Function that determines if an input string is a pangram. + A pangram is a sentence that contains all the letters of the alphabet. + + Args: + sentence (str): The sentence to be analyzed. + + Returns: + bool: True if the sentence is a pangram. + """ + if sentence.strip() == '': + return False + input_set = set(sentence.strip().lower()) + alphabet = set(string.ascii_lowercase) + return alphabet.issubset(input_set) From be3030d3f01afe8f95f40e6c48523ac70fd2be97 Mon Sep 17 00:00:00 2001 From: phets Date: Thu, 2 Mar 2023 12:02:22 +0100 Subject: [PATCH 39/55] Completed isogram exercise. --- exercises/practice/isogram/isogram.py | 28 +++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/exercises/practice/isogram/isogram.py b/exercises/practice/isogram/isogram.py index 98b31c5b00e..559844e4996 100644 --- a/exercises/practice/isogram/isogram.py +++ b/exercises/practice/isogram/isogram.py @@ -1,2 +1,26 @@ -def is_isogram(string): - pass +""" +Module to find isogram strings. +An isogram (also known as a "non-pattern word") is a word +or phrase without a repeating letter, however spaces and +hyphens are allowed to appear multiple times. + +Examples of isograms: + +lumberjacks +background +downstream +six-year-old +""" + +def is_isogram(phrase: str) -> bool: + """ + Determines if the input phrase is an isogram. + + Args: + phrase (str): The string to be checked. + + Returns: + bool: True if the input is an isogram. + """ + phrase_only_alnum = ''.join(filter(str.isalnum, phrase.strip().lower())) + return len(set(phrase_only_alnum)) == len(phrase_only_alnum) From cb6dc88e955ae5fa50a56d9e31c9c62fefa0fbc0 Mon Sep 17 00:00:00 2001 From: phets Date: Thu, 2 Mar 2023 23:18:28 +0100 Subject: [PATCH 40/55] Completed hamming exercise. --- exercises/practice/hamming/hamming.py | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/exercises/practice/hamming/hamming.py b/exercises/practice/hamming/hamming.py index 056fec41a1c..8fe5e65cd0f 100644 --- a/exercises/practice/hamming/hamming.py +++ b/exercises/practice/hamming/hamming.py @@ -1,2 +1,21 @@ -def distance(strand_a, strand_b): - pass +""" +Module to calculate the "Hamming distance" between two strands of DNA. +""" + +def distance(strand_a: str, strand_b: str) -> int: + """Calculates the Hamming distance between two strands of DNA. + + Args: + strand_a (str): String representing the first strand in the format "CGATATCA" + strand_b (str): String representing the second strand in the format "CGATATCA" + + Raises: + ValueError: Exception in case the two strands are not of equal length + + Returns: + int: The Hamming distance between the two strands. + """ + if len(strand_a) != len(strand_b): + raise ValueError("Strands must be of equal length.") + return sum( base_a != base_b for base_a, base_b in zip(strand_a, strand_b)) + From 86c3b7a8cb92750e04ae43c61cbbaea5922d7f2e Mon Sep 17 00:00:00 2001 From: phets Date: Thu, 2 Mar 2023 23:43:35 +0100 Subject: [PATCH 41/55] Completed rna-transcription exercise. --- .../rna-transcription/rna_transcription.py | 28 +++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/exercises/practice/rna-transcription/rna_transcription.py b/exercises/practice/rna-transcription/rna_transcription.py index bb7be7795da..f19b2169292 100644 --- a/exercises/practice/rna-transcription/rna_transcription.py +++ b/exercises/practice/rna-transcription/rna_transcription.py @@ -1,2 +1,26 @@ -def to_rna(dna_strand): - pass +""" +Module to translate strands of DNA to RNA. +""" + +# Dictionary holding the nucleotide complements. +translation = { + "G": "C", + "C": "G", + "T": "A", + "A": "U" +} + + +def to_rna(dna_strand: str) -> str: + """ + Translates a strand of DNA into the complementary strand of RNA. + + Args: + dna_strand (str): String representing a DNA strand. e.g. "CGAT" + + Returns: + str: String representing the RNA strand. e.g. "GCUA" + """ + return ''.join(translation[base] for base in dna_strand) + + From c0604f9b8606e0881ca14f33eea5015ac895b64f Mon Sep 17 00:00:00 2001 From: phets Date: Fri, 3 Mar 2023 00:02:24 +0100 Subject: [PATCH 42/55] Completed etl exercise. --- exercises/practice/etl/etl.py | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/exercises/practice/etl/etl.py b/exercises/practice/etl/etl.py index 993dca2e7ff..0843994c805 100644 --- a/exercises/practice/etl/etl.py +++ b/exercises/practice/etl/etl.py @@ -1,2 +1,22 @@ -def transform(legacy_data): - pass +""" +Module to solve the etl exercise from exercism.org. +""" + +def transform(legacy_data: dict[int, list[str]]) -> dict[str, int]: + """ + Transform the scrabble scoring data from the legacy format + to the new format. + + Args: + legacy_data (dict[int, list[str]]): Scrabble scoring data + in the form {1: ["A", "E"], 2: ["D", "G"]} + + Returns: + dict[str, int]: Scrabble scoring data in the new form + {"a": 1, "d": 2, "e": 1, "g": 2} + """ + return { + letter.lower(): score + for score in legacy_data + for letter in legacy_data[score] + } From 910f08ec5cbdcb3d3517ba7a0d1a40a3704ca887 Mon Sep 17 00:00:00 2001 From: phets Date: Fri, 3 Mar 2023 00:13:54 +0100 Subject: [PATCH 43/55] Completed darts exercise. --- exercises/practice/darts/darts.py | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/exercises/practice/darts/darts.py b/exercises/practice/darts/darts.py index 4ba1b3b935f..c60e4c42d30 100644 --- a/exercises/practice/darts/darts.py +++ b/exercises/practice/darts/darts.py @@ -1,2 +1,29 @@ -def score(x, y): - pass +""" +Module to calculate dart score based on distance from centre. +""" + +# math to use the math.dist function +import math + +def score(x: float, y: float) -> int: + """ + Calculates the score of a dart based on the coordinates + where it hits. + + Args: + x (float): x-coordinate of dart. + y (float): y-coordinate of dart. + + Returns: + int: The dart's score. + """ + distance = math.dist([0,0], [x,y]) + + if distance > 10: + return 0 + if 10 >= distance > 5: + return 1 + if 5 >= distance > 1: + return 5 + if 1 >= distance: + return 10 From 3bd3405cd8e667b2dfdfb4e59357bdfb92c8fa0e Mon Sep 17 00:00:00 2001 From: phets Date: Fri, 3 Mar 2023 10:15:53 +0100 Subject: [PATCH 44/55] Completed sum-of-multiples exercise. --- .../sum-of-multiples/sum_of_multiples.py | 40 ++++++++++++++++++- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/exercises/practice/sum-of-multiples/sum_of_multiples.py b/exercises/practice/sum-of-multiples/sum_of_multiples.py index 39758440741..def6969295c 100644 --- a/exercises/practice/sum-of-multiples/sum_of_multiples.py +++ b/exercises/practice/sum-of-multiples/sum_of_multiples.py @@ -1,2 +1,38 @@ -def sum_of_multiples(limit, multiples): - pass +""" +Module to compute the sum of all the unique multiples of particular +numbers up to but not including that number. +""" + +def sum_of_multiples(limit: int, multiples: list[int]) -> int: + """ + Calculates the sum of all the unique multiples of the numbers + in multiples up to but not including limit. + + 1. The function uses a set comprehension to generate a set of + all the multiples of the given numbers. + 2. The comprehension iterates over each number "base" in the multiples + argument, checking that base is not zero (the if base condition). + If base is zero, it would lead to an infinite loop and an error. + 3. For each valid base, the comprehension generates a range object using + the range() function. + The range object starts at base and goes up to, but not including, + the limit argument, with a step of base. This generates all the + multiples of base up to the limit. + 4. The set comprehension gathers all the multiples of the numbers into a + set. The use of set ensures that each multiple is unique, even if it is + a multiple of multiple numbers. + 5. Finally, the function returns the sum of all the multiples in the set + using the built-in sum() function. + + Args: + limit (int): The limit below which multiples are summed. + multiples (list[int]): The numbers whose multiples should be summed. + + Returns: + int: The sum of the unique multiples. + """ + return sum({ + n + for base in multiples if base + for n in range(base, limit, base) + }) From ce0c74c0babb49f66f61354b64b8a5048e3aab2f Mon Sep 17 00:00:00 2001 From: phets Date: Fri, 3 Mar 2023 11:23:20 +0100 Subject: [PATCH 45/55] Completed anagram exercise. --- exercises/practice/anagram/anagram.py | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/exercises/practice/anagram/anagram.py b/exercises/practice/anagram/anagram.py index 9812eb18f25..f2603cd3044 100644 --- a/exercises/practice/anagram/anagram.py +++ b/exercises/practice/anagram/anagram.py @@ -1,2 +1,22 @@ -def find_anagrams(word, candidates): - pass +""" +Module to solve the anagram exercise from exercism.org. +""" + +def find_anagrams(word: str, candidates: list[str]) -> list[str]: + """ + Selects the entries in the candidates list that are anagrams + of word. + + Args: + word (str): The word for which the candidates must be an anagram. + candidates (list[str]): The list of anagram candidates. + + Returns: + list[str]: The anagrams of word included in the candidates list. + """ + return [ + anagram + for anagram in candidates + if anagram.lower().strip() != word.lower().strip() + and sorted(anagram.lower().strip()) == sorted(word.lower().strip()) + ] From 65bd5447da6a35ef028f01e87c62cbc346a393f1 Mon Sep 17 00:00:00 2001 From: phets Date: Sun, 5 Mar 2023 14:44:55 +0100 Subject: [PATCH 46/55] Completed pig-latin exercise. --- exercises/practice/pig-latin/pig_latin.py | 13 +++++++++++-- exercises/practice/pig-latin/pig_latin_test.py | 6 ++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/exercises/practice/pig-latin/pig_latin.py b/exercises/practice/pig-latin/pig_latin.py index dda3f072a58..cd7c13e3c00 100644 --- a/exercises/practice/pig-latin/pig_latin.py +++ b/exercises/practice/pig-latin/pig_latin.py @@ -1,2 +1,11 @@ -def translate(text): - pass +import re + +RE = re.compile('^(x+(?!r)|y+(?!t)|[^aeiouqxy]*(?:qu?)?)(.+)$') + +def translate(text: str) -> str: + return ' '.join(map(translate_word, text.split(' '))) + + +def translate_word(word: str) -> str: + return RE.sub(lambda m: '{1}{0}ay'.format(*m.groups()), word) + diff --git a/exercises/practice/pig-latin/pig_latin_test.py b/exercises/practice/pig-latin/pig_latin_test.py index cf666ddf3ad..fc006ab54f1 100644 --- a/exercises/practice/pig-latin/pig_latin_test.py +++ b/exercises/practice/pig-latin/pig_latin_test.py @@ -71,6 +71,12 @@ def test_y_is_treated_like_a_vowel_at_the_end_of_a_consonant_cluster(self): def test_y_as_second_letter_in_two_letter_word(self): self.assertEqual(translate("my"), "ymay") + def test_multiple_x_at_the_beginning_of_a_word(self): + self.assertEqual(translate("xxyst"), "ystxxay") + + def test_multiple_y_at_the_beginning_of_a_word(self): + self.assertEqual(translate("yyyellow"), "ellowyyyay") + def test_a_whole_phrase(self): self.assertEqual(translate("quick fast run"), "ickquay astfay unray") From a28f288a4e72c291066a7ff354a37f9742376120 Mon Sep 17 00:00:00 2001 From: phets Date: Sun, 5 Mar 2023 18:33:48 +0100 Subject: [PATCH 47/55] Completed perfect-numbers exercise. --- .../perfect-numbers/perfect_numbers.py | 18 ++++++++++++++++-- exercises/practice/pig-latin/pig_latin_test.py | 2 +- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/exercises/practice/perfect-numbers/perfect_numbers.py b/exercises/practice/perfect-numbers/perfect_numbers.py index eb093dd6c98..1311f6e1fe0 100644 --- a/exercises/practice/perfect-numbers/perfect_numbers.py +++ b/exercises/practice/perfect-numbers/perfect_numbers.py @@ -1,7 +1,21 @@ -def classify(number): +def classify(number: int) -> str: """ A perfect number equals the sum of its positive divisors. :param number: int a positive integer :return: str the classification of the input integer """ - pass + if number < 1: + # if a number to be classified is less than 1. + raise ValueError("Classification is only possible for positive integers.") + if sum(find_factors(number)) < number: + return "deficient" + if sum(find_factors(number)) == number: + return "perfect" + if sum(find_factors(number)) > number: + return "abundant" + +def find_factors(number: int) -> list[int]: + if number < 1: + # if a number to be classified is less than 1. + raise ValueError("Classification is only possible for positive integers.") + return [factor for factor in range(1, number//2 + 1) if number % factor == 0 ] diff --git a/exercises/practice/pig-latin/pig_latin_test.py b/exercises/practice/pig-latin/pig_latin_test.py index fc006ab54f1..f95120f553c 100644 --- a/exercises/practice/pig-latin/pig_latin_test.py +++ b/exercises/practice/pig-latin/pig_latin_test.py @@ -73,7 +73,7 @@ def test_y_as_second_letter_in_two_letter_word(self): def test_multiple_x_at_the_beginning_of_a_word(self): self.assertEqual(translate("xxyst"), "ystxxay") - + def test_multiple_y_at_the_beginning_of_a_word(self): self.assertEqual(translate("yyyellow"), "ellowyyyay") From c7e181f9fcff3e1571633b4e285325562c543cbf Mon Sep 17 00:00:00 2001 From: phets Date: Sun, 5 Mar 2023 19:19:01 +0100 Subject: [PATCH 48/55] Completed flatten-array exercise. --- .../practice/flatten-array/flatten_array.py | 27 +++++++++++++++++-- .../flatten-array/flatten_array_test.py | 5 ++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/exercises/practice/flatten-array/flatten_array.py b/exercises/practice/flatten-array/flatten_array.py index db774c8e255..5f10be8e214 100644 --- a/exercises/practice/flatten-array/flatten_array.py +++ b/exercises/practice/flatten-array/flatten_array.py @@ -1,2 +1,25 @@ -def flatten(iterable): - pass +""" +Module to flatten an arbitrarily nested list and remove None values. +""" + +def flatten(iterable: list) -> list: + """ + Recursive function to flatten a nested list. + The nesting can be arbitrarily deep. + When the list is flat None values are removed. + + Args: + iterable (list): The list to flatten. + + Returns: + list: The flattened list. + """ + out = [] + for item in iterable: + if isinstance(item, list): + out.extend(flatten(item)) + elif item is not None: + out.append(item) + return out + + diff --git a/exercises/practice/flatten-array/flatten_array_test.py b/exercises/practice/flatten-array/flatten_array_test.py index 1552f0edb6a..54730123a7c 100644 --- a/exercises/practice/flatten-array/flatten_array_test.py +++ b/exercises/practice/flatten-array/flatten_array_test.py @@ -61,6 +61,11 @@ def test_6_level_nest_list_with_null_values(self): inputs = [0, 2, [[2, 3], 8, [[100]], None, [[None]]], -2] expected = [0, 2, 2, 3, 8, 100, -2] self.assertEqual(flatten(inputs), expected) + + def test_6_level_nest_list_with_null_values_and_string(self): + inputs = [0, 2, [[2, 3], 8, [[100]], None, [["gino"]]], -2] + expected = [0, 2, 2, 3, 8, 100, "gino", -2] + self.assertEqual(flatten(inputs), expected) def test_all_values_in_nested_list_are_null(self): inputs = [None, [[[None]]], None, None, [[None, None], None], None] From ededa142927af84e41b77dd7dea23c369e8124a4 Mon Sep 17 00:00:00 2001 From: phets Date: Sun, 5 Mar 2023 19:31:55 +0100 Subject: [PATCH 49/55] Completed gigasecond exercise. --- exercises/practice/gigasecond/gigasecond.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/exercises/practice/gigasecond/gigasecond.py b/exercises/practice/gigasecond/gigasecond.py index 95fcc6fbceb..570ec8ef075 100644 --- a/exercises/practice/gigasecond/gigasecond.py +++ b/exercises/practice/gigasecond/gigasecond.py @@ -1,2 +1,4 @@ -def add(moment): - pass +from datetime import datetime, timedelta + +def add(moment: datetime) -> datetime: + return moment + timedelta(seconds=1000000000) From 44d31d319005dc4dcf3cab7200c186bda4e2dca9 Mon Sep 17 00:00:00 2001 From: phets Date: Sun, 5 Mar 2023 19:32:39 +0100 Subject: [PATCH 50/55] Exponent notation for gigasecond exercise. --- exercises/practice/gigasecond/gigasecond.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exercises/practice/gigasecond/gigasecond.py b/exercises/practice/gigasecond/gigasecond.py index 570ec8ef075..c270a43e299 100644 --- a/exercises/practice/gigasecond/gigasecond.py +++ b/exercises/practice/gigasecond/gigasecond.py @@ -1,4 +1,4 @@ from datetime import datetime, timedelta def add(moment: datetime) -> datetime: - return moment + timedelta(seconds=1000000000) + return moment + timedelta(seconds=10**9) From cffb571c3416f2c89d5ac9b5e3f9cacf2508f6fa Mon Sep 17 00:00:00 2001 From: phets Date: Tue, 7 Mar 2023 16:07:22 +0100 Subject: [PATCH 51/55] Completed isbn-verifier exercise. --- .../practice/isbn-verifier/isbn_verifier.py | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/exercises/practice/isbn-verifier/isbn_verifier.py b/exercises/practice/isbn-verifier/isbn_verifier.py index 58c138f4d17..e33b0ca078c 100644 --- a/exercises/practice/isbn-verifier/isbn_verifier.py +++ b/exercises/practice/isbn-verifier/isbn_verifier.py @@ -1,2 +1,18 @@ -def is_valid(isbn): - pass +def is_valid(isbn: str) -> bool: + # Convert into a list of characters removing everything except digits and X + isbn_as_list = [c for c in isbn if c.isalnum()] + # Not a valid ISBN is there are non-digits in all but the last position + # or there are not exactly 10 characters. + if any([not c.isdigit() for c in isbn_as_list[:-1]]) or len(isbn_as_list) != 10: + return False + # Not a valid ISBN if the last character is anything but a digit or an 'X' + if not isbn_as_list[-1].isdigit() and not isbn_as_list[-1].upper() == 'X': + return False + # If the last character is an 'X' convert it into a 10 + if isbn_as_list[-1] == 'X': + isbn_as_list[-1] = 10 + # Turn the list of characters into a list of ints + numeric_isbn_as_list = [int(c) for c in isbn_as_list] + + control = sum(a * b for a, b in zip(numeric_isbn_as_list, range(10,0,-1))) + return control % 11 == 0 From 001bce06fdb8e94caa7117676e562601b57cb1f7 Mon Sep 17 00:00:00 2001 From: phets Date: Tue, 7 Mar 2023 16:32:12 +0100 Subject: [PATCH 52/55] Completed space-age exercise. --- exercises/practice/space-age/space_age.py | 43 +++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/exercises/practice/space-age/space_age.py b/exercises/practice/space-age/space_age.py index bdcdb682870..1a466d5c8b8 100644 --- a/exercises/practice/space-age/space_age.py +++ b/exercises/practice/space-age/space_age.py @@ -1,3 +1,42 @@ +""" +Module to solve the space-age exercise from exercism.org. +https://exercism.org/tracks/python/exercises/space-age +""" class SpaceAge: - def __init__(self, seconds): - pass + """ + The exercise requires creation of the SpaceAge class. + """ + # A list of tuples containing the planets and their + # orbital period in seconds. + ORBIT_SECONDS = [ + (planet, orbital_period * 31557600) + for planet, orbital_period in ( + ('mercury', 0.2408467), + ('venus', 0.61519726), + ('earth', 1.0), + ('mars', 1.8808158), + ('jupiter', 11.862615), + ('saturn', 29.447498), + ('uranus', 84.016846), + ('neptune', 164.79132) + ) + ] + + def __init__(self, seconds: int): + """ + The constructor initializes the age_in_seconds instance variable + and then creates a function called on_ for each + planet in the ORBIT_SECONDS list. + + Args: + seconds (int): The number of seconds to transfor in planet years. + """ + self.age_in_seconds = seconds + + for planet, orbital_period in self.ORBIT_SECONDS: + # setattr adds "attributes", in this case functions, to the class. + setattr( + self, + f'on_{planet}', + lambda orbital_period = orbital_period: round(self.age_in_seconds / orbital_period, 2 ) + ) From bb64b5cdf12a0c9edaa31e4983d9347539a99d5f Mon Sep 17 00:00:00 2001 From: phets Date: Wed, 8 Mar 2023 15:29:07 +0100 Subject: [PATCH 53/55] Completed secret-handshake exercise. --- .../secret-handshake/secret_handshake.py | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/exercises/practice/secret-handshake/secret_handshake.py b/exercises/practice/secret-handshake/secret_handshake.py index e3691e18400..2f630b24013 100644 --- a/exercises/practice/secret-handshake/secret_handshake.py +++ b/exercises/practice/secret-handshake/secret_handshake.py @@ -1,2 +1,24 @@ -def commands(binary_str): - pass +""" +Module to solve the secret handshake exercise from exercism.org. +https://exercism.org/tracks/python/exercises/secret-handshake +""" +events = ['wink', 'double blink', 'close your eyes', 'jump'] + +def commands(binary_str: str) -> list[str]: + """ + Calculates the event sequence for the secret handshake by: + 1. Converting the string into an integer. + 2. Using binary logic (&) and shift (<<) operators to determine the events. + + Args: + binary_str (str): A string representing a binary number. + The string must have 5 digits e.g. "10010". + + Returns: + list[str]: The sequence of events for the secret handshake. + """ + int_code = int(binary_str.strip(), 2) + out = [j for i,j in enumerate(events) if int_code & 1 << i] + if int_code & 1 << 4: + out.reverse() + return out From 2af1229d7fef91dd3e7e4cb07e0b83cec3518a16 Mon Sep 17 00:00:00 2001 From: phets Date: Thu, 16 Mar 2023 04:23:25 +0100 Subject: [PATCH 54/55] Completed wordy exercise. --- exercises/practice/wordy/wordy.py | 41 +++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/exercises/practice/wordy/wordy.py b/exercises/practice/wordy/wordy.py index 6fdad1f4190..fea63a8ced2 100644 --- a/exercises/practice/wordy/wordy.py +++ b/exercises/practice/wordy/wordy.py @@ -1,2 +1,39 @@ -def answer(question): - pass +import re + + +OPS = { + "plus": "__add__", "minus": "__sub__", + "multiplied": "__mul__", "divided": "__truediv__" +} + + +def answer(question: str) -> int: + + question = question.removeprefix("What is").removesuffix("?").strip() + + if not question: + raise ValueError("syntax error") + + if question.isdigit(): + return int(question) + + ret = re.split(' by | ', question) + + # A valid operation must have at least three elements, + # left operand, operator, right operand. + if len(ret) == 2: + if ret[1] not in OPS.keys(): raise ValueError("unknown operation") + raise ValueError("syntax error") + + while len(ret) > 1: + try: + x, op, y, *tail = ret + if op not in OPS.keys() and not op.isnumeric(): + raise ValueError("unknown operation") + op = OPS[op] + # put result as first element and append what remains + ret = [int(x).__getattribute__(op)(int(y)), *tail] + except Exception as e: + if repr(e) == "ValueError('unknown operation')": raise e + else: raise ValueError("syntax error") + return ret[0] From cae60e6df7296892aa3e507f9306533e632a5a16 Mon Sep 17 00:00:00 2001 From: phets Date: Tue, 21 Mar 2023 16:46:28 +0100 Subject: [PATCH 55/55] Completed house exercise. --- exercises/practice/house/house.py | 41 ++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/exercises/practice/house/house.py b/exercises/practice/house/house.py index ed87e69c4e9..a4efc66f837 100644 --- a/exercises/practice/house/house.py +++ b/exercises/practice/house/house.py @@ -1,2 +1,41 @@ def recite(start_verse, end_verse): - pass + + predicates = [ + 'the house that Jack built', + 'the malt', + 'the rat', + 'the cat', + 'the dog', + 'the cow with the crumpled horn', + 'the maiden all forlorn', + 'the man all tattered and torn', + 'the priest all shaven and shorn', + 'the rooster that crowed in the morn', + 'the farmer sowing his corn', + 'the horse and the hound and the horn' + ] + + verbs = [ + 'lay in', + 'ate', + 'killed', + 'worried', + 'tossed', + 'milked', + 'kissed', + 'married', + 'woke', + 'kept', + 'belonged to' + ] + + output = [] + + for verse in range(start_verse - 1,end_verse): + phrase = f'This is {predicates[verse]}' + for inside_verse in range(verse,0,-1): + phrase += f' that {verbs[inside_verse-1]} {predicates[inside_verse-1]}' + phrase += '.' + output.append(phrase) + + return output