Date: Sun, 4 Dec 2022 23:26:45 -0800
Subject: [PATCH 173/826] Update example.py
Deleted unnecessary blank line 50.
---
exercises/practice/sgf-parsing/.meta/example.py | 1 -
1 file changed, 1 deletion(-)
diff --git a/exercises/practice/sgf-parsing/.meta/example.py b/exercises/practice/sgf-parsing/.meta/example.py
index f32b20b853..7711625518 100644
--- a/exercises/practice/sgf-parsing/.meta/example.py
+++ b/exercises/practice/sgf-parsing/.meta/example.py
@@ -47,7 +47,6 @@ def parse(input_string):
root = None
current = None
stack = list(input_string)
-
if input_string == '()':
raise ValueError('tree with no nodes')
From 384e8e219089188c4329776448cc3ab153deb402 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Tue, 29 Nov 2022 08:05:14 -0600
Subject: [PATCH 174/826] Create config.json
---
.../practice/pig-latin/.approaches/config.json | 15 +++++++++++++++
1 file changed, 15 insertions(+)
create mode 100644 exercises/practice/pig-latin/.approaches/config.json
diff --git a/exercises/practice/pig-latin/.approaches/config.json b/exercises/practice/pig-latin/.approaches/config.json
new file mode 100644
index 0000000000..bd808f77fb
--- /dev/null
+++ b/exercises/practice/pig-latin/.approaches/config.json
@@ -0,0 +1,15 @@
+{
+ "introduction": {
+ "authors": ["bobahop"],
+ "contributors": []
+ },
+ "approaches": [
+ {
+ "uuid": "505e545c-d56c-45e3-8cc6-df3fdb54cc0c",
+ "slug": "sets-and-slices",
+ "title": "Sets and slices",
+ "blurb": "Use sets with slices for parsing.",
+ "authors": ["bobahop"]
+ }
+ ]
+}
From 75a3a577e127b185afc995085d99f8bf0f512fe1 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Tue, 29 Nov 2022 08:35:54 -0600
Subject: [PATCH 175/826] Create introduction.md
---
.../pig-latin/.approaches/introduction.md | 42 +++++++++++++++++++
1 file changed, 42 insertions(+)
create mode 100644 exercises/practice/pig-latin/.approaches/introduction.md
diff --git a/exercises/practice/pig-latin/.approaches/introduction.md b/exercises/practice/pig-latin/.approaches/introduction.md
new file mode 100644
index 0000000000..9965022d55
--- /dev/null
+++ b/exercises/practice/pig-latin/.approaches/introduction.md
@@ -0,0 +1,42 @@
+# Introduction
+
+There are various ways to solve Pig Latin.
+One way is to use [regular expressions][regex] (also known as [regex][regex-ops]) for processing the input.
+Solutions using regex can be very succinct, but require familiarity with regex patterns, which are like another language.
+Another way is to use a series of conditional statements to test which of several rules the input matches.
+Another approach is to use [set][set]s for look-up and then [slice][slicing] the input to return the correct value.
+
+## General guidance
+
+At the time of writing only four rules need to be handled, but if they have similar output, they don't need to be handled completely separately.
+
+```python
+VOWELS = {"a", "e", "i", "o", "u"}
+VOWELS_Y = {"a", "e", "i", "o", "u", "y"}
+SPECIALS = {"xr", "yt"}
+
+
+def translate(text):
+ piggyfied = []
+
+ for word in text.split():
+ if word[0] in VOWELS or word[0:2] in SPECIALS:
+ piggyfied.append(word + "ay")
+ continue
+
+ for pos in range(1, len(word)):
+ if word[pos] in VOWELS_Y:
+ pos += 1 if word[pos] == 'u' and word[pos - 1] == "q" else 0
+ piggyfied.append(word[pos:] + word[:pos] + "ay")
+ break
+ return " ".join(piggyfied)
+
+```
+
+For more information, check the [sets and slices approach][approach-sets-and-slices].
+
+[regex]: https://docs.python.org/3/howto/regex.html#regex-howto
+[regex-ops]: https://docs.python.org/3/library/re.html?regex
+[set]: https://docs.python.org/3/library/stdtypes.html?#set
+[slicing]: https://www.learnbyexample.org/python-string-slicing/
+[approach-sets-and-slices]: https://exercism.org/tracks/python/exercises/pig-latin/approaches/sets-and-slices
From 75b17840c35aa26e80d3656927435372625b6e66 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Tue, 29 Nov 2022 08:40:23 -0600
Subject: [PATCH 176/826] Update introduction.md
---
exercises/practice/pig-latin/.approaches/introduction.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/exercises/practice/pig-latin/.approaches/introduction.md b/exercises/practice/pig-latin/.approaches/introduction.md
index 9965022d55..09cd0ab5d8 100644
--- a/exercises/practice/pig-latin/.approaches/introduction.md
+++ b/exercises/practice/pig-latin/.approaches/introduction.md
@@ -29,6 +29,7 @@ def translate(text):
pos += 1 if word[pos] == 'u' and word[pos - 1] == "q" else 0
piggyfied.append(word[pos:] + word[:pos] + "ay")
break
+
return " ".join(piggyfied)
```
From d66103336cac5ea5e0525b769a15dabd6cde5664 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Tue, 29 Nov 2022 08:40:46 -0600
Subject: [PATCH 177/826] Create snippet.txt
---
.../pig-latin/.approaches/sets-and-slices/snippet.txt | 7 +++++++
1 file changed, 7 insertions(+)
create mode 100644 exercises/practice/pig-latin/.approaches/sets-and-slices/snippet.txt
diff --git a/exercises/practice/pig-latin/.approaches/sets-and-slices/snippet.txt b/exercises/practice/pig-latin/.approaches/sets-and-slices/snippet.txt
new file mode 100644
index 0000000000..e23cee9c2e
--- /dev/null
+++ b/exercises/practice/pig-latin/.approaches/sets-and-slices/snippet.txt
@@ -0,0 +1,7 @@
+for pos in range(1, len(word)):
+ if word[pos] in VOWELS_Y:
+ pos += 1 if word[pos] == 'u' and word[pos - 1] == "q" else 0
+ piggyfied.append(word[pos:] + word[:pos] + "ay")
+ break
+
+return " ".join(piggyfied)
From c8525eef7b297d56882df3abeac5159c728e57b5 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Tue, 29 Nov 2022 08:43:10 -0600
Subject: [PATCH 178/826] Update introduction.md
---
exercises/practice/pig-latin/.approaches/introduction.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/exercises/practice/pig-latin/.approaches/introduction.md b/exercises/practice/pig-latin/.approaches/introduction.md
index 09cd0ab5d8..0f8807acce 100644
--- a/exercises/practice/pig-latin/.approaches/introduction.md
+++ b/exercises/practice/pig-latin/.approaches/introduction.md
@@ -10,6 +10,8 @@ Another approach is to use [set][set]s for look-up and then [slice][slicing] the
At the time of writing only four rules need to be handled, but if they have similar output, they don't need to be handled completely separately.
+## Approach: Sets and slices
+
```python
VOWELS = {"a", "e", "i", "o", "u"}
VOWELS_Y = {"a", "e", "i", "o", "u", "y"}
From 219078c9596e931584f5b4e8bae760efed93ec9b Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Tue, 29 Nov 2022 09:33:22 -0600
Subject: [PATCH 179/826] Create content.md
Saving work...
---
.../.approaches/sets-and-slices/content.md | 69 +++++++++++++++++++
1 file changed, 69 insertions(+)
create mode 100644 exercises/practice/pig-latin/.approaches/sets-and-slices/content.md
diff --git a/exercises/practice/pig-latin/.approaches/sets-and-slices/content.md b/exercises/practice/pig-latin/.approaches/sets-and-slices/content.md
new file mode 100644
index 0000000000..88454415cb
--- /dev/null
+++ b/exercises/practice/pig-latin/.approaches/sets-and-slices/content.md
@@ -0,0 +1,69 @@
+# Sets and slices
+
+```python
+VOWELS = {"a", "e", "i", "o", "u"}
+VOWELS_Y = {"a", "e", "i", "o", "u", "y"}
+SPECIALS = {"xr", "yt"}
+
+
+def translate(text):
+ piggyfied = []
+
+ for word in text.split():
+ if word[0] in VOWELS or word[0:2] in SPECIALS:
+ piggyfied.append(word + "ay")
+ continue
+
+ for pos in range(1, len(word)):
+ if word[pos] in VOWELS_Y:
+ pos += 1 if word[pos] == 'u' and word[pos - 1] == "q" else 0
+ piggyfied.append(word[pos:] + word[:pos] + "ay")
+ break
+ return " ".join(piggyfied)
+
+```
+
+This approach begins by defining [sets][set] for looking up matching characters from the input.
+Python doesn't _enforce_ having real constant values,
+but the sets are defined with all uppercase letters, which is the naming convention for a Python [constant][const].
+It indicates that the value is not intended to be changed.
+
+The `translate` function begins by defining the list which will hold the parsed value(s).
+
+The input is [`split()`][split] into a list of its words, which is then iterated.
+
+[String indexing][string-indexing] is used to check if the first letter of the word is in the set of vowels.
+If so, the logical [or][logical-or] operator "short-circuits" and the word plus "ay" is appended to the list.
+If the first letter is not a vowel, then a [slice][slicing] of the first two letters is used to check if they match any of the special beginning characters.
+If the letters match, the word plus "ay" will be appended to the list.
+
+If the beginning of the word has not yet matched a rule, then that leaves [ranging][ranging] its characters from position 1 until the [`len()`][len] of the word.
+
+```exercism/note
+When a [range](https://docs.python.org/3/library/stdtypes.html?#range) is provided two arguments,
+it generates values from the `start` argument up to but not including the `stop` argument.
+This can be referred to as start inclusive, stop exclusive.
+```
+ Each character is iterated until finding a vowel (at this point, the letter `y` is now considered a vowel.)
+If the vowel is `u`, the previous consonant is checked to be `q`.
+If so, the position is advanced to be one position after the found `u`.
+
+A slice is then taken from the position until the end of the word,
+plus a slice taken from the beginning of the word and stopped before the position, plus `ay`.
+That concatenated string is appended to the list.
+
+Once all of the words in the input have been iterated,
+the [join][join] method is called on a space character to connect all of the words in the list back into a single string.
+Since the space is only used as a separator _between_ elements of the list, if the list has only one element,
+the space will not be added to the beginning or end of the output string.
+
+[set]: https://docs.python.org/3/library/stdtypes.html?#set
+[const]: https://realpython.com/python-constants/
+[split]: https://docs.python.org/3/library/stdtypes.html?#str.split
+[string-indexing]: https://realpython.com/lessons/string-indexing/
+[logical-or]: https://realpython.com/python-or-operator/
+[ranging]: https://www.w3schools.com/python/gloss_python_for_range.asp
+[range]: https://docs.python.org/3/library/stdtypes.html?#range
+[len]: https://docs.python.org/3/library/functions.html?#len
+[slicing]: https://www.learnbyexample.org/python-string-slicing/
+[join]: https://docs.python.org/3/library/stdtypes.html?#str.join
From ad2c51f433e15b235b7fc52c5f588d6af7993294 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Tue, 29 Nov 2022 10:29:29 -0600
Subject: [PATCH 180/826] Update content.md
---
.../.approaches/sets-and-slices/content.md | 17 ++++++++++++-----
1 file changed, 12 insertions(+), 5 deletions(-)
diff --git a/exercises/practice/pig-latin/.approaches/sets-and-slices/content.md b/exercises/practice/pig-latin/.approaches/sets-and-slices/content.md
index 88454415cb..5ee7719700 100644
--- a/exercises/practice/pig-latin/.approaches/sets-and-slices/content.md
+++ b/exercises/practice/pig-latin/.approaches/sets-and-slices/content.md
@@ -36,21 +36,26 @@ The input is [`split()`][split] into a list of its words, which is then iterated
If so, the logical [or][logical-or] operator "short-circuits" and the word plus "ay" is appended to the list.
If the first letter is not a vowel, then a [slice][slicing] of the first two letters is used to check if they match any of the special beginning characters.
If the letters match, the word plus "ay" will be appended to the list.
+If the beginning of the word matches either condition, the loop [continue][continue]s to the next word.
-If the beginning of the word has not yet matched a rule, then that leaves [ranging][ranging] its characters from position 1 until the [`len()`][len] of the word.
+If the beginning of the word did not match either condition,
+then that leaves [ranging][ranging] its characters from position 1 until the [`len()`][len] of the word.
```exercism/note
When a [range](https://docs.python.org/3/library/stdtypes.html?#range) is provided two arguments,
-it generates values from the `start` argument up to but not including the `stop` argument.
-This can be referred to as start inclusive, stop exclusive.
+it generates values from the `start` argument up to _but not including_ the `stop` argument.
+This behavior can be referred to as start inclusive, stop exclusive.
```
- Each character is iterated until finding a vowel (at this point, the letter `y` is now considered a vowel.)
+
+The inner loop iterating characters is nested within the outer loop that iterates the words.
+Each character is iterated until finding a vowel (at this point, the letter `y` is now considered a vowel.)
If the vowel is `u`, the previous consonant is checked to be `q`.
If so, the position is advanced to be one position after the found `u`.
A slice is then taken from the position until the end of the word,
plus a slice taken from the beginning of the word and stopped before the position, plus `ay`.
-That concatenated string is appended to the list.
+That concatenated string is appended to the list, [break][break] is called to exit the inner loop,
+and execution returns to the outer loop.
Once all of the words in the input have been iterated,
the [join][join] method is called on a space character to connect all of the words in the list back into a single string.
@@ -62,8 +67,10 @@ the space will not be added to the beginning or end of the output string.
[split]: https://docs.python.org/3/library/stdtypes.html?#str.split
[string-indexing]: https://realpython.com/lessons/string-indexing/
[logical-or]: https://realpython.com/python-or-operator/
+[continue]: https://docs.python.org/3/reference/simple_stmts.html#the-continue-statement
[ranging]: https://www.w3schools.com/python/gloss_python_for_range.asp
[range]: https://docs.python.org/3/library/stdtypes.html?#range
[len]: https://docs.python.org/3/library/functions.html?#len
[slicing]: https://www.learnbyexample.org/python-string-slicing/
+[break]: https://docs.python.org/3/reference/simple_stmts.html#the-break-statement
[join]: https://docs.python.org/3/library/stdtypes.html?#str.join
From 7a0e4346c7166f81bfd51422d09c8b39de06e6ce Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Tue, 29 Nov 2022 10:33:07 -0600
Subject: [PATCH 181/826] Update content.md
---
.../practice/pig-latin/.approaches/sets-and-slices/content.md | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/exercises/practice/pig-latin/.approaches/sets-and-slices/content.md b/exercises/practice/pig-latin/.approaches/sets-and-slices/content.md
index 5ee7719700..0f7ab8c28f 100644
--- a/exercises/practice/pig-latin/.approaches/sets-and-slices/content.md
+++ b/exercises/practice/pig-latin/.approaches/sets-and-slices/content.md
@@ -19,6 +19,7 @@ def translate(text):
pos += 1 if word[pos] == 'u' and word[pos - 1] == "q" else 0
piggyfied.append(word[pos:] + word[:pos] + "ay")
break
+
return " ".join(piggyfied)
```
@@ -53,7 +54,7 @@ If the vowel is `u`, the previous consonant is checked to be `q`.
If so, the position is advanced to be one position after the found `u`.
A slice is then taken from the position until the end of the word,
-plus a slice taken from the beginning of the word and stopped before the position, plus `ay`.
+plus a slice taken from the beginning of the word and stopped just before the position, plus `ay`.
That concatenated string is appended to the list, [break][break] is called to exit the inner loop,
and execution returns to the outer loop.
From 38b9e9ec679450f2b68fc9ea71a4c4d9cdff7c0e Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Sat, 3 Dec 2022 08:01:04 -0600
Subject: [PATCH 182/826] Update content.md
---
.../practice/pig-latin/.approaches/sets-and-slices/content.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/exercises/practice/pig-latin/.approaches/sets-and-slices/content.md b/exercises/practice/pig-latin/.approaches/sets-and-slices/content.md
index 0f7ab8c28f..54d5a6e809 100644
--- a/exercises/practice/pig-latin/.approaches/sets-and-slices/content.md
+++ b/exercises/practice/pig-latin/.approaches/sets-and-slices/content.md
@@ -29,7 +29,7 @@ Python doesn't _enforce_ having real constant values,
but the sets are defined with all uppercase letters, which is the naming convention for a Python [constant][const].
It indicates that the value is not intended to be changed.
-The `translate` function begins by defining the list which will hold the parsed value(s).
+The `translate()` function begins by defining the list which will hold the parsed value(s).
The input is [`split()`][split] into a list of its words, which is then iterated.
@@ -40,7 +40,7 @@ If the letters match, the word plus "ay" will be appended to the list.
If the beginning of the word matches either condition, the loop [continue][continue]s to the next word.
If the beginning of the word did not match either condition,
-then that leaves [ranging][ranging] its characters from position 1 until the [`len()`][len] of the word.
+that leaves [ranging][ranging] its characters from position 1 until the [`len()`][len] of the word.
```exercism/note
When a [range](https://docs.python.org/3/library/stdtypes.html?#range) is provided two arguments,
From bc9667613470ed9dee41123e178745234e215451 Mon Sep 17 00:00:00 2001
From: meatball <69751659+meatball133@users.noreply.github.com>
Date: Mon, 5 Dec 2022 18:38:25 +0100
Subject: [PATCH 183/826] Update track_exercises_overview.md
---
reference/track_exercises_overview.md | 437 +++++++++++++-------------
1 file changed, 224 insertions(+), 213 deletions(-)
diff --git a/reference/track_exercises_overview.md b/reference/track_exercises_overview.md
index 383e1e1ebb..8b8fe4f62e 100644
--- a/reference/track_exercises_overview.md
+++ b/reference/track_exercises_overview.md
@@ -5,180 +5,192 @@
-
## Implemented Practice Exercises
Practice Exercises with Difficulty, Solutions, and Mentor Notes
+| Exercise | Difficulty | Solutions | prereqs | Practices | Mentor Notes | Jinja? |
+| ------------------------------------------------------------------------------------------------------------------------------------------ | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | ------ |
+| [**Hello World**](https://github.com/exercism/python/blob/main/exercises/practice/hello-world/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/hello-world/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/hello-world/solutions?passed_head_tests=true) | NONE | `basics` | | ✅ |
+| [Acronym](https://github.com/exercism/python/blob/main/exercises/practice/acronym/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/acronym/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/acronym/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L337) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L330) | [acronym](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/acronym/) | ✅ |
+| [Affine Cipher](https://github.com/exercism/python/blob/main/exercises/practice/affine-cipher/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/affine-cipher/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/affine-cipher/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1174) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1173) | | ✅ |
+| [All Your Base](https://github.com/exercism/python/blob/main/exercises/practice/all-your-base/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/all-your-base/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/all-your-base/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1394) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1393) | | ✅ |
+| [Allergies](https://github.com/exercism/python/blob/main/exercises/practice/allergies/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/allergies/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/allergies/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L701) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L700) | [allergies](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/allergies/) | ✅ |
+| [Alphametics](https://github.com/exercism/python/blob/main/exercises/practice/alphametics/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/alphametics/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/alphametics/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1935) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1934) | | ✅ |
+| [Anagram](https://github.com/exercism/python/blob/main/exercises/practice/anagram/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/anagram/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/anagram/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L577) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L576) | | ✅ |
+| [Armstrong Numbers](https://github.com/exercism/python/blob/main/exercises/practice/armstrong-numbers/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/armstrong-numbers/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/armstrong-numbers/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L512) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L511) | | ✅ |
+| [Atbash Cipher](https://github.com/exercism/python/blob/main/exercises/practice/atbash-cipher/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/atbash-cipher/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/atbash-cipher/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1102) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1101) | | ✅ |
+| [Bank Account](https://github.com/exercism/python/blob/main/exercises/practice/bank-account/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/bank-account/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/bank-account/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2207) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2206) | | ❌ |
+| [Binary Search Tree](https://github.com/exercism/python/blob/main/exercises/practice/binary-search-tree/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/binary-search-tree/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/binary-search-tree/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1157) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1156) | | ✅ |
+| [Binary Search](https://github.com/exercism/python/blob/main/exercises/practice/binary-search/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/binary-search/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/binary-search/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1192) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1191) | [binary-search](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/binary-search/) | ✅ |
+| [Bob](https://github.com/exercism/python/blob/main/exercises/practice/bob/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/bob/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/bob/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L715) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L714) | [bob](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/bob/) | ✅ |
+| [Book Store](https://github.com/exercism/python/blob/main/exercises/practice/book-store/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/book-store/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/book-store/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L445) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L437) | | ✅ |
+| [Bottle Song](https://github.com/exercism/python/blob/main/exercises/practice/bottle-song/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/bottle-song/.meta/example.py)┋[most⭐](https://exercism.org/tracks/python/exercises/bottle-song/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/main/config.json/#LC1012) | [config.json](https://github.com/exercism/python/blob/main/config.json#LC1012) | | ✅ |
+| [Bowling](https://github.com/exercism/python/blob/main/exercises/practice/bowling/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/bowling/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/bowling/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1553) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1552) | | ✅ |
+| [Change](https://github.com/exercism/python/blob/main/exercises/practice/change/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/change/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/change/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1412) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1411) | | ✅ |
+| [Circular Buffer](https://github.com/exercism/python/blob/main/exercises/practice/circular-buffer/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/circular-buffer/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/circular-buffer/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1475) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1469) | | ✅ |
+| [Clock](https://github.com/exercism/python/blob/main/exercises/practice/clock/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/clock/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/clock/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L394) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L389) | [clock](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/clock/) | ✅ |
+| [Collatz Conjecture](https://github.com/exercism/python/blob/main/exercises/practice/collatz-conjecture/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/collatz-conjecture/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/collatz-conjecture/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L593) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L592) | | ✅ |
+| [Complex Numbers](https://github.com/exercism/python/blob/main/exercises/practice/complex-numbers/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/complex-numbers/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/complex-numbers/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L799) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L792) | | ✅ |
+| [Connect](https://github.com/exercism/python/blob/main/exercises/practice/connect/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/connect/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/connect/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L960) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L959) | | ✅ |
+| [Crypto Square](https://github.com/exercism/python/blob/main/exercises/practice/crypto-square/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/crypto-square/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/crypto-square/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1263) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1262) | | ✅ |
+| [Darts](https://github.com/exercism/python/blob/main/exercises/practice/darts/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/darts/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/darts/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2199) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2198) | | ✅ |
+| [Diamond](https://github.com/exercism/python/blob/main/exercises/practice/diamond/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/diamond/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/diamond/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1696) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1695) | | ✅ |
+| [Difference Of Squares](https://github.com/exercism/python/blob/main/exercises/practice/difference-of-squares/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/difference-of-squares/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/difference-of-squares/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L601) | NONE | | ✅ |
+| [Diffie Hellman](https://github.com/exercism/python/blob/main/exercises/practice/diffie-hellman/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/diffie-hellman/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/diffie-hellman/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1956) | NONE | | ✅ |
+| [Dnd Character](https://github.com/exercism/python/blob/main/exercises/practice/dnd-character/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/dnd-character/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/dnd-character/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2078) | NONE | | ✅ |
+| [Dominoes](https://github.com/exercism/python/blob/main/exercises/practice/dominoes/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/dominoes/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/dominoes/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1767) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1755) | | ✅ |
+| [Dot Dsl](https://github.com/exercism/python/blob/main/exercises/practice/dot-dsl/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/dot-dsl/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/dot-dsl/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1434) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1427) | | ❌ |
+| [Etl](https://github.com/exercism/python/blob/main/exercises/practice/etl/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/etl/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/etl/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1118) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1117) | | ✅ |
+| [Flatten Array](https://github.com/exercism/python/blob/main/exercises/practice/flatten-array/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/flatten-array/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/flatten-array/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1126) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1125) | | ✅ |
+| [Food Chain](https://github.com/exercism/python/blob/main/exercises/practice/food-chain/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/food-chain/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/food-chain/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1737) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1731) | | ✅ |
+| [Forth](https://github.com/exercism/python/blob/main/exercises/practice/forth/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/forth/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/forth/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1571) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1570) | | ✅ |
+| [Gigasecond](https://github.com/exercism/python/blob/main/exercises/practice/gigasecond/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/gigasecond/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/gigasecond/solutions?passed_head_tests=true) | | NONE | | ✅ |
+| [Go Counting](https://github.com/exercism/python/blob/main/exercises/practice/go-counting/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/go-counting/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/go-counting/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L868) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L867) | | ✅ |
+| [Grade School](https://github.com/exercism/python/blob/main/exercises/practice/grade-school/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/grade-school/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/grade-school/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L364) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L363) | [grade-school](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/grade-school/) | ✅ |
+| [Grains](https://github.com/exercism/python/blob/main/exercises/practice/grains/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/grains/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/grains/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L678) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L677) | | ✅ |
+| [Grep](https://github.com/exercism/python/blob/main/exercises/practice/grep/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/grep/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/grep/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1536) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1528) | | ✅ |
+| [Hamming](https://github.com/exercism/python/blob/main/exercises/practice/hamming/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/hamming/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/hamming/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L259) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L254) | [hamming](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/hamming/) | ✅ |
+| [Hangman](https://github.com/exercism/python/blob/main/exercises/practice/hangman/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/hangman/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/hangman/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2221) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2220) | | ❌ |
+| [High Scores](https://github.com/exercism/python/blob/main/exercises/practice/high-scores/.docs/instructions.md) | 🔹 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/high-scores/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/high-scores/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L226) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L225) | [high-scores](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/high-scores/) | ✅ |
+| [House](https://github.com/exercism/python/blob/main/exercises/practice/house/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/house/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/house/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1279) | NONE | | ✅ |
+| [Isbn Verifier](https://github.com/exercism/python/blob/main/exercises/practice/isbn-verifier/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/isbn-verifier/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/isbn-verifier/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L609) | NONE | | ✅ |
+| [Isogram](https://github.com/exercism/python/blob/main/exercises/practice/isogram/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/isogram/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/isogram/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L273) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L272) | [isogram](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/isogram/) | ✅ |
+| [Kindergarten Garden](https://github.com/exercism/python/blob/main/exercises/practice/kindergarten-garden/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/kindergarten-garden/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/kindergarten-garden/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L350) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L344) | [kindergarten-garden](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/kindergarten-garden/) | ✅ |
+| [Knapsack](https://github.com/exercism/python/blob/main/exercises/practice/knapsack/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/knapsack/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/knapsack/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1453) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1452) | | ✅ |
+| [Largest Series Product](https://github.com/exercism/python/blob/main/exercises/practice/largest-series-product/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/largest-series-product/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/largest-series-product/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L945) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L939) | | ✅ |
+| [Leap](https://github.com/exercism/python/blob/main/exercises/practice/leap/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/leap/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/leap/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2103) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2102) | [leap](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/leap/) | ✅ |
+| [Ledger](https://github.com/exercism/python/blob/main/exercises/practice/ledger/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/ledger/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/ledger/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1590) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1589) | | ❌ |
+| [Linked List](https://github.com/exercism/python/blob/main/exercises/practice/linked-list/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/linked-list/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/linked-list/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1379) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1371) | | ❌ |
+| [List Ops](https://github.com/exercism/python/blob/main/exercises/practice/list-ops/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/list-ops/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/list-ops/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1294) | NONE | | ✅ |
+| [Luhn](https://github.com/exercism/python/blob/main/exercises/practice/luhn/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/luhn/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/luhn/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L372) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L371) | [luhn](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/luhn/) | ✅ |
+| [Markdown](https://github.com/exercism/python/blob/main/exercises/practice/markdown/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/markdown/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/markdown/solutions?passed_head_tests=true) | | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L401) | [markdown](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/markdown/) | ✅ |
+| [Matching Brackets](https://github.com/exercism/python/blob/main/exercises/practice/matching-brackets/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/matching-brackets/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/matching-brackets/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L728) | NONE | [matching-brackets](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/matching-brackets/) | ✅ |
+| [Matrix](https://github.com/exercism/python/blob/main/exercises/practice/matrix/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/matrix/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/matrix/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1714) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1713) | [matrix](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/matrix/) | ✅ |
+| [Meetup](https://github.com/exercism/python/blob/main/exercises/practice/meetup/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/meetup/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/meetup/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L818) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L812) | | ✅ |
+| [Minesweeper](https://github.com/exercism/python/blob/main/exercises/practice/minesweeper/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/minesweeper/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/minesweeper/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L981) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L980) | | ✅ |
+| [Nth Prime](https://github.com/exercism/python/blob/main/exercises/practice/nth-prime/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/nth-prime/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/nth-prime/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1814) | NONE | | ✅ |
+| [Ocr Numbers](https://github.com/exercism/python/blob/main/exercises/practice/ocr-numbers/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/ocr-numbers/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/ocr-numbers/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L997) | NONE | | ✅ |
+| [Paasio](https://github.com/exercism/python/blob/main/exercises/practice/paasio/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/paasio/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/paasio/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1917) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1906) | | ❌ |
+| [Palindrome Products](https://github.com/exercism/python/blob/main/exercises/practice/palindrome-products/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/palindrome-products/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/palindrome-products/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L626) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L625) | | ✅ |
+| [Pangram](https://github.com/exercism/python/blob/main/exercises/practice/pangram/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/pangram/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/pangram/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L463) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L462) | | ✅ |
+| [Perfect Numbers](https://github.com/exercism/python/blob/main/exercises/practice/perfect-numbers/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/perfect-numbers/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/perfect-numbers/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L527) | NONE | | ✅ |
+| [Phone Number](https://github.com/exercism/python/blob/main/exercises/practice/phone-number/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/phone-number/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/phone-number/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L547) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L542) | | ✅ |
+| [Pig Latin](https://github.com/exercism/python/blob/main/exercises/practice/pig-latin/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/pig-latin/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/pig-latin/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1832) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1831) | | ✅ |
+| [Poker](https://github.com/exercism/python/blob/main/exercises/practice/poker/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/poker/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/poker/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1016) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1015) | | ✅ |
+| [Pov](https://github.com/exercism/python/blob/main/exercises/practice/pov/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/pov/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/pov/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1677) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1676) | | ✅ |
+| [Prime Factors](https://github.com/exercism/python/blob/main/exercises/practice/prime-factors/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/prime-factors/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/prime-factors/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L686) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L685) | | ✅ |
+| [Protein Translation](https://github.com/exercism/python/blob/main/exercises/practice/protein-translation/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/protein-translation/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/protein-translation/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L496) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L495) | | ✅ |
+| [Proverb](https://github.com/exercism/python/blob/main/exercises/practice/proverb/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/proverb/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/proverb/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/main/config.json#LC661) | [config.json](https://github.com/exercism/python/blob/main/config.json#L661) | | ✅ |
+| [Pythagorean Triplet](https://github.com/exercism/python/blob/main/exercises/practice/pythagorean-triplet/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/pythagorean-triplet/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/pythagorean-triplet/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L745) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L744) | | ✅ |
+| [Queen Attack](https://github.com/exercism/python/blob/main/exercises/practice/queen-attack/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/queen-attack/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/queen-attack/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1302) | NONE | | ✅ |
+| [Rail Fence Cipher](https://github.com/exercism/python/blob/main/exercises/practice/rail-fence-cipher/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/rail-fence-cipher/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/rail-fence-cipher/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1849) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1848) | | ✅ |
+| [Raindrops](https://github.com/exercism/python/blob/main/exercises/practice/raindrops/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/raindrops/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/raindrops/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L210) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L209) | [raindrops](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/raindrops/) | ✅ |
+| [Rational Numbers](https://github.com/exercism/python/blob/main/exercises/practice/rational-numbers/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/rational-numbers/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/rational-numbers/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2240) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2239) | | ✅ |
+| [React](https://github.com/exercism/python/blob/main/exercises/practice/react/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/react/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/react/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L890) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L884) | | ❌ |
+| [Rectangles](https://github.com/exercism/python/blob/main/exercises/practice/rectangles/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/rectangles/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/rectangles/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1032) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1031) | | ✅ |
+| [Resistor Color Trio](https://github.com/exercism/python/blob/main/exercises/practice/resistor-color-trio/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/resistor-color-trio/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/resistor-color-trio/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/main/config.json#LC631) | [config.json](https://github.com/exercism/python/blob/main/config.json#L631) | | ✅ |
+| [Resistor Color Duo](https://github.com/exercism/python/blob/main/exercises/practice/resistor-color-duo/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/resistor-color-duo/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/resistor-color-duo/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2119) | NONE | | ✅ |
+| [Resistor Color](https://github.com/exercism/python/blob/main/exercises/practice/resistor-color/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/resistor-color/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/resistor-color/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2111) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2110) | | ✅ |
+| [Rest Api](https://github.com/exercism/python/blob/main/exercises/practice/rest-api/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/rest-api/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/rest-api/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1795) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1787) | | ✅ |
+| [Reverse String](https://github.com/exercism/python/blob/main/exercises/practice/reverse-string/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/reverse-string/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/reverse-string/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2133) | NONE | [reverse-string](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/reverse-string/) | ✅ |
+| [Rna Transcription](https://github.com/exercism/python/blob/main/exercises/practice/rna-transcription/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/rna-transcription/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/rna-transcription/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2149) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2148) | | ✅ |
+| [Robot Name](https://github.com/exercism/python/blob/main/exercises/practice/robot-name/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/robot-name/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/robot-name/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L479) | NONE | | ❌ |
+| [Robot Simulator](https://github.com/exercism/python/blob/main/exercises/practice/robot-simulator/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/robot-simulator/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/robot-simulator/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1324) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1315) | | ✅ |
+| [Roman Numerals](https://github.com/exercism/python/blob/main/exercises/practice/roman-numerals/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/roman-numerals/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/roman-numerals/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1340) | NONE | | ✅ |
+| [Rotational Cipher](https://github.com/exercism/python/blob/main/exercises/practice/rotational-cipher/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/rotational-cipher/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/rotational-cipher/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1209) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1208) | | ✅ |
+| [Run Length Encoding](https://github.com/exercism/python/blob/main/exercises/practice/run-length-encoding/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/run-length-encoding/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/run-length-encoding/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1493) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1492) | | ✅ |
+| [Saddle Points](https://github.com/exercism/python/blob/main/exercises/practice/saddle-points/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/saddle-points/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/saddle-points/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L649) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L643) | | ✅ |
+| [Satellite](https://github.com/exercism/python/blob/main/exercises/practice/satellite/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/satellite/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/satellite/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1640) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1634) | | ✅ |
+| [Say](https://github.com/exercism/python/blob/main/exercises/practice/say/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/say/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/say/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1052) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1051) | | ✅ |
+| [Scale Generator](https://github.com/exercism/python/blob/main/exercises/practice/scale-generator/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/scale-generator/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/scale-generator/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1084) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1083) | | ✅ |
+| [Scrabble Score](https://github.com/exercism/python/blob/main/exercises/practice/scrabble-score/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/scrabble-score/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/scrabble-score/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L316) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L315) | [scrabble-score](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/scrabble-score/) | ✅ |
+| [Secret Handshake](https://github.com/exercism/python/blob/main/exercises/practice/secret-handshake/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/secret-handshake/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/secret-handshake/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L924) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L923) | | ✅ |
+| [Series](https://github.com/exercism/python/blob/main/exercises/practice/series/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/series/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/series/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L562) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L561) | | ✅ |
+| [Sgf Parsing](https://github.com/exercism/python/blob/main/exercises/practice/sgf-parsing/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/sgf-parsing/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/sgf-parsing/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2261) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2255) | | ✅ |
+| [Sieve](https://github.com/exercism/python/blob/main/exercises/practice/sieve/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/sieve/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/sieve/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L836) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L835) | | ✅ |
+| [Simple Cipher](https://github.com/exercism/python/blob/main/exercises/practice/simple-cipher/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/simple-cipher/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/simple-cipher/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L761) | NONE | | ✅ |
+| [Simple Linked List](https://github.com/exercism/python/blob/main/exercises/practice/simple-linked-list/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/simple-linked-list/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/simple-linked-list/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1357) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1356) | | ❌ |
+| [Space Age](https://github.com/exercism/python/blob/main/exercises/practice/space-age/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/space-age/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/space-age/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2164) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2163) | | ✅ |
+| [Spiral Matrix](https://github.com/exercism/python/blob/main/exercises/practice/spiral-matrix/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/spiral-matrix/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/spiral-matrix/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1714) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1713) | | ✅ |
+| [Sublist](https://github.com/exercism/python/blob/main/exercises/practice/sublist/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/sublist/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/sublist/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1141) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1140) | | ✅ |
+| [Sum Of Multiples](https://github.com/exercism/python/blob/main/exercises/practice/sum-of-multiples/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/sum-of-multiples/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/sum-of-multiples/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L778) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L777) | [sum-of-multiples](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/sum-of-multiples/) | ✅ |
+| [Tournament](https://github.com/exercism/python/blob/main/exercises/practice/tournament/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/tournament/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/tournament/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L421) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L409) | | ✅ |
+| [Transpose](https://github.com/exercism/python/blob/main/exercises/practice/transpose/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/transpose/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/transpose/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1511) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1510) | | ✅ |
+| [Tree Building](https://github.com/exercism/python/blob/main/exercises/practice/tree-building/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/tree-building/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/tree-building/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L852) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L851) | | ❌ |
+| [Triangle](https://github.com/exercism/python/blob/main/exercises/practice/triangle/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/triangle/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/triangle/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L664) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L663) | | ✅ |
+| [Twelve Days](https://github.com/exercism/python/blob/main/exercises/practice/twelve-days/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/twelve-days/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/twelve-days/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L281) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L280) | [twelve-days](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/twelve-days/) | ✅ |
+| [Two Bucket](https://github.com/exercism/python/blob/main/exercises/practice/two-bucket/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/two-bucket/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/two-bucket/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1244) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1243) | | ✅ |
+| [Two Fer](https://github.com/exercism/python/blob/main/exercises/practice/two-fer/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/two-fer/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/two-fer/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L202) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L201) | [two-fer](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/two-fer/) | ✅ |
+| [Variable Length Quantity](https://github.com/exercism/python/blob/main/exercises/practice/variable-length-quantity/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/variable-length-quantity/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/variable-length-quantity/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1226) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1225) | | ✅ |
+| [Word Count](https://github.com/exercism/python/blob/main/exercises/practice/word-count/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/word-count/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/word-count/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L302) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L296) | [word-count](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/word-count/) | ✅ |
+| [Word Search](https://github.com/exercism/python/blob/main/exercises/practice/word-search/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/word-search/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/word-search/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1616) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1606) | | ✅ |
+| [Wordy](https://github.com/exercism/python/blob/main/exercises/practice/wordy/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/wordy/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/wordy/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1069) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1068) | | ✅ |
+| [Yacht](https://github.com/exercism/python/blob/main/exercises/practice/yacht/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/yacht/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/yacht/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2180) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2179) | | ✅ |
+| [Zebra Puzzle](https://github.com/exercism/python/blob/main/exercises/practice/zebra-puzzle/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/zebra-puzzle/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/zebra-puzzle/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1866) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1865) | | ✅ |
+| [Zipper](https://github.com/exercism/python/blob/main/exercises/practice/zipper/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/zipper/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/zipper/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1659) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1658) | | ✅ |
+
+
-| Exercise | Difficulty | Solutions | Prereqs | Practices | Mentor
Notes |
-|--------------------------------------------------------------------------------------------------------------------------------------------|------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------|
-| [**Hello World**](https://github.com/exercism/python/blob/main/exercises/practice/hello-world/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/hello-world/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/hello-world/solutions?passed_head_tests=true) | NONE | `basics` | |
-| [Acronym](https://github.com/exercism/python/blob/main/exercises/practice/acronym/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/acronym/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/acronym/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L337) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L330) | [acronym](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/acronym/) |
-| [Affine Cipher](https://github.com/exercism/python/blob/main/exercises/practice/affine-cipher/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/affine-cipher/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/affine-cipher/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1174) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1173) | |
-| [All Your Base](https://github.com/exercism/python/blob/main/exercises/practice/all-your-base/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/all-your-base/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/all-your-base/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1394) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1393) | |
-| [Allergies](https://github.com/exercism/python/blob/main/exercises/practice/allergies/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/allergies/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/allergies/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L701) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L700) | [allergies](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/allergies/) |
-| [Alphametics](https://github.com/exercism/python/blob/main/exercises/practice/alphametics/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/alphametics/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/alphametics/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1935) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1934) | |
-| [Anagram](https://github.com/exercism/python/blob/main/exercises/practice/anagram/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/anagram/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/anagram/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L577) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L576) | |
-| [Armstrong Numbers](https://github.com/exercism/python/blob/main/exercises/practice/armstrong-numbers/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/armstrong-numbers/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/armstrong-numbers/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L512) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L511) | |
-| [Atbash Cipher](https://github.com/exercism/python/blob/main/exercises/practice/atbash-cipher/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/atbash-cipher/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/atbash-cipher/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1102) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1101) | |
-| [Bank Account](https://github.com/exercism/python/blob/main/exercises/practice/bank-account/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/bank-account/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/bank-account/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2207) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2206) | |
-| [Beer Song](https://github.com/exercism/python/blob/main/exercises/practice/beer-song/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/beer-song/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/beer-song/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L905) | NONE | |
-| [Binary Search Tree](https://github.com/exercism/python/blob/main/exercises/practice/binary-search-tree/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/binary-search-tree/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/binary-search-tree/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1157) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1156) | |
-| [Binary Search](https://github.com/exercism/python/blob/main/exercises/practice/binary-search/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/binary-search/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/binary-search/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1192) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1191) | [binary-search](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/binary-search/) |
-| [Bob](https://github.com/exercism/python/blob/main/exercises/practice/bob/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/bob/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/bob/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L715) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L714) | [bob](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/bob/) |
-| [Book Store](https://github.com/exercism/python/blob/main/exercises/practice/book-store/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/book-store/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/book-store/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L445) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L437) | |
-| [Bowling](https://github.com/exercism/python/blob/main/exercises/practice/bowling/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/bowling/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/bowling/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1553) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1552) | |
-| [Change](https://github.com/exercism/python/blob/main/exercises/practice/change/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/change/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/change/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1412) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1411) | |
-| [Circular Buffer](https://github.com/exercism/python/blob/main/exercises/practice/circular-buffer/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/circular-buffer/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/circular-buffer/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1475) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1469) | |
-| [Clock](https://github.com/exercism/python/blob/main/exercises/practice/clock/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/clock/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/clock/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L394) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L389) | [clock](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/clock/) |
-| [Collatz Conjecture](https://github.com/exercism/python/blob/main/exercises/practice/collatz-conjecture/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/collatz-conjecture/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/collatz-conjecture/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L593) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L592) | |
-| [Complex Numbers](https://github.com/exercism/python/blob/main/exercises/practice/complex-numbers/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/complex-numbers/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/complex-numbers/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L799) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L792) | |
-| [Connect](https://github.com/exercism/python/blob/main/exercises/practice/connect/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/connect/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/connect/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L960) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L959) | |
-| [Crypto Square](https://github.com/exercism/python/blob/main/exercises/practice/crypto-square/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/crypto-square/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/crypto-square/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1263) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1262) | |
-| [Custom Set](https://github.com/exercism/python/blob/main/exercises/practice/custom-set/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/custom-set/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/custom-set/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1888) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1883) | |
-| [Darts](https://github.com/exercism/python/blob/main/exercises/practice/darts/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/darts/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/darts/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2199) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2198) | |
-| [Diamond](https://github.com/exercism/python/blob/main/exercises/practice/diamond/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/diamond/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/diamond/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1696) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1695) | |
-| [Difference Of Squares](https://github.com/exercism/python/blob/main/exercises/practice/difference-of-squares/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/difference-of-squares/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/difference-of-squares/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L601) | NONE | |
-| [Diffie Hellman](https://github.com/exercism/python/blob/main/exercises/practice/diffie-hellman/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/diffie-hellman/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/diffie-hellman/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1956) | NONE | |
-| [Dnd Character](https://github.com/exercism/python/blob/main/exercises/practice/dnd-character/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/dnd-character/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/dnd-character/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2078) | NONE | |
-| [Dominoes](https://github.com/exercism/python/blob/main/exercises/practice/dominoes/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/dominoes/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/dominoes/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1767) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1755) | |
-| [Dot Dsl](https://github.com/exercism/python/blob/main/exercises/practice/dot-dsl/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/dot-dsl/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/dot-dsl/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1434) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1427) | |
-| [Etl](https://github.com/exercism/python/blob/main/exercises/practice/etl/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/etl/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/etl/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1118) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1117) | |
-| [Flatten Array](https://github.com/exercism/python/blob/main/exercises/practice/flatten-array/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/flatten-array/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/flatten-array/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1126) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1125) | |
-| [Food Chain](https://github.com/exercism/python/blob/main/exercises/practice/food-chain/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/food-chain/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/food-chain/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1737) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1731) | |
-| [Forth](https://github.com/exercism/python/blob/main/exercises/practice/forth/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/forth/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/forth/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1571) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1570) | |
-| [Gigasecond](https://github.com/exercism/python/blob/main/exercises/practice/gigasecond/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/gigasecond/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/gigasecond/solutions?passed_head_tests=true) | | NONE | |
-| [Go Counting](https://github.com/exercism/python/blob/main/exercises/practice/go-counting/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/go-counting/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/go-counting/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L868) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L867) | |
-| [Grade School](https://github.com/exercism/python/blob/main/exercises/practice/grade-school/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/grade-school/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/grade-school/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L364) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L363) | [grade-school](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/grade-school/) |
-| [Grains](https://github.com/exercism/python/blob/main/exercises/practice/grains/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/grains/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/grains/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L678) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L677) | |
-| [Grep](https://github.com/exercism/python/blob/main/exercises/practice/grep/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/grep/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/grep/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1536) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1528) | |
-| [Hamming](https://github.com/exercism/python/blob/main/exercises/practice/hamming/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/hamming/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/hamming/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L259) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L254) | [hamming](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/hamming/) |
-| [Hangman](https://github.com/exercism/python/blob/main/exercises/practice/hangman/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/hangman/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/hangman/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2221) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2220) | |
-| [High Scores](https://github.com/exercism/python/blob/main/exercises/practice/high-scores/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/high-scores/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/high-scores/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L226) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L225) | [high-scores](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/high-scores/) |
-| [House](https://github.com/exercism/python/blob/main/exercises/practice/house/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/house/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/house/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1279) | NONE | |
-| [Isbn Verifier](https://github.com/exercism/python/blob/main/exercises/practice/isbn-verifier/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/isbn-verifier/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/isbn-verifier/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L609) | NONE | |
-| [Isogram](https://github.com/exercism/python/blob/main/exercises/practice/isogram/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/isogram/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/isogram/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L273) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L272) | [isogram](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/isogram/) |
-| [Kindergarten Garden](https://github.com/exercism/python/blob/main/exercises/practice/kindergarten-garden/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/kindergarten-garden/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/kindergarten-garden/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L350) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L344) | [kindergarten-garden](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/kindergarten-garden/) |
-| [Knapsack](https://github.com/exercism/python/blob/main/exercises/practice/knapsack/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/knapsack/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/knapsack/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1453) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1452) | |
-| [Largest Series Product](https://github.com/exercism/python/blob/main/exercises/practice/largest-series-product/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/largest-series-product/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/largest-series-product/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L945) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L939) | |
-| [Leap](https://github.com/exercism/python/blob/main/exercises/practice/leap/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/leap/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/leap/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2103) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2102) | [leap](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/leap/) |
-| [Ledger](https://github.com/exercism/python/blob/main/exercises/practice/ledger/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/ledger/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/ledger/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1590) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1589) | |
-| [Linked List](https://github.com/exercism/python/blob/main/exercises/practice/linked-list/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/linked-list/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/linked-list/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1379) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1371) | |
-| [List Ops](https://github.com/exercism/python/blob/main/exercises/practice/list-ops/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/list-ops/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/list-ops/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1294) | NONE | |
-| [Luhn](https://github.com/exercism/python/blob/main/exercises/practice/luhn/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/luhn/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/luhn/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L372) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L371) | [luhn](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/luhn/) |
-| [Markdown](https://github.com/exercism/python/blob/main/exercises/practice/markdown/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/markdown/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/markdown/solutions?passed_head_tests=true) | | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L401) | [markdown](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/markdown/) |
-| [Matching Brackets](https://github.com/exercism/python/blob/main/exercises/practice/matching-brackets/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/matching-brackets/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/matching-brackets/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L728) | NONE | [matching-brackets](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/matching-brackets/) |
-| [Matrix](https://github.com/exercism/python/blob/main/exercises/practice/matrix/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/matrix/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/matrix/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1714) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1713) | [matrix](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/matrix/) |
-| [Meetup](https://github.com/exercism/python/blob/main/exercises/practice/meetup/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/meetup/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/meetup/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L818) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L812) | |
-| [Minesweeper](https://github.com/exercism/python/blob/main/exercises/practice/minesweeper/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/minesweeper/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/minesweeper/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L981) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L980) | |
-| [Nth Prime](https://github.com/exercism/python/blob/main/exercises/practice/nth-prime/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/nth-prime/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/nth-prime/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1814) | NONE | |
-| [Ocr Numbers](https://github.com/exercism/python/blob/main/exercises/practice/ocr-numbers/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/ocr-numbers/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/ocr-numbers/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L997) | NONE | |
-| [Paasio](https://github.com/exercism/python/blob/main/exercises/practice/paasio/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/paasio/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/paasio/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1917) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1906) | |
-| [Palindrome Products](https://github.com/exercism/python/blob/main/exercises/practice/palindrome-products/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/palindrome-products/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/palindrome-products/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L626) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L625) | |
-| [Pangram](https://github.com/exercism/python/blob/main/exercises/practice/pangram/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/pangram/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/pangram/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L463) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L462) | |
-| [Perfect Numbers](https://github.com/exercism/python/blob/main/exercises/practice/perfect-numbers/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/perfect-numbers/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/perfect-numbers/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L527) | NONE | |
-| [Phone Number](https://github.com/exercism/python/blob/main/exercises/practice/phone-number/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/phone-number/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/phone-number/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L547) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L542) | |
-| [Pig Latin](https://github.com/exercism/python/blob/main/exercises/practice/pig-latin/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/pig-latin/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/pig-latin/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1832) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1831) | |
-| [Poker](https://github.com/exercism/python/blob/main/exercises/practice/poker/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/poker/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/poker/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1016) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1015) | |
-| [Pov](https://github.com/exercism/python/blob/main/exercises/practice/pov/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/pov/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/pov/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1677) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1676) | |
-| [Prime Factors](https://github.com/exercism/python/blob/main/exercises/practice/prime-factors/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/prime-factors/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/prime-factors/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L686) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L685) | |
-| [Protein Translation](https://github.com/exercism/python/blob/main/exercises/practice/protein-translation/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/protein-translation/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/protein-translation/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L496) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L495) | |
-| [Pythagorean Triplet](https://github.com/exercism/python/blob/main/exercises/practice/pythagorean-triplet/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/pythagorean-triplet/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/pythagorean-triplet/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L745) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L744) | |
-| [Queen Attack](https://github.com/exercism/python/blob/main/exercises/practice/queen-attack/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/queen-attack/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/queen-attack/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1302) | NONE | |
-| [Rail Fence Cipher](https://github.com/exercism/python/blob/main/exercises/practice/rail-fence-cipher/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/rail-fence-cipher/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/rail-fence-cipher/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1849) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1848) | |
-| [Raindrops](https://github.com/exercism/python/blob/main/exercises/practice/raindrops/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/raindrops/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/raindrops/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L210) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L209) | [raindrops](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/raindrops/) |
-| [Rational Numbers](https://github.com/exercism/python/blob/main/exercises/practice/rational-numbers/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/rational-numbers/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/rational-numbers/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2240) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2239) | |
-| [React](https://github.com/exercism/python/blob/main/exercises/practice/react/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/react/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/react/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L890) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L884) | |
-| [Rectangles](https://github.com/exercism/python/blob/main/exercises/practice/rectangles/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/rectangles/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/rectangles/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1032) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1031) | |
-| [Resistor Color Duo](https://github.com/exercism/python/blob/main/exercises/practice/resistor-color-duo/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/resistor-color-duo/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/resistor-color-duo/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2119) | NONE | |
-| [Resistor Color](https://github.com/exercism/python/blob/main/exercises/practice/resistor-color/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/resistor-color/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/resistor-color/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2111) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2110) | |
-| [Rest Api](https://github.com/exercism/python/blob/main/exercises/practice/rest-api/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/rest-api/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/rest-api/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1795) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1787) | |
-| [Reverse String](https://github.com/exercism/python/blob/main/exercises/practice/reverse-string/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/reverse-string/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/reverse-string/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2133) | NONE | [reverse-string](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/reverse-string/) |
-| [Rna Transcription](https://github.com/exercism/python/blob/main/exercises/practice/rna-transcription/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/rna-transcription/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/rna-transcription/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2149) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2148) | |
-| [Robot Name](https://github.com/exercism/python/blob/main/exercises/practice/robot-name/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/robot-name/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/robot-name/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L479) | NONE | |
-| [Robot Simulator](https://github.com/exercism/python/blob/main/exercises/practice/robot-simulator/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/robot-simulator/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/robot-simulator/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1324) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1315) | |
-| [Roman Numerals](https://github.com/exercism/python/blob/main/exercises/practice/roman-numerals/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/roman-numerals/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/roman-numerals/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1340) | NONE | |
-| [Rotational Cipher](https://github.com/exercism/python/blob/main/exercises/practice/rotational-cipher/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/rotational-cipher/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/rotational-cipher/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1209) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1208) | |
-| [Run Length Encoding](https://github.com/exercism/python/blob/main/exercises/practice/run-length-encoding/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/run-length-encoding/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/run-length-encoding/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1493) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1492) | |
-| [Saddle Points](https://github.com/exercism/python/blob/main/exercises/practice/saddle-points/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/saddle-points/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/saddle-points/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L649) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L643) | |
-| [Satellite](https://github.com/exercism/python/blob/main/exercises/practice/satellite/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/satellite/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/satellite/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1640) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1634) | |
-| [Say](https://github.com/exercism/python/blob/main/exercises/practice/say/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/say/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/say/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1052) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1051) | |
-| [Scale Generator](https://github.com/exercism/python/blob/main/exercises/practice/scale-generator/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/scale-generator/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/scale-generator/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1084) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1083) | |
-| [Scrabble Score](https://github.com/exercism/python/blob/main/exercises/practice/scrabble-score/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/scrabble-score/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/scrabble-score/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L316) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L315) | [scrabble-score](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/scrabble-score/) |
-| [Secret Handshake](https://github.com/exercism/python/blob/main/exercises/practice/secret-handshake/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/secret-handshake/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/secret-handshake/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L924) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L923) | |
-| [Series](https://github.com/exercism/python/blob/main/exercises/practice/series/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/series/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/series/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L562) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L561) | |
-| [Sgf Parsing](https://github.com/exercism/python/blob/main/exercises/practice/sgf-parsing/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/sgf-parsing/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/sgf-parsing/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2261) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2255) | |
-| [Sieve](https://github.com/exercism/python/blob/main/exercises/practice/sieve/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/sieve/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/sieve/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L836) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L835) | |
-| [Simple Cipher](https://github.com/exercism/python/blob/main/exercises/practice/simple-cipher/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/simple-cipher/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/simple-cipher/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L761) | NONE | |
-| [Simple Linked List](https://github.com/exercism/python/blob/main/exercises/practice/simple-linked-list/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/simple-linked-list/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/simple-linked-list/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1357) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1356) | |
-| [Space Age](https://github.com/exercism/python/blob/main/exercises/practice/space-age/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/space-age/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/space-age/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2164) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2163) | |
-| [Spiral Matrix](https://github.com/exercism/python/blob/main/exercises/practice/spiral-matrix/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/spiral-matrix/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/spiral-matrix/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1714) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1713) | |
-| [Sublist](https://github.com/exercism/python/blob/main/exercises/practice/sublist/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/sublist/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/sublist/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1141) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1140) | |
-| [Sum Of Multiples](https://github.com/exercism/python/blob/main/exercises/practice/sum-of-multiples/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/sum-of-multiples/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/sum-of-multiples/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L778) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L777) | [sum-of-multiples](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/sum-of-multiples/) |
-| [Tournament](https://github.com/exercism/python/blob/main/exercises/practice/tournament/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/tournament/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/tournament/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L421) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L409) | |
-| [Transpose](https://github.com/exercism/python/blob/main/exercises/practice/transpose/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/transpose/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/transpose/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1511) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1510) | |
-| [Tree Building](https://github.com/exercism/python/blob/main/exercises/practice/tree-building/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/tree-building/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/tree-building/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L852) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L851) | |
-| [Triangle](https://github.com/exercism/python/blob/main/exercises/practice/triangle/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/triangle/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/triangle/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L664) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L663) | |
-| [Twelve Days](https://github.com/exercism/python/blob/main/exercises/practice/twelve-days/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/twelve-days/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/twelve-days/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L281) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L280) | [twelve-days](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/twelve-days/) |
-| [Two Bucket](https://github.com/exercism/python/blob/main/exercises/practice/two-bucket/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/two-bucket/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/two-bucket/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1244) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1243) | |
-| [Two Fer](https://github.com/exercism/python/blob/main/exercises/practice/two-fer/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/two-fer/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/two-fer/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L202) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L201) | [two-fer](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/two-fer/) |
-| [Variable Length Quantity](https://github.com/exercism/python/blob/main/exercises/practice/variable-length-quantity/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/variable-length-quantity/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/variable-length-quantity/solutions?passed_head_tests=true)| [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1226) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1225) | |
-| [Word Count](https://github.com/exercism/python/blob/main/exercises/practice/word-count/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/word-count/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/word-count/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L302) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L296) | [word-count](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/word-count/) |
-| [Word Search](https://github.com/exercism/python/blob/main/exercises/practice/word-search/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/word-search/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/word-search/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1616) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1606) | |
-| [Wordy](https://github.com/exercism/python/blob/main/exercises/practice/wordy/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/wordy/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/wordy/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1069) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1068) | |
-| [Yacht](https://github.com/exercism/python/blob/main/exercises/practice/yacht/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/yacht/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/yacht/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2180) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2179) | |
-| [Zebra Puzzle](https://github.com/exercism/python/blob/main/exercises/practice/zebra-puzzle/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/zebra-puzzle/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/zebra-puzzle/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1866) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1865) | |
-| [Zipper](https://github.com/exercism/python/blob/main/exercises/practice/zipper/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/zipper/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/zipper/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1659) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1658) | |
+### Deprecated
+
+| Exercise | Difficulty |
+| -------------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
+| [Accumulate](https://github.com/exercism/python/blob/main/exercises/practice/accumulate/.docs/instructions.md) | 🔹🔹 |
+| [Beer Song](https://github.com/exercism/python/blob/main/exercises/practice/beer-song/.docs/instructions.md) | 🔹🔹🔹 |
+| [Binary](https://github.com/exercism/python/blob/main/exercises/practice/binary/.docs/instructions.md) | 🔹🔹🔹 |
+| [Error Handling](https://github.com/exercism/python/blob/main/exercises/practice/error-handling/.docs/instructions.md) | 🔹🔹🔹 |
+| [Hexadecimal](https://github.com/exercism/python/blob/main/exercises/practice/hexadecimal/.docs/instructions.md) | 🔹🔹🔹 |
+| [Nucleotide Count](https://github.com/exercism/python/blob/main/exercises/practice/nucleotide-count/.docs/instructions.md) | 🔹🔹 |
+| [Parallel Letter Frequency](https://github.com/exercism/python/blob/main/exercises/practice/parallel-letter-frequency/.docs/instructions.md) | 🔹🔹🔹 |
+| [Pascal's Triangle](https://github.com/exercism/python/blob/main/exercises/practice/pascals-triangle/.docs/instructions.md) | 🔹🔹🔹 |
+| [Point Mutations](https://github.com/exercism/python/blob/main/exercises/practice/point-mutations/.docs/instructions.md) | 🔹🔹🔹 |
+| [Trinary](https://github.com/exercism/python/blob/main/exercises/practice/trinary/.docs/instructions.md) | 🔹🔹🔹🔹 |
+| [Custom Set](https://github.com/exercism/python/blob/main/exercises/practice/custom-set/.docs/instructions.md) | 🔹🔹🔹🔹🔹 |
-
-
## Concepts Without Planned Exercises
No Exercises Planned
-
-| Status | Concept | About&Intro | Exercise | Design Doc or Issue |
-|:---------------------------------------------------------------------------------------------------: |------------------------------------------------------------------------------------------------------------------------------ |:---------------------------------------------------------------------------------------------------: |------------------------------------------------------------------------------------------------------------------------------- |------------------------------------------------------------------------------------------------------------------ |
-| ~~ | \*general--Composition | ~~ | NA | NA |
-| ~~ | \*general--Data Structures] | ~~ | NA | NA |
-| ~~ | \*general--Encapsulation | ~~ | NA | NA |
-| ~~ | \*general--Interfaces] | ~~ | NA | NA |
-| ~~ | \*general--Lookup efficiency] | ~~ | NA | NA |
-| ~~ | \*general--Mutability in Python] | ~~ | NA | NA |
-| ~~ | \*general--Mutation | ~~ | NA | NA |
-| ~~ | \*general--Polymorphism | ~~ | NA | NA |
-| ~~ | \*general--Recursive data structures | ~~ | NA | NA |
-| ~~ | \*general--Scope | ~~ | NA | NA |
-| ~~ | \*general--Standard Library | ~~ | NA | NA |
-| ~~ | \*general--State | ~~ | NA | NA |
-| ~~ | \*no stand-alone--del | ~~ | Multiple | Multiple |
-| ~~ | \*no stand-alone--Duck Typing | ~~ | Multiple | Multiple |
-| ~~ | \*no stand-alone--Dynamic Typing | ~~ | Multiple | Multiple |
-| ~~ | \*no stand-alone--Expressions | ~~ | Multiple | Multiple |
-| ~~ | \*no stand-alone--Immutability in Python | ~~ | Multiple | Multiple |
-| ~~ | \*no stand-alone--Operator precedence | ~~ | Multiple | Multiple |
-| ~~ | \*no stand-alone--Operators] | ~~ | Multiple | Multiple |
-| ~~ | \*no stand-alone--Order of Evaluation | ~~ | Multiple | Multiple |
-| ~~ | \*no stand-alone--type | ~~ | Multiple | Multiple |
-| ~~ | \*no stand-alone--type conversion | ~~ | Multiple | Multiple |
+| Status | Concept | About&Intro | Exercise | Design Doc or Issue |
+| :----: | ---------------------------------------- | :---------: | -------- | ------------------- |
+| ~~ | \*general--Composition | ~~ | NA | NA |
+| ~~ | \*general--Data Structures] | ~~ | NA | NA |
+| ~~ | \*general--Encapsulation | ~~ | NA | NA |
+| ~~ | \*general--Interfaces] | ~~ | NA | NA |
+| ~~ | \*general--Lookup efficiency] | ~~ | NA | NA |
+| ~~ | \*general--Mutability in Python] | ~~ | NA | NA |
+| ~~ | \*general--Mutation | ~~ | NA | NA |
+| ~~ | \*general--Polymorphism | ~~ | NA | NA |
+| ~~ | \*general--Recursive data structures | ~~ | NA | NA |
+| ~~ | \*general--Scope | ~~ | NA | NA |
+| ~~ | \*general--Standard Library | ~~ | NA | NA |
+| ~~ | \*general--State | ~~ | NA | NA |
+| ~~ | \*no stand-alone--del | ~~ | Multiple | Multiple |
+| ~~ | \*no stand-alone--Duck Typing | ~~ | Multiple | Multiple |
+| ~~ | \*no stand-alone--Dynamic Typing | ~~ | Multiple | Multiple |
+| ~~ | \*no stand-alone--Expressions | ~~ | Multiple | Multiple |
+| ~~ | \*no stand-alone--Immutability in Python | ~~ | Multiple | Multiple |
+| ~~ | \*no stand-alone--Operator precedence | ~~ | Multiple | Multiple |
+| ~~ | \*no stand-alone--Operators] | ~~ | Multiple | Multiple |
+| ~~ | \*no stand-alone--Order of Evaluation | ~~ | Multiple | Multiple |
+| ~~ | \*no stand-alone--type | ~~ | Multiple | Multiple |
+| ~~ | \*no stand-alone--type conversion | ~~ | Multiple | Multiple |
-
## Implemented & Planned Concept Exercises
-
= live on exercism.org
= drafted but not live
= planned or in progress
@@ -186,73 +198,72 @@
-| Status | Concept | About&Intro | Exercise | Design Doc or Issue |Stub
Docstring Level|
-|:----------------------------------------------:|:-----------------------------------------------------------------------------------------------------------------------------|:----------------------------------------------:|:------------------------------------------------------------------------------------------------------------------------------|:-----------------------------------------------------------------------------------------------------------------|:---|
-|
| [basics](https://github.com/exercism/python/blob/main/concepts/basics) |
| [Guidos Gorgeous Lasagna](https://github.com/exercism/python/tree/main/tree/main/exercises/concept/guidos-gorgeous-lasagna) | [`.meta`folder](https://github.com/exercism/python/tree/main/exercises/concept/guidos-gorgeous-lasagna/.meta) | Full |
-|
| [bools](https://github.com/exercism/python/blob/main/concepts/bools) |
| [Ghost Gobble Arcade Game](https://github.com/exercism/python/tree/main/tree/main/exercises/concept/ghost-gobble-arcade-game) | [`.meta`folder](https://github.com/exercism/python/tree/main/exercises/concept/ghost-gobble-arcade-game/.meta) | Full |
-|
| [numbers](https://github.com/exercism/python/blob/main/concepts/numbers) |
| [Currency Exchange](https://github.com/exercism/python/tree/main/exercises/concept/currency-exchange) | [`.meta`folder](https://github.com/exercism/python/tree/main/exercises/concept/currency-exchange/.meta) | Full |
-|
| [complex-numbers](https://github.com/exercism/python/blob/main/concepts/complex-numbers) |
| ~ | [#2208](https://github.com/exercism/v3/issues/2208) | TBD |
-|
| [conditionals](https://github.com/exercism/python/blob/main/concepts/conditionals) |
| [Meltdown Mitigation ](https://github.com/exercism/python/tree/main/exercises/concept/meltdown-mitigation) | [`.meta`folder](https://github.com/exercism/python/tree/main/exercises/concept/meltdown-mitigation/.meta) | Full |
-|
| [comparisons](https://github.com/exercism/python/blob/main/concepts/comparisons) |
| [Black Jack](https://github.com/exercism/python/tree/main/exercises/concept/black-jack) | [`.meta`folder](https://github.com/exercism/python/tree/main/exercises/concept/black-jack/.meta) | Full |
-|
| [strings](https://github.com/exercism/python/blob/main/concepts/strings) |
| [Litte Sister's Vocab](https://github.com/exercism/python/tree/main/exercises/concept/little-sisters-vocab) | [`.meta`folder](https://github.com/exercism/python/tree/main/exercises/concept/little-sisters-vocab/.meta) | Full |
-|
| [string-methods](https://github.com/exercism/python/blob/main/concepts/string-methods) |
| [Litte Sister's Essay](https://github.com/exercism/python/tree/main/exercises/concept/little-sisters-essay) | [`.meta`folder](https://github.com/exercism/python/tree/main/exercises/concept/little-sisters-essay/.meta) | Full |
-|
| [string-formatting](https://github.com/exercism/python/blob/main/concepts/string-formatting) |
| [Pretty Leaflet ](https://github.com/exercism/python/tree/main/exercises/concept/pretty-leaflet) | [`.meta`folder](https://github.com/exercism/python/tree/main/exercises/concept/pretty-leaflet/.meta) | Full |
-|
| [lists](https://github.com/exercism/python/blob/main/concepts/lists) |
| [Card Games](https://github.com/exercism/python/tree/main/exercises/concept/card-games) | [`.meta`folder](https://github.com/exercism/python/tree/main/exercises/concept/card-games/.meta) | Full |
-|
| [list-methods](https://github.com/exercism/python/blob/main/concepts/list-methods) |
| [Chaitanas Colossal Coaster](https://github.com/exercism/python/tree/main/exercises/concept/chaitanas-colossal-coaster) | [`.meta`folder](https://github.com/exercism/python/tree/main/exercises/concept/chaitanas-colossal-coaster/.meta) | Full |
-|
| [loops](https://github.com/exercism/python/blob/main/concepts/loops) |
| [Making the Grade](https://github.com/exercism/python/tree/main/exercises/concept/making-the-grade) | [`.meta`folder](https://github.com/exercism/python/tree/main/exercises/concept/making-the-grade/.meta) | Full |
-|
| [tuples](https://github.com/exercism/python/blob/main/concepts/tuples) |
| [Tisbury Treasure Hunt](https://github.com/exercism/python/tree/main/exercises/concept/tisbury-treasure-hunt) | [`.meta`folder](https://github.com/exercism/python/tree/main/exercises/concept/tisbury-treasure-hunt/.meta) | Full |
-|
| [sequences](https://github.com/exercism/python/blob/main/concepts/sequences) |
| ~ | [#2290](https://github.com/exercism/python/issues/2290) | TBD |
-|
| [dicts](https://github.com/exercism/python/blob/main/concepts/dicts) |
| [Inventory Management](https://github.com/exercism/python/tree/main/exercises/concept/inventory-management) | [`.meta`folder](https://github.com/exercism/python/tree/main/exercises/concept/inventory-management) | Full |
-|
| [dict-methods](https://github.com/exercism/python/blob/main/concepts/dict-methods) |
| ~ | [#2348](https://github.com/exercism/python/issues/2348) | |
-|
| [sets](https://github.com/exercism/python/blob/main/concepts/sets) |
| [Cater Waiter ](https://github.com/exercism/python/tree/main/exercises/concept/cater-waiter) | [`.meta`folder](https://github.com/exercism/python/tree/main/exercises/concept/cater-waiter/.meta) | Full |
-|
| [list-comprehensions](https://github.com/exercism/python/blob/main/concepts/list-comprehensions) |
| ~ | [#2295](https://github.com/exercism/python/issues/2295) | |
-|
| [other-comprehensions](https://github.com/exercism/python/blob/main/concepts/other-comprehensions) |
| ~ | [#2294](https://github.com/exercism/python/issues/2294) | |
-|
| [classes](https://github.com/exercism/python/blob/main/concepts/classes) |
| [Ellen's Alien Game](https://github.com/exercism/python/tree/main/exercises/concept/ellens-alien-game) | [`.meta`folder](https://github.com/exercism/python/tree/main/exercises/concept/ellens-alien-game/.meta) | Minimal |
-|
| [generators](https://github.com/exercism/python/blob/main/concepts/generators) |
| Plane Tickets | [PR#2729](https://github.com/exercism/python/pull/2729)/[#2293](https://github.com/exercism/python/issues/2293) | Minimal |
-|
| [generator-expressions](https://github.com/exercism/python/blob/main/concepts/generator-expressions) |
| ~ | [#2292](https://github.com/exercism/python/issues/2292) | |
-|
| [iterators](https://github.com/exercism/python/blob/main/concepts/iterators) |
| ~ | [#2367](https://github.com/exercism/python/issues/2367) | TBD |
-|
| [functions](https://github.com/exercism/python/blob/main/concepts/functions) |
| ~ | [#2353](https://github.com/exercism/python/issues/2353) | |
-|
| [unpacking-and-multiple-assignment](https://github.com/exercism/python/blob/main/concepts/unpacking-and-multiple-assignment) |
| ~ | [#2360](https://github.com/exercism/python/issues/2360) | |
-|
| [raising-and-handling-errors](https://github.com/exercism/python/blob/main/concepts/raising-and-handling-errors) |
| ~ | TBD | |
-|
| [itertools](https://github.com/exercism/python/blob/main/concepts/itertools) |
| ~ | [#2368](https://github.com/exercism/python/issues/2368) | |
-|
| [with-statement](https://github.com/exercism/python/blob/main/concepts/with-statement) |
| ~ | [#2369](https://github.com/exercism/python/issues/2369) | |
-|
| [enums](https://github.com/exercism/python/blob/main/concepts/enums) |
| [Log Levels](https://github.com/exercism/python/tree/main/exercises/concept/log-levels) | [`.meta`folder](https://github.com/exercism/python/tree/main/exercises/concept/log-levels) | Minimal |
-|
| [none](https://github.com/exercism/python/blob/main/concepts/none) |
| [Restaurant Rozalynn](https://github.com/exercism/python/tree/main/exercises/concept/restaurant-rozalynn) | [`.meta`folder](https://github.com/exercism/python/tree/main/exercises/concept/restaurant-rozalynn/.meta) | Minimal |
-|
| [decorators](https://github.com/exercism/python/blob/main/concepts/decorators) |
| ~ | [#2356](https://github.com/exercism/python/issues/2356) | |
-|
| [rich-comparisons](https://github.com/exercism/python/blob/main/concepts/rich-comparisons) |
| ~ | [#2287](https://github.com/exercism/python/issues/2287) | |
-|
| [function-arguments](https://github.com/exercism/python/blob/main/concepts/function-arguments) |
| ~ | [#2354](https://github.com/exercism/python/issues/2354) | |
-|
| [class-customization](https://github.com/exercism/python/blob/main/concepts/class-customization) |
| ~ | [#2350](https://github.com/exercism/python/issues/2350) | |
-|
| [class-inheritance](https://github.com/exercism/python/blob/main/concepts/class-inheritance) |
| ~ | [#2351](https://github.com/exercism/python/issues/2351) | |
-|
| [user-defined-errors](https://github.com/exercism/python/blob/main/concepts/user-defined-errors) |
| ~ | TBD | |
-|
| [context-manager-customization](https://github.com/exercism/python/blob/main/concepts/context-manager-customization) |
| ~ | [#2370](https://github.com/exercism/python/issues/2370) | |
-|
| [higher-order-functions](https://github.com/exercism/python/blob/main/concepts/higher-order-functions) |
| ~ | [#2355](https://github.com/exercism/python/issues/2355) | |
-|
| [functional-tools](https://github.com/exercism/python/blob/main/concepts/functional-tools) |
| ~ | [#2359](https://github.com/exercism/python/issues/2359) | |
-|
| [functools](https://github.com/exercism/python/blob/main/concepts/functools) |
| ~ | [#2366](https://github.com/exercism/python/issues/2366) | |
-|
| [anonymous-functions](https://github.com/exercism/python/blob/main/concepts) |
| ~ | [#2357](https://github.com/exercism/python/issues/2357) | |
-|
| [descriptors](https://github.com/exercism/python/blob/main/concepts/descriptors) |
| ~ | [#2365](https://github.com/exercism/python/issues/2365) | |
-|
| [aliasing](https://github.com/exercism/python/blob/main/concepts) |
| ~ | TBD | |
-|
| [binary data](https://github.com/exercism/python/blob/main/concepts/binary-data) |
| ~ | TBD | |
-|
| [bitflags](https://github.com/exercism/python/blob/main/concepts/bitflags) |
| ~ | TBD | |
-|
| [bitwise-operators](https://github.com/exercism/python/blob/main/concepts/bitwise-operators) |
| ~ | TBD | |
-|
| [bytes](https://github.com/exercism/python/blob/main/concepts/bytes) |
| ~ | TBD | |
-|
| [class-composition](https://github.com/exercism/python/blob/main/concepts/class-composition) |
| ~ | [#2352](https://github.com/exercism/python/issues/2352) | |
-|
| [class-interfaces](https://github.com/exercism/python/blob/main/concepts/class-interfaces) |
| ~ | TBD | |
-|
| [collections](https://github.com/exercism/python/blob/main/concepts/collections) |
| ~ | TBD | |
-|
| [dataclasses-and-namedtuples](https://github.com/exercism/python/blob/main/concepts/dataclasses-and-namedtuples) |
| ~ | [#2361](https://github.com/exercism/python/issues/2361) | |
-|
| [import](https://github.com/exercism/python/blob/main/concepts/import) |
| ~ | ON HOLD | |
-|
| [memoryview](https://github.com/exercism/python/blob/main/concepts/memoryview) |
| ~ | TBD | |
-|
| [operator-overloading](https://github.com/exercism/python/blob/main/concepts/operator-overloading) |
| ~ | TBD | |
-|
| [regular-expressions](https://github.com/exercism/python/blob/main/concepts/regular-expressions) |
| ~ | TBD | |
-|
| [string-methods-splitting](https://github.com/exercism/python/blob/main/concepts/string-methods-splitting) |
| ~ | TBD | |
-|
| [testing](https://github.com/exercism/python/blob/main/concepts/testing) |
| ~ | TBD | |
-|
| [text-processing](https://github.com/exercism/python/blob/main/concepts/text-processing) |
| ~ | TBD | |
-|
| [type-hinting](https://github.com/exercism/python/blob/main/concepts/type-hinting) |
| ~ | TBD | |
-|
| [unicode-regular-expressions](https://github.com/exercism/python/blob/main/concepts/unicode-regular-expressions) |
| ~ | TBD | |
-|
| [walrus-operator](https://github.com/exercism/python/blob/main/concepts/walrus-operator) |
| ~ | TBD | |
+| Status | Concept | About&Intro | Exercise | Design Doc or Issue | Stub
Docstring Level |
+| :--------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------- | :---------------------- |
+|
| [basics](https://github.com/exercism/python/blob/main/concepts/basics) |
| [Guidos Gorgeous Lasagna](https://github.com/exercism/python/tree/main/tree/main/exercises/concept/guidos-gorgeous-lasagna) | [`.meta`folder](https://github.com/exercism/python/tree/main/exercises/concept/guidos-gorgeous-lasagna/.meta) | Full |
+|
| [bools](https://github.com/exercism/python/blob/main/concepts/bools) |
| [Ghost Gobble Arcade Game](https://github.com/exercism/python/tree/main/tree/main/exercises/concept/ghost-gobble-arcade-game) | [`.meta`folder](https://github.com/exercism/python/tree/main/exercises/concept/ghost-gobble-arcade-game/.meta) | Full |
+|
| [numbers](https://github.com/exercism/python/blob/main/concepts/numbers) |
| [Currency Exchange](https://github.com/exercism/python/tree/main/exercises/concept/currency-exchange) | [`.meta`folder](https://github.com/exercism/python/tree/main/exercises/concept/currency-exchange/.meta) | Full |
+|
| [complex-numbers](https://github.com/exercism/python/blob/main/concepts/complex-numbers) |
| ~ | [#2208](https://github.com/exercism/v3/issues/2208) | TBD |
+|
| [conditionals](https://github.com/exercism/python/blob/main/concepts/conditionals) |
| [Meltdown Mitigation ](https://github.com/exercism/python/tree/main/exercises/concept/meltdown-mitigation) | [`.meta`folder](https://github.com/exercism/python/tree/main/exercises/concept/meltdown-mitigation/.meta) | Full |
+|
| [comparisons](https://github.com/exercism/python/blob/main/concepts/comparisons) |
| [Black Jack](https://github.com/exercism/python/tree/main/exercises/concept/black-jack) | [`.meta`folder](https://github.com/exercism/python/tree/main/exercises/concept/black-jack/.meta) | Full |
+|
| [strings](https://github.com/exercism/python/blob/main/concepts/strings) |
| [Litte Sister's Vocab](https://github.com/exercism/python/tree/main/exercises/concept/little-sisters-vocab) | [`.meta`folder](https://github.com/exercism/python/tree/main/exercises/concept/little-sisters-vocab/.meta) | Full |
+|
| [string-methods](https://github.com/exercism/python/blob/main/concepts/string-methods) |
| [Litte Sister's Essay](https://github.com/exercism/python/tree/main/exercises/concept/little-sisters-essay) | [`.meta`folder](https://github.com/exercism/python/tree/main/exercises/concept/little-sisters-essay/.meta) | Full |
+|
| [string-formatting](https://github.com/exercism/python/blob/main/concepts/string-formatting) |
| [Pretty Leaflet ](https://github.com/exercism/python/tree/main/exercises/concept/pretty-leaflet) | [`.meta`folder](https://github.com/exercism/python/tree/main/exercises/concept/pretty-leaflet/.meta) | Full |
+|
| [lists](https://github.com/exercism/python/blob/main/concepts/lists) |
| [Card Games](https://github.com/exercism/python/tree/main/exercises/concept/card-games) | [`.meta`folder](https://github.com/exercism/python/tree/main/exercises/concept/card-games/.meta) | Full |
+|
| [list-methods](https://github.com/exercism/python/blob/main/concepts/list-methods) |
| [Chaitanas Colossal Coaster](https://github.com/exercism/python/tree/main/exercises/concept/chaitanas-colossal-coaster) | [`.meta`folder](https://github.com/exercism/python/tree/main/exercises/concept/chaitanas-colossal-coaster/.meta) | Full |
+|
| [loops](https://github.com/exercism/python/blob/main/concepts/loops) |
| [Making the Grade](https://github.com/exercism/python/tree/main/exercises/concept/making-the-grade) | [`.meta`folder](https://github.com/exercism/python/tree/main/exercises/concept/making-the-grade/.meta) | Full |
+|
| [tuples](https://github.com/exercism/python/blob/main/concepts/tuples) |
| [Tisbury Treasure Hunt](https://github.com/exercism/python/tree/main/exercises/concept/tisbury-treasure-hunt) | [`.meta`folder](https://github.com/exercism/python/tree/main/exercises/concept/tisbury-treasure-hunt/.meta) | Full |
+|
| [sequences](https://github.com/exercism/python/blob/main/concepts/sequences) |
| ~ | [#2290](https://github.com/exercism/python/issues/2290) | TBD |
+|
| [dicts](https://github.com/exercism/python/blob/main/concepts/dicts) |
| [Inventory Management](https://github.com/exercism/python/tree/main/exercises/concept/inventory-management) | [`.meta`folder](https://github.com/exercism/python/tree/main/exercises/concept/inventory-management) | Full |
+|
| [dict-methods](https://github.com/exercism/python/blob/main/concepts/dict-methods) |
| ~ | [#2348](https://github.com/exercism/python/issues/2348) | |
+|
| [sets](https://github.com/exercism/python/blob/main/concepts/sets) |
| [Cater Waiter ](https://github.com/exercism/python/tree/main/exercises/concept/cater-waiter) | [`.meta`folder](https://github.com/exercism/python/tree/main/exercises/concept/cater-waiter/.meta) | Full |
+|
| [list-comprehensions](https://github.com/exercism/python/blob/main/concepts/list-comprehensions) |
| ~ | [#2295](https://github.com/exercism/python/issues/2295) | |
+|
| [other-comprehensions](https://github.com/exercism/python/blob/main/concepts/other-comprehensions) |
| ~ | [#2294](https://github.com/exercism/python/issues/2294) | |
+|
| [classes](https://github.com/exercism/python/blob/main/concepts/classes) |
| [Ellen's Alien Game](https://github.com/exercism/python/tree/main/exercises/concept/ellens-alien-game) | [`.meta`folder](https://github.com/exercism/python/tree/main/exercises/concept/ellens-alien-game/.meta) | Minimal |
+|
| [generators](https://github.com/exercism/python/blob/main/concepts/generators) |
| Plane Tickets | [PR#2729](https://github.com/exercism/python/pull/2729)/[#2293](https://github.com/exercism/python/issues/2293) | Minimal |
+|
| [generator-expressions](https://github.com/exercism/python/blob/main/concepts/generator-expressions) |
| ~ | [#2292](https://github.com/exercism/python/issues/2292) | |
+|
| [iterators](https://github.com/exercism/python/blob/main/concepts/iterators) |
| ~ | [#2367](https://github.com/exercism/python/issues/2367) | TBD |
+|
| [functions](https://github.com/exercism/python/blob/main/concepts/functions) |
| ~ | [#2353](https://github.com/exercism/python/issues/2353) | |
+|
| [unpacking-and-multiple-assignment](https://github.com/exercism/python/blob/main/concepts/unpacking-and-multiple-assignment) |
| ~ | [#2360](https://github.com/exercism/python/issues/2360) | |
+|
| [raising-and-handling-errors](https://github.com/exercism/python/blob/main/concepts/raising-and-handling-errors) |
| ~ | TBD | |
+|
| [itertools](https://github.com/exercism/python/blob/main/concepts/itertools) |
| ~ | [#2368](https://github.com/exercism/python/issues/2368) | |
+|
| [with-statement](https://github.com/exercism/python/blob/main/concepts/with-statement) |
| ~ | [#2369](https://github.com/exercism/python/issues/2369) | |
+|
| [enums](https://github.com/exercism/python/blob/main/concepts/enums) |
| [Log Levels](https://github.com/exercism/python/tree/main/exercises/concept/log-levels) | [`.meta`folder](https://github.com/exercism/python/tree/main/exercises/concept/log-levels) | Minimal |
+|
| [none](https://github.com/exercism/python/blob/main/concepts/none) |
| [Restaurant Rozalynn](https://github.com/exercism/python/tree/main/exercises/concept/restaurant-rozalynn) | [`.meta`folder](https://github.com/exercism/python/tree/main/exercises/concept/restaurant-rozalynn/.meta) | Minimal |
+|
| [decorators](https://github.com/exercism/python/blob/main/concepts/decorators) |
| ~ | [#2356](https://github.com/exercism/python/issues/2356) | |
+|
| [rich-comparisons](https://github.com/exercism/python/blob/main/concepts/rich-comparisons) |
| ~ | [#2287](https://github.com/exercism/python/issues/2287) | |
+|
| [function-arguments](https://github.com/exercism/python/blob/main/concepts/function-arguments) |
| ~ | [#2354](https://github.com/exercism/python/issues/2354) | |
+|
| [class-customization](https://github.com/exercism/python/blob/main/concepts/class-customization) |
| ~ | [#2350](https://github.com/exercism/python/issues/2350) | |
+|
| [class-inheritance](https://github.com/exercism/python/blob/main/concepts/class-inheritance) |
| ~ | [#2351](https://github.com/exercism/python/issues/2351) | |
+|
| [user-defined-errors](https://github.com/exercism/python/blob/main/concepts/user-defined-errors) |
| ~ | TBD | |
+|
| [context-manager-customization](https://github.com/exercism/python/blob/main/concepts/context-manager-customization) |
| ~ | [#2370](https://github.com/exercism/python/issues/2370) | |
+|
| [higher-order-functions](https://github.com/exercism/python/blob/main/concepts/higher-order-functions) |
| ~ | [#2355](https://github.com/exercism/python/issues/2355) | |
+|
| [functional-tools](https://github.com/exercism/python/blob/main/concepts/functional-tools) |
| ~ | [#2359](https://github.com/exercism/python/issues/2359) | |
+|
| [functools](https://github.com/exercism/python/blob/main/concepts/functools) |
| ~ | [#2366](https://github.com/exercism/python/issues/2366) | |
+|
| [anonymous-functions](https://github.com/exercism/python/blob/main/concepts) |
| ~ | [#2357](https://github.com/exercism/python/issues/2357) | |
+|
| [descriptors](https://github.com/exercism/python/blob/main/concepts/descriptors) |
| ~ | [#2365](https://github.com/exercism/python/issues/2365) | |
+|
| [aliasing](https://github.com/exercism/python/blob/main/concepts) |
| ~ | TBD | |
+|
| [binary data](https://github.com/exercism/python/blob/main/concepts/binary-data) |
| ~ | TBD | |
+|
| [bitflags](https://github.com/exercism/python/blob/main/concepts/bitflags) |
| ~ | TBD | |
+|
| [bitwise-operators](https://github.com/exercism/python/blob/main/concepts/bitwise-operators) |
| ~ | TBD | |
+|
| [bytes](https://github.com/exercism/python/blob/main/concepts/bytes) |
| ~ | TBD | |
+|
| [class-composition](https://github.com/exercism/python/blob/main/concepts/class-composition) |
| ~ | [#2352](https://github.com/exercism/python/issues/2352) | |
+|
| [class-interfaces](https://github.com/exercism/python/blob/main/concepts/class-interfaces) |
| ~ | TBD | |
+|
| [collections](https://github.com/exercism/python/blob/main/concepts/collections) |
| ~ | TBD | |
+|
| [dataclasses-and-namedtuples](https://github.com/exercism/python/blob/main/concepts/dataclasses-and-namedtuples) |
| ~ | [#2361](https://github.com/exercism/python/issues/2361) | |
+|
| [import](https://github.com/exercism/python/blob/main/concepts/import) |
| ~ | ON HOLD | |
+|
| [memoryview](https://github.com/exercism/python/blob/main/concepts/memoryview) |
| ~ | TBD | |
+|
| [operator-overloading](https://github.com/exercism/python/blob/main/concepts/operator-overloading) |
| ~ | TBD | |
+|
| [regular-expressions](https://github.com/exercism/python/blob/main/concepts/regular-expressions) |
| ~ | TBD | |
+|
| [string-methods-splitting](https://github.com/exercism/python/blob/main/concepts/string-methods-splitting) |
| ~ | TBD | |
+|
| [testing](https://github.com/exercism/python/blob/main/concepts/testing) |
| ~ | TBD | |
+|
| [text-processing](https://github.com/exercism/python/blob/main/concepts/text-processing) |
| ~ | TBD | |
+|
| [type-hinting](https://github.com/exercism/python/blob/main/concepts/type-hinting) |
| ~ | TBD | |
+|
| [unicode-regular-expressions](https://github.com/exercism/python/blob/main/concepts/unicode-regular-expressions) |
| ~ | TBD | |
+|
| [walrus-operator](https://github.com/exercism/python/blob/main/concepts/walrus-operator) |
| ~ | TBD | |
-
## Concept Exercise Tree
```mermaid
From 1168afc639646525e3214fdf92233320250b9221 Mon Sep 17 00:00:00 2001
From: meatball <69751659+meatball133@users.noreply.github.com>
Date: Mon, 5 Dec 2022 19:37:16 +0100
Subject: [PATCH 184/826] Update track_exercises_overview.md
---
reference/track_exercises_overview.md | 240 +++++++++++++-------------
1 file changed, 120 insertions(+), 120 deletions(-)
diff --git a/reference/track_exercises_overview.md b/reference/track_exercises_overview.md
index 8b8fe4f62e..f7a0416803 100644
--- a/reference/track_exercises_overview.md
+++ b/reference/track_exercises_overview.md
@@ -11,126 +11,126 @@
Practice Exercises with Difficulty, Solutions, and Mentor Notes
-| Exercise | Difficulty | Solutions | prereqs | Practices | Mentor Notes | Jinja? |
-| ------------------------------------------------------------------------------------------------------------------------------------------ | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | ------ |
-| [**Hello World**](https://github.com/exercism/python/blob/main/exercises/practice/hello-world/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/hello-world/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/hello-world/solutions?passed_head_tests=true) | NONE | `basics` | | ✅ |
-| [Acronym](https://github.com/exercism/python/blob/main/exercises/practice/acronym/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/acronym/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/acronym/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L337) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L330) | [acronym](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/acronym/) | ✅ |
-| [Affine Cipher](https://github.com/exercism/python/blob/main/exercises/practice/affine-cipher/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/affine-cipher/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/affine-cipher/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1174) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1173) | | ✅ |
-| [All Your Base](https://github.com/exercism/python/blob/main/exercises/practice/all-your-base/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/all-your-base/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/all-your-base/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1394) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1393) | | ✅ |
-| [Allergies](https://github.com/exercism/python/blob/main/exercises/practice/allergies/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/allergies/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/allergies/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L701) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L700) | [allergies](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/allergies/) | ✅ |
-| [Alphametics](https://github.com/exercism/python/blob/main/exercises/practice/alphametics/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/alphametics/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/alphametics/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1935) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1934) | | ✅ |
-| [Anagram](https://github.com/exercism/python/blob/main/exercises/practice/anagram/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/anagram/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/anagram/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L577) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L576) | | ✅ |
-| [Armstrong Numbers](https://github.com/exercism/python/blob/main/exercises/practice/armstrong-numbers/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/armstrong-numbers/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/armstrong-numbers/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L512) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L511) | | ✅ |
-| [Atbash Cipher](https://github.com/exercism/python/blob/main/exercises/practice/atbash-cipher/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/atbash-cipher/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/atbash-cipher/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1102) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1101) | | ✅ |
-| [Bank Account](https://github.com/exercism/python/blob/main/exercises/practice/bank-account/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/bank-account/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/bank-account/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2207) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2206) | | ❌ |
-| [Binary Search Tree](https://github.com/exercism/python/blob/main/exercises/practice/binary-search-tree/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/binary-search-tree/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/binary-search-tree/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1157) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1156) | | ✅ |
-| [Binary Search](https://github.com/exercism/python/blob/main/exercises/practice/binary-search/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/binary-search/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/binary-search/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1192) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1191) | [binary-search](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/binary-search/) | ✅ |
-| [Bob](https://github.com/exercism/python/blob/main/exercises/practice/bob/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/bob/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/bob/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L715) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L714) | [bob](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/bob/) | ✅ |
-| [Book Store](https://github.com/exercism/python/blob/main/exercises/practice/book-store/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/book-store/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/book-store/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L445) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L437) | | ✅ |
-| [Bottle Song](https://github.com/exercism/python/blob/main/exercises/practice/bottle-song/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/bottle-song/.meta/example.py)┋[most⭐](https://exercism.org/tracks/python/exercises/bottle-song/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/main/config.json/#LC1012) | [config.json](https://github.com/exercism/python/blob/main/config.json#LC1012) | | ✅ |
-| [Bowling](https://github.com/exercism/python/blob/main/exercises/practice/bowling/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/bowling/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/bowling/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1553) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1552) | | ✅ |
-| [Change](https://github.com/exercism/python/blob/main/exercises/practice/change/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/change/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/change/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1412) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1411) | | ✅ |
-| [Circular Buffer](https://github.com/exercism/python/blob/main/exercises/practice/circular-buffer/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/circular-buffer/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/circular-buffer/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1475) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1469) | | ✅ |
-| [Clock](https://github.com/exercism/python/blob/main/exercises/practice/clock/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/clock/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/clock/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L394) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L389) | [clock](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/clock/) | ✅ |
-| [Collatz Conjecture](https://github.com/exercism/python/blob/main/exercises/practice/collatz-conjecture/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/collatz-conjecture/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/collatz-conjecture/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L593) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L592) | | ✅ |
-| [Complex Numbers](https://github.com/exercism/python/blob/main/exercises/practice/complex-numbers/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/complex-numbers/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/complex-numbers/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L799) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L792) | | ✅ |
-| [Connect](https://github.com/exercism/python/blob/main/exercises/practice/connect/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/connect/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/connect/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L960) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L959) | | ✅ |
-| [Crypto Square](https://github.com/exercism/python/blob/main/exercises/practice/crypto-square/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/crypto-square/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/crypto-square/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1263) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1262) | | ✅ |
-| [Darts](https://github.com/exercism/python/blob/main/exercises/practice/darts/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/darts/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/darts/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2199) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2198) | | ✅ |
-| [Diamond](https://github.com/exercism/python/blob/main/exercises/practice/diamond/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/diamond/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/diamond/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1696) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1695) | | ✅ |
-| [Difference Of Squares](https://github.com/exercism/python/blob/main/exercises/practice/difference-of-squares/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/difference-of-squares/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/difference-of-squares/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L601) | NONE | | ✅ |
-| [Diffie Hellman](https://github.com/exercism/python/blob/main/exercises/practice/diffie-hellman/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/diffie-hellman/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/diffie-hellman/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1956) | NONE | | ✅ |
-| [Dnd Character](https://github.com/exercism/python/blob/main/exercises/practice/dnd-character/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/dnd-character/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/dnd-character/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2078) | NONE | | ✅ |
-| [Dominoes](https://github.com/exercism/python/blob/main/exercises/practice/dominoes/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/dominoes/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/dominoes/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1767) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1755) | | ✅ |
-| [Dot Dsl](https://github.com/exercism/python/blob/main/exercises/practice/dot-dsl/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/dot-dsl/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/dot-dsl/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1434) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1427) | | ❌ |
-| [Etl](https://github.com/exercism/python/blob/main/exercises/practice/etl/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/etl/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/etl/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1118) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1117) | | ✅ |
-| [Flatten Array](https://github.com/exercism/python/blob/main/exercises/practice/flatten-array/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/flatten-array/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/flatten-array/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1126) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1125) | | ✅ |
-| [Food Chain](https://github.com/exercism/python/blob/main/exercises/practice/food-chain/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/food-chain/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/food-chain/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1737) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1731) | | ✅ |
-| [Forth](https://github.com/exercism/python/blob/main/exercises/practice/forth/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/forth/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/forth/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1571) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1570) | | ✅ |
-| [Gigasecond](https://github.com/exercism/python/blob/main/exercises/practice/gigasecond/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/gigasecond/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/gigasecond/solutions?passed_head_tests=true) | | NONE | | ✅ |
-| [Go Counting](https://github.com/exercism/python/blob/main/exercises/practice/go-counting/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/go-counting/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/go-counting/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L868) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L867) | | ✅ |
-| [Grade School](https://github.com/exercism/python/blob/main/exercises/practice/grade-school/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/grade-school/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/grade-school/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L364) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L363) | [grade-school](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/grade-school/) | ✅ |
-| [Grains](https://github.com/exercism/python/blob/main/exercises/practice/grains/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/grains/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/grains/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L678) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L677) | | ✅ |
-| [Grep](https://github.com/exercism/python/blob/main/exercises/practice/grep/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/grep/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/grep/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1536) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1528) | | ✅ |
-| [Hamming](https://github.com/exercism/python/blob/main/exercises/practice/hamming/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/hamming/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/hamming/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L259) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L254) | [hamming](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/hamming/) | ✅ |
-| [Hangman](https://github.com/exercism/python/blob/main/exercises/practice/hangman/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/hangman/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/hangman/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2221) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2220) | | ❌ |
-| [High Scores](https://github.com/exercism/python/blob/main/exercises/practice/high-scores/.docs/instructions.md) | 🔹 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/high-scores/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/high-scores/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L226) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L225) | [high-scores](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/high-scores/) | ✅ |
-| [House](https://github.com/exercism/python/blob/main/exercises/practice/house/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/house/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/house/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1279) | NONE | | ✅ |
-| [Isbn Verifier](https://github.com/exercism/python/blob/main/exercises/practice/isbn-verifier/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/isbn-verifier/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/isbn-verifier/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L609) | NONE | | ✅ |
-| [Isogram](https://github.com/exercism/python/blob/main/exercises/practice/isogram/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/isogram/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/isogram/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L273) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L272) | [isogram](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/isogram/) | ✅ |
-| [Kindergarten Garden](https://github.com/exercism/python/blob/main/exercises/practice/kindergarten-garden/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/kindergarten-garden/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/kindergarten-garden/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L350) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L344) | [kindergarten-garden](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/kindergarten-garden/) | ✅ |
-| [Knapsack](https://github.com/exercism/python/blob/main/exercises/practice/knapsack/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/knapsack/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/knapsack/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1453) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1452) | | ✅ |
-| [Largest Series Product](https://github.com/exercism/python/blob/main/exercises/practice/largest-series-product/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/largest-series-product/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/largest-series-product/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L945) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L939) | | ✅ |
-| [Leap](https://github.com/exercism/python/blob/main/exercises/practice/leap/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/leap/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/leap/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2103) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2102) | [leap](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/leap/) | ✅ |
-| [Ledger](https://github.com/exercism/python/blob/main/exercises/practice/ledger/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/ledger/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/ledger/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1590) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1589) | | ❌ |
-| [Linked List](https://github.com/exercism/python/blob/main/exercises/practice/linked-list/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/linked-list/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/linked-list/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1379) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1371) | | ❌ |
-| [List Ops](https://github.com/exercism/python/blob/main/exercises/practice/list-ops/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/list-ops/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/list-ops/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1294) | NONE | | ✅ |
-| [Luhn](https://github.com/exercism/python/blob/main/exercises/practice/luhn/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/luhn/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/luhn/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L372) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L371) | [luhn](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/luhn/) | ✅ |
-| [Markdown](https://github.com/exercism/python/blob/main/exercises/practice/markdown/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/markdown/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/markdown/solutions?passed_head_tests=true) | | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L401) | [markdown](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/markdown/) | ✅ |
-| [Matching Brackets](https://github.com/exercism/python/blob/main/exercises/practice/matching-brackets/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/matching-brackets/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/matching-brackets/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L728) | NONE | [matching-brackets](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/matching-brackets/) | ✅ |
-| [Matrix](https://github.com/exercism/python/blob/main/exercises/practice/matrix/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/matrix/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/matrix/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1714) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1713) | [matrix](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/matrix/) | ✅ |
-| [Meetup](https://github.com/exercism/python/blob/main/exercises/practice/meetup/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/meetup/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/meetup/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L818) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L812) | | ✅ |
-| [Minesweeper](https://github.com/exercism/python/blob/main/exercises/practice/minesweeper/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/minesweeper/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/minesweeper/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L981) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L980) | | ✅ |
-| [Nth Prime](https://github.com/exercism/python/blob/main/exercises/practice/nth-prime/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/nth-prime/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/nth-prime/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1814) | NONE | | ✅ |
-| [Ocr Numbers](https://github.com/exercism/python/blob/main/exercises/practice/ocr-numbers/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/ocr-numbers/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/ocr-numbers/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L997) | NONE | | ✅ |
-| [Paasio](https://github.com/exercism/python/blob/main/exercises/practice/paasio/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/paasio/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/paasio/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1917) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1906) | | ❌ |
-| [Palindrome Products](https://github.com/exercism/python/blob/main/exercises/practice/palindrome-products/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/palindrome-products/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/palindrome-products/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L626) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L625) | | ✅ |
-| [Pangram](https://github.com/exercism/python/blob/main/exercises/practice/pangram/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/pangram/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/pangram/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L463) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L462) | | ✅ |
-| [Perfect Numbers](https://github.com/exercism/python/blob/main/exercises/practice/perfect-numbers/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/perfect-numbers/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/perfect-numbers/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L527) | NONE | | ✅ |
-| [Phone Number](https://github.com/exercism/python/blob/main/exercises/practice/phone-number/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/phone-number/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/phone-number/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L547) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L542) | | ✅ |
-| [Pig Latin](https://github.com/exercism/python/blob/main/exercises/practice/pig-latin/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/pig-latin/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/pig-latin/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1832) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1831) | | ✅ |
-| [Poker](https://github.com/exercism/python/blob/main/exercises/practice/poker/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/poker/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/poker/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1016) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1015) | | ✅ |
-| [Pov](https://github.com/exercism/python/blob/main/exercises/practice/pov/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/pov/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/pov/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1677) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1676) | | ✅ |
-| [Prime Factors](https://github.com/exercism/python/blob/main/exercises/practice/prime-factors/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/prime-factors/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/prime-factors/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L686) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L685) | | ✅ |
-| [Protein Translation](https://github.com/exercism/python/blob/main/exercises/practice/protein-translation/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/protein-translation/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/protein-translation/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L496) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L495) | | ✅ |
-| [Proverb](https://github.com/exercism/python/blob/main/exercises/practice/proverb/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/proverb/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/proverb/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/main/config.json#LC661) | [config.json](https://github.com/exercism/python/blob/main/config.json#L661) | | ✅ |
-| [Pythagorean Triplet](https://github.com/exercism/python/blob/main/exercises/practice/pythagorean-triplet/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/pythagorean-triplet/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/pythagorean-triplet/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L745) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L744) | | ✅ |
-| [Queen Attack](https://github.com/exercism/python/blob/main/exercises/practice/queen-attack/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/queen-attack/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/queen-attack/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1302) | NONE | | ✅ |
-| [Rail Fence Cipher](https://github.com/exercism/python/blob/main/exercises/practice/rail-fence-cipher/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/rail-fence-cipher/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/rail-fence-cipher/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1849) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1848) | | ✅ |
-| [Raindrops](https://github.com/exercism/python/blob/main/exercises/practice/raindrops/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/raindrops/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/raindrops/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L210) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L209) | [raindrops](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/raindrops/) | ✅ |
-| [Rational Numbers](https://github.com/exercism/python/blob/main/exercises/practice/rational-numbers/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/rational-numbers/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/rational-numbers/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2240) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2239) | | ✅ |
-| [React](https://github.com/exercism/python/blob/main/exercises/practice/react/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/react/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/react/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L890) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L884) | | ❌ |
-| [Rectangles](https://github.com/exercism/python/blob/main/exercises/practice/rectangles/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/rectangles/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/rectangles/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1032) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1031) | | ✅ |
-| [Resistor Color Trio](https://github.com/exercism/python/blob/main/exercises/practice/resistor-color-trio/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/resistor-color-trio/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/resistor-color-trio/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/main/config.json#LC631) | [config.json](https://github.com/exercism/python/blob/main/config.json#L631) | | ✅ |
-| [Resistor Color Duo](https://github.com/exercism/python/blob/main/exercises/practice/resistor-color-duo/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/resistor-color-duo/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/resistor-color-duo/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2119) | NONE | | ✅ |
-| [Resistor Color](https://github.com/exercism/python/blob/main/exercises/practice/resistor-color/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/resistor-color/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/resistor-color/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2111) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2110) | | ✅ |
-| [Rest Api](https://github.com/exercism/python/blob/main/exercises/practice/rest-api/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/rest-api/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/rest-api/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1795) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1787) | | ✅ |
-| [Reverse String](https://github.com/exercism/python/blob/main/exercises/practice/reverse-string/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/reverse-string/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/reverse-string/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2133) | NONE | [reverse-string](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/reverse-string/) | ✅ |
-| [Rna Transcription](https://github.com/exercism/python/blob/main/exercises/practice/rna-transcription/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/rna-transcription/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/rna-transcription/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2149) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2148) | | ✅ |
-| [Robot Name](https://github.com/exercism/python/blob/main/exercises/practice/robot-name/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/robot-name/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/robot-name/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L479) | NONE | | ❌ |
-| [Robot Simulator](https://github.com/exercism/python/blob/main/exercises/practice/robot-simulator/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/robot-simulator/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/robot-simulator/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1324) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1315) | | ✅ |
-| [Roman Numerals](https://github.com/exercism/python/blob/main/exercises/practice/roman-numerals/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/roman-numerals/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/roman-numerals/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1340) | NONE | | ✅ |
-| [Rotational Cipher](https://github.com/exercism/python/blob/main/exercises/practice/rotational-cipher/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/rotational-cipher/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/rotational-cipher/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1209) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1208) | | ✅ |
-| [Run Length Encoding](https://github.com/exercism/python/blob/main/exercises/practice/run-length-encoding/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/run-length-encoding/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/run-length-encoding/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1493) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1492) | | ✅ |
-| [Saddle Points](https://github.com/exercism/python/blob/main/exercises/practice/saddle-points/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/saddle-points/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/saddle-points/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L649) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L643) | | ✅ |
-| [Satellite](https://github.com/exercism/python/blob/main/exercises/practice/satellite/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/satellite/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/satellite/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1640) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1634) | | ✅ |
-| [Say](https://github.com/exercism/python/blob/main/exercises/practice/say/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/say/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/say/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1052) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1051) | | ✅ |
-| [Scale Generator](https://github.com/exercism/python/blob/main/exercises/practice/scale-generator/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/scale-generator/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/scale-generator/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1084) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1083) | | ✅ |
-| [Scrabble Score](https://github.com/exercism/python/blob/main/exercises/practice/scrabble-score/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/scrabble-score/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/scrabble-score/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L316) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L315) | [scrabble-score](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/scrabble-score/) | ✅ |
-| [Secret Handshake](https://github.com/exercism/python/blob/main/exercises/practice/secret-handshake/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/secret-handshake/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/secret-handshake/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L924) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L923) | | ✅ |
-| [Series](https://github.com/exercism/python/blob/main/exercises/practice/series/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/series/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/series/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L562) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L561) | | ✅ |
-| [Sgf Parsing](https://github.com/exercism/python/blob/main/exercises/practice/sgf-parsing/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/sgf-parsing/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/sgf-parsing/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2261) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2255) | | ✅ |
-| [Sieve](https://github.com/exercism/python/blob/main/exercises/practice/sieve/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/sieve/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/sieve/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L836) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L835) | | ✅ |
-| [Simple Cipher](https://github.com/exercism/python/blob/main/exercises/practice/simple-cipher/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/simple-cipher/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/simple-cipher/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L761) | NONE | | ✅ |
-| [Simple Linked List](https://github.com/exercism/python/blob/main/exercises/practice/simple-linked-list/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/simple-linked-list/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/simple-linked-list/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1357) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1356) | | ❌ |
-| [Space Age](https://github.com/exercism/python/blob/main/exercises/practice/space-age/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/space-age/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/space-age/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2164) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2163) | | ✅ |
-| [Spiral Matrix](https://github.com/exercism/python/blob/main/exercises/practice/spiral-matrix/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/spiral-matrix/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/spiral-matrix/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1714) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1713) | | ✅ |
-| [Sublist](https://github.com/exercism/python/blob/main/exercises/practice/sublist/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/sublist/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/sublist/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1141) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1140) | | ✅ |
-| [Sum Of Multiples](https://github.com/exercism/python/blob/main/exercises/practice/sum-of-multiples/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/sum-of-multiples/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/sum-of-multiples/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L778) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L777) | [sum-of-multiples](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/sum-of-multiples/) | ✅ |
-| [Tournament](https://github.com/exercism/python/blob/main/exercises/practice/tournament/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/tournament/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/tournament/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L421) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L409) | | ✅ |
-| [Transpose](https://github.com/exercism/python/blob/main/exercises/practice/transpose/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/transpose/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/transpose/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1511) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1510) | | ✅ |
-| [Tree Building](https://github.com/exercism/python/blob/main/exercises/practice/tree-building/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/tree-building/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/tree-building/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L852) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L851) | | ❌ |
-| [Triangle](https://github.com/exercism/python/blob/main/exercises/practice/triangle/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/triangle/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/triangle/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L664) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L663) | | ✅ |
-| [Twelve Days](https://github.com/exercism/python/blob/main/exercises/practice/twelve-days/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/twelve-days/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/twelve-days/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L281) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L280) | [twelve-days](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/twelve-days/) | ✅ |
-| [Two Bucket](https://github.com/exercism/python/blob/main/exercises/practice/two-bucket/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/two-bucket/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/two-bucket/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1244) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1243) | | ✅ |
-| [Two Fer](https://github.com/exercism/python/blob/main/exercises/practice/two-fer/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/two-fer/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/two-fer/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L202) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L201) | [two-fer](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/two-fer/) | ✅ |
-| [Variable Length Quantity](https://github.com/exercism/python/blob/main/exercises/practice/variable-length-quantity/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/variable-length-quantity/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/variable-length-quantity/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1226) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1225) | | ✅ |
-| [Word Count](https://github.com/exercism/python/blob/main/exercises/practice/word-count/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/word-count/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/word-count/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L302) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L296) | [word-count](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/word-count/) | ✅ |
-| [Word Search](https://github.com/exercism/python/blob/main/exercises/practice/word-search/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/word-search/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/word-search/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1616) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1606) | | ✅ |
-| [Wordy](https://github.com/exercism/python/blob/main/exercises/practice/wordy/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/wordy/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/wordy/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1069) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1068) | | ✅ |
-| [Yacht](https://github.com/exercism/python/blob/main/exercises/practice/yacht/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/yacht/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/yacht/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2180) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2179) | | ✅ |
-| [Zebra Puzzle](https://github.com/exercism/python/blob/main/exercises/practice/zebra-puzzle/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/zebra-puzzle/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/zebra-puzzle/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1866) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1865) | | ✅ |
-| [Zipper](https://github.com/exercism/python/blob/main/exercises/practice/zipper/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/zipper/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/zipper/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1659) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1658) | | ✅ |
+| Exercise | Difficulty | Solutions | Prereqs | Practices | Mentor Notes | Jinja? | Approaches? |
+| ------------------------------------------------------------------------------------------------------------------------------------------ | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | ------------------------------------ | ----------- |
+| [**Hello World**](https://github.com/exercism/python/blob/main/exercises/practice/hello-world/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/hello-world/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/hello-world/solutions?passed_head_tests=true) | NONE | `basics` | | ✅ | ❌ |
+| [Acronym](https://github.com/exercism/python/blob/main/exercises/practice/acronym/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/acronym/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/acronym/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L337) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L330) | [acronym](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/acronym/) | ✅ | ❌ |
+| [Affine Cipher](https://github.com/exercism/python/blob/main/exercises/practice/affine-cipher/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/affine-cipher/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/affine-cipher/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1174) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1173) | | ✅ | ❌ |
+| [All Your Base](https://github.com/exercism/python/blob/main/exercises/practice/all-your-base/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/all-your-base/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/all-your-base/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1394) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1393) | | ✅ | ❌ |
+| [Allergies](https://github.com/exercism/python/blob/main/exercises/practice/allergies/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/allergies/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/allergies/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L701) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L700) | [allergies](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/allergies/) | ✅ | ❌ |
+| [Alphametics](https://github.com/exercism/python/blob/main/exercises/practice/alphametics/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/alphametics/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/alphametics/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1935) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1934) | | ✅ | ❌ |
+| [Anagram](https://github.com/exercism/python/blob/main/exercises/practice/anagram/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/anagram/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/anagram/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L577) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L576) | | ✅ | ❌ |
+| [Armstrong Numbers](https://github.com/exercism/python/blob/main/exercises/practice/armstrong-numbers/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/armstrong-numbers/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/armstrong-numbers/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L512) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L511) | | ✅ | ❌ |
+| [Atbash Cipher](https://github.com/exercism/python/blob/main/exercises/practice/atbash-cipher/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/atbash-cipher/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/atbash-cipher/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1102) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1101) | | ✅ | ❌ |
+| [Bank Account](https://github.com/exercism/python/blob/main/exercises/practice/bank-account/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/bank-account/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/bank-account/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2207) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2206) | | ❌ | ❌ |
+| [Binary Search Tree](https://github.com/exercism/python/blob/main/exercises/practice/binary-search-tree/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/binary-search-tree/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/binary-search-tree/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1157) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1156) | | ✅ | ❌ |
+| [Binary Search](https://github.com/exercism/python/blob/main/exercises/practice/binary-search/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/binary-search/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/binary-search/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1192) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1191) | [binary-search](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/binary-search/) | ✅ | ❌ |
+| [Bob](https://github.com/exercism/python/blob/main/exercises/practice/bob/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/bob/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/bob/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L715) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L714) | [bob](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/bob/) | ✅ | ✅ |
+| [Book Store](https://github.com/exercism/python/blob/main/exercises/practice/book-store/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/book-store/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/book-store/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L445) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L437) | | ✅ | ❌ |
+| [Bottle Song](https://github.com/exercism/python/blob/main/exercises/practice/bottle-song/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/bottle-song/.meta/example.py)┋[most⭐](https://exercism.org/tracks/python/exercises/bottle-song/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/main/config.json/#LC1012) | [config.json](https://github.com/exercism/python/blob/main/config.json#LC1012) | | ✅ | ❌ |
+| [Bowling](https://github.com/exercism/python/blob/main/exercises/practice/bowling/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/bowling/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/bowling/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1553) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1552) | | ✅ | ❌ |
+| [Change](https://github.com/exercism/python/blob/main/exercises/practice/change/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/change/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/change/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1412) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1411) | | ✅ | ❌ |
+| [Circular Buffer](https://github.com/exercism/python/blob/main/exercises/practice/circular-buffer/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/circular-buffer/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/circular-buffer/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1475) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1469) | | ✅ | ❌ |
+| [Clock](https://github.com/exercism/python/blob/main/exercises/practice/clock/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/clock/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/clock/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L394) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L389) | [clock](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/clock/) | ✅ | ❌ |
+| [Collatz Conjecture](https://github.com/exercism/python/blob/main/exercises/practice/collatz-conjecture/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/collatz-conjecture/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/collatz-conjecture/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L593) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L592) | | ✅ | ❌ |
+| [Complex Numbers](https://github.com/exercism/python/blob/main/exercises/practice/complex-numbers/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/complex-numbers/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/complex-numbers/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L799) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L792) | | ✅ | ❌ |
+| [Connect](https://github.com/exercism/python/blob/main/exercises/practice/connect/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/connect/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/connect/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L960) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L959) | | ✅ | ❌ |
+| [Crypto Square](https://github.com/exercism/python/blob/main/exercises/practice/crypto-square/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/crypto-square/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/crypto-square/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1263) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1262) | | ✅ | ❌ |
+| [Darts](https://github.com/exercism/python/blob/main/exercises/practice/darts/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/darts/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/darts/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2199) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2198) | | ✅ | ❌ |
+| [Diamond](https://github.com/exercism/python/blob/main/exercises/practice/diamond/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/diamond/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/diamond/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1696) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1695) | | ✅ | ❌ |
+| [Difference Of Squares](https://github.com/exercism/python/blob/main/exercises/practice/difference-of-squares/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/difference-of-squares/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/difference-of-squares/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L601) | NONE | | ✅ | ❌ |
+| [Diffie Hellman](https://github.com/exercism/python/blob/main/exercises/practice/diffie-hellman/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/diffie-hellman/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/diffie-hellman/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1956) | NONE | | ✅ | ❌ |
+| [Dnd Character](https://github.com/exercism/python/blob/main/exercises/practice/dnd-character/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/dnd-character/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/dnd-character/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2078) | NONE | | ✅ | ❌ |
+| [Dominoes](https://github.com/exercism/python/blob/main/exercises/practice/dominoes/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/dominoes/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/dominoes/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1767) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1755) | | ✅ | ❌ |
+| [Dot Dsl](https://github.com/exercism/python/blob/main/exercises/practice/dot-dsl/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/dot-dsl/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/dot-dsl/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1434) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1427) | | ❌ | ❌ |
+| [Etl](https://github.com/exercism/python/blob/main/exercises/practice/etl/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/etl/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/etl/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1118) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1117) | | ✅ | ❌ |
+| [Flatten Array](https://github.com/exercism/python/blob/main/exercises/practice/flatten-array/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/flatten-array/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/flatten-array/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1126) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1125) | | ✅ | ❌ |
+| [Food Chain](https://github.com/exercism/python/blob/main/exercises/practice/food-chain/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/food-chain/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/food-chain/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1737) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1731) | | ✅ | ❌ |
+| [Forth](https://github.com/exercism/python/blob/main/exercises/practice/forth/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/forth/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/forth/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1571) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1570) | | ✅ | ❌ |
+| [Gigasecond](https://github.com/exercism/python/blob/main/exercises/practice/gigasecond/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/gigasecond/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/gigasecond/solutions?passed_head_tests=true) | | NONE | | ✅ | ❌ |
+| [Go Counting](https://github.com/exercism/python/blob/main/exercises/practice/go-counting/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/go-counting/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/go-counting/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L868) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L867) | | ✅ | ❌ |
+| [Grade School](https://github.com/exercism/python/blob/main/exercises/practice/grade-school/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/grade-school/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/grade-school/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L364) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L363) | [grade-school](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/grade-school/) | ✅ | ❌ |
+| [Grains](https://github.com/exercism/python/blob/main/exercises/practice/grains/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/grains/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/grains/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L678) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L677) | | ✅ | ✅ |
+| [Grep](https://github.com/exercism/python/blob/main/exercises/practice/grep/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/grep/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/grep/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1536) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1528) | | ✅ | ❌ |
+| [Hamming](https://github.com/exercism/python/blob/main/exercises/practice/hamming/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/hamming/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/hamming/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L259) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L254) | [hamming](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/hamming/) | ✅ | ❌ |
+| [Hangman](https://github.com/exercism/python/blob/main/exercises/practice/hangman/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/hangman/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/hangman/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2221) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2220) | | ❌ | ❌ |
+| [High Scores](https://github.com/exercism/python/blob/main/exercises/practice/high-scores/.docs/instructions.md) | 🔹 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/high-scores/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/high-scores/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L226) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L225) | [high-scores](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/high-scores/) | ✅ | ❌ |
+| [House](https://github.com/exercism/python/blob/main/exercises/practice/house/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/house/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/house/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1279) | NONE | | ✅ | ❌ |
+| [Isbn Verifier](https://github.com/exercism/python/blob/main/exercises/practice/isbn-verifier/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/isbn-verifier/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/isbn-verifier/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L609) | NONE | | ✅ | ❌ |
+| [Isogram](https://github.com/exercism/python/blob/main/exercises/practice/isogram/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/isogram/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/isogram/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L273) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L272) | [isogram](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/isogram/) | ✅ | ✅ |
+| [Kindergarten Garden](https://github.com/exercism/python/blob/main/exercises/practice/kindergarten-garden/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/kindergarten-garden/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/kindergarten-garden/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L350) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L344) | [kindergarten-garden](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/kindergarten-garden/) | ✅ | ❌ |
+| [Knapsack](https://github.com/exercism/python/blob/main/exercises/practice/knapsack/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/knapsack/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/knapsack/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1453) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1452) | | ✅ | ❌ |
+| [Largest Series Product](https://github.com/exercism/python/blob/main/exercises/practice/largest-series-product/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/largest-series-product/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/largest-series-product/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L945) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L939) | | ✅ | ❌ |
+| [Leap](https://github.com/exercism/python/blob/main/exercises/practice/leap/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/leap/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/leap/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2103) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2102) | [leap](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/leap/) | ✅ | ✅ |
+| [Ledger](https://github.com/exercism/python/blob/main/exercises/practice/ledger/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/ledger/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/ledger/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1590) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1589) | | ❌ | ❌ |
+| [Linked List](https://github.com/exercism/python/blob/main/exercises/practice/linked-list/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/linked-list/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/linked-list/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1379) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1371) | | ❌ | ❌ |
+| [List Ops](https://github.com/exercism/python/blob/main/exercises/practice/list-ops/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/list-ops/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/list-ops/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1294) | NONE | | ✅ | ❌ |
+| [Luhn](https://github.com/exercism/python/blob/main/exercises/practice/luhn/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/luhn/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/luhn/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L372) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L371) | [luhn](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/luhn/) | ✅ | ❌ |
+| [Markdown](https://github.com/exercism/python/blob/main/exercises/practice/markdown/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/markdown/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/markdown/solutions?passed_head_tests=true) | | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L401) | [markdown](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/markdown/) | ✅ | ❌ |
+| [Matching Brackets](https://github.com/exercism/python/blob/main/exercises/practice/matching-brackets/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/matching-brackets/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/matching-brackets/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L728) | NONE | [matching-brackets](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/matching-brackets/) | ✅ | ❌ |
+| [Matrix](https://github.com/exercism/python/blob/main/exercises/practice/matrix/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/matrix/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/matrix/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1714) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1713) | [matrix](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/matrix/) | ✅ | ❌ |
+| [Meetup](https://github.com/exercism/python/blob/main/exercises/practice/meetup/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/meetup/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/meetup/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L818) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L812) | | ✅ | ❌ |
+| [Minesweeper](https://github.com/exercism/python/blob/main/exercises/practice/minesweeper/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/minesweeper/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/minesweeper/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L981) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L980) | | ✅ | ❌ |
+| [Nth Prime](https://github.com/exercism/python/blob/main/exercises/practice/nth-prime/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/nth-prime/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/nth-prime/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1814) | NONE | | ✅ | ❌ |
+| [Ocr Numbers](https://github.com/exercism/python/blob/main/exercises/practice/ocr-numbers/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/ocr-numbers/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/ocr-numbers/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L997) | NONE | | ✅ | ❌ |
+| [Paasio](https://github.com/exercism/python/blob/main/exercises/practice/paasio/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/paasio/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/paasio/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1917) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1906) | | ❌ | ❌ |
+| [Palindrome Products](https://github.com/exercism/python/blob/main/exercises/practice/palindrome-products/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/palindrome-products/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/palindrome-products/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L626) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L625) | | ✅ | ❌ |
+| [Pangram](https://github.com/exercism/python/blob/main/exercises/practice/pangram/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/pangram/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/pangram/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L463) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L462) | | ✅ | ✅ |
+| [Perfect Numbers](https://github.com/exercism/python/blob/main/exercises/practice/perfect-numbers/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/perfect-numbers/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/perfect-numbers/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L527) | NONE | | ✅ | ❌ |
+| [Phone Number](https://github.com/exercism/python/blob/main/exercises/practice/phone-number/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/phone-number/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/phone-number/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L547) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L542) | | ✅ | ❌ |
+| [Pig Latin](https://github.com/exercism/python/blob/main/exercises/practice/pig-latin/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/pig-latin/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/pig-latin/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1832) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1831) | | ✅ | ✅ |
+| [Poker](https://github.com/exercism/python/blob/main/exercises/practice/poker/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/poker/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/poker/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1016) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1015) | | ✅ | ❌ |
+| [Pov](https://github.com/exercism/python/blob/main/exercises/practice/pov/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/pov/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/pov/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1677) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1676) | | ✅ | ❌ |
+| [Prime Factors](https://github.com/exercism/python/blob/main/exercises/practice/prime-factors/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/prime-factors/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/prime-factors/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L686) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L685) | | ✅ | ❌ |
+| [Protein Translation](https://github.com/exercism/python/blob/main/exercises/practice/protein-translation/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/protein-translation/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/protein-translation/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L496) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L495) | | ✅ | ❌ |
+| [Proverb](https://github.com/exercism/python/blob/main/exercises/practice/proverb/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/proverb/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/proverb/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/main/config.json#LC661) | [config.json](https://github.com/exercism/python/blob/main/config.json#L661) | | ✅ | ❌ |
+| [Pythagorean Triplet](https://github.com/exercism/python/blob/main/exercises/practice/pythagorean-triplet/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/pythagorean-triplet/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/pythagorean-triplet/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L745) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L744) | | ✅ | ❌ |
+| [Queen Attack](https://github.com/exercism/python/blob/main/exercises/practice/queen-attack/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/queen-attack/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/queen-attack/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1302) | NONE | | ✅ | ❌ |
+| [Rail Fence Cipher](https://github.com/exercism/python/blob/main/exercises/practice/rail-fence-cipher/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/rail-fence-cipher/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/rail-fence-cipher/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1849) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1848) | | ✅ | ❌ |
+| [Raindrops](https://github.com/exercism/python/blob/main/exercises/practice/raindrops/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/raindrops/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/raindrops/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L210) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L209) | [raindrops](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/raindrops/) | ✅ | ❌ |
+| [Rational Numbers](https://github.com/exercism/python/blob/main/exercises/practice/rational-numbers/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/rational-numbers/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/rational-numbers/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2240) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2239) | | ✅ | ❌ |
+| [React](https://github.com/exercism/python/blob/main/exercises/practice/react/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/react/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/react/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L890) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L884) | | ❌ | ❌ |
+| [Rectangles](https://github.com/exercism/python/blob/main/exercises/practice/rectangles/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/rectangles/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/rectangles/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1032) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1031) | | ✅ | ❌ |
+| [Resistor Color Trio](https://github.com/exercism/python/blob/main/exercises/practice/resistor-color-trio/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/resistor-color-trio/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/resistor-color-trio/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/main/config.json#LC631) | [config.json](https://github.com/exercism/python/blob/main/config.json#L631) | | ✅ | ❌ |
+| [Resistor Color Duo](https://github.com/exercism/python/blob/main/exercises/practice/resistor-color-duo/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/resistor-color-duo/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/resistor-color-duo/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2119) | NONE | | ✅ | ❌ |
+| [Resistor Color](https://github.com/exercism/python/blob/main/exercises/practice/resistor-color/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/resistor-color/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/resistor-color/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2111) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2110) | | ✅ | ❌ |
+| [Rest Api](https://github.com/exercism/python/blob/main/exercises/practice/rest-api/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/rest-api/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/rest-api/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1795) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1787) | | ✅ | ❌ |
+| [Reverse String](https://github.com/exercism/python/blob/main/exercises/practice/reverse-string/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/reverse-string/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/reverse-string/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2133) | NONE | [reverse-string](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/reverse-string/) | ✅ | ❌ |
+| [Rna Transcription](https://github.com/exercism/python/blob/main/exercises/practice/rna-transcription/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/rna-transcription/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/rna-transcription/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2149) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2148) | | ✅ | ✅ |
+| [Robot Name](https://github.com/exercism/python/blob/main/exercises/practice/robot-name/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/robot-name/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/robot-name/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L479) | NONE | | ❌ | ❌ |
+| [Robot Simulator](https://github.com/exercism/python/blob/main/exercises/practice/robot-simulator/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/robot-simulator/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/robot-simulator/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1324) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1315) | | ✅ | ❌ |
+| [Roman Numerals](https://github.com/exercism/python/blob/main/exercises/practice/roman-numerals/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/roman-numerals/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/roman-numerals/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1340) | NONE | | ✅ | ❌ |
+| [Rotational Cipher](https://github.com/exercism/python/blob/main/exercises/practice/rotational-cipher/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/rotational-cipher/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/rotational-cipher/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1209) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1208) | | ✅ | ❌ |
+| [Run Length Encoding](https://github.com/exercism/python/blob/main/exercises/practice/run-length-encoding/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/run-length-encoding/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/run-length-encoding/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1493) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1492) | | ✅ | ❌ |
+| [Saddle Points](https://github.com/exercism/python/blob/main/exercises/practice/saddle-points/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/saddle-points/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/saddle-points/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L649) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L643) | | ✅ | ❌ |
+| [Satellite](https://github.com/exercism/python/blob/main/exercises/practice/satellite/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/satellite/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/satellite/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1640) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1634) | | ✅ | ❌ |
+| [Say](https://github.com/exercism/python/blob/main/exercises/practice/say/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/say/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/say/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1052) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1051) | | ✅ | ❌ |
+| [Scale Generator](https://github.com/exercism/python/blob/main/exercises/practice/scale-generator/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/scale-generator/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/scale-generator/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1084) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1083) | | ✅ | ❌ |
+| [Scrabble Score](https://github.com/exercism/python/blob/main/exercises/practice/scrabble-score/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/scrabble-score/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/scrabble-score/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L316) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L315) | [scrabble-score](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/scrabble-score/) | ✅ | ❌ |
+| [Secret Handshake](https://github.com/exercism/python/blob/main/exercises/practice/secret-handshake/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/secret-handshake/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/secret-handshake/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L924) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L923) | | ✅ | ❌ |
+| [Series](https://github.com/exercism/python/blob/main/exercises/practice/series/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/series/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/series/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L562) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L561) | | ✅ | ❌ |
+| [Sgf Parsing](https://github.com/exercism/python/blob/main/exercises/practice/sgf-parsing/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/sgf-parsing/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/sgf-parsing/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2261) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2255) | | ✅ | ❌ |
+| [Sieve](https://github.com/exercism/python/blob/main/exercises/practice/sieve/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/sieve/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/sieve/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L836) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L835) | | ✅ | ❌ |
+| [Simple Cipher](https://github.com/exercism/python/blob/main/exercises/practice/simple-cipher/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/simple-cipher/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/simple-cipher/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L761) | NONE | | ✅ | ❌ |
+| [Simple Linked List](https://github.com/exercism/python/blob/main/exercises/practice/simple-linked-list/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/simple-linked-list/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/simple-linked-list/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1357) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1356) | | ❌ | ❌ |
+| [Space Age](https://github.com/exercism/python/blob/main/exercises/practice/space-age/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/space-age/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/space-age/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2164) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2163) | | ✅ | ❌ |
+| [Spiral Matrix](https://github.com/exercism/python/blob/main/exercises/practice/spiral-matrix/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/spiral-matrix/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/spiral-matrix/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1714) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1713) | | ✅ | ❌ |
+| [Sublist](https://github.com/exercism/python/blob/main/exercises/practice/sublist/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/sublist/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/sublist/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1141) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1140) | | ✅ | ❌ |
+| [Sum Of Multiples](https://github.com/exercism/python/blob/main/exercises/practice/sum-of-multiples/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/sum-of-multiples/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/sum-of-multiples/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L778) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L777) | [sum-of-multiples](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/sum-of-multiples/) | ✅ | ❌ |
+| [Tournament](https://github.com/exercism/python/blob/main/exercises/practice/tournament/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/tournament/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/tournament/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L421) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L409) | | ✅ | ❌ |
+| [Transpose](https://github.com/exercism/python/blob/main/exercises/practice/transpose/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/transpose/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/transpose/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1511) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1510) | | ✅ | ❌ |
+| [Tree Building](https://github.com/exercism/python/blob/main/exercises/practice/tree-building/.docs/instructions.md) | 🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/tree-building/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/tree-building/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L852) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L851) | | ❌ | ❌ |
+| [Triangle](https://github.com/exercism/python/blob/main/exercises/practice/triangle/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/triangle/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/triangle/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L664) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L663) | | ✅ | ❌ |
+| [Twelve Days](https://github.com/exercism/python/blob/main/exercises/practice/twelve-days/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/twelve-days/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/twelve-days/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L281) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L280) | [twelve-days](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/twelve-days/) | ✅ | ❌ |
+| [Two Bucket](https://github.com/exercism/python/blob/main/exercises/practice/two-bucket/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/two-bucket/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/two-bucket/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1244) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1243) | | ✅ | ❌ |
+| [Two Fer](https://github.com/exercism/python/blob/main/exercises/practice/two-fer/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/two-fer/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/two-fer/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L202) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L201) | [two-fer](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/two-fer/) | ✅ | ❌ |
+| [Variable Length Quantity](https://github.com/exercism/python/blob/main/exercises/practice/variable-length-quantity/.docs/instructions.md) | 🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/variable-length-quantity/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/variable-length-quantity/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1226) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1225) | | ✅ | ❌ |
+| [Word Count](https://github.com/exercism/python/blob/main/exercises/practice/word-count/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/word-count/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/word-count/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L302) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L296) | [word-count](https://github.com/exercism/website-copy/tree/main/tracks/python/exercises/word-count/) | ✅ | ❌ |
+| [Word Search](https://github.com/exercism/python/blob/main/exercises/practice/word-search/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/word-search/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/word-search/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1616) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1606) | | ✅ | ❌ |
+| [Wordy](https://github.com/exercism/python/blob/main/exercises/practice/wordy/.docs/instructions.md) | 🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/wordy/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/wordy/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1069) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1068) | | ✅ | ✅ |
+| [Yacht](https://github.com/exercism/python/blob/main/exercises/practice/yacht/.docs/instructions.md) | 🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/yacht/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/yacht/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2180) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L2179) | | ✅ | ❌ |
+| [Zebra Puzzle](https://github.com/exercism/python/blob/main/exercises/practice/zebra-puzzle/.docs/instructions.md) | 🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/zebra-puzzle/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/zebra-puzzle/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1866) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1865) | | ✅ | ❌ |
+| [Zipper](https://github.com/exercism/python/blob/main/exercises/practice/zipper/.docs/instructions.md) | 🔹🔹🔹🔹🔹🔹 | [example](https://github.com/exercism/python/blob/main/exercises/practice/zipper/.meta/example.py)┋[most⭐](https://exercism.io/tracks/python/exercises/zipper/solutions?passed_head_tests=true) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1659) | [config.json](https://github.com/exercism/python/blob/64396fd483c6c6770c1313b71cb4d972e5ab9819/config.json#L1658) | | ✅ | ❌ |
From eb69066cde65f84b8b18055023fd87f53ac47c7c Mon Sep 17 00:00:00 2001
From: Meatball
Date: Mon, 5 Dec 2022 20:51:36 +0100
Subject: [PATCH 185/826] Added killer-sudoku-helper
---
config.json | 16 +++++
.../.docs/instructions.md | 63 +++++++++++++++++++
.../killer-sudoku-helper/.meta/config.json | 21 +++++++
.../killer-sudoku-helper/.meta/example.py | 10 +++
.../killer-sudoku-helper/.meta/template.j2 | 19 ++++++
.../killer-sudoku-helper/.meta/tests.toml | 49 +++++++++++++++
.../killer_sudoku_helper.py | 13 ++++
.../killer_sudoku_helper_test.py | 48 ++++++++++++++
8 files changed, 239 insertions(+)
create mode 100644 exercises/practice/killer-sudoku-helper/.docs/instructions.md
create mode 100644 exercises/practice/killer-sudoku-helper/.meta/config.json
create mode 100644 exercises/practice/killer-sudoku-helper/.meta/example.py
create mode 100644 exercises/practice/killer-sudoku-helper/.meta/template.j2
create mode 100644 exercises/practice/killer-sudoku-helper/.meta/tests.toml
create mode 100644 exercises/practice/killer-sudoku-helper/killer_sudoku_helper.py
create mode 100644 exercises/practice/killer-sudoku-helper/killer_sudoku_helper_test.py
diff --git a/config.json b/config.json
index b2a3c8ebc3..748c14b105 100644
--- a/config.json
+++ b/config.json
@@ -1005,6 +1005,22 @@
],
"difficulty": 3
},
+ {
+ "slug": "killer-sudoku-helper",
+ "name": "Killer Sudoku Helper",
+ "uuid": "7b16fc93-791b-42a9-8aae-1f78fef2f2f3",
+ "practices": ["list-comprehensions"],
+ "prerequisites": [
+ "conditionals",
+ "lists",
+ "list-methods",
+ "loops",
+ "numbers",
+ "strings",
+ "string-methods"
+ ],
+ "difficulty": 3
+ },
{
"slug": "bottle-song",
"name": "Bottle Song",
diff --git a/exercises/practice/killer-sudoku-helper/.docs/instructions.md b/exercises/practice/killer-sudoku-helper/.docs/instructions.md
new file mode 100644
index 0000000000..93469a09cc
--- /dev/null
+++ b/exercises/practice/killer-sudoku-helper/.docs/instructions.md
@@ -0,0 +1,63 @@
+# Instructions
+
+A friend of yours is learning how to solve Killer Sudokus (rules below) but struggling to figure out which digits can go in a cage.
+They ask you to help them out by writing a small program that lists all valid combinations for a given cage, and any constraints that affect the cage.
+
+To make the output of your program easy to read, the combinations it returns must be sorted.
+
+## Killer Sudoku Rules
+
+- [Standard Sudoku rules][sudoku-rules] apply.
+- The digits in a cage, usually marked by a dotted line, add up to the small number given in the corner of the cage.
+- A digit may only occur once in a cage.
+
+For a more detailed explanation, check out [this guide][killer-guide].
+
+## Example 1: Cage with only 1 possible combination
+
+In a 3-digit cage with a sum of 7, there is only one valid combination: 124.
+
+- 1 + 2 + 4 = 7
+- Any other combination that adds up to 7, e.g. 232, would violate the rule of not repeating digits within a cage.
+
+![Sudoku grid, with three killer cages that are marked as grouped together. The first killer cage is in the 3×3 box in the top left corner of the grid. The middle column of that box forms the cage, with the followings cells from top to bottom: first cell contains a 1 and a pencil mark of 7, indicating a cage sum of 7, second cell contains a 2, third cell contains a 5. The numbers are highlighted in red to indicate a mistake. The second killer cage is in the central 3×3 box of the grid. The middle column of that box forms the cage, with the followings cells from top to bottom: first cell contains a 1 and a pencil mark of 7, indicating a cage sum of 7, second cell contains a 2, third cell contains a 4. None of the numbers in this cage are highlighted and therefore don't contain any mistakes. The third killer cage follows the outside corner of the central 3×3 box of the grid. It is made up of the following three cells: the top left cell of the cage contains a 2, highlighted in red, and a cage sum of 7. The top right cell of the cage contains a 3. The bottom right cell of the cage contains a 2, highlighted in red. All other cells are empty.][one-solution-img]
+
+## Example 2: Cage with several combinations
+
+In a 2-digit cage with a sum 10, there are 4 possible combinations:
+
+- 19
+- 28
+- 37
+- 46
+
+![Sudoku grid, all squares empty except for the middle column, column 5, which has 8 rows filled. Each continguous two rows form a killer cage and are marked as grouped together. From top to bottom: first group is a cell with value 1 and a pencil mark indicating a cage sum of 10, cell with value 9. Second group is a cell with value 2 and a pencil mark of 10, cell with value 8. Third group is a cell with value 3 and a pencil mark of 10, cell with value 7. Fourth group is a cell with value 4 and a pencil mark of 10, cell with value 6. The last cell in the column is empty.][four-solutions-img]
+
+## Example 3: Cage with several combinations that is restricted
+
+In a 2-digit cage with a sum 10, where the column already contains a 1 and a 4, there are 2 possible combinations:
+
+- 28
+- 37
+
+19 and 46 are not possible due to the 1 and 4 in the column according to standard Sudoku rules.
+
+![Sudoku grid, all squares empty except for the middle column, column 5, which has 8 rows filled. The first row contains a 4, the second is empty, and the third contains a 1. The 1 is highlighted in red to indicate a mistake. The last 6 rows in the column form killer cages of two cells each. From top to bottom: first group is a cell with value 2 and a pencil mark indicating a cage sum of 10, cell with value 8. Second group is a cell with value 3 and a pencil mark of 10, cell with value 7. Third group is a cell with value 1, highlighted in red, and a pencil mark of 10, cell with value 9.][not-possible-img]
+
+## Trying it yourself
+
+If you want to give an approachable Killer Sudoku a go, you can try out [this puzzle][clover-puzzle] by Clover, featured by [Mark Goodliffe on Cracking The Cryptic on the 21st of June 2021][goodliffe-video].
+
+You can also find Killer Sudokus in varying difficulty in numerous newspapers, as well as Sudoku apps, books and websites.
+
+## Credit
+
+The screenshots above have been generated using [F-Puzzles.com](https://www.f-puzzles.com/), a Puzzle Setting Tool by Eric Fox.
+
+[sudoku-rules]: https://masteringsudoku.com/sudoku-rules-beginners/
+[killer-guide]: https://masteringsudoku.com/killer-sudoku/
+[one-solution-img]: https://media.githubusercontent.com/media/exercism/v3-files/main/julia/killer-sudoku-helper/example1.png
+[four-solutions-img]: https://media.githubusercontent.com/media/exercism/v3-files/main/julia/killer-sudoku-helper/example2.png
+[not-possible-img]: https://media.githubusercontent.com/media/exercism/v3-files/main/julia/killer-sudoku-helper/example3.png
+[clover-puzzle]: https://app.crackingthecryptic.com/sudoku/HqTBn3Pr6R
+[goodliffe-video]: https://youtu.be/c_NjEbFEeW0?t=1180
diff --git a/exercises/practice/killer-sudoku-helper/.meta/config.json b/exercises/practice/killer-sudoku-helper/.meta/config.json
new file mode 100644
index 0000000000..eb5a6f84bf
--- /dev/null
+++ b/exercises/practice/killer-sudoku-helper/.meta/config.json
@@ -0,0 +1,21 @@
+{
+ "authors": [
+ "meatball133",
+ "Bethanyg"
+ ],
+ "contributors": [],
+ "files": {
+ "solution": [
+ "killer_sudoku_helper.py"
+ ],
+ "test": [
+ "killer_sudoku_helper_test.py"
+ ],
+ "example": [
+ ".meta/example.py"
+ ]
+ },
+ "blurb": "Write a tool that makes it easier to solve Killer Sudokus",
+ "source": "Created by Sascha Mann, Jeremy Walker, and BethanyG for the Julia track on Exercism.",
+ "source_url": "https://github.com/exercism/julia/pull/413"
+}
diff --git a/exercises/practice/killer-sudoku-helper/.meta/example.py b/exercises/practice/killer-sudoku-helper/.meta/example.py
new file mode 100644
index 0000000000..ba42d25f7d
--- /dev/null
+++ b/exercises/practice/killer-sudoku-helper/.meta/example.py
@@ -0,0 +1,10 @@
+import itertools
+
+def combinations(target, size, exclude):
+ possible = [i for i in range(1, target) if i not in exclude]
+ result = [seq for i in range(len(possible), 0, -1)
+ for seq in itertools.combinations(possible, i)
+ if sum(seq) == target]
+
+
+ return result
diff --git a/exercises/practice/killer-sudoku-helper/.meta/template.j2 b/exercises/practice/killer-sudoku-helper/.meta/template.j2
new file mode 100644
index 0000000000..c107933784
--- /dev/null
+++ b/exercises/practice/killer-sudoku-helper/.meta/template.j2
@@ -0,0 +1,19 @@
+{%- import "generator_macros.j2" as macros with context -%}
+{% macro test_case(case) -%}
+ {%- set input = case["input"] -%}
+ def test_{{ case["description"] | to_snake }}(self):
+ self.assertEqual({{ case["property"] | to_snake }}(
+ {{ case["input"]["cage"]["sum"] }},
+ {{ case["input"]["cage"]["size"] }},
+ {{ case["input"]["cage"]["exclude"] }}),
+ {{ case["expected"] }})
+{%- endmacro %}
+{{ macros.header()}}
+
+class {{ exercise | camel_case }}Test(unittest.TestCase):
+ {% for case in cases[0]["cases"] -%}
+ {{ test_case(case) }}
+ {% endfor %}
+ {% for case in cases[1:] -%}
+ {{ test_case(case) }}
+ {% endfor %}
diff --git a/exercises/practice/killer-sudoku-helper/.meta/tests.toml b/exercises/practice/killer-sudoku-helper/.meta/tests.toml
new file mode 100644
index 0000000000..19c23e8a92
--- /dev/null
+++ b/exercises/practice/killer-sudoku-helper/.meta/tests.toml
@@ -0,0 +1,49 @@
+# This is an auto-generated file.
+#
+# Regenerating this file via `configlet sync` will:
+# - Recreate every `description` key/value pair
+# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications
+# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion)
+# - Preserve any other key/value pair
+#
+# As user-added comments (using the # character) will be removed when this file
+# is regenerated, comments can be added via a `comment` key.
+
+[2aaa8f13-11b5-4054-b95c-a906e4d79fb6]
+description = "Trivial 1-digit cages -> 1"
+
+[4645da19-9fdd-4087-a910-a6ed66823563]
+description = "Trivial 1-digit cages -> 2"
+
+[07cfc704-f8aa-41b2-8f9a-cbefb674cb48]
+description = "Trivial 1-digit cages -> 3"
+
+[22b8b2ba-c4fd-40b3-b1bf-40aa5e7b5f24]
+description = "Trivial 1-digit cages -> 4"
+
+[b75d16e2-ff9b-464d-8578-71f73094cea7]
+description = "Trivial 1-digit cages -> 5"
+
+[bcbf5afc-4c89-4ff6-9357-07ab4d42788f]
+description = "Trivial 1-digit cages -> 6"
+
+[511b3bf8-186f-4e35-844f-c804d86f4a7a]
+description = "Trivial 1-digit cages -> 7"
+
+[bd09a60d-3aca-43bd-b6aa-6ccad01bedda]
+description = "Trivial 1-digit cages -> 8"
+
+[9b539f27-44ea-4ff8-bd3d-c7e136bee677]
+description = "Trivial 1-digit cages -> 9"
+
+[0a8b2078-b3a4-4dbd-be0d-b180f503d5c3]
+description = "Cage with sum 45 contains all digits 1:9"
+
+[2635d7c9-c716-4da1-84f1-c96e03900142]
+description = "Cage with only 1 possible combination"
+
+[a5bde743-e3a2-4a0c-8aac-e64fceea4228]
+description = "Cage with several combinations"
+
+[dfbf411c-737d-465a-a873-ca556360c274]
+description = "Cage with several combinations that is restricted"
diff --git a/exercises/practice/killer-sudoku-helper/killer_sudoku_helper.py b/exercises/practice/killer-sudoku-helper/killer_sudoku_helper.py
new file mode 100644
index 0000000000..f5103b776e
--- /dev/null
+++ b/exercises/practice/killer-sudoku-helper/killer_sudoku_helper.py
@@ -0,0 +1,13 @@
+import itertools
+
+def combinations(target, size, exclude):
+ result = []
+ possible = [i for i in range(1, target) if i not in exclude]
+ if size == 1:
+ return [[target]]
+ else:
+ for i in range(len(possible), 0, -1):
+ for seq in itertools.combinations(possible, i):
+ if sum(seq) == target and len(seq) == size:
+ result.append(list(seq))
+ return result
diff --git a/exercises/practice/killer-sudoku-helper/killer_sudoku_helper_test.py b/exercises/practice/killer-sudoku-helper/killer_sudoku_helper_test.py
new file mode 100644
index 0000000000..b573205465
--- /dev/null
+++ b/exercises/practice/killer-sudoku-helper/killer_sudoku_helper_test.py
@@ -0,0 +1,48 @@
+import unittest
+
+from killer_sudoku_helper import (
+ combinations,
+)
+
+# Tests adapted from `problem-specifications//canonical-data.json`
+
+
+class KillerSudokuHelperTest(unittest.TestCase):
+ def test_1(self):
+ self.assertEqual(combinations(1, 1, []), [[1]])
+
+ def test_2(self):
+ self.assertEqual(combinations(2, 1, []), [[2]])
+
+ def test_3(self):
+ self.assertEqual(combinations(3, 1, []), [[3]])
+
+ def test_4(self):
+ self.assertEqual(combinations(4, 1, []), [[4]])
+
+ def test_5(self):
+ self.assertEqual(combinations(5, 1, []), [[5]])
+
+ def test_6(self):
+ self.assertEqual(combinations(6, 1, []), [[6]])
+
+ def test_7(self):
+ self.assertEqual(combinations(7, 1, []), [[7]])
+
+ def test_8(self):
+ self.assertEqual(combinations(8, 1, []), [[8]])
+
+ def test_9(self):
+ self.assertEqual(combinations(9, 1, []), [[9]])
+
+ def test_cage_with_sum_45_contains_all_digits_1_9(self):
+ self.assertEqual(combinations(45, 9, []), [[1, 2, 3, 4, 5, 6, 7, 8, 9]])
+
+ def test_cage_with_only_1_possible_combination(self):
+ self.assertEqual(combinations(7, 3, []), [[1, 2, 4]])
+
+ def test_cage_with_several_combinations(self):
+ self.assertEqual(combinations(10, 2, []), [[1, 9], [2, 8], [3, 7], [4, 6]])
+
+ def test_cage_with_several_combinations_that_is_restricted(self):
+ self.assertEqual(combinations(10, 2, [1, 4]), [[2, 8], [3, 7]])
From 14545d736f29c204fe858850d8ebaec7e0857061 Mon Sep 17 00:00:00 2001
From: Meatball
Date: Mon, 5 Dec 2022 20:54:19 +0100
Subject: [PATCH 186/826] fixes
---
.../practice/killer-sudoku-helper/.meta/example.py | 13 ++++++++-----
.../killer-sudoku-helper/killer_sudoku_helper.py | 13 +------------
2 files changed, 9 insertions(+), 17 deletions(-)
diff --git a/exercises/practice/killer-sudoku-helper/.meta/example.py b/exercises/practice/killer-sudoku-helper/.meta/example.py
index ba42d25f7d..f5103b776e 100644
--- a/exercises/practice/killer-sudoku-helper/.meta/example.py
+++ b/exercises/practice/killer-sudoku-helper/.meta/example.py
@@ -1,10 +1,13 @@
import itertools
def combinations(target, size, exclude):
+ result = []
possible = [i for i in range(1, target) if i not in exclude]
- result = [seq for i in range(len(possible), 0, -1)
- for seq in itertools.combinations(possible, i)
- if sum(seq) == target]
-
-
+ if size == 1:
+ return [[target]]
+ else:
+ for i in range(len(possible), 0, -1):
+ for seq in itertools.combinations(possible, i):
+ if sum(seq) == target and len(seq) == size:
+ result.append(list(seq))
return result
diff --git a/exercises/practice/killer-sudoku-helper/killer_sudoku_helper.py b/exercises/practice/killer-sudoku-helper/killer_sudoku_helper.py
index f5103b776e..03632d749c 100644
--- a/exercises/practice/killer-sudoku-helper/killer_sudoku_helper.py
+++ b/exercises/practice/killer-sudoku-helper/killer_sudoku_helper.py
@@ -1,13 +1,2 @@
-import itertools
-
def combinations(target, size, exclude):
- result = []
- possible = [i for i in range(1, target) if i not in exclude]
- if size == 1:
- return [[target]]
- else:
- for i in range(len(possible), 0, -1):
- for seq in itertools.combinations(possible, i):
- if sum(seq) == target and len(seq) == size:
- result.append(list(seq))
- return result
+ pass
From 2c102dcef1e0a1325d188c08b0f1db827e135075 Mon Sep 17 00:00:00 2001
From: Meatball
Date: Mon, 5 Dec 2022 21:26:06 +0100
Subject: [PATCH 187/826] Fix
---
.../killer-sudoku-helper/killer_sudoku_helper.py | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/exercises/practice/killer-sudoku-helper/killer_sudoku_helper.py b/exercises/practice/killer-sudoku-helper/killer_sudoku_helper.py
index 03632d749c..ba6e873ab2 100644
--- a/exercises/practice/killer-sudoku-helper/killer_sudoku_helper.py
+++ b/exercises/practice/killer-sudoku-helper/killer_sudoku_helper.py
@@ -1,2 +1,13 @@
+import itertools
+
def combinations(target, size, exclude):
- pass
+ result = []
+ if size == 1:
+ return [[target]]
+ else:
+ possible = [index for index in range(1, int((target ** 2 / size) ** 0.6)) if index not in exclude]
+ for index in range(len(possible), 0, -1):
+ for seq in itertools.combinations(possible, index):
+ if sum(seq) == target and len(seq) == size:
+ result.append(list(seq))
+ return result
From dcfe63766ccb15f15bfba09d47b964cbbda9258e Mon Sep 17 00:00:00 2001
From: Meatball
Date: Mon, 5 Dec 2022 21:28:16 +0100
Subject: [PATCH 188/826] Fixes
---
.../practice/killer-sudoku-helper/.meta/example.py | 6 +++---
.../killer-sudoku-helper/killer_sudoku_helper.py | 13 +------------
2 files changed, 4 insertions(+), 15 deletions(-)
diff --git a/exercises/practice/killer-sudoku-helper/.meta/example.py b/exercises/practice/killer-sudoku-helper/.meta/example.py
index f5103b776e..ba6e873ab2 100644
--- a/exercises/practice/killer-sudoku-helper/.meta/example.py
+++ b/exercises/practice/killer-sudoku-helper/.meta/example.py
@@ -2,12 +2,12 @@
def combinations(target, size, exclude):
result = []
- possible = [i for i in range(1, target) if i not in exclude]
if size == 1:
return [[target]]
else:
- for i in range(len(possible), 0, -1):
- for seq in itertools.combinations(possible, i):
+ possible = [index for index in range(1, int((target ** 2 / size) ** 0.6)) if index not in exclude]
+ for index in range(len(possible), 0, -1):
+ for seq in itertools.combinations(possible, index):
if sum(seq) == target and len(seq) == size:
result.append(list(seq))
return result
diff --git a/exercises/practice/killer-sudoku-helper/killer_sudoku_helper.py b/exercises/practice/killer-sudoku-helper/killer_sudoku_helper.py
index ba6e873ab2..03632d749c 100644
--- a/exercises/practice/killer-sudoku-helper/killer_sudoku_helper.py
+++ b/exercises/practice/killer-sudoku-helper/killer_sudoku_helper.py
@@ -1,13 +1,2 @@
-import itertools
-
def combinations(target, size, exclude):
- result = []
- if size == 1:
- return [[target]]
- else:
- possible = [index for index in range(1, int((target ** 2 / size) ** 0.6)) if index not in exclude]
- for index in range(len(possible), 0, -1):
- for seq in itertools.combinations(possible, index):
- if sum(seq) == target and len(seq) == size:
- result.append(list(seq))
- return result
+ pass
From 3e05a50b2763039cff72e1b938c8c9908d1290f4 Mon Sep 17 00:00:00 2001
From: Meatball
Date: Mon, 5 Dec 2022 21:32:42 +0100
Subject: [PATCH 189/826] Changed to 4 as difficutly
---
config.json | 32 ++++++++++++++++----------------
1 file changed, 16 insertions(+), 16 deletions(-)
diff --git a/config.json b/config.json
index 748c14b105..f267cbd83a 100644
--- a/config.json
+++ b/config.json
@@ -1005,22 +1005,6 @@
],
"difficulty": 3
},
- {
- "slug": "killer-sudoku-helper",
- "name": "Killer Sudoku Helper",
- "uuid": "7b16fc93-791b-42a9-8aae-1f78fef2f2f3",
- "practices": ["list-comprehensions"],
- "prerequisites": [
- "conditionals",
- "lists",
- "list-methods",
- "loops",
- "numbers",
- "strings",
- "string-methods"
- ],
- "difficulty": 3
- },
{
"slug": "bottle-song",
"name": "Bottle Song",
@@ -1359,6 +1343,22 @@
],
"difficulty": 4
},
+ {
+ "slug": "killer-sudoku-helper",
+ "name": "Killer Sudoku Helper",
+ "uuid": "7b16fc93-791b-42a9-8aae-1f78fef2f2f3",
+ "practices": ["list-comprehensions"],
+ "prerequisites": [
+ "conditionals",
+ "lists",
+ "list-methods",
+ "loops",
+ "numbers",
+ "strings",
+ "string-methods"
+ ],
+ "difficulty": 4
+ },
{
"slug": "tournament",
"name": "Tournament",
From 30d481c50d816f1bc79ce87f08ea6c8e06130cd6 Mon Sep 17 00:00:00 2001
From: meatball <69751659+meatball133@users.noreply.github.com>
Date: Mon, 5 Dec 2022 21:36:35 +0100
Subject: [PATCH 190/826] Update
exercises/practice/killer-sudoku-helper/.meta/example.py
Co-authored-by: BethanyG
---
exercises/practice/killer-sudoku-helper/.meta/example.py | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/exercises/practice/killer-sudoku-helper/.meta/example.py b/exercises/practice/killer-sudoku-helper/.meta/example.py
index ba6e873ab2..3da5f2ccfd 100644
--- a/exercises/practice/killer-sudoku-helper/.meta/example.py
+++ b/exercises/practice/killer-sudoku-helper/.meta/example.py
@@ -2,12 +2,15 @@
def combinations(target, size, exclude):
result = []
+ possible = [index for index in
+ range(1, int((target ** 2 /size) ** 0.6))
+ if index not in exclude]
+
if size == 1:
return [[target]]
else:
- possible = [index for index in range(1, int((target ** 2 / size) ** 0.6)) if index not in exclude]
for index in range(len(possible), 0, -1):
- for seq in itertools.combinations(possible, index):
+ for seq in itertools.combinations(possible, i):
if sum(seq) == target and len(seq) == size:
result.append(list(seq))
return result
From 87d6924da0fa9d9cf4e618e1e9bc8d9becafbac8 Mon Sep 17 00:00:00 2001
From: meatball <69751659+meatball133@users.noreply.github.com>
Date: Mon, 5 Dec 2022 21:38:49 +0100
Subject: [PATCH 191/826] Update
exercises/practice/killer-sudoku-helper/.meta/example.py
Co-authored-by: BethanyG
---
exercises/practice/killer-sudoku-helper/.meta/example.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/exercises/practice/killer-sudoku-helper/.meta/example.py b/exercises/practice/killer-sudoku-helper/.meta/example.py
index 3da5f2ccfd..ac3a7f8796 100644
--- a/exercises/practice/killer-sudoku-helper/.meta/example.py
+++ b/exercises/practice/killer-sudoku-helper/.meta/example.py
@@ -10,7 +10,7 @@ def combinations(target, size, exclude):
return [[target]]
else:
for index in range(len(possible), 0, -1):
- for seq in itertools.combinations(possible, i):
+ for seq in itertools.combinations(possible, index):
if sum(seq) == target and len(seq) == size:
result.append(list(seq))
return result
From 0d28a142d3f54be564991aaa2e64c287e3955b3c Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Wed, 30 Nov 2022 06:09:30 -0600
Subject: [PATCH 192/826] Create snippet.md
---
.../rna-transcription/.articles/performance/snippet.md | 4 ++++
1 file changed, 4 insertions(+)
create mode 100644 exercises/practice/rna-transcription/.articles/performance/snippet.md
diff --git a/exercises/practice/rna-transcription/.articles/performance/snippet.md b/exercises/practice/rna-transcription/.articles/performance/snippet.md
new file mode 100644
index 0000000000..f51d300d51
--- /dev/null
+++ b/exercises/practice/rna-transcription/.articles/performance/snippet.md
@@ -0,0 +1,4 @@
+```
+translate maketrans: 2.502872000914067e-07
+dictionary join: 1.0920033999718725e-06
+```
From caa61dbdaf9f6b4b2b55f2c75a3b03423d896cd1 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Wed, 30 Nov 2022 06:12:00 -0600
Subject: [PATCH 193/826] Create config.json
---
.../practice/rna-transcription/.articles/config.json | 11 +++++++++++
1 file changed, 11 insertions(+)
create mode 100644 exercises/practice/rna-transcription/.articles/config.json
diff --git a/exercises/practice/rna-transcription/.articles/config.json b/exercises/practice/rna-transcription/.articles/config.json
new file mode 100644
index 0000000000..0b28ebcbaa
--- /dev/null
+++ b/exercises/practice/rna-transcription/.articles/config.json
@@ -0,0 +1,11 @@
+{
+ "articles": [
+ {
+ "uuid": "bc3b17f7-a748-4cb3-b44d-e7049e321bc3",
+ "slug": "performance",
+ "title": "Performance deep dive",
+ "blurb": "Deep dive to find out the most performant approach for RNA Transcription.",
+ "authors": ["bobahop"]
+ }
+ ]
+}
From 5ce38dbfcc1fbc2a9121f42a75d1f461c2b70224 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Wed, 30 Nov 2022 06:12:47 -0600
Subject: [PATCH 194/826] Create Benchmark.py
---
.../.articles/performance/code/Benchmark.py | 25 +++++++++++++++++++
1 file changed, 25 insertions(+)
create mode 100644 exercises/practice/rna-transcription/.articles/performance/code/Benchmark.py
diff --git a/exercises/practice/rna-transcription/.articles/performance/code/Benchmark.py b/exercises/practice/rna-transcription/.articles/performance/code/Benchmark.py
new file mode 100644
index 0000000000..3980aa1748
--- /dev/null
+++ b/exercises/practice/rna-transcription/.articles/performance/code/Benchmark.py
@@ -0,0 +1,25 @@
+import timeit
+
+loops = 1_000_000
+
+val = timeit.timeit("""to_rna("ACGTGGTCTTAA")""",
+ """
+LOOKUP = str.maketrans("GCTA","CGAU")
+
+def to_rna(dna_strand):
+ return dna_strand.translate(LOOKUP)
+
+""", number=loops) / loops
+
+print(f"translate maketrans: {val}")
+
+val = timeit.timeit("""to_rna("ACGTGGTCTTAA")""",
+ """
+LOOKUP = {"G": "C", "C": "G", "T": "A", "A": "U"}
+
+def to_rna(dna_strand):
+ return ''.join(LOOKUP[chr] for chr in dna_strand)
+
+""", number=loops) / loops
+
+print(f"dictionary join: {val}")
From 13b0fbf24c3057fb77103d142588f95db519c2d2 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Wed, 30 Nov 2022 06:16:38 -0600
Subject: [PATCH 195/826] Create config.json
---
.../rna-transcription/.approaches/config.json | 22 +++++++++++++++++++
1 file changed, 22 insertions(+)
create mode 100644 exercises/practice/rna-transcription/.approaches/config.json
diff --git a/exercises/practice/rna-transcription/.approaches/config.json b/exercises/practice/rna-transcription/.approaches/config.json
new file mode 100644
index 0000000000..9ab4114548
--- /dev/null
+++ b/exercises/practice/rna-transcription/.approaches/config.json
@@ -0,0 +1,22 @@
+{
+ "introduction": {
+ "authors": ["bobahop"],
+ "contributors": []
+ },
+ "approaches": [
+ {
+ "uuid": "9c3d3762-8473-49f2-8004-6d611c958c38",
+ "slug": "translate-maketrans",
+ "title": "translate maketrans",
+ "blurb": "Use translate with maketrans to return the value.",
+ "authors": ["bobahop"]
+ },
+ {
+ "uuid": "fbc6be87-dec4-4c4b-84cf-fcc1ed2d6d41",
+ "slug": "dictionary-join",
+ "title": "dictionary join",
+ "blurb": "Use a dictionary look-up with join to return the value.",
+ "authors": ["bobahop"]
+ }
+ ]
+}
From 2e5efcf01977718a4364edf542a844e02326ecc9 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Wed, 30 Nov 2022 06:40:41 -0600
Subject: [PATCH 196/826] Create introduction.md
---
.../.approaches/introduction.md | 45 +++++++++++++++++++
1 file changed, 45 insertions(+)
create mode 100644 exercises/practice/rna-transcription/.approaches/introduction.md
diff --git a/exercises/practice/rna-transcription/.approaches/introduction.md b/exercises/practice/rna-transcription/.approaches/introduction.md
new file mode 100644
index 0000000000..fa580afdb4
--- /dev/null
+++ b/exercises/practice/rna-transcription/.approaches/introduction.md
@@ -0,0 +1,45 @@
+# Introduction
+
+There are at least two idiomatic approaches to solve RNA Transcription.
+One approach is to use `translate` with `maketrans`.
+Another approach is to do a dictionary lookup on each character and join the results.
+
+## General guidance
+
+Whichever approach is used needs to return the RNA complement for each DNA value.
+`translate` with `maketrans` transcribes using the [ASCII][ASCII] values of the characters.
+Using a dictionary with `join` transcribes using the string values of the characters.
+
+## Approach: `translate` with `maketrans`
+
+```python
+LOOKUP = str.maketrans("GCTA", "CGAU")
+
+
+def to_rna(dna_strand):
+ return dna_strand.translate(LOOKUP)
+
+```
+
+For more information, check the [`translate` with `maketrans` approach][approach-translate-maketrans].
+
+## Approach: dictionary look-up with `join`
+
+```python
+LOOKUP = {"G": "C", "C": "G", "T": "A", "A": "U"}
+
+
+def to_rna(dna_strand):
+ return ''.join(LOOKUP[chr] for chr in dna_strand)
+
+```
+
+For more information, check the [dictionary look-up with `join` approach][approach-dictionary-join].
+
+## Which approach to use?
+
+The `translate` with `maketrans` approach benchmarked over four times faster than the dictionary look-up with `join` approach.
+
+[ASCII]: https://www.asciitable.com/
+[approach-translate-maketrans]: https://exercism.org/tracks/python/exercises/rna-transcription/approaches/translate-maketrans
+[approach-dictionary-join]: https://exercism.org/tracks/python/exercises/rna-transcription/approaches/dictionary-join
From a4f312abf41f12dc8d8c0b6bf65656e06b3bb97b Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Wed, 30 Nov 2022 06:41:46 -0600
Subject: [PATCH 197/826] Create snippet.txt
---
.../.approaches/translate-maketrans/snippet.txt | 5 +++++
1 file changed, 5 insertions(+)
create mode 100644 exercises/practice/rna-transcription/.approaches/translate-maketrans/snippet.txt
diff --git a/exercises/practice/rna-transcription/.approaches/translate-maketrans/snippet.txt b/exercises/practice/rna-transcription/.approaches/translate-maketrans/snippet.txt
new file mode 100644
index 0000000000..2d00b83be6
--- /dev/null
+++ b/exercises/practice/rna-transcription/.approaches/translate-maketrans/snippet.txt
@@ -0,0 +1,5 @@
+LOOKUP = str.maketrans("GCTA", "CGAU")
+
+
+def to_rna(dna_strand):
+ return dna_strand.translate(LOOKUP)
From bf4515ed172100d72aa34b11422372c24db8a644 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Wed, 30 Nov 2022 06:42:27 -0600
Subject: [PATCH 198/826] Create snippet.txt
---
.../.approaches/dictionary-join/snippet.txt | 5 +++++
1 file changed, 5 insertions(+)
create mode 100644 exercises/practice/rna-transcription/.approaches/dictionary-join/snippet.txt
diff --git a/exercises/practice/rna-transcription/.approaches/dictionary-join/snippet.txt b/exercises/practice/rna-transcription/.approaches/dictionary-join/snippet.txt
new file mode 100644
index 0000000000..558bf98140
--- /dev/null
+++ b/exercises/practice/rna-transcription/.approaches/dictionary-join/snippet.txt
@@ -0,0 +1,5 @@
+LOOKUP = {"G": "C", "C": "G", "T": "A", "A": "U"}
+
+
+def to_rna(dna_strand):
+ return ''.join(LOOKUP[chr] for chr in dna_strand)
From eae8944e382dcf97a56a7646d3e37275c3c0bd99 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Wed, 30 Nov 2022 07:04:00 -0600
Subject: [PATCH 199/826] Create content.md
---
.../translate-maketrans/content.md | 34 +++++++++++++++++++
1 file changed, 34 insertions(+)
create mode 100644 exercises/practice/rna-transcription/.approaches/translate-maketrans/content.md
diff --git a/exercises/practice/rna-transcription/.approaches/translate-maketrans/content.md b/exercises/practice/rna-transcription/.approaches/translate-maketrans/content.md
new file mode 100644
index 0000000000..fcd55730d9
--- /dev/null
+++ b/exercises/practice/rna-transcription/.approaches/translate-maketrans/content.md
@@ -0,0 +1,34 @@
+# `translate()` with `maketrans()`
+
+```python
+LOOKUP = str.maketrans("GCTA", "CGAU")
+
+
+def to_rna(dna_strand):
+ return dna_strand.translate(LOOKUP)
+
+```
+
+This approach starts by defining a [dictionary][dictionaries] (also called a translation table in this context) by calling the [`maketrans()`][maketrans] method.
+
+Python doesn't _enforce_ having real constant values,
+but the `LOOKUP` translation table is defined with all uppercase letters, which is the naming convention for a Python [constant][const].
+It indicates that the value is not intended to be changed.
+
+The translation table that is created uses the [ASCII][ASCII] values (also called the ordinal values) for each letter in the two strings.
+The ASCII value for "G" in the first string is the key for the ASCII value of "C" in the second string, and so on.
+
+In the `to_rna()` function, the [`translate()`][translate] method is called on the input,
+and is passed the translation table.
+The output of `translate()` is a string where all of the input DNA characters have been replaced by their RNA complement in the translation table.
+
+
+```exercism/note
+As of this writing, no invalid DNA characters are in the argument to `to_rna()`, so there is no error handling required for invalid input.
+```
+
+[dictionaries]: https://docs.python.org/3/tutorial/datastructures.html?#dictionaries
+[maketrans]: https://docs.python.org/3/library/stdtypes.html?#str.maketrans
+[const]: https://realpython.com/python-constants/
+[translate]: https://docs.python.org/3/library/stdtypes.html?#str.translate
+[ASCII]: https://www.asciitable.com/
From 80d267beb61167391853affda6fc21e51ac2643d Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Wed, 30 Nov 2022 07:07:08 -0600
Subject: [PATCH 200/826] Update introduction.md
---
.../.approaches/introduction.md | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/exercises/practice/rna-transcription/.approaches/introduction.md b/exercises/practice/rna-transcription/.approaches/introduction.md
index fa580afdb4..ca2d74a109 100644
--- a/exercises/practice/rna-transcription/.approaches/introduction.md
+++ b/exercises/practice/rna-transcription/.approaches/introduction.md
@@ -1,16 +1,16 @@
# Introduction
There are at least two idiomatic approaches to solve RNA Transcription.
-One approach is to use `translate` with `maketrans`.
+One approach is to use `translate()` with `maketrans()`.
Another approach is to do a dictionary lookup on each character and join the results.
## General guidance
Whichever approach is used needs to return the RNA complement for each DNA value.
-`translate` with `maketrans` transcribes using the [ASCII][ASCII] values of the characters.
-Using a dictionary with `join` transcribes using the string values of the characters.
+The `translate()` method with `maketrans()` transcribes using the [ASCII][ASCII] values of the characters.
+Using a dictionary look-up with `join()` transcribes using the string values of the characters.
-## Approach: `translate` with `maketrans`
+## Approach: `translate()` with `maketrans()`
```python
LOOKUP = str.maketrans("GCTA", "CGAU")
@@ -21,9 +21,9 @@ def to_rna(dna_strand):
```
-For more information, check the [`translate` with `maketrans` approach][approach-translate-maketrans].
+For more information, check the [`translate()` with `maketrans()` approach][approach-translate-maketrans].
-## Approach: dictionary look-up with `join`
+## Approach: dictionary look-up with `join()`
```python
LOOKUP = {"G": "C", "C": "G", "T": "A", "A": "U"}
@@ -34,11 +34,11 @@ def to_rna(dna_strand):
```
-For more information, check the [dictionary look-up with `join` approach][approach-dictionary-join].
+For more information, check the [dictionary look-up with `join()` approach][approach-dictionary-join].
## Which approach to use?
-The `translate` with `maketrans` approach benchmarked over four times faster than the dictionary look-up with `join` approach.
+The `translate()` with `maketrans()` approach benchmarked over four times faster than the dictionary look-up with `join()` approach.
[ASCII]: https://www.asciitable.com/
[approach-translate-maketrans]: https://exercism.org/tracks/python/exercises/rna-transcription/approaches/translate-maketrans
From e5f076f88c03ccf1cef29bfa0475a194d2d61177 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Wed, 30 Nov 2022 07:18:15 -0600
Subject: [PATCH 201/826] Create content.md
---
.../.approaches/dictionary-join/content.md | 30 +++++++++++++++++++
1 file changed, 30 insertions(+)
create mode 100644 exercises/practice/rna-transcription/.approaches/dictionary-join/content.md
diff --git a/exercises/practice/rna-transcription/.approaches/dictionary-join/content.md b/exercises/practice/rna-transcription/.approaches/dictionary-join/content.md
new file mode 100644
index 0000000000..f3ec1f755f
--- /dev/null
+++ b/exercises/practice/rna-transcription/.approaches/dictionary-join/content.md
@@ -0,0 +1,30 @@
+# dictionary look-up with `join`
+
+```python
+LOOKUP = {"G": "C", "C": "G", "T": "A", "A": "U"}
+
+
+def to_rna(dna_strand):
+ return ''.join(LOOKUP[chr] for chr in dna_strand)
+
+```
+
+This approach starts by defining a [dictionary][dictionaries] to map the DNA values to RNA values.
+
+Python doesn't _enforce_ having real constant values,
+but the `LOOKUP` dictionary is defined with all uppercase letters, which is the naming convention for a Python [constant][const].
+It indicates that the value is not intended to be changed.
+
+In the `to_rna()` function, the [`join()`][join] method is called on an empty string,
+and is passed the list created from a [list comprehension][list-comprehension].
+
+The list comprehension iterates each character in the input,
+looks up the DNA character in the look-up dictionary, and outputs its matching RNA character as an element in the list.
+
+The `join()` method collects the list of RNA characters back into a string.
+Since an empty string is the separator for the `join()`, there are no spaces between the RNA characters in the string.
+
+[dictionaries]: https://docs.python.org/3/tutorial/datastructures.html?#dictionaries
+[const]: https://realpython.com/python-constants/
+[join]: https://docs.python.org/3/library/stdtypes.html?#str.join
+[list-comprehension]: https://realpython.com/list-comprehension-python/#using-list-comprehensions
From caf3bf1b2b0091f8f6304cec27b1805f9cb8a29c Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Wed, 30 Nov 2022 10:31:39 -0600
Subject: [PATCH 202/826] Create content.md
---
.../.articles/performance/content.md | 27 +++++++++++++++++++
1 file changed, 27 insertions(+)
create mode 100644 exercises/practice/rna-transcription/.articles/performance/content.md
diff --git a/exercises/practice/rna-transcription/.articles/performance/content.md b/exercises/practice/rna-transcription/.articles/performance/content.md
new file mode 100644
index 0000000000..9419c17567
--- /dev/null
+++ b/exercises/practice/rna-transcription/.articles/performance/content.md
@@ -0,0 +1,27 @@
+# Performance
+
+In this approach, we'll find out how to most efficiently calculate the RNA Transcription.
+
+The [approaches page][approaches] lists two idiomatic approaches to this exercise:
+
+1. [Using `translate()` with `maketrans()` approach][approach-translate-maketrans]
+2. [Using dictionary look-up with `join()` approach][approach-dictionary-join]
+
+
+## Benchmarks
+
+To benchmark the approaches, we wrote a [small benchmark application][benchmark-application] using the [`timeit`][timeit] library.
+
+```
+translate maketrans: 2.502872000914067e-07
+dictionary join: 1.0920033999718725e-06
+```
+
+At about `250` nanoseconds, the `translate()` with `maketrans()` approach is more than four times faster than the dictionary with `join()` approach,
+which takes about `1092` nanoseconds.
+
+[approaches]: https://exercism.org/tracks/python/exercises/rna-transcription/approaches
+[approach-translate-maketrans]: https://exercism.org/tracks/python/exercises/rna-transcription/approaches/translate-maketrans
+[approach-dictionary-join]: https://exercism.org/tracks/python/exercises/rna-transcription/approaches/dictionary-join
+[benchmark-application]: https://github.com/exercism/python/blob/main/exercises/practice/rna-transcription/.articles/performance/code/Benchmark.py
+[timeit]: https://docs.python.org/3/library/timeit.html
From 62b9769a9372ee5312f3f37ee80651bd18335e14 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Wed, 7 Dec 2022 07:40:49 -0600
Subject: [PATCH 203/826] Update introduction.md
---
exercises/practice/bob/.approaches/introduction.md | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/exercises/practice/bob/.approaches/introduction.md b/exercises/practice/bob/.approaches/introduction.md
index ccdfa93f90..07d68d1a1e 100644
--- a/exercises/practice/bob/.approaches/introduction.md
+++ b/exercises/practice/bob/.approaches/introduction.md
@@ -55,8 +55,7 @@ def response(hey_bob):
if is_shout:
if is_question:
return "Calm down, I know what I'm doing!"
- else:
- return 'Whoa, chill out!'
+ return 'Whoa, chill out!'
if is_question:
return 'Sure.'
return 'Whatever.'
From 2d4d1724cf9dc9a887dc1fb1725c6b6475167d8e Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Wed, 7 Dec 2022 07:43:58 -0600
Subject: [PATCH 204/826] Update content.md
---
.../bob/.approaches/if-statements-nested/content.md | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/exercises/practice/bob/.approaches/if-statements-nested/content.md b/exercises/practice/bob/.approaches/if-statements-nested/content.md
index 06b5ed527c..5867427afd 100644
--- a/exercises/practice/bob/.approaches/if-statements-nested/content.md
+++ b/exercises/practice/bob/.approaches/if-statements-nested/content.md
@@ -10,8 +10,7 @@ def response(hey_bob):
if is_shout:
if is_question:
return "Calm down, I know what I'm doing!"
- else:
- return 'Whoa, chill out!'
+ return 'Whoa, chill out!'
if is_question:
return 'Sure.'
return 'Whatever.'
@@ -21,6 +20,12 @@ def response(hey_bob):
In this approach you have a series of `if` statements using the calculated variables to evaluate the conditions, some of which are nested.
As soon as a `True` condition is found, the correct response is returned.
+```exercism/note
+Note that there are no `elif` or `else` statements.
+If an `if` statement can return, then an `elif` or `else` is not needed.
+Execution will either return or will continue to the next statement anyway.
+```
+
The [`rstrip`][rstrip] method is applied to the input to eliminate any whitespace at the end of the input.
If the input has no characters left, it uses the [falsiness][falsiness] of an empty string with the [`not`][not] operator to return the response for saying nothing.
Since it doesn't matter if there is leading whitespace, the `rstrip` function is used instead of [`strip`][strip].
From c29af2e826efe41f0873a393c47aa91b964b0976 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Wed, 7 Dec 2022 07:44:41 -0600
Subject: [PATCH 205/826] Update snippet.txt
---
.../practice/bob/.approaches/if-statements-nested/snippet.txt | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/exercises/practice/bob/.approaches/if-statements-nested/snippet.txt b/exercises/practice/bob/.approaches/if-statements-nested/snippet.txt
index 0df6c2fe9e..2362a7ac91 100644
--- a/exercises/practice/bob/.approaches/if-statements-nested/snippet.txt
+++ b/exercises/practice/bob/.approaches/if-statements-nested/snippet.txt
@@ -1,8 +1,7 @@
if is_shout:
if is_question:
return "Calm down, I know what I'm doing!"
- else:
- return 'Whoa, chill out!'
+ return 'Whoa, chill out!'
if is_question:
return 'Sure.'
return 'Whatever.'
From 07bbb745c779bbcaf4955e0f03189246c6a30ef7 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Wed, 7 Dec 2022 07:48:23 -0600
Subject: [PATCH 206/826] Update Benchmark.py
---
exercises/practice/bob/.articles/performance/code/Benchmark.py | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/exercises/practice/bob/.articles/performance/code/Benchmark.py b/exercises/practice/bob/.articles/performance/code/Benchmark.py
index e726ed1953..3e5a011fcb 100644
--- a/exercises/practice/bob/.articles/performance/code/Benchmark.py
+++ b/exercises/practice/bob/.articles/performance/code/Benchmark.py
@@ -34,8 +34,7 @@ def response(hey_bob):
if is_shout:
if is_question:
return "Calm down, I know what I'm doing!"
- else:
- return 'Whoa, chill out!'
+ return 'Whoa, chill out!'
if is_question:
return 'Sure.'
return 'Whatever.'
From ce0a2593ccdc3ff44d996b76403a67bfe8d9d7bf Mon Sep 17 00:00:00 2001
From: meatball <69751659+meatball133@users.noreply.github.com>
Date: Wed, 7 Dec 2022 22:25:34 +0100
Subject: [PATCH 207/826] fix
---
.../locomotive_engineer_test.py | 54 +++++++++----------
1 file changed, 27 insertions(+), 27 deletions(-)
diff --git a/exercises/concept/locomotive-engineer/locomotive_engineer_test.py b/exercises/concept/locomotive-engineer/locomotive_engineer_test.py
index b3a46939fd..d3d3178fdf 100644
--- a/exercises/concept/locomotive-engineer/locomotive_engineer_test.py
+++ b/exercises/concept/locomotive-engineer/locomotive_engineer_test.py
@@ -15,7 +15,7 @@ def test_get_list_of_wagons(self):
output_data = [[1,5,2,7,4], [1,5], [1], [1,9,3], [1,10,6,3,9,8,4,14,24,7]]
for variant, (input_data, output_data) in enumerate(zip(input_data, output_data), start=1):
- with self.subTest(f"variation #{variant}", input_data=input_data, output_data=output_data):
+ with self.subTest(f'variation #{variant}', input_data=input_data, output_data=output_data):
error_msg=f'Expected: {output_data} but got a different wagon list instead.'
self.assertEqual(get_list_of_wagons(*input_data), output_data, msg=error_msg)
@@ -32,59 +32,59 @@ def test_fix_list_of_wagons(self):
[1, 8, 6, 4, 5, 9, 21, 2, 13, 25, 7, 19, 10, 3, 14]
]
for variant, (input_data, output_data) in enumerate(zip(input_data, output_data), start=1):
- with self.subTest(f"variation #{variant}", input_data=input_data, output_data=output_data):
+ with self.subTest(f'variation #{variant}', input_data=input_data, output_data=output_data):
error_msg=f'Expected: {output_data} but got a different wagon list instead.'
self.assertEqual(fix_list_of_wagons(input_data[0], input_data[1]), output_data, msg=error_msg)
@pytest.mark.task(taskno=3)
def test_add_missing_stops(self):
- input_data = (({"from": "Berlin", "to": "Hamburg"}, {"stop_1": "Lepzig", "stop_2": "Hannover", "stop_3": "Frankfurt"}),
- ({"from": "Paris", "to": "London"}, {"stop_1": "Lille"}),
- ({"from": "New York", "to": "Philadelphia"},{}),
- ({"from": "Gothenburg", "to": "Copenhagen"}, {"stop_1": "Kungsbacka", "stop_2": "Varberg", "stop_3": "Halmstad", "stop_4": "Angelholm", "stop_5": "Lund", "stop_6": "Malmo"})
+ input_data = (({'from': 'Berlin', 'to': 'Hamburg'}, {'stop_1': 'Lepzig', 'stop_2': 'Hannover', 'stop_3': 'Frankfurt'}),
+ ({'from': 'Paris', 'to': 'London'}, {'stop_1': 'Lille'}),
+ ({'from': 'New York', 'to': 'Philadelphia'},{}),
+ ({'from': 'Gothenburg', 'to': 'Copenhagen'}, {'stop_1': 'Kungsbacka', 'stop_2': 'Varberg', 'stop_3': 'Halmstad', 'stop_4': 'Angelholm', 'stop_5': 'Lund', 'stop_6': 'Malmo'})
)
- output_data = [{"from": "Berlin", "to": "Hamburg", "stops": ["Lepzig", "Hannover", "Frankfurt"]},
- {"from": "Paris", "to": "London", "stops": ["Lille"]},
- {"from": "New York", "to": "Philadelphia", "stops": []},
- {"from": "Gothenburg", "to": "Copenhagen", "stops": ["Kungsbacka", "Varberg", "Halmstad", "Angelholm", "Lund", "Malmo"]}
+ output_data = [{'from': 'Berlin', 'to': 'Hamburg', 'stops': ['Lepzig', 'Hannover', 'Frankfurt']},
+ {'from': 'Paris', 'to': 'London', 'stops': ['Lille']},
+ {'from': 'New York', 'to': 'Philadelphia', 'stops': []},
+ {'from': 'Gothenburg', 'to': 'Copenhagen', 'stops': ['Kungsbacka', 'Varberg', 'Halmstad', 'Angelholm', 'Lund', 'Malmo']}
]
for variant, (input_data, output_data) in enumerate(zip(input_data, output_data), start=1):
- with self.subTest(f"variation #{variant}", input_data=input_data, output_data=output_data):
+ with self.subTest(f'variation #{variant}', input_data=input_data, output_data=output_data):
error_msg=f'Expected: {output_data} but got a different set of stops instead.'
self.assertEqual(add_missing_stops(input_data[0], **input_data[1]), output_data, msg=error_msg)
@pytest.mark.task(taskno=4)
def test_extend_route_information(self):
- input_data = [({"from": "Berlin", "to": "Hamburg"}, {"timeOfArrival": "12:00", "precipitation": "10", "temperature": "5", "caboose": "yes"}),
- ({"from": "Paris", "to": "London"}, {"timeOfArrival": "10:30", "temperature": "20", "length": 15}),
- ({"from": "Gothenburg", "to": "Copenhagen"}, {"precipitation": "1", "timeOfArrival": "21:20", "temperature": "-6"})]
- output_data = [{"from": "Berlin", "to": "Hamburg", "timeOfArrival": "12:00", "precipitation": "10", "temperature": "5", "caboose": "yes"},
- {"from": "Paris", "to": "London", "timeOfArrival": "10:30", "temperature": "20", "length": 15},
- {"from": "Gothenburg", "to": "Copenhagen", "precipitation": "1", "timeOfArrival": "21:20", "temperature": "-6"}
+ input_data = [({'from': 'Berlin', 'to': 'Hamburg'}, {'timeOfArrival': '12:00', 'precipitation': '10', 'temperature': '5', 'caboose': 'yes'}),
+ ({'from': 'Paris', 'to': 'London'}, {'timeOfArrival': '10:30', 'temperature': '20', 'length': '15'}),
+ ({'from': 'Gothenburg', 'to': 'Copenhagen'}, {'precipitation': '1', 'timeOfArrival': '21:20', 'temperature': '-6'})]
+ output_data = [{'from': 'Berlin', 'to': 'Hamburg', 'timeOfArrival': '12:00', 'precipitation': '10', 'temperature': '5', 'caboose': 'yes'},
+ {'from': 'Paris', 'to': 'London', 'timeOfArrival': '10:30', 'temperature': '20', 'length': '15'},
+ {'from': 'Gothenburg', 'to': 'Copenhagen', 'precipitation': '1', 'timeOfArrival': '21:20', 'temperature': '-6'}
]
for variant, (input_data, output_data) in enumerate(zip(input_data, output_data), start=1):
- with self.subTest(f"variation #{variant}", input_data=input_data, output_data=output_data):
+ with self.subTest(f'variation #{variant}', input_data=input_data, output_data=output_data):
error_msg=f'Expected: {output_data} but got a different route dictionary instead.'
self.assertEqual(extend_route_information(input_data[0], input_data[1]), output_data, msg=error_msg)
@pytest.mark.task(taskno=5)
def test_fix_wagon_depot(self):
input_data = (
- [[(2, "red"), (4, "red"), (8, "red")], [(5, "blue"), (9, "blue"), (13, "blue")], [(3, "orange"), (7, "orange"), (11, "orange")]],
- [[(6, "blue"), (10, "blue"), (14, "blue")], [(7, "red"), (4, "red"), (2, "red")], [(3, "orange"), (11, "orange"), (15, "orange")]],
- [[(7, "pink"), (4, "pink"), (2, "pink")],[(10, "green"), (6, "green"), (14, "green")], [(9, "yellow"), (5, "yellow"), (13, "yellow")]],
- [[(3, "purple"), (11, "purple"), (15, "purple")], [(20, "black"), (16, "black"), (12, "black")], [(19, "white"), (17, "white"), (18, "white")]]
+ [[(2, 'red'), (4, 'red'), (8, 'red')], [(5, 'blue'), (9, 'blue'), (13, 'blue')], [(3, 'orange'), (7, 'orange'), (11, 'orange')]],
+ [[(6, 'blue'), (10, 'blue'), (14, 'blue')], [(7, 'red'), (4, 'red'), (2, 'red')], [(3, 'orange'), (11, 'orange'), (15, 'orange')]],
+ [[(7, 'pink'), (4, 'pink'), (2, 'pink')], [(10, 'green'), (6, 'green'), (14, 'green')], [(9, 'yellow'), (5, 'yellow'), (13, 'yellow')]],
+ [[(3, 'purple'), (11, 'purple'), (15, 'purple')], [(20, 'black'), (16, 'black'), (12, 'black')], [(19, 'white'), (17, 'white'), (18, 'white')]]
)
output_data = (
- [[(2, "red"), (5, "blue"), (3, "orange")],[(4, "red"), (9, "blue"), (7, "orange")], [(8, "red"), (13, "blue"), (11, "orange")]],
- [[(6, "blue"), (7, "red"), (3, "orange")],[(10, "blue"), (4, "red"), (11, "orange")], [(14, "blue"), (2, "red"), (15, "orange")]],
- [[(7, "pink"), (10, "green"), (9, "yellow")], [(4, "pink"), (6, "green"), (5, "yellow")], [(2, "pink"), (14, "green"), (13, "yellow")]],
- [[(3, "purple"), (20, "black"), (19, "white")], [(11, "purple"), (16, "black"), (17, "white")], [(15, "purple"), (12, "black"), (18, "white")]]
+ [[(2, 'red'), (5, 'blue'), (3, 'orange')], [(4, 'red'), (9, 'blue'), (7, 'orange')], [(8, 'red'), (13, 'blue'), (11, 'orange')]],
+ [[(6, 'blue'), (7, 'red'), (3, 'orange')], [(10, 'blue'), (4, 'red'), (11, 'orange')], [(14, 'blue'), (2, 'red'), (15, 'orange')]],
+ [[(7, 'pink'), (10, 'green'), (9, 'yellow')], [(4, 'pink'), (6, 'green'), (5, 'yellow')], [(2, 'pink'), (14, 'green'), (13, 'yellow')]],
+ [[(3, 'purple'), (20, 'black'), (19, 'white')], [(11, 'purple'), (16, 'black'), (17, 'white')], [(15, 'purple'), (12, 'black'), (18, 'white')]]
)
for variant, (input_data, output_data) in enumerate(zip(input_data, output_data), start=1):
- with self.subTest(f"variation #{variant}", input_data=input_data, output_data=output_data):
+ with self.subTest(f'variation #{variant}', input_data=input_data, output_data=output_data):
error_msg=f'Expected: {output_data} but got a different wagon depot list instead.'
self.assertEqual(fix_wagon_depot(input_data), output_data, msg=error_msg)
From 154a809493e4f41a44942c0a17d5755226045d1b Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 1 Dec 2022 06:47:54 -0600
Subject: [PATCH 208/826] Create config.json
---
.../practice/luhn/.approaches/config.json | 29 +++++++++++++++++++
1 file changed, 29 insertions(+)
create mode 100644 exercises/practice/luhn/.approaches/config.json
diff --git a/exercises/practice/luhn/.approaches/config.json b/exercises/practice/luhn/.approaches/config.json
new file mode 100644
index 0000000000..37536d78c1
--- /dev/null
+++ b/exercises/practice/luhn/.approaches/config.json
@@ -0,0 +1,29 @@
+{
+ "introduction": {
+ "authors": ["bobahop"],
+ "contributors": []
+ },
+ "approaches": [
+ {
+ "uuid": "568ca4f5-1046-4750-b3c2-6a50b84ed8ed",
+ "slug": "reversed-for",
+ "title": "reversed for loop",
+ "blurb": "Use reversed with a for loop to validate the number.",
+ "authors": ["bobahop"]
+ },
+ {
+ "uuid": "24d6c283-3a7d-4315-91db-b3160f3df567",
+ "slug": "replace-reverse-enumerate",
+ "title": "replace, reverse, enumerate",
+ "blurb": "Use replace, reverse, and enumerate to validate the number.",
+ "authors": ["bobahop"]
+ },
+ {
+ "uuid": "fca6a89a-baa9-4aeb-b419-130d6ad14564",
+ "slug": "recursion",
+ "title": "recursion",
+ "blurb": "Use recursion to validate the number.",
+ "authors": ["bobahop"]
+ }
+ ]
+}
From 2b0d5b7a0e15611ad93a3cae1059a59e8524dde3 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 1 Dec 2022 06:49:40 -0600
Subject: [PATCH 209/826] Create config.json
---
exercises/practice/luhn/.articles/config.json | 11 +++++++++++
1 file changed, 11 insertions(+)
create mode 100644 exercises/practice/luhn/.articles/config.json
diff --git a/exercises/practice/luhn/.articles/config.json b/exercises/practice/luhn/.articles/config.json
new file mode 100644
index 0000000000..132e55513e
--- /dev/null
+++ b/exercises/practice/luhn/.articles/config.json
@@ -0,0 +1,11 @@
+{
+ "articles": [
+ {
+ "uuid": "fa3d646c-b075-49a4-80b7-b339f9022aea",
+ "slug": "performance",
+ "title": "Performance deep dive",
+ "blurb": "Deep dive to find out the most performant approach to validating Luhn.",
+ "authors": ["bobahop"]
+ }
+ ]
+}
From de25ba653df2dc43146be1c4a10da1b6f2de0adb Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 1 Dec 2022 06:50:27 -0600
Subject: [PATCH 210/826] Create snippet.md
---
exercises/practice/luhn/.articles/performance/snippet.md | 5 +++++
1 file changed, 5 insertions(+)
create mode 100644 exercises/practice/luhn/.articles/performance/snippet.md
diff --git a/exercises/practice/luhn/.articles/performance/snippet.md b/exercises/practice/luhn/.articles/performance/snippet.md
new file mode 100644
index 0000000000..de2b9ca564
--- /dev/null
+++ b/exercises/practice/luhn/.articles/performance/snippet.md
@@ -0,0 +1,5 @@
+```
+reversed for: 1.1550310099963099e-05
+replace reverse enumerate: 1.0071774299954995e-05
+recursion: 2.4321520800003783e-05
+```
From fa11649647278783f1d772c8f61c1994d248c2db Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 1 Dec 2022 06:51:03 -0600
Subject: [PATCH 211/826] Create Benchmark.py
---
.../.articles/performance/code/Benchmark.py | 104 ++++++++++++++++++
1 file changed, 104 insertions(+)
create mode 100644 exercises/practice/luhn/.articles/performance/code/Benchmark.py
diff --git a/exercises/practice/luhn/.articles/performance/code/Benchmark.py b/exercises/practice/luhn/.articles/performance/code/Benchmark.py
new file mode 100644
index 0000000000..2f95127822
--- /dev/null
+++ b/exercises/practice/luhn/.articles/performance/code/Benchmark.py
@@ -0,0 +1,104 @@
+import timeit
+
+loops = 1_000_000
+
+val = timeit.timeit("""Luhn("9999999999 9999999999 9999999999 9999999999").valid()""",
+ """
+class Luhn:
+
+ def __init__(self, card_num):
+ self.isValid = Luhn.luhny_bin(card_num)
+
+ def valid(self):
+ return self.isValid
+
+ @staticmethod
+ def luhny_tune(num):
+ return dbl - 9 if (dbl := 2 * num) > 9 else dbl
+
+ @staticmethod
+ def luhny_bin(num):
+ total = 0
+ pos = 0
+ for ltr in reversed(num):
+ if ltr == " ":
+ continue
+ if not ltr.isdigit():
+ return False
+ if not pos % 2:
+ total+= int(ltr)
+ else:
+ total += Luhn.luhny_tune(int(ltr))
+ pos += 1
+ return pos > 1 and not total % 10
+
+
+""", number=loops) / loops
+
+print(f"reversed for: {val}")
+
+val = timeit.timeit("""Luhn("9999999999 9999999999 9999999999 9999999999").valid()""",
+ """
+class Luhn:
+
+ def __init__(self, card_num):
+ self.isValid = Luhn.luhny_bin(card_num)
+
+ def valid(self):
+ return self.isValid
+
+ @staticmethod
+ def luhny_tune(num):
+ return dbl - 9 if (dbl := 2 * num) > 9 else dbl
+
+ @staticmethod
+ def luhny_bin(num):
+ num = num.replace(' ', '')
+ if not num.isdigit():
+ return False
+ total = 0
+ for pos, ltr in enumerate(num[::-1]):
+ if not pos % 2:
+ total+= int(ltr)
+ else:
+ total += Luhn.luhny_tune(int(ltr))
+ pos += 1
+ return pos > 1 and not total % 10
+
+""", number=loops) / loops
+
+print(f"replace reverse enumerate: {val}")
+
+val = timeit.timeit("""Luhn("9999999999 9999999999 9999999999 9999999999").valid()""",
+ """
+class Luhn:
+ def __init__(self, card_num):
+ self.isValid = Luhn.luhny_bin(0, 0, list(card_num[::-1]))
+
+ def valid(self):
+ return self.isValid
+
+ @staticmethod
+ def luhny_tune(num):
+ return dbl - 9 if (dbl := 2 * num) > 9 else dbl
+
+ @staticmethod
+ def luhny_bin(pos, sum, chars):
+ if not chars:
+ if pos < 2:
+ return False
+ return sum % 10 == 0
+ else:
+ head, *tail = chars
+ if head.isdigit():
+ if pos % 2 == 0:
+ return Luhn.luhny_bin(pos + 1, sum + int(head), tail)
+ else:
+ return Luhn.luhny_bin(pos + 1, sum + Luhn.luhny_tune(int(head)), tail)
+ if head == " ":
+ return Luhn.luhny_bin(pos, sum, tail)
+ return False
+
+""", number=loops) / loops
+
+print(f"recursion: {val}")
From d9e5d3a991051860aef7294355a0508ed880b894 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 1 Dec 2022 07:12:32 -0600
Subject: [PATCH 212/826] Create introduction.md
---
.../practice/luhn/.approaches/introduction.md | 93 +++++++++++++++++++
1 file changed, 93 insertions(+)
create mode 100644 exercises/practice/luhn/.approaches/introduction.md
diff --git a/exercises/practice/luhn/.approaches/introduction.md b/exercises/practice/luhn/.approaches/introduction.md
new file mode 100644
index 0000000000..378537c17f
--- /dev/null
+++ b/exercises/practice/luhn/.approaches/introduction.md
@@ -0,0 +1,93 @@
+# Introduction
+
+There are many idiomatic ways to solve Luhn.
+Among them are:
+- You can use a for loop on a `reversed()` iterator.
+- You can scrub the input with `replace()` and test with `isdigit()` before reversing the input to `enumerate()` it.
+
+## General guidance
+
+One important aspect to solving Luhn is to allow for spaces in the input and to disallow all other non-numeric characters.
+Another important aspect is to handle the value of each digit according to its position in the string.
+Another consideration may be to calculate the validity only once, no matter how many times `valid()` is called.
+
+## Approach: `reversed()` `for` loop
+
+```python
+class Luhn:
+
+ def __init__(self, card_num):
+ self.isValid = Luhn.luhny_bin(card_num)
+
+ def valid(self):
+ return self.isValid
+
+ @staticmethod
+ def luhny_tune(num):
+ return dbl - 9 if (dbl := 2 * num) > 9 else dbl
+
+ @staticmethod
+ def luhny_bin(num):
+ total = 0
+ pos = 0
+ for ltr in reversed(num):
+ if ltr == " ":
+ continue
+ if not ltr.isdigit():
+ return False
+ if not pos % 2:
+ total+= int(ltr)
+ else:
+ total += Luhn.luhny_tune(int(ltr))
+ pos += 1
+ return pos > 1 and not total % 10
+
+```
+
+For more information, check the [`reversed()` `for` loop approach][approach-reversed-for].
+
+## Approach: `replace()`, reverse, `enumerate()`
+
+```python
+class Luhn:
+
+ def __init__(self, card_num):
+ self.isValid = Luhn.luhny_bin(card_num)
+
+ def valid(self):
+ return self.isValid
+
+ @staticmethod
+ def luhny_tune(num):
+ return dbl - 9 if (dbl := 2 * num) > 9 else dbl
+
+ @staticmethod
+ def luhny_bin(num):
+ num = num.replace(' ', '')
+ if not num.isdigit():
+ return False
+ total = 0
+ for pos, ltr in enumerate(num[::-1]):
+ if not pos % 2:
+ total+= int(ltr)
+ else:
+ total += Luhn.luhny_tune(int(ltr))
+ pos += 1
+ return pos > 1 and not total % 10
+
+```
+
+For more information, check the [`replace()`, reverse, `enumerate()` approach][approach-replace-reverse-enumerate].
+
+## Other approaches
+
+Besides the aforementioned, idiomatic approaches, you could also approach the exercise as follows:
+
+### Other approach: recursion
+
+Another approach can use recursion to validate the number.
+For more information, check the [recursion approach][approach-recursion].
+
+[approach-reversed-for]: https://exercism.org/tracks/python/exercises/luhn/approaches/reversed-for
+[approach-replace-reverse-enumerate]: https://exercism.org/tracks/python/exercises/luhn/approaches/replace-reverse-enumerate
+[approach-recursion]: https://exercism.org/tracks/python/exercises/luhn/approaches/recursion
From 661677505bba98dd73f7fbbb520d71c66b64ace0 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 1 Dec 2022 07:14:17 -0600
Subject: [PATCH 213/826] Create content.md
---
.../luhn/.approaches/recursion/content.md | 32 +++++++++++++++++++
1 file changed, 32 insertions(+)
create mode 100644 exercises/practice/luhn/.approaches/recursion/content.md
diff --git a/exercises/practice/luhn/.approaches/recursion/content.md b/exercises/practice/luhn/.approaches/recursion/content.md
new file mode 100644
index 0000000000..a4f682c4e7
--- /dev/null
+++ b/exercises/practice/luhn/.approaches/recursion/content.md
@@ -0,0 +1,32 @@
+# Recursion
+
+```python
+class Luhn:
+ def __init__(self, card_num):
+ self.isValid = Luhn.luhny_bin(0, 0, list(card_num[::-1]))
+
+ def valid(self):
+ return self.isValid
+
+ @staticmethod
+ def luhny_tune(num):
+ return dbl - 9 if (dbl := 2 * num) > 9 else dbl
+
+ @staticmethod
+ def luhny_bin(pos, sum, chars):
+ if not chars:
+ if pos < 2:
+ return False
+ return sum % 10 == 0
+ else:
+ head, *tail = chars
+ if head.isdigit():
+ if pos % 2 == 0:
+ return Luhn.luhny_bin(pos + 1, sum + int(head), tail)
+ else:
+ return Luhn.luhny_bin(pos + 1, sum + Luhn.luhny_tune(int(head)), tail)
+ if head == " ":
+ return Luhn.luhny_bin(pos, sum, tail)
+ return False
+
+```
From 8dca95d30ae40d0aae7add69ae6d0e438df2f2b0 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 1 Dec 2022 07:20:54 -0600
Subject: [PATCH 214/826] Update snippet.md
---
exercises/practice/luhn/.articles/performance/snippet.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/exercises/practice/luhn/.articles/performance/snippet.md b/exercises/practice/luhn/.articles/performance/snippet.md
index de2b9ca564..532b085ce2 100644
--- a/exercises/practice/luhn/.articles/performance/snippet.md
+++ b/exercises/practice/luhn/.articles/performance/snippet.md
@@ -1,5 +1,5 @@
```
-reversed for: 1.1550310099963099e-05
-replace reverse enumerate: 1.0071774299954995e-05
+reversed for: 1.0783263299963438e-05
+replace reverse enumerate: 9.933844099985436e-06
recursion: 2.4321520800003783e-05
```
From ee40d4d4bd32918f1f5fc627137cf674c4b4464e Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 1 Dec 2022 07:23:39 -0600
Subject: [PATCH 215/826] Update Benchmark.py
---
.../luhn/.articles/performance/code/Benchmark.py | 16 +++++++---------
1 file changed, 7 insertions(+), 9 deletions(-)
diff --git a/exercises/practice/luhn/.articles/performance/code/Benchmark.py b/exercises/practice/luhn/.articles/performance/code/Benchmark.py
index 2f95127822..66afd6f49e 100644
--- a/exercises/practice/luhn/.articles/performance/code/Benchmark.py
+++ b/exercises/practice/luhn/.articles/performance/code/Benchmark.py
@@ -21,18 +21,16 @@ def luhny_bin(num):
total = 0
pos = 0
for ltr in reversed(num):
- if ltr == " ":
- continue
- if not ltr.isdigit():
+ if ltr.isdigit():
+ if not pos % 2:
+ total+= int(ltr)
+ else:
+ total += Luhn.luhny_tune(int(ltr))
+ pos += 1
+ elif ltr != " ":
return False
- if not pos % 2:
- total+= int(ltr)
- else:
- total += Luhn.luhny_tune(int(ltr))
- pos += 1
return pos > 1 and not total % 10
-
""", number=loops) / loops
print(f"reversed for: {val}")
From bd450818d35e4dbfa9e212eb4e0ee62a938db482 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 1 Dec 2022 07:25:28 -0600
Subject: [PATCH 216/826] Create snippet.txt
---
exercises/practice/luhn/.approaches/recursion/snippet.txt | 8 ++++++++
1 file changed, 8 insertions(+)
create mode 100644 exercises/practice/luhn/.approaches/recursion/snippet.txt
diff --git a/exercises/practice/luhn/.approaches/recursion/snippet.txt b/exercises/practice/luhn/.approaches/recursion/snippet.txt
new file mode 100644
index 0000000000..c59dd9a5fe
--- /dev/null
+++ b/exercises/practice/luhn/.approaches/recursion/snippet.txt
@@ -0,0 +1,8 @@
+head, *tail = chars
+if head.isdigit():
+ if pos % 2 == 0:
+ return Luhn.luhny_bin(pos + 1, sum + int(head), tail)
+ else:
+ return Luhn.luhny_bin(pos + 1, sum + Luhn.luhny_tune(int(head)), tail)
+if head == " ":
+ return Luhn.luhny_bin(pos, sum, tail)
From 8c895d6cbf488b5258f9bbdffee8e09925c40c9f Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 1 Dec 2022 11:14:49 -0600
Subject: [PATCH 217/826] Update introduction.md
---
.../practice/luhn/.approaches/introduction.md | 15 +++++++--------
1 file changed, 7 insertions(+), 8 deletions(-)
diff --git a/exercises/practice/luhn/.approaches/introduction.md b/exercises/practice/luhn/.approaches/introduction.md
index 378537c17f..904d1e2036 100644
--- a/exercises/practice/luhn/.approaches/introduction.md
+++ b/exercises/practice/luhn/.approaches/introduction.md
@@ -31,15 +31,14 @@ class Luhn:
total = 0
pos = 0
for ltr in reversed(num):
- if ltr == " ":
- continue
- if not ltr.isdigit():
+ if ltr.isdigit():
+ if not pos % 2:
+ total+= int(ltr)
+ else:
+ total += Luhn.luhny_tune(int(ltr))
+ pos += 1
+ elif ltr != " ":
return False
- if not pos % 2:
- total+= int(ltr)
- else:
- total += Luhn.luhny_tune(int(ltr))
- pos += 1
return pos > 1 and not total % 10
```
From 7f01407de8786b92af48e77c9c0861e5ad2ff2b9 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 1 Dec 2022 11:41:09 -0600
Subject: [PATCH 218/826] Create content.md
---
.../luhn/.approaches/reversed-for/content.md | 73 +++++++++++++++++++
1 file changed, 73 insertions(+)
create mode 100644 exercises/practice/luhn/.approaches/reversed-for/content.md
diff --git a/exercises/practice/luhn/.approaches/reversed-for/content.md b/exercises/practice/luhn/.approaches/reversed-for/content.md
new file mode 100644
index 0000000000..24d2897b32
--- /dev/null
+++ b/exercises/practice/luhn/.approaches/reversed-for/content.md
@@ -0,0 +1,73 @@
+# `reversed()` with a `for` loop
+
+```python
+class Luhn:
+
+ def __init__(self, card_num):
+ self.isValid = Luhn.luhny_bin(card_num)
+
+ def valid(self):
+ return self.isValid
+
+ @staticmethod
+ def luhny_tune(num):
+ return dbl - 9 if (dbl := 2 * num) > 9 else dbl
+
+ @staticmethod
+ def luhny_bin(num):
+ total = 0
+ pos = 0
+ for ltr in reversed(num):
+ if ltr.isdigit():
+ if not pos % 2:
+ total+= int(ltr)
+ else:
+ total += Luhn.luhny_tune(int(ltr))
+ pos += 1
+ elif ltr != " ":
+ return False
+ return pos > 1 and not total % 10
+
+```
+
+The `Luhn` object is initialzed with the `card_num` value, which is the number to be validated with the Luhn algorithm.
+The result of the validation is returned from `Luhn`'s `valid()` method.
+In this approach, a member variable is set to the result of running the Luhn algorithm.
+That variable is returned from the `valid()` method.
+
+The methods that do the work have the [`@staticmethod`][static-method] decorator.
+This indicates that the method belongs to the class and is not recreated for every object instance.
+
+In the code example the `luhny_bin` method initializes the `total` and `pos` variables to `0`.
+It then calls [`reversed()`][reversed] on the input and iterates the characters from right to left with a [`for`][for] loop.
+
+The [`isdigit()`][isdigit] method is used to see if the character is a digit.
+The [modulo operator][modulo-operator] is used to check if the character's position is evenly divided by `2`.
+By using the [falsiness][falsiness] of `0`, the [`not` operator][not-operator] can be used instead of comparing equality to `0`.
+It can be thought of as the expression _not_ having a remainder.
+
+If the position is evenly divided by `2`, then it is even, and the character is converted to an [`int()`][int] and added to the total variable.
+
+If the position is odd, then the number is converted to an `int` and is passed to the static method which always doubles it,
+and will subtract `9` from the doubled value if the doubled value is greater than `9`.
+It does this using a [ternary operator][ternary-operator].
+Inside the ternary operator an [assignment expression][assignment-expression] assigns the doubled value to the `dbl` variable with `(dbl := 2 * num)`
+The ternary operator returns `dbl - 9` if `dbl` is greater than `9`, otherwise it returns `dbl`.
+The resulting value is added to the total variable.
+
+Whether the digit is even or odd, the position is incremented by `1`.
+If the character is not a digit, the the function returns `False` if the character is not a space.
+If the character is a space, the loop will go to the next iteration without incrementing the position variable.
+
+After the iteration of the characters is done, the function return if the position is greater than `1` and if the total is evenly divisible by `10`.
+
+[static-method]: https://docs.python.org/3/library/functions.html?#staticmethod
+[reversed]: https://docs.python.org/3/library/functions.html?#reversed
+[for]: https://docs.python.org/3/tutorial/controlflow.html#for-statements
+[isdigit]: https://docs.python.org/3/library/stdtypes.html?#str.isdigit
+[modulo-operator]: https://realpython.com/python-modulo-operator/
+[falsiness]: https://www.pythontutorial.net/python-basics/python-boolean/
+[not-operator]: https://realpython.com/python-not-operator/
+[int]: https://docs.python.org/3/library/functions.html?#int
+[ternary-operator]: https://www.pythontutorial.net/python-basics/python-ternary-operator/
+[assignment-expression]: https://peps.python.org/pep-0572/
From 71d468ae80fefde30849903e433cd99942818d14 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 1 Dec 2022 11:42:51 -0600
Subject: [PATCH 219/826] Create snippet.txt
---
.../practice/luhn/.approaches/reversed-for/snippet.txt | 8 ++++++++
1 file changed, 8 insertions(+)
create mode 100644 exercises/practice/luhn/.approaches/reversed-for/snippet.txt
diff --git a/exercises/practice/luhn/.approaches/reversed-for/snippet.txt b/exercises/practice/luhn/.approaches/reversed-for/snippet.txt
new file mode 100644
index 0000000000..46e9ec07fc
--- /dev/null
+++ b/exercises/practice/luhn/.approaches/reversed-for/snippet.txt
@@ -0,0 +1,8 @@
+if ltr.isdigit():
+ if not pos % 2:
+ total+= int(ltr)
+ else:
+ total += Luhn.luhny_tune(int(ltr))
+ pos += 1
+elif ltr != " ":
+ return False
From 53c714185e9dd20542e37c9ddd84ae3728adaeb5 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Fri, 2 Dec 2022 06:05:56 -0600
Subject: [PATCH 220/826] Create content.md
---
.../replace-reverse-enumerate/content.md | 73 +++++++++++++++++++
1 file changed, 73 insertions(+)
create mode 100644 exercises/practice/luhn/.approaches/replace-reverse-enumerate/content.md
diff --git a/exercises/practice/luhn/.approaches/replace-reverse-enumerate/content.md b/exercises/practice/luhn/.approaches/replace-reverse-enumerate/content.md
new file mode 100644
index 0000000000..8b2a5919d9
--- /dev/null
+++ b/exercises/practice/luhn/.approaches/replace-reverse-enumerate/content.md
@@ -0,0 +1,73 @@
+# `replace()`, reverse, `enumerate()`
+
+```python
+class Luhn:
+
+ def __init__(self, card_num):
+ self.isValid = Luhn.luhny_bin(card_num)
+
+ def valid(self):
+ return self.isValid
+
+ @staticmethod
+ def luhny_tune(num):
+ return dbl - 9 if (dbl := 2 * num) > 9 else dbl
+
+ @staticmethod
+ def luhny_bin(num):
+ num = num.replace(' ', '')
+ if not num.isdigit():
+ return False
+ total = 0
+ for pos, ltr in enumerate(num[::-1]):
+ if not pos % 2:
+ total+= int(ltr)
+ else:
+ total += Luhn.luhny_tune(int(ltr))
+ pos += 1
+ return pos > 1 and not total % 10
+
+```
+
+The `Luhn` object is initialzed with the `card_num` value, which is the number to be validated with the Luhn algorithm.
+The result of the validation is returned from `Luhn`'s `valid()` method.
+In this approach, a member variable is set to the result of running the Luhn algorithm.
+That variable is returned from the `valid()` method.
+
+The methods that do the work have the [`@staticmethod`][static-method] decorator.
+This indicates that the method belongs to the class and is not recreated for every object instance.
+
+In the code example the `luhny_bin` method uses the [`replace()`][replace] method to replace all occurrences of a space in the input with an empty string.
+The [`isdigit()`][isdigit] method is then used to see if all of the remaining characters are digits.
+If not, the function returns `False`.
+
+[Slicing][slicing] syntax (`[::-1`) is used to reverse the characters in the input, which is then passed into the [`enumerate()`][enumerate] method.
+The [`for`][for] loop uses `enumerate()` to iterate the reversed characters in the input, returning the character and its position in the string.
+
+The [modulo operator][modulo-operator] is used to check if the character's position is evenly divided by `2`.
+By using the [falsiness][falsiness] of `0`, the [`not` operator][not-operator] can be used instead of comparing equality to `0`.
+It can be thought of as the expression _not_ having a remainder.
+
+If the position is evenly divided by `2`, then it is even, and the character is converted to an [`int()`][int] and added to the total variable.
+
+If the position is odd, then the number is converted to an `int` and is passed to the static method which always doubles it,
+and will subtract `9` from the doubled value if the doubled value is greater than `9`.
+It does this using a [ternary operator][ternary-operator].
+Inside the ternary operator an [assignment expression][assignment-expression] assigns the doubled value to the `dbl` variable with `(dbl := 2 * num)`.
+The ternary operator returns `dbl - 9` if `dbl` is greater than `9`, otherwise it returns `dbl`.
+The resulting value is added to the total variable.
+
+After the iteration of the characters is done, the function return if the position is greater than `1` and if the total is evenly divisible by `10`.
+
+[static-method]: https://docs.python.org/3/library/functions.html?#staticmethod
+[replace]: https://docs.python.org/3/library/stdtypes.html?#str.replace
+[isdigit]: https://docs.python.org/3/library/stdtypes.html?#str.isdigit
+[enumerate]: https://docs.python.org/3/library/functions.html?#enumerate
+[slicing]: https://www.learnbyexample.org/python-string-slicing/
+[for]: https://docs.python.org/3/tutorial/controlflow.html#for-statements
+[modulo-operator]: https://realpython.com/python-modulo-operator/
+[falsiness]: https://www.pythontutorial.net/python-basics/python-boolean/
+[not-operator]: https://realpython.com/python-not-operator/
+[int]: https://docs.python.org/3/library/functions.html?#int
+[ternary-operator]: https://www.pythontutorial.net/python-basics/python-ternary-operator/
+[assignment-expression]: https://peps.python.org/pep-0572/
From ea5ec24997851c517069c14666d37309914be7e3 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Fri, 2 Dec 2022 06:06:21 -0600
Subject: [PATCH 221/826] Update content.md
---
exercises/practice/luhn/.approaches/reversed-for/content.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/exercises/practice/luhn/.approaches/reversed-for/content.md b/exercises/practice/luhn/.approaches/reversed-for/content.md
index 24d2897b32..58bd061b2b 100644
--- a/exercises/practice/luhn/.approaches/reversed-for/content.md
+++ b/exercises/practice/luhn/.approaches/reversed-for/content.md
@@ -51,7 +51,7 @@ If the position is evenly divided by `2`, then it is even, and the character is
If the position is odd, then the number is converted to an `int` and is passed to the static method which always doubles it,
and will subtract `9` from the doubled value if the doubled value is greater than `9`.
It does this using a [ternary operator][ternary-operator].
-Inside the ternary operator an [assignment expression][assignment-expression] assigns the doubled value to the `dbl` variable with `(dbl := 2 * num)`
+Inside the ternary operator an [assignment expression][assignment-expression] assigns the doubled value to the `dbl` variable with `(dbl := 2 * num)`.
The ternary operator returns `dbl - 9` if `dbl` is greater than `9`, otherwise it returns `dbl`.
The resulting value is added to the total variable.
From 934dde9a684ab51aa9bedcd5f601150dcef53893 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Fri, 2 Dec 2022 06:07:54 -0600
Subject: [PATCH 222/826] Create snippet.txt
---
.../luhn/.approaches/replace-reverse-enumerate/snippet.txt | 7 +++++++
1 file changed, 7 insertions(+)
create mode 100644 exercises/practice/luhn/.approaches/replace-reverse-enumerate/snippet.txt
diff --git a/exercises/practice/luhn/.approaches/replace-reverse-enumerate/snippet.txt b/exercises/practice/luhn/.approaches/replace-reverse-enumerate/snippet.txt
new file mode 100644
index 0000000000..d02d2c5a2d
--- /dev/null
+++ b/exercises/practice/luhn/.approaches/replace-reverse-enumerate/snippet.txt
@@ -0,0 +1,7 @@
+for pos, ltr in enumerate(num[::-1]):
+ if not pos % 2:
+ total+= int(ltr)
+ else:
+ total += Luhn.luhny_tune(int(ltr))
+ pos += 1
+return pos > 1 and not total % 10
From 6471b042a34fde874d708255e54143d11ffa5782 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Fri, 2 Dec 2022 06:57:46 -0600
Subject: [PATCH 223/826] Update Benchmark.py
---
.../practice/luhn/.articles/performance/code/Benchmark.py | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/exercises/practice/luhn/.articles/performance/code/Benchmark.py b/exercises/practice/luhn/.articles/performance/code/Benchmark.py
index 66afd6f49e..21d4d2f26b 100644
--- a/exercises/practice/luhn/.articles/performance/code/Benchmark.py
+++ b/exercises/practice/luhn/.articles/performance/code/Benchmark.py
@@ -83,9 +83,7 @@ def luhny_tune(num):
@staticmethod
def luhny_bin(pos, sum, chars):
if not chars:
- if pos < 2:
- return False
- return sum % 10 == 0
+ return pos > 1 and sum % 10 == 0
else:
head, *tail = chars
if head.isdigit():
From 3668b80b19c5a015ec0393b99d6e1bc846856a8b Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Fri, 2 Dec 2022 07:05:53 -0600
Subject: [PATCH 224/826] Update content.md
---
exercises/practice/luhn/.approaches/reversed-for/content.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/exercises/practice/luhn/.approaches/reversed-for/content.md b/exercises/practice/luhn/.approaches/reversed-for/content.md
index 58bd061b2b..291f5df13a 100644
--- a/exercises/practice/luhn/.approaches/reversed-for/content.md
+++ b/exercises/practice/luhn/.approaches/reversed-for/content.md
@@ -42,7 +42,7 @@ In the code example the `luhny_bin` method initializes the `total` and `pos` var
It then calls [`reversed()`][reversed] on the input and iterates the characters from right to left with a [`for`][for] loop.
The [`isdigit()`][isdigit] method is used to see if the character is a digit.
-The [modulo operator][modulo-operator] is used to check if the character's position is evenly divided by `2`.
+If so, the [modulo operator][modulo-operator] is used to check if the character's position is evenly divided by `2`.
By using the [falsiness][falsiness] of `0`, the [`not` operator][not-operator] can be used instead of comparing equality to `0`.
It can be thought of as the expression _not_ having a remainder.
From 7322348adfae497d6036c9f7a400def098b255df Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Fri, 2 Dec 2022 07:06:48 -0600
Subject: [PATCH 225/826] Update Benchmark.py
---
exercises/practice/luhn/.articles/performance/code/Benchmark.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/exercises/practice/luhn/.articles/performance/code/Benchmark.py b/exercises/practice/luhn/.articles/performance/code/Benchmark.py
index 21d4d2f26b..27372f5282 100644
--- a/exercises/practice/luhn/.articles/performance/code/Benchmark.py
+++ b/exercises/practice/luhn/.articles/performance/code/Benchmark.py
@@ -87,7 +87,7 @@ def luhny_bin(pos, sum, chars):
else:
head, *tail = chars
if head.isdigit():
- if pos % 2 == 0:
+ if not pos % 2:
return Luhn.luhny_bin(pos + 1, sum + int(head), tail)
else:
return Luhn.luhny_bin(pos + 1, sum + Luhn.luhny_tune(int(head)), tail)
From e8ab1cd663c4140bdf64306772b785b139a4e2eb Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Fri, 2 Dec 2022 07:22:37 -0600
Subject: [PATCH 226/826] Update content.md
---
.../luhn/.approaches/recursion/content.md | 72 +++++++++++++++++--
1 file changed, 68 insertions(+), 4 deletions(-)
diff --git a/exercises/practice/luhn/.approaches/recursion/content.md b/exercises/practice/luhn/.approaches/recursion/content.md
index a4f682c4e7..0c146cbdcd 100644
--- a/exercises/practice/luhn/.approaches/recursion/content.md
+++ b/exercises/practice/luhn/.approaches/recursion/content.md
@@ -15,13 +15,11 @@ class Luhn:
@staticmethod
def luhny_bin(pos, sum, chars):
if not chars:
- if pos < 2:
- return False
- return sum % 10 == 0
+ return pos > 1 and sum % 10 == 0
else:
head, *tail = chars
if head.isdigit():
- if pos % 2 == 0:
+ if not pos % 2:
return Luhn.luhny_bin(pos + 1, sum + int(head), tail)
else:
return Luhn.luhny_bin(pos + 1, sum + Luhn.luhny_tune(int(head)), tail)
@@ -30,3 +28,69 @@ class Luhn:
return False
```
+
+The `Luhn` object is initialzed with the `card_num` value, which is the number to be validated with the Luhn algorithm.
+The result of the validation is returned from `Luhn`'s `valid()` method.
+In this approach, a member variable is set to the result of running the Luhn algorithm.
+That variable is returned from the `valid()` method.
+
+The methods that do the work have the [`@staticmethod`][static-method] decorator.
+This indicates that the method belongs to the class and is not recreated for every object instance.
+
+In the code example the `__init__` method uses [slicing][slicing] syntax (`[::-1]`) to reverse the characters in the input,
+which is then passed into the [list][list] constructor.
+The `luhny_bin()` method takes that list, along with two `0` values that represent the initialized values for the position and the sum.
+
+The `luhny_bin()` can call itself, which is a behavior called [recursion][recursion].
+Since `luhny_bin()` can call itself, the first thing it does is to check that it is done calling itself.
+
+```exercism/note
+This check is called the terminating condition.
+It's critical to have a terminating condition, since every call of a recursive function to itself places another
+[frame on the stack](https://realpython.com/lessons/stack-frames-and-stack-traces/#:~:text=A%20stack%20frame%20represents%20a,is%20removed%20from%20the%20stack.).
+If there is no terminating condition, then the recursive function will keep calling itself until the stack runs out of space
+and a stack overflow error will occur.
+```
+
+The `luhny_bin()` mtehod should terminate when there are no more characters to process.
+By using the [falsiness][falsiness] of an empty list, the [`not` operator][not-operator] can be used instead of comparing the `len()` of the list to `0`.
+When all of the characters have been iterated, the function returns if the position is greater than `1` and if the sum is evenly divisible by `10`.
+
+While there are still characters in the list to iterate, the list is [destructured][destructure] into `head, *tail`.
+The [`isdigit()`][isdigit] method is used to see if the head character is a digit.
+If so, the [modulo operator][modulo-operator] is used to check if the character's position is evenly divided by `2`.
+By using the [falsiness][falsiness] of `0`, the [`not` operator][not-operator] can be used instead of comparing equality to `0`.
+It can be thought of as the expression _not_ having a remainder.
+
+If the position is evenly divided by `2`, then it is even, and the character is converted to an [`int()`][int] and will be added to the sum variable.
+
+If the position is odd, then the number is converted to an `int` and is passed to the static method which always doubles it,
+and will subtract `9` from the doubled value if the doubled value is greater than `9`.
+It does this using a [ternary operator][ternary-operator].
+Inside the ternary operator an [assignment expression][assignment-expression] assigns the doubled value to the `dbl` variable with `(dbl := 2 * num)`.
+The ternary operator returns `dbl - 9` if `dbl` is greater than `9`, otherwise it returns `dbl`.
+The resulting value will be added to the sum variable.
+
+Whether the digit is even or odd, the position is added to `1` when it and the sum are passed into the next call of the recursive method.
+Also passed in is the tail of the list, which is the list of all the remaining characters after the head character.
+Note that the sum and position variables are not being directly changed.
+(In other words, they are not being mutated.)
+The new sum and position values are calculated as the new arguments to the recursive function.
+
+If the head character is a space, the recursive function calls itself with the same position and sum values, and the tail.
+
+If the head character is neither a digit or a space, the function returns `False`.
+
+[static-method]: https://docs.python.org/3/library/functions.html?#staticmethod
+[slicing]: https://www.learnbyexample.org/python-string-slicing/
+[list]: https://docs.python.org/3/library/functions.html?#func-list
+[recursion]: https://realpython.com/python-recursion/
+[stack-frame]: https://realpython.com/lessons/stack-frames-and-stack-traces/#:~:text=A%20stack%20frame%20represents%20a,is%20removed%20from%20the%20stack.
+[destructure]: https://riptutorial.com/python/example/14981/destructuring-assignment
+[isdigit]: https://docs.python.org/3/library/stdtypes.html?#str.isdigit
+[modulo-operator]: https://realpython.com/python-modulo-operator/
+[falsiness]: https://www.pythontutorial.net/python-basics/python-boolean/
+[not-operator]: https://realpython.com/python-not-operator/
+[int]: https://docs.python.org/3/library/functions.html?#int
+[ternary-operator]: https://www.pythontutorial.net/python-basics/python-ternary-operator/
+[assignment-expression]: https://peps.python.org/pep-0572/
From fa37586695db8a02f73e3ad2bd4e2c9b72ccd773 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Fri, 2 Dec 2022 07:23:02 -0600
Subject: [PATCH 227/826] Update snippet.txt
---
exercises/practice/luhn/.approaches/recursion/snippet.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/exercises/practice/luhn/.approaches/recursion/snippet.txt b/exercises/practice/luhn/.approaches/recursion/snippet.txt
index c59dd9a5fe..be95f0818e 100644
--- a/exercises/practice/luhn/.approaches/recursion/snippet.txt
+++ b/exercises/practice/luhn/.approaches/recursion/snippet.txt
@@ -1,6 +1,6 @@
head, *tail = chars
if head.isdigit():
- if pos % 2 == 0:
+ if not pos % 2:
return Luhn.luhny_bin(pos + 1, sum + int(head), tail)
else:
return Luhn.luhny_bin(pos + 1, sum + Luhn.luhny_tune(int(head)), tail)
From e29bbeb1cd178d3872c6bb8112ce60801d3dab51 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Fri, 2 Dec 2022 07:24:15 -0600
Subject: [PATCH 228/826] Update content.md
---
exercises/practice/luhn/.approaches/reversed-for/content.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/exercises/practice/luhn/.approaches/reversed-for/content.md b/exercises/practice/luhn/.approaches/reversed-for/content.md
index 291f5df13a..fcc7a58fe6 100644
--- a/exercises/practice/luhn/.approaches/reversed-for/content.md
+++ b/exercises/practice/luhn/.approaches/reversed-for/content.md
@@ -38,7 +38,7 @@ That variable is returned from the `valid()` method.
The methods that do the work have the [`@staticmethod`][static-method] decorator.
This indicates that the method belongs to the class and is not recreated for every object instance.
-In the code example the `luhny_bin` method initializes the `total` and `pos` variables to `0`.
+In the code example the `luhny_bin()` method initializes the `total` and `pos` variables to `0`.
It then calls [`reversed()`][reversed] on the input and iterates the characters from right to left with a [`for`][for] loop.
The [`isdigit()`][isdigit] method is used to see if the character is a digit.
From 123c50163aa64c0ed466a6217a4700b915946e9e Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Fri, 2 Dec 2022 07:24:41 -0600
Subject: [PATCH 229/826] Update content.md
---
.../luhn/.approaches/replace-reverse-enumerate/content.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/exercises/practice/luhn/.approaches/replace-reverse-enumerate/content.md b/exercises/practice/luhn/.approaches/replace-reverse-enumerate/content.md
index 8b2a5919d9..812f51909c 100644
--- a/exercises/practice/luhn/.approaches/replace-reverse-enumerate/content.md
+++ b/exercises/practice/luhn/.approaches/replace-reverse-enumerate/content.md
@@ -37,7 +37,7 @@ That variable is returned from the `valid()` method.
The methods that do the work have the [`@staticmethod`][static-method] decorator.
This indicates that the method belongs to the class and is not recreated for every object instance.
-In the code example the `luhny_bin` method uses the [`replace()`][replace] method to replace all occurrences of a space in the input with an empty string.
+In the code example the `luhny_bin()` method uses the [`replace()`][replace] method to replace all occurrences of a space in the input with an empty string.
The [`isdigit()`][isdigit] method is then used to see if all of the remaining characters are digits.
If not, the function returns `False`.
From 78f399be4df4c57636d0eba52ba3558b76cffbab Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Fri, 2 Dec 2022 07:27:23 -0600
Subject: [PATCH 230/826] Update content.md
---
.../practice/luhn/.approaches/recursion/content.md | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/exercises/practice/luhn/.approaches/recursion/content.md b/exercises/practice/luhn/.approaches/recursion/content.md
index 0c146cbdcd..b6f5f67f7f 100644
--- a/exercises/practice/luhn/.approaches/recursion/content.md
+++ b/exercises/practice/luhn/.approaches/recursion/content.md
@@ -52,9 +52,9 @@ If there is no terminating condition, then the recursive function will keep call
and a stack overflow error will occur.
```
-The `luhny_bin()` mtehod should terminate when there are no more characters to process.
+The `luhny_bin()` method should terminate when there are no more characters to process.
By using the [falsiness][falsiness] of an empty list, the [`not` operator][not-operator] can be used instead of comparing the `len()` of the list to `0`.
-When all of the characters have been iterated, the function returns if the position is greater than `1` and if the sum is evenly divisible by `10`.
+When all of the characters have been iterated, the method returns if the position is greater than `1` and if the sum is evenly divisible by `10`.
While there are still characters in the list to iterate, the list is [destructured][destructure] into `head, *tail`.
The [`isdigit()`][isdigit] method is used to see if the head character is a digit.
@@ -75,11 +75,11 @@ Whether the digit is even or odd, the position is added to `1` when it and the s
Also passed in is the tail of the list, which is the list of all the remaining characters after the head character.
Note that the sum and position variables are not being directly changed.
(In other words, they are not being mutated.)
-The new sum and position values are calculated as the new arguments to the recursive function.
+The new sum and position values are calculated as the new arguments to the recursive method.
-If the head character is a space, the recursive function calls itself with the same position and sum values, and the tail.
+If the head character is a space, the recursive method calls itself with the same position and sum values, and the tail.
-If the head character is neither a digit or a space, the function returns `False`.
+If the head character is neither a digit or a space, the method returns `False`.
[static-method]: https://docs.python.org/3/library/functions.html?#staticmethod
[slicing]: https://www.learnbyexample.org/python-string-slicing/
From 0060e552cbd2531a559a1c28440e6a5b3ee3506c Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Fri, 2 Dec 2022 07:28:51 -0600
Subject: [PATCH 231/826] Update content.md
---
.../luhn/.approaches/replace-reverse-enumerate/content.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/exercises/practice/luhn/.approaches/replace-reverse-enumerate/content.md b/exercises/practice/luhn/.approaches/replace-reverse-enumerate/content.md
index 812f51909c..fd4e49b12c 100644
--- a/exercises/practice/luhn/.approaches/replace-reverse-enumerate/content.md
+++ b/exercises/practice/luhn/.approaches/replace-reverse-enumerate/content.md
@@ -39,7 +39,7 @@ This indicates that the method belongs to the class and is not recreated for eve
In the code example the `luhny_bin()` method uses the [`replace()`][replace] method to replace all occurrences of a space in the input with an empty string.
The [`isdigit()`][isdigit] method is then used to see if all of the remaining characters are digits.
-If not, the function returns `False`.
+If not, the method returns `False`.
[Slicing][slicing] syntax (`[::-1`) is used to reverse the characters in the input, which is then passed into the [`enumerate()`][enumerate] method.
The [`for`][for] loop uses `enumerate()` to iterate the reversed characters in the input, returning the character and its position in the string.
@@ -57,7 +57,7 @@ Inside the ternary operator an [assignment expression][assignment-expression] as
The ternary operator returns `dbl - 9` if `dbl` is greater than `9`, otherwise it returns `dbl`.
The resulting value is added to the total variable.
-After the iteration of the characters is done, the function return if the position is greater than `1` and if the total is evenly divisible by `10`.
+After the iteration of the characters is done, the method returns if the position is greater than `1` and if the total is evenly divisible by `10`.
[static-method]: https://docs.python.org/3/library/functions.html?#staticmethod
[replace]: https://docs.python.org/3/library/stdtypes.html?#str.replace
From 4bbdb16bb5f66e5ab1a4726e2beb543f791aa374 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Fri, 2 Dec 2022 07:30:16 -0600
Subject: [PATCH 232/826] Update content.md
---
exercises/practice/luhn/.approaches/reversed-for/content.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/exercises/practice/luhn/.approaches/reversed-for/content.md b/exercises/practice/luhn/.approaches/reversed-for/content.md
index fcc7a58fe6..abf5a591ca 100644
--- a/exercises/practice/luhn/.approaches/reversed-for/content.md
+++ b/exercises/practice/luhn/.approaches/reversed-for/content.md
@@ -56,10 +56,10 @@ The ternary operator returns `dbl - 9` if `dbl` is greater than `9`, otherwise i
The resulting value is added to the total variable.
Whether the digit is even or odd, the position is incremented by `1`.
-If the character is not a digit, the the function returns `False` if the character is not a space.
+If the character is not a digit, the method returns `False` if the character is not a space.
If the character is a space, the loop will go to the next iteration without incrementing the position variable.
-After the iteration of the characters is done, the function return if the position is greater than `1` and if the total is evenly divisible by `10`.
+After the iteration of the characters is done, the method returns if the position is greater than `1` and if the total is evenly divisible by `10`.
[static-method]: https://docs.python.org/3/library/functions.html?#staticmethod
[reversed]: https://docs.python.org/3/library/functions.html?#reversed
From b43688d439f89128bedb5bf15f3b1abb88328423 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Fri, 2 Dec 2022 09:04:14 -0600
Subject: [PATCH 233/826] Create content.md
---
exercises/practice/luhn/.articles/content.md | 31 ++++++++++++++++++++
1 file changed, 31 insertions(+)
create mode 100644 exercises/practice/luhn/.articles/content.md
diff --git a/exercises/practice/luhn/.articles/content.md b/exercises/practice/luhn/.articles/content.md
new file mode 100644
index 0000000000..5069a2e53d
--- /dev/null
+++ b/exercises/practice/luhn/.articles/content.md
@@ -0,0 +1,31 @@
+# Performance
+
+In this approach, we'll find out how to most efficiently validate a number with the Luhn algorithm.
+
+The [approaches page][approaches] lists two idiomatic approaches to this exercise:
+
+1. [Using `reversed()` with a `for` loop][approach-reversed-for]
+2. [Using `replace()`, reverse, `enumerate()`][approach-replace-reverse-enumerate]
+
+For our performance investigation, we'll also include a third approach that [uses recursion][approach-recursion].
+
+## Benchmarks
+
+To benchmark the approaches, we wrote a [small benchmark application][benchmark-application] using the [`timeit`][timeit] library.
+
+```
+reversed for: 1.0783263299963438e-05
+replace reverse enumerate: 9.933844099985436e-06
+recursion: 2.4321520800003783e-05
+```
+
+At an avergae time per call of `9934` nanoseconds, the `replace()`, reverse, `enumerate()` approach was the fastest.
+The `reversed()` with a `for` loop approach was a bit slower, at `10783` nanoseconds.
+The recursive approach was much slower, at about `24321` nanoseconds.
+
+[approaches]: https://exercism.org/tracks/python/exercises/luhn/approaches
+[approach-reversed-for]: https://exercism.org/tracks/python/exercises/luhn/approaches/reversed-for
+[approach-replace-reverse-enumerate]: https://exercism.org/tracks/python/exercises/luhn/approaches/replace-reverse-enumerate
+[approach-recursion]: https://exercism.org/tracks/python/exercises/luhn/approaches/recursion
+[benchmark-application]: https://github.com/exercism/python/blob/main/exercises/practice/luhn/.articles/performance/code/Benchmark.py
+[timeit]: https://docs.python.org/3/library/timeit.html
From a5b79c0a016b3e433be82496d1d0d802495b6df0 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Fri, 2 Dec 2022 09:05:58 -0600
Subject: [PATCH 234/826] Update introduction.md
---
exercises/practice/luhn/.approaches/introduction.md | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/exercises/practice/luhn/.approaches/introduction.md b/exercises/practice/luhn/.approaches/introduction.md
index 904d1e2036..2eff18ae07 100644
--- a/exercises/practice/luhn/.approaches/introduction.md
+++ b/exercises/practice/luhn/.approaches/introduction.md
@@ -87,6 +87,13 @@ Besides the aforementioned, idiomatic approaches, you could also approach the ex
Another approach can use recursion to validate the number.
For more information, check the [recursion approach][approach-recursion].
+## Which approach to use?
+
+The `replace()`, reverse, `enumerate()` approach benchmarked the fastest.
+
+To compare performance of the approaches, check the [Performance article][article-performance].
+
[approach-reversed-for]: https://exercism.org/tracks/python/exercises/luhn/approaches/reversed-for
[approach-replace-reverse-enumerate]: https://exercism.org/tracks/python/exercises/luhn/approaches/replace-reverse-enumerate
[approach-recursion]: https://exercism.org/tracks/python/exercises/luhn/approaches/recursion
+[article-performance]: https://exercism.org/tracks/python/exercises/luhn/articles/performance
From b0a957b6cf7d41ff39b1b9f97158e3046b2ac6a7 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Wed, 7 Dec 2022 06:05:49 -0600
Subject: [PATCH 235/826] Rename exercises/practice/luhn/.articles/content.md
to exercises/practice/luhn/.articles/performance/content.md
---
exercises/practice/luhn/.articles/{ => performance}/content.md | 0
1 file changed, 0 insertions(+), 0 deletions(-)
rename exercises/practice/luhn/.articles/{ => performance}/content.md (100%)
diff --git a/exercises/practice/luhn/.articles/content.md b/exercises/practice/luhn/.articles/performance/content.md
similarity index 100%
rename from exercises/practice/luhn/.articles/content.md
rename to exercises/practice/luhn/.articles/performance/content.md
From de3c41e0deae222c595406ddfa03906a9d200a38 Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Fri, 2 Dec 2022 14:42:14 -0800
Subject: [PATCH 236/826] Start of linked-list update.
---
.../practice/linked-list/.meta/tests.toml | 67 +++++++++++++++++++
1 file changed, 67 insertions(+)
create mode 100644 exercises/practice/linked-list/.meta/tests.toml
diff --git a/exercises/practice/linked-list/.meta/tests.toml b/exercises/practice/linked-list/.meta/tests.toml
new file mode 100644
index 0000000000..96906d2cc7
--- /dev/null
+++ b/exercises/practice/linked-list/.meta/tests.toml
@@ -0,0 +1,67 @@
+# This is an auto-generated file.
+#
+# Regenerating this file via `configlet sync` will:
+# - Recreate every `description` key/value pair
+# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications
+# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion)
+# - Preserve any other key/value pair
+#
+# As user-added comments (using the # character) will be removed when this file
+# is regenerated, comments can be added via a `comment` key.
+
+[7f7e3987-b954-41b8-8084-99beca08752c]
+description = "pop gets element from the list"
+
+[c3f67e5d-cfa2-4c3e-a18f-7ce999c3c885]
+description = "push/pop respectively add/remove at the end of the list"
+
+[00ea24ce-4f5c-4432-abb4-cc6e85462657]
+description = "shift gets an element from the list"
+
+[37962ee0-3324-4a29-b588-5a4c861e6564]
+description = "shift gets first element from the list"
+
+[30a3586b-e9dc-43fb-9a73-2770cec2c718]
+description = "unshift adds element at start of the list"
+
+[042f71e4-a8a7-4cf0-8953-7e4f3a21c42d]
+description = "pop, push, shift, and unshift can be used in any order"
+
+[88f65c0c-4532-4093-8295-2384fb2f37df]
+description = "count an empty list"
+
+[fc055689-5cbe-4cd9-b994-02e2abbb40a5]
+description = "count a list with items"
+
+[8272cef5-130d-40ea-b7f6-5ffd0790d650]
+description = "count is correct after mutation"
+
+[229b8f7a-bd8a-4798-b64f-0dc0bb356d95]
+description = "popping to empty doesn't break the list"
+
+[4e1948b4-514e-424b-a3cf-a1ebbfa2d1ad]
+description = "shifting to empty doesn't break the list"
+
+[e8f7c600-d597-4f79-949d-8ad8bae895a6]
+description = "deletes the only element"
+
+[fd65e422-51f3-45c0-9fd0-c33da638f89b]
+description = "deletes the element with the specified value from the list"
+
+[59db191a-b17f-4ab7-9c5c-60711ec1d013]
+description = "deletes the element with the specified value from the list, re-assigns tail"
+
+[58242222-5d39-415b-951d-8128247f8993]
+description = "deletes the element with the specified value from the list, re-assigns head"
+
+[ee3729ee-3405-4bd2-9bad-de0d4aa5d647]
+description = "deletes the first of two elements"
+
+[47e3b3b4-b82c-4c23-8c1a-ceb9b17cb9fb]
+description = "deletes the second of two elements"
+
+[7b420958-f285-4922-b8f9-10d9dcab5179]
+description = "delete does not modify the list if the element is not found"
+
+[7e04828f-6082-44e3-a059-201c63252a76]
+description = "deletes only the first occurrence"
From 228f421a13f953f98ec4bd0db2b7aa2e995f6cee Mon Sep 17 00:00:00 2001
From: Meatball
Date: Sat, 3 Dec 2022 18:50:33 +0100
Subject: [PATCH 237/826] Added Jinja template
---
.../practice/linked-list/.meta/config.json | 3 +-
.../practice/linked-list/.meta/example.py | 20 +-
.../practice/linked-list/.meta/template.j2 | 31 +++
.../practice/linked-list/linked_list_test.py | 196 +++++++++++++-----
4 files changed, 189 insertions(+), 61 deletions(-)
create mode 100644 exercises/practice/linked-list/.meta/template.j2
diff --git a/exercises/practice/linked-list/.meta/config.json b/exercises/practice/linked-list/.meta/config.json
index c2d286f667..894e0a5d3a 100644
--- a/exercises/practice/linked-list/.meta/config.json
+++ b/exercises/practice/linked-list/.meta/config.json
@@ -9,7 +9,8 @@
"Mofeywalker",
"N-Parsons",
"pheanex",
- "tqa236"
+ "tqa236",
+ "meatball"
],
"files": {
"solution": [
diff --git a/exercises/practice/linked-list/.meta/example.py b/exercises/practice/linked-list/.meta/example.py
index 280509f244..997831bd57 100644
--- a/exercises/practice/linked-list/.meta/example.py
+++ b/exercises/practice/linked-list/.meta/example.py
@@ -54,8 +54,18 @@ def unshift(self, value):
def __len__(self):
return self.length
- def __iter__(self):
- current_node = self.head
- while current_node:
- yield current_node.value
- current_node = current_node.succeeding
+ def delete(self, delete):
+ node = self.head
+ while node:
+ if node.value == delete:
+ if node.prev:
+ node.prev.succeeding = node.succeeding
+ else:
+ self.head = node.succeeding
+ if node.succeeding:
+ node.succeeding.prev = node.prev
+ else:
+ self.tail = node.prev
+ self.length -= 1
+ break
+ node = node.succeeding
\ No newline at end of file
diff --git a/exercises/practice/linked-list/.meta/template.j2 b/exercises/practice/linked-list/.meta/template.j2
new file mode 100644
index 0000000000..c765d68a32
--- /dev/null
+++ b/exercises/practice/linked-list/.meta/template.j2
@@ -0,0 +1,31 @@
+{%- import "generator_macros.j2" as macros with context -%}
+{% macro test_case(case) -%}
+ {%- set input = case["input"]["operations"] -%}
+ def test_{{ case["description"] | to_snake }}(self):
+ lst = LinkedList()
+ {% for x in input -%}
+ {%- if "push" == x["operation"] -%}
+ lst.push({{ x["value"] }})
+ {%- elif "pop" == x["operation"] and x["expected"] -%}
+ self.assertEqual(lst.pop(), {{ x["expected"] }})
+ {%- elif "pop" == x["operation"] -%}
+ lst.pop()
+ {%- elif "shift" == x["operation"] and x["expected"] -%}
+ self.assertEqual(lst.shift(), {{ x["expected"] }})
+ {%- elif "shift" == x["operation"] -%}
+ lst.shift()
+ {%- elif "unshift" == x["operation"] -%}
+ lst.unshift({{ x["value"] }})
+ {%- elif "delete" == x["operation"] -%}
+ lst.delete({{ x["value"] }})
+ {%- elif "count" == x["operation"] -%}
+ self.assertEqual(len(lst), {{ x["expected"] }})
+ {%- endif %}
+ {% endfor %}
+{%- endmacro %}
+{{ macros.header(["LinkedList"]) }}
+
+class {{ exercise | camel_case }}Test(unittest.TestCase):
+ {% for case in cases -%}
+ {{ test_case(case) }}
+ {% endfor %}
diff --git a/exercises/practice/linked-list/linked_list_test.py b/exercises/practice/linked-list/linked_list_test.py
index 74a5314781..822948b823 100644
--- a/exercises/practice/linked-list/linked_list_test.py
+++ b/exercises/practice/linked-list/linked_list_test.py
@@ -1,82 +1,168 @@
import unittest
-from linked_list import LinkedList
+from linked_list import (
+ LinkedList,
+)
+
+# Tests adapted from `problem-specifications//canonical-data.json`
class LinkedListTest(unittest.TestCase):
- def test_push_pop(self):
+ def test_pop_gets_element_from_the_list(self):
+ lst = LinkedList()
+ lst.push(7)
+ self.assertEqual(lst.pop(), 7)
+
+ def test_push_pop_respectively_add_remove_at_the_end_of_the_list(self):
+ lst = LinkedList()
+ lst.push(11)
+ lst.push(13)
+ self.assertEqual(lst.pop(), 13)
+ self.assertEqual(lst.pop(), 11)
+
+ def test_shift_gets_an_element_from_the_list(self):
lst = LinkedList()
- lst.push(10)
- lst.push(20)
- self.assertEqual(lst.pop(), 20)
- self.assertEqual(lst.pop(), 10)
+ lst.push(17)
+ self.assertEqual(lst.shift(), 17)
- def test_push_shift(self):
+ def test_shift_gets_first_element_from_the_list(self):
lst = LinkedList()
- lst.push(10)
- lst.push(20)
- self.assertEqual(lst.shift(), 10)
- self.assertEqual(lst.shift(), 20)
+ lst.push(23)
+ lst.push(5)
+ self.assertEqual(lst.shift(), 23)
+ self.assertEqual(lst.shift(), 5)
- def test_unshift_shift(self):
+ def test_unshift_adds_element_at_start_of_the_list(self):
lst = LinkedList()
- lst.unshift(10)
- lst.unshift(20)
- self.assertEqual(lst.shift(), 20)
- self.assertEqual(lst.shift(), 10)
+ lst.unshift(23)
+ lst.unshift(5)
+ self.assertEqual(lst.shift(), 5)
+ self.assertEqual(lst.shift(), 23)
- def test_unshift_pop(self):
+ def test_pop_push_shift_and_unshift_can_be_used_in_any_order(self):
lst = LinkedList()
- lst.unshift(10)
- lst.unshift(20)
- self.assertEqual(lst.pop(), 10)
- self.assertEqual(lst.pop(), 20)
+ lst.push(1)
+ lst.push(2)
+ self.assertEqual(lst.pop(), 2)
+ lst.push(3)
+ self.assertEqual(lst.shift(), 1)
+ lst.unshift(4)
+ lst.push(5)
+ self.assertEqual(lst.shift(), 4)
+ self.assertEqual(lst.pop(), 5)
+ self.assertEqual(lst.shift(), 3)
- def test_all(self):
+ def test_count_an_empty_list(self):
lst = LinkedList()
- lst.push(10)
- lst.push(20)
- self.assertEqual(lst.pop(), 20)
- lst.push(30)
- self.assertEqual(lst.shift(), 10)
- lst.unshift(40)
- lst.push(50)
- self.assertEqual(lst.shift(), 40)
- self.assertEqual(lst.pop(), 50)
- self.assertEqual(lst.shift(), 30)
+ self.assertEqual(len(lst), 0)
- @unittest.skip("extra-credit")
- def test_length(self):
+ def test_count_a_list_with_items(self):
lst = LinkedList()
- lst.push(10)
- lst.push(20)
+ lst.push(37)
+ lst.push(1)
+ self.assertEqual(len(lst), 2)
+
+ def test_count_is_correct_after_mutation(self):
+ lst = LinkedList()
+ lst.push(31)
+ self.assertEqual(len(lst), 1)
+ lst.unshift(43)
self.assertEqual(len(lst), 2)
lst.shift()
self.assertEqual(len(lst), 1)
lst.pop()
self.assertEqual(len(lst), 0)
- @unittest.skip("extra-credit")
- def test_iterator(self):
+ def test_popping_to_empty_doesn_t_break_the_list(self):
lst = LinkedList()
- lst.push(10)
- lst.push(20)
- iterator = iter(lst)
- self.assertEqual(next(iterator), 10)
- self.assertEqual(next(iterator), 20)
+ lst.push(41)
+ lst.push(59)
+ lst.pop()
+ lst.pop()
+ lst.push(47)
+ self.assertEqual(len(lst), 1)
+ self.assertEqual(lst.pop(), 47)
- @unittest.skip("extra-credit")
- def test_iterator_independence(self):
+ def test_shifting_to_empty_doesn_t_break_the_list(self):
lst = LinkedList()
- lst.push(10)
- lst.push(20)
- iterator_a = iter(lst)
- iterator_b = iter(lst)
- self.assertEqual(next(iterator_a), 10)
- self.assertEqual(next(iterator_a), 20)
- self.assertEqual(next(iterator_b), 10)
- self.assertEqual(next(iterator_b), 20)
+ lst.push(41)
+ lst.push(59)
+ lst.shift()
+ lst.shift()
+ lst.push(47)
+ self.assertEqual(len(lst), 1)
+ self.assertEqual(lst.shift(), 47)
+ def test_deletes_the_only_element(self):
+ lst = LinkedList()
+ lst.push(61)
+ lst.delete(61)
+ self.assertEqual(len(lst), 0)
-if __name__ == '__main__':
- unittest.main()
+ def test_deletes_the_element_with_the_specified_value_from_the_list(self):
+ lst = LinkedList()
+ lst.push(71)
+ lst.push(83)
+ lst.push(79)
+ lst.delete(83)
+ self.assertEqual(len(lst), 2)
+ self.assertEqual(lst.pop(), 79)
+ self.assertEqual(lst.shift(), 71)
+
+ def test_deletes_the_element_with_the_specified_value_from_the_list_re_assigns_tail(
+ self,
+ ):
+ lst = LinkedList()
+ lst.push(71)
+ lst.push(83)
+ lst.push(79)
+ lst.delete(83)
+ self.assertEqual(len(lst), 2)
+ self.assertEqual(lst.pop(), 79)
+ self.assertEqual(lst.pop(), 71)
+
+ def test_deletes_the_element_with_the_specified_value_from_the_list_re_assigns_head(
+ self,
+ ):
+ lst = LinkedList()
+ lst.push(71)
+ lst.push(83)
+ lst.push(79)
+ lst.delete(83)
+ self.assertEqual(len(lst), 2)
+ self.assertEqual(lst.shift(), 71)
+ self.assertEqual(lst.shift(), 79)
+
+ def test_deletes_the_first_of_two_elements(self):
+ lst = LinkedList()
+ lst.push(97)
+ lst.push(101)
+ lst.delete(97)
+ self.assertEqual(len(lst), 1)
+ self.assertEqual(lst.pop(), 101)
+
+ def test_deletes_the_second_of_two_elements(self):
+ lst = LinkedList()
+ lst.push(97)
+ lst.push(101)
+ lst.delete(101)
+ self.assertEqual(len(lst), 1)
+ self.assertEqual(lst.pop(), 97)
+
+ def test_delete_does_not_modify_the_list_if_the_element_is_not_found(self):
+ lst = LinkedList()
+ lst.push(89)
+ lst.delete(103)
+ self.assertEqual(len(lst), 1)
+
+ def test_deletes_only_the_first_occurrence(self):
+ lst = LinkedList()
+ lst.push(73)
+ lst.push(9)
+ lst.push(9)
+ lst.push(107)
+ lst.delete(9)
+ self.assertEqual(len(lst), 3)
+ self.assertEqual(lst.pop(), 107)
+ self.assertEqual(lst.pop(), 9)
+ self.assertEqual(lst.pop(), 73)
From 2aa781a4974b9ed7d769a89725dba970758fa868 Mon Sep 17 00:00:00 2001
From: Meatball
Date: Sat, 3 Dec 2022 18:51:25 +0100
Subject: [PATCH 238/826] Fix
---
exercises/practice/linked-list/.meta/config.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/exercises/practice/linked-list/.meta/config.json b/exercises/practice/linked-list/.meta/config.json
index 894e0a5d3a..3c1731c0be 100644
--- a/exercises/practice/linked-list/.meta/config.json
+++ b/exercises/practice/linked-list/.meta/config.json
@@ -10,7 +10,7 @@
"N-Parsons",
"pheanex",
"tqa236",
- "meatball"
+ "meatball133"
],
"files": {
"solution": [
From 2ff1eebd13a741dd6098c9e893b234d0d8d0d2f4 Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Sat, 3 Dec 2022 10:51:44 -0800
Subject: [PATCH 239/826] Create instructions.apped.md
Rough outline of instructions.append
---
.../linked-list/.docs/instructions.apped.md | 42 +++++++++++++++++++
1 file changed, 42 insertions(+)
create mode 100644 exercises/practice/linked-list/.docs/instructions.apped.md
diff --git a/exercises/practice/linked-list/.docs/instructions.apped.md b/exercises/practice/linked-list/.docs/instructions.apped.md
new file mode 100644
index 0000000000..7b649b857b
--- /dev/null
+++ b/exercises/practice/linked-list/.docs/instructions.apped.md
@@ -0,0 +1,42 @@
+# Instructions append
+
+## How this Exercise is Structured in Python
+
+While linked lists can be implemented in a variety of ways with a variety of underlying data structures, we ask here that you implement your linked list in an OOP fashion.
+
+In the stub file, you will see a `Node` class and a `LinkedList` class.
+Your `Node` class should keep track of its value, as well as which other nodes preceed or follow.
+Your `push`, `pop`, `shift`, `unshift`, and the special method for `len` should be implemented in the `LinkedList` class.
+
+
+In addition to the methods outlined above, we also ask that you implement `delete`.
+`delete` will take one argument, which is the vaule to be removed from the linked list.
+If the value appears more than once, the **first** occurance should be removed.
+
+
+
+## Exception messages
+
+Sometimes it is necessary to [raise an exception](https://docs.python.org/3/tutorial/errors.html#raising-exceptions). When you do this, you should always include a **meaningful error message** to indicate what the source of the error is. This makes your code more readable and helps significantly with debugging. For situations where you know that the error source will be a certain type, you can choose to raise one of the [built in error types](https://docs.python.org/3/library/exceptions.html#base-classes), but should still include a meaningful message.
+
+This particular exercise requires that you use the [raise statement](https://docs.python.org/3/reference/simple_stmts.html#the-raise-statement) to "throw" a `ValueError` when a node value being `delet()`-ed is not found in the linked list. Additionally, an `IndexError` should be thrown if there are no nodes left to `pop()`. The tests will only pass if you both `raise` these `exceptions` and include messages with them.
+
+To raise a `ValueError` with a message, write the message as an argument to the `exception` type:
+
+```python
+# When the value passed to `delete()` is not found.
+
+
+# When pop() is called and there are no nodes left in the linked list
+
+
+```
+
+## Special Methods in Python
+
+The tests for this exercise will also be calling `len()` on your `LinkedList`.
+In order for `len()` to work, you will need to create a `__len__` special method.
+For details on implementing special or "dunder" methods in Python, see [Python Docs: Basic Object Customization][basic customization] and [Python Docs: object.__len__(self)][__len__].
+
+
+
From 9c90b83909e5af366d43d4044e76b0518ed81483 Mon Sep 17 00:00:00 2001
From: Meatball
Date: Sat, 3 Dec 2022 20:20:36 +0100
Subject: [PATCH 240/826] progress
---
.../linked-list/.meta/additional_tests.json | 14 ++++++++++++++
exercises/practice/linked-list/.meta/template.j2 | 15 +++++++++++++++
.../practice/linked-list/linked_list_test.py | 6 ++++++
3 files changed, 35 insertions(+)
create mode 100644 exercises/practice/linked-list/.meta/additional_tests.json
diff --git a/exercises/practice/linked-list/.meta/additional_tests.json b/exercises/practice/linked-list/.meta/additional_tests.json
new file mode 100644
index 0000000000..a18e57356d
--- /dev/null
+++ b/exercises/practice/linked-list/.meta/additional_tests.json
@@ -0,0 +1,14 @@
+{
+ "cases": [
+ {
+ "description": "Hi",
+ "property": "list",
+ "input": {
+ "operations": [
+ {
+ "operation": "pop"
+ }
+ ]
+ }, "expected": {"error": "List is empty"}
+ }
+]}
\ No newline at end of file
diff --git a/exercises/practice/linked-list/.meta/template.j2 b/exercises/practice/linked-list/.meta/template.j2
index c765d68a32..31eed581c3 100644
--- a/exercises/practice/linked-list/.meta/template.j2
+++ b/exercises/practice/linked-list/.meta/template.j2
@@ -1,9 +1,11 @@
{%- import "generator_macros.j2" as macros with context -%}
{% macro test_case(case) -%}
{%- set input = case["input"]["operations"] -%}
+ {%- set last = "" -%}
def test_{{ case["description"] | to_snake }}(self):
lst = LinkedList()
{% for x in input -%}
+ {%- set last = x["operation"] -%}
{%- if "push" == x["operation"] -%}
lst.push({{ x["value"] }})
{%- elif "pop" == x["operation"] and x["expected"] -%}
@@ -22,6 +24,11 @@
self.assertEqual(len(lst), {{ x["expected"] }})
{%- endif %}
{% endfor %}
+
+ {% if case["expected"] and last == "pop" %}
+ hi
+ {% endif %}
+
{%- endmacro %}
{{ macros.header(["LinkedList"]) }}
@@ -29,3 +36,11 @@ class {{ exercise | camel_case }}Test(unittest.TestCase):
{% for case in cases -%}
{{ test_case(case) }}
{% endfor %}
+ {% if additional_cases | length -%}
+
+ # Additional tests for this track
+
+ {% for case in additional_cases -%}
+ {{ test_case(case) }}
+ {% endfor %}
+ {%- endif %}
diff --git a/exercises/practice/linked-list/linked_list_test.py b/exercises/practice/linked-list/linked_list_test.py
index 822948b823..e6bc7270d7 100644
--- a/exercises/practice/linked-list/linked_list_test.py
+++ b/exercises/practice/linked-list/linked_list_test.py
@@ -166,3 +166,9 @@ def test_deletes_only_the_first_occurrence(self):
self.assertEqual(lst.pop(), 107)
self.assertEqual(lst.pop(), 9)
self.assertEqual(lst.pop(), 73)
+
+ # Additional tests for this track
+
+ def test_hi(self):
+ lst = LinkedList()
+ lst.pop()
From fc4c03b45d797c19fb8b192560da1ba0199ab92b Mon Sep 17 00:00:00 2001
From: Meatball
Date: Sat, 3 Dec 2022 20:29:50 +0100
Subject: [PATCH 241/826] more stuff
---
exercises/practice/linked-list/.meta/template.j2 | 7 +++----
exercises/practice/linked-list/linked_list_test.py | 2 ++
2 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/exercises/practice/linked-list/.meta/template.j2 b/exercises/practice/linked-list/.meta/template.j2
index 31eed581c3..4a9ec4b932 100644
--- a/exercises/practice/linked-list/.meta/template.j2
+++ b/exercises/practice/linked-list/.meta/template.j2
@@ -1,11 +1,10 @@
{%- import "generator_macros.j2" as macros with context -%}
{% macro test_case(case) -%}
{%- set input = case["input"]["operations"] -%}
- {%- set last = "" -%}
def test_{{ case["description"] | to_snake }}(self):
lst = LinkedList()
{% for x in input -%}
- {%- set last = x["operation"] -%}
+ {% set last = x["operation"] %}
{%- if "push" == x["operation"] -%}
lst.push({{ x["value"] }})
{%- elif "pop" == x["operation"] and x["expected"] -%}
@@ -25,8 +24,8 @@
{%- endif %}
{% endfor %}
- {% if case["expected"] and last == "pop" %}
- hi
+ {% if case["expected"] and input[-1]["operation"] == "pop" %}
+
{% endif %}
{%- endmacro %}
diff --git a/exercises/practice/linked-list/linked_list_test.py b/exercises/practice/linked-list/linked_list_test.py
index e6bc7270d7..1c58d4995c 100644
--- a/exercises/practice/linked-list/linked_list_test.py
+++ b/exercises/practice/linked-list/linked_list_test.py
@@ -172,3 +172,5 @@ def test_deletes_only_the_first_occurrence(self):
def test_hi(self):
lst = LinkedList()
lst.pop()
+
+ hi
From b85fc64b53c4f988efac1110e5925c7bf60838f9 Mon Sep 17 00:00:00 2001
From: Meatball
Date: Sat, 3 Dec 2022 21:16:23 +0100
Subject: [PATCH 242/826] progress
---
.../linked-list/.meta/additional_tests.json | 11 ++--
.../practice/linked-list/.meta/template.j2 | 50 +++++++++++--------
.../practice/linked-list/linked_list_test.py | 8 +--
3 files changed, 42 insertions(+), 27 deletions(-)
diff --git a/exercises/practice/linked-list/.meta/additional_tests.json b/exercises/practice/linked-list/.meta/additional_tests.json
index a18e57356d..5a31665473 100644
--- a/exercises/practice/linked-list/.meta/additional_tests.json
+++ b/exercises/practice/linked-list/.meta/additional_tests.json
@@ -5,9 +5,14 @@
"property": "list",
"input": {
"operations": [
- {
- "operation": "pop"
- }
+ {
+ "operation": "push",
+ "value": 1
+ },
+ {
+ "operation": "pop"
+ }
+
]
}, "expected": {"error": "List is empty"}
}
diff --git a/exercises/practice/linked-list/.meta/template.j2 b/exercises/practice/linked-list/.meta/template.j2
index 4a9ec4b932..9fe03b776d 100644
--- a/exercises/practice/linked-list/.meta/template.j2
+++ b/exercises/practice/linked-list/.meta/template.j2
@@ -1,31 +1,39 @@
{%- import "generator_macros.j2" as macros with context -%}
{% macro test_case(case) -%}
- {%- set input = case["input"]["operations"] -%}
def test_{{ case["description"] | to_snake }}(self):
lst = LinkedList()
- {% for x in input -%}
- {% set last = x["operation"] %}
- {%- if "push" == x["operation"] -%}
- lst.push({{ x["value"] }})
- {%- elif "pop" == x["operation"] and x["expected"] -%}
- self.assertEqual(lst.pop(), {{ x["expected"] }})
- {%- elif "pop" == x["operation"] -%}
+ {%- set inputs = case["input"]["operations"] -%}
+ {%- if case["expected"] and inputs|length > 1 -%}
+ {%- set last = inputs[-1]["operation"] -%}
+ {% set inputs = inputs[:-1] %}
+ {%- elif case["expected"] -%}
+ {% set last = inputs[-1]["operation"] %}
+ {% set inputs = [{"operation": "None"}] %}
+ {%- endif %}
+ {% for input in inputs -%}
+ {%- if "push" == input["operation"] -%}
+ lst.push({{ input["value"] }})
+ {%- elif "pop" == input["operation"] and input["expected"] -%}
+ self.assertEqual(lst.pop(), {{ input["expected"] }})
+ {%- elif "pop" == input["operation"] -%}
lst.pop()
- {%- elif "shift" == x["operation"] and x["expected"] -%}
- self.assertEqual(lst.shift(), {{ x["expected"] }})
- {%- elif "shift" == x["operation"] -%}
+ {%- elif "shift" == input["operation"] and input["expected"] -%}
+ self.assertEqual(lst.shift(), {{ input["expected"] }})
+ {%- elif "shift" == input["operation"] -%}
lst.shift()
- {%- elif "unshift" == x["operation"] -%}
- lst.unshift({{ x["value"] }})
- {%- elif "delete" == x["operation"] -%}
- lst.delete({{ x["value"] }})
- {%- elif "count" == x["operation"] -%}
- self.assertEqual(len(lst), {{ x["expected"] }})
+ {%- elif "unshift" == input["operation"] -%}
+ lst.unshift({{ input["value"] }})
+ {%- elif "delete" == input["operation"] -%}
+ lst.delete({{ input["value"] }})
+ {%- elif "count" == input["operation"] -%}
+ self.assertEqual(len(lst), {{ input["expected"] }})
{%- endif %}
- {% endfor %}
-
- {% if case["expected"] and input[-1]["operation"] == "pop" %}
-
+ {% endfor -%}
+ {%- if case["expected"] and last -%}
+ with self.assertRaises(ValueError) as err:
+ lst.pop()
+ self.assertEqual(type(err.exception), IndexError)
+ self.assertEqual(err.exception.args[0], "{{case["expected"]["error"]}}")
{% endif %}
{%- endmacro %}
diff --git a/exercises/practice/linked-list/linked_list_test.py b/exercises/practice/linked-list/linked_list_test.py
index 1c58d4995c..c1eeaca35a 100644
--- a/exercises/practice/linked-list/linked_list_test.py
+++ b/exercises/practice/linked-list/linked_list_test.py
@@ -171,6 +171,8 @@ def test_deletes_only_the_first_occurrence(self):
def test_hi(self):
lst = LinkedList()
- lst.pop()
-
- hi
+ lst.push(1)
+ with self.assertRaises(ValueError) as err:
+ lst.pop()
+ self.assertEqual(type(err.exception), IndexError)
+ self.assertEqual(err.exception.args[0], "List is empty")
From 2d413b1c6d99151acfeae420ed9f7bd7254c3e09 Mon Sep 17 00:00:00 2001
From: Meatball
Date: Tue, 6 Dec 2022 08:57:15 +0100
Subject: [PATCH 243/826] Updated the test file
---
.../linked-list/.meta/additional_tests.json | 106 +++++++++++++++++-
1 file changed, 105 insertions(+), 1 deletion(-)
diff --git a/exercises/practice/linked-list/.meta/additional_tests.json b/exercises/practice/linked-list/.meta/additional_tests.json
index 5a31665473..ce01e33972 100644
--- a/exercises/practice/linked-list/.meta/additional_tests.json
+++ b/exercises/practice/linked-list/.meta/additional_tests.json
@@ -1,7 +1,19 @@
{
"cases": [
{
- "description": "Hi",
+ "description": "Using pop raises an error if the list is empty",
+ "property": "list",
+ "input": {
+ "operations": [
+ {
+ "operation": "pop"
+ }
+
+ ]
+ }, "expected": {"error": "List is empty"}
+ },
+ {
+ "description": "Can return with pop and then raise an error if empty",
"property": "list",
"input": {
"operations": [
@@ -9,11 +21,103 @@
"operation": "push",
"value": 1
},
+ {
+ "operation": "unshift",
+ "value": 5
+ },
+ {
+ "operation": "pop",
+ "expected": 5
+ },
+ {
+ "operation": "pop",
+ "expected": 1
+ },
{
"operation": "pop"
}
]
}, "expected": {"error": "List is empty"}
+ },
+ {
+ "description": "Using shift raises an error if the list is empty",
+ "property": "list",
+ "input": {
+ "operations": [
+ {
+ "operation": "shift"
+ }
+
+ ]
+ }, "expected": {"error": "List is empty"}
+ },
+ {
+ "description": "Can return with shift and then raise an error if empty",
+ "property": "list",
+ "input": {
+ "operations": [
+ {
+ "operation": "push",
+ "value": 1
+ },
+ {
+ "operation": "unshift",
+ "value": 5
+ },
+ {
+ "operation": "pop",
+ "expected": 5
+ },
+ {
+ "operation": "shift",
+ "expected": 1
+ },
+ {
+ "operation": "shift"
+ }
+
+ ]
+ }, "expected": {"error": "List is empty"}
+ },
+ {
+ "description": "Using delete raises an error if the list is empty",
+ "property": "list",
+ "input": {
+ "operations": [
+ {
+ "operation": "delete",
+ "value": 0
+ }
+
+ ]
+ }, "expected": {"error": "Value not found"}
+ },
+ {
+ "description": "Using delete raises an error if the it is not found",
+ "property": "list",
+ "input": {
+ "operations": [
+ {
+ "operation": "push",
+ "value": 5
+ },
+ {
+ "operation": "push",
+ "value": 7
+ },
+ {
+ "operation": "pop",
+ "expected": 7
+ },
+ {
+ "operation": "delete",
+ "value": 7
+ }
+
+ ]
+ }, "expected": {"error": "Value not found"}
}
+
+
]}
\ No newline at end of file
From 7b46c1c302648784c9417fe09b274d88e107326f Mon Sep 17 00:00:00 2001
From: Meatball
Date: Tue, 6 Dec 2022 08:58:35 +0100
Subject: [PATCH 244/826] fix
---
exercises/practice/linked-list/.meta/additional_tests.json | 2 --
1 file changed, 2 deletions(-)
diff --git a/exercises/practice/linked-list/.meta/additional_tests.json b/exercises/practice/linked-list/.meta/additional_tests.json
index ce01e33972..25d8ef0776 100644
--- a/exercises/practice/linked-list/.meta/additional_tests.json
+++ b/exercises/practice/linked-list/.meta/additional_tests.json
@@ -118,6 +118,4 @@
]
}, "expected": {"error": "Value not found"}
}
-
-
]}
\ No newline at end of file
From 95b83515d79ff11f1f62432b4eca81beb8e77659 Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Tue, 6 Dec 2022 00:01:05 -0800
Subject: [PATCH 245/826] Update template.j2
Reworked template.
---
.../practice/linked-list/.meta/template.j2 | 31 +++++++++----------
1 file changed, 15 insertions(+), 16 deletions(-)
diff --git a/exercises/practice/linked-list/.meta/template.j2 b/exercises/practice/linked-list/.meta/template.j2
index 9fe03b776d..bbbada690d 100644
--- a/exercises/practice/linked-list/.meta/template.j2
+++ b/exercises/practice/linked-list/.meta/template.j2
@@ -11,22 +11,21 @@
{% set inputs = [{"operation": "None"}] %}
{%- endif %}
{% for input in inputs -%}
- {%- if "push" == input["operation"] -%}
- lst.push({{ input["value"] }})
- {%- elif "pop" == input["operation"] and input["expected"] -%}
- self.assertEqual(lst.pop(), {{ input["expected"] }})
- {%- elif "pop" == input["operation"] -%}
- lst.pop()
- {%- elif "shift" == input["operation"] and input["expected"] -%}
- self.assertEqual(lst.shift(), {{ input["expected"] }})
- {%- elif "shift" == input["operation"] -%}
- lst.shift()
- {%- elif "unshift" == input["operation"] -%}
- lst.unshift({{ input["value"] }})
- {%- elif "delete" == input["operation"] -%}
- lst.delete({{ input["value"] }})
- {%- elif "count" == input["operation"] -%}
- self.assertEqual(len(lst), {{ input["expected"] }})
+ {%- set operation = input["operation"] -%}
+ {%- set value = input["value"] -%}
+ {%- set expected = input["expected"] -%}
+ {%- if operation and value -%}
+ lst.{{ operation }}({{ value }})
+ {%- elif operation and expected -%}
+ {%- if operation == "count" -%}
+ self.assertEqual(len(lst), {{ expected }})
+ {%- else -%}
+ self.assertEqual(lst.{{ operation }}(), {{ expected }})
+ {%- endif -%}
+ {%- elif operation == "count" -%}
+ self.assertEqual(len(lst), {{ expected }})
+ {%- else -%}
+ lst.{{ operation }}()
{%- endif %}
{% endfor -%}
{%- if case["expected"] and last -%}
From d9255250a986b3b59dcf2e6755ad9f66048ef37b Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Tue, 6 Dec 2022 01:29:45 -0800
Subject: [PATCH 246/826] JinJa Redo Number Two
---
.../practice/linked-list/.meta/template.j2 | 42 +++++++++----------
.../practice/linked-list/linked_list_test.py | 10 -----
2 files changed, 21 insertions(+), 31 deletions(-)
diff --git a/exercises/practice/linked-list/.meta/template.j2 b/exercises/practice/linked-list/.meta/template.j2
index bbbada690d..08294ae78f 100644
--- a/exercises/practice/linked-list/.meta/template.j2
+++ b/exercises/practice/linked-list/.meta/template.j2
@@ -5,34 +5,34 @@
{%- set inputs = case["input"]["operations"] -%}
{%- if case["expected"] and inputs|length > 1 -%}
{%- set last = inputs[-1]["operation"] -%}
- {% set inputs = inputs[:-1] %}
+ {%- set inputs = inputs[:-1] -%}
{%- elif case["expected"] -%}
- {% set last = inputs[-1]["operation"] %}
- {% set inputs = [{"operation": "None"}] %}
- {%- endif %}
+ {%- set last = inputs[-1]["operation"] -%}
+ {%- set inputs = [{"operation": "None"}] -%}
+ {%- endif -%}
{% for input in inputs -%}
{%- set operation = input["operation"] -%}
{%- set value = input["value"] -%}
- {%- set expected = input["expected"] -%}
- {%- if operation and value -%}
- lst.{{ operation }}({{ value }})
- {%- elif operation and expected -%}
- {%- if operation == "count" -%}
- self.assertEqual(len(lst), {{ expected }})
- {%- else -%}
- self.assertEqual(lst.{{ operation }}(), {{ expected }})
- {%- endif -%}
- {%- elif operation == "count" -%}
- self.assertEqual(len(lst), {{ expected }})
- {%- else -%}
- lst.{{ operation }}()
+ {%- set expected = input["expected"] %}
+ {%- if operation and value %}
+ lst.{{ operation }}({{ value }})
+ {%- elif operation and expected %}
+ {%- if operation == "count" %}
+ self.assertEqual(len(lst), {{ expected }})
+ {%- else %}
+ self.assertEqual(lst.{{ operation }}(), {{ expected }})
+ {%- endif %}
+ {%- elif operation == "count" %}
+ self.assertEqual(len(lst), {{ expected }})
+ {%- else %}
+ lst.{{ operation }}()
{%- endif %}
- {% endfor -%}
- {%- if case["expected"] and last -%}
+ {%- endfor %}
+ {% if case["expected"] and last %}
with self.assertRaises(ValueError) as err:
lst.pop()
- self.assertEqual(type(err.exception), IndexError)
- self.assertEqual(err.exception.args[0], "{{case["expected"]["error"]}}")
+ self.assertEqual(type(err.exception), IndexError)
+ self.assertEqual(err.exception.args[0], "{{case["expected"]["error"]}}")
{% endif %}
{%- endmacro %}
diff --git a/exercises/practice/linked-list/linked_list_test.py b/exercises/practice/linked-list/linked_list_test.py
index c1eeaca35a..822948b823 100644
--- a/exercises/practice/linked-list/linked_list_test.py
+++ b/exercises/practice/linked-list/linked_list_test.py
@@ -166,13 +166,3 @@ def test_deletes_only_the_first_occurrence(self):
self.assertEqual(lst.pop(), 107)
self.assertEqual(lst.pop(), 9)
self.assertEqual(lst.pop(), 73)
-
- # Additional tests for this track
-
- def test_hi(self):
- lst = LinkedList()
- lst.push(1)
- with self.assertRaises(ValueError) as err:
- lst.pop()
- self.assertEqual(type(err.exception), IndexError)
- self.assertEqual(err.exception.args[0], "List is empty")
From cedb63b9d224d552b0d10ffb6e1bada489e26f7f Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Wed, 7 Dec 2022 00:53:00 -0800
Subject: [PATCH 247/826] Updated JinJa Template
Updated template
Re-generated test cases
Re-formatted json file
---
.../linked-list/.meta/additional_tests.json | 63 +++++++++++--------
.../practice/linked-list/.meta/template.j2 | 38 +++++++----
.../practice/linked-list/linked_list_test.py | 60 ++++++++++++++++--
3 files changed, 116 insertions(+), 45 deletions(-)
diff --git a/exercises/practice/linked-list/.meta/additional_tests.json b/exercises/practice/linked-list/.meta/additional_tests.json
index 25d8ef0776..2b28b6aa0e 100644
--- a/exercises/practice/linked-list/.meta/additional_tests.json
+++ b/exercises/practice/linked-list/.meta/additional_tests.json
@@ -1,6 +1,7 @@
{
"cases": [
{
+ "uuid": "c472bec8-45b5-477f-96c1-3bed58ca0b1a",
"description": "Using pop raises an error if the list is empty",
"property": "list",
"input": {
@@ -13,34 +14,36 @@
}, "expected": {"error": "List is empty"}
},
{
- "description": "Can return with pop and then raise an error if empty",
+ "uuid": "394a61d3-7a69-4153-9eb7-f8d8e23cf6f0",
+ "description" : "Can return with pop and then raise an error if empty",
"property": "list",
"input": {
- "operations": [
- {
- "operation": "push",
- "value": 1
- },
- {
- "operation": "unshift",
- "value": 5
- },
- {
- "operation": "pop",
- "expected": 5
- },
- {
- "operation": "pop",
- "expected": 1
- },
- {
- "operation": "pop"
- }
-
- ]
- }, "expected": {"error": "List is empty"}
+ "operations": [
+ {
+ "operation": "push",
+ "value": 1
+ },
+ {
+ "operation": "unshift",
+ "value": 5
+ },
+ {
+ "operation": "pop",
+ "expected": 5
+ },
+ {
+ "operation": "pop",
+ "expected": 1
+ },
+ {
+ "operation": "pop"
+ }
+ ]
+ },
+ "expected": {"error": "List is empty"}
},
{
+ "uuid": "6d1a1817-0d4c-4953-abfd-c5cd5639871f",
"description": "Using shift raises an error if the list is empty",
"property": "list",
"input": {
@@ -50,9 +53,11 @@
}
]
- }, "expected": {"error": "List is empty"}
+ },
+ "expected": {"error": "List is empty"}
},
{
+ "uuid": "cae5d135-f948-44ce-8940-c9b898b95756",
"description": "Can return with shift and then raise an error if empty",
"property": "list",
"input": {
@@ -78,9 +83,11 @@
}
]
- }, "expected": {"error": "List is empty"}
+ },
+ "expected": {"error": "List is empty"}
},
{
+ "uuid": "1fa81a88-9af7-49a1-a4b1-b1aa641bfcbb",
"description": "Using delete raises an error if the list is empty",
"property": "list",
"input": {
@@ -91,9 +98,11 @@
}
]
- }, "expected": {"error": "Value not found"}
+ },
+ "expected": {"error": "Value not found"}
},
{
+ "uuid": "f7f9d2dc-be1b-4f42-be2e-c29980ce0823",
"description": "Using delete raises an error if the it is not found",
"property": "list",
"input": {
diff --git a/exercises/practice/linked-list/.meta/template.j2 b/exercises/practice/linked-list/.meta/template.j2
index 08294ae78f..0659aff2d8 100644
--- a/exercises/practice/linked-list/.meta/template.j2
+++ b/exercises/practice/linked-list/.meta/template.j2
@@ -4,10 +4,15 @@
lst = LinkedList()
{%- set inputs = case["input"]["operations"] -%}
{%- if case["expected"] and inputs|length > 1 -%}
- {%- set last = inputs[-1]["operation"] -%}
+ {%- set final = inputs[-1]["operation"] -%}
{%- set inputs = inputs[:-1] -%}
- {%- elif case["expected"] -%}
- {%- set last = inputs[-1]["operation"] -%}
+ {% if case["expected"]["error"] %}
+ {%- set error_case = true %}
+ {%- set error_msg = case["expected"]["error"] %}
+ {%- set error_operation = inputs[-1]["operation"] %}
+ {%- endif %}
+ {%- elif case["expected"] and not error_case -%}
+ {%- set final = inputs[-1]["operation"] -%}
{%- set inputs = [{"operation": "None"}] -%}
{%- endif -%}
{% for input in inputs -%}
@@ -24,18 +29,28 @@
{%- endif %}
{%- elif operation == "count" %}
self.assertEqual(len(lst), {{ expected }})
- {%- else %}
- lst.{{ operation }}()
+ {%- elif case["expected"]["error"] %}
+ with self.assertRaises(IndexError) as err:
+ lst.{{ case["input"]["operations"][-1]["operation"] }}()
+ self.assertEqual(type(err.exception), IndexError)
+ self.assertEqual(err.exception.args[0], "{{ case["expected"]["error"] }}")
{%- endif %}
{%- endfor %}
- {% if case["expected"] and last %}
- with self.assertRaises(ValueError) as err:
- lst.pop()
+ {%- if error_case %}
+ {%- if final == "pop" or final == "shift" %}
+ with self.assertRaises(IndexError) as err:
+ lst.{{ error_operation }}()
self.assertEqual(type(err.exception), IndexError)
- self.assertEqual(err.exception.args[0], "{{case["expected"]["error"]}}")
- {% endif %}
-
+ self.assertEqual(err.exception.args[0], "{{ error_msg }}")
+ {% elif final == "delete" %}
+ with self.assertRaises(ValueError) as err:
+ lst.{{ error_operation }}()
+ self.assertEqual(type(err.exception), ValueError)
+ self.assertEqual(err.exception.args[0], "{{ error_msg }}")
+ {%- endif %}
+ {%- endif %}
{%- endmacro %}
+
{{ macros.header(["LinkedList"]) }}
class {{ exercise | camel_case }}Test(unittest.TestCase):
@@ -45,7 +60,6 @@ class {{ exercise | camel_case }}Test(unittest.TestCase):
{% if additional_cases | length -%}
# Additional tests for this track
-
{% for case in additional_cases -%}
{{ test_case(case) }}
{% endfor %}
diff --git a/exercises/practice/linked-list/linked_list_test.py b/exercises/practice/linked-list/linked_list_test.py
index 822948b823..97cab20bab 100644
--- a/exercises/practice/linked-list/linked_list_test.py
+++ b/exercises/practice/linked-list/linked_list_test.py
@@ -68,17 +68,13 @@ def test_count_is_correct_after_mutation(self):
self.assertEqual(len(lst), 1)
lst.unshift(43)
self.assertEqual(len(lst), 2)
- lst.shift()
self.assertEqual(len(lst), 1)
- lst.pop()
self.assertEqual(len(lst), 0)
def test_popping_to_empty_doesn_t_break_the_list(self):
lst = LinkedList()
lst.push(41)
lst.push(59)
- lst.pop()
- lst.pop()
lst.push(47)
self.assertEqual(len(lst), 1)
self.assertEqual(lst.pop(), 47)
@@ -87,8 +83,6 @@ def test_shifting_to_empty_doesn_t_break_the_list(self):
lst = LinkedList()
lst.push(41)
lst.push(59)
- lst.shift()
- lst.shift()
lst.push(47)
self.assertEqual(len(lst), 1)
self.assertEqual(lst.shift(), 47)
@@ -166,3 +160,57 @@ def test_deletes_only_the_first_occurrence(self):
self.assertEqual(lst.pop(), 107)
self.assertEqual(lst.pop(), 9)
self.assertEqual(lst.pop(), 73)
+
+ # Additional tests for this track
+ def test_using_pop_raises_an_error_if_the_list_is_empty(self):
+ lst = LinkedList()
+ with self.assertRaises(IndexError) as err:
+ lst.pop()
+ self.assertEqual(type(err.exception), IndexError)
+ self.assertEqual(err.exception.args[0], "List is empty")
+
+ def test_can_return_with_pop_and_then_raise_an_error_if_empty(self):
+ lst = LinkedList()
+ lst.push(1)
+ lst.unshift(5)
+ self.assertEqual(lst.pop(), 5)
+ self.assertEqual(lst.pop(), 1)
+ with self.assertRaises(IndexError) as err:
+ lst.pop()
+ self.assertEqual(type(err.exception), IndexError)
+ self.assertEqual(err.exception.args[0], "List is empty")
+
+ def test_using_shift_raises_an_error_if_the_list_is_empty(self):
+ lst = LinkedList()
+ with self.assertRaises(IndexError) as err:
+ lst.shift()
+ self.assertEqual(type(err.exception), IndexError)
+ self.assertEqual(err.exception.args[0], "List is empty")
+
+ def test_can_return_with_shift_and_then_raise_an_error_if_empty(self):
+ lst = LinkedList()
+ lst.push(1)
+ lst.unshift(5)
+ self.assertEqual(lst.pop(), 5)
+ self.assertEqual(lst.shift(), 1)
+ with self.assertRaises(IndexError) as err:
+ lst.shift()
+ self.assertEqual(type(err.exception), IndexError)
+ self.assertEqual(err.exception.args[0], "List is empty")
+
+ def test_using_delete_raises_an_error_if_the_list_is_empty(self):
+ lst = LinkedList()
+ with self.assertRaises(IndexError) as err:
+ lst.delete()
+ self.assertEqual(type(err.exception), IndexError)
+ self.assertEqual(err.exception.args[0], "Value not found")
+
+ def test_using_delete_raises_an_error_if_the_it_is_not_found(self):
+ lst = LinkedList()
+ lst.push(5)
+ lst.push(7)
+ self.assertEqual(lst.pop(), 7)
+ with self.assertRaises(ValueError) as err:
+ lst.pop()
+ self.assertEqual(type(err.exception), ValueError)
+ self.assertEqual(err.exception.args[0], "Value not found")
From ce16d819b9a92c4db9d9401255fda4b2ea90e361 Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Wed, 7 Dec 2022 01:48:44 -0800
Subject: [PATCH 248/826] Template Correction
---
exercises/practice/linked-list/.meta/template.j2 | 7 +++++++
exercises/practice/linked-list/linked_list_test.py | 4 ++--
2 files changed, 9 insertions(+), 2 deletions(-)
diff --git a/exercises/practice/linked-list/.meta/template.j2 b/exercises/practice/linked-list/.meta/template.j2
index 0659aff2d8..4760945d65 100644
--- a/exercises/practice/linked-list/.meta/template.j2
+++ b/exercises/practice/linked-list/.meta/template.j2
@@ -30,11 +30,18 @@
{%- elif operation == "count" %}
self.assertEqual(len(lst), {{ expected }})
{%- elif case["expected"]["error"] %}
+ {%- if final == "pop" or final == "shift" %}
with self.assertRaises(IndexError) as err:
lst.{{ case["input"]["operations"][-1]["operation"] }}()
self.assertEqual(type(err.exception), IndexError)
+ self.assertEqual(err.exception.args[0], "{{ case["expected"]["error"] }}")
+ {% elif final == "delete" %}
+ with self.assertRaises(ValueError) as err:
+ lst.{{ case["input"]["operations"][-1]["operation"] }}()
+ self.assertEqual(type(err.exception), ValueError)
self.assertEqual(err.exception.args[0], "{{ case["expected"]["error"] }}")
{%- endif %}
+ {%- endif %}
{%- endfor %}
{%- if error_case %}
{%- if final == "pop" or final == "shift" %}
diff --git a/exercises/practice/linked-list/linked_list_test.py b/exercises/practice/linked-list/linked_list_test.py
index 97cab20bab..0080921a2f 100644
--- a/exercises/practice/linked-list/linked_list_test.py
+++ b/exercises/practice/linked-list/linked_list_test.py
@@ -200,9 +200,9 @@ def test_can_return_with_shift_and_then_raise_an_error_if_empty(self):
def test_using_delete_raises_an_error_if_the_list_is_empty(self):
lst = LinkedList()
- with self.assertRaises(IndexError) as err:
+ with self.assertRaises(ValueError) as err:
lst.delete()
- self.assertEqual(type(err.exception), IndexError)
+ self.assertEqual(type(err.exception), ValueError)
self.assertEqual(err.exception.args[0], "Value not found")
def test_using_delete_raises_an_error_if_the_it_is_not_found(self):
From cfdf420f94393bb0685cd5ac75fcc64244161039 Mon Sep 17 00:00:00 2001
From: Meatball
Date: Wed, 7 Dec 2022 16:35:09 +0100
Subject: [PATCH 249/826] Updated example and tests
---
.../practice/linked-list/.meta/additional_tests.json | 8 ++++----
exercises/practice/linked-list/.meta/example.py | 10 +++++++++-
exercises/practice/linked-list/.meta/tests.toml | 1 +
3 files changed, 14 insertions(+), 5 deletions(-)
diff --git a/exercises/practice/linked-list/.meta/additional_tests.json b/exercises/practice/linked-list/.meta/additional_tests.json
index 2b28b6aa0e..5be9653f3f 100644
--- a/exercises/practice/linked-list/.meta/additional_tests.json
+++ b/exercises/practice/linked-list/.meta/additional_tests.json
@@ -29,11 +29,11 @@
},
{
"operation": "pop",
- "expected": 5
+ "expected": 1
},
{
"operation": "pop",
- "expected": 1
+ "expected": 5
},
{
"operation": "pop"
@@ -72,11 +72,11 @@
},
{
"operation": "pop",
- "expected": 5
+ "expected": 1
},
{
"operation": "shift",
- "expected": 1
+ "expected": 5
},
{
"operation": "shift"
diff --git a/exercises/practice/linked-list/.meta/example.py b/exercises/practice/linked-list/.meta/example.py
index 997831bd57..536bd2491c 100644
--- a/exercises/practice/linked-list/.meta/example.py
+++ b/exercises/practice/linked-list/.meta/example.py
@@ -23,6 +23,8 @@ def push(self, value):
def pop(self):
node = self.tail
+ if self.length == 0:
+ raise IndexError("List is empty")
if node is None or node.prev is None:
self.head = self.tail = None
else:
@@ -32,6 +34,8 @@ def pop(self):
return node.value
def shift(self):
+ if self.length == 0:
+ raise IndexError("List is empty")
node = self.head
if node is None or node.succeeding is None:
self.head = self.tail = None
@@ -55,6 +59,7 @@ def __len__(self):
return self.length
def delete(self, delete):
+ found = False
node = self.head
while node:
if node.value == delete:
@@ -66,6 +71,9 @@ def delete(self, delete):
node.succeeding.prev = node.prev
else:
self.tail = node.prev
+ found = True
self.length -= 1
break
- node = node.succeeding
\ No newline at end of file
+ node = node.succeeding
+ if not found:
+ raise ValueError("Value not found")
\ No newline at end of file
diff --git a/exercises/practice/linked-list/.meta/tests.toml b/exercises/practice/linked-list/.meta/tests.toml
index 96906d2cc7..c5862ff4c4 100644
--- a/exercises/practice/linked-list/.meta/tests.toml
+++ b/exercises/practice/linked-list/.meta/tests.toml
@@ -62,6 +62,7 @@ description = "deletes the second of two elements"
[7b420958-f285-4922-b8f9-10d9dcab5179]
description = "delete does not modify the list if the element is not found"
+include = false
[7e04828f-6082-44e3-a059-201c63252a76]
description = "deletes only the first occurrence"
From 75ba3565c0009bbdecba8a51b47149ef2da5f0c1 Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Thu, 8 Dec 2022 01:32:12 -0800
Subject: [PATCH 250/826] More LL Edits
Final (I hope) touchups on template
Adjustments to example solution
Re-Generated test files.
---
.../practice/linked-list/.meta/example.py | 65 ++++++++++++-------
.../practice/linked-list/.meta/template.j2 | 30 +++++----
.../practice/linked-list/linked_list_test.py | 22 +++----
3 files changed, 70 insertions(+), 47 deletions(-)
diff --git a/exercises/practice/linked-list/.meta/example.py b/exercises/practice/linked-list/.meta/example.py
index 536bd2491c..1496ef2596 100644
--- a/exercises/practice/linked-list/.meta/example.py
+++ b/exercises/practice/linked-list/.meta/example.py
@@ -11,6 +11,41 @@ def __init__(self):
self.tail = None
self.length = 0
+ def __len__(self):
+ return self.length
+
+ def __iter__(self):
+ current_node = self.head
+ while current_node:
+ yield (current_node.value, current_node.succeeding, current_node.prev)
+ current_node = current_node.succeeding
+
+ def delete(self, to_delete):
+ if self.length == 0:
+ raise ValueError("Value not found")
+ found = False
+ node = self.head
+
+ for value, succeeding, prev in self:
+ if value == to_delete:
+ if prev:
+ node.prev.succeeding = succeeding
+ else:
+ self.head = succeeding
+ if succeeding:
+ node.succeeding.prev = prev
+ else:
+ self.tail = prev
+
+ found = True
+ self.length -= 1
+ break
+
+ node = node.succeeding
+
+ if not found:
+ raise ValueError("Value not found")
+
def push(self, value):
new_node = Node(value)
if not self.head:
@@ -19,10 +54,12 @@ def push(self, value):
new_node.prev = self.tail
self.tail.succeeding = new_node
self.tail = new_node
+
self.length += 1
def pop(self):
node = self.tail
+
if self.length == 0:
raise IndexError("List is empty")
if node is None or node.prev is None:
@@ -30,7 +67,10 @@ def pop(self):
else:
self.tail = self.tail.prev
self.tail.succeeding = None
+
self.length -= 1
+ print(self.length)
+
return node.value
def shift(self):
@@ -43,6 +83,7 @@ def shift(self):
self.head = self.head.succeeding
self.head.prev = None
self.length -= 1
+
return node.value
def unshift(self, value):
@@ -53,27 +94,5 @@ def unshift(self, value):
new_node.succeeding = self.head
self.head.prev = new_node
self.head = new_node
- self.length += 1
- def __len__(self):
- return self.length
-
- def delete(self, delete):
- found = False
- node = self.head
- while node:
- if node.value == delete:
- if node.prev:
- node.prev.succeeding = node.succeeding
- else:
- self.head = node.succeeding
- if node.succeeding:
- node.succeeding.prev = node.prev
- else:
- self.tail = node.prev
- found = True
- self.length -= 1
- break
- node = node.succeeding
- if not found:
- raise ValueError("Value not found")
\ No newline at end of file
+ self.length += 1
diff --git a/exercises/practice/linked-list/.meta/template.j2 b/exercises/practice/linked-list/.meta/template.j2
index 4760945d65..e9c6a30954 100644
--- a/exercises/practice/linked-list/.meta/template.j2
+++ b/exercises/practice/linked-list/.meta/template.j2
@@ -6,19 +6,21 @@
{%- if case["expected"] and inputs|length > 1 -%}
{%- set final = inputs[-1]["operation"] -%}
{%- set inputs = inputs[:-1] -%}
- {% if case["expected"]["error"] %}
- {%- set error_case = true %}
- {%- set error_msg = case["expected"]["error"] %}
- {%- set error_operation = inputs[-1]["operation"] %}
- {%- endif %}
+ {%- if case["expected"]["error"] -%}
+ {%- set error_case = true -%}
+ {%- set error_msg = case["expected"]["error"] -%}
+ {%- set error_operation = inputs[-1]["operation"] -%}
+ {% endif %}
{%- elif case["expected"] and not error_case -%}
{%- set final = inputs[-1]["operation"] -%}
{%- set inputs = [{"operation": "None"}] -%}
{%- endif -%}
- {% for input in inputs -%}
+ {%- for input in inputs -%}
{%- set operation = input["operation"] -%}
{%- set value = input["value"] -%}
{%- set expected = input["expected"] %}
+ {%- set error_msg = case["expected"]["error"] -%}
+ {%- set error_operation = inputs[-1]["operation"] -%}
{%- if operation and value %}
lst.{{ operation }}({{ value }})
{%- elif operation and expected %}
@@ -27,6 +29,8 @@
{%- else %}
self.assertEqual(lst.{{ operation }}(), {{ expected }})
{%- endif %}
+ {%- elif operation == "pop" or operation == "shift" %}
+ lst.{{ operation }}({{ value }})
{%- elif operation == "count" %}
self.assertEqual(len(lst), {{ expected }})
{%- elif case["expected"]["error"] %}
@@ -34,15 +38,15 @@
with self.assertRaises(IndexError) as err:
lst.{{ case["input"]["operations"][-1]["operation"] }}()
self.assertEqual(type(err.exception), IndexError)
- self.assertEqual(err.exception.args[0], "{{ case["expected"]["error"] }}")
- {% elif final == "delete" %}
+ self.assertEqual(err.exception.args[0], "{{ error_msg }}")
+ {%- elif final == "delete" %}
with self.assertRaises(ValueError) as err:
- lst.{{ case["input"]["operations"][-1]["operation"] }}()
+ lst.{{ case["input"]["operations"][-1]["operation"] }}({{ value if value else 0 }})
self.assertEqual(type(err.exception), ValueError)
- self.assertEqual(err.exception.args[0], "{{ case["expected"]["error"] }}")
+ self.assertEqual(err.exception.args[0], "{{ error_msg }}")
{%- endif %}
- {%- endif %}
- {%- endfor %}
+ {%- endif %}
+ {%- endfor %}
{%- if error_case %}
{%- if final == "pop" or final == "shift" %}
with self.assertRaises(IndexError) as err:
@@ -51,7 +55,7 @@
self.assertEqual(err.exception.args[0], "{{ error_msg }}")
{% elif final == "delete" %}
with self.assertRaises(ValueError) as err:
- lst.{{ error_operation }}()
+ lst.{{ final }}({{ value if value else 0 }})
self.assertEqual(type(err.exception), ValueError)
self.assertEqual(err.exception.args[0], "{{ error_msg }}")
{%- endif %}
diff --git a/exercises/practice/linked-list/linked_list_test.py b/exercises/practice/linked-list/linked_list_test.py
index 0080921a2f..37d48b3a8d 100644
--- a/exercises/practice/linked-list/linked_list_test.py
+++ b/exercises/practice/linked-list/linked_list_test.py
@@ -68,13 +68,17 @@ def test_count_is_correct_after_mutation(self):
self.assertEqual(len(lst), 1)
lst.unshift(43)
self.assertEqual(len(lst), 2)
+ lst.shift()
self.assertEqual(len(lst), 1)
+ lst.pop()
self.assertEqual(len(lst), 0)
def test_popping_to_empty_doesn_t_break_the_list(self):
lst = LinkedList()
lst.push(41)
lst.push(59)
+ lst.pop()
+ lst.pop()
lst.push(47)
self.assertEqual(len(lst), 1)
self.assertEqual(lst.pop(), 47)
@@ -83,6 +87,8 @@ def test_shifting_to_empty_doesn_t_break_the_list(self):
lst = LinkedList()
lst.push(41)
lst.push(59)
+ lst.shift()
+ lst.shift()
lst.push(47)
self.assertEqual(len(lst), 1)
self.assertEqual(lst.shift(), 47)
@@ -143,12 +149,6 @@ def test_deletes_the_second_of_two_elements(self):
self.assertEqual(len(lst), 1)
self.assertEqual(lst.pop(), 97)
- def test_delete_does_not_modify_the_list_if_the_element_is_not_found(self):
- lst = LinkedList()
- lst.push(89)
- lst.delete(103)
- self.assertEqual(len(lst), 1)
-
def test_deletes_only_the_first_occurrence(self):
lst = LinkedList()
lst.push(73)
@@ -173,8 +173,8 @@ def test_can_return_with_pop_and_then_raise_an_error_if_empty(self):
lst = LinkedList()
lst.push(1)
lst.unshift(5)
- self.assertEqual(lst.pop(), 5)
self.assertEqual(lst.pop(), 1)
+ self.assertEqual(lst.pop(), 5)
with self.assertRaises(IndexError) as err:
lst.pop()
self.assertEqual(type(err.exception), IndexError)
@@ -191,8 +191,8 @@ def test_can_return_with_shift_and_then_raise_an_error_if_empty(self):
lst = LinkedList()
lst.push(1)
lst.unshift(5)
- self.assertEqual(lst.pop(), 5)
- self.assertEqual(lst.shift(), 1)
+ self.assertEqual(lst.pop(), 1)
+ self.assertEqual(lst.shift(), 5)
with self.assertRaises(IndexError) as err:
lst.shift()
self.assertEqual(type(err.exception), IndexError)
@@ -201,7 +201,7 @@ def test_can_return_with_shift_and_then_raise_an_error_if_empty(self):
def test_using_delete_raises_an_error_if_the_list_is_empty(self):
lst = LinkedList()
with self.assertRaises(ValueError) as err:
- lst.delete()
+ lst.delete(0)
self.assertEqual(type(err.exception), ValueError)
self.assertEqual(err.exception.args[0], "Value not found")
@@ -211,6 +211,6 @@ def test_using_delete_raises_an_error_if_the_it_is_not_found(self):
lst.push(7)
self.assertEqual(lst.pop(), 7)
with self.assertRaises(ValueError) as err:
- lst.pop()
+ lst.delete(0)
self.assertEqual(type(err.exception), ValueError)
self.assertEqual(err.exception.args[0], "Value not found")
From 92bf6e3f1be3735188631e446818ee3e41c6c085 Mon Sep 17 00:00:00 2001
From: Meatball
Date: Thu, 8 Dec 2022 21:56:07 +0100
Subject: [PATCH 251/826] Made the error handeling shorter
---
.../practice/linked-list/.meta/template.j2 | 24 +++++--------------
1 file changed, 6 insertions(+), 18 deletions(-)
diff --git a/exercises/practice/linked-list/.meta/template.j2 b/exercises/practice/linked-list/.meta/template.j2
index e9c6a30954..471ca2f098 100644
--- a/exercises/practice/linked-list/.meta/template.j2
+++ b/exercises/practice/linked-list/.meta/template.j2
@@ -3,15 +3,15 @@
def test_{{ case["description"] | to_snake }}(self):
lst = LinkedList()
{%- set inputs = case["input"]["operations"] -%}
+ {%- if case["expected"] -%}
+ {%- set error_case = true -%}
+ {%- set error_msg = case["expected"]["error"] -%}
+ {%- set error_operation = inputs[-1]["operation"] -%}
+ {% endif %}
{%- if case["expected"] and inputs|length > 1 -%}
{%- set final = inputs[-1]["operation"] -%}
{%- set inputs = inputs[:-1] -%}
- {%- if case["expected"]["error"] -%}
- {%- set error_case = true -%}
- {%- set error_msg = case["expected"]["error"] -%}
- {%- set error_operation = inputs[-1]["operation"] -%}
- {% endif %}
- {%- elif case["expected"] and not error_case -%}
+ {%- elif case["expected"] -%}
{%- set final = inputs[-1]["operation"] -%}
{%- set inputs = [{"operation": "None"}] -%}
{%- endif -%}
@@ -33,18 +33,6 @@
lst.{{ operation }}({{ value }})
{%- elif operation == "count" %}
self.assertEqual(len(lst), {{ expected }})
- {%- elif case["expected"]["error"] %}
- {%- if final == "pop" or final == "shift" %}
- with self.assertRaises(IndexError) as err:
- lst.{{ case["input"]["operations"][-1]["operation"] }}()
- self.assertEqual(type(err.exception), IndexError)
- self.assertEqual(err.exception.args[0], "{{ error_msg }}")
- {%- elif final == "delete" %}
- with self.assertRaises(ValueError) as err:
- lst.{{ case["input"]["operations"][-1]["operation"] }}({{ value if value else 0 }})
- self.assertEqual(type(err.exception), ValueError)
- self.assertEqual(err.exception.args[0], "{{ error_msg }}")
- {%- endif %}
{%- endif %}
{%- endfor %}
{%- if error_case %}
From 83530a738d508492e63df2dc8b67ff028bf23c75 Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Thu, 8 Dec 2022 19:52:59 -0800
Subject: [PATCH 252/826] Final Edits(I hope)
Added raising error message examples to instruction append.
Added hint about implementing __iter__ to instruction append.
Added ref links for documentation.
Lightly edited example file.
---
.../linked-list/.docs/instructions.apped.md | 25 ++++++++++++++++---
.../practice/linked-list/.meta/example.py | 4 ---
2 files changed, 21 insertions(+), 8 deletions(-)
diff --git a/exercises/practice/linked-list/.docs/instructions.apped.md b/exercises/practice/linked-list/.docs/instructions.apped.md
index 7b649b857b..bf0892ff00 100644
--- a/exercises/practice/linked-list/.docs/instructions.apped.md
+++ b/exercises/practice/linked-list/.docs/instructions.apped.md
@@ -4,9 +4,10 @@
While linked lists can be implemented in a variety of ways with a variety of underlying data structures, we ask here that you implement your linked list in an OOP fashion.
-In the stub file, you will see a `Node` class and a `LinkedList` class.
+In the stub file, you will see the start of a `Node` class, as well as a `LinkedList` class.
Your `Node` class should keep track of its value, as well as which other nodes preceed or follow.
Your `push`, `pop`, `shift`, `unshift`, and the special method for `len` should be implemented in the `LinkedList` class.
+You might also find it useful to implement a special `iter` method for iteration.
In addition to the methods outlined above, we also ask that you implement `delete`.
@@ -17,26 +18,42 @@ If the value appears more than once, the **first** occurance should be removed.
## Exception messages
-Sometimes it is necessary to [raise an exception](https://docs.python.org/3/tutorial/errors.html#raising-exceptions). When you do this, you should always include a **meaningful error message** to indicate what the source of the error is. This makes your code more readable and helps significantly with debugging. For situations where you know that the error source will be a certain type, you can choose to raise one of the [built in error types](https://docs.python.org/3/library/exceptions.html#base-classes), but should still include a meaningful message.
+Sometimes it is necessary to [raise an exception][raising]. When you do this, you should always include a **meaningful error message** to indicate what the source of the error is. This makes your code more readable and helps significantly with debugging. For situations where you know that the error source will be a certain type, you can choose to raise one of the [built in error types][error types], but should still include a meaningful message.
-This particular exercise requires that you use the [raise statement](https://docs.python.org/3/reference/simple_stmts.html#the-raise-statement) to "throw" a `ValueError` when a node value being `delet()`-ed is not found in the linked list. Additionally, an `IndexError` should be thrown if there are no nodes left to `pop()`. The tests will only pass if you both `raise` these `exceptions` and include messages with them.
+This particular exercise requires that you use the [raise statement][raise] to "throw" a `ValueError` when a node value being `delet()`-ed is not found in the linked list. Additionally, an `IndexError` should be thrown if there are no nodes left to `pop()`. The tests will only pass if you both `raise` these `exceptions` and include messages with them.
To raise a `ValueError` with a message, write the message as an argument to the `exception` type:
```python
# When the value passed to `delete()` is not found.
+if not found:
+ raise ValueError("Value not found")
+```
-# When pop() is called and there are no nodes left in the linked list
+To raise an `IndexError` with a message, write the message as an argument to the `exception` type:
+```python
+# When pop() is called and there are no nodes left in the linked list
+if self.length == 0:
+ raise IndexError("List is empty")
```
+
## Special Methods in Python
The tests for this exercise will also be calling `len()` on your `LinkedList`.
In order for `len()` to work, you will need to create a `__len__` special method.
For details on implementing special or "dunder" methods in Python, see [Python Docs: Basic Object Customization][basic customization] and [Python Docs: object.__len__(self)][__len__].
+We also recommend creating a special [`__iter__`][__iter__] method to help with iterating over your linked list.
+
+[__iter__]: https://docs.python.org/3/reference/datamodel.html#object.__iter__
+[__len__]: https://docs.python.org/3/reference/datamodel.html#object.__len__
+[basic customization]: https://docs.python.org/3/reference/datamodel.html#basic-customization
+[error types]: https://docs.python.org/3/library/exceptions.html#base-classes
+[raise]: https://docs.python.org/3/reference/simple_stmts.html#the-raise-statement
+[raising]: https://docs.python.org/3/tutorial/errors.html#raising-exceptions
diff --git a/exercises/practice/linked-list/.meta/example.py b/exercises/practice/linked-list/.meta/example.py
index 1496ef2596..acc13d4f9e 100644
--- a/exercises/practice/linked-list/.meta/example.py
+++ b/exercises/practice/linked-list/.meta/example.py
@@ -40,9 +40,7 @@ def delete(self, to_delete):
found = True
self.length -= 1
break
-
node = node.succeeding
-
if not found:
raise ValueError("Value not found")
@@ -67,9 +65,7 @@ def pop(self):
else:
self.tail = self.tail.prev
self.tail.succeeding = None
-
self.length -= 1
- print(self.length)
return node.value
From 00c560771a1c9f99271d68653cb04f0146b527f5 Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Thu, 8 Dec 2022 20:17:27 -0800
Subject: [PATCH 253/826] More Edits on Instruction Append
---
exercises/practice/linked-list/.docs/instructions.apped.md | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/exercises/practice/linked-list/.docs/instructions.apped.md b/exercises/practice/linked-list/.docs/instructions.apped.md
index bf0892ff00..08f494b655 100644
--- a/exercises/practice/linked-list/.docs/instructions.apped.md
+++ b/exercises/practice/linked-list/.docs/instructions.apped.md
@@ -9,10 +9,11 @@ Your `Node` class should keep track of its value, as well as which other nodes p
Your `push`, `pop`, `shift`, `unshift`, and the special method for `len` should be implemented in the `LinkedList` class.
You might also find it useful to implement a special `iter` method for iteration.
+Unlike the core exercise, we will be testing error conditions by calling `pop` and `shift` on empty `LinkedLists`, so you will need to `raise` errors appropriately.
-In addition to the methods outlined above, we also ask that you implement `delete`.
+Finally, we would like you to implement `delete` in addition to the methods outlined above.
`delete` will take one argument, which is the vaule to be removed from the linked list.
-If the value appears more than once, the **first** occurance should be removed.
+If the value appears more than once, only the **first** occurance should be removed.
From acee9f230d79984be9bba74c989f4cc061d4c001 Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Thu, 8 Dec 2022 20:31:00 -0800
Subject: [PATCH 254/826] One. Last. Change.
---
exercises/practice/linked-list/.meta/template.j2 | 2 --
1 file changed, 2 deletions(-)
diff --git a/exercises/practice/linked-list/.meta/template.j2 b/exercises/practice/linked-list/.meta/template.j2
index 471ca2f098..103be69f0d 100644
--- a/exercises/practice/linked-list/.meta/template.j2
+++ b/exercises/practice/linked-list/.meta/template.j2
@@ -19,8 +19,6 @@
{%- set operation = input["operation"] -%}
{%- set value = input["value"] -%}
{%- set expected = input["expected"] %}
- {%- set error_msg = case["expected"]["error"] -%}
- {%- set error_operation = inputs[-1]["operation"] -%}
{%- if operation and value %}
lst.{{ operation }}({{ value }})
{%- elif operation and expected %}
From 09bd542547a5a9d125ae22dcc8b88516443e4277 Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Fri, 9 Dec 2022 09:21:41 -0800
Subject: [PATCH 255/826] Apply suggestions from code review
Co-authored-by: Katrina Owen
---
exercises/practice/linked-list/.docs/instructions.apped.md | 6 +++---
exercises/practice/linked-list/.meta/additional_tests.json | 2 +-
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/exercises/practice/linked-list/.docs/instructions.apped.md b/exercises/practice/linked-list/.docs/instructions.apped.md
index 08f494b655..975494d025 100644
--- a/exercises/practice/linked-list/.docs/instructions.apped.md
+++ b/exercises/practice/linked-list/.docs/instructions.apped.md
@@ -12,8 +12,8 @@ You might also find it useful to implement a special `iter` method for iteration
Unlike the core exercise, we will be testing error conditions by calling `pop` and `shift` on empty `LinkedLists`, so you will need to `raise` errors appropriately.
Finally, we would like you to implement `delete` in addition to the methods outlined above.
-`delete` will take one argument, which is the vaule to be removed from the linked list.
-If the value appears more than once, only the **first** occurance should be removed.
+`delete` will take one argument, which is the value to be removed from the linked list.
+If the value appears more than once, only the **first** occurrence should be removed.
@@ -21,7 +21,7 @@ If the value appears more than once, only the **first** occurance should be remo
Sometimes it is necessary to [raise an exception][raising]. When you do this, you should always include a **meaningful error message** to indicate what the source of the error is. This makes your code more readable and helps significantly with debugging. For situations where you know that the error source will be a certain type, you can choose to raise one of the [built in error types][error types], but should still include a meaningful message.
-This particular exercise requires that you use the [raise statement][raise] to "throw" a `ValueError` when a node value being `delet()`-ed is not found in the linked list. Additionally, an `IndexError` should be thrown if there are no nodes left to `pop()`. The tests will only pass if you both `raise` these `exceptions` and include messages with them.
+This particular exercise requires that you use the [raise statement][raise] to "throw" a `ValueError` when a node value being `delete()`-ed is not found in the linked list. Additionally, an `IndexError` should be thrown if there are no nodes left to `pop()`. The tests will only pass if you both `raise` these `exceptions` and include messages with them.
To raise a `ValueError` with a message, write the message as an argument to the `exception` type:
diff --git a/exercises/practice/linked-list/.meta/additional_tests.json b/exercises/practice/linked-list/.meta/additional_tests.json
index 5be9653f3f..a791426a61 100644
--- a/exercises/practice/linked-list/.meta/additional_tests.json
+++ b/exercises/practice/linked-list/.meta/additional_tests.json
@@ -103,7 +103,7 @@
},
{
"uuid": "f7f9d2dc-be1b-4f42-be2e-c29980ce0823",
- "description": "Using delete raises an error if the it is not found",
+ "description": "Using delete raises an error if the value is not found",
"property": "list",
"input": {
"operations": [
From a7b7f8ec92ed16eaf282a2c4201f10717fe4702f Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Fri, 9 Dec 2022 09:38:47 -0800
Subject: [PATCH 256/826] Updates/Edits
Regenerated test file (don't know why it needed it, but ok.
Re tested solution.
Edited instruction append to be one sentence per line.
---
.../practice/linked-list/.docs/instructions.apped.md | 8 ++++++--
exercises/practice/linked-list/linked_list_test.py | 2 +-
2 files changed, 7 insertions(+), 3 deletions(-)
diff --git a/exercises/practice/linked-list/.docs/instructions.apped.md b/exercises/practice/linked-list/.docs/instructions.apped.md
index 975494d025..00032862c2 100644
--- a/exercises/practice/linked-list/.docs/instructions.apped.md
+++ b/exercises/practice/linked-list/.docs/instructions.apped.md
@@ -19,9 +19,13 @@ If the value appears more than once, only the **first** occurrence should be rem
## Exception messages
-Sometimes it is necessary to [raise an exception][raising]. When you do this, you should always include a **meaningful error message** to indicate what the source of the error is. This makes your code more readable and helps significantly with debugging. For situations where you know that the error source will be a certain type, you can choose to raise one of the [built in error types][error types], but should still include a meaningful message.
+Sometimes it is necessary to [raise an exception][raising]. When you do this, you should always include a **meaningful error message** to indicate what the source of the error is.
+This makes your code more readable and helps significantly with debugging.
+For situations where you know that the error source will be a certain type, you can choose to raise one of the [built in error types][error types], but should still include a meaningful message.
-This particular exercise requires that you use the [raise statement][raise] to "throw" a `ValueError` when a node value being `delete()`-ed is not found in the linked list. Additionally, an `IndexError` should be thrown if there are no nodes left to `pop()`. The tests will only pass if you both `raise` these `exceptions` and include messages with them.
+This particular exercise requires that you use the [raise statement][raise] to "throw" a `ValueError` when a node value being `delete()`-ed is not found in the linked list.
+Additionally, an `IndexError` should be thrown if there are no nodes left to `pop()`.
+The tests will only pass if you both `raise` these `exceptions` and include messages with them.
To raise a `ValueError` with a message, write the message as an argument to the `exception` type:
diff --git a/exercises/practice/linked-list/linked_list_test.py b/exercises/practice/linked-list/linked_list_test.py
index 37d48b3a8d..3af5bc221e 100644
--- a/exercises/practice/linked-list/linked_list_test.py
+++ b/exercises/practice/linked-list/linked_list_test.py
@@ -205,7 +205,7 @@ def test_using_delete_raises_an_error_if_the_list_is_empty(self):
self.assertEqual(type(err.exception), ValueError)
self.assertEqual(err.exception.args[0], "Value not found")
- def test_using_delete_raises_an_error_if_the_it_is_not_found(self):
+ def test_using_delete_raises_an_error_if_the_value_is_not_found(self):
lst = LinkedList()
lst.push(5)
lst.push(7)
From ed310e19496aa3d91114324a30b7060626e75ef6 Mon Sep 17 00:00:00 2001
From: Katrina Owen
Date: Fri, 9 Dec 2022 09:59:51 +0100
Subject: [PATCH 257/826] Add custom token to community contributions workflow
The pause-community-contributions workflow makes a call to the
GitHub API to check if the person contributing is a member of
the organization.
However, this call currently fails if the contributor has set their
membership to 'private'.
This is because the default token provided for GitHub Actions only
has permissions for the repository, not for the organization. With
this token, we're not allowed to see private memberships.
We've created a custom, org-wide secret containing a personal token
that has permissions to read organization membership.
Unfortunately the secret cannot be accessed directly by the shared
workflow, it has to be passed in.
We updated the shared workflow to use the token, if it is provided,
and this PR updates the workflow in this repo to pass the secret.
Until this is merged, contributions from people with private membership
in the Exercism organization will be automatically closed.
Note that this PR also removes the workflow_dispatch which fails if
you try to use it.
---
.github/workflows/pause-community-contributions.yml | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/.github/workflows/pause-community-contributions.yml b/.github/workflows/pause-community-contributions.yml
index e91c86cf68..d764bfe8b6 100644
--- a/.github/workflows/pause-community-contributions.yml
+++ b/.github/workflows/pause-community-contributions.yml
@@ -10,7 +10,6 @@ on:
paths-ignore:
- 'exercises/*/*/.approaches/**'
- 'exercises/*/*/.articles/**'
- workflow_dispatch:
permissions:
issues: write
@@ -22,3 +21,5 @@ jobs:
uses: exercism/github-actions/.github/workflows/community-contributions.yml@main
with:
forum_category: python
+ secrets:
+ github_membership_token: ${{ secrets.COMMUNITY_CONTRIBUTIONS_WORKFLOW_TOKEN }}
From ab87e6f7e153261b478a72a98d440346e9909fb9 Mon Sep 17 00:00:00 2001
From: Meatball
Date: Sat, 10 Dec 2022 23:24:12 +0100
Subject: [PATCH 258/826] Added Jinja template
---
exercises/practice/react/.meta/config.json | 4 +-
exercises/practice/react/.meta/template.j2 | 62 ++++
exercises/practice/react/react.py | 1 +
exercises/practice/react/react_test.py | 314 +++++++++++++--------
4 files changed, 257 insertions(+), 124 deletions(-)
create mode 100644 exercises/practice/react/.meta/template.j2
diff --git a/exercises/practice/react/.meta/config.json b/exercises/practice/react/.meta/config.json
index 71e3b58ad1..6ecc14244d 100644
--- a/exercises/practice/react/.meta/config.json
+++ b/exercises/practice/react/.meta/config.json
@@ -6,7 +6,9 @@
"Dog",
"N-Parsons",
"Nishant23",
- "tqa236"
+ "tqa236",
+ "meatball133",
+ "Bethanyg"
],
"files": {
"solution": [
diff --git a/exercises/practice/react/.meta/template.j2 b/exercises/practice/react/.meta/template.j2
new file mode 100644
index 0000000000..f9fb637c43
--- /dev/null
+++ b/exercises/practice/react/.meta/template.j2
@@ -0,0 +1,62 @@
+{%- import "generator_macros.j2" as macros with context -%}
+from functools import partial
+{% macro test_case(case) -%}
+ {%- set input = case["input"] -%}
+ {%- set callback = [] -%}
+ {%- for operation in input["operations"] -%}
+ {%- if operation["type"] == "add_callback" -%}
+ {% set callback = callback.append(operation["name"]) %}
+ {%- endif -%}
+ {%- endfor -%}
+ def test_{{ case["description"] | to_snake }}(self):
+ {%- for cell in input["cells"] -%}
+ {%- if cell["type"] == "input" %}
+ {{ cell["name"] }} = InputCell({{ cell["initial_value"] }})
+ {%- elif cell["type"] == "compute" -%}
+ {%- if "if" in cell["compute_function"] %}
+ output = ComputeCell([input], lambda inputs: 111 if inputs[0] < 3 else 222)
+ {%- else %}
+ {{ cell["name"] }} = ComputeCell([{%- for input in cell["inputs"] -%}{{ input }},{%- endfor -%}], lambda inputs: {{ cell["compute_function"] }})
+ {%- endif -%}
+ {%- endif -%}
+ {%- endfor -%}
+ {%- if callback -%}
+ {%- for _ in callback %}
+ cb{{ loop.index0 +1 }}_observer = []
+ {%- endfor -%}
+ {%- endif %}
+ {% for sub_callback in callback -%}
+ {{ sub_callback }} = self.callback_factory(cb{{ loop.index0 + 1 }}_observer)
+ {% endfor -%}
+ {%- for operation in input["operations"] -%}
+ {%- if operation["type"] == "add_callback" or operation["type"] == "remove_callback" -%}
+ {{ operation["cell"] }}.{{ operation["type"] }}({{ operation["name"] }})
+ {%- elif operation["type"] == "expect_cell_value" -%}
+ self.assertEqual({{ operation["cell"] }}.value, {{ operation["value"] }})
+ {%- elif operation["type"] == "set_value" -%}
+ {{ operation["cell"] }}.value = {{ operation["value"] }}
+ {%- if operation["expect_callbacks_not_to_be_called"] %}
+ {%- if callback | length == 3 %}
+ self.assertEqual(len(cb{{ operation["expect_callbacks_not_to_be_called"][0][-1] }}_observer), 1)
+ {%- else %}
+ self.assertEqual(cb{{ operation["expect_callbacks_not_to_be_called"][0][-1] }}_observer, [])
+ {%- endif %}
+ {%- endif -%}
+ {%- for exp_callback in operation["expect_callbacks"] %}
+ self.assertEqual(cb{{ exp_callback[-1] }}_observer[-1], {{ operation["expect_callbacks"][exp_callback] }})
+ {%- endfor -%}
+ {%- endif %}
+ {% endfor -%}
+{%- endmacro %}
+{{ macros.header(["InputCell", "ComputeCell"])}}
+
+class {{ exercise | camel_case }}Test(unittest.TestCase):
+ {% for case in cases -%}
+ {{ test_case(case) }}
+ {% endfor %}
+
+ # Utility functions.
+ def callback_factory(self, observer):
+ def callback(observer, value):
+ observer.append(value)
+ return partial(callback, observer)
diff --git a/exercises/practice/react/react.py b/exercises/practice/react/react.py
index 03ff02e789..ab6be311d9 100644
--- a/exercises/practice/react/react.py
+++ b/exercises/practice/react/react.py
@@ -12,3 +12,4 @@ def add_callback(self, callback):
def remove_callback(self, callback):
pass
+
\ No newline at end of file
diff --git a/exercises/practice/react/react_test.py b/exercises/practice/react/react_test.py
index 7c1870cfda..2ba86a80ff 100644
--- a/exercises/practice/react/react_test.py
+++ b/exercises/practice/react/react_test.py
@@ -1,202 +1,270 @@
-import unittest
from functools import partial
-from react import InputCell, ComputeCell
+import unittest
+
+from react import (
+ InputCell,
+ ComputeCell,
+)
+# Tests adapted from `problem-specifications//canonical-data.json`
-# Tests adapted from `problem-specifications//canonical-data.json` @ v2.0.0
class ReactTest(unittest.TestCase):
-
def test_input_cells_have_a_value(self):
- input_ = InputCell(10)
- self.assertEqual(input_.value, 10)
+ input = InputCell(10)
+ self.assertEqual(input.value, 10)
- def test_can_set_input_cell_value(self):
- input_ = InputCell(4)
- input_.value = 20
- self.assertEqual(input_.value, 20)
+ def test_an_input_cell_s_value_can_be_set(self):
+ input = InputCell(4)
+ input.value = 20
+ self.assertEqual(input.value, 20)
def test_compute_cells_calculate_initial_value(self):
- input_ = InputCell(1)
- output = ComputeCell([input_], lambda inputs: inputs[0] + 1)
+ input = InputCell(1)
+ output = ComputeCell(
+ [
+ input,
+ ],
+ lambda inputs: inputs[0] + 1,
+ )
self.assertEqual(output.value, 2)
- def test_compute_cells_take_inputs_in_right_order(self):
+ def test_compute_cells_take_inputs_in_the_right_order(self):
one = InputCell(1)
two = InputCell(2)
output = ComputeCell(
- [one, two],
- lambda inputs: inputs[0] + inputs[1]*10
+ [
+ one,
+ two,
+ ],
+ lambda inputs: inputs[0] + inputs[1] * 10,
)
self.assertEqual(output.value, 21)
def test_compute_cells_update_value_when_dependencies_are_changed(self):
- input_ = InputCell(1)
- output = ComputeCell([input_], lambda inputs: inputs[0] + 1)
-
- input_.value = 3
+ input = InputCell(1)
+ output = ComputeCell(
+ [
+ input,
+ ],
+ lambda inputs: inputs[0] + 1,
+ )
+ input.value = 3
self.assertEqual(output.value, 4)
def test_compute_cells_can_depend_on_other_compute_cells(self):
- input_ = InputCell(1)
- times_two = ComputeCell([input_], lambda inputs: inputs[0] * 2)
- times_thirty = ComputeCell([input_], lambda inputs: inputs[0] * 30)
+ input = InputCell(1)
+ times_two = ComputeCell(
+ [
+ input,
+ ],
+ lambda inputs: inputs[0] * 2,
+ )
+ times_thirty = ComputeCell(
+ [
+ input,
+ ],
+ lambda inputs: inputs[0] * 30,
+ )
output = ComputeCell(
- [times_two, times_thirty],
- lambda inputs: inputs[0] + inputs[1]
+ [
+ times_two,
+ times_thirty,
+ ],
+ lambda inputs: inputs[0] + inputs[1],
)
-
self.assertEqual(output.value, 32)
- input_.value = 3
+ input.value = 3
self.assertEqual(output.value, 96)
def test_compute_cells_fire_callbacks(self):
- input_ = InputCell(1)
- output = ComputeCell([input_], lambda inputs: inputs[0] + 1)
-
- observer = []
- callback1 = self.callback_factory(observer)
-
- output.add_callback(callback1)
- input_.value = 3
- self.assertEqual(observer[-1], 4)
-
- def test_callbacks_only_fire_on_change(self):
- input_ = InputCell(1)
+ input = InputCell(1)
output = ComputeCell(
- [input_],
- lambda inputs: 111 if inputs[0] < 3 else 222
+ [
+ input,
+ ],
+ lambda inputs: inputs[0] + 1,
)
+ cb1_observer = []
+ callback1 = self.callback_factory(cb1_observer)
+ output.add_callback(callback1)
+ input.value = 3
+ self.assertEqual(cb1_observer[-1], 4)
- observer = []
- callback1 = self.callback_factory(observer)
-
+ def test_callback_cells_only_fire_on_change(self):
+ input = InputCell(1)
+ output = ComputeCell([input], lambda inputs: 111 if inputs[0] < 3 else 222)
+ cb1_observer = []
+ callback1 = self.callback_factory(cb1_observer)
output.add_callback(callback1)
- input_.value = 2
- self.assertEqual(observer, [])
- input_.value = 4
- self.assertEqual(observer[-1], 222)
+ input.value = 2
+ self.assertEqual(cb1_observer, [])
+ input.value = 4
+ self.assertEqual(cb1_observer[-1], 222)
def test_callbacks_do_not_report_already_reported_values(self):
- input_ = InputCell(1)
- output = ComputeCell([input_], lambda inputs: inputs[0] + 1)
-
- observer = []
- callback1 = self.callback_factory(observer)
-
+ input = InputCell(1)
+ output = ComputeCell(
+ [
+ input,
+ ],
+ lambda inputs: inputs[0] + 1,
+ )
+ cb1_observer = []
+ callback1 = self.callback_factory(cb1_observer)
output.add_callback(callback1)
- input_.value = 2
- self.assertEqual(observer[-1], 3)
- input_.value = 3
- self.assertEqual(observer[-1], 4)
+ input.value = 2
+ self.assertEqual(cb1_observer[-1], 3)
+ input.value = 3
+ self.assertEqual(cb1_observer[-1], 4)
def test_callbacks_can_fire_from_multiple_cells(self):
- input_ = InputCell(1)
- plus_one = ComputeCell([input_], lambda inputs: inputs[0] + 1)
- minus_one = ComputeCell([input_], lambda inputs: inputs[0] - 1)
-
- cb1_observer, cb2_observer = [], []
+ input = InputCell(1)
+ plus_one = ComputeCell(
+ [
+ input,
+ ],
+ lambda inputs: inputs[0] + 1,
+ )
+ minus_one = ComputeCell(
+ [
+ input,
+ ],
+ lambda inputs: inputs[0] - 1,
+ )
+ cb1_observer = []
+ cb2_observer = []
callback1 = self.callback_factory(cb1_observer)
callback2 = self.callback_factory(cb2_observer)
-
plus_one.add_callback(callback1)
minus_one.add_callback(callback2)
- input_.value = 10
-
+ input.value = 10
self.assertEqual(cb1_observer[-1], 11)
self.assertEqual(cb2_observer[-1], 9)
def test_callbacks_can_be_added_and_removed(self):
- input_ = InputCell(11)
- output = ComputeCell([input_], lambda inputs: inputs[0] + 1)
-
- cb1_observer, cb2_observer, cb3_observer = [], [], []
+ input = InputCell(11)
+ output = ComputeCell(
+ [
+ input,
+ ],
+ lambda inputs: inputs[0] + 1,
+ )
+ cb1_observer = []
+ cb2_observer = []
+ cb3_observer = []
callback1 = self.callback_factory(cb1_observer)
callback2 = self.callback_factory(cb2_observer)
callback3 = self.callback_factory(cb3_observer)
-
output.add_callback(callback1)
output.add_callback(callback2)
- input_.value = 31
+ input.value = 31
self.assertEqual(cb1_observer[-1], 32)
self.assertEqual(cb2_observer[-1], 32)
-
output.remove_callback(callback1)
output.add_callback(callback3)
- input_.value = 41
+ input.value = 41
+ self.assertEqual(len(cb1_observer), 1)
self.assertEqual(cb2_observer[-1], 42)
self.assertEqual(cb3_observer[-1], 42)
- # Expect callback1 not to be called.
- self.assertEqual(len(cb1_observer), 1)
-
- def test_removing_a_callback_multiple_times(self):
- """Guard against incorrect implementations which store their
- callbacks in an array."""
- input_ = InputCell(1)
- output = ComputeCell([input_], lambda inputs: inputs[0] + 1)
-
- cb1_observer, cb2_observer = [], []
+ def test_removing_a_callback_multiple_times_doesn_t_interfere_with_other_callbacks(
+ self,
+ ):
+ input = InputCell(1)
+ output = ComputeCell(
+ [
+ input,
+ ],
+ lambda inputs: inputs[0] + 1,
+ )
+ cb1_observer = []
+ cb2_observer = []
callback1 = self.callback_factory(cb1_observer)
callback2 = self.callback_factory(cb2_observer)
-
output.add_callback(callback1)
output.add_callback(callback2)
output.remove_callback(callback1)
output.remove_callback(callback1)
output.remove_callback(callback1)
- input_.value = 2
-
+ input.value = 2
self.assertEqual(cb1_observer, [])
self.assertEqual(cb2_observer[-1], 3)
- def test_callbacks_should_only_be_called_once(self):
- """Guard against incorrect implementations which call a callback
- function multiple times when multiple dependencies change."""
- input_ = InputCell(1)
- plus_one = ComputeCell([input_], lambda inputs: inputs[0] + 1)
- minus_one1 = ComputeCell([input_], lambda inputs: inputs[0] - 1)
- minus_one2 = ComputeCell([minus_one1], lambda inputs: inputs[0] - 1)
+ def test_callbacks_should_only_be_called_once_even_if_multiple_dependencies_change(
+ self,
+ ):
+ input = InputCell(1)
+ plus_one = ComputeCell(
+ [
+ input,
+ ],
+ lambda inputs: inputs[0] + 1,
+ )
+ minus_one1 = ComputeCell(
+ [
+ input,
+ ],
+ lambda inputs: inputs[0] - 1,
+ )
+ minus_one2 = ComputeCell(
+ [
+ minus_one1,
+ ],
+ lambda inputs: inputs[0] - 1,
+ )
output = ComputeCell(
- [plus_one, minus_one2],
- lambda inputs: inputs[0] * inputs[1]
+ [
+ plus_one,
+ minus_one2,
+ ],
+ lambda inputs: inputs[0] * inputs[1],
)
-
- observer = []
- callback1 = self.callback_factory(observer)
-
+ cb1_observer = []
+ callback1 = self.callback_factory(cb1_observer)
output.add_callback(callback1)
- input_.value = 4
- self.assertEqual(observer[-1], 10)
-
- def test_callbacks_not_called_so_long_as_output_not_changed(self):
- """Guard against incorrect implementations which call callbacks
- if dependencies change but output value doesn't change."""
- input_ = InputCell(1)
- plus_one = ComputeCell([input_], lambda inputs: inputs[0] + 1)
- minus_one = ComputeCell([input_], lambda inputs: inputs[0] - 1)
+ input.value = 4
+ self.assertEqual(cb1_observer[-1], 10)
+
+ def test_callbacks_should_not_be_called_if_dependencies_change_but_output_value_doesn_t_change(
+ self,
+ ):
+ input = InputCell(1)
+ plus_one = ComputeCell(
+ [
+ input,
+ ],
+ lambda inputs: inputs[0] + 1,
+ )
+ minus_one = ComputeCell(
+ [
+ input,
+ ],
+ lambda inputs: inputs[0] - 1,
+ )
always_two = ComputeCell(
- [plus_one, minus_one],
- lambda inputs: inputs[0] - inputs[1]
+ [
+ plus_one,
+ minus_one,
+ ],
+ lambda inputs: inputs[0] - inputs[1],
)
-
- observer = []
- callback1 = self.callback_factory(observer)
-
+ cb1_observer = []
+ callback1 = self.callback_factory(cb1_observer)
always_two.add_callback(callback1)
- input_.value = 2
- input_.value = 3
- input_.value = 4
- input_.value = 5
- self.assertEqual(observer, [])
+ input.value = 2
+ self.assertEqual(cb1_observer, [])
+ input.value = 3
+ self.assertEqual(cb1_observer, [])
+ input.value = 4
+ self.assertEqual(cb1_observer, [])
+ input.value = 5
+ self.assertEqual(cb1_observer, [])
# Utility functions.
def callback_factory(self, observer):
def callback(observer, value):
observer.append(value)
- return partial(callback, observer)
-
-if __name__ == '__main__':
- unittest.main()
+ return partial(callback, observer)
From c5da4971d562bf401502292be7bcd981d8abfc31 Mon Sep 17 00:00:00 2001
From: PaulT89 <97411029+PaulT89@users.noreply.github.com>
Date: Sun, 11 Dec 2022 09:58:59 +1100
Subject: [PATCH 259/826] [Pascals Triangle] Remastered for recursion (#3150)
* [Pascals Triangle] Remastered for recursion
Rewrote example.py:
- Uses recursion rather than loops.
- Raises a meaningful error when row_count < 0, instead of just returning None.
Added template.j2:
- Based general layout of the template on the old pascals_triangle_test file.
Added instructions.append.md:
- Encourage student to use recursion to solve this problem
- Added boilerplate message about how to raise exceptions
.meta/config.json:
- Added name as co-author (though maybe contributor would be more appropriate?)
config.json:
- practices recursion
- added (seemingly) sensible sounding prerequisites
- kept difficulty
- status = wip for now
- moved entire block up to be in line with other exercises of similar difficulty.
* [Pascals Triangle] Remastered for recursion
Rewrote example.py:
- Uses recursion rather than loops.
- Raises a meaningful error when row_count < 0, instead of just returning None.
Added template.j2:
- Based general layout of the template on the old pascals_triangle_test file.
Added instructions.append.md:
- Encourage student to use recursion to solve this problem
- Added boilerplate message about how to raise exceptions
.meta/config.json:
- Added name as co-author (though maybe contributor would be more appropriate?)
config.json:
- practices recursion
- added (seemingly) sensible sounding prerequisites
- kept difficulty
- moved entire block up to be in line with other exercises of similar difficulty.
* Updated Exercise
-Fixed config prerequisite bug (exercise is now accessible).
- Moved hardcoded additional tests to additional_tests.json file.
- Added test for recursion.
* Fixed bug
Moved import sys
* Fixed template indentation
* Manual recursion limit
* Revert "Manual recursion limit"
This reverts commit f11762983ff1a548cb8930fa83c07d637b722a05.
* Minor formatting changes
* Canonical sync fix
Apparently the RecursionError message changed in Python 3.9. Hopefully this will work across all tested versions now.
* [Pascals Triangle] Remastered for recursion
Rewrote example.py:
- Uses recursion rather than loops.
- Raises a meaningful error when row_count < 0, instead of just returning None.
Added template.j2:
- Based general layout of the template on the old pascals_triangle_test file.
Added instructions.append.md:
- Encourage student to use recursion to solve this problem
- Added boilerplate message about how to raise exceptions
.meta/config.json:
- Added name as co-author (though maybe contributor would be more appropriate?)
config.json:
- practices recursion
- added (seemingly) sensible sounding prerequisites
- kept difficulty
- moved entire block up to be in line with other exercises of similar difficulty.
* [Pascals Triangle] Remastered for recursion
Rewrote example.py:
- Uses recursion rather than loops.
- Raises a meaningful error when row_count < 0, instead of just returning None.
Added template.j2:
- Based general layout of the template on the old pascals_triangle_test file.
Added instructions.append.md:
- Encourage student to use recursion to solve this problem
- Added boilerplate message about how to raise exceptions
.meta/config.json:
- Added name as co-author (though maybe contributor would be more appropriate?)
config.json:
- practices recursion
- added (seemingly) sensible sounding prerequisites
- kept difficulty
- status = wip for now
- moved entire block up to be in line with other exercises of similar difficulty.
* Updated Exercise
-Fixed config prerequisite bug (exercise is now accessible).
- Moved hardcoded additional tests to additional_tests.json file.
- Added test for recursion.
* Fixed bug
Moved import sys
* Fixed template indentation
* Manual recursion limit
* Revert "Manual recursion limit"
This reverts commit f11762983ff1a548cb8930fa83c07d637b722a05.
* Minor formatting changes
* Canonical sync fix
Apparently the RecursionError message changed in Python 3.9. Hopefully this will work across all tested versions now.
* Updated instructions
-Updated Instructions.append.md
-Added hints.md
* Template changes and small reflink edits
Shortened the JinJa Template a bit.
Edited instruction append to use reflinks and a recursion widget, and be one sentence per line.
Retested everything.
* Update config.json
* Update config.json
Co-authored-by: BethanyG
---
config.json | 22 ++++++----
.../practice/pascals-triangle/.docs/hints.md | 10 +++++
.../.docs/instructions.append.md | 43 +++++++++++++++++++
.../.meta/additional_tests.json | 26 +++++++++++
.../pascals-triangle/.meta/config.json | 3 +-
.../pascals-triangle/.meta/example.py | 14 +++---
.../pascals-triangle/.meta/template.j2 | 39 +++++++++++++++++
.../pascals-triangle/pascals_triangle_test.py | 32 +++++++++-----
8 files changed, 159 insertions(+), 30 deletions(-)
create mode 100644 exercises/practice/pascals-triangle/.docs/hints.md
create mode 100644 exercises/practice/pascals-triangle/.docs/instructions.append.md
create mode 100644 exercises/practice/pascals-triangle/.meta/additional_tests.json
create mode 100644 exercises/practice/pascals-triangle/.meta/template.j2
diff --git a/config.json b/config.json
index f267cbd83a..6c8340b289 100644
--- a/config.json
+++ b/config.json
@@ -1271,6 +1271,19 @@
],
"difficulty": 3
},
+ {
+ "slug": "pascals-triangle",
+ "name": "Pascals Triangle",
+ "uuid": "e1e1c7d7-c1d9-4027-b90d-fad573182419",
+ "practices": ["recursion"],
+ "prerequisites": [
+ "basics",
+ "conditionals",
+ "lists",
+ "numbers"
+ ],
+ "difficulty": 4
+ },
{
"slug": "grep",
"name": "Grep",
@@ -2140,15 +2153,6 @@
"difficulty": 3,
"status": "deprecated"
},
- {
- "slug": "pascals-triangle",
- "name": "Pascal's Triangle",
- "uuid": "e1e1c7d7-c1d9-4027-b90d-fad573182419",
- "practices": [],
- "prerequisites": [],
- "difficulty": 3,
- "status": "deprecated"
- },
{
"slug": "point-mutations",
"name": "Point Mutations",
diff --git a/exercises/practice/pascals-triangle/.docs/hints.md b/exercises/practice/pascals-triangle/.docs/hints.md
new file mode 100644
index 0000000000..235e786ff8
--- /dev/null
+++ b/exercises/practice/pascals-triangle/.docs/hints.md
@@ -0,0 +1,10 @@
+# Hints
+
+## General
+
+- A more detailed description of recursive programming can be found [here][g4g]
+- This exercise involves a test to ensure that you used a recursive solution
+- If you are having trouble completing this exercise, try [using a loop first, and then convert it into a recursive solution][educative]
+
+[g4g]: https://www.geeksforgeeks.org/recursion/
+[educative]: https://www.educative.io/collection/page/6151088528949248/4547996664463360/6292303276670976
diff --git a/exercises/practice/pascals-triangle/.docs/instructions.append.md b/exercises/practice/pascals-triangle/.docs/instructions.append.md
new file mode 100644
index 0000000000..3b236ae489
--- /dev/null
+++ b/exercises/practice/pascals-triangle/.docs/instructions.append.md
@@ -0,0 +1,43 @@
+# Instructions append
+
+## How this Exercise is Implemented in Python: Recursion
+
+This exercise is designed to be completed using [concept:python/recursion](), rather than loops.
+A recursive function is a function that calls itself, which is useful when solving problems that are defined in terms of themselves.
+To avoid infinite recursion (or more specifically, to avoid overflowing the stack), something called a "base case" is used.
+When the base case is reached, a non-recursive value is returned, which allows the previous function call to resolve and return its value, and so on, rippling back down the stack until the first function call returns the answer.
+We could write a recursive function to find the answer to 5! (i.e. 5 * 4 * 3 * 2 * 1) like so:
+
+````python
+def factorial(number):
+ if number <= 1: # base case
+ return 1
+
+ return number * factorial(number - 1) # recursive case
+
+print(factorial(5)) # returns 120
+````
+
+Finally, it should be noted that Python limits the number of times recursive calls can be made (1000 by default) and does not optimize for [tail recursion][tail-recursion].
+
+## Exception messages
+
+Sometimes it is necessary to [raise an exception][raising].
+When you do this, you should always include a **meaningful error message** to indicate what the source of the error is.
+This makes your code more readable and helps significantly with debugging.
+For situations where you know that the error source will be a certain type, you can choose to raise one of the [built in error types][built-in-errors], but should still include a meaningful message.
+
+This particular exercise requires that you use the [raise statement][raise-statement] to "throw" multiple `ValueErrors` if the `rows()` function is passed a negative number.
+The tests will only pass if you both `raise` the `exception` and include a message with it.
+
+To raise a `ValueError` with a message, write the message as an argument to the `exception` type:
+
+```python
+# if the rows function is passed a negative number.
+raise ValueError("number of rows is negative")
+```
+
+[built-in-errors]: https://docs.python.org/3/library/exceptions.html#base-classes
+[raise-statement]: https://docs.python.org/3/reference/simple_stmts.html#the-raise-statement
+[raising]: https://docs.python.org/3/tutorial/errors.html#raising-exceptions
+[tail-recursion]: https://www.geeksforgeeks.org/tail-recursion/
diff --git a/exercises/practice/pascals-triangle/.meta/additional_tests.json b/exercises/practice/pascals-triangle/.meta/additional_tests.json
new file mode 100644
index 0000000000..7c1375ab5a
--- /dev/null
+++ b/exercises/practice/pascals-triangle/.meta/additional_tests.json
@@ -0,0 +1,26 @@
+{
+ "cases": [
+ {
+ "description": "negative rows are invalid",
+ "property": "rows",
+ "input": {
+ "count": -1
+ },
+ "expected": {
+ "error": "number of rows is negative",
+ "type": "ValueError"
+ }
+ },
+ {
+ "description": "solution is recursive",
+ "property": "rows",
+ "input": {
+ "count": "OVERFLOW"
+ },
+ "expected": {
+ "error": "maximum recursion depth exceeded",
+ "type": "RecursionError"
+ }
+ }
+ ]
+}
diff --git a/exercises/practice/pascals-triangle/.meta/config.json b/exercises/practice/pascals-triangle/.meta/config.json
index 167b937cce..4793e16f1c 100644
--- a/exercises/practice/pascals-triangle/.meta/config.json
+++ b/exercises/practice/pascals-triangle/.meta/config.json
@@ -1,7 +1,8 @@
{
"blurb": "Compute Pascal's triangle up to a given number of rows.",
"authors": [
- "betegelse"
+ "betegelse",
+ "PaulT89"
],
"contributors": [
"behrtam",
diff --git a/exercises/practice/pascals-triangle/.meta/example.py b/exercises/practice/pascals-triangle/.meta/example.py
index 70f3aec1dd..9181199bf2 100644
--- a/exercises/practice/pascals-triangle/.meta/example.py
+++ b/exercises/practice/pascals-triangle/.meta/example.py
@@ -1,12 +1,8 @@
-def rows(row_count):
+def rows(row_count, previous_row=[1]):
if row_count < 0:
- return None
+ raise ValueError("number of rows is negative")
elif row_count == 0:
return []
- row_list = []
- for idx in range(row_count):
- ronald = [1]
- for edx in range(idx):
- ronald.append(sum(row_list[-1][edx:edx+2]))
- row_list.append(ronald)
- return row_list
+ temp_row = previous_row + [0]
+ new_row = list(map(sum, zip(temp_row, temp_row[::-1])))
+ return [previous_row] + rows(row_count - 1, new_row)
diff --git a/exercises/practice/pascals-triangle/.meta/template.j2 b/exercises/practice/pascals-triangle/.meta/template.j2
new file mode 100644
index 0000000000..0041fd53c2
--- /dev/null
+++ b/exercises/practice/pascals-triangle/.meta/template.j2
@@ -0,0 +1,39 @@
+{%- import "generator_macros.j2" as macros with context -%}
+import sys
+{{ macros.header() }}
+
+TRIANGLE = [
+[1],
+[1, 1],
+[1, 2, 1],
+[1, 3, 3, 1],
+[1, 4, 6, 4, 1],
+[1, 5, 10, 10, 5, 1],
+[1, 6, 15, 20, 15, 6, 1],
+[1, 7, 21, 35, 35, 21, 7, 1],
+[1, 8, 28, 56, 70, 56, 28, 8, 1],
+[1, 9, 36, 84, 126, 126, 84, 36, 9, 1]
+]
+
+class {{ exercise | camel_case }}Test(unittest.TestCase):
+{%- for case in cases %}
+ def test_{{ case["description"] | to_snake }}(self):
+ self.assertEqual({{ case["property"] }}({{ case["input"]["count"] }}), TRIANGLE[:{{ case["input"]["count"] }}])
+{% endfor %}
+
+# Additional tests for this track
+{%- for case in additional_cases %}
+ def test_{{ case["description"] | to_snake }}(self):
+ {%- if case is error_case %}
+ with self.assertRaises({{ case["expected"]["type"] }}) as err:
+ {%- if case["input"]["count"] == "OVERFLOW" %}
+ rows(sys.getrecursionlimit() + 10)
+ self.assertEqual(type(err.exception), RecursionError)
+ self.assertEqual(err.exception.args[0][:32], "maximum recursion depth exceeded")
+ {%- else %}
+ rows(-1)
+ self.assertEqual(type(err.exception), ValueError)
+ self.assertEqual(err.exception.args[0], "number of rows is negative")
+ {%- endif %}
+ {%- endif %}
+{% endfor %}
diff --git a/exercises/practice/pascals-triangle/pascals_triangle_test.py b/exercises/practice/pascals-triangle/pascals_triangle_test.py
index c2c5068145..dc7b7ce111 100644
--- a/exercises/practice/pascals-triangle/pascals_triangle_test.py
+++ b/exercises/practice/pascals-triangle/pascals_triangle_test.py
@@ -1,9 +1,11 @@
+import sys
import unittest
-from pascals_triangle import rows
+from pascals_triangle import (
+ rows,
+)
-
-# Tests adapted from `problem-specifications//canonical-data.json` @ v1.2.0
+# Tests adapted from `problem-specifications//canonical-data.json`
TRIANGLE = [
[1],
@@ -15,13 +17,13 @@
[1, 6, 15, 20, 15, 6, 1],
[1, 7, 21, 35, 35, 21, 7, 1],
[1, 8, 28, 56, 70, 56, 28, 8, 1],
- [1, 9, 36, 84, 126, 126, 84, 36, 9, 1]
+ [1, 9, 36, 84, 126, 126, 84, 36, 9, 1],
]
class PascalsTriangleTest(unittest.TestCase):
def test_zero_rows(self):
- self.assertEqual(rows(0), [])
+ self.assertEqual(rows(0), TRIANGLE[:0])
def test_single_row(self):
self.assertEqual(rows(1), TRIANGLE[:1])
@@ -44,9 +46,17 @@ def test_six_rows(self):
def test_ten_rows(self):
self.assertEqual(rows(10), TRIANGLE[:10])
- def test_negative_rows(self):
- self.assertEqual(rows(-1), None)
-
-
-if __name__ == '__main__':
- unittest.main()
+ # Additional tests for this track
+ def test_negative_rows_are_invalid(self):
+ with self.assertRaises(ValueError) as err:
+ rows(-1)
+ self.assertEqual(type(err.exception), ValueError)
+ self.assertEqual(err.exception.args[0], "number of rows is negative")
+
+ def test_solution_is_recursive(self):
+ with self.assertRaises(RecursionError) as err:
+ rows(sys.getrecursionlimit() + 10)
+ self.assertEqual(type(err.exception), RecursionError)
+ self.assertEqual(
+ err.exception.args[0][:32], "maximum recursion depth exceeded"
+ )
From ee4d9e4120a68b094c32be03ad6baa3882eae045 Mon Sep 17 00:00:00 2001
From: meatball <69751659+meatball133@users.noreply.github.com>
Date: Sun, 11 Dec 2022 01:44:49 +0100
Subject: [PATCH 260/826] [Exercise]: added square-root (#3279)
* added square-root
* Instruction Append
Co-authored-by: BethanyG
---
config.json | 13 +++++++++
.../square-root/.docs/instructions.append.md | 16 +++++++++++
.../square-root/.docs/instructions.md | 13 +++++++++
.../practice/square-root/.meta/config.json | 21 ++++++++++++++
.../practice/square-root/.meta/example.py | 5 ++++
.../practice/square-root/.meta/template.j2 | 15 ++++++++++
.../practice/square-root/.meta/tests.toml | 28 +++++++++++++++++++
exercises/practice/square-root/square_root.py | 2 ++
.../practice/square-root/square_root_test.py | 27 ++++++++++++++++++
9 files changed, 140 insertions(+)
create mode 100644 exercises/practice/square-root/.docs/instructions.append.md
create mode 100644 exercises/practice/square-root/.docs/instructions.md
create mode 100644 exercises/practice/square-root/.meta/config.json
create mode 100644 exercises/practice/square-root/.meta/example.py
create mode 100644 exercises/practice/square-root/.meta/template.j2
create mode 100644 exercises/practice/square-root/.meta/tests.toml
create mode 100644 exercises/practice/square-root/square_root.py
create mode 100644 exercises/practice/square-root/square_root_test.py
diff --git a/config.json b/config.json
index 6c8340b289..d1399f2f3f 100644
--- a/config.json
+++ b/config.json
@@ -593,6 +593,19 @@
],
"difficulty": 2
},
+ {
+ "slug": "square-root",
+ "name": "Square Root",
+ "uuid": "c32f994a-1080-4f05-bf88-051975a75d64",
+ "practices": ["number"],
+ "prerequisites": [
+ "basics",
+ "number",
+ "conditionals",
+ "loops"
+ ],
+ "difficulty": 2
+ },
{
"slug": "scrabble-score",
"name": "Scrabble Score",
diff --git a/exercises/practice/square-root/.docs/instructions.append.md b/exercises/practice/square-root/.docs/instructions.append.md
new file mode 100644
index 0000000000..535038e466
--- /dev/null
+++ b/exercises/practice/square-root/.docs/instructions.append.md
@@ -0,0 +1,16 @@
+# Instructions append
+
+## How this Exercise is Structured in Python
+
+
+Python offers a wealth of mathematical functions in the form of the [math module][math-module] and built-ins such as [`pow()`][pow] and [`sum()`][sum].
+However, we'd like you to consider the challenge of solving this exercise without those built-ins or modules.
+
+While there is a mathematical formula that will find the square root of _any_ number, we have gone the route of only testing [natural numbers][nautral-number] (positive integers).
+
+
+[math-module]: https://docs.python.org/3/library/math.html
+[pow]: https://docs.python.org/3/library/functions.html#pow
+[sum]: https://docs.python.org/3/library/functions.html#sum
+[nautral-number]: https://en.wikipedia.org/wiki/Natural_number
+
diff --git a/exercises/practice/square-root/.docs/instructions.md b/exercises/practice/square-root/.docs/instructions.md
new file mode 100644
index 0000000000..e9905e9d41
--- /dev/null
+++ b/exercises/practice/square-root/.docs/instructions.md
@@ -0,0 +1,13 @@
+# Instructions
+
+Given a natural radicand, return its square root.
+
+Note that the term "radicand" refers to the number for which the root is to be determined.
+That is, it is the number under the root symbol.
+
+Check out the Wikipedia pages on [square root][square-root] and [methods of computing square roots][computing-square-roots].
+
+Recall also that natural numbers are positive real whole numbers (i.e. 1, 2, 3 and up).
+
+[square-root]: https://en.wikipedia.org/wiki/Square_root
+[computing-square-roots]: https://en.wikipedia.org/wiki/Methods_of_computing_square_roots
diff --git a/exercises/practice/square-root/.meta/config.json b/exercises/practice/square-root/.meta/config.json
new file mode 100644
index 0000000000..b1b9630b31
--- /dev/null
+++ b/exercises/practice/square-root/.meta/config.json
@@ -0,0 +1,21 @@
+{
+ "authors": [
+ "meatball133",
+ "Bethanyg"
+ ],
+ "contributors": [],
+ "files": {
+ "solution": [
+ "square_root.py"
+ ],
+ "test": [
+ "square_root_test.py"
+ ],
+ "example": [
+ ".meta/example.py"
+ ]
+ },
+ "blurb": "Given a natural radicand, return its square root.",
+ "source": "wolf99",
+ "source_url": "https://github.com/exercism/problem-specifications/pull/1582"
+}
diff --git a/exercises/practice/square-root/.meta/example.py b/exercises/practice/square-root/.meta/example.py
new file mode 100644
index 0000000000..94cd0fdc71
--- /dev/null
+++ b/exercises/practice/square-root/.meta/example.py
@@ -0,0 +1,5 @@
+def square_root(number):
+ n = 0
+ while n ** 2 != number:
+ n += 1
+ return n
diff --git a/exercises/practice/square-root/.meta/template.j2 b/exercises/practice/square-root/.meta/template.j2
new file mode 100644
index 0000000000..9334317859
--- /dev/null
+++ b/exercises/practice/square-root/.meta/template.j2
@@ -0,0 +1,15 @@
+{%- import "generator_macros.j2" as macros with context -%}
+{% macro test_case(case) -%}
+ {%- set input = case["input"] -%}
+ def test_{{ case["description"] | to_snake }}(self):
+ self.assertEqual(
+ {{ case["property"] | to_snake }}({{ case["input"]["radicand"] }}),
+ {{ case["expected"] }}
+ )
+{%- endmacro %}
+{{ macros.header()}}
+
+class {{ exercise | camel_case }}Test(unittest.TestCase):
+ {% for case in cases -%}
+ {{ test_case(case) }}
+ {% endfor %}
diff --git a/exercises/practice/square-root/.meta/tests.toml b/exercises/practice/square-root/.meta/tests.toml
new file mode 100644
index 0000000000..ead7882fc3
--- /dev/null
+++ b/exercises/practice/square-root/.meta/tests.toml
@@ -0,0 +1,28 @@
+# This is an auto-generated file.
+#
+# Regenerating this file via `configlet sync` will:
+# - Recreate every `description` key/value pair
+# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications
+# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion)
+# - Preserve any other key/value pair
+#
+# As user-added comments (using the # character) will be removed when this file
+# is regenerated, comments can be added via a `comment` key.
+
+[9b748478-7b0a-490c-b87a-609dacf631fd]
+description = "root of 1"
+
+[7d3aa9ba-9ac6-4e93-a18b-2e8b477139bb]
+description = "root of 4"
+
+[6624aabf-3659-4ae0-a1c8-25ae7f33c6ef]
+description = "root of 25"
+
+[93beac69-265e-4429-abb1-94506b431f81]
+description = "root of 81"
+
+[fbddfeda-8c4f-4bc4-87ca-6991af35360e]
+description = "root of 196"
+
+[c03d0532-8368-4734-a8e0-f96a9eb7fc1d]
+description = "root of 65025"
diff --git a/exercises/practice/square-root/square_root.py b/exercises/practice/square-root/square_root.py
new file mode 100644
index 0000000000..0a2fc38927
--- /dev/null
+++ b/exercises/practice/square-root/square_root.py
@@ -0,0 +1,2 @@
+def square_root(number):
+ pass
diff --git a/exercises/practice/square-root/square_root_test.py b/exercises/practice/square-root/square_root_test.py
new file mode 100644
index 0000000000..635728fee4
--- /dev/null
+++ b/exercises/practice/square-root/square_root_test.py
@@ -0,0 +1,27 @@
+import unittest
+
+from square_root import (
+ square_root,
+)
+
+# Tests adapted from `problem-specifications//canonical-data.json`
+
+
+class SquareRootTest(unittest.TestCase):
+ def test_root_of_1(self):
+ self.assertEqual(square_root(1), 1)
+
+ def test_root_of_4(self):
+ self.assertEqual(square_root(4), 2)
+
+ def test_root_of_25(self):
+ self.assertEqual(square_root(25), 5)
+
+ def test_root_of_81(self):
+ self.assertEqual(square_root(81), 9)
+
+ def test_root_of_196(self):
+ self.assertEqual(square_root(196), 14)
+
+ def test_root_of_65025(self):
+ self.assertEqual(square_root(65025), 255)
From 0765a702e117ba7aad6d80578f3cfb4f5e17e86b Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Fri, 16 Dec 2022 16:21:02 -0600
Subject: [PATCH 261/826] Update content.md (#3280)
Changed `This concept is also aparat of` to `This concept is also a part of`.
---
.../practice/wordy/.approaches/dunder-getattribute/content.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/exercises/practice/wordy/.approaches/dunder-getattribute/content.md b/exercises/practice/wordy/.approaches/dunder-getattribute/content.md
index f64bc6fac3..dd3e6de39d 100644
--- a/exercises/practice/wordy/.approaches/dunder-getattribute/content.md
+++ b/exercises/practice/wordy/.approaches/dunder-getattribute/content.md
@@ -76,7 +76,7 @@ It sets the list to the result of the dunder method plus the remaining elements
```exercism/note
The `*` prefix in `*tail` [unpacks](https://treyhunner.com/2018/10/asterisks-in-python-what-they-are-and-how-to-use-them/) the `tail` list back into its elements.
-This concept is also aparat of [unpacking-and-multiple-assignment](https://exercism.org/tracks/python/concepts/unpacking-and-multiple-assignment) concept in the syllabus..
+This concept is also a part of [unpacking-and-multiple-assignment](https://exercism.org/tracks/python/concepts/unpacking-and-multiple-assignment) concept in the syllabus.
```
When the loop exhausts, the first element of the list is selected as the function return value.
From 090a5eb2828c4acd36f0cf6e9fa855ec2ed3f016 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Fri, 16 Dec 2022 23:01:30 +0000
Subject: [PATCH 262/826] Bump exercism/pr-commenter-action from 1.3.1 to 1.4.0
Bumps [exercism/pr-commenter-action](https://github.com/exercism/pr-commenter-action) from 1.3.1 to 1.4.0.
- [Release notes](https://github.com/exercism/pr-commenter-action/releases)
- [Changelog](https://github.com/exercism/pr-commenter-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/exercism/pr-commenter-action/compare/v1.3.1...v1.4.0)
---
updated-dependencies:
- dependency-name: exercism/pr-commenter-action
dependency-type: direct:production
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot]
---
.github/workflows/pr-commenter.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/pr-commenter.yml b/.github/workflows/pr-commenter.yml
index 12591d4900..13c0b62d44 100644
--- a/.github/workflows/pr-commenter.yml
+++ b/.github/workflows/pr-commenter.yml
@@ -6,7 +6,7 @@ jobs:
pr-comment:
runs-on: ubuntu-latest
steps:
- - uses: exercism/pr-commenter-action@v1.3.1
+ - uses: exercism/pr-commenter-action@v1.4.0
with:
github-token: "${{ github.token }}"
config-file: ".github/pr-commenter.yml"
\ No newline at end of file
From 6ac97d58b28f8669f9e332bfcb8399cb5efd192d Mon Sep 17 00:00:00 2001
From: Isaac Good
Date: Sun, 18 Dec 2022 10:53:15 -0800
Subject: [PATCH 263/826] Update SGF Parsing template, tests and escape
function
---
.../practice/sgf-parsing/.meta/example.py | 215 +++++++++---------
.../practice/sgf-parsing/.meta/template.j2 | 8 +-
.../practice/sgf-parsing/.meta/tests.toml | 8 +-
.../practice/sgf-parsing/sgf_parsing_test.py | 46 +++-
4 files changed, 160 insertions(+), 117 deletions(-)
diff --git a/exercises/practice/sgf-parsing/.meta/example.py b/exercises/practice/sgf-parsing/.meta/example.py
index 7711625518..369c7de643 100644
--- a/exercises/practice/sgf-parsing/.meta/example.py
+++ b/exercises/practice/sgf-parsing/.meta/example.py
@@ -1,108 +1,115 @@
-class SgfTree:
- def __init__(self, properties=None, children=None):
- self.properties = properties or {}
- self.children = children or []
-
- def __eq__(self, other):
- if not isinstance(other, SgfTree):
- return False
-
- for key, value in self.properties.items():
- if key not in other.properties:
- return False
-
- if other.properties[key] != value:
- return False
-
- for key in other.properties.keys():
- if key not in self.properties:
- return False
-
- if len(self.children) != len(other.children):
- return False
-
- for child, other_child in zip(self.children, other.children):
- if not child == other_child:
- return False
- return True
-
- def __repr__(self):
- """Ironically, encoding to SGF is much easier."""
- rep = '(;'
- for key, property_value in self.properties.items():
- rep += key
- for value in property_value:
- rep += '[{}]'.format(value)
- if self.children:
- if len(self.children) > 1:
- rep += '('
- for child in self.children:
- rep += repr(child)[1:-1]
- if len(self.children) > 1:
- rep += ')'
- return rep + ')'
-
-
-def parse(input_string):
- root = None
- current = None
- stack = list(input_string)
- if input_string == '()':
- raise ValueError('tree with no nodes')
+"""Parse an SGF tree."""
+from __future__ import annotations
- if not stack:
- raise ValueError('tree missing')
+import collections
+import dataclasses
+import string
- def pop():
- if stack[0] == '\\':
- stack.pop(0)
- characters = stack.pop(0)
- return ' ' if characters in ['\t'] else characters
-
- def pop_until(delimiter):
- try:
- value = ''
- while stack[0] != delimiter:
- if stack[0] == "\n":
- stack[0] = "n"
- if stack[0] == "\t":
- stack[0] = "t"
- value += pop()
- return value
- except IndexError as error:
- raise ValueError('properties without delimiter') from error
-
- while stack:
- if pop() == '(' and stack[0] == ';':
- while pop() == ';':
- properties = {}
- while stack[0].isupper():
- key = pop_until('[')
- if not key.isupper():
- raise ValueError('property must be in uppercase')
- values = []
- while stack[0] == '[':
- pop()
- values.append(pop_until(']'))
- pop()
- properties[key] = values
-
- if stack[0].isalpha():
- if not stack[0].isupper():
- raise ValueError('property must be in uppercase')
-
- if root is None:
- current = root = SgfTree(properties)
+@dataclasses.dataclass
+class SgfTree:
+ """SGF Node."""
+
+ properties: dict[str, str] = dataclasses.field(default_factory=dict)
+ children: list[SgfTree] = dataclasses.field(default_factory=list)
+
+
+def parse_property_vals(sgf: str, idx: int) -> tuple[int, list[str]]:
+ """Parse property values, returning the next index and values."""
+ values = []
+ while idx < len(sgf):
+ if sgf[idx] != "[":
+ break
+
+ # Start of the value.
+ idx += 1
+ prop_val = ""
+ while sgf[idx] != "]":
+ # \ has special SGF handling.
+ if sgf[idx] == "\\":
+ if sgf[idx:idx + 2] == "\\\n":
+ # Newlines are removed if they come immediately after a \,
+ # otherwise they remain as newlines.
+ pass
else:
- current = SgfTree(properties)
- root.children.append(current)
-
- while stack[0] == '(':
- child_input = pop() + pop_until(')') + pop()
- current.children.append(parse(child_input))
-
- elif root is None and current is None:
- raise ValueError('tree missing')
-
- return root
+ # \ is the escape character. Any non-whitespace character
+ # after \ is inserted as-is
+ prop_val += sgf[idx + 1]
+ idx += 2
+ else:
+ prop_val += sgf[idx]
+ idx += 1
+
+ # All whitespace characters other than newline are converted to spaces.
+ for char in string.whitespace:
+ if char == "\n":
+ continue
+ prop_val = prop_val.replace(char, " ")
+
+ values.append(prop_val)
+ idx += 1
+
+ return idx, values
+
+
+def parse_node(sgf: str) -> SgfTree:
+ """Parse and return a Node."""
+ if not sgf.startswith(";"):
+ raise ValueError("node must start with ';'")
+
+ idx = 1
+ prop_key_start = idx
+
+ properties = collections.defaultdict(list)
+ children = []
+
+ while idx < len(sgf):
+ match sgf[idx]:
+ case "[":
+ # Parse property values.
+ if idx == prop_key_start:
+ raise ValueError("propery key is empty")
+ prop_key = sgf[prop_key_start:idx]
+ if not prop_key.isupper():
+ raise ValueError('property must be in uppercase')
+
+ idx, prop_vals = parse_property_vals(sgf, idx)
+ properties[prop_key].extend(prop_vals)
+
+ # New property.
+ prop_key_start = idx
+ case ";":
+ # Single child.
+ child = parse_node(sgf[idx:])
+ children.append(child)
+ break
+ case "(":
+ # Multiple children.
+ children = []
+ while idx < len(sgf):
+ if sgf[idx] != "(":
+ break
+ # Child start.
+ idx += 1
+ child_start = idx
+ while sgf[idx] != ")":
+ idx += 1
+ # Child end.
+ child = parse_node(sgf[child_start:idx])
+ children.append(child)
+ idx += 1
+ case _:
+ idx += 1
+
+ if idx > prop_key_start and not properties:
+ raise ValueError('properties without delimiter')
+ return SgfTree(children=children, properties=dict(properties))
+
+
+def parse(sgf: str) -> SgfTree:
+ """Parse an SGF tree."""
+ if not sgf.startswith("(") and not sgf.endswith(")"):
+ raise ValueError('tree missing')
+ if not sgf.startswith("(;"):
+ raise ValueError('tree with no nodes')
+ return parse_node(sgf.removeprefix("(").removesuffix(")"))
diff --git a/exercises/practice/sgf-parsing/.meta/template.j2 b/exercises/practice/sgf-parsing/.meta/template.j2
index f3da29b39c..7017a43648 100644
--- a/exercises/practice/sgf-parsing/.meta/template.j2
+++ b/exercises/practice/sgf-parsing/.meta/template.j2
@@ -1,8 +1,12 @@
{%- import "generator_macros.j2" as macros with context -%}
+{% macro escape_sequences(string) -%}
+ {{ string | replace("\\", "\\\\") | replace("\n", "\\n") | replace("\t", "\\t") }}
+{%- endmacro -%}
+
{% macro test_case(case) -%}
def test_{{ case["description"] | to_snake }}(self):
- input_string = '{{ case["input"]["encoded"] | escape_invalid_escapes }}'
+ input_string = '{{ escape_sequences(case["input"]["encoded"]) }}'
{% if case["expected"]["error"] -%}
with self.assertRaises(ValueError) as err:
{{ case["property"] | to_snake }}(input_string)
@@ -33,7 +37,7 @@
{%- for key, values in properties.items() -%}
'{{ key }}':[
{%- for value in values -%}
- '{{ value }}'{% if not loop.last %}, {% endif -%}
+ '{{ escape_sequences(value) }}'{% if not loop.last %}, {% endif -%}
{% endfor -%}]
{%- if not loop.last %}, {% endif -%}
{% endfor -%} }
diff --git a/exercises/practice/sgf-parsing/.meta/tests.toml b/exercises/practice/sgf-parsing/.meta/tests.toml
index 71f5b83a64..2a9d7d927b 100644
--- a/exercises/practice/sgf-parsing/.meta/tests.toml
+++ b/exercises/practice/sgf-parsing/.meta/tests.toml
@@ -47,23 +47,18 @@ description = "multiple property values"
[28092c06-275f-4b9f-a6be-95663e69d4db]
description = "within property values, whitespace characters such as tab are converted to spaces"
-include = false
[deaecb9d-b6df-4658-aa92-dcd70f4d472a]
-description = "within property values, newli es remain as newlines"
-include = false
+description = "within property values, newlines remain as newlines"
[8e4c970e-42d7-440e-bfef-5d7a296868ef]
description = "escaped closing bracket within property value becomes just a closing bracket"
-include = false
[cf371fa8-ba4a-45ec-82fb-38668edcb15f]
description = "escaped backslash in property value becomes just a backslash"
-include = false
[dc13ca67-fac0-4b65-b3fe-c584d6a2c523]
description = "opening bracket within property value doesn't need to be escaped"
-include = false
[a780b97e-8dbb-474e-8f7e-4031902190e8]
description = "semicolon in property value doesn't need to be escaped"
@@ -83,7 +78,6 @@ description = "escaped t and n in property value are just letters, not whitespac
[08e4b8ba-bb07-4431-a3d9-b1f4cdea6dab]
description = "mixing various kinds of whitespace and escaped characters in property value"
reimplements = "11c36323-93fc-495d-bb23-c88ee5844b8c"
-include = false
[11c36323-93fc-495d-bb23-c88ee5844b8c]
description = "escaped property"
diff --git a/exercises/practice/sgf-parsing/sgf_parsing_test.py b/exercises/practice/sgf-parsing/sgf_parsing_test.py
index 9b01fb11d0..b3d43ed09d 100644
--- a/exercises/practice/sgf-parsing/sgf_parsing_test.py
+++ b/exercises/practice/sgf-parsing/sgf_parsing_test.py
@@ -84,6 +84,38 @@ def test_multiple_property_values(self):
expected = SgfTree(properties={"A": ["b", "c", "d"]})
self.assertEqual(parse(input_string), expected)
+ def test_within_property_values_whitespace_characters_such_as_tab_are_converted_to_spaces(
+ self,
+ ):
+ input_string = "(;A[hello\t\tworld])"
+ expected = SgfTree(properties={"A": ["hello world"]})
+ self.assertEqual(parse(input_string), expected)
+
+ def test_within_property_values_newlines_remain_as_newlines(self):
+ input_string = "(;A[hello\n\nworld])"
+ expected = SgfTree(properties={"A": ["hello\n\nworld"]})
+ self.assertEqual(parse(input_string), expected)
+
+ def test_escaped_closing_bracket_within_property_value_becomes_just_a_closing_bracket(
+ self,
+ ):
+ input_string = "(;A[\\]])"
+ expected = SgfTree(properties={"A": ["]"]})
+ self.assertEqual(parse(input_string), expected)
+
+ def test_escaped_backslash_in_property_value_becomes_just_a_backslash(self):
+ input_string = "(;A[\\\\])"
+ expected = SgfTree(properties={"A": ["\\"]})
+ self.assertEqual(parse(input_string), expected)
+
+ def test_opening_bracket_within_property_value_doesn_t_need_to_be_escaped(self):
+ input_string = "(;A[x[y\\]z][foo]B[bar];C[baz])"
+ expected = SgfTree(
+ properties={"A": ["x[y]z", "foo"], "B": ["bar"]},
+ children=[SgfTree({"C": ["baz"]})],
+ )
+ self.assertEqual(parse(input_string), expected)
+
def test_semicolon_in_property_value_doesn_t_need_to_be_escaped(self):
input_string = "(;A[a;b][foo]B[bar];C[baz])"
expected = SgfTree(
@@ -101,17 +133,23 @@ def test_parentheses_in_property_value_don_t_need_to_be_escaped(self):
self.assertEqual(parse(input_string), expected)
def test_escaped_tab_in_property_value_is_converted_to_space(self):
- input_string = "(;A[hello\\ world])"
+ input_string = "(;A[hello\\\tworld])"
expected = SgfTree(properties={"A": ["hello world"]})
self.assertEqual(parse(input_string), expected)
def test_escaped_newline_in_property_value_is_converted_to_nothing_at_all(self):
- input_string = "(;A[hello\
-world])"
+ input_string = "(;A[hello\\\nworld])"
expected = SgfTree(properties={"A": ["helloworld"]})
self.assertEqual(parse(input_string), expected)
def test_escaped_t_and_n_in_property_value_are_just_letters_not_whitespace(self):
- input_string = "(;A[\t = t and \n = n])"
+ input_string = "(;A[\\t = t and \\n = n])"
expected = SgfTree(properties={"A": ["t = t and n = n"]})
self.assertEqual(parse(input_string), expected)
+
+ def test_mixing_various_kinds_of_whitespace_and_escaped_characters_in_property_value(
+ self,
+ ):
+ input_string = "(;A[\\]b\nc\\\nd\t\te\\\\ \\\n\\]])"
+ expected = SgfTree(properties={"A": ["]b\ncd e\\ ]"]})
+ self.assertEqual(parse(input_string), expected)
From cfbebf6708d1ab3d81519b7beed3f097159dfa4a Mon Sep 17 00:00:00 2001
From: Isaac Good
Date: Sun, 18 Dec 2022 12:32:57 -0800
Subject: [PATCH 264/826] Bump flake8 version for requirements-generator from
3.7.8 to 5.0.4
---
requirements-generator.txt | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/requirements-generator.txt b/requirements-generator.txt
index 44391b2d66..3c096bebc1 100644
--- a/requirements-generator.txt
+++ b/requirements-generator.txt
@@ -1,6 +1,6 @@
black==22.3.0
-flake8==3.7.8
+flake8~=5.0.4
Jinja2~=3.1.2
python-dateutil==2.8.1
markupsafe==2.0.1
-tomli~=2.0.1
\ No newline at end of file
+tomli~=2.0.1
From 33afa506a44a0991bb121b02ab618eacf29f2bcb Mon Sep 17 00:00:00 2001
From: Isaac Good
Date: Sun, 18 Dec 2022 12:35:38 -0800
Subject: [PATCH 265/826] Add IsaacG as a contributor to SGF Parsing
---
exercises/practice/sgf-parsing/.meta/config.json | 1 +
1 file changed, 1 insertion(+)
diff --git a/exercises/practice/sgf-parsing/.meta/config.json b/exercises/practice/sgf-parsing/.meta/config.json
index 284ea0fb2f..565a8ce0d5 100644
--- a/exercises/practice/sgf-parsing/.meta/config.json
+++ b/exercises/practice/sgf-parsing/.meta/config.json
@@ -7,6 +7,7 @@
"crsmi",
"Dog",
"elyashiv",
+ "IsaacG",
"thomasjpfan",
"tqa236",
"yawpitch"
From 2fba89110c2567b3e7f0ff7c6e00cdc910fd2bbd Mon Sep 17 00:00:00 2001
From: Isaac Good
Date: Sun, 18 Dec 2022 12:43:51 -0800
Subject: [PATCH 266/826] Drop newer features like `match case` and
str.removeprefix
---
.../practice/sgf-parsing/.meta/example.py | 70 +++++++++----------
1 file changed, 35 insertions(+), 35 deletions(-)
diff --git a/exercises/practice/sgf-parsing/.meta/example.py b/exercises/practice/sgf-parsing/.meta/example.py
index 369c7de643..2b14e37153 100644
--- a/exercises/practice/sgf-parsing/.meta/example.py
+++ b/exercises/practice/sgf-parsing/.meta/example.py
@@ -64,42 +64,41 @@ def parse_node(sgf: str) -> SgfTree:
children = []
while idx < len(sgf):
- match sgf[idx]:
- case "[":
- # Parse property values.
- if idx == prop_key_start:
- raise ValueError("propery key is empty")
- prop_key = sgf[prop_key_start:idx]
- if not prop_key.isupper():
- raise ValueError('property must be in uppercase')
-
- idx, prop_vals = parse_property_vals(sgf, idx)
- properties[prop_key].extend(prop_vals)
-
- # New property.
- prop_key_start = idx
- case ";":
- # Single child.
- child = parse_node(sgf[idx:])
- children.append(child)
- break
- case "(":
- # Multiple children.
- children = []
- while idx < len(sgf):
- if sgf[idx] != "(":
- break
- # Child start.
- idx += 1
- child_start = idx
- while sgf[idx] != ")":
- idx += 1
- # Child end.
- child = parse_node(sgf[child_start:idx])
- children.append(child)
+ if sgf[idx] == "[":
+ # Parse property values.
+ if idx == prop_key_start:
+ raise ValueError("propery key is empty")
+ prop_key = sgf[prop_key_start:idx]
+ if not prop_key.isupper():
+ raise ValueError('property must be in uppercase')
+
+ idx, prop_vals = parse_property_vals(sgf, idx)
+ properties[prop_key].extend(prop_vals)
+
+ # New property.
+ prop_key_start = idx
+ elif sgf[idx] == ";":
+ # Single child.
+ child = parse_node(sgf[idx:])
+ children.append(child)
+ break
+ elif sgf[idx] == "(":
+ # Multiple children.
+ children = []
+ while idx < len(sgf):
+ if sgf[idx] != "(":
+ break
+ # Child start.
+ idx += 1
+ child_start = idx
+ while sgf[idx] != ")":
idx += 1
- case _:
+ # Child end.
+ child = parse_node(sgf[child_start:idx])
+ children.append(child)
idx += 1
+ else:
+ idx += 1
if idx > prop_key_start and not properties:
raise ValueError('properties without delimiter')
@@ -112,4 +111,5 @@ def parse(sgf: str) -> SgfTree:
raise ValueError('tree missing')
if not sgf.startswith("(;"):
raise ValueError('tree with no nodes')
- return parse_node(sgf.removeprefix("(").removesuffix(")"))
+ inside = sgf[1:-1]
+ return parse_node(inside)
From 2e58919bfb22b20f89782b79a6a06e0bc3c4c90d Mon Sep 17 00:00:00 2001
From: Meatball
Date: Mon, 19 Dec 2022 09:34:52 +0100
Subject: [PATCH 267/826] removed 3 lines
---
exercises/practice/linked-list/.meta/template.j2 | 13 +++++--------
1 file changed, 5 insertions(+), 8 deletions(-)
diff --git a/exercises/practice/linked-list/.meta/template.j2 b/exercises/practice/linked-list/.meta/template.j2
index 103be69f0d..a753efc3df 100644
--- a/exercises/practice/linked-list/.meta/template.j2
+++ b/exercises/practice/linked-list/.meta/template.j2
@@ -9,11 +9,9 @@
{%- set error_operation = inputs[-1]["operation"] -%}
{% endif %}
{%- if case["expected"] and inputs|length > 1 -%}
- {%- set final = inputs[-1]["operation"] -%}
{%- set inputs = inputs[:-1] -%}
{%- elif case["expected"] -%}
- {%- set final = inputs[-1]["operation"] -%}
- {%- set inputs = [{"operation": "None"}] -%}
+ {%- set inputs = [] -%}
{%- endif -%}
{%- for input in inputs -%}
{%- set operation = input["operation"] -%}
@@ -34,17 +32,16 @@
{%- endif %}
{%- endfor %}
{%- if error_case %}
- {%- if final == "pop" or final == "shift" %}
+ {%- if error_operation == "pop" or error_operation == "shift" %}
with self.assertRaises(IndexError) as err:
lst.{{ error_operation }}()
self.assertEqual(type(err.exception), IndexError)
- self.assertEqual(err.exception.args[0], "{{ error_msg }}")
- {% elif final == "delete" %}
+ {%- elif error_operation == "delete" %}
with self.assertRaises(ValueError) as err:
- lst.{{ final }}({{ value if value else 0 }})
+ lst.{{ error_operation }}({{ value if value else 0 }})
self.assertEqual(type(err.exception), ValueError)
- self.assertEqual(err.exception.args[0], "{{ error_msg }}")
{%- endif %}
+ self.assertEqual(err.exception.args[0], "{{ error_msg }}")
{%- endif %}
{%- endmacro %}
From 2107057937c77f0a3e36f7154483d5cb7c7f8498 Mon Sep 17 00:00:00 2001
From: Carl
Date: Thu, 22 Dec 2022 22:15:27 +0100
Subject: [PATCH 268/826] Fix file-name
---
.../.docs/{instructions.apped.md => instructions.append.md} | 0
1 file changed, 0 insertions(+), 0 deletions(-)
rename exercises/practice/linked-list/.docs/{instructions.apped.md => instructions.append.md} (100%)
diff --git a/exercises/practice/linked-list/.docs/instructions.apped.md b/exercises/practice/linked-list/.docs/instructions.append.md
similarity index 100%
rename from exercises/practice/linked-list/.docs/instructions.apped.md
rename to exercises/practice/linked-list/.docs/instructions.append.md
From ad8e8c94058ada935479b22024811b5e2f0e0c8b Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 20 Dec 2022 23:01:32 +0000
Subject: [PATCH 269/826] Bump actions/stale from 6 to 7
Bumps [actions/stale](https://github.com/actions/stale) from 6 to 7.
- [Release notes](https://github.com/actions/stale/releases)
- [Changelog](https://github.com/actions/stale/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/stale/compare/v6...v7)
---
updated-dependencies:
- dependency-name: actions/stale
dependency-type: direct:production
update-type: version-update:semver-major
...
Signed-off-by: dependabot[bot]
---
.github/workflows/stale.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml
index 9e4ecd1d5c..4742a0db0a 100644
--- a/.github/workflows/stale.yml
+++ b/.github/workflows/stale.yml
@@ -8,7 +8,7 @@ jobs:
stale:
runs-on: ubuntu-latest
steps:
- - uses: actions/stale@v6
+ - uses: actions/stale@v7
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-stale: 21
From a45dce1c28c66812800eb9fa5b0bacb37f6bd892 Mon Sep 17 00:00:00 2001
From: meatball <69751659+meatball133@users.noreply.github.com>
Date: Wed, 21 Dec 2022 20:47:56 +0100
Subject: [PATCH 270/826] First push
---
.../electric-bill/.docs/instructions.md | 81 +++++++++++++++++++
.../concept/electric-bill/.meta/config.json | 19 +++++
.../concept/electric-bill/.meta/exemplar.py | 48 +++++++++++
.../concept/electric-bill/electric_bill.py | 48 +++++++++++
.../electric-bill/electric_bill_test.py | 58 +++++++++++++
5 files changed, 254 insertions(+)
create mode 100644 exercises/concept/electric-bill/.docs/instructions.md
create mode 100644 exercises/concept/electric-bill/.meta/config.json
create mode 100644 exercises/concept/electric-bill/.meta/exemplar.py
create mode 100644 exercises/concept/electric-bill/electric_bill.py
create mode 100644 exercises/concept/electric-bill/electric_bill_test.py
diff --git a/exercises/concept/electric-bill/.docs/instructions.md b/exercises/concept/electric-bill/.docs/instructions.md
new file mode 100644
index 0000000000..c8022915da
--- /dev/null
+++ b/exercises/concept/electric-bill/.docs/instructions.md
@@ -0,0 +1,81 @@
+# Instructions
+
+The company you work for want to reduce their carbon footprint, so they wants you to write a program which calculates the power usage of their electronics and the cost of running them.
+
+1. Get extra hours
+
+Your employer wants a program that calculates the time it takes to run different electronics.
+Currently the time is stored in hours.
+When your employer added the hours they noticed that the time was not correct and they want you to add 3 extra hours to the time data.
+They also would like to know how many hours needs to be removed to get the data in full days(24 hours).
+The time given doesn't have to be in full days.
+
+Implement a function `get_extra_hours()` that accepts an integer which holds the number of hours.
+The function should then `return` an integer with how many hours which has to be removed to get the time in full days.
+
+```python
+>>> get_extra_hours(25)
+4
+```
+
+2. Get kW value
+
+Your employer wants to know the power usage of the different electronics.
+They want to know the power usage in kW.
+kW stands for kilowatt, there watts is a unit of power.
+Kilo in the unit name is a prefix in the metric system meaning 1000.
+So 1 kilowatt is equal to 1000 watts.
+
+Implement a function `get_kW_value()` that accepts an integer which holds the number of watts.
+The function should then `return` the watts as kilowatts rounded to 1 decimal.
+
+```python
+>>> get_kW_value(1150)
+1.2
+```
+
+3. Get kwh value
+
+To be able to calculate the cost of running the electronics your employer wants to know the power usage in kWh.
+kWh stands for kilowatt-hour, there hour is a unit of time.
+So 1 kilowatt-hour is equal to 1000 watts used for 1 hour.
+An hour is made of 60 minutes and a minute is made of 60 seconds.
+So 1 hour is equal to 3600 seconds.
+To get the kWh value you have to have to convert the watts to kW and then floor-divide it by 3600.
+
+Implement a function `get_kWh_value()` that accepts an integer which holds the number of watts.
+The function should then `return` the watts as an integer.
+
+```python
+>>> get_kWh_value(5000000)
+1
+```
+
+4. Get efficiency
+
+Electronics are not 100% efficient.
+Therefore your employer wants to know the efficiency of the electronics.
+To get efficiency you have to divide the power factor by 100.
+The power factor is a float between 0 and 100.
+
+Implement a function `get_efficiency()` that accepts a float that holds the power factor.
+The function should then `return` the power factor as a float.
+
+```python
+>>> get_efficiency(80)
+0.8
+```
+
+5. Get cost
+
+Your employer wants to know the cost of running the electronics.
+The power used is the power given divided by the efficiency.
+The cost of running the electronics is the power used multiplied by the cost per kWh.
+
+Implement a function `get_cost()` that accepts an integer that holds the number of watts and a float that has the power factor and a float that holds the cost per kwh.
+The function should then `return` the cost of running the electronics as a float rounded to 2 decimals.
+
+```python
+>>> get_cost(5000000, 80, 0.25)
+0.3125
+```
diff --git a/exercises/concept/electric-bill/.meta/config.json b/exercises/concept/electric-bill/.meta/config.json
new file mode 100644
index 0000000000..d1560b2033
--- /dev/null
+++ b/exercises/concept/electric-bill/.meta/config.json
@@ -0,0 +1,19 @@
+{
+ "authors": [
+ "meatball133",
+ "BethanyG"
+ ],
+ "files": {
+ "solution": [
+ "electric_bill.py"
+ ],
+ "test": [
+ "electric_bill_test.py"
+ ],
+ "exemplar": [
+ ".meta/exemplar.py"
+ ]
+ },
+ "icon": "city-office",
+ "blurb": "to do"
+}
diff --git a/exercises/concept/electric-bill/.meta/exemplar.py b/exercises/concept/electric-bill/.meta/exemplar.py
new file mode 100644
index 0000000000..996b9e3afa
--- /dev/null
+++ b/exercises/concept/electric-bill/.meta/exemplar.py
@@ -0,0 +1,48 @@
+"""Functions which helps company to calculate their power usage"""
+
+
+def get_the_amount_of_hours(hours, missing_hours):
+ """Return the amount of hours.
+
+ :param: hours: int - amount of hours.
+ :return: int - amount of hours.
+ """
+ return (hours + missing_hours) % 24
+
+
+def get_kW_value(watts):
+ """Return the kW value of a given watt value.
+
+ :param: watts: int - watt value.
+ :return: float - kW value.
+ """
+ return round(watts / 1000, 1) # rounds here
+
+
+def get_kwh_value(watts):
+ """Return the kWh value of a given watt value and hours.
+
+ :param: watts: int - watt value.
+ :param: hours: int - kilowatt hour value.
+ """
+ return get_kW_value(watts) // 3600
+
+
+def get_efficiency(efficiency):
+ """Return the efficiency as a power factor.
+
+ :param: efficiency: float - efficiency.
+ :return: float - efficiency.
+ """
+ return efficiency / 100
+
+
+def get_price_of_kwh(watts, efficiency, price):
+ """Return the price of a given kWh value, efficiency and price.
+
+ :param: watts: int - watt value.
+ :param: efficiency: float - efficiency.
+ :param: price: float - price of kWh.
+ :return: float - price of kWh.
+ """
+ return price * (get_kwh_value(watts) / get_efficiency(efficiency))
diff --git a/exercises/concept/electric-bill/electric_bill.py b/exercises/concept/electric-bill/electric_bill.py
new file mode 100644
index 0000000000..e7e90c59c6
--- /dev/null
+++ b/exercises/concept/electric-bill/electric_bill.py
@@ -0,0 +1,48 @@
+"""Functions which helps company to calculate their power usage"""
+
+
+def get_the_amount_of_hours(hours):
+ """Return the amount of hours.
+
+ :param: hours: int - amount of hours.
+ :return: int - amount of hours.
+ """
+ return (hours + 3) % 24
+
+
+def get_kW_value(watts):
+ """Return the kW value of a given watt value.
+
+ :param: watts: int - watt value.
+ :return: float - kW value.
+ """
+ return round(watts / 1000, 1) # rounds here
+
+
+def get_kwh_value(watts):
+ """Return the kWh value of a given watt value and hours.
+
+ :param: watts: int - watt value.
+ :param: hours: int - kilowatt hour value.
+ """
+ return int(get_kW_value(watts) // 3600)
+
+
+def get_efficiency(efficiency):
+ """Return the efficiency as a power factor.
+
+ :param: efficiency: float - efficiency.
+ :return: float - efficiency.
+ """
+ return efficiency / 100
+
+
+def get_price_of_kwh(watts, efficiency, price):
+ """Return the price of a given kWh value, efficiency and price.
+
+ :param: watts: int - watt value.
+ :param: efficiency: float - efficiency.
+ :param: price: float - price of kWh.
+ :return: float - price of kWh.
+ """
+ return price * (get_kwh_value(watts) / get_efficiency(efficiency))
diff --git a/exercises/concept/electric-bill/electric_bill_test.py b/exercises/concept/electric-bill/electric_bill_test.py
new file mode 100644
index 0000000000..95745e74a8
--- /dev/null
+++ b/exercises/concept/electric-bill/electric_bill_test.py
@@ -0,0 +1,58 @@
+import unittest
+import pytest
+from electric_bill import (get_the_amount_of_hours,
+ get_kW_value,
+ get_kwh_value,
+ get_efficiency,
+ get_price_of_kwh)
+
+
+class LocomotiveEngineerTest(unittest.TestCase):
+
+ @pytest.mark.task(taskno=1)
+ def test_get_the_amount_of_hours(self):
+ input_data = [25, 10, 5, 2, 1, 120, 21]
+ output_data = [4, 13, 8, 5, 4, 3, 0]
+
+ for variant, (input_data, output_data) in enumerate(zip(input_data, output_data), start=1):
+ with self.subTest(f'variation #{variant}', input_data=input_data, output_data=output_data):
+ error_msg=f'Expected: {output_data} but got a different value.'
+ self.assertEqual(get_the_amount_of_hours(input_data), output_data, msg=error_msg)
+
+ @pytest.mark.task(taskno=2)
+ def test_get_kW_value(self):
+ input_data = [1000, 2200, 2900, 900, 1160]
+ output_data = [1, 2.2, 2.9, 0.9, 1.2]
+ for variant, (input_data, output_data) in enumerate(zip(input_data, output_data), start=1):
+ with self.subTest(f'variation #{variant}', input_data=input_data, output_data=output_data):
+ error_msg=f'Expected: {output_data} but got a different value.'
+ self.assertEqual(get_kW_value(input_data), output_data, msg=error_msg)
+
+ @pytest.mark.task(taskno=3)
+ def test_get_kwh_value(self):
+ input_data = (5000000, 2141241, 43252135, 5324623462, 4321512)
+ output_data = [1, 0, 12, 1479, 1]
+ for variant, (input_data, output_data) in enumerate(zip(input_data, output_data), start=1):
+ with self.subTest(f'variation #{variant}', input_data=input_data, output_data=output_data):
+ error_msg=f'Expected: {output_data} but got a different value.'
+ self.assertEqual(get_kwh_value(input_data), output_data, msg=error_msg)
+
+ @pytest.mark.task(taskno=4)
+ def test_get_efficiency(self):
+ input_data = [80.0, 99.99, 0.8, 40.0]
+ output_data = [0.8, 0.9999, 0.008, 0.4]
+
+ for variant, (input_data, output_data) in enumerate(zip(input_data, output_data), start=1):
+ with self.subTest(f'variation #{variant}', input_data=input_data, output_data=output_data):
+ error_msg=f'Expected: {output_data} but got a different value.'
+ self.assertAlmostEqual(get_efficiency(input_data), output_data, msg=error_msg)
+
+ @pytest.mark.task(taskno=5)
+ def test_get_price_of_kwh(self):
+ input_data = ((5000000, 80.0, 0.25), (2141241, 99.99, 2), (43252135, 0.8, 4), (4321512, 40.0, 2))
+ output_data = (0.3125, 0, 6000, 5)
+
+ for variant, (input_data, output_data) in enumerate(zip(input_data, output_data), start=1):
+ with self.subTest(f'variation #{variant}', input_data=input_data, output_data=output_data):
+ error_msg=f'Expected: {output_data} but got a different value.'
+ self.assertEqual(get_price_of_kwh(*input_data), output_data, msg=error_msg)
From a4d877299b6a57c6f0cf12ef1f364b5460276c8f Mon Sep 17 00:00:00 2001
From: Carl
Date: Wed, 21 Dec 2022 21:21:52 +0100
Subject: [PATCH 271/826] Update
---
.../concept/electric-bill/.docs/instructions.md | 13 ++++++-------
exercises/concept/electric-bill/.meta/exemplar.py | 4 ++--
2 files changed, 8 insertions(+), 9 deletions(-)
diff --git a/exercises/concept/electric-bill/.docs/instructions.md b/exercises/concept/electric-bill/.docs/instructions.md
index c8022915da..ee81c2b23f 100644
--- a/exercises/concept/electric-bill/.docs/instructions.md
+++ b/exercises/concept/electric-bill/.docs/instructions.md
@@ -54,12 +54,11 @@ The function should then `return` the watts as an integer.
4. Get efficiency
Electronics are not 100% efficient.
-Therefore your employer wants to know the efficiency of the electronics.
-To get efficiency you have to divide the power factor by 100.
-The power factor is a float between 0 and 100.
+Therefore, your employer wants you to calculate the efficiency of the electronics.
+To get efficiency you have to divide the power factor (a float between 0 and 100) by 100.
Implement a function `get_efficiency()` that accepts a float that holds the power factor.
-The function should then `return` the power factor as a float.
+The function should then `return` the calculated efficiency as a float.
```python
>>> get_efficiency(80)
@@ -69,11 +68,11 @@ The function should then `return` the power factor as a float.
5. Get cost
Your employer wants to know the cost of running the electronics.
-The power used is the power given divided by the efficiency.
The cost of running the electronics is the power used multiplied by the cost per kWh.
+The power used is the power given divided by the calculated efficiency.
-Implement a function `get_cost()` that accepts an integer that holds the number of watts and a float that has the power factor and a float that holds the cost per kwh.
-The function should then `return` the cost of running the electronics as a float rounded to 2 decimals.
+Implement a function `get_cost()` that accepts an integer that holds the number of watts, a float that has the power factor, and a float that holds the cost per kwh.
+The function should then `return` the cost of running the electronics as a float.
```python
>>> get_cost(5000000, 80, 0.25)
diff --git a/exercises/concept/electric-bill/.meta/exemplar.py b/exercises/concept/electric-bill/.meta/exemplar.py
index 996b9e3afa..7102c5d6c1 100644
--- a/exercises/concept/electric-bill/.meta/exemplar.py
+++ b/exercises/concept/electric-bill/.meta/exemplar.py
@@ -1,13 +1,13 @@
"""Functions which helps company to calculate their power usage"""
-def get_the_amount_of_hours(hours, missing_hours):
+def get_the_amount_of_hours(hours):
"""Return the amount of hours.
:param: hours: int - amount of hours.
:return: int - amount of hours.
"""
- return (hours + missing_hours) % 24
+ return (hours + 3) % 24
def get_kW_value(watts):
From a652bb712c1874381c45af9e4f8eff32c456c20b Mon Sep 17 00:00:00 2001
From: Carl
Date: Thu, 22 Dec 2022 22:13:20 +0100
Subject: [PATCH 272/826] started
---
concepts/numbers/about.md | 136 +++++++++++++++++++-------------------
1 file changed, 68 insertions(+), 68 deletions(-)
diff --git a/concepts/numbers/about.md b/concepts/numbers/about.md
index e746972d1f..34ddd9573c 100644
--- a/concepts/numbers/about.md
+++ b/concepts/numbers/about.md
@@ -15,115 +15,115 @@ Whole numbers (_including hex, octals and binary numbers_) **without** decimal p
-12
```
-Hex numbers are prefixed with `0x`:
+Numbers containing a decimal point (with or without fractional parts) are identified as `floats`:
```python
-# Hex numbers start with 0x.
->>> 0x17
-23
->>> type(0x17)
-
+>>> 3.45
+3.45
+>>> type(3.45)
+
```
-Octals are prefixed with a `0o`:
+## Arithmetic
+
+Python fully supports arithmetic between these different number types, and will convert narrower numbers to match their less narrow counterparts when used with the binary arithmetic operators (`+`, `-`, `*`, `/`, `//`, and `%`).
+
+### Addition and subtraction
+
+Addition and subtraction act like in normal math.
+If one of the operands is a `float`, the other will be converted to a `float` as well.
+Otherwise both operands will be converted to `ints`:
```python
-# Octal numbers start with a 0o.
->>> 0o446
-294
->>> type(0o446)
-
+>>> 5 - 3
+2
+# The int is widened to a float here, and a float is returned.
+>>> 3 + 4.0
+7.0
```
-Binary numbers are prefixed with `0b`, and written with only zeros and ones:
+### Multiplication
+
+As with addition and subtraction, multiplication will convert narrower numbers to match their less narrow counterparts:
```python
-# Binary numbers are made up of 0s and 1s.
->>> 0b1100110
-102
->>> type(0b1100110)
-
+>>> 3 * 2
+6
+
+>>> 3 * 2.0
+6.0
```
-Each of these `int` displays can be converted into the other via constructor:
+### Division
-```python
+Division always returns a `float`, even if the result is a whole number:
->>> starting_number = 1234
+```python
+>>> 6/5
+1.2
->>> hex(starting_number)
-'0x4d2'
+>>> 6/2
+3.0
+```
->>> oct(starting_number)
-'0o2322'
+### Floor division
->>> bin(starting_number)
-'0b10011010010'
+If an `int` result is needed, you can use floor division to truncate the result.
+Floor division is performed using the `//` operator:
->>> hex(0b10011010010)
-'0x4d2'
+```python
+>>> 6//5
+1
->>> int(0x4d2)
-1234
+>>> 6//2
+3
```
-Numbers containing a decimal point (with or without fractional parts) are identified as `floats`:
+### Modulo
+
+The modulo operator (`%`) returns the remainder of the division of the two operands:
```python
->>> 3.45
-3.45
->>> type(3.45)
-
+>>> 5 % 3
+2
```
-Appending `j` or `J` to a number creates an _imaginary number_ -- a `complex` number with a zero real part. `ints` or `floats` can then be added to an imaginary number to create a `complex` number with both real and imaginary parts:
+### Exponentiation
-```python
->>> 3j
-3j
->>> type(3j)
-
+Exponentiation is performed using the `**` operator:
->>> 3.5+4j
-(3.5+4j)
+```python
+>>> 2 ** 3
+8
```
-## Arithmetic
+All numbers (except complex) support all [arithmetic operations][arethmetic-operations], evaluated according to [operator precedence][operator precedence]. Support for mathematical functions (beyond `+`, `-`, `/`) for complex numbers can be found in the [cmath][cmath] module.
-Python fully supports arithmetic between these different number types, and will convert narrower numbers to match their less narrow counterparts when used with the binary arithmetic operators (`+`, `-`, `*`, `/`, `//`, and `%`).
+## Conversions
-Python considers `ints` narrower than `floats`, which are considered narrower than `complex` numbers. Comparisons between different number types behave as if the _exact_ values of those numbers were being compared:
+Numbers can be converted from one type to another using the built-in functions `int()` and `float()`:
```python
-# The int is widened to a float here, and a float is returned.
->>> 3 + 4.0
-7.0
-
-# The int is widened to a complex number, and a complex number is returned.
->>> 6/(3+2j)
-(2+2j)
+>>> int(3.45)
+3
-# Division always returns a float, even if integers are used.
->>> 6/2
+>>> float(3)
3.0
+```
-# If an int result is needed, you can use floor division to truncate the result.
->>> 6//2
-3
+## Round
-# When comparing, exact values are used.
->>> 23 == 0x17
-True
+Python provides a built-in function `round()` to round off a floating point number to a given number of decimal places.
+If no number of decimal places is specified, the number is rounded off to the nearest integer and will return an `int`:
->>> 0b10111 == 0x17
-True
+```python
+>>> round(3.1415926535, 2)
+3.14
->>> 6 == (6+0j)
-True
+>>> round(3.1415926535)
+3
```
-All numbers (except complex) support all [arithmetic operations][arethmetic-operations], evaluated according to [operator precedence][operator precedence]. Support for mathematical functions (beyond `+`, `-`, `/`) for complex numbers can be found in the [cmath][cmath] module.
-
## Precision & Representation
Integers in Python have [arbitrary precision](https://en.wikipedia.org/wiki/Arbitrary-precision_arithmetic) -- the amount of digits is limited only by the available memory of the host system.
From edce70c43929ed46160f5d339ccf7a4d0262c328 Mon Sep 17 00:00:00 2001
From: Carl
Date: Fri, 23 Dec 2022 16:59:18 +0100
Subject: [PATCH 273/826] Add round
---
concepts/numbers/about.md | 33 +++++++++++++++++++++++++++++++--
1 file changed, 31 insertions(+), 2 deletions(-)
diff --git a/concepts/numbers/about.md b/concepts/numbers/about.md
index 34ddd9573c..1ea487843f 100644
--- a/concepts/numbers/about.md
+++ b/concepts/numbers/about.md
@@ -113,7 +113,7 @@ Numbers can be converted from one type to another using the built-in functions `
## Round
-Python provides a built-in function `round()` to round off a floating point number to a given number of decimal places.
+Python provides a built-in function `round(number, )` to round off a floating point number to a given number of decimal places.
If no number of decimal places is specified, the number is rounded off to the nearest integer and will return an `int`:
```python
@@ -124,13 +124,42 @@ If no number of decimal places is specified, the number is rounded off to the ne
3
```
+## Priority and parentheses
+
+Python allows you to use parentheses to group expressions.
+This is useful when you want to override the default order of operations.
+
+```python
+>>> 2 + 3 * 4
+14
+
+>>> (2 + 3) * 4
+20
+```
+
+Python follows the [PEMDAS][operator precedence] rule for operator precedence.
+This means `()` have the highest priority, followed by `**`, then `*`, `/`, `//`, `%`, `+`, and `-`:
+
+```python
+>>> 2 + 3 - 4 * 4
+-11
+
+>>> (2 + 3 - 4) * 4
+20
+
+# In the following example, the `**` operator has the highest priority, then `*`, then `+`
+# Meaning we first do 4 ** 4, then 3 * 64, then 2 + 192
+>>> 2 + 3 * 4 ** 4
+770
+```
+
## Precision & Representation
Integers in Python have [arbitrary precision](https://en.wikipedia.org/wiki/Arbitrary-precision_arithmetic) -- the amount of digits is limited only by the available memory of the host system.
Floating point numbers are usually implemented using a `double` in C (_15 decimal places of precision_), but will vary in representation based on the host system. Complex numbers have a `real` and an `imaginary` part, both of which are represented by floating point numbers.
-For a more detailed discussions of the issues and limitations of floating point arithmetic across programming langages, take a look at [0.30000000000000004.com][0.30000000000000004.com] and [The Python Tutorial][floating point math].
+For a more detailed discussions of the issues and limitations of floating point arithmetic across programming languages, take a look at [0.30000000000000004.com][0.30000000000000004.com] and [The Python Tutorial][floating point math].
[int]: https://docs.python.org/3/library/functions.html#int
[float]: https://docs.python.org/3/library/functions.html#float
From eed14b390110b245c0c24de7e10b049794110ea5 Mon Sep 17 00:00:00 2001
From: Carl
Date: Sat, 24 Dec 2022 23:16:56 +0100
Subject: [PATCH 274/826] Minor changes
---
concepts/numbers/about.md | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)
diff --git a/concepts/numbers/about.md b/concepts/numbers/about.md
index 1ea487843f..2c3863aa58 100644
--- a/concepts/numbers/about.md
+++ b/concepts/numbers/about.md
@@ -31,7 +31,7 @@ Python fully supports arithmetic between these different number types, and will
### Addition and subtraction
Addition and subtraction act like in normal math.
-If one of the operands is a `float`, the other will be converted to a `float` as well.
+If atleast one of the operands is a `float`, the other will be converted to a `float` as well.
Otherwise both operands will be converted to `ints`:
```python
@@ -86,6 +86,9 @@ The modulo operator (`%`) returns the remainder of the division of the two opera
```python
>>> 5 % 3
2
+
+>>> 8 % 2
+0
```
### Exponentiation
@@ -95,13 +98,16 @@ Exponentiation is performed using the `**` operator:
```python
>>> 2 ** 3
8
+
+>>> 4 ** 0.5
+2
```
All numbers (except complex) support all [arithmetic operations][arethmetic-operations], evaluated according to [operator precedence][operator precedence]. Support for mathematical functions (beyond `+`, `-`, `/`) for complex numbers can be found in the [cmath][cmath] module.
## Conversions
-Numbers can be converted from one type to another using the built-in functions `int()` and `float()`:
+Numbers can be converted from `int` to `floats` and `floats` to `int` using the built-in functions `int()` and `float()`:
```python
>>> int(3.45)
@@ -138,7 +144,7 @@ This is useful when you want to override the default order of operations.
```
Python follows the [PEMDAS][operator precedence] rule for operator precedence.
-This means `()` have the highest priority, followed by `**`, then `*`, `/`, `//`, `%`, `+`, and `-`:
+This means calculations within `()` have the highest priority, followed by `**`, then `*`, `/`, `//`, `%`, `+`, and `-`:
```python
>>> 2 + 3 - 4 * 4
From 37ed22a9519e669d51a9174fb3581f986be30f9c Mon Sep 17 00:00:00 2001
From: Carl
Date: Sun, 25 Dec 2022 19:22:27 +0100
Subject: [PATCH 275/826] Fix config
---
config.json | 14 +++++++++++---
1 file changed, 11 insertions(+), 3 deletions(-)
diff --git a/config.json b/config.json
index d1399f2f3f..a96132bd47 100644
--- a/config.json
+++ b/config.json
@@ -43,9 +43,9 @@
"status": "beta"
},
{
- "slug": "currency-exchange",
- "name": "Currency Exchange",
- "uuid": "1335ca33-3af0-4720-bcf2-bfa68ffc6862",
+ "slug": "electric-bill",
+ "name": "Electric Bill",
+ "uuid": "b2fd556b-07a8-47d6-9811-4d847cf0c6da",
"concepts": ["numbers"],
"prerequisites": ["basics"],
"status": "beta"
@@ -2103,6 +2103,14 @@
],
"difficulty": 9
},
+ {
+ "slug": "currency-exchange",
+ "name": "Currency Exchange",
+ "uuid": "1335ca33-3af0-4720-bcf2-bfa68ffc6862",
+ "concepts": [],
+ "prerequisites": [],
+ "status": "deprecated"
+ },
{
"slug": "accumulate",
"name": "Accumulate",
From 4de9c1b89663e80516c0e7f532b03b1bc8e830a7 Mon Sep 17 00:00:00 2001
From: Carl
Date: Sun, 25 Dec 2022 19:25:24 +0100
Subject: [PATCH 276/826] Fixed
---
config.json | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/config.json b/config.json
index a96132bd47..e292ece5b7 100644
--- a/config.json
+++ b/config.json
@@ -50,6 +50,14 @@
"prerequisites": ["basics"],
"status": "beta"
},
+ {
+ "slug": "currency-exchange",
+ "name": "Currency Exchange",
+ "uuid": "1335ca33-3af0-4720-bcf2-bfa68ffc6862",
+ "concepts": [],
+ "prerequisites": [],
+ "status": "wip"
+ },
{
"slug": "meltdown-mitigation",
"name": "Meltdown Mitigation",
@@ -2103,14 +2111,6 @@
],
"difficulty": 9
},
- {
- "slug": "currency-exchange",
- "name": "Currency Exchange",
- "uuid": "1335ca33-3af0-4720-bcf2-bfa68ffc6862",
- "concepts": [],
- "prerequisites": [],
- "status": "deprecated"
- },
{
"slug": "accumulate",
"name": "Accumulate",
From 8cf33d44f5f2b35f69bf8580a71212702ad269ac Mon Sep 17 00:00:00 2001
From: Carl
Date: Sun, 25 Dec 2022 19:27:24 +0100
Subject: [PATCH 277/826] Added introduction
---
exercises/concept/electric-bill/.docs/hint.md | 1 +
.../electric-bill/.docs/introduction.md | 179 ++++++++++++++++++
2 files changed, 180 insertions(+)
create mode 100644 exercises/concept/electric-bill/.docs/hint.md
create mode 100644 exercises/concept/electric-bill/.docs/introduction.md
diff --git a/exercises/concept/electric-bill/.docs/hint.md b/exercises/concept/electric-bill/.docs/hint.md
new file mode 100644
index 0000000000..1f600ada68
--- /dev/null
+++ b/exercises/concept/electric-bill/.docs/hint.md
@@ -0,0 +1 @@
+# Todo
diff --git a/exercises/concept/electric-bill/.docs/introduction.md b/exercises/concept/electric-bill/.docs/introduction.md
new file mode 100644
index 0000000000..2c3863aa58
--- /dev/null
+++ b/exercises/concept/electric-bill/.docs/introduction.md
@@ -0,0 +1,179 @@
+# About
+
+Python has three different types of built-in numbers: integers ([`int`][int]), floating-point ([`float`][float]), and complex ([`complex`][complex]). Fractions ([`fractions.Fraction`][fractions]) and Decimals ([`decimal.Decimal`][decimals]) are also available via import from the standard library.
+
+Whole numbers (_including hex, octals and binary numbers_) **without** decimal places are identified as `ints`:
+
+```python
+# Ints are whole numbers.
+>>> 1234
+1234
+>>> type(1234)
+
+
+>>> -12
+-12
+```
+
+Numbers containing a decimal point (with or without fractional parts) are identified as `floats`:
+
+```python
+>>> 3.45
+3.45
+>>> type(3.45)
+
+```
+
+## Arithmetic
+
+Python fully supports arithmetic between these different number types, and will convert narrower numbers to match their less narrow counterparts when used with the binary arithmetic operators (`+`, `-`, `*`, `/`, `//`, and `%`).
+
+### Addition and subtraction
+
+Addition and subtraction act like in normal math.
+If atleast one of the operands is a `float`, the other will be converted to a `float` as well.
+Otherwise both operands will be converted to `ints`:
+
+```python
+>>> 5 - 3
+2
+# The int is widened to a float here, and a float is returned.
+>>> 3 + 4.0
+7.0
+```
+
+### Multiplication
+
+As with addition and subtraction, multiplication will convert narrower numbers to match their less narrow counterparts:
+
+```python
+>>> 3 * 2
+6
+
+>>> 3 * 2.0
+6.0
+```
+
+### Division
+
+Division always returns a `float`, even if the result is a whole number:
+
+```python
+>>> 6/5
+1.2
+
+>>> 6/2
+3.0
+```
+
+### Floor division
+
+If an `int` result is needed, you can use floor division to truncate the result.
+Floor division is performed using the `//` operator:
+
+```python
+>>> 6//5
+1
+
+>>> 6//2
+3
+```
+
+### Modulo
+
+The modulo operator (`%`) returns the remainder of the division of the two operands:
+
+```python
+>>> 5 % 3
+2
+
+>>> 8 % 2
+0
+```
+
+### Exponentiation
+
+Exponentiation is performed using the `**` operator:
+
+```python
+>>> 2 ** 3
+8
+
+>>> 4 ** 0.5
+2
+```
+
+All numbers (except complex) support all [arithmetic operations][arethmetic-operations], evaluated according to [operator precedence][operator precedence]. Support for mathematical functions (beyond `+`, `-`, `/`) for complex numbers can be found in the [cmath][cmath] module.
+
+## Conversions
+
+Numbers can be converted from `int` to `floats` and `floats` to `int` using the built-in functions `int()` and `float()`:
+
+```python
+>>> int(3.45)
+3
+
+>>> float(3)
+3.0
+```
+
+## Round
+
+Python provides a built-in function `round(number, )` to round off a floating point number to a given number of decimal places.
+If no number of decimal places is specified, the number is rounded off to the nearest integer and will return an `int`:
+
+```python
+>>> round(3.1415926535, 2)
+3.14
+
+>>> round(3.1415926535)
+3
+```
+
+## Priority and parentheses
+
+Python allows you to use parentheses to group expressions.
+This is useful when you want to override the default order of operations.
+
+```python
+>>> 2 + 3 * 4
+14
+
+>>> (2 + 3) * 4
+20
+```
+
+Python follows the [PEMDAS][operator precedence] rule for operator precedence.
+This means calculations within `()` have the highest priority, followed by `**`, then `*`, `/`, `//`, `%`, `+`, and `-`:
+
+```python
+>>> 2 + 3 - 4 * 4
+-11
+
+>>> (2 + 3 - 4) * 4
+20
+
+# In the following example, the `**` operator has the highest priority, then `*`, then `+`
+# Meaning we first do 4 ** 4, then 3 * 64, then 2 + 192
+>>> 2 + 3 * 4 ** 4
+770
+```
+
+## Precision & Representation
+
+Integers in Python have [arbitrary precision](https://en.wikipedia.org/wiki/Arbitrary-precision_arithmetic) -- the amount of digits is limited only by the available memory of the host system.
+
+Floating point numbers are usually implemented using a `double` in C (_15 decimal places of precision_), but will vary in representation based on the host system. Complex numbers have a `real` and an `imaginary` part, both of which are represented by floating point numbers.
+
+For a more detailed discussions of the issues and limitations of floating point arithmetic across programming languages, take a look at [0.30000000000000004.com][0.30000000000000004.com] and [The Python Tutorial][floating point math].
+
+[int]: https://docs.python.org/3/library/functions.html#int
+[float]: https://docs.python.org/3/library/functions.html#float
+[complex]: https://docs.python.org/3/library/functions.html#complex
+[fractions]: https://docs.python.org/3/library/fractions.html
+[decimals]: https://docs.python.org/3/library/decimal.html#module-decimal
+[0.30000000000000004.com]: https://0.30000000000000004.com/
+[cmath]: https://docs.python.org/3.9/library/cmath.html
+[arethmetic-operations]: https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex
+[operator precedence]: https://docs.python.org/3/reference/expressions.html#operator-precedence
+[floating point math]: https://docs.python.org/3.9/tutorial/floatingpoint.html
From 2172e42befaccd2f72b1c6aa4264b5c03acf1c4f Mon Sep 17 00:00:00 2001
From: Carl
Date: Sun, 25 Dec 2022 19:29:15 +0100
Subject: [PATCH 278/826] Fix
---
config.json | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/config.json b/config.json
index e292ece5b7..c7e5b9e080 100644
--- a/config.json
+++ b/config.json
@@ -54,8 +54,8 @@
"slug": "currency-exchange",
"name": "Currency Exchange",
"uuid": "1335ca33-3af0-4720-bcf2-bfa68ffc6862",
- "concepts": [],
- "prerequisites": [],
+ "concepts": ["numbers"],
+ "prerequisites": ["basics"],
"status": "wip"
},
{
From 68ec0e47665325ea3a39c83075c94d9ef8036782 Mon Sep 17 00:00:00 2001
From: Carl
Date: Sun, 25 Dec 2022 19:30:56 +0100
Subject: [PATCH 279/826] fix
---
exercises/concept/electric-bill/.docs/{hint.md => hints.md} | 0
1 file changed, 0 insertions(+), 0 deletions(-)
rename exercises/concept/electric-bill/.docs/{hint.md => hints.md} (100%)
diff --git a/exercises/concept/electric-bill/.docs/hint.md b/exercises/concept/electric-bill/.docs/hints.md
similarity index 100%
rename from exercises/concept/electric-bill/.docs/hint.md
rename to exercises/concept/electric-bill/.docs/hints.md
From 33fc34c77fbc61de4092035fecf1911d59e8bdff Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Sun, 25 Dec 2022 12:10:44 -0800
Subject: [PATCH 280/826] Small Edits for Modulo
---
concepts/numbers/about.md | 62 ++++++++++++++++++++++++++++-----------
1 file changed, 45 insertions(+), 17 deletions(-)
diff --git a/concepts/numbers/about.md b/concepts/numbers/about.md
index 2c3863aa58..05f214825b 100644
--- a/concepts/numbers/about.md
+++ b/concepts/numbers/about.md
@@ -1,8 +1,9 @@
# About
-Python has three different types of built-in numbers: integers ([`int`][int]), floating-point ([`float`][float]), and complex ([`complex`][complex]). Fractions ([`fractions.Fraction`][fractions]) and Decimals ([`decimal.Decimal`][decimals]) are also available via import from the standard library.
+Python has three different types of built-in numbers: integers ([`int`][int]), floating-point ([`float`][float]), and complex ([`complex`][complex]).
+Fractions ([`fractions.Fraction`][fractions]) and Decimals ([`decimal.Decimal`][decimals]) are also available via import from the standard library.
-Whole numbers (_including hex, octals and binary numbers_) **without** decimal places are identified as `ints`:
+Whole numbers including hexadecimal ([_`hex()`_][hex]), octal ([_`oct()`_][oct]) and binary ([_`bin()`_][bin]) numbers **without** decimal places are also identified as `ints`:
```python
# Ints are whole numbers.
@@ -28,11 +29,11 @@ Numbers containing a decimal point (with or without fractional parts) are identi
Python fully supports arithmetic between these different number types, and will convert narrower numbers to match their less narrow counterparts when used with the binary arithmetic operators (`+`, `-`, `*`, `/`, `//`, and `%`).
+
### Addition and subtraction
-Addition and subtraction act like in normal math.
-If atleast one of the operands is a `float`, the other will be converted to a `float` as well.
-Otherwise both operands will be converted to `ints`:
+Addition and subtraction operators behave as they do in normal math.
+If one or more of the operands is a `float`, the remaining `int`s will be converted to `float`s as well:
```python
>>> 5 - 3
@@ -84,11 +85,29 @@ Floor division is performed using the `//` operator:
The modulo operator (`%`) returns the remainder of the division of the two operands:
```python
+# The result of % is zero here, because dividing 8 by 2 leaves no remainder
+>>> 8 % 2
+0
+
+
>>> 5 % 3
2
+```
+
+Which is equivalent to:
+
+
+```python
+>>> whole_part = int(5/3)
+1
+
+>>> decimal_part = 5/3 - whole_part
+0.6666666666666667
+
+>>> whole_remainder = decimal_part * 3
+2.0
+```
->>> 8 % 2
-0
```
### Exponentiation
@@ -103,12 +122,15 @@ Exponentiation is performed using the `**` operator:
2
```
-All numbers (except complex) support all [arithmetic operations][arethmetic-operations], evaluated according to [operator precedence][operator precedence]. Support for mathematical functions (beyond `+`, `-`, `/`) for complex numbers can be found in the [cmath][cmath] module.
+All numbers (except complex) support all [arithmetic operations][arithmetic-operations], evaluated according to [operator precedence][operator precedence].
+Support for mathematical functions (beyond `+` and `-`) for complex numbers can be found in the [cmath][cmath] module.
+
## Conversions
Numbers can be converted from `int` to `floats` and `floats` to `int` using the built-in functions `int()` and `float()`:
+
```python
>>> int(3.45)
3
@@ -117,9 +139,10 @@ Numbers can be converted from `int` to `floats` and `floats` to `int` using the
3.0
```
+
## Round
-Python provides a built-in function `round(number, )` to round off a floating point number to a given number of decimal places.
+Python provides a built-in function [`round(number, )`][round] to round off a floating point number to a given number of decimal places.
If no number of decimal places is specified, the number is rounded off to the nearest integer and will return an `int`:
```python
@@ -163,17 +186,22 @@ This means calculations within `()` have the highest priority, followed by `**`,
Integers in Python have [arbitrary precision](https://en.wikipedia.org/wiki/Arbitrary-precision_arithmetic) -- the amount of digits is limited only by the available memory of the host system.
-Floating point numbers are usually implemented using a `double` in C (_15 decimal places of precision_), but will vary in representation based on the host system. Complex numbers have a `real` and an `imaginary` part, both of which are represented by floating point numbers.
+Floating point numbers are usually implemented using a `double` in C (_15 decimal places of precision_), but will vary in representation based on the host system.
+Complex numbers have a `real` and an `imaginary` part, both of which are represented by floating point numbers.
For a more detailed discussions of the issues and limitations of floating point arithmetic across programming languages, take a look at [0.30000000000000004.com][0.30000000000000004.com] and [The Python Tutorial][floating point math].
-[int]: https://docs.python.org/3/library/functions.html#int
-[float]: https://docs.python.org/3/library/functions.html#float
-[complex]: https://docs.python.org/3/library/functions.html#complex
-[fractions]: https://docs.python.org/3/library/fractions.html
-[decimals]: https://docs.python.org/3/library/decimal.html#module-decimal
[0.30000000000000004.com]: https://0.30000000000000004.com/
-[cmath]: https://docs.python.org/3.9/library/cmath.html
[arethmetic-operations]: https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex
-[operator precedence]: https://docs.python.org/3/reference/expressions.html#operator-precedence
+[bin]: https://docs.python.org/3/library/functions.html#bin
+[cmath]: https://docs.python.org/3.9/library/cmath.html
+[complex]: https://docs.python.org/3/library/functions.html#complex
+[decimals]: https://docs.python.org/3/library/decimal.html#module-decimal
+[float]: https://docs.python.org/3/library/functions.html#float
[floating point math]: https://docs.python.org/3.9/tutorial/floatingpoint.html
+[fractions]: https://docs.python.org/3/library/fractions.html
+[hex]: https://docs.python.org/3/library/functions.html#hex
+[int]: https://docs.python.org/3/library/functions.html#int
+[oct]: https://docs.python.org/3/library/functions.html#oct
+[operator precedence]: https://docs.python.org/3/reference/expressions.html#operator-precedence
+[round]: https://docs.python.org/3/library/functions.html#round
From 7b7ec005e52b16b33c32d06e7feaa56a40c96e84 Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Sun, 25 Dec 2022 12:22:38 -0800
Subject: [PATCH 281/826] More Modulo
---
concepts/numbers/about.md | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/concepts/numbers/about.md b/concepts/numbers/about.md
index 05f214825b..66eb14801d 100644
--- a/concepts/numbers/about.md
+++ b/concepts/numbers/about.md
@@ -89,12 +89,12 @@ The modulo operator (`%`) returns the remainder of the division of the two opera
>>> 8 % 2
0
-
+# The result of % is 2 here, because 3 only goes into 5 once, with 2 left over
>>> 5 % 3
2
```
-Which is equivalent to:
+Another way to look at 5 % 3:
```python
@@ -108,7 +108,6 @@ Which is equivalent to:
2.0
```
-```
### Exponentiation
From c8e5a7572fe57ed3d94585b136afc04d42ca5df0 Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Sun, 25 Dec 2022 12:40:15 -0800
Subject: [PATCH 282/826] added links
---
concepts/numbers/about.md | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/concepts/numbers/about.md b/concepts/numbers/about.md
index 66eb14801d..659d63cb8c 100644
--- a/concepts/numbers/about.md
+++ b/concepts/numbers/about.md
@@ -165,7 +165,7 @@ This is useful when you want to override the default order of operations.
20
```
-Python follows the [PEMDAS][operator precedence] rule for operator precedence.
+Python follows the [PEMDAS][pemdas] rule for operator precedence.
This means calculations within `()` have the highest priority, followed by `**`, then `*`, `/`, `//`, `%`, `+`, and `-`:
```python
@@ -191,7 +191,8 @@ Complex numbers have a `real` and an `imaginary` part, both of which are represe
For a more detailed discussions of the issues and limitations of floating point arithmetic across programming languages, take a look at [0.30000000000000004.com][0.30000000000000004.com] and [The Python Tutorial][floating point math].
[0.30000000000000004.com]: https://0.30000000000000004.com/
-[arethmetic-operations]: https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex
+[pemdas]: https://mathworld.wolfram.com/PEMDAS.html
+[arithmetic-operations]: https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex
[bin]: https://docs.python.org/3/library/functions.html#bin
[cmath]: https://docs.python.org/3.9/library/cmath.html
[complex]: https://docs.python.org/3/library/functions.html#complex
@@ -203,4 +204,4 @@ For a more detailed discussions of the issues and limitations of floating point
[int]: https://docs.python.org/3/library/functions.html#int
[oct]: https://docs.python.org/3/library/functions.html#oct
[operator precedence]: https://docs.python.org/3/reference/expressions.html#operator-precedence
-[round]: https://docs.python.org/3/library/functions.html#round
+[round]: https://docs.python.org/3/library/functions.html#round
\ No newline at end of file
From 36ad472fea996dd14995622d039c9bbfa5cd2920 Mon Sep 17 00:00:00 2001
From: Carl
Date: Sun, 25 Dec 2022 21:41:36 +0100
Subject: [PATCH 283/826] Fix
---
concepts/numbers/.meta/config.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/concepts/numbers/.meta/config.json b/concepts/numbers/.meta/config.json
index 583a8284a8..25c2db7981 100644
--- a/concepts/numbers/.meta/config.json
+++ b/concepts/numbers/.meta/config.json
@@ -1,5 +1,5 @@
{
"blurb": "There are three different types of built-in numbers: integers (\"int\"), floating-point (\"float\"), and complex (\"complex\"). Ints have arbitrary precision and floats typically have 15 decimal places of precision -- but both Int and float precision vary by host system. Complex numbers have a real and an imaginary part - each represented by floats.",
"authors": ["Ticktakto", "Yabby1997", "limm-jk", "OMEGA-Y", "wnstj2007"],
- "contributors": ["BethanyG", "KaiAragaki"]
+ "contributors": ["BethanyG", "KaiAragaki", "meatball133"]
}
From 13ee826acd1f7815577d2af7a38b8defb677d3f5 Mon Sep 17 00:00:00 2001
From: Carl
Date: Sun, 25 Dec 2022 21:48:22 +0100
Subject: [PATCH 284/826] Removed finished code
---
exercises/concept/electric-bill/electric_bill.py | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/exercises/concept/electric-bill/electric_bill.py b/exercises/concept/electric-bill/electric_bill.py
index e7e90c59c6..0c2938d2dd 100644
--- a/exercises/concept/electric-bill/electric_bill.py
+++ b/exercises/concept/electric-bill/electric_bill.py
@@ -7,7 +7,7 @@ def get_the_amount_of_hours(hours):
:param: hours: int - amount of hours.
:return: int - amount of hours.
"""
- return (hours + 3) % 24
+ pass
def get_kW_value(watts):
@@ -16,7 +16,7 @@ def get_kW_value(watts):
:param: watts: int - watt value.
:return: float - kW value.
"""
- return round(watts / 1000, 1) # rounds here
+ pass
def get_kwh_value(watts):
@@ -25,7 +25,7 @@ def get_kwh_value(watts):
:param: watts: int - watt value.
:param: hours: int - kilowatt hour value.
"""
- return int(get_kW_value(watts) // 3600)
+ pass
def get_efficiency(efficiency):
@@ -34,7 +34,7 @@ def get_efficiency(efficiency):
:param: efficiency: float - efficiency.
:return: float - efficiency.
"""
- return efficiency / 100
+ pass
def get_price_of_kwh(watts, efficiency, price):
@@ -45,4 +45,4 @@ def get_price_of_kwh(watts, efficiency, price):
:param: price: float - price of kWh.
:return: float - price of kWh.
"""
- return price * (get_kwh_value(watts) / get_efficiency(efficiency))
+ pass
From f03898b0d2235134f63deccf0ff19fb3aaf78277 Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Sun, 25 Dec 2022 12:54:54 -0800
Subject: [PATCH 285/826] Introduction files Edits
Edited concept and exercise introduction.md files.
---
concepts/numbers/introduction.md | 97 +------------------
.../electric-bill/.docs/introduction.md | 71 +++++++-------
2 files changed, 38 insertions(+), 130 deletions(-)
diff --git a/concepts/numbers/introduction.md b/concepts/numbers/introduction.md
index c72139289a..aad55a0e61 100644
--- a/concepts/numbers/introduction.md
+++ b/concepts/numbers/introduction.md
@@ -1,97 +1,8 @@
# Introduction
-## Numbers
+Python has three different types of built-in numbers: integers ([`int`][int]), floating-point ([`float`][float]), and complex ([`complex`][complex]).
+Fractions ([`fractions.Fraction`][fractions]) and Decimals ([`decimal.Decimal`][decimals]) are also available via import from the standard library.
-There are three different kinds of built-in numbers in Python : `ints`, `floats`, and `complex`. However, in this exercise you'll be dealing only with `ints` and `floats`.
+Whole numbers including hexadecimal ([_`hex()`_][hex]), octal ([_`oct()`_][oct]) and binary ([_`bin()`_][bin]) numbers **without** decimal places are also identified as `ints`.
-### ints
-
-`ints` are whole numbers. e.g. `1234`, `-10`, `20201278`.
-
-Integers in Python have [arbitrary precision][arbitrary-precision] -- the amount of digits is limited only by the available memory of the host system.
-
-### floats
-
-`floats` or `floating point numbers` contain a decimal point. e.g. `0.0`,`3.14`,`-9.01`.
-
-Floating point numbers are usually implemented in Python using a `double` in C (_15 decimal places of precision_), but will vary in representation based on the host system and other implementation details. This can create some surprises when working with floats, but is "good enough" for most situations.
-
-You can see more details and discussions in the following resources:
-
-- [Python numeric type documentation][numeric-type-docs]
-- [The Python Tutorial][floating point math]
-- [Documentation for `int()` built in][`int()` built in]
-- [Documentation for `float()` built in][`float()` built in]
-- [0.30000000000000004.com][0.30000000000000004.com]
-
-### Precision
-
-Before diving into arithmetic, it is worth thinking about what precision means. Precision is the level of exactness at which a number can be represented. An `int` is less precise than a `float` in the same way that `1` is less precise than `1.125`.
-
-## Arithmetic
-
-Python fully supports arithmetic between `ints` and `floats`. It will convert narrower numbers to match their wider (or more precise) counterparts when used with the binary arithmetic operators (`+`, `-`, `*`, `/`, `//`, and `%`). When division with `/`, `//` returns the quotient and `%` returns the remainder.
-
-Python considers `ints` narrower than `floats`. So, using a float in an expression ensures the result will be a float too. However, when doing division, the result will always be a float, even if only integers are used.
-
-```python
-# The int is widened to a float here, and a float type is returned.
->>> 3 + 4.0
-7.0
->>> 3 * 4.0
-12.0
->>> 3 - 2.0
-1.0
-# Division always returns a float.
->>> 6 / 2
-3.0
->>> 7 / 4
-1.75
-# Calculating remainders.
->>> 7 % 4
-3
->>> 2 % 4
-2
->>> 12.75 % 3
-0.75
-```
-
-If an int result is needed, you can use `//` to truncate the result.
-
-```python
->>> 6 // 2
-3
->>> 7 // 4
-1
-```
-
-To convert a float to an integer, you can use `int()`. Also, to convert an integer to a float, you can use `float()`.
-
-```python
->>> int(6 / 2)
-3
->>> float(1 + 2)
-3.0
-```
-
-## Underscores in Numeric Literals
-
-As of version 3.6, Python supports the use of underscores in numerical literals to improve readability:
-```python
-# A float with underscores
->>> dollars = 35_000_000.0
->>> print(dollars)
-35000000.0
-```
-
-The rules for underscores are outline in [pep 515][pep 515] under 'Literal Grammar' are quite dense, but essentially boil down to:
-* Underscores can only be between two digits (not at beginning or ends of numbers, or next to signs (+/-) or decimals points)
-* No consecutive underscores
-
-[arbitrary-precision]: https://en.wikipedia.org/wiki/Arbitrary-precision_arithmetic#:~:text=In%20computer%20science%2C%20arbitrary%2Dprecision,memory%20of%20the%20host%20system.
-[numeric-type-docs]: https://docs.python.org/3/library/stdtypes.html#typesnumeric
-[`int()` built in]: https://docs.python.org/3/library/functions.html#int
-[`float()` built in]: https://docs.python.org/3/library/functions.html#float
-[0.30000000000000004.com]: https://0.30000000000000004.com/
-[floating point math]: https://docs.python.org/3.9/tutorial/floatingpoint.html
-[pep 515]: https://www.python.org/dev/peps/pep-0515/
+Python fully supports arithmetic between these different number types, and will convert narrower numbers to match their less narrow counterparts when used with the binary arithmetic operators (`+`, `-`, `*`, `/`, `//`, and `%`).
diff --git a/exercises/concept/electric-bill/.docs/introduction.md b/exercises/concept/electric-bill/.docs/introduction.md
index 2c3863aa58..c3187bbe7f 100644
--- a/exercises/concept/electric-bill/.docs/introduction.md
+++ b/exercises/concept/electric-bill/.docs/introduction.md
@@ -1,8 +1,9 @@
-# About
+# Introduction
-Python has three different types of built-in numbers: integers ([`int`][int]), floating-point ([`float`][float]), and complex ([`complex`][complex]). Fractions ([`fractions.Fraction`][fractions]) and Decimals ([`decimal.Decimal`][decimals]) are also available via import from the standard library.
+Python has three different types of built-in numbers: integers ([`int`][int]), floating-point ([`float`][float]), and complex ([`complex`][complex]).
+Fractions ([`fractions.Fraction`][fractions]) and Decimals ([`decimal.Decimal`][decimals]) are also available via import from the standard library.
-Whole numbers (_including hex, octals and binary numbers_) **without** decimal places are identified as `ints`:
+Whole numbers including hexadecimal ([_`hex()`_][hex]), octal ([_`oct()`_][oct]) and binary ([_`bin()`_][bin]) numbers **without** decimal places are also identified as `ints`:
```python
# Ints are whole numbers.
@@ -28,11 +29,11 @@ Numbers containing a decimal point (with or without fractional parts) are identi
Python fully supports arithmetic between these different number types, and will convert narrower numbers to match their less narrow counterparts when used with the binary arithmetic operators (`+`, `-`, `*`, `/`, `//`, and `%`).
+
### Addition and subtraction
-Addition and subtraction act like in normal math.
-If atleast one of the operands is a `float`, the other will be converted to a `float` as well.
-Otherwise both operands will be converted to `ints`:
+Addition and subtraction operators behave as they do in normal math.
+If one or more of the operands is a `float`, the remaining `int`s will be converted to `float`s as well:
```python
>>> 5 - 3
@@ -84,42 +85,32 @@ Floor division is performed using the `//` operator:
The modulo operator (`%`) returns the remainder of the division of the two operands:
```python
->>> 5 % 3
-2
-
+# The result of % is zero here, because dividing 8 by 2 leaves no remainder
>>> 8 % 2
0
-```
-
-### Exponentiation
-
-Exponentiation is performed using the `**` operator:
-```python
->>> 2 ** 3
-8
-
->>> 4 ** 0.5
+# The result of % is 2 here, because 3 only goes into 5 once, with 2 left over
+>>> 5 % 3
2
```
-All numbers (except complex) support all [arithmetic operations][arethmetic-operations], evaluated according to [operator precedence][operator precedence]. Support for mathematical functions (beyond `+`, `-`, `/`) for complex numbers can be found in the [cmath][cmath] module.
-
-## Conversions
+Another way to look at 5 % 3:
-Numbers can be converted from `int` to `floats` and `floats` to `int` using the built-in functions `int()` and `float()`:
```python
->>> int(3.45)
-3
+>>> whole_part = int(5/3)
+1
->>> float(3)
-3.0
+>>> decimal_part = 5/3 - whole_part
+0.6666666666666667
+
+>>> whole_remainder = decimal_part * 3
+2.0
```
## Round
-Python provides a built-in function `round(number, )` to round off a floating point number to a given number of decimal places.
+Python provides a built-in function [`round(number, )`][round] to round off a floating point number to a given number of decimal places.
If no number of decimal places is specified, the number is rounded off to the nearest integer and will return an `int`:
```python
@@ -143,7 +134,7 @@ This is useful when you want to override the default order of operations.
20
```
-Python follows the [PEMDAS][operator precedence] rule for operator precedence.
+Python follows the [PEMDAS][pemdas] rule for operator precedence.
This means calculations within `()` have the highest priority, followed by `**`, then `*`, `/`, `//`, `%`, `+`, and `-`:
```python
@@ -163,17 +154,23 @@ This means calculations within `()` have the highest priority, followed by `**`,
Integers in Python have [arbitrary precision](https://en.wikipedia.org/wiki/Arbitrary-precision_arithmetic) -- the amount of digits is limited only by the available memory of the host system.
-Floating point numbers are usually implemented using a `double` in C (_15 decimal places of precision_), but will vary in representation based on the host system. Complex numbers have a `real` and an `imaginary` part, both of which are represented by floating point numbers.
+Floating point numbers are usually implemented using a `double` in C (_15 decimal places of precision_), but will vary in representation based on the host system.
+Complex numbers have a `real` and an `imaginary` part, both of which are represented by floating point numbers.
For a more detailed discussions of the issues and limitations of floating point arithmetic across programming languages, take a look at [0.30000000000000004.com][0.30000000000000004.com] and [The Python Tutorial][floating point math].
-[int]: https://docs.python.org/3/library/functions.html#int
-[float]: https://docs.python.org/3/library/functions.html#float
-[complex]: https://docs.python.org/3/library/functions.html#complex
-[fractions]: https://docs.python.org/3/library/fractions.html
-[decimals]: https://docs.python.org/3/library/decimal.html#module-decimal
[0.30000000000000004.com]: https://0.30000000000000004.com/
+[pemdas]: https://mathworld.wolfram.com/PEMDAS.html
+[arithmetic-operations]: https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex
+[bin]: https://docs.python.org/3/library/functions.html#bin
[cmath]: https://docs.python.org/3.9/library/cmath.html
-[arethmetic-operations]: https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex
-[operator precedence]: https://docs.python.org/3/reference/expressions.html#operator-precedence
+[complex]: https://docs.python.org/3/library/functions.html#complex
+[decimals]: https://docs.python.org/3/library/decimal.html#module-decimal
+[float]: https://docs.python.org/3/library/functions.html#float
[floating point math]: https://docs.python.org/3.9/tutorial/floatingpoint.html
+[fractions]: https://docs.python.org/3/library/fractions.html
+[hex]: https://docs.python.org/3/library/functions.html#hex
+[int]: https://docs.python.org/3/library/functions.html#int
+[oct]: https://docs.python.org/3/library/functions.html#oct
+[operator precedence]: https://docs.python.org/3/reference/expressions.html#operator-precedence
+[round]: https://docs.python.org/3/library/functions.html#round
From f0195b3175ede1d98ad08930d65f79b9fa4bf90b Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Sun, 25 Dec 2022 13:05:24 -0800
Subject: [PATCH 286/826] Link Adjustmets
---
concepts/numbers/about.md | 7 ++++---
concepts/numbers/introduction.md | 10 ++++++++++
exercises/concept/electric-bill/.docs/introduction.md | 8 +++-----
3 files changed, 17 insertions(+), 8 deletions(-)
diff --git a/concepts/numbers/about.md b/concepts/numbers/about.md
index 659d63cb8c..874e600edc 100644
--- a/concepts/numbers/about.md
+++ b/concepts/numbers/about.md
@@ -183,7 +183,7 @@ This means calculations within `()` have the highest priority, followed by `**`,
## Precision & Representation
-Integers in Python have [arbitrary precision](https://en.wikipedia.org/wiki/Arbitrary-precision_arithmetic) -- the amount of digits is limited only by the available memory of the host system.
+Integers in Python have [arbitrary precision][arbitrary-precision] -- the amount of digits is limited only by the available memory of the host system.
Floating point numbers are usually implemented using a `double` in C (_15 decimal places of precision_), but will vary in representation based on the host system.
Complex numbers have a `real` and an `imaginary` part, both of which are represented by floating point numbers.
@@ -191,7 +191,7 @@ Complex numbers have a `real` and an `imaginary` part, both of which are represe
For a more detailed discussions of the issues and limitations of floating point arithmetic across programming languages, take a look at [0.30000000000000004.com][0.30000000000000004.com] and [The Python Tutorial][floating point math].
[0.30000000000000004.com]: https://0.30000000000000004.com/
-[pemdas]: https://mathworld.wolfram.com/PEMDAS.html
+[arbitrary-precision]: https://en.wikipedia.org/wiki/Arbitrary-precision_arithmetic
[arithmetic-operations]: https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex
[bin]: https://docs.python.org/3/library/functions.html#bin
[cmath]: https://docs.python.org/3.9/library/cmath.html
@@ -204,4 +204,5 @@ For a more detailed discussions of the issues and limitations of floating point
[int]: https://docs.python.org/3/library/functions.html#int
[oct]: https://docs.python.org/3/library/functions.html#oct
[operator precedence]: https://docs.python.org/3/reference/expressions.html#operator-precedence
-[round]: https://docs.python.org/3/library/functions.html#round
\ No newline at end of file
+[pemdas]: https://mathworld.wolfram.com/PEMDAS.html
+[round]: https://docs.python.org/3/library/functions.html#round
diff --git a/concepts/numbers/introduction.md b/concepts/numbers/introduction.md
index aad55a0e61..3491bc20a3 100644
--- a/concepts/numbers/introduction.md
+++ b/concepts/numbers/introduction.md
@@ -6,3 +6,13 @@ Fractions ([`fractions.Fraction`][fractions]) and Decimals ([`decimal.Decimal`][
Whole numbers including hexadecimal ([_`hex()`_][hex]), octal ([_`oct()`_][oct]) and binary ([_`bin()`_][bin]) numbers **without** decimal places are also identified as `ints`.
Python fully supports arithmetic between these different number types, and will convert narrower numbers to match their less narrow counterparts when used with the binary arithmetic operators (`+`, `-`, `*`, `/`, `//`, and `%`).
+
+
+[bin]: https://docs.python.org/3/library/functions.html#bin
+[complex]: https://docs.python.org/3/library/functions.html#complex
+[decimals]: https://docs.python.org/3/library/decimal.html#module-decimal
+[float]: https://docs.python.org/3/library/functions.html#float
+[fractions]: https://docs.python.org/3/library/fractions.html
+[hex]: https://docs.python.org/3/library/functions.html#hex
+[int]: https://docs.python.org/3/library/functions.html#int
+[oct]: https://docs.python.org/3/library/functions.html#oct
diff --git a/exercises/concept/electric-bill/.docs/introduction.md b/exercises/concept/electric-bill/.docs/introduction.md
index c3187bbe7f..5b6f2d19da 100644
--- a/exercises/concept/electric-bill/.docs/introduction.md
+++ b/exercises/concept/electric-bill/.docs/introduction.md
@@ -149,10 +149,11 @@ This means calculations within `()` have the highest priority, followed by `**`,
>>> 2 + 3 * 4 ** 4
770
```
+[arbitrary-precision]: https://en.wikipedia.org/wiki/Arbitrary-precision_arithmetic
## Precision & Representation
-Integers in Python have [arbitrary precision](https://en.wikipedia.org/wiki/Arbitrary-precision_arithmetic) -- the amount of digits is limited only by the available memory of the host system.
+Integers in Python have [arbitrary precision][arbitrary-precision] -- the amount of digits is limited only by the available memory of the host system.
Floating point numbers are usually implemented using a `double` in C (_15 decimal places of precision_), but will vary in representation based on the host system.
Complex numbers have a `real` and an `imaginary` part, both of which are represented by floating point numbers.
@@ -160,10 +161,7 @@ Complex numbers have a `real` and an `imaginary` part, both of which are represe
For a more detailed discussions of the issues and limitations of floating point arithmetic across programming languages, take a look at [0.30000000000000004.com][0.30000000000000004.com] and [The Python Tutorial][floating point math].
[0.30000000000000004.com]: https://0.30000000000000004.com/
-[pemdas]: https://mathworld.wolfram.com/PEMDAS.html
-[arithmetic-operations]: https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex
[bin]: https://docs.python.org/3/library/functions.html#bin
-[cmath]: https://docs.python.org/3.9/library/cmath.html
[complex]: https://docs.python.org/3/library/functions.html#complex
[decimals]: https://docs.python.org/3/library/decimal.html#module-decimal
[float]: https://docs.python.org/3/library/functions.html#float
@@ -172,5 +170,5 @@ For a more detailed discussions of the issues and limitations of floating point
[hex]: https://docs.python.org/3/library/functions.html#hex
[int]: https://docs.python.org/3/library/functions.html#int
[oct]: https://docs.python.org/3/library/functions.html#oct
-[operator precedence]: https://docs.python.org/3/reference/expressions.html#operator-precedence
+[pemdas]: https://mathworld.wolfram.com/PEMDAS.html
[round]: https://docs.python.org/3/library/functions.html#round
From e1d6ff256ff862d83d615542b9f81acfdf8dc58d Mon Sep 17 00:00:00 2001
From: Carl
Date: Sun, 25 Dec 2022 22:07:49 +0100
Subject: [PATCH 287/826] Smal changes
---
concepts/numbers/about.md | 12 +++---------
1 file changed, 3 insertions(+), 9 deletions(-)
diff --git a/concepts/numbers/about.md b/concepts/numbers/about.md
index 874e600edc..1f894b0aa8 100644
--- a/concepts/numbers/about.md
+++ b/concepts/numbers/about.md
@@ -29,6 +29,8 @@ Numbers containing a decimal point (with or without fractional parts) are identi
Python fully supports arithmetic between these different number types, and will convert narrower numbers to match their less narrow counterparts when used with the binary arithmetic operators (`+`, `-`, `*`, `/`, `//`, and `%`).
+All numbers (except complex) support all [arithmetic operations][arithmetic-operations], evaluated according to [operator precedence][operator precedence].
+Support for mathematical functions (beyond `+` and `-`) for complex numbers can be found in the [cmath][cmath] module.
### Addition and subtraction
@@ -96,7 +98,6 @@ The modulo operator (`%`) returns the remainder of the division of the two opera
Another way to look at 5 % 3:
-
```python
>>> whole_part = int(5/3)
1
@@ -108,8 +109,7 @@ Another way to look at 5 % 3:
2.0
```
-
-### Exponentiation
+## Exponentiation
Exponentiation is performed using the `**` operator:
@@ -121,15 +121,10 @@ Exponentiation is performed using the `**` operator:
2
```
-All numbers (except complex) support all [arithmetic operations][arithmetic-operations], evaluated according to [operator precedence][operator precedence].
-Support for mathematical functions (beyond `+` and `-`) for complex numbers can be found in the [cmath][cmath] module.
-
-
## Conversions
Numbers can be converted from `int` to `floats` and `floats` to `int` using the built-in functions `int()` and `float()`:
-
```python
>>> int(3.45)
3
@@ -138,7 +133,6 @@ Numbers can be converted from `int` to `floats` and `floats` to `int` using the
3.0
```
-
## Round
Python provides a built-in function [`round(number, )`][round] to round off a floating point number to a given number of decimal places.
From 065ef79030d76a5d9f5e2ee0fc5b29f0a9eb89be Mon Sep 17 00:00:00 2001
From: Carl
Date: Sun, 25 Dec 2022 22:13:30 +0100
Subject: [PATCH 288/826] Fixed capital
---
concepts/numbers/.meta/config.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/concepts/numbers/.meta/config.json b/concepts/numbers/.meta/config.json
index 25c2db7981..7898f6099a 100644
--- a/concepts/numbers/.meta/config.json
+++ b/concepts/numbers/.meta/config.json
@@ -1,5 +1,5 @@
{
- "blurb": "There are three different types of built-in numbers: integers (\"int\"), floating-point (\"float\"), and complex (\"complex\"). Ints have arbitrary precision and floats typically have 15 decimal places of precision -- but both Int and float precision vary by host system. Complex numbers have a real and an imaginary part - each represented by floats.",
+ "blurb": "There are three different types of built-in numbers: integers (\"int\"), floating-point (\"float\"), and complex (\"complex\"). Ints have arbitrary precision and floats typically have 15 decimal places of precision -- but both int and float precision vary by host system. Complex numbers have a real and an imaginary part - each represented by floats.",
"authors": ["Ticktakto", "Yabby1997", "limm-jk", "OMEGA-Y", "wnstj2007"],
"contributors": ["BethanyG", "KaiAragaki", "meatball133"]
}
From 036ad99247757d2024f7c3ac1e30919f5053c0bf Mon Sep 17 00:00:00 2001
From: Carl
Date: Sun, 25 Dec 2022 22:24:29 +0100
Subject: [PATCH 289/826] Fixes
---
exercises/concept/electric-bill/.docs/introduction.md | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/exercises/concept/electric-bill/.docs/introduction.md b/exercises/concept/electric-bill/.docs/introduction.md
index 5b6f2d19da..1543afe5d5 100644
--- a/exercises/concept/electric-bill/.docs/introduction.md
+++ b/exercises/concept/electric-bill/.docs/introduction.md
@@ -29,7 +29,6 @@ Numbers containing a decimal point (with or without fractional parts) are identi
Python fully supports arithmetic between these different number types, and will convert narrower numbers to match their less narrow counterparts when used with the binary arithmetic operators (`+`, `-`, `*`, `/`, `//`, and `%`).
-
### Addition and subtraction
Addition and subtraction operators behave as they do in normal math.
@@ -96,7 +95,6 @@ The modulo operator (`%`) returns the remainder of the division of the two opera
Another way to look at 5 % 3:
-
```python
>>> whole_part = int(5/3)
1
@@ -149,7 +147,6 @@ This means calculations within `()` have the highest priority, followed by `**`,
>>> 2 + 3 * 4 ** 4
770
```
-[arbitrary-precision]: https://en.wikipedia.org/wiki/Arbitrary-precision_arithmetic
## Precision & Representation
@@ -161,6 +158,7 @@ Complex numbers have a `real` and an `imaginary` part, both of which are represe
For a more detailed discussions of the issues and limitations of floating point arithmetic across programming languages, take a look at [0.30000000000000004.com][0.30000000000000004.com] and [The Python Tutorial][floating point math].
[0.30000000000000004.com]: https://0.30000000000000004.com/
+[arbitrary-precision]: https://en.wikipedia.org/wiki/Arbitrary-precision_arithmetic
[bin]: https://docs.python.org/3/library/functions.html#bin
[complex]: https://docs.python.org/3/library/functions.html#complex
[decimals]: https://docs.python.org/3/library/decimal.html#module-decimal
From a170b492a9d4bcba4c1fd78e6e5124512bbade95 Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Sun, 25 Dec 2022 14:13:35 -0800
Subject: [PATCH 290/826] Hints File and Edits
---
.../concept/electric-bill/.docs/hints.md | 28 ++++++++++++-
.../electric-bill/.docs/instructions.md | 42 +++++++++----------
.../concept/electric-bill/.meta/config.json | 2 +-
.../concept/electric-bill/.meta/exemplar.py | 29 +++++++------
.../concept/electric-bill/electric_bill.py | 18 ++++----
.../electric-bill/electric_bill_test.py | 12 +++---
6 files changed, 80 insertions(+), 51 deletions(-)
diff --git a/exercises/concept/electric-bill/.docs/hints.md b/exercises/concept/electric-bill/.docs/hints.md
index 1f600ada68..77aaa50d9c 100644
--- a/exercises/concept/electric-bill/.docs/hints.md
+++ b/exercises/concept/electric-bill/.docs/hints.md
@@ -1 +1,27 @@
-# Todo
+# General
+
+Remember that you can always reuse/call previously completed functions when writing new ones.
+
+
+## 1. Get extra hours
+- This is all about calculating the _remainder_ left after whole division.
+- Take a look at [`divmod()`][divmod], and look for an operator that does something similar.
+
+## 2. Get kW value
+- Remember to give [`round()`][round] a number of _decimal places_, or you will get a whole number back as a result.
+
+## 3. Get kwh value
+- The result of dividing an `int` by a `float` is always a `float`.
+- To get only an integer value from division, use [_floor_ division][floor], which will truncate the decimal.
+
+## 4. Get efficiency
+- The result of dividing an `int` by a `float` is always a `float`.
+
+## 5. Get cost
+- It might be good to _reuse_ or call other functions you have already completed here.
+- The result of dividing an `int` by a `float` is always a `float`.
+
+
+[divmod]: https://docs.python.org/3/library/functions.html#divmod
+[floor]: https://docs.python.org/3/glossary.html#term-floor-division
+[round]: https://docs.python.org/3/library/functions.html#round
diff --git a/exercises/concept/electric-bill/.docs/instructions.md b/exercises/concept/electric-bill/.docs/instructions.md
index ee81c2b23f..9c96ee924f 100644
--- a/exercises/concept/electric-bill/.docs/instructions.md
+++ b/exercises/concept/electric-bill/.docs/instructions.md
@@ -1,17 +1,18 @@
# Instructions
-The company you work for want to reduce their carbon footprint, so they wants you to write a program which calculates the power usage of their electronics and the cost of running them.
+The company you work for wants to reduce their carbon footprint, so they want you to write a program to calculate the power usage and cost of running their electronics.
1. Get extra hours
-Your employer wants a program that calculates the time it takes to run different electronics.
-Currently the time is stored in hours.
-When your employer added the hours they noticed that the time was not correct and they want you to add 3 extra hours to the time data.
-They also would like to know how many hours needs to be removed to get the data in full days(24 hours).
-The time given doesn't have to be in full days.
+Your employer has a program that calculates the time it takes to run different electronics.
+Currently, the time is stored in hours.
+When your employer added the hours, they noticed that the time duration was not correct.
+They want you to add 3 extra hours to the time data.
+They would also like to know how many "extra" hours there are after converting the data to "full" days (a day is 24 hours).
+The time to convert may not be in full days.
Implement a function `get_extra_hours()` that accepts an integer which holds the number of hours.
-The function should then `return` an integer with how many hours which has to be removed to get the time in full days.
+The function should make the appropriate "extra hours" adjustment, and then `return` an integer representing how many hours needs to be removed from the total to get the time in "full" days.
```python
>>> get_extra_hours(25)
@@ -20,14 +21,13 @@ The function should then `return` an integer with how many hours which has to be
2. Get kW value
-Your employer wants to know the power usage of the different electronics.
-They want to know the power usage in kW.
-kW stands for kilowatt, there watts is a unit of power.
+Your employer wants to know the power usage of the different electronics in kW.
+kW stands for kilowatt, where watts are a unit of power.
Kilo in the unit name is a prefix in the metric system meaning 1000.
-So 1 kilowatt is equal to 1000 watts.
+One kilowatt == 1000 watts.
Implement a function `get_kW_value()` that accepts an integer which holds the number of watts.
-The function should then `return` the watts as kilowatts rounded to 1 decimal.
+The function should then `return` the watts as kilowatts rounded to 1 decimal place.
```python
>>> get_kW_value(1150)
@@ -36,12 +36,12 @@ The function should then `return` the watts as kilowatts rounded to 1 decimal.
3. Get kwh value
-To be able to calculate the cost of running the electronics your employer wants to know the power usage in kWh.
-kWh stands for kilowatt-hour, there hour is a unit of time.
-So 1 kilowatt-hour is equal to 1000 watts used for 1 hour.
-An hour is made of 60 minutes and a minute is made of 60 seconds.
-So 1 hour is equal to 3600 seconds.
-To get the kWh value you have to have to convert the watts to kW and then floor-divide it by 3600.
+To be able to calculate the cost of running the electronics, your employer needs to know the power usage in kWh.
+kWh stands for kilowatt-hour, where hour is a unit of time.
+One kilowatt-hour == 1000 watts used for 1 hour.
+An hour is made up of 60 minutes and a minute is made up of 60 seconds.
+One hour is equal to 3600 seconds.
+To calculate the kWh value, you must convert watts to kW, and then floor-divide the result by 3600.
Implement a function `get_kWh_value()` that accepts an integer which holds the number of watts.
The function should then `return` the watts as an integer.
@@ -54,8 +54,8 @@ The function should then `return` the watts as an integer.
4. Get efficiency
Electronics are not 100% efficient.
-Therefore, your employer wants you to calculate the efficiency of the electronics.
-To get efficiency you have to divide the power factor (a float between 0 and 100) by 100.
+Therefore, your employer wants you to calculate the _efficiency_ of the electronics.
+To get efficiency, you must divide the power factor (_a float between 0 and 100_) by 100.
Implement a function `get_efficiency()` that accepts a float that holds the power factor.
The function should then `return` the calculated efficiency as a float.
@@ -71,7 +71,7 @@ Your employer wants to know the cost of running the electronics.
The cost of running the electronics is the power used multiplied by the cost per kWh.
The power used is the power given divided by the calculated efficiency.
-Implement a function `get_cost()` that accepts an integer that holds the number of watts, a float that has the power factor, and a float that holds the cost per kwh.
+Implement a function `get_cost(,,)` that accepts an integer that holds the number of watts, a float that has the power factor, and a float that holds the cost per kwh.
The function should then `return` the cost of running the electronics as a float.
```python
diff --git a/exercises/concept/electric-bill/.meta/config.json b/exercises/concept/electric-bill/.meta/config.json
index d1560b2033..eca6e91c08 100644
--- a/exercises/concept/electric-bill/.meta/config.json
+++ b/exercises/concept/electric-bill/.meta/config.json
@@ -15,5 +15,5 @@
]
},
"icon": "city-office",
- "blurb": "to do"
+ "blurb": "Learn about numbers in Python while saving your employers money on their electric bill."
}
diff --git a/exercises/concept/electric-bill/.meta/exemplar.py b/exercises/concept/electric-bill/.meta/exemplar.py
index 7102c5d6c1..230f892519 100644
--- a/exercises/concept/electric-bill/.meta/exemplar.py
+++ b/exercises/concept/electric-bill/.meta/exemplar.py
@@ -1,12 +1,13 @@
-"""Functions which helps company to calculate their power usage"""
+"""Functions to help the company calculate their power usage."""
-def get_the_amount_of_hours(hours):
+def get_extra_hours(hours):
"""Return the amount of hours.
:param: hours: int - amount of hours.
- :return: int - amount of hours.
+ :return: int - amount of "extra" hours.
"""
+
return (hours + 3) % 24
@@ -16,7 +17,9 @@ def get_kW_value(watts):
:param: watts: int - watt value.
:return: float - kW value.
"""
- return round(watts / 1000, 1) # rounds here
+
+ # rounds to one decimal place here
+ return round(watts / 1000, 1)
def get_kwh_value(watts):
@@ -28,21 +31,21 @@ def get_kwh_value(watts):
return get_kW_value(watts) // 3600
-def get_efficiency(efficiency):
- """Return the efficiency as a power factor.
+def get_efficiency(power_factor):
+ """Return the efficiency calculated from the power factor.
- :param: efficiency: float - efficiency.
+ :param: power_factor: float.
:return: float - efficiency.
"""
- return efficiency / 100
+ return power_factor / 100
-def get_price_of_kwh(watts, efficiency, price):
- """Return the price of a given kWh value, efficiency and price.
+def get_cost(watts, power_factor, price):
+ """Calculate the cost of a given kWh value, efficiency and price.
:param: watts: int - watt value.
- :param: efficiency: float - efficiency.
+ :param: power_factor: float - efficiency.
:param: price: float - price of kWh.
- :return: float - price of kWh.
+ :return: float - cost of kWh.
"""
- return price * (get_kwh_value(watts) / get_efficiency(efficiency))
+ return price * (get_kwh_value(watts) / get_efficiency(power_factor))
diff --git a/exercises/concept/electric-bill/electric_bill.py b/exercises/concept/electric-bill/electric_bill.py
index 0c2938d2dd..5a3ba53973 100644
--- a/exercises/concept/electric-bill/electric_bill.py
+++ b/exercises/concept/electric-bill/electric_bill.py
@@ -1,11 +1,11 @@
"""Functions which helps company to calculate their power usage"""
-def get_the_amount_of_hours(hours):
+def get_extra_hours(hours):
"""Return the amount of hours.
:param: hours: int - amount of hours.
- :return: int - amount of hours.
+ :return: int - amount of "extra" hours.
"""
pass
@@ -28,21 +28,21 @@ def get_kwh_value(watts):
pass
-def get_efficiency(efficiency):
- """Return the efficiency as a power factor.
+def get_efficiency(power_factor):
+ """Return the efficiency calculated from the power factor.
- :param: efficiency: float - efficiency.
+ :param: power_factor: float.
:return: float - efficiency.
"""
pass
-def get_price_of_kwh(watts, efficiency, price):
- """Return the price of a given kWh value, efficiency and price.
+def get_cost(watts, power_factor, price):
+ """Calculate the cost of a given kWh value, efficiency and price.
:param: watts: int - watt value.
- :param: efficiency: float - efficiency.
+ :param: power_factor: float - efficiency.
:param: price: float - price of kWh.
- :return: float - price of kWh.
+ :return: float - cost of kWh.
"""
pass
diff --git a/exercises/concept/electric-bill/electric_bill_test.py b/exercises/concept/electric-bill/electric_bill_test.py
index 95745e74a8..fdcfe32064 100644
--- a/exercises/concept/electric-bill/electric_bill_test.py
+++ b/exercises/concept/electric-bill/electric_bill_test.py
@@ -1,23 +1,23 @@
import unittest
import pytest
-from electric_bill import (get_the_amount_of_hours,
+from electric_bill import (get_extra_hours,
get_kW_value,
get_kwh_value,
get_efficiency,
- get_price_of_kwh)
+ get_cost)
class LocomotiveEngineerTest(unittest.TestCase):
@pytest.mark.task(taskno=1)
- def test_get_the_amount_of_hours(self):
+ def test_get_extra_hours(self):
input_data = [25, 10, 5, 2, 1, 120, 21]
output_data = [4, 13, 8, 5, 4, 3, 0]
for variant, (input_data, output_data) in enumerate(zip(input_data, output_data), start=1):
with self.subTest(f'variation #{variant}', input_data=input_data, output_data=output_data):
error_msg=f'Expected: {output_data} but got a different value.'
- self.assertEqual(get_the_amount_of_hours(input_data), output_data, msg=error_msg)
+ self.assertEqual(get_extra_hours(input_data), output_data, msg=error_msg)
@pytest.mark.task(taskno=2)
def test_get_kW_value(self):
@@ -48,11 +48,11 @@ def test_get_efficiency(self):
self.assertAlmostEqual(get_efficiency(input_data), output_data, msg=error_msg)
@pytest.mark.task(taskno=5)
- def test_get_price_of_kwh(self):
+ def test_get_cost(self):
input_data = ((5000000, 80.0, 0.25), (2141241, 99.99, 2), (43252135, 0.8, 4), (4321512, 40.0, 2))
output_data = (0.3125, 0, 6000, 5)
for variant, (input_data, output_data) in enumerate(zip(input_data, output_data), start=1):
with self.subTest(f'variation #{variant}', input_data=input_data, output_data=output_data):
error_msg=f'Expected: {output_data} but got a different value.'
- self.assertEqual(get_price_of_kwh(*input_data), output_data, msg=error_msg)
+ self.assertEqual(get_cost(*input_data), output_data, msg=error_msg)
From aa638625f3a7c349c8f5da0cfbd12e448618a4a3 Mon Sep 17 00:00:00 2001
From: meatball <69751659+meatball133@users.noreply.github.com>
Date: Sun, 25 Dec 2022 23:27:05 +0100
Subject: [PATCH 291/826] Fixes
---
exercises/concept/electric-bill/.docs/hints.md | 17 ++++++++++-------
.../concept/electric-bill/.docs/instructions.md | 6 +++---
.../concept/electric-bill/.docs/introduction.md | 2 +-
.../concept/electric-bill/electric_bill.py | 2 +-
.../concept/electric-bill/electric_bill_test.py | 4 +++-
5 files changed, 18 insertions(+), 13 deletions(-)
diff --git a/exercises/concept/electric-bill/.docs/hints.md b/exercises/concept/electric-bill/.docs/hints.md
index 77aaa50d9c..34a6eb7aa4 100644
--- a/exercises/concept/electric-bill/.docs/hints.md
+++ b/exercises/concept/electric-bill/.docs/hints.md
@@ -2,26 +2,29 @@
Remember that you can always reuse/call previously completed functions when writing new ones.
-
## 1. Get extra hours
+
- This is all about calculating the _remainder_ left after whole division.
- Take a look at [`divmod()`][divmod], and look for an operator that does something similar.
-## 2. Get kW value
+## 2. Get kW value
+
- Remember to give [`round()`][round] a number of _decimal places_, or you will get a whole number back as a result.
-## 3. Get kwh value
+## 3. Get kwh value
+
- The result of dividing an `int` by a `float` is always a `float`.
- To get only an integer value from division, use [_floor_ division][floor], which will truncate the decimal.
## 4. Get efficiency
+
- The result of dividing an `int` by a `float` is always a `float`.
-## 5. Get cost
+## 5. Get cost
+
- It might be good to _reuse_ or call other functions you have already completed here.
- The result of dividing an `int` by a `float` is always a `float`.
-
[divmod]: https://docs.python.org/3/library/functions.html#divmod
-[floor]: https://docs.python.org/3/glossary.html#term-floor-division
-[round]: https://docs.python.org/3/library/functions.html#round
+[floor]: https://docs.python.org/3/glossary.html#term-floor-division
+[round]: https://docs.python.org/3/library/functions.html#round
diff --git a/exercises/concept/electric-bill/.docs/instructions.md b/exercises/concept/electric-bill/.docs/instructions.md
index 9c96ee924f..59aed2919b 100644
--- a/exercises/concept/electric-bill/.docs/instructions.md
+++ b/exercises/concept/electric-bill/.docs/instructions.md
@@ -34,7 +34,7 @@ The function should then `return` the watts as kilowatts rounded to 1 decimal pl
1.2
```
-3. Get kwh value
+3. Get kWh value
To be able to calculate the cost of running the electronics, your employer needs to know the power usage in kWh.
kWh stands for kilowatt-hour, where hour is a unit of time.
@@ -44,7 +44,7 @@ One hour is equal to 3600 seconds.
To calculate the kWh value, you must convert watts to kW, and then floor-divide the result by 3600.
Implement a function `get_kWh_value()` that accepts an integer which holds the number of watts.
-The function should then `return` the watts as an integer.
+The function should then `return` the kilowatt-hours as an integer.
```python
>>> get_kWh_value(5000000)
@@ -71,7 +71,7 @@ Your employer wants to know the cost of running the electronics.
The cost of running the electronics is the power used multiplied by the cost per kWh.
The power used is the power given divided by the calculated efficiency.
-Implement a function `get_cost(,,)` that accepts an integer that holds the number of watts, a float that has the power factor, and a float that holds the cost per kwh.
+Implement a function `get_cost(,,)` that accepts an integer that holds the number of watts, a float that has the power factor, and a float that holds the cost per kWh.
The function should then `return` the cost of running the electronics as a float.
```python
diff --git a/exercises/concept/electric-bill/.docs/introduction.md b/exercises/concept/electric-bill/.docs/introduction.md
index 1543afe5d5..719aea0529 100644
--- a/exercises/concept/electric-bill/.docs/introduction.md
+++ b/exercises/concept/electric-bill/.docs/introduction.md
@@ -88,7 +88,7 @@ The modulo operator (`%`) returns the remainder of the division of the two opera
>>> 8 % 2
0
-# The result of % is 2 here, because 3 only goes into 5 once, with 2 left over
+# The result of % is 2 here, because 3 only goes into 5 once, with 2 leftover
>>> 5 % 3
2
```
diff --git a/exercises/concept/electric-bill/electric_bill.py b/exercises/concept/electric-bill/electric_bill.py
index 5a3ba53973..2a2dc715fd 100644
--- a/exercises/concept/electric-bill/electric_bill.py
+++ b/exercises/concept/electric-bill/electric_bill.py
@@ -1,4 +1,4 @@
-"""Functions which helps company to calculate their power usage"""
+"""Functions to help the company calculate their power usage."""
def get_extra_hours(hours):
diff --git a/exercises/concept/electric-bill/electric_bill_test.py b/exercises/concept/electric-bill/electric_bill_test.py
index fdcfe32064..d5963c988f 100644
--- a/exercises/concept/electric-bill/electric_bill_test.py
+++ b/exercises/concept/electric-bill/electric_bill_test.py
@@ -23,6 +23,7 @@ def test_get_extra_hours(self):
def test_get_kW_value(self):
input_data = [1000, 2200, 2900, 900, 1160]
output_data = [1, 2.2, 2.9, 0.9, 1.2]
+
for variant, (input_data, output_data) in enumerate(zip(input_data, output_data), start=1):
with self.subTest(f'variation #{variant}', input_data=input_data, output_data=output_data):
error_msg=f'Expected: {output_data} but got a different value.'
@@ -32,6 +33,7 @@ def test_get_kW_value(self):
def test_get_kwh_value(self):
input_data = (5000000, 2141241, 43252135, 5324623462, 4321512)
output_data = [1, 0, 12, 1479, 1]
+
for variant, (input_data, output_data) in enumerate(zip(input_data, output_data), start=1):
with self.subTest(f'variation #{variant}', input_data=input_data, output_data=output_data):
error_msg=f'Expected: {output_data} but got a different value.'
@@ -51,7 +53,7 @@ def test_get_efficiency(self):
def test_get_cost(self):
input_data = ((5000000, 80.0, 0.25), (2141241, 99.99, 2), (43252135, 0.8, 4), (4321512, 40.0, 2))
output_data = (0.3125, 0, 6000, 5)
-
+
for variant, (input_data, output_data) in enumerate(zip(input_data, output_data), start=1):
with self.subTest(f'variation #{variant}', input_data=input_data, output_data=output_data):
error_msg=f'Expected: {output_data} but got a different value.'
From 6008ca8da6e8a5d821241319813d5adac9f26dd9 Mon Sep 17 00:00:00 2001
From: meatball <69751659+meatball133@users.noreply.github.com>
Date: Mon, 26 Dec 2022 00:10:05 +0100
Subject: [PATCH 292/826] Fixes
---
.../concept/electric-bill/.docs/instructions.md | 10 +++++-----
exercises/concept/electric-bill/.meta/design.md | 12 ++++++++++++
2 files changed, 17 insertions(+), 5 deletions(-)
create mode 100644 exercises/concept/electric-bill/.meta/design.md
diff --git a/exercises/concept/electric-bill/.docs/instructions.md b/exercises/concept/electric-bill/.docs/instructions.md
index 59aed2919b..03fa156074 100644
--- a/exercises/concept/electric-bill/.docs/instructions.md
+++ b/exercises/concept/electric-bill/.docs/instructions.md
@@ -2,7 +2,7 @@
The company you work for wants to reduce their carbon footprint, so they want you to write a program to calculate the power usage and cost of running their electronics.
-1. Get extra hours
+## 1. Get extra hours
Your employer has a program that calculates the time it takes to run different electronics.
Currently, the time is stored in hours.
@@ -19,7 +19,7 @@ The function should make the appropriate "extra hours" adjustment, and then `ret
4
```
-2. Get kW value
+## 2. Get kW value
Your employer wants to know the power usage of the different electronics in kW.
kW stands for kilowatt, where watts are a unit of power.
@@ -34,7 +34,7 @@ The function should then `return` the watts as kilowatts rounded to 1 decimal pl
1.2
```
-3. Get kWh value
+## 3. Get kWh value
To be able to calculate the cost of running the electronics, your employer needs to know the power usage in kWh.
kWh stands for kilowatt-hour, where hour is a unit of time.
@@ -51,7 +51,7 @@ The function should then `return` the kilowatt-hours as an integer.
1
```
-4. Get efficiency
+## 4. Get efficiency
Electronics are not 100% efficient.
Therefore, your employer wants you to calculate the _efficiency_ of the electronics.
@@ -65,7 +65,7 @@ The function should then `return` the calculated efficiency as a float.
0.8
```
-5. Get cost
+## 5. Get cost
Your employer wants to know the cost of running the electronics.
The cost of running the electronics is the power used multiplied by the cost per kWh.
diff --git a/exercises/concept/electric-bill/.meta/design.md b/exercises/concept/electric-bill/.meta/design.md
new file mode 100644
index 0000000000..bc4c298dac
--- /dev/null
+++ b/exercises/concept/electric-bill/.meta/design.md
@@ -0,0 +1,12 @@
+# Design
+
+## Goal
+
+The goal of this exercise is to teach the student how to use arithmetic operators and type casting between `int` and `float` in Python
+
+## Learning objectives
+
+- use `+`, `-`, `*`, `/` to add, subtract, multiply, divide numbers(`int` and `float`).
+- use `round()` to round values.
+- use `//` to floor divide
+- use `%` to calculate remainders.
From 644f66b3c0ffb13a62c190ccb313fe1ce4230e21 Mon Sep 17 00:00:00 2001
From: Carl
Date: Mon, 26 Dec 2022 01:17:23 +0100
Subject: [PATCH 293/826] depricate exercise
---
config.json | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/config.json b/config.json
index c7e5b9e080..575558b764 100644
--- a/config.json
+++ b/config.json
@@ -54,9 +54,9 @@
"slug": "currency-exchange",
"name": "Currency Exchange",
"uuid": "1335ca33-3af0-4720-bcf2-bfa68ffc6862",
- "concepts": ["numbers"],
- "prerequisites": ["basics"],
- "status": "wip"
+ "concepts": [],
+ "prerequisites": [],
+ "status": "deprecated"
},
{
"slug": "meltdown-mitigation",
From 2fe2e6ffc5b0aaf3b242ab4f82ec81d146d359b5 Mon Sep 17 00:00:00 2001
From: Jason Hollis
Date: Sun, 25 Dec 2022 22:53:25 -0500
Subject: [PATCH 294/826] Fixed example with wrong answer
---
exercises/concept/electric-bill/.docs/introduction.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/exercises/concept/electric-bill/.docs/introduction.md b/exercises/concept/electric-bill/.docs/introduction.md
index 719aea0529..02f56f06c2 100644
--- a/exercises/concept/electric-bill/.docs/introduction.md
+++ b/exercises/concept/electric-bill/.docs/introduction.md
@@ -140,7 +140,7 @@ This means calculations within `()` have the highest priority, followed by `**`,
-11
>>> (2 + 3 - 4) * 4
-20
+4
# In the following example, the `**` operator has the highest priority, then `*`, then `+`
# Meaning we first do 4 ** 4, then 3 * 64, then 2 + 192
From 145b291ed49fdc4b1197462665e17c35a1e39af5 Mon Sep 17 00:00:00 2001
From: Carl
Date: Mon, 26 Dec 2022 11:13:19 +0100
Subject: [PATCH 295/826] Fix stuff
---
exercises/concept/electric-bill/.meta/exemplar.py | 2 +-
exercises/concept/electric-bill/electric_bill.py | 2 +-
exercises/concept/electric-bill/electric_bill_test.py | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/exercises/concept/electric-bill/.meta/exemplar.py b/exercises/concept/electric-bill/.meta/exemplar.py
index 230f892519..789ffcf395 100644
--- a/exercises/concept/electric-bill/.meta/exemplar.py
+++ b/exercises/concept/electric-bill/.meta/exemplar.py
@@ -26,7 +26,7 @@ def get_kwh_value(watts):
"""Return the kWh value of a given watt value and hours.
:param: watts: int - watt value.
- :param: hours: int - kilowatt hour value.
+ :return: int - kilowatt hour value.
"""
return get_kW_value(watts) // 3600
diff --git a/exercises/concept/electric-bill/electric_bill.py b/exercises/concept/electric-bill/electric_bill.py
index 2a2dc715fd..cae6af33a2 100644
--- a/exercises/concept/electric-bill/electric_bill.py
+++ b/exercises/concept/electric-bill/electric_bill.py
@@ -23,7 +23,7 @@ def get_kwh_value(watts):
"""Return the kWh value of a given watt value and hours.
:param: watts: int - watt value.
- :param: hours: int - kilowatt hour value.
+ :return: int - kilowatt hour value.
"""
pass
diff --git a/exercises/concept/electric-bill/electric_bill_test.py b/exercises/concept/electric-bill/electric_bill_test.py
index d5963c988f..c6c1611fec 100644
--- a/exercises/concept/electric-bill/electric_bill_test.py
+++ b/exercises/concept/electric-bill/electric_bill_test.py
@@ -7,7 +7,7 @@
get_cost)
-class LocomotiveEngineerTest(unittest.TestCase):
+class ElecticBillTest(unittest.TestCase):
@pytest.mark.task(taskno=1)
def test_get_extra_hours(self):
From 9d9cc93b637126365d61fcec8c61cc983e124806 Mon Sep 17 00:00:00 2001
From: Carl
Date: Mon, 26 Dec 2022 11:16:36 +0100
Subject: [PATCH 296/826] Changed to amount
---
.../concept/electric-bill/.meta/config.json | 1 +
.../concept/electric-bill/.meta/exemplar.py | 16 ++++++++--------
exercises/concept/electric-bill/electric_bill.py | 16 ++++++++--------
.../concept/electric-bill/electric_bill_test.py | 12 ++++++------
4 files changed, 23 insertions(+), 22 deletions(-)
diff --git a/exercises/concept/electric-bill/.meta/config.json b/exercises/concept/electric-bill/.meta/config.json
index eca6e91c08..752ed40c1a 100644
--- a/exercises/concept/electric-bill/.meta/config.json
+++ b/exercises/concept/electric-bill/.meta/config.json
@@ -3,6 +3,7 @@
"meatball133",
"BethanyG"
],
+ "contributors": ["MatthijsBlom"],
"files": {
"solution": [
"electric_bill.py"
diff --git a/exercises/concept/electric-bill/.meta/exemplar.py b/exercises/concept/electric-bill/.meta/exemplar.py
index 789ffcf395..1b60339011 100644
--- a/exercises/concept/electric-bill/.meta/exemplar.py
+++ b/exercises/concept/electric-bill/.meta/exemplar.py
@@ -11,22 +11,22 @@ def get_extra_hours(hours):
return (hours + 3) % 24
-def get_kW_value(watts):
- """Return the kW value of a given watt value.
+def get_kW_amount(watts):
+ """Return the kW amount of a given watt amount.
- :param: watts: int - watt value.
- :return: float - kW value.
+ :param: watts: int - watt amount.
+ :return: float - kW amount.
"""
# rounds to one decimal place here
return round(watts / 1000, 1)
-def get_kwh_value(watts):
- """Return the kWh value of a given watt value and hours.
+def get_kwh_amount(watts):
+ """Return the kWh amount of a given watt amount and hours.
- :param: watts: int - watt value.
- :return: int - kilowatt hour value.
+ :param: watts: int - watt amount.
+ :return: int - kilowatt hour amount.
"""
return get_kW_value(watts) // 3600
diff --git a/exercises/concept/electric-bill/electric_bill.py b/exercises/concept/electric-bill/electric_bill.py
index cae6af33a2..52a92865a0 100644
--- a/exercises/concept/electric-bill/electric_bill.py
+++ b/exercises/concept/electric-bill/electric_bill.py
@@ -10,20 +10,20 @@ def get_extra_hours(hours):
pass
-def get_kW_value(watts):
- """Return the kW value of a given watt value.
+def get_kW_amount(watts):
+ """Return the kW amount of a given watt amount.
- :param: watts: int - watt value.
- :return: float - kW value.
+ :param: watts: int - watt amount.
+ :return: float - kW amount.
"""
pass
-def get_kwh_value(watts):
- """Return the kWh value of a given watt value and hours.
+def get_kwh_amount(watts):
+ """Return the kWh amount of a given watt amount and hours.
- :param: watts: int - watt value.
- :return: int - kilowatt hour value.
+ :param: watts: int - watt amount.
+ :return: int - kilowatt hour amount.
"""
pass
diff --git a/exercises/concept/electric-bill/electric_bill_test.py b/exercises/concept/electric-bill/electric_bill_test.py
index c6c1611fec..dea3b66c15 100644
--- a/exercises/concept/electric-bill/electric_bill_test.py
+++ b/exercises/concept/electric-bill/electric_bill_test.py
@@ -1,8 +1,8 @@
import unittest
import pytest
from electric_bill import (get_extra_hours,
- get_kW_value,
- get_kwh_value,
+ get_kW_amount,
+ get_kwh_amount,
get_efficiency,
get_cost)
@@ -20,24 +20,24 @@ def test_get_extra_hours(self):
self.assertEqual(get_extra_hours(input_data), output_data, msg=error_msg)
@pytest.mark.task(taskno=2)
- def test_get_kW_value(self):
+ def test_get_kW_amount(self):
input_data = [1000, 2200, 2900, 900, 1160]
output_data = [1, 2.2, 2.9, 0.9, 1.2]
for variant, (input_data, output_data) in enumerate(zip(input_data, output_data), start=1):
with self.subTest(f'variation #{variant}', input_data=input_data, output_data=output_data):
error_msg=f'Expected: {output_data} but got a different value.'
- self.assertEqual(get_kW_value(input_data), output_data, msg=error_msg)
+ self.assertEqual(get_kW_amount(input_data), output_data, msg=error_msg)
@pytest.mark.task(taskno=3)
- def test_get_kwh_value(self):
+ def test_get_kwh_amount(self):
input_data = (5000000, 2141241, 43252135, 5324623462, 4321512)
output_data = [1, 0, 12, 1479, 1]
for variant, (input_data, output_data) in enumerate(zip(input_data, output_data), start=1):
with self.subTest(f'variation #{variant}', input_data=input_data, output_data=output_data):
error_msg=f'Expected: {output_data} but got a different value.'
- self.assertEqual(get_kwh_value(input_data), output_data, msg=error_msg)
+ self.assertEqual(get_kwh_amount(input_data), output_data, msg=error_msg)
@pytest.mark.task(taskno=4)
def test_get_efficiency(self):
From 9bf352d547d15c505bec44fa82e743d098a89a94 Mon Sep 17 00:00:00 2001
From: Carl
Date: Mon, 26 Dec 2022 11:19:00 +0100
Subject: [PATCH 297/826] Fix
---
exercises/concept/electric-bill/.meta/exemplar.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/exercises/concept/electric-bill/.meta/exemplar.py b/exercises/concept/electric-bill/.meta/exemplar.py
index 1b60339011..2a00a39b9b 100644
--- a/exercises/concept/electric-bill/.meta/exemplar.py
+++ b/exercises/concept/electric-bill/.meta/exemplar.py
@@ -28,7 +28,7 @@ def get_kwh_amount(watts):
:param: watts: int - watt amount.
:return: int - kilowatt hour amount.
"""
- return get_kW_value(watts) // 3600
+ return get_kW_amount(watts) // 3600
def get_efficiency(power_factor):
@@ -48,4 +48,4 @@ def get_cost(watts, power_factor, price):
:param: price: float - price of kWh.
:return: float - cost of kWh.
"""
- return price * (get_kwh_value(watts) / get_efficiency(power_factor))
+ return price * (get_kwh_amount(watts) / get_efficiency(power_factor))
From b4c2a9f446f353343e867fd05965feb48291459c Mon Sep 17 00:00:00 2001
From: Carl
Date: Mon, 26 Dec 2022 11:23:24 +0100
Subject: [PATCH 298/826] fix
---
exercises/concept/electric-bill/electric_bill_test.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/exercises/concept/electric-bill/electric_bill_test.py b/exercises/concept/electric-bill/electric_bill_test.py
index dea3b66c15..ffbd2fa5a6 100644
--- a/exercises/concept/electric-bill/electric_bill_test.py
+++ b/exercises/concept/electric-bill/electric_bill_test.py
@@ -16,7 +16,7 @@ def test_get_extra_hours(self):
for variant, (input_data, output_data) in enumerate(zip(input_data, output_data), start=1):
with self.subTest(f'variation #{variant}', input_data=input_data, output_data=output_data):
- error_msg=f'Expected: {output_data} but got a different value.'
+ error_msg=f'Expected: {output_data} but got a different amount.'
self.assertEqual(get_extra_hours(input_data), output_data, msg=error_msg)
@pytest.mark.task(taskno=2)
@@ -26,7 +26,7 @@ def test_get_kW_amount(self):
for variant, (input_data, output_data) in enumerate(zip(input_data, output_data), start=1):
with self.subTest(f'variation #{variant}', input_data=input_data, output_data=output_data):
- error_msg=f'Expected: {output_data} but got a different value.'
+ error_msg=f'Expected: {output_data} but got a different amount.'
self.assertEqual(get_kW_amount(input_data), output_data, msg=error_msg)
@pytest.mark.task(taskno=3)
@@ -36,7 +36,7 @@ def test_get_kwh_amount(self):
for variant, (input_data, output_data) in enumerate(zip(input_data, output_data), start=1):
with self.subTest(f'variation #{variant}', input_data=input_data, output_data=output_data):
- error_msg=f'Expected: {output_data} but got a different value.'
+ error_msg=f'Expected: {output_data} but got a different amount.'
self.assertEqual(get_kwh_amount(input_data), output_data, msg=error_msg)
@pytest.mark.task(taskno=4)
From 71e353a32f281db01eb9d16f05fa03c7d3eaf93e Mon Sep 17 00:00:00 2001
From: Carl
Date: Tue, 27 Dec 2022 00:42:35 +0100
Subject: [PATCH 299/826] Revert number
---
concepts/numbers/about.md | 2 +-
config.json | 12 ++++++------
2 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/concepts/numbers/about.md b/concepts/numbers/about.md
index 1f894b0aa8..5171b69354 100644
--- a/concepts/numbers/about.md
+++ b/concepts/numbers/about.md
@@ -170,7 +170,7 @@ This means calculations within `()` have the highest priority, followed by `**`,
20
# In the following example, the `**` operator has the highest priority, then `*`, then `+`
-# Meaning we first do 4 ** 4, then 3 * 64, then 2 + 192
+# Meaning we first do 4 ** 4, then 3 * 256, then 2 + 768
>>> 2 + 3 * 4 ** 4
770
```
diff --git a/config.json b/config.json
index 575558b764..b2222755eb 100644
--- a/config.json
+++ b/config.json
@@ -46,17 +46,17 @@
"slug": "electric-bill",
"name": "Electric Bill",
"uuid": "b2fd556b-07a8-47d6-9811-4d847cf0c6da",
- "concepts": ["numbers"],
- "prerequisites": ["basics"],
- "status": "beta"
+ "concepts": [],
+ "prerequisites": [],
+ "status": "deprecated"
},
{
"slug": "currency-exchange",
"name": "Currency Exchange",
"uuid": "1335ca33-3af0-4720-bcf2-bfa68ffc6862",
- "concepts": [],
- "prerequisites": [],
- "status": "deprecated"
+ "concepts": ["numbers"],
+ "prerequisites": ["basics"],
+ "status": "beta"
},
{
"slug": "meltdown-mitigation",
From 92b57da775356996bc164962307f87ba0fca26f6 Mon Sep 17 00:00:00 2001
From: Carl
Date: Tue, 27 Dec 2022 21:48:54 +0100
Subject: [PATCH 300/826] minor improvments
---
.../practice/dnd-character/.meta/template.j2 | 18 +++++++--------
.../practice/dnd-character/dnd_character.py | 4 ++++
.../dnd-character/dnd_character_test.py | 22 ++++++++-----------
3 files changed, 22 insertions(+), 22 deletions(-)
diff --git a/exercises/practice/dnd-character/.meta/template.j2 b/exercises/practice/dnd-character/.meta/template.j2
index 25642bb760..473af1609a 100644
--- a/exercises/practice/dnd-character/.meta/template.j2
+++ b/exercises/practice/dnd-character/.meta/template.j2
@@ -1,9 +1,7 @@
{%- import "generator_macros.j2" as macros with context -%}
-{% set class = exercise | camel_case -%}
{{ macros.header(["Character", "modifier"]) }}
-class {{ exercise | camel_case }}Test(unittest.TestCase):
- {% for supercase in cases -%}
+{% macro test_case(supercase) -%}
{% set property = supercase["property"] -%}
{% set description = supercase["description"] | to_snake -%}
{% if "cases" in supercase -%}
@@ -17,7 +15,7 @@ class {{ exercise | camel_case }}Test(unittest.TestCase):
{% elif property == "ability" -%}
def test_{{ description }}(self):
score = Character().{{ property }}()
- self.assertIs({{ supercase["expected"] | replace("&&","and") }}, True)
+ self.assertTrue({{ supercase["expected"] | replace("&&","and") }})
{% elif property == "character" -%}
def test_{{ description}}(self):
@@ -27,15 +25,17 @@ class {{ exercise | camel_case }}Test(unittest.TestCase):
{% if ability == "hitpoints" -%}
{% set statement = statement | replace("constitution","Char.constitution") -%}
{%- endif -%}
- self.assertIs({{ statement }}, True)
+ self.assertTrue({{ statement }})
{% endfor %}
{% elif property == "strength" -%}
def test_{{ description }}(self):
Char = Character()
- self.assertIs({{ supercase["expected"] | replace(property , ["Char.", property]|join(""))}}, True)
-
+ self.assertTrue({{ supercase["expected"] | replace(property , ["Char.", property]|join(""))}})
{%- endif -%}
- {% endfor %}
+{%- endmacro %}
-{{ macros.footer() }}
+class {{ exercise | camel_case }}Test(unittest.TestCase):
+ {% for supercase in cases -%}
+ {{ test_case(supercase) }}
+ {% endfor %}
\ No newline at end of file
diff --git a/exercises/practice/dnd-character/dnd_character.py b/exercises/practice/dnd-character/dnd_character.py
index f8ecd30bb8..acfe6aef06 100644
--- a/exercises/practice/dnd-character/dnd_character.py
+++ b/exercises/practice/dnd-character/dnd_character.py
@@ -1,3 +1,7 @@
class Character:
def __init__(self):
pass
+
+
+def modifier(value):
+ pass
diff --git a/exercises/practice/dnd-character/dnd_character_test.py b/exercises/practice/dnd-character/dnd_character_test.py
index b3a93e5759..d79074ca3a 100644
--- a/exercises/practice/dnd-character/dnd_character_test.py
+++ b/exercises/practice/dnd-character/dnd_character_test.py
@@ -59,22 +59,18 @@ def test_ability_modifier_for_score_18_is_4(self):
def test_random_ability_is_within_range(self):
score = Character().ability()
- self.assertIs(score >= 3 and score <= 18, True)
+ self.assertTrue(score >= 3 and score <= 18)
def test_random_character_is_valid(self):
Char = Character()
- self.assertIs(Char.strength >= 3 and Char.strength <= 18, True)
- self.assertIs(Char.dexterity >= 3 and Char.dexterity <= 18, True)
- self.assertIs(Char.constitution >= 3 and Char.constitution <= 18, True)
- self.assertIs(Char.intelligence >= 3 and Char.intelligence <= 18, True)
- self.assertIs(Char.wisdom >= 3 and Char.wisdom <= 18, True)
- self.assertIs(Char.charisma >= 3 and Char.charisma <= 18, True)
- self.assertIs(Char.hitpoints == 10 + modifier(Char.constitution), True)
+ self.assertTrue(Char.strength >= 3 and Char.strength <= 18)
+ self.assertTrue(Char.dexterity >= 3 and Char.dexterity <= 18)
+ self.assertTrue(Char.constitution >= 3 and Char.constitution <= 18)
+ self.assertTrue(Char.intelligence >= 3 and Char.intelligence <= 18)
+ self.assertTrue(Char.wisdom >= 3 and Char.wisdom <= 18)
+ self.assertTrue(Char.charisma >= 3 and Char.charisma <= 18)
+ self.assertTrue(Char.hitpoints == 10 + modifier(Char.constitution))
def test_each_ability_is_only_calculated_once(self):
Char = Character()
- self.assertIs(Char.strength == Char.strength, True)
-
-
-if __name__ == "__main__":
- unittest.main()
+ self.assertTrue(Char.strength == Char.strength)
From 6841758d29a757cfd10a2f2b2dab272e97dc9fca Mon Sep 17 00:00:00 2001
From: Carl
Date: Wed, 28 Dec 2022 14:26:16 +0100
Subject: [PATCH 301/826] Reversed some changes
---
exercises/practice/dnd-character/dnd_character.py | 4 ----
1 file changed, 4 deletions(-)
diff --git a/exercises/practice/dnd-character/dnd_character.py b/exercises/practice/dnd-character/dnd_character.py
index acfe6aef06..f8ecd30bb8 100644
--- a/exercises/practice/dnd-character/dnd_character.py
+++ b/exercises/practice/dnd-character/dnd_character.py
@@ -1,7 +1,3 @@
class Character:
def __init__(self):
pass
-
-
-def modifier(value):
- pass
From 6b8b69de4f5816bd68dedfd56dee26ee2f53a958 Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Wed, 28 Dec 2022 12:46:09 -0800
Subject: [PATCH 302/826] Apply suggestions from code review
Reverting to `assertIs`.
---
exercises/practice/dnd-character/.meta/template.j2 | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/exercises/practice/dnd-character/.meta/template.j2 b/exercises/practice/dnd-character/.meta/template.j2
index 473af1609a..adb1565053 100644
--- a/exercises/practice/dnd-character/.meta/template.j2
+++ b/exercises/practice/dnd-character/.meta/template.j2
@@ -15,7 +15,7 @@
{% elif property == "ability" -%}
def test_{{ description }}(self):
score = Character().{{ property }}()
- self.assertTrue({{ supercase["expected"] | replace("&&","and") }})
+ self.assertIs({{ supercase["expected"] | replace("&&","and") }}, True)
{% elif property == "character" -%}
def test_{{ description}}(self):
@@ -25,13 +25,13 @@
{% if ability == "hitpoints" -%}
{% set statement = statement | replace("constitution","Char.constitution") -%}
{%- endif -%}
- self.assertTrue({{ statement }})
+ self.assertIs({{ statement }}, True)
{% endfor %}
{% elif property == "strength" -%}
def test_{{ description }}(self):
Char = Character()
- self.assertTrue({{ supercase["expected"] | replace(property , ["Char.", property]|join(""))}})
+ self.assertIs({{ supercase["expected"] | replace(property , ["Char.", property]|join(""))}}, True)
{%- endif -%}
{%- endmacro %}
From 01d9cafa04e71cea70f7eed2ff34d8ed9c195049 Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Wed, 28 Dec 2022 12:52:29 -0800
Subject: [PATCH 303/826] Regenerated Test File
---
.../dnd-character/dnd_character_test.py | 18 +++++++++---------
1 file changed, 9 insertions(+), 9 deletions(-)
diff --git a/exercises/practice/dnd-character/dnd_character_test.py b/exercises/practice/dnd-character/dnd_character_test.py
index d79074ca3a..3f370bc4bd 100644
--- a/exercises/practice/dnd-character/dnd_character_test.py
+++ b/exercises/practice/dnd-character/dnd_character_test.py
@@ -59,18 +59,18 @@ def test_ability_modifier_for_score_18_is_4(self):
def test_random_ability_is_within_range(self):
score = Character().ability()
- self.assertTrue(score >= 3 and score <= 18)
+ self.assertIs(score >= 3 and score <= 18, True)
def test_random_character_is_valid(self):
Char = Character()
- self.assertTrue(Char.strength >= 3 and Char.strength <= 18)
- self.assertTrue(Char.dexterity >= 3 and Char.dexterity <= 18)
- self.assertTrue(Char.constitution >= 3 and Char.constitution <= 18)
- self.assertTrue(Char.intelligence >= 3 and Char.intelligence <= 18)
- self.assertTrue(Char.wisdom >= 3 and Char.wisdom <= 18)
- self.assertTrue(Char.charisma >= 3 and Char.charisma <= 18)
- self.assertTrue(Char.hitpoints == 10 + modifier(Char.constitution))
+ self.assertIs(Char.strength >= 3 and Char.strength <= 18, True)
+ self.assertIs(Char.dexterity >= 3 and Char.dexterity <= 18, True)
+ self.assertIs(Char.constitution >= 3 and Char.constitution <= 18, True)
+ self.assertIs(Char.intelligence >= 3 and Char.intelligence <= 18, True)
+ self.assertIs(Char.wisdom >= 3 and Char.wisdom <= 18, True)
+ self.assertIs(Char.charisma >= 3 and Char.charisma <= 18, True)
+ self.assertIs(Char.hitpoints == 10 + modifier(Char.constitution), True)
def test_each_ability_is_only_calculated_once(self):
Char = Character()
- self.assertTrue(Char.strength == Char.strength)
+ self.assertIs(Char.strength == Char.strength, True)
From 2983d723ddc9ec9cde4268295741e6d76cd0ee3b Mon Sep 17 00:00:00 2001
From: Carl
Date: Fri, 30 Dec 2022 21:57:36 +0100
Subject: [PATCH 304/826] Fixed spacing
---
exercises/practice/collatz-conjecture/.meta/template.j2 | 4 ++--
.../practice/collatz-conjecture/collatz_conjecture_test.py | 6 ------
2 files changed, 2 insertions(+), 8 deletions(-)
diff --git a/exercises/practice/collatz-conjecture/.meta/template.j2 b/exercises/practice/collatz-conjecture/.meta/template.j2
index e8b36f8b4d..4dcbae8550 100644
--- a/exercises/practice/collatz-conjecture/.meta/template.j2
+++ b/exercises/practice/collatz-conjecture/.meta/template.j2
@@ -3,12 +3,12 @@
def test_{{ case["description"] | to_snake }}(self):
{% set expected = case["expected"] -%}
{% set exp_error = expected["error"] -%}
- {%- if case is error_case %}
+ {%- if case is error_case -%}
with self.assertRaises(ValueError) as err:
{{ case["property"] }}({{ case["input"]["number"] }})
self.assertEqual(type(err.exception), ValueError)
self.assertEqual(err.exception.args[0], "{{ exp_error }}")
- {% else %}
+ {%- else -%}
self.assertEqual(
{{ case["property"] }}({{ case["input"]["number"] }}),
{{ case["expected"] }}
diff --git a/exercises/practice/collatz-conjecture/collatz_conjecture_test.py b/exercises/practice/collatz-conjecture/collatz_conjecture_test.py
index c11e246b54..b9c6df93c8 100644
--- a/exercises/practice/collatz-conjecture/collatz_conjecture_test.py
+++ b/exercises/practice/collatz-conjecture/collatz_conjecture_test.py
@@ -9,30 +9,24 @@
class CollatzConjectureTest(unittest.TestCase):
def test_zero_steps_for_one(self):
-
self.assertEqual(steps(1), 0)
def test_divide_if_even(self):
-
self.assertEqual(steps(16), 4)
def test_even_and_odd_steps(self):
-
self.assertEqual(steps(12), 9)
def test_large_number_of_even_and_odd_steps(self):
-
self.assertEqual(steps(1000000), 152)
def test_zero_is_an_error(self):
-
with self.assertRaises(ValueError) as err:
steps(0)
self.assertEqual(type(err.exception), ValueError)
self.assertEqual(err.exception.args[0], "Only positive integers are allowed")
def test_negative_value_is_an_error(self):
-
with self.assertRaises(ValueError) as err:
steps(-15)
self.assertEqual(type(err.exception), ValueError)
From 5d384f236d21e330ea90c9e155c7120b8944a97a Mon Sep 17 00:00:00 2001
From: Carl
Date: Fri, 30 Dec 2022 19:05:25 +0100
Subject: [PATCH 305/826] Spell fix
---
.../practice/leap/.approaches/datetime-addition/content.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/exercises/practice/leap/.approaches/datetime-addition/content.md b/exercises/practice/leap/.approaches/datetime-addition/content.md
index 3748fa47ca..681e19101a 100644
--- a/exercises/practice/leap/.approaches/datetime-addition/content.md
+++ b/exercises/practice/leap/.approaches/datetime-addition/content.md
@@ -17,9 +17,9 @@ This approach may be considered a "cheat" for this exercise.
By adding a day to February 28th for the year, you can see if the new day is the 29th or the 1st.
If it is the 29th, then the function returns `True` for the year being a leap year.
-- A new [datetime][datetime] object is created for Febuary 28th of the year.
+- A new [datetime][datetime] object is created for February 28th of the year.
- Then the [timedelta][timedelta] of one day is added to that `datetime`,
-and the function returns if the [day][day] property of the resulting `datetime` object is the 29th.
+ and the function returns if the [day][day] property of the resulting `datetime` object is the 29th.
[timedelta]: https://docs.python.org/3/library/datetime.html#timedelta-objects
[day]: https://docs.python.org/3/library/datetime.html#datetime.datetime.day
From ef7760ce2296fdb4a198ac544bc5c625de54b0a5 Mon Sep 17 00:00:00 2001
From: Carl
Date: Wed, 28 Dec 2022 17:42:17 +0100
Subject: [PATCH 306/826] Start
---
.../practice/hamming/.approaches/config.json | 29 ++++++++
.../hamming/.approaches/introduction.md | 67 +++++++++++++++++++
.../hamming/.approaches/range/content.md | 38 +++++++++++
.../hamming/.approaches/sum/content.md | 55 +++++++++++++++
.../hamming/.approaches/zip/content.md | 47 +++++++++++++
5 files changed, 236 insertions(+)
create mode 100644 exercises/practice/hamming/.approaches/config.json
create mode 100644 exercises/practice/hamming/.approaches/introduction.md
create mode 100644 exercises/practice/hamming/.approaches/range/content.md
create mode 100644 exercises/practice/hamming/.approaches/sum/content.md
create mode 100644 exercises/practice/hamming/.approaches/zip/content.md
diff --git a/exercises/practice/hamming/.approaches/config.json b/exercises/practice/hamming/.approaches/config.json
new file mode 100644
index 0000000000..9b5ee619e1
--- /dev/null
+++ b/exercises/practice/hamming/.approaches/config.json
@@ -0,0 +1,29 @@
+{
+ "introduction": {
+ "authors": ["meatball133", "bethanyg"],
+ "contributors": []
+ },
+ "approaches": [
+ {
+ "uuid": "9e0baea1-8b3b-46be-8d26-e247b6a59eee",
+ "slug": "range",
+ "title": "Range",
+ "blurb": "Use range to compare 2 strings",
+ "authors": ["meatball133", "bethanyg"]
+ },
+ {
+ "uuid": "75883c53-cc84-483a-8375-eb9375a5f739",
+ "slug": "zip",
+ "title": "Zip",
+ "blurb": "Use zip to compare 2 strings",
+ "authors": ["meatball133", "bethanyg"]
+ },
+ {
+ "uuid": "9db3f200-2816-40a8-b660-a6bf87abe470",
+ "slug": "sum",
+ "title": "Sum",
+ "blurb": "Use sum to compare 2 strings",
+ "authors": ["meatball133", "bethanyg"]
+ }
+ ]
+}
diff --git a/exercises/practice/hamming/.approaches/introduction.md b/exercises/practice/hamming/.approaches/introduction.md
new file mode 100644
index 0000000000..88419a879b
--- /dev/null
+++ b/exercises/practice/hamming/.approaches/introduction.md
@@ -0,0 +1,67 @@
+# Introduction
+
+There are various ways to solve Hamming.
+One approach is to iterate over either a range of indexs or to use [zip][zip].
+Another appraoch is to use the range of indexs.
+
+## General guidance
+
+The goal of this exercise is to compare two DNA strands and count how many of the nucleotides are different from their equivalent in the other string.
+The most common way is to use some kind of loop to iterate over the two strands and compare the nucleotides which has the same index.
+
+## Approach: Iterating over a range of indexes
+
+```python
+def distance(strand_a, strand_b):
+ if len(strand_a) != len(strand_b):
+ raise ValueError("Strands must be of equal length.")
+ count = 0
+ for x in range(len(strand_a)):
+ if strand_a[x] != strand_b[x]:
+ count += 1
+ return count
+```
+
+For more information, check the [range approach][approach-range].
+
+## Approach: Iterating with zip
+
+```python
+def distance(strand_a, strand_b):
+ if len(strand_a) != len(strand_b):
+ raise ValueError("Strands must be of equal length.")
+ count = 0
+ for a, b in zip(strand_a, strand_b):
+ if a != b:
+ count += 1
+ return count
+```
+
+For more information, check the [zip approach][approach-zip].
+
+## Approach: Using sum
+
+With zip:
+
+```python
+def distance(strand_a, strand_b):
+ if len(strand_a) != len(strand_b):
+ raise ValueError("Strands must be of equal length.")
+ return sum(a != b for a, b in zip(strand_a, strand_b))
+```
+
+With range:
+
+```python
+def distance(strand_a, strand_b):
+ if len(strand_a) != len(strand_b):
+ raise ValueError("Strands must be of equal length.")
+ return sum(strand_a[index] != strand_b[index] for index in range(len(strand_a)))
+```
+
+For more information, check the [sum approach][approach-sum].
+
+[zip]: https://docs.python.org/3/library/functions.html#zip
+[approach-range]: https://exercism.org/tracks/python/exercises/hamming/approaches/range
+[approach-sum]: https://exercism.org/tracks/python/exercises/hamming/approaches/sum
+[approach-zip]: https://exercism.org/tracks/python/exercises/hamming/approaches/zip
diff --git a/exercises/practice/hamming/.approaches/range/content.md b/exercises/practice/hamming/.approaches/range/content.md
new file mode 100644
index 0000000000..00c633066f
--- /dev/null
+++ b/exercises/practice/hamming/.approaches/range/content.md
@@ -0,0 +1,38 @@
+# range
+
+```python
+def distance(strand_a, strand_b):
+ if len(strand_a) != len(strand_b):
+ raise ValueError("Strands must be of equal length.")
+ count = 0
+ for index in range(len(strand_a)):
+ if strand_a[index] != strand_b[index]:
+ count += 1
+ return count
+```
+
+This approach starts by checking if the two strands are of equal length.
+If not, a [`ValueError`][value-error] is raised.
+
+After that is checked, a variable `count` is initialized to 0.
+The count variable will be used to keep track of the number of differences between the two strands.
+
+[`range()`][range] in Python is a built-in function that returns a sequence of numbers.
+Range is an infinite sequence, but it can be limited by providing a `stop` argument.
+The `range()` function can also take a `start` argument and a `step` argument.
+The inputs are built up like this: `range(, stop, )`.
+Since we are only providing a `stop` argument, the `start` argument defaults to 0 and the `step` argument defaults to 1.
+
+We use range to iterate over the indexes of the `strand_a` string.
+We do that by passing the length of the string to the `range()` function by using [`len()`][len].
+The iteration gives us the index of the character in the string.
+We then use that index to access the character in the string.
+
+Then we compare the character at the index in `strand_a` to the character at the same index in `strand_b`.
+If they are not equal, we increment the `count` variable by 1.
+
+After the loop is finished, we return the `count` variable.
+
+[len]: https://docs.python.org/3/library/functions.html?#len
+[range]: https://docs.python.org/3/library/stdtypes.html?#range
+[value-error]: https://docs.python.org/3/library/exceptions.html#ValueError
diff --git a/exercises/practice/hamming/.approaches/sum/content.md b/exercises/practice/hamming/.approaches/sum/content.md
new file mode 100644
index 0000000000..7d03212c52
--- /dev/null
+++ b/exercises/practice/hamming/.approaches/sum/content.md
@@ -0,0 +1,55 @@
+# sum
+
+The benefit of using [`sum()`][sum] is that we can use a list comprehension to create a list of booleans.
+Then we can pass that list to `sum()` and it will add up all the booleans.
+Where `True` is treated as 1 and `False` is treated as 0.
+Then that sum is returned.
+
+This can make the code a bit more concise.
+
+Here is an example with using `sum()` and `zip()`:
+
+```python
+def distance(strand_a, strand_b):
+ if len(strand_a) != len(strand_b):
+ raise ValueError("Strands must be of equal length.")
+ return sum(nucleotide_a != nucleotide_b for nucleotide_a, nucleotide_b in zip(strand_a, strand_b))
+```
+
+This approach starts by checking if the two strands are of equal length by using [`len()`][len].
+If not, a [`ValueError`][value-error] is raised.
+
+After that is checked, a variable `count` is initialized to 0.
+The count variable will be used to keep track of the number of differences between the two strands.
+
+This approach uses the [`zip()`][zip] function to iterate over two strings.
+You can read more about how to solve this exercise with `zip()` in the [zip approach][approach-zip].
+
+What differs in this approach is that we use a list comprehension to create a list of booleans.
+The list comprehension iterates over the tuples returned by `zip()`.
+Then under the iteration so are the tuples unpacked into two variables, `nucleotide_a` and `nucleotide_b`.
+We then compare the characters `nucleotide_a` and `nucleotide_b`.
+If they are not equal, we add `True` to the list.
+If they are equal, we add `False` to the list.
+The list comprehension is then passed to the `sum()` function.
+
+The [`sum()`][sum] function will add up all the booleans in the list.
+Where `True` is treated as 1 and `False` is treated as 0.
+You can read more about this behavior in [Boolean as numbers][booleans].
+Then that sum is returned.
+
+This approach is also doable with range but it is a bit more verbose:
+
+```python
+def distance(strand_a, strand_b):
+ if len(strand_a) != len(strand_b):
+ raise ValueError("Strands must be of equal length.")
+ return sum(strand_a[index] != strand_b[index] for index in range(len(strand_a)))
+```
+
+[booleans]: https://realpython.com/python-boolean/#python-booleans-as-numbers
+[len]: https://docs.python.org/3/library/functions.html?#len
+[sum]: https://docs.python.org/3/library/functions.html?#sum
+[value-error]: https://docs.python.org/3/library/exceptions.html#ValueError
+[zip]: https://docs.python.org/3.3/library/functions.html#zip
+[approach-zip]: https://exercism.org/tracks/python/exercises/hamming/approaches/zip
diff --git a/exercises/practice/hamming/.approaches/zip/content.md b/exercises/practice/hamming/.approaches/zip/content.md
new file mode 100644
index 0000000000..e8bfab7d09
--- /dev/null
+++ b/exercises/practice/hamming/.approaches/zip/content.md
@@ -0,0 +1,47 @@
+# zip
+
+```python
+def distance(strand_a, strand_b):
+ if len(strand_a) != len(strand_b):
+ raise ValueError("Strands must be of equal length.")
+ count = 0
+ for nucleotide_a, nucleotide_b in zip(strand_a, strand_b):
+ if nucleotide_a != nucleotide_b:
+ count += 1
+ return count
+```
+
+This approach starts by checking if the two strands are of equal length by using [`len()`][len].
+If not, a [`ValueError`][value-error] is raised.
+
+After that is checked, a variable `count` is initialized to 0.
+The count variable will be used to keep track of the number of differences between the two strands.
+
+We use [`zip()`][zip] to iterate over the characters in `strand_a` and `strand_b` simultaneously.
+`zip()` is a built in function.
+It takes any number of iterables and returns an iterator of tuples.
+Where the i-th tuple contains the i-th element from each of the argument iterables.
+For example, the first tuple will contain the first element from each iterable, the second tuple will contain the second element from each iterable, and so on until the shortest iterable is exhausted.
+
+In python are strings are iterables.
+
+Here is an example of using `zip()` to iterate over two strings:
+
+```python
+>>> zipped = zip("GGACGG", "AGGACG")
+>>> list(zipped)
+[('G', 'A'), ('G', 'G'), ('A', 'G'), ('C', 'A'), ('G', 'C'), ('G', 'G')]
+```
+
+We then use the `zip()` iterator to iterate over the tuples.
+We unpack the tuple into two variables, `nucleotide_a` and `nucleotide_b`.
+You can read more about unpacking in the concept [concept:python/unpacking-and-multiple-assignment]().
+
+We then compare the characters `nucleotide_a` and `nucleotide_b`.
+If they are not equal, we increment the `count` variable by 1.
+
+After the loop is finished, we return the `count` variable.
+
+[len]: https://docs.python.org/3/library/functions.html?#len
+[value-error]: https://docs.python.org/3/library/exceptions.html#ValueError
+[zip]: https://docs.python.org/3.3/library/functions.html#zip
From d043098b04689f5dc97f489165c2dd25be0289b7 Mon Sep 17 00:00:00 2001
From: Carl
Date: Wed, 28 Dec 2022 17:43:32 +0100
Subject: [PATCH 307/826] Fix variable naming
---
exercises/practice/hamming/.approaches/introduction.md | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/exercises/practice/hamming/.approaches/introduction.md b/exercises/practice/hamming/.approaches/introduction.md
index 88419a879b..f6cd3f77cd 100644
--- a/exercises/practice/hamming/.approaches/introduction.md
+++ b/exercises/practice/hamming/.approaches/introduction.md
@@ -31,8 +31,8 @@ def distance(strand_a, strand_b):
if len(strand_a) != len(strand_b):
raise ValueError("Strands must be of equal length.")
count = 0
- for a, b in zip(strand_a, strand_b):
- if a != b:
+ for nucleotide_a, nucleotide_b in zip(strand_a, strand_b):
+ if nucleotide_a != nucleotide_b:
count += 1
return count
```
@@ -47,7 +47,7 @@ With zip:
def distance(strand_a, strand_b):
if len(strand_a) != len(strand_b):
raise ValueError("Strands must be of equal length.")
- return sum(a != b for a, b in zip(strand_a, strand_b))
+ return sum(nucleotide_a != nucleotide_b for nucleotide_a, nucleotide_b in zip(strand_a, strand_b))
```
With range:
From 2ac003c060e87fed3dba0b6575b60680588cae84 Mon Sep 17 00:00:00 2001
From: Carl
Date: Wed, 28 Dec 2022 17:46:18 +0100
Subject: [PATCH 308/826] Add missing files
---
exercises/practice/hamming/.approaches/range/snippet.txt | 8 ++++++++
exercises/practice/hamming/.approaches/sum/snippet.txt | 4 ++++
exercises/practice/hamming/.approaches/zip/snippet.txt | 8 ++++++++
3 files changed, 20 insertions(+)
create mode 100644 exercises/practice/hamming/.approaches/range/snippet.txt
create mode 100644 exercises/practice/hamming/.approaches/sum/snippet.txt
create mode 100644 exercises/practice/hamming/.approaches/zip/snippet.txt
diff --git a/exercises/practice/hamming/.approaches/range/snippet.txt b/exercises/practice/hamming/.approaches/range/snippet.txt
new file mode 100644
index 0000000000..85caf5179b
--- /dev/null
+++ b/exercises/practice/hamming/.approaches/range/snippet.txt
@@ -0,0 +1,8 @@
+def distance(strand_a, strand_b):
+ if len(strand_a) != len(strand_b):
+ raise ValueError("Strands must be of equal length.")
+ count = 0
+ for index in range(len(strand_a)):
+ if strand_a[index] != strand_b[index]:
+ count += 1
+ return count
diff --git a/exercises/practice/hamming/.approaches/sum/snippet.txt b/exercises/practice/hamming/.approaches/sum/snippet.txt
new file mode 100644
index 0000000000..d8cc2f6393
--- /dev/null
+++ b/exercises/practice/hamming/.approaches/sum/snippet.txt
@@ -0,0 +1,4 @@
+def distance(strand_a, strand_b):
+ if len(strand_a) != len(strand_b):
+ raise ValueError("Strands must be of equal length.")
+ return sum(nucleotide_a != nucleotide_b for nucleotide_a, nucleotide_b in zip(strand_a, strand_b))
diff --git a/exercises/practice/hamming/.approaches/zip/snippet.txt b/exercises/practice/hamming/.approaches/zip/snippet.txt
new file mode 100644
index 0000000000..3c310fe376
--- /dev/null
+++ b/exercises/practice/hamming/.approaches/zip/snippet.txt
@@ -0,0 +1,8 @@
+def distance(strand_a, strand_b):
+ if len(strand_a) != len(strand_b):
+ raise ValueError("Strands must be of equal length.")
+ count = 0
+ for nucleotide_a, nucleotide_b in zip(strand_a, strand_b):
+ if nucleotide_a != nucleotide_b:
+ count += 1
+ return count
From 774fa921dabe3e222ab47e004b7135710bdbe700 Mon Sep 17 00:00:00 2001
From: Carl
Date: Thu, 29 Dec 2022 12:08:49 +0100
Subject: [PATCH 309/826] Fixes and added an explainer to why you might want to
use that method
---
.../hamming/.approaches/introduction.md | 24 ++++++++++++++++---
.../hamming/.approaches/sum/content.md | 2 +-
2 files changed, 22 insertions(+), 4 deletions(-)
diff --git a/exercises/practice/hamming/.approaches/introduction.md b/exercises/practice/hamming/.approaches/introduction.md
index f6cd3f77cd..7fa2fff95b 100644
--- a/exercises/practice/hamming/.approaches/introduction.md
+++ b/exercises/practice/hamming/.approaches/introduction.md
@@ -3,6 +3,7 @@
There are various ways to solve Hamming.
One approach is to iterate over either a range of indexs or to use [zip][zip].
Another appraoch is to use the range of indexs.
+Some other approaches could be to use enumerate, or filter with lambda.
## General guidance
@@ -11,13 +12,19 @@ The most common way is to use some kind of loop to iterate over the two strands
## Approach: Iterating over a range of indexes
+Using range is an approach to iterate over a sequence.
+Although it is not the most pythonic way, it is a good way to start.
+The reasson to use range is that it is a built-in function and it is very fast.
+The downside is that it only works with iterators that can be indexed, like lists and strings.
+While a built in function like `enumerate()` can take any iterator.
+
```python
def distance(strand_a, strand_b):
if len(strand_a) != len(strand_b):
raise ValueError("Strands must be of equal length.")
count = 0
- for x in range(len(strand_a)):
- if strand_a[x] != strand_b[x]:
+ for index in range(len(strand_a)):
+ if strand_a[index] != strand_b[index]:
count += 1
return count
```
@@ -26,6 +33,11 @@ For more information, check the [range approach][approach-range].
## Approach: Iterating with zip
+The `zip()` function returns an iterator of tuples where the first item in each passed iterator is paired together, and then the second item in each passed iterator are paired together etc.
+The approach to use `zip()` to iterate removes the need to index the iterators.
+The downside is that if you need to index the iterators, zip wont work.
+Although it is possible to use `enumerate()` with zip to get the index.
+
```python
def distance(strand_a, strand_b):
if len(strand_a) != len(strand_b):
@@ -41,6 +53,11 @@ For more information, check the [zip approach][approach-zip].
## Approach: Using sum
+Using `sum()` makes it possible to remove the need for a counter variable.
+Since there is no need for a counter variable, the code is more concise.
+To make use of sum, so are the examples using a [list comprehension][list-comprehension], although it is not required.
+Using sum although requires a bit more knowledge of python compared to the other approaches.
+
With zip:
```python
@@ -61,7 +78,8 @@ def distance(strand_a, strand_b):
For more information, check the [sum approach][approach-sum].
-[zip]: https://docs.python.org/3/library/functions.html#zip
[approach-range]: https://exercism.org/tracks/python/exercises/hamming/approaches/range
[approach-sum]: https://exercism.org/tracks/python/exercises/hamming/approaches/sum
[approach-zip]: https://exercism.org/tracks/python/exercises/hamming/approaches/zip
+[list-comprehension]: https://docs.python.org/3/tutorial/datastructures.html#list-comprehensions
+[zip]: https://docs.python.org/3/library/functions.html#zip
diff --git a/exercises/practice/hamming/.approaches/sum/content.md b/exercises/practice/hamming/.approaches/sum/content.md
index 7d03212c52..5254af969f 100644
--- a/exercises/practice/hamming/.approaches/sum/content.md
+++ b/exercises/practice/hamming/.approaches/sum/content.md
@@ -47,9 +47,9 @@ def distance(strand_a, strand_b):
return sum(strand_a[index] != strand_b[index] for index in range(len(strand_a)))
```
+[approach-zip]: https://exercism.org/tracks/python/exercises/hamming/approaches/zip
[booleans]: https://realpython.com/python-boolean/#python-booleans-as-numbers
[len]: https://docs.python.org/3/library/functions.html?#len
[sum]: https://docs.python.org/3/library/functions.html?#sum
[value-error]: https://docs.python.org/3/library/exceptions.html#ValueError
[zip]: https://docs.python.org/3.3/library/functions.html#zip
-[approach-zip]: https://exercism.org/tracks/python/exercises/hamming/approaches/zip
From f2716a18e05ac2fee59000ddf44c540da23ae0b4 Mon Sep 17 00:00:00 2001
From: Carl
Date: Thu, 29 Dec 2022 23:21:40 +0100
Subject: [PATCH 310/826] spell fixes
---
exercises/practice/hamming/.approaches/introduction.md | 10 +++++-----
exercises/practice/hamming/.approaches/sum/content.md | 2 +-
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/exercises/practice/hamming/.approaches/introduction.md b/exercises/practice/hamming/.approaches/introduction.md
index 7fa2fff95b..9436d1cc9b 100644
--- a/exercises/practice/hamming/.approaches/introduction.md
+++ b/exercises/practice/hamming/.approaches/introduction.md
@@ -1,20 +1,20 @@
# Introduction
There are various ways to solve Hamming.
-One approach is to iterate over either a range of indexs or to use [zip][zip].
-Another appraoch is to use the range of indexs.
+One approach is to iterate over either a range of indexes or to use [zip][zip].
+Another approach is to use the range of indexes.
Some other approaches could be to use enumerate, or filter with lambda.
## General guidance
The goal of this exercise is to compare two DNA strands and count how many of the nucleotides are different from their equivalent in the other string.
-The most common way is to use some kind of loop to iterate over the two strands and compare the nucleotides which has the same index.
+The most common way is to use some kind of loop to iterate over the two strands and compare the nucleotides with the same index.
## Approach: Iterating over a range of indexes
Using range is an approach to iterate over a sequence.
Although it is not the most pythonic way, it is a good way to start.
-The reasson to use range is that it is a built-in function and it is very fast.
+The reason to use range is that it is a built-in function and it is very fast.
The downside is that it only works with iterators that can be indexed, like lists and strings.
While a built in function like `enumerate()` can take any iterator.
@@ -35,7 +35,7 @@ For more information, check the [range approach][approach-range].
The `zip()` function returns an iterator of tuples where the first item in each passed iterator is paired together, and then the second item in each passed iterator are paired together etc.
The approach to use `zip()` to iterate removes the need to index the iterators.
-The downside is that if you need to index the iterators, zip wont work.
+The downside is that if you need to index the iterators, zip won't work.
Although it is possible to use `enumerate()` with zip to get the index.
```python
diff --git a/exercises/practice/hamming/.approaches/sum/content.md b/exercises/practice/hamming/.approaches/sum/content.md
index 5254af969f..f590f14048 100644
--- a/exercises/practice/hamming/.approaches/sum/content.md
+++ b/exercises/practice/hamming/.approaches/sum/content.md
@@ -7,7 +7,7 @@ Then that sum is returned.
This can make the code a bit more concise.
-Here is an example with using `sum()` and `zip()`:
+Here is an example using `sum()` and `zip()`:
```python
def distance(strand_a, strand_b):
From 85ef185a521e73b714add30e093f44226905f772 Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Tue, 3 Jan 2023 16:31:47 -0800
Subject: [PATCH 311/826] Apply suggestions from code review
---
.../hamming/.approaches/introduction.md | 52 +++++++++++--------
.../hamming/.approaches/range/content.md | 25 +++++----
.../hamming/.approaches/sum/content.md | 36 ++++++-------
.../hamming/.approaches/zip/content.md | 18 +++----
4 files changed, 70 insertions(+), 61 deletions(-)
diff --git a/exercises/practice/hamming/.approaches/introduction.md b/exercises/practice/hamming/.approaches/introduction.md
index 9436d1cc9b..8cf91f229e 100644
--- a/exercises/practice/hamming/.approaches/introduction.md
+++ b/exercises/practice/hamming/.approaches/introduction.md
@@ -3,20 +3,20 @@
There are various ways to solve Hamming.
One approach is to iterate over either a range of indexes or to use [zip][zip].
Another approach is to use the range of indexes.
-Some other approaches could be to use enumerate, or filter with lambda.
+Some other approaches include the use of [`enumerate`][enumerate], or [`filter`][filter] with a [`lambda`][lambda].
## General guidance
The goal of this exercise is to compare two DNA strands and count how many of the nucleotides are different from their equivalent in the other string.
-The most common way is to use some kind of loop to iterate over the two strands and compare the nucleotides with the same index.
+The most common solution uses some kind of loop to iterate over the two strands and compare nucleotides with the same index.
## Approach: Iterating over a range of indexes
-Using range is an approach to iterate over a sequence.
-Although it is not the most pythonic way, it is a good way to start.
-The reason to use range is that it is a built-in function and it is very fast.
-The downside is that it only works with iterators that can be indexed, like lists and strings.
-While a built in function like `enumerate()` can take any iterator.
+Using [`range`][range] is an approach to iterate over a sequence.
+Although it may not be the most _pythonic_ strategy, it is a good way to start.
+`range` is a [built-in function][built-in-functions] and it is very fast.
+The downside is that `range` only works with [iterators][iterators] that can be indexed, like [concept:python/lists](https://docs.python.org/3/library/stdtypes.html#sequence-types-list-tuple-range) and [concept:python/strings](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str).
+While the built-in function `enumerate` can take any iterator.
```python
def distance(strand_a, strand_b):
@@ -33,10 +33,10 @@ For more information, check the [range approach][approach-range].
## Approach: Iterating with zip
-The `zip()` function returns an iterator of tuples where the first item in each passed iterator is paired together, and then the second item in each passed iterator are paired together etc.
-The approach to use `zip()` to iterate removes the need to index the iterators.
-The downside is that if you need to index the iterators, zip won't work.
-Although it is possible to use `enumerate()` with zip to get the index.
+The built-in `zip` function returns an iterator of [concept:python/tuples](https://docs.python.org/3.10/tutorial/datastructures.html#tuples-and-sequences) where the first item in each passed iterator is paired together, and then the second item in each passed iterator are paired together, and so on.
+Using `zip()` to iterate removes the need to index into the strands.
+The downside is that if you _need to_ index into your iterators, `zip` won't work.
+Although it is possible to combine `zip` with `enumerate` to generate indexes.
```python
def distance(strand_a, strand_b):
@@ -53,27 +53,29 @@ For more information, check the [zip approach][approach-zip].
## Approach: Using sum
-Using `sum()` makes it possible to remove the need for a counter variable.
-Since there is no need for a counter variable, the code is more concise.
-To make use of sum, so are the examples using a [list comprehension][list-comprehension], although it is not required.
-Using sum although requires a bit more knowledge of python compared to the other approaches.
+Using the built-in [`sum`][sum] removes the need for a counter variable.
+Removing the counter variable makes the code is more concise.
+The examples making use of `sum` also use a [generator expression][generator-expression], although that it is not required.
+Using `sum` in this fashion requires a bit more Python knowledge compared to the other approaches.
-With zip:
+With `zip`:
```python
def distance(strand_a, strand_b):
if len(strand_a) != len(strand_b):
raise ValueError("Strands must be of equal length.")
- return sum(nucleotide_a != nucleotide_b for nucleotide_a, nucleotide_b in zip(strand_a, strand_b))
+ return sum(nucleotide_a != nucleotide_b for
+ nucleotide_a, nucleotide_b in zip(strand_a, strand_b))
```
-With range:
+With `range`:
```python
def distance(strand_a, strand_b):
if len(strand_a) != len(strand_b):
raise ValueError("Strands must be of equal length.")
- return sum(strand_a[index] != strand_b[index] for index in range(len(strand_a)))
+ return sum(strand_a[index] != strand_b[index] for
+ index in range(len(strand_a)))
```
For more information, check the [sum approach][approach-sum].
@@ -81,5 +83,13 @@ For more information, check the [sum approach][approach-sum].
[approach-range]: https://exercism.org/tracks/python/exercises/hamming/approaches/range
[approach-sum]: https://exercism.org/tracks/python/exercises/hamming/approaches/sum
[approach-zip]: https://exercism.org/tracks/python/exercises/hamming/approaches/zip
-[list-comprehension]: https://docs.python.org/3/tutorial/datastructures.html#list-comprehensions
-[zip]: https://docs.python.org/3/library/functions.html#zip
+[built-in-functions]: https://docs.python.org/3.10/library/functions.html
+[enumerate]: https://docs.python.org/3.10/library/functions.html#enumerate
+[filter]: https://docs.python.org/3.10/library/functions.html#filter
+[generator-expression]: https://docs.python.org/3/reference/expressions.html#grammar-token-python-grammar-generator_expression
+[iterators]: https://docs.python.org/3/glossary.html#term-iterator
+[lambda]: https://docs.python.org/3/glossary.html#term-lambda
+[range]: https://docs.python.org/3.10/library/functions.html#func-range
+[sum]: https://docs.python.org/3/library/functions.html#sum
+[zip]: https://docs.python.org/3.10/library/functions.html#zip
+
diff --git a/exercises/practice/hamming/.approaches/range/content.md b/exercises/practice/hamming/.approaches/range/content.md
index 00c633066f..3f50c2622a 100644
--- a/exercises/practice/hamming/.approaches/range/content.md
+++ b/exercises/practice/hamming/.approaches/range/content.md
@@ -14,24 +14,23 @@ def distance(strand_a, strand_b):
This approach starts by checking if the two strands are of equal length.
If not, a [`ValueError`][value-error] is raised.
-After that is checked, a variable `count` is initialized to 0.
+After that is checked, a `` variable is initialized to 0.
The count variable will be used to keep track of the number of differences between the two strands.
-[`range()`][range] in Python is a built-in function that returns a sequence of numbers.
-Range is an infinite sequence, but it can be limited by providing a `stop` argument.
-The `range()` function can also take a `start` argument and a `step` argument.
-The inputs are built up like this: `range(, stop, )`.
-Since we are only providing a `stop` argument, the `start` argument defaults to 0 and the `step` argument defaults to 1.
+[`range`][range] in Python is a built-in function that returns a sequence of numbers.
+`range` produces an infinite sequence, but it can be limited by providing a `` argument.
+The `range` function can also take a `` argument and a `` argument.
+The inputs are built up like this: `range(, , )`.
+When only a `` argument is provided, `` defaults to 0 and `` defaults to 1.
-We use range to iterate over the indexes of the `strand_a` string.
-We do that by passing the length of the string to the `range()` function by using [`len()`][len].
-The iteration gives us the index of the character in the string.
-We then use that index to access the character in the string.
+We use `range` to iterate over the indexes of the `strand_a` string.
+We do that by passing the length of the string to `range` by using the built-in function [`len`][len].
+Iterating over `range` gives us an index number we can use to access a character in the string.
-Then we compare the character at the index in `strand_a` to the character at the same index in `strand_b`.
-If they are not equal, we increment the `count` variable by 1.
+We can then compare the character at the index in `strand_a` to the character at the same index in `strand_b`.
+If the two values are not equal, we increment the `` variable by 1.
-After the loop is finished, we return the `count` variable.
+After the loop completes, we return the `` variable.
[len]: https://docs.python.org/3/library/functions.html?#len
[range]: https://docs.python.org/3/library/stdtypes.html?#range
diff --git a/exercises/practice/hamming/.approaches/sum/content.md b/exercises/practice/hamming/.approaches/sum/content.md
index f590f14048..9123fa9257 100644
--- a/exercises/practice/hamming/.approaches/sum/content.md
+++ b/exercises/practice/hamming/.approaches/sum/content.md
@@ -1,13 +1,13 @@
# sum
-The benefit of using [`sum()`][sum] is that we can use a list comprehension to create a list of booleans.
-Then we can pass that list to `sum()` and it will add up all the booleans.
+The benefit of using `sum` is that we can use a generator expression to create a list of booleans.
+We can then pass that generator to `sum` and it will iterate through and add up all the booleans.
Where `True` is treated as 1 and `False` is treated as 0.
-Then that sum is returned.
+Then that total is returned.
This can make the code a bit more concise.
-Here is an example using `sum()` and `zip()`:
+Here is an example using `sum` with `zip`:
```python
def distance(strand_a, strand_b):
@@ -16,29 +16,29 @@ def distance(strand_a, strand_b):
return sum(nucleotide_a != nucleotide_b for nucleotide_a, nucleotide_b in zip(strand_a, strand_b))
```
-This approach starts by checking if the two strands are of equal length by using [`len()`][len].
+This approach starts by checking if the two strands are of equal length by using [`len`][len].
If not, a [`ValueError`][value-error] is raised.
-After that is checked, a variable `count` is initialized to 0.
+After that is checked, a `` variable is initialized to 0.
The count variable will be used to keep track of the number of differences between the two strands.
-This approach uses the [`zip()`][zip] function to iterate over two strings.
-You can read more about how to solve this exercise with `zip()` in the [zip approach][approach-zip].
+This approach uses the [`zip`][zip] function to iterate over two strings.
+You can read more about how to solve this exercise with `zip` in the [zip approach][approach-zip].
-What differs in this approach is that we use a list comprehension to create a list of booleans.
-The list comprehension iterates over the tuples returned by `zip()`.
-Then under the iteration so are the tuples unpacked into two variables, `nucleotide_a` and `nucleotide_b`.
-We then compare the characters `nucleotide_a` and `nucleotide_b`.
-If they are not equal, we add `True` to the list.
-If they are equal, we add `False` to the list.
-The list comprehension is then passed to the `sum()` function.
+What differs in this approach is that we use a generator expression to create booleans.
+The generator expression returns an iterator over the tuples returned by `zip`.
+Within the iteration, the tuples are unpacked into two variables, `nucleotide_a` and `nucleotide_b`.
+We can then compare `nucleotide_a` and `nucleotide_b`.
+If they are **not** equal, `True` is produced.
+If they **are** equal, `False` is produced.
+The generator expression is then passed to the `sum` function.
-The [`sum()`][sum] function will add up all the booleans in the list.
+`sum` will then iterate over the generator expression and add up all the booleans.
Where `True` is treated as 1 and `False` is treated as 0.
You can read more about this behavior in [Boolean as numbers][booleans].
-Then that sum is returned.
+Finally the totaled booleans are returned.
-This approach is also doable with range but it is a bit more verbose:
+This approach is also doable with `range` but it is a bit more verbose:
```python
def distance(strand_a, strand_b):
diff --git a/exercises/practice/hamming/.approaches/zip/content.md b/exercises/practice/hamming/.approaches/zip/content.md
index e8bfab7d09..8b4bdd23bc 100644
--- a/exercises/practice/hamming/.approaches/zip/content.md
+++ b/exercises/practice/hamming/.approaches/zip/content.md
@@ -11,21 +11,21 @@ def distance(strand_a, strand_b):
return count
```
-This approach starts by checking if the two strands are of equal length by using [`len()`][len].
+This approach starts by checking if the two strands are of equal length by using [`len`][len].
If not, a [`ValueError`][value-error] is raised.
-After that is checked, a variable `count` is initialized to 0.
+After that is checked, a `` variable is initialized to 0.
The count variable will be used to keep track of the number of differences between the two strands.
-We use [`zip()`][zip] to iterate over the characters in `strand_a` and `strand_b` simultaneously.
-`zip()` is a built in function.
+We use [`zip`][zip] to iterate over the characters in `strand_a` and `strand_b` simultaneously.
+`zip` is a built in function.
It takes any number of iterables and returns an iterator of tuples.
Where the i-th tuple contains the i-th element from each of the argument iterables.
For example, the first tuple will contain the first element from each iterable, the second tuple will contain the second element from each iterable, and so on until the shortest iterable is exhausted.
-In python are strings are iterables.
+In Python, strings are iterable.
-Here is an example of using `zip()` to iterate over two strings:
+Here is an example of using `zip` to iterate over two strings:
```python
>>> zipped = zip("GGACGG", "AGGACG")
@@ -33,14 +33,14 @@ Here is an example of using `zip()` to iterate over two strings:
[('G', 'A'), ('G', 'G'), ('A', 'G'), ('C', 'A'), ('G', 'C'), ('G', 'G')]
```
-We then use the `zip()` iterator to iterate over the tuples.
+We then use the `zip` iterator to iterate over the tuples.
We unpack the tuple into two variables, `nucleotide_a` and `nucleotide_b`.
You can read more about unpacking in the concept [concept:python/unpacking-and-multiple-assignment]().
We then compare the characters `nucleotide_a` and `nucleotide_b`.
-If they are not equal, we increment the `count` variable by 1.
+If they are not equal, we increment the count variable by 1.
-After the loop is finished, we return the `count` variable.
+After the loop is finished, we return the count variable.
[len]: https://docs.python.org/3/library/functions.html?#len
[value-error]: https://docs.python.org/3/library/exceptions.html#ValueError
From 949d146f471e68ecfa75f5b36754b77eaa63fb94 Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Tue, 3 Jan 2023 16:43:48 -0800
Subject: [PATCH 312/826] Update
exercises/practice/hamming/.approaches/introduction.md
Co-authored-by: meatball <69751659+meatball133@users.noreply.github.com>
---
exercises/practice/hamming/.approaches/introduction.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/exercises/practice/hamming/.approaches/introduction.md b/exercises/practice/hamming/.approaches/introduction.md
index 8cf91f229e..3272a8bd05 100644
--- a/exercises/practice/hamming/.approaches/introduction.md
+++ b/exercises/practice/hamming/.approaches/introduction.md
@@ -54,7 +54,7 @@ For more information, check the [zip approach][approach-zip].
## Approach: Using sum
Using the built-in [`sum`][sum] removes the need for a counter variable.
-Removing the counter variable makes the code is more concise.
+Removing the counter variable makes the code more concise.
The examples making use of `sum` also use a [generator expression][generator-expression], although that it is not required.
Using `sum` in this fashion requires a bit more Python knowledge compared to the other approaches.
From e6c795657f01f5217693927fdfce33b859195145 Mon Sep 17 00:00:00 2001
From: Isaac Good
Date: Thu, 29 Dec 2022 22:24:38 -0800
Subject: [PATCH 313/826] Add common exceptions to the TRACEBACK docs.
---
docs/TRACEBACKS.md | 27 +++++++++++++++++++++++++++
1 file changed, 27 insertions(+)
diff --git a/docs/TRACEBACKS.md b/docs/TRACEBACKS.md
index ddda2eb8bd..05cdc2024c 100644
--- a/docs/TRACEBACKS.md
+++ b/docs/TRACEBACKS.md
@@ -54,6 +54,32 @@ ValueError: not enough values to unpack (expected 2, got 1)
Working backwards from the bottom, we see that the call where the exception happened is on line 2 in `my_func`.
We got there by calling `my_func` on line 5.
+## Common Exceptions
+
+Python 3.11 defines 67 [built-in exception classes][exception-hierarchy].
+Here is a brief overview of some of the more common exceptions and what they indicate.
+
+* **SyntaxError**: Python is unable to understand the code as the code has invalid syntax.
+ For example, there may be an open parenthesis without a matching closing parenthesis.
+* **AssertionError**: An `assert` statement (see below) failed.
+* **AttributeError**: The code (or unit test!) tried to access the attribute of an object but that object has no attribute.
+ For example, a unit test excepts a `Robot` object to have a `direction` attribute but when it tried to access `robot.direction`, it does not exist.
+ This could also indicate a typo, such as using `"Hello".lowercase()` when the correct syntax is `"Hello".lower()`.
+ `"Hello".lowercase()` raises `AttributeError: 'str' object has no attribute 'lowercase'`.
+* **ImportError**: The code tried to import something, but it wasn't there.
+ For example, a unit test does `from exercise import Thing` but the `exercise.py` file does not define a `Thing`.
+* **IndexError**: An invalid index was used to look up a value in a list.
+ This often indicates the index is not computed properly and is often an off-by-one error.
+ For example, given `numbers = [1, 2, 3]`, the code `print(numbers[len(numbers)])` will raise an IndexError since `len(numbers)` is 3 but the last valid index is 2.
+ `[1, 2, 3][3]` raises `IndexError: list index out of range`.
+* **KeyError**: Similar to IndexError, this exception is raised when using a key to look up a dictionary value but the key is not set in the dictionary.
+ For example, `{"Alice": 1}["Bob"]` raises `KeyError: 'Bob'`.
+* **TypeError**: Typically, this is raised when the wrong type of data is passed to a function or used in an operation.
+ For example, `"Hello" + 1` will raise `TypeError: can only concatenate str (not "int") to str.
+* **ValueError**: This is usually raised when an invalid value is passed to function.
+ For example, real square roots only exist for position numbers.
+ Calling `math.sqrt(-1)` will raise `ValueError: math domain error`.
+
## Using the `print` function
Sometimes an error is not being raised, but a value is not what is expected.
@@ -305,3 +331,4 @@ print(sum)
[logging]: https://docs.python.org/3/howto/logging.html
[print]: https://www.w3schools.com/python/ref_func_print.asp
[pdb]: https://www.geeksforgeeks.org/python-debugger-python-pdb/
+[exception-hierarchy]: https://docs.python.org/3/library/exceptions.html#exception-hierarchy
From 11d4006f2904daedbaf4bbfe9438552732ad57f6 Mon Sep 17 00:00:00 2001
From: Isaac Good
Date: Thu, 5 Jan 2023 19:52:08 -0800
Subject: [PATCH 314/826] Add examples to all the discussed exceptions
---
docs/TRACEBACKS.md | 348 ++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 326 insertions(+), 22 deletions(-)
diff --git a/docs/TRACEBACKS.md b/docs/TRACEBACKS.md
index 05cdc2024c..7a2adbf669 100644
--- a/docs/TRACEBACKS.md
+++ b/docs/TRACEBACKS.md
@@ -56,29 +56,333 @@ We got there by calling `my_func` on line 5.
## Common Exceptions
-Python 3.11 defines 67 [built-in exception classes][exception-hierarchy].
+Python defines over 60 [built-in exception classes][exception-hierarchy].
Here is a brief overview of some of the more common exceptions and what they indicate.
-* **SyntaxError**: Python is unable to understand the code as the code has invalid syntax.
- For example, there may be an open parenthesis without a matching closing parenthesis.
-* **AssertionError**: An `assert` statement (see below) failed.
-* **AttributeError**: The code (or unit test!) tried to access the attribute of an object but that object has no attribute.
- For example, a unit test excepts a `Robot` object to have a `direction` attribute but when it tried to access `robot.direction`, it does not exist.
- This could also indicate a typo, such as using `"Hello".lowercase()` when the correct syntax is `"Hello".lower()`.
- `"Hello".lowercase()` raises `AttributeError: 'str' object has no attribute 'lowercase'`.
-* **ImportError**: The code tried to import something, but it wasn't there.
- For example, a unit test does `from exercise import Thing` but the `exercise.py` file does not define a `Thing`.
-* **IndexError**: An invalid index was used to look up a value in a list.
- This often indicates the index is not computed properly and is often an off-by-one error.
- For example, given `numbers = [1, 2, 3]`, the code `print(numbers[len(numbers)])` will raise an IndexError since `len(numbers)` is 3 but the last valid index is 2.
- `[1, 2, 3][3]` raises `IndexError: list index out of range`.
-* **KeyError**: Similar to IndexError, this exception is raised when using a key to look up a dictionary value but the key is not set in the dictionary.
- For example, `{"Alice": 1}["Bob"]` raises `KeyError: 'Bob'`.
-* **TypeError**: Typically, this is raised when the wrong type of data is passed to a function or used in an operation.
- For example, `"Hello" + 1` will raise `TypeError: can only concatenate str (not "int") to str.
-* **ValueError**: This is usually raised when an invalid value is passed to function.
- For example, real square roots only exist for position numbers.
- Calling `math.sqrt(-1)` will raise `ValueError: math domain error`.
+### **SyntaxError**
+
+Python raises a `SyntaxError` when it is unable to understand the code due to invalid syntax.
+For example, there may be an open parenthesis without a matching closing parenthesis.
+
+
+
+Click here for a code example.
+
+
+Running this code:
+
+```python
+def distance(strand_a, strand_b):
+ if len(strand_a) != len(strand_b):
+ raise ValueError("Strands must be of equal length." # This is missing the closing parenthesis
+```
+
+will result in a stack trace similar to this (_note the message on the final line_):
+
+```
+.usr.local.lib.python3.10.site-packages._pytest.python.py:608: in _importtestmodule
+ mod = import_path(self.path, mode=importmode, root=self.config.rootpath)
+.usr.local.lib.python3.10.site-packages._pytest.pathlib.py:533: in import_path
+ importlib.import_module(module_name)
+.usr.local.lib.python3.10.importlib.__init__.py:126: in import_module
+ return _bootstrap._gcd_import(name[level:], package, level)
+:1050: in _gcd_import ???
+:1027: in _find_and_load ???
+:1006: in _find_and_load_unlocked ???
+:688: in _load_unlocked ???
+.usr.local.lib.python3.10.site-packages._pytest.assertion.rewrite.py:168: in exec_module
+ exec(co, module.__dict__)
+.mnt.exercism-iteration.hamming_test.py:3: in
+ from hamming import (
+E File ".mnt.exercism-iteration.hamming.py", line 10
+E raise ValueError("Strands must be of equal length."
+E ^
+E SyntaxError: '(' was never closed
+```
+
+
+
+
+
+### **AssertionError**
+Python raises an `AssertionError` when an `assert` statement (see below) fails.
+
+
+
+Click here for a code example.
+
+
+Running this code:
+
+```python
+def distance(strand_a, strand_b):
+ assert len(strand_a) == len(strand_b)
+
+
+distance("ab", "abc")
+```
+
+will result in a stack trace similar to this (_note the message on the final line_):
+
+```
+hamming_test.py:3: in
+ from hamming import (
+hamming.py:5: in
+ distance("ab", "abc")
+hamming.py:2: in distance
+ assert len(strand_a) == len(strand_b)
+E AssertionError
+```
+
+
+
+
+
+### **AttributeError**
+An `AttributeError` is raised when code (or a unit test!) tries to access the attribute of an object but that object has no such attribute.
+For example, a unit test excepts a `Robot` object to have a `direction` attribute but when it tried to access `robot.direction`, it does not exist.
+
+This could also indicate a typo, such as using `"Hello".lowercase()` when the correct syntax is `"Hello".lower()`.
+`"Hello".lowercase()` raises `AttributeError: 'str' object has no attribute 'lowercase'`.
+
+
+
+Click here for a Robot class example.
+
+
+Running this code:
+
+```python
+class Robot:
+ def __init__():
+ #note that there is no self.direction listed here
+ self.position = (0, 0)
+ self.orientation = 'SW'
+
+ def forward():
+ pass
+
+
+robby = Robot
+robby.direction
+```
+
+will result in a stack trace similar to this (_note the message on the final line_):
+
+```
+robot_simulator_test.py:3: in
+ from robot_simulator import (
+robot_simulator.py:12: in
+ robby.direction
+E AttributeError: type object 'Robot' has no attribute 'direction'
+```
+
+
+
+
+
+
+Click here for a string example.
+
+
+Running this code:
+
+```python
+def distance(strand_a, strand_b):
+ if strand_a.lowercase() == strand_b:
+ return 0
+
+
+distance("ab", "abc")
+```
+
+will result in a stack trace similar to this (_note the message on the final line_):
+
+```
+ def distance(strand_a, strand_b):
+> if strand_a.lowercase() == strand_b:
+E AttributeError: 'str' object has no attribute 'lowercase'
+```
+
+
+
+
+
+
+### **ImportError**
+An `ImportError` is raised when code tries to import something, but Python is unable to do so.
+For example, a unit test for `Guidos Gorgeous Lasagna` does `from lasagna import bake_time_remaining`, but the `lasgana.py` solution file might not define `bake_time_remaining`.
+
+
+
+Click here for code example
+
+
+Running the `lasgana.py` file without the defined function would result in the following error:
+
+```python
+We received the following error when we ran your code:
+
+ ImportError while importing test module '.mnt.exercism-iteration.lasagna_test.py'.
+Hint: make sure your test modules.packages have valid Python names.
+
+Traceback:
+.mnt.exercism-iteration.lasagna_test.py:6: in
+ from lasagna import (EXPECTED_BAKE_TIME,
+E ImportError: cannot import name 'bake_time_remaining' from 'lasagna' (.mnt.exercism-iteration.lasagna.py)
+
+During handling of the above exception, another exception occurred:
+.usr.local.lib.python3.10.importlib.__init__.py:126: in import_module
+ return _bootstrap._gcd_import(name[level:], package, level)
+.mnt.exercism-iteration.lasagna_test.py:23: in
+ raise ImportError("In your 'lasagna.py' file, we can not find or import the"
+
+E ImportError: In your 'lasagna.py' file, we can not find or import the function named 'bake_time_remaining()'. Did you mis-name or forget to define it?
+```
+
+### **IndexError**
+
+Python raises an `IndexError` when an invalid index is used to look up a value in a list.
+This often indicates the index is not computed properly and is often an off-by-one error.
+
+
+
+Click here for code example
+
+
+Consider the following code.
+
+```python
+def distance(strand_a, strand_b):
+ same = 0
+ for i in range(len(strand_a)):
+ if strand_a[i] == strand_b[i]:
+ same += 1
+ return same
+
+
+distance("abc", "ab") # Note the first strand is longer than the second strand.
+```
+
+Running that code will result in an error similar to this one.
+(_Note the last line._)
+
+```
+hamming_test.py:3: in
+ from hamming import (
+hamming.py:9: in
+ distance("abc", "ab") # Note the first strand is longer than the second strand.
+hamming.py:4: in distance
+ if strand_a[i] == strand_b[i]:
+E IndexError: string index out of range
+```
+
+
+
+
+
+### **KeyError**
+Similar to `IndexError`, this exception is raised when using a key to look up a dictionary value but the key is not set in the dictionary.
+
+
+
+Click here for code example
+
+
+Consider the following code.
+
+```python
+def to_rna(dna_letter):
+ translation = {"G": "C", "C": "G", "A": "U", "T": "A"}
+ return translation[dna_letter]
+
+
+print(to_rna("Q")) # Note, "Q" is not in the translation.
+```
+
+Running that code will result in an error similar to this one.
+(_Note the last line._)
+
+```
+rna_transcription_test.py:3: in
+ from rna_transcription import to_rna
+rna_transcription.py:6: in
+ print(to_rna("Q"))
+rna_transcription.py:3: in to_rna
+ return translation[dna_letter]
+E KeyError: 'Q'
+```
+
+
+
+
+### **TypeError**
+Typically, a `TypeError` is raised when the wrong type of data is passed to a function or used in an operation.
+
+
+
+
+Click here for code example
+
+
+Consider the following code.
+
+```python
+def hello(name): # This function expects a string.
+ return 'Hello, ' + name + '!'
+
+
+print(hello(100)) # 100 is not a string.
+```
+
+Running that code will result in an error similar to this one.
+(_Note the last line._)
+
+```
+hello_world_test.py:3: in
+ import hello_world
+hello_world.py:5: in
+ print(hello(100))
+hello_world.py:2: in hello
+ return 'Hello, ' + name + '!'
+E TypeError: can only concatenate str (not "int") to str
+```
+
+
+
+
+
+### **ValueError**
+A `ValueError` is usually raised when an invalid value is passed to function.
+
+Click here for code example
+
+
+Note, real square roots only exist for position numbers.
+Calling `math.sqrt(-1)` will raise `ValueError: math domain error` since `-1` is not a valid value for a square root.
+In (mathematical) technical terms, -1 is not in the domain of square roots.
+
+
+```python
+import math
+
+math.sqrt(-1)
+```
+
+Running that code will result in an error similar to this one.
+(_Note the last line._)
+
+```
+square_root_test.py:3: in
+ from square_root import (
+square_root.py:3: in
+ math.sqrt(-1)
+E ValueError: math domain error
+```
+
+
+
+
## Using the `print` function
@@ -302,7 +606,7 @@ Here is an example of how to use the above debugger commands based on the code e
...
```
-In Python 3.7+ there is an easier way to create breakpoints
+In Python 3.7+ there is an easier way to create breakpoints.
Simply writing `breakpoint()` where needed will create one.
```python
From 6bdb1aede8cf75c240c8d9bee2d0c5cf1b2ce4b0 Mon Sep 17 00:00:00 2001
From: Isaac Good
Date: Thu, 5 Jan 2023 20:53:39 -0800
Subject: [PATCH 315/826] Apply suggestions from code review
Co-authored-by: BethanyG
---
docs/TRACEBACKS.md | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/docs/TRACEBACKS.md b/docs/TRACEBACKS.md
index 7a2adbf669..974b7753d4 100644
--- a/docs/TRACEBACKS.md
+++ b/docs/TRACEBACKS.md
@@ -140,7 +140,7 @@ E AssertionError
### **AttributeError**
An `AttributeError` is raised when code (or a unit test!) tries to access the attribute of an object but that object has no such attribute.
-For example, a unit test excepts a `Robot` object to have a `direction` attribute but when it tried to access `robot.direction`, it does not exist.
+For example, a unit test expects a `Robot` object to have a `direction` attribute, but when it tried to access `robot.direction`, it does not exist.
This could also indicate a typo, such as using `"Hello".lowercase()` when the correct syntax is `"Hello".lower()`.
`"Hello".lowercase()` raises `AttributeError: 'str' object has no attribute 'lowercase'`.
@@ -242,7 +242,7 @@ E ImportError: In your 'lasagna.py' file, we can not find or import the functi
### **IndexError**
-Python raises an `IndexError` when an invalid index is used to look up a value in a list.
+Python raises an `IndexError` when an invalid index is used to look up a value in a sequence.
This often indicates the index is not computed properly and is often an off-by-one error.
@@ -354,11 +354,13 @@ E TypeError: can only concatenate str (not "int") to str
### **ValueError**
A `ValueError` is usually raised when an invalid value is passed to function.
+
+
Click here for code example
-Note, real square roots only exist for position numbers.
+Note, real square roots only exist for positive numbers.
Calling `math.sqrt(-1)` will raise `ValueError: math domain error` since `-1` is not a valid value for a square root.
In (mathematical) technical terms, -1 is not in the domain of square roots.
From 5d9319abe322bfbeb2c70c617f67c6e6398d308f Mon Sep 17 00:00:00 2001
From: Isaac Good
Date: Thu, 5 Jan 2023 21:03:49 -0800
Subject: [PATCH 316/826] Update docs/TRACEBACKS.md
Co-authored-by: BethanyG
---
docs/TRACEBACKS.md | 1 -
1 file changed, 1 deletion(-)
diff --git a/docs/TRACEBACKS.md b/docs/TRACEBACKS.md
index 974b7753d4..4bdf92cc67 100644
--- a/docs/TRACEBACKS.md
+++ b/docs/TRACEBACKS.md
@@ -238,7 +238,6 @@ During handling of the above exception, another exception occurred:
raise ImportError("In your 'lasagna.py' file, we can not find or import the"
E ImportError: In your 'lasagna.py' file, we can not find or import the function named 'bake_time_remaining()'. Did you mis-name or forget to define it?
-```
### **IndexError**
From ddd0c0aad1f31b654511602ebabf717693ca4805 Mon Sep 17 00:00:00 2001
From: Carl
Date: Sat, 31 Dec 2022 15:49:11 +0100
Subject: [PATCH 317/826] Started
---
.../.approaches/config.json | 22 ++++
.../.approaches/introduction.md | 120 +++++++++++++++++
.../nested-for-loop-optimized/content.md | 93 +++++++++++++
.../nested-for-loop-optimized/snippet.txt | 5 +
.../.approaches/nested-for-loop/content.md | 76 +++++++++++
.../.approaches/nested-for-loop/snippet.txt | 5 +
.../palindrome-products/.articles/config.json | 11 ++
.../.articles/performance/code/Benchmark.py | 123 ++++++++++++++++++
.../.articles/performance/content.md | 36 +++++
.../.articles/performance/snippet.md | 6 +
10 files changed, 497 insertions(+)
create mode 100644 exercises/practice/palindrome-products/.approaches/config.json
create mode 100644 exercises/practice/palindrome-products/.approaches/introduction.md
create mode 100644 exercises/practice/palindrome-products/.approaches/nested-for-loop-optimized/content.md
create mode 100644 exercises/practice/palindrome-products/.approaches/nested-for-loop-optimized/snippet.txt
create mode 100644 exercises/practice/palindrome-products/.approaches/nested-for-loop/content.md
create mode 100644 exercises/practice/palindrome-products/.approaches/nested-for-loop/snippet.txt
create mode 100644 exercises/practice/palindrome-products/.articles/config.json
create mode 100644 exercises/practice/palindrome-products/.articles/performance/code/Benchmark.py
create mode 100644 exercises/practice/palindrome-products/.articles/performance/content.md
create mode 100644 exercises/practice/palindrome-products/.articles/performance/snippet.md
diff --git a/exercises/practice/palindrome-products/.approaches/config.json b/exercises/practice/palindrome-products/.approaches/config.json
new file mode 100644
index 0000000000..bcd2aca80d
--- /dev/null
+++ b/exercises/practice/palindrome-products/.approaches/config.json
@@ -0,0 +1,22 @@
+{
+ "introduction": {
+ "authors": ["meatball133", "bethanyg"],
+ "contributors": []
+ },
+ "approaches": [
+ {
+ "uuid": "7d26e763-daa0-4396-bc7b-7624fbc2ac5c",
+ "slug": "nested-for-loop",
+ "title": "Nested for loop",
+ "blurb": "Use a nested for loop",
+ "authors": ["meatball133", "bethanyg"]
+ },
+ {
+ "uuid": "e6495842-a557-43db-b051-b99ffba03f00",
+ "slug": "nested-for-loop-optimized",
+ "title": "Nested for loop optimized",
+ "blurb": "Nested for loop optimized edition",
+ "authors": ["meatball133", "bethanyg"]
+ }
+ ]
+}
diff --git a/exercises/practice/palindrome-products/.approaches/introduction.md b/exercises/practice/palindrome-products/.approaches/introduction.md
new file mode 100644
index 0000000000..5622723d71
--- /dev/null
+++ b/exercises/practice/palindrome-products/.approaches/introduction.md
@@ -0,0 +1,120 @@
+# Introduction
+
+There are various approaches to solving this problem.
+These approaches show 2 common ways to solve this problem.
+With one being a lot more efficient than the other.
+Although with that being said there are other ways to solve this problem.
+
+## General guidance
+
+The goal of this exercise is to generate the largest and smallest palindromes from a given range of numbers.
+
+## Approach: Using a nested for loop
+
+A nested for loop is a good approach to this problem.
+It is simple and easy to understand and it works.
+
+```python
+def largest(min_factor, max_factor):
+ if min_factor > max_factor:
+ raise ValueError("min must be <= max")
+ result = 0
+ answer = []
+ for number_a in range(min_factor, max_factor+1):
+ for number_b in range(min_factor, max_factor+1):
+ if number_a * number_b >= result:
+ test_value = str(number_a * number_b)
+ if test_value == test_value[::-1]:
+ if number_a * number_b > result:
+ answer = []
+ result = int(test_value)
+ answer.append([number_a, number_b])
+ if result == 0:
+ result = None
+ return (result, answer)
+
+
+def smallest(min_factor, max_factor):
+ if min_factor > max_factor:
+ raise ValueError("min must be <= max")
+ result = 0
+ answer = []
+ for number_a in range(min_factor, max_factor+1):
+ for number_b in range(min_factor, max_factor+1):
+ if number_a * number_b <= result or result == 0:
+ test_value = str(number_a * number_b)
+ if test_value == test_value[::-1]:
+ if number_a * number_b < result:
+ answer = []
+ result = int(test_value)
+ answer.append([number_a, number_b])
+ if result == 0:
+ result = None
+ return (result, answer)
+```
+
+For more information, check the [Nested for loop approach][approach-nested-for-loop].
+
+## Approach: Using a nested for loop, optimized edition
+
+This approach is similar to the previous one, but what if I say that with adding a few lines of code, we can make it faster?
+Then you might say, how much faster?
+Well, for some inputs it reduces the time from 9 minutes to 0.01 seconds.
+You can read more about it in the [Performance article][article-performance].
+
+```python
+def largest(min_factor, max_factor):
+ if min_factor > max_factor:
+ raise ValueError("min must be <= max")
+ result = 0
+ answer = []
+ for number_a in range(max_factor, min_factor - 1,-1):
+ was_bigger = False
+ for number_b in range(max_factor, number_a - 1, -1):
+ if number_a * number_b >= result:
+ was_bigger = True
+ test_value = str(number_a * number_b)
+ if test_value == test_value[::-1]:
+ if number_a * number_b > result:
+ answer = []
+ result = int(test_value)
+ answer.append([number_a, number_b])
+ if not was_bigger:
+ break
+ if result == 0:
+ result = None
+ return (result, answer)
+
+
+def smallest(min_factor, max_factor):
+ if min_factor > max_factor:
+ raise ValueError("min must be <= max")
+ result = 0
+ answer = []
+ for number_a in range(min_factor, max_factor+1):
+ was_smaller = False
+ for number_b in range(min_factor, max_factor+1):
+ if number_a * number_b <= result or result == 0:
+ was_smaller = True
+ test_value = str(number_a * number_b)
+ if test_value == test_value[::-1]:
+ if number_a * number_b < result:
+ answer = []
+ result = int(test_value)
+ answer.append([number_a, number_b])
+ if not was_smaller:
+ break
+ if result == 0:
+ result = None
+ return (result, answer)
+```
+
+You can read more about how to achieve this optimization in: [Nested for loop optimized][approach-nested-for-loop-optimized].
+
+## Benchmark
+
+For more information, check the [Performance article][article-performance].
+
+[approach-nested-for-loop]: https://exercism.org/tracks/python/exercises/palindrome-products/approaches/nested-for-loop
+[approach-nested-for-loop-optimized]: https://exercism.org/tracks/python/exercises/palindrome-products/approaches/nested-for-loop-optimized
+[article-performance]: https://exercism.org/tracks/python/exercises/palindrome-products/articles/performance
diff --git a/exercises/practice/palindrome-products/.approaches/nested-for-loop-optimized/content.md b/exercises/practice/palindrome-products/.approaches/nested-for-loop-optimized/content.md
new file mode 100644
index 0000000000..9c28157db7
--- /dev/null
+++ b/exercises/practice/palindrome-products/.approaches/nested-for-loop-optimized/content.md
@@ -0,0 +1,93 @@
+# Nested For Loop Optimized
+
+The point of this approach is to show with just a few changes, the runtime can be improved by a lot.
+
+```python
+def largest(min_factor, max_factor):
+ if min_factor > max_factor:
+ raise ValueError("min must be <= max")
+ result = 0
+ answer = []
+ for number_a in range(max_factor, min_factor - 1,-1):
+ was_bigger = False
+ for number_b in range(max_factor, number_a - 1, -1):
+ if number_a * number_b >= result:
+ was_bigger = True
+ test_value = str(number_a * number_b)
+ if test_value == test_value[::-1]:
+ if number_a * number_b > result:
+ answer = []
+ result = int(test_value)
+ answer.append([number_a, number_b])
+ if not was_bigger:
+ break
+ if result == 0:
+ result = None
+ return (result, answer)
+
+
+def smallest(min_factor, max_factor):
+ if min_factor > max_factor:
+ raise ValueError("min must be <= max")
+ result = 0
+ answer = []
+ for number_a in range(min_factor, max_factor+1):
+ was_smaller = False
+ for number_b in range(min_factor, max_factor+1):
+ if number_a * number_b <= result or result == 0:
+ was_smaller = True
+ test_value = str(number_a * number_b)
+ if test_value == test_value[::-1]:
+ if number_a * number_b < result:
+ answer = []
+ result = int(test_value)
+ answer.append([number_a, number_b])
+ if not was_smaller:
+ break
+ if result == 0:
+ result = None
+ return (result, answer)
+```
+
+This approach is very similar to the [nested for loop approach][approach-nested-for-loop], but it has a few optimizations that make it faster.
+To optimize the largest function, we have to start the inner loop from the maximum factor and go down to the minimum factor.
+This way, we can stop the inner loop earlier than before.
+We also in the inner loop set the minimum value to the current value of the outer loop.
+
+Here is an example of how the algorithm work and why we have to modify the loops.
+Say we take max to be 99 and min 10.
+It would go first round:
+
+```
+x => [99 , 98, 97 ...]
+y => [99]
+```
+
+And already here we have our result which is `9009[91,99]`
+Although the loops has to continue to make us sure there are no higher value.
+
+```
+x => [98, 97, 96 ...]
+y => [99, 98]
+...
+x => [90, 89, 88 ...]
+y => [99, 98,97,96,95,94,93,92,91,90]
+```
+
+Here we can see that the highest value for this "run" is 90 \* 99 = 8910.
+Meaning running beyond this point wont give us any higher value than 9009.
+
+That is why we introduce the `was_bigger` variable.
+With that variable we can check the inner loop if it has been bigger than the current result.
+If there has not been a bigger value, we can stop the inner loop and stop the outer loop.
+We do that by using the [`break`][break] statement.
+
+If we hadn't modified the inner loop, it would have started from the minimum factor and gone up to the maximum factor.
+This would mean that for every new run in the outer loop, the values would be bigger than the previous run.
+
+The smallest function is optimized in a similar way as largest function compared to the original approach.
+The only difference is that we have to start the inner loop and outer loop from the minimum factor and go up to the maximum factor.
+Since what we want is the smallest value, we have to start from the smallest value and go up to the biggest value.
+
+[approach-nested-for-loop]: https://exercism.org/tracks/python/exercises/palindrome-products/approaches/nested-for-loop
+[break]: https://docs.python.org/3/tutorial/controlflow.html#break-and-continue-statements-and-else-clauses-on-loops
diff --git a/exercises/practice/palindrome-products/.approaches/nested-for-loop-optimized/snippet.txt b/exercises/practice/palindrome-products/.approaches/nested-for-loop-optimized/snippet.txt
new file mode 100644
index 0000000000..bf91dd6753
--- /dev/null
+++ b/exercises/practice/palindrome-products/.approaches/nested-for-loop-optimized/snippet.txt
@@ -0,0 +1,5 @@
+for number_a in range(max_factor, min_factor - 1,-1):
+ was_bigger = False
+ for number_b in range(max_factor, number_a - 1, -1):
+ if number_a * number_b >= result:
+ was_bigger = True
diff --git a/exercises/practice/palindrome-products/.approaches/nested-for-loop/content.md b/exercises/practice/palindrome-products/.approaches/nested-for-loop/content.md
new file mode 100644
index 0000000000..b2fe86d0ac
--- /dev/null
+++ b/exercises/practice/palindrome-products/.approaches/nested-for-loop/content.md
@@ -0,0 +1,76 @@
+# Nested For Loop
+
+```python
+def largest(min_factor, max_factor):
+ if min_factor > max_factor:
+ raise ValueError("min must be <= max")
+ result = 0
+ answer = []
+ for number_a in range(min_factor, max_factor+1):
+ for number_b in range(min_factor, max_factor+1):
+ if number_a * number_b >= result:
+ test_value = str(number_a * number_b)
+ if test_value == test_value[::-1]:
+ if number_a * number_b > result:
+ answer = []
+ result = int(test_value)
+ answer.append([number_a, number_b])
+ if result == 0:
+ result = None
+ return (result, answer)
+
+
+def smallest(min_factor, max_factor):
+ if min_factor > max_factor:
+ raise ValueError("min must be <= max")
+ result = 0
+ answer = []
+ for number_a in range(min_factor, max_factor+1):
+ for number_b in range(min_factor, max_factor+1):
+ if number_a * number_b <= result or result == 0:
+ test_value = str(number_a * number_b)
+ if test_value == test_value[::-1]:
+ if number_a * number_b < result:
+ answer = []
+ result = int(test_value)
+ answer.append([number_a, number_b])
+ if result == 0:
+ result = None
+ return (result, answer)
+```
+
+The largest and smallest functions are very similar.
+Although there are some small differences.
+
+The largest function starts with checking if the min_factor is bigger than the max_factor.
+If it is, it raises a [`ValueError`][value-error].
+
+After that, it initializes the result to 0 and the answer to an empty list.
+Then it starts `for in range` loop.
+The first for loop iterates over the range of numbers from min_factor to max_factor.
+The second for loop iterates over the same range of numbers.
+Then it checks if the product of the two numbers is bigger than the result.
+If it is, it converts the product to a string and checks if it is a palindrome.
+
+We can check if a string is a palindrome by comparing it to its reverse.
+There are multiple ways to reverse a string.
+The most common way is to use the slice notation.
+The slice notation is `string[start:stop:step]`.
+You can read more about it in: [reverse string with slice][string-reverse].
+
+If it is, it checks if the product is bigger than the result.
+If it is, it clears the answer list and sets the result to the product.
+Then it appends the two numbers to the answer list.
+
+After the two for loops, it checks if the result is 0.
+If it is, it sets the result to [`None`][none].
+Then it returns the result and the answer.
+
+The smallest one is very similar.
+The differences are that it checks if the product is smaller than the result or if the result is 0.
+And that it reset result outside of the if statement.
+Instead of inside of it.
+
+[none]: https://realpython.com/null-in-python/
+[string-reverse]: https://realpython.com/reverse-string-python/#reversing-strings-through-slicing
+[value-error]: https://docs.python.org/3/library/exceptions.html#ValueError
diff --git a/exercises/practice/palindrome-products/.approaches/nested-for-loop/snippet.txt b/exercises/practice/palindrome-products/.approaches/nested-for-loop/snippet.txt
new file mode 100644
index 0000000000..f678cd824a
--- /dev/null
+++ b/exercises/practice/palindrome-products/.approaches/nested-for-loop/snippet.txt
@@ -0,0 +1,5 @@
+ for number_a in range(min_factor, max_factor+1):
+ for number_b in range(min_factor, max_factor+1):
+ if number_a * number_b >= result:
+ test_value = str(number_a * number_b)
+ if test_value == test_value[::-1]:
diff --git a/exercises/practice/palindrome-products/.articles/config.json b/exercises/practice/palindrome-products/.articles/config.json
new file mode 100644
index 0000000000..2cf84af89d
--- /dev/null
+++ b/exercises/practice/palindrome-products/.articles/config.json
@@ -0,0 +1,11 @@
+{
+ "articles": [
+ {
+ "uuid": "c90d5c22-3133-4f1b-88b6-3ea9b6df298e",
+ "slug": "performance",
+ "title": "Performance deep dive",
+ "blurb": "Deep dive to find out the performance between different approaches",
+ "authors": ["meatball133", "bethanyg"]
+ }
+ ]
+}
diff --git a/exercises/practice/palindrome-products/.articles/performance/code/Benchmark.py b/exercises/practice/palindrome-products/.articles/performance/code/Benchmark.py
new file mode 100644
index 0000000000..9bfbbf35b3
--- /dev/null
+++ b/exercises/practice/palindrome-products/.articles/performance/code/Benchmark.py
@@ -0,0 +1,123 @@
+import timeit
+import sys
+
+
+print(sys.version)
+
+
+def largest(min_factor, max_factor):
+ if min_factor > max_factor:
+ raise ValueError("min must be <= max")
+ result = 0
+ answer = []
+ for number_a in range(min_factor, max_factor+1):
+ for number_b in range(min_factor, max_factor+1):
+ if number_a * number_b >= result:
+ test_value = str(number_a * number_b)
+ if test_value == test_value[::-1]:
+ if number_a * number_b > result:
+ answer = []
+ result = int(test_value)
+ answer.append([number_a, number_b])
+ if result == 0:
+ result = None
+ return (result, answer)
+
+
+def smallest(min_factor, max_factor):
+ if min_factor > max_factor:
+ raise ValueError("min must be <= max")
+ result = 0
+ answer = []
+ for number_a in range(min_factor, max_factor+1):
+ for number_b in range(min_factor, max_factor+1):
+ if number_a * number_b <= result or result == 0:
+ test_value = str(number_a * number_b)
+ if test_value == test_value[::-1]:
+ if number_a * number_b < result:
+ answer = []
+ result = int(test_value)
+ answer.append([number_a, number_b])
+ if result == 0:
+ result = None
+ return (result, answer)
+
+def largest_optimized(min_factor, max_factor):
+ if min_factor > max_factor:
+ raise ValueError("min must be <= max")
+ result = 0
+ answer = []
+ for number_a in range(max_factor, min_factor - 1,-1):
+ was_bigger = False
+ for number_b in range(max_factor, number_a - 1, -1):
+ if number_a * number_b >= result:
+ was_bigger = True
+ test_value = str(number_a * number_b)
+ if test_value == test_value[::-1]:
+ if number_a * number_b > result:
+ answer = []
+ result = int(test_value)
+ answer.append([number_a, number_b])
+ if not was_bigger:
+ break
+ if result == 0:
+ result = None
+ return (result, answer)
+
+
+def smallest_optimized(min_factor, max_factor):
+ if min_factor > max_factor:
+ raise ValueError("min must be <= max")
+ result = 0
+ answer = []
+ for number_a in range(min_factor, max_factor+1):
+ was_smaller = False
+ for number_b in range(min_factor, max_factor+1):
+ if number_a * number_b <= result or result == 0:
+ was_smaller = True
+ test_value = str(number_a * number_b)
+ if test_value == test_value[::-1]:
+ if number_a * number_b < result:
+ answer = []
+ result = int(test_value)
+ answer.append([number_a, number_b])
+ if not was_smaller:
+ break
+ if result == 0:
+ result = None
+ return (result, answer)
+
+
+starttime = timeit.default_timer()
+largest(1, 1000)
+print("largest, min=1, max=1000 :", timeit.default_timer() - starttime)
+
+starttime = timeit.default_timer()
+largest_optimized(1, 1000)
+print("largest_optimized, min=1, max=1000 :", timeit.default_timer() - starttime)
+
+starttime = timeit.default_timer()
+smallest(1, 1000)
+print("smallest, min=1, max=1000 :", timeit.default_timer() - starttime)
+
+starttime = timeit.default_timer()
+smallest_optimized(1, 1000)
+print("smallest_optimized, min=1, max=1000 :", timeit.default_timer() - starttime)
+
+
+
+starttime = timeit.default_timer()
+largest(100, 100000)
+print("largest, min=100, max=100000 :", timeit.default_timer() - starttime)
+
+starttime = timeit.default_timer()
+largest_optimized(100, 100000)
+print("largest_optimized, min=100, max=100000 :", timeit.default_timer() - starttime)
+
+starttime = timeit.default_timer()
+smallest(100, 100000)
+print("smallest, min=100, max=100000 :", timeit.default_timer() - starttime)
+
+starttime = timeit.default_timer()
+smallest_optimized(100, 100000)
+print("smallest_optimized, min=100, max=100000 :", timeit.default_timer() - starttime)
diff --git a/exercises/practice/palindrome-products/.articles/performance/content.md b/exercises/practice/palindrome-products/.articles/performance/content.md
new file mode 100644
index 0000000000..f0bce43e18
--- /dev/null
+++ b/exercises/practice/palindrome-products/.articles/performance/content.md
@@ -0,0 +1,36 @@
+# Performance
+
+In this approach, we'll find out the performance difference between approaches for palindrome product in Python.
+
+The [approaches page][approaches] lists two approaches to this exercise:
+
+1. [Using a nested for loop][approach-nested-for-loop]
+2. [Using a nested for loop, optimized edition][approach-nested-for-loop-optimized]
+
+## Benchmarks
+
+To benchmark the approaches, we wrote a [small benchmark application][benchmark-application] using the [`timeit`][timeit] library.
+These test were run in windows 11, using Python 3.11.1.
+
+```
+largest, min=1, max=1000 : 0.05235219991300255
+largest_optimized, min=1, max=1000 : 0.000758000067435205
+smallest, min=1, max=1000 : 0.04553140001371503
+smallest_optimized, min=1, max=1000 : 0.00010269996710121632
+
+largest, min=100, max=100000 : 512.5731259000022
+largest_optimized, min=100, max=100000 : 0.013197900028899312
+smallest, min=100, max=100000 : 549.5989698000485
+smallest_optimized, min=100, max=100000 : 0.03933039994444698
+```
+
+## Conclusion
+
+As we can see, the optimized approach is much faster than the original approach.
+Although the difference becomes most noticeable when the range is large, the optimized approach is still faster in the small range.
+
+[approaches]: https://exercism.org/tracks/python/exercises/palindrome-products/approaches
+[approach-nested-for-loop]: https://exercism.org/tracks/python/exercises/palindrome-products/approaches/nested-for-loop
+[approach-nested-for-loop-optimized]: https://exercism.org/tracks/python/exercises/palindrome-products/approaches/nested-for-loop-optimized
+[benchmark-application]: https://github.com/exercism/python/blob/main/exercises/practice/palindrome-products/.articles/performance/code/Benchmark.py
+[timeit]: https://docs.python.org/3/library/timeit.html
diff --git a/exercises/practice/palindrome-products/.articles/performance/snippet.md b/exercises/practice/palindrome-products/.articles/performance/snippet.md
new file mode 100644
index 0000000000..da2648a6a9
--- /dev/null
+++ b/exercises/practice/palindrome-products/.articles/performance/snippet.md
@@ -0,0 +1,6 @@
+```
+largest, min=100, max=100000 : 512.5731259000022
+largest_optimized, min=100, max=100000 : 0.013197900028899312
+smallest, min=100, max=100000 : 549.5989698000485
+smallest_optimized, min=100, max=100000 : 0.03933039994444698
+```
From dfd47334201c64edee2540460fb711d356f58e22 Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Fri, 6 Jan 2023 17:06:21 -0800
Subject: [PATCH 318/826] Apply suggestions from code review
Suggestions and nits.
---
.../.approaches/introduction.md | 13 +++---
.../nested-for-loop-optimized/content.md | 41 +++++++++----------
.../.approaches/nested-for-loop/content.md | 19 +++++----
.../.articles/performance/content.md | 5 ++-
4 files changed, 39 insertions(+), 39 deletions(-)
diff --git a/exercises/practice/palindrome-products/.approaches/introduction.md b/exercises/practice/palindrome-products/.approaches/introduction.md
index 5622723d71..2637f1ae4c 100644
--- a/exercises/practice/palindrome-products/.approaches/introduction.md
+++ b/exercises/practice/palindrome-products/.approaches/introduction.md
@@ -1,9 +1,8 @@
# Introduction
-There are various approaches to solving this problem.
-These approaches show 2 common ways to solve this problem.
-With one being a lot more efficient than the other.
-Although with that being said there are other ways to solve this problem.
+There are various ways to solve `palindrome-products`.
+This approaches document shows 2 _common_ strategies, with one being a lot more efficient than the other.
+That being said, neither approach here is considered canonical, and other "pythonic" approaches could be added/expanded on in the future.
## General guidance
@@ -57,9 +56,9 @@ For more information, check the [Nested for loop approach][approach-nested-for-l
## Approach: Using a nested for loop, optimized edition
-This approach is similar to the previous one, but what if I say that with adding a few lines of code, we can make it faster?
-Then you might say, how much faster?
-Well, for some inputs it reduces the time from 9 minutes to 0.01 seconds.
+This approach is similar to the previous one, but with the addition of a few lines of code we can make it much faster.
+The question then becomes _how much faster_?
+Well, for some input sizes it reduces the time from **9 minutes to 0.01 seconds**.
You can read more about it in the [Performance article][article-performance].
```python
diff --git a/exercises/practice/palindrome-products/.approaches/nested-for-loop-optimized/content.md b/exercises/practice/palindrome-products/.approaches/nested-for-loop-optimized/content.md
index 9c28157db7..2bc2718391 100644
--- a/exercises/practice/palindrome-products/.approaches/nested-for-loop-optimized/content.md
+++ b/exercises/practice/palindrome-products/.approaches/nested-for-loop-optimized/content.md
@@ -1,6 +1,6 @@
# Nested For Loop Optimized
-The point of this approach is to show with just a few changes, the runtime can be improved by a lot.
+This approach shows that just a few changes can improve the running time of the solution significantly.
```python
def largest(min_factor, max_factor):
@@ -49,43 +49,42 @@ def smallest(min_factor, max_factor):
return (result, answer)
```
-This approach is very similar to the [nested for loop approach][approach-nested-for-loop], but it has a few optimizations that make it faster.
-To optimize the largest function, we have to start the inner loop from the maximum factor and go down to the minimum factor.
-This way, we can stop the inner loop earlier than before.
-We also in the inner loop set the minimum value to the current value of the outer loop.
+This approach is very similar to the [nested for loop approach][approach-nested-for-loop], but it has a few optimizations.
+To optimize the `largest` function, we have to start the inner loop from the maximum factor and proceed down to the minimum factor.
+This allows us to stop the inner loop earlier than before.
+We also set the minimum value in the _inner_ loop to the current value of the _outer_ loop.
-Here is an example of how the algorithm work and why we have to modify the loops.
-Say we take max to be 99 and min 10.
-It would go first round:
+Here is an example of how the algorithm works and why the loops need to be modified.
+Say we take maximum to be 99 and the minimum 10.
+In the first round:
```
-x => [99 , 98, 97 ...]
-y => [99]
+x = [99 , 98, 97 ...]
+y = [99]
```
-And already here we have our result which is `9009[91,99]`
-Although the loops has to continue to make us sure there are no higher value.
+And already we have our result: `9009[91,99]`
+Although the loops have to continue to make us sure there are no higher values.
```
-x => [98, 97, 96 ...]
-y => [99, 98]
+x = [98, 97, 96 ...]
+y = [99, 98]
...
-x => [90, 89, 88 ...]
-y => [99, 98,97,96,95,94,93,92,91,90]
-```
+x = [90, 89, 88 ...]
+y = [99, 98,97,96,95,94,93,92,91,90]
Here we can see that the highest value for this "run" is 90 \* 99 = 8910.
-Meaning running beyond this point wont give us any higher value than 9009.
+Meaning that running beyond this point won't give us any values higher than 9009.
That is why we introduce the `was_bigger` variable.
-With that variable we can check the inner loop if it has been bigger than the current result.
+With `was_bigger`, we can check if the inner loop has a bigger value than the current result.
If there has not been a bigger value, we can stop the inner loop and stop the outer loop.
We do that by using the [`break`][break] statement.
-If we hadn't modified the inner loop, it would have started from the minimum factor and gone up to the maximum factor.
+If we hadn't modified the direction of the inner loop, it would have started from the minimum factor and gone up to the maximum factor.
This would mean that for every new run in the outer loop, the values would be bigger than the previous run.
-The smallest function is optimized in a similar way as largest function compared to the original approach.
+The `smallest` function is optimized in a similar way as `largest` function compared to the original approach.
The only difference is that we have to start the inner loop and outer loop from the minimum factor and go up to the maximum factor.
Since what we want is the smallest value, we have to start from the smallest value and go up to the biggest value.
diff --git a/exercises/practice/palindrome-products/.approaches/nested-for-loop/content.md b/exercises/practice/palindrome-products/.approaches/nested-for-loop/content.md
index b2fe86d0ac..5f5086f5b6 100644
--- a/exercises/practice/palindrome-products/.approaches/nested-for-loop/content.md
+++ b/exercises/practice/palindrome-products/.approaches/nested-for-loop/content.md
@@ -39,38 +39,39 @@ def smallest(min_factor, max_factor):
return (result, answer)
```
-The largest and smallest functions are very similar.
+The `largest` and `smallest` functions are very similar here.
Although there are some small differences.
-The largest function starts with checking if the min_factor is bigger than the max_factor.
+The `largest` function starts with checking if the min_factor is bigger than the max_factor.
If it is, it raises a [`ValueError`][value-error].
After that, it initializes the result to 0 and the answer to an empty list.
-Then it starts `for in range` loop.
-The first for loop iterates over the range of numbers from min_factor to max_factor.
-The second for loop iterates over the same range of numbers.
+Then it starts a `for in range` loop.
+The outer `for` loop iterates over the range of numbers from min_factor to max_factor.
+The inner `for` loop iterates over the same range of numbers.
Then it checks if the product of the two numbers is bigger than the result.
If it is, it converts the product to a string and checks if it is a palindrome.
We can check if a string is a palindrome by comparing it to its reverse.
There are multiple ways to reverse a string.
-The most common way is to use the slice notation.
+The most common way is to use [slice notation][slice-notation].
The slice notation is `string[start:stop:step]`.
You can read more about it in: [reverse string with slice][string-reverse].
-If it is, it checks if the product is bigger than the result.
-If it is, it clears the answer list and sets the result to the product.
+If the string is a palindrome, the solution next checks if the product is bigger than the current result.
+If the product is bigger, the answer list is cleared, and the current result is set to the most recent product.
Then it appends the two numbers to the answer list.
After the two for loops, it checks if the result is 0.
If it is, it sets the result to [`None`][none].
Then it returns the result and the answer.
-The smallest one is very similar.
+The `smallest` one is very similar.
The differences are that it checks if the product is smaller than the result or if the result is 0.
And that it reset result outside of the if statement.
Instead of inside of it.
[none]: https://realpython.com/null-in-python/
+[slice-notation]: https://www.learnbyexample.org/python-list-slicing/
[string-reverse]: https://realpython.com/reverse-string-python/#reversing-strings-through-slicing
[value-error]: https://docs.python.org/3/library/exceptions.html#ValueError
diff --git a/exercises/practice/palindrome-products/.articles/performance/content.md b/exercises/practice/palindrome-products/.articles/performance/content.md
index f0bce43e18..f052088335 100644
--- a/exercises/practice/palindrome-products/.articles/performance/content.md
+++ b/exercises/practice/palindrome-products/.articles/performance/content.md
@@ -1,6 +1,6 @@
# Performance
-In this approach, we'll find out the performance difference between approaches for palindrome product in Python.
+In this article, we'll examine the performance difference between approaches for `palindrome-products` in Python.
The [approaches page][approaches] lists two approaches to this exercise:
@@ -10,7 +10,8 @@ The [approaches page][approaches] lists two approaches to this exercise:
## Benchmarks
To benchmark the approaches, we wrote a [small benchmark application][benchmark-application] using the [`timeit`][timeit] library.
-These test were run in windows 11, using Python 3.11.1.
+These tests were run in windows 11, using Python 3.11.1.
+Your system results may vary.
```
largest, min=1, max=1000 : 0.05235219991300255
From 6d14e3c949aa6fbe739674baf838ec90d7118a16 Mon Sep 17 00:00:00 2001
From: Matthijs <19817960+MatthijsBlom@users.noreply.github.com>
Date: Sat, 7 Jan 2023 20:54:58 +0100
Subject: [PATCH 319/826] Clarify Currency Exchange instructions
---
exercises/concept/currency-exchange/.docs/instructions.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/exercises/concept/currency-exchange/.docs/instructions.md b/exercises/concept/currency-exchange/.docs/instructions.md
index 03f7e6a1e3..3bb6bff971 100644
--- a/exercises/concept/currency-exchange/.docs/instructions.md
+++ b/exercises/concept/currency-exchange/.docs/instructions.md
@@ -52,8 +52,8 @@ Unfortunately, the booth gets to keep the remainder/change as an added bonus.
Create the `get_number_of_bills()` function, taking `budget` and `denomination`.
-This function should return the _number of new currency bills_ that you can receive within the given _budget_.
-In other words: How many _whole bills_ of new currency fit into the amount of old currency you have in your budget?
+This function should return the _number of currency bills_ that you can receive within the given _budget_.
+In other words: How many _whole bills_ of currency fit into the amount of currency you have in your budget?
Remember -- you can only receive _whole bills_, not fractions of bills, so remember to divide accordingly.
Effectively, you are rounding _down_ to the nearest whole bill/denomination.
From cac72fbb92f90087d8d87f2c40733c60796572d5 Mon Sep 17 00:00:00 2001
From: Carl
Date: Fri, 30 Dec 2022 18:55:52 +0100
Subject: [PATCH 320/826] Started
---
.../.approaches/config.json | 29 +++++++
.../.approaches/if-else/content.md | 32 ++++++++
.../.approaches/if-else/snippet.txt | 11 +++
.../.approaches/introduction.md | 82 +++++++++++++++++++
.../.approaches/recursion/content.md | 46 +++++++++++
.../.approaches/recursion/snippet.txt | 7 ++
.../.approaches/ternary-operator/content.md | 31 +++++++
.../.approaches/ternary-operator/snippet.txt | 8 ++
.../collatz-conjecture/.articles/config.json | 11 +++
.../.articles/performance/code/Benchmark.py | 43 ++++++++++
.../.articles/performance/content.md | 32 ++++++++
.../.articles/performance/snippet.md | 5 ++
12 files changed, 337 insertions(+)
create mode 100644 exercises/practice/collatz-conjecture/.approaches/config.json
create mode 100644 exercises/practice/collatz-conjecture/.approaches/if-else/content.md
create mode 100644 exercises/practice/collatz-conjecture/.approaches/if-else/snippet.txt
create mode 100644 exercises/practice/collatz-conjecture/.approaches/introduction.md
create mode 100644 exercises/practice/collatz-conjecture/.approaches/recursion/content.md
create mode 100644 exercises/practice/collatz-conjecture/.approaches/recursion/snippet.txt
create mode 100644 exercises/practice/collatz-conjecture/.approaches/ternary-operator/content.md
create mode 100644 exercises/practice/collatz-conjecture/.approaches/ternary-operator/snippet.txt
create mode 100644 exercises/practice/collatz-conjecture/.articles/config.json
create mode 100644 exercises/practice/collatz-conjecture/.articles/performance/code/Benchmark.py
create mode 100644 exercises/practice/collatz-conjecture/.articles/performance/content.md
create mode 100644 exercises/practice/collatz-conjecture/.articles/performance/snippet.md
diff --git a/exercises/practice/collatz-conjecture/.approaches/config.json b/exercises/practice/collatz-conjecture/.approaches/config.json
new file mode 100644
index 0000000000..2ba3bf4c5b
--- /dev/null
+++ b/exercises/practice/collatz-conjecture/.approaches/config.json
@@ -0,0 +1,29 @@
+{
+ "introduction": {
+ "authors": ["bethanyg", "meatball133"],
+ "contributors": []
+ },
+ "approaches": [
+ {
+ "uuid": "7b3aeaef-11ef-427c-9086-d2c6d4c022f1",
+ "slug": "if-else",
+ "title": "If/Else",
+ "blurb": "Use if and else",
+ "authors": ["bethanyg", "meatball133"]
+ },
+ {
+ "uuid": "a2d8742b-9516-44c8-832d-111874430de0",
+ "slug": "ternary-operator",
+ "title": "Ternary operator",
+ "blurb": "Use a ternary operator",
+ "authors": ["bethanyg", "meatball133"]
+ },
+ {
+ "uuid": "01da60d9-c133-41de-90fd-afbba7df2980",
+ "slug": "recursion",
+ "title": "Recursion",
+ "blurb": "Use recursion",
+ "authors": ["bethanyg", "meatball133"]
+ }
+ ]
+}
diff --git a/exercises/practice/collatz-conjecture/.approaches/if-else/content.md b/exercises/practice/collatz-conjecture/.approaches/if-else/content.md
new file mode 100644
index 0000000000..c78fc06a7e
--- /dev/null
+++ b/exercises/practice/collatz-conjecture/.approaches/if-else/content.md
@@ -0,0 +1,32 @@
+# If / Else
+
+```python
+def steps(number):
+ if number <= 0:
+ raise ValueError("Only positive integers are allowed")
+ counter = 0
+ while number != 1:
+ if number % 2 == 0:
+ number /= 2
+ else:
+ number = number * 3 + 1
+ counter += 1
+ return counter
+```
+
+This approach starts with checking if the number is less than or equal to zero.
+If it is, then it raises a [`ValueError`][value-error].
+After that we declare a counter variable and set it to zero.
+Then we start a [`while` loop][while-loop] that will run until the number is equal to one.
+Meaning the loop wont run if the number is already one.
+
+Inside the loop we check if the number is even.
+If it is, then we divide it by two.
+If it isn't, then we multiply it by three and add one.
+After that we increment the counter by one.
+After the loop is done, we return the counter variable.
+
+We have to use a `while` loop here because we don't know how many times the loop will run.
+
+[value-error]: https://docs.python.org/3/library/exceptions.html#ValueError
+[while-loop]: https://realpython.com/python-while-loop/
diff --git a/exercises/practice/collatz-conjecture/.approaches/if-else/snippet.txt b/exercises/practice/collatz-conjecture/.approaches/if-else/snippet.txt
new file mode 100644
index 0000000000..eee0d4a3dc
--- /dev/null
+++ b/exercises/practice/collatz-conjecture/.approaches/if-else/snippet.txt
@@ -0,0 +1,11 @@
+def steps(number):
+ if number <= 0:
+ raise ValueError("Only positive integers are allowed")
+ counter = 0
+ while number != 1:
+ if number % 2 == 0:
+ number /= 2
+ else:
+ number = number * 3 + 1
+ counter += 1
+ return counter
diff --git a/exercises/practice/collatz-conjecture/.approaches/introduction.md b/exercises/practice/collatz-conjecture/.approaches/introduction.md
new file mode 100644
index 0000000000..20fbe6f90f
--- /dev/null
+++ b/exercises/practice/collatz-conjecture/.approaches/introduction.md
@@ -0,0 +1,82 @@
+# Introduction
+
+There are various approaches to solving the Collatz Conjecture exercise in Python.
+You can for example use a while loop or a recursive function.
+You can use if and else statements or the ternary operator.
+
+## General guidance
+
+The key to this exercise is to check if the number and then do the correct operation.
+Under this process you are suppose to count how many steps it takes to get to one.
+
+## Approach: If/Else
+
+This is a good way to solve the exercise, it is easy to understand and it is very readable.
+The reason to why you might not want to use this approach is because it is longer than the other approaches.
+
+```python
+def steps(number):
+ if number <= 0:
+ raise ValueError("Only positive integers are allowed")
+ counter = 0
+ while number != 1:
+ if number % 2 == 0:
+ number /= 2
+ else:
+ number = number * 3 + 1
+ counter += 1
+ return counter
+```
+
+For more information, check the [if/else approach][approach-if-else].
+
+## Approach: Ternary operator
+
+In this approach we use the ternary operator.
+It allows us to write a one-line if and else statement.
+This makes the code more concise.
+
+```python
+def steps(number):
+ if number <= 0:
+ raise ValueError("Only positive integers are allowed")
+ counter = 0
+ while number != 1:
+ number = number / 2 if number % 2 == 0 else number * 3 + 1
+ counter += 1
+ return counter
+```
+
+For more information, check the [Ternary operator approach][approach-ternary-operator].
+
+## Approach: Recursive function
+
+In this approach we use a recursive function.
+A recursive function is a function that calls itself.
+The reason to why you want to use this approach is because it is more concise than the other approaches.
+Since it doesn't need a counter variable.
+
+The reason to why you might not want to use this approach is that python has a limit on how many times a function can call itself.
+This limit is 1000 times.
+
+```python
+def steps(number):
+ if number <= 0:
+ raise ValueError("Only positive integers are allowed")
+ if number == 1:
+ return 0
+ number = number / 2 if number % 2 == 0 else number * 3 + 1
+ return 1 + steps(number)
+```
+
+For more information, check the [Recursion approach][approach-recursion].
+
+## Benchmarks
+
+To get a better understanding of the performance of the different approaches, we have created benchmarks.
+For more information, check the [Performance article].
+
+[performance article]: https://exercism.org/tracks/python/exercises/collatz-conjecture/articles/performance
+[approach-if-else]: https://exercism.org/tracks/python/exercises/collatz-conjecture/approaches/if-else
+[approach-ternary-operator]: https://exercism.org/tracks/python/exercises/collatz-conjecture/approaches/ternary-operator
+[approach-recursion]: https://exercism.org/tracks/python/exercises/collatz-conjecture/approaches/recursion
diff --git a/exercises/practice/collatz-conjecture/.approaches/recursion/content.md b/exercises/practice/collatz-conjecture/.approaches/recursion/content.md
new file mode 100644
index 0000000000..b452045ed8
--- /dev/null
+++ b/exercises/practice/collatz-conjecture/.approaches/recursion/content.md
@@ -0,0 +1,46 @@
+# Recursion
+
+```python
+def steps(number):
+ if number <= 0:
+ raise ValueError("Only positive integers are allowed")
+ if number == 1:
+ return 0
+ number = number / 2 if number % 2 == 0 else number * 3 + 1
+ return 1 + steps(number)
+```
+
+This approach uses [concept:python/recursion]() to solve the problem.
+Recursion is a programming technique where a function calls itself.
+Recursion is a powerful technique, but can be more tricky.
+Recursion isn't that common in Python, it is more common in functional programming languages, like: [Elixir][elixir], [Haskell][haskell], and [Clojure][clojure].
+
+This approach starts with checking if the number is less than or equal to zero.
+If it is, then it raises a [`ValueError`][value-error].
+
+After that we check if the number is equal to one.
+If it is, then we return zero.
+
+Then we use the same ternary operator as in the [ternary operator][ternary-operator] approach.
+We declare that number is equal to the result of the ternary operator.
+The ternary operator checks if the number is even.
+If it is, then we divide it by two.
+If it isn't, then we multiply it by three and add one.
+
+After that we return one plus the result of calling the `steps` function with the new number.
+This is the recursion part.
+
+Doing this exercise with recursion removes the need for a "counter" variable.
+Since what we do is that if the number is not one we do `1 + steps(number)`.
+Then that function can do the same thing.
+Meaning we can get a long chain of `1 + steps(number)` until we reach one and add 0.
+That will translate to something like this: `1 + 1 + 1 + 1 + 0`.
+
+In python we can't have a function call itself more than 1000 times.
+
+[clojure]: https://exercism.org/tracks/clojure
+[elixir]: https://exercism.org/tracks/elixir
+[haskell]: https://exercism.org/tracks/haskell
+[recursion]: https://realpython.com/python-thinking-recursively/
+[ternary-operator]: https://exercism.org/tracks/python/exercises/collatz-conjecture/approaches/ternary-operator
+[value-error]: https://docs.python.org/3/library/exceptions.html#ValueError
diff --git a/exercises/practice/collatz-conjecture/.approaches/recursion/snippet.txt b/exercises/practice/collatz-conjecture/.approaches/recursion/snippet.txt
new file mode 100644
index 0000000000..addae0aeae
--- /dev/null
+++ b/exercises/practice/collatz-conjecture/.approaches/recursion/snippet.txt
@@ -0,0 +1,7 @@
+def steps(number):
+ if number < 1:
+ raise ValueError("Only positive integers are allowed")
+ if number == 1:
+ return 0
+ number = number / 2 if number % 2 == 0 else number * 3 + 1
+ return 1 + steps(number)
diff --git a/exercises/practice/collatz-conjecture/.approaches/ternary-operator/content.md b/exercises/practice/collatz-conjecture/.approaches/ternary-operator/content.md
new file mode 100644
index 0000000000..518455461c
--- /dev/null
+++ b/exercises/practice/collatz-conjecture/.approaches/ternary-operator/content.md
@@ -0,0 +1,31 @@
+# Ternary operator
+
+```python
+def steps(number):
+ if number <= 0:
+ raise ValueError("Only positive integers are allowed")
+ counter = 0
+ while number != 1:
+ number = number / 2 if number % 2 == 0 else number * 3 + 1
+ counter += 1
+ return counter
+```
+
+If it is, then it raises a [`ValueError`][value-error].
+After that we declare a counter variable and set it to zero.
+
+Then we start a `while` loop that will run until the number is equal to one.
+Meaning the loop wont run if the number is already one.
+
+Inside of the loop we have a [ternary operator][ternary-operator].
+The ternary operator is a one-line `if` and `else` statement.
+That means that we can make the code more concise by using it.
+We declare that number is equal to the result of the ternary operator.
+The ternary operator checks if the number is even.
+If it is, then we divide it by two.
+If it isn't, then we multiply it by three and add one.
+After that we increment the counter by one.
+After the loop is done, we return the counter variable.
+
+[ternary-operator]: https://www.pythontutorial.net/python-basics/python-ternary-operator/
+[value-error]: https://docs.python.org/3/library/exceptions.html#ValueError
diff --git a/exercises/practice/collatz-conjecture/.approaches/ternary-operator/snippet.txt b/exercises/practice/collatz-conjecture/.approaches/ternary-operator/snippet.txt
new file mode 100644
index 0000000000..5e6fcd8c03
--- /dev/null
+++ b/exercises/practice/collatz-conjecture/.approaches/ternary-operator/snippet.txt
@@ -0,0 +1,8 @@
+def steps(number):
+ if number <= 0:
+ raise ValueError("Only positive integers are allowed")
+ counter = 0
+ while number != 1:
+ number = number / 2 if number % 2 == 0 else number * 3 + 1
+ counter += 1
+ return counter
diff --git a/exercises/practice/collatz-conjecture/.articles/config.json b/exercises/practice/collatz-conjecture/.articles/config.json
new file mode 100644
index 0000000000..afb7c6f9e6
--- /dev/null
+++ b/exercises/practice/collatz-conjecture/.articles/config.json
@@ -0,0 +1,11 @@
+{
+ "articles": [
+ {
+ "uuid": "c6435e29-fd40-42a4-95ba-7c06b9b48f7a",
+ "slug": "performance",
+ "title": "Performance deep dive",
+ "blurb": "Deep dive to find out the most performant approach for collatz conjecture.",
+ "authors": ["meatball133", "bethanyg"]
+ }
+ ]
+}
diff --git a/exercises/practice/collatz-conjecture/.articles/performance/code/Benchmark.py b/exercises/practice/collatz-conjecture/.articles/performance/code/Benchmark.py
new file mode 100644
index 0000000000..aea0162e59
--- /dev/null
+++ b/exercises/practice/collatz-conjecture/.articles/performance/code/Benchmark.py
@@ -0,0 +1,43 @@
+import timeit
+
+def steps_if(number):
+ if number <= 0:
+ raise ValueError("Only positive integers are allowed")
+ counter = 0
+ while number != 1:
+ if number % 2 == 0:
+ number /= 2
+ else:
+ number = number * 3 + 1
+ counter += 1
+ return counter
+
+def steps_recursion(number):
+ if number <= 0:
+ raise ValueError("Only positive integers are allowed")
+ if number == 1:
+ return 0
+ number = number / 2 if number % 2 == 0 else number * 3 + 1
+ return 1 + steps_recursion(number)
+
+def steps_ternary(number):
+ if number <= 0:
+ raise ValueError("Only positive integers are allowed")
+ counter = 0
+ while number != 1:
+ number = number / 2 if number % 2 == 0 else number * 3 + 1
+ counter += 1
+ return counter
+
+
+starttime = timeit.default_timer()
+steps_recursion(100000)
+print("Steps with recursion :", timeit.default_timer() - starttime)
+
+starttime = timeit.default_timer()
+steps_ternary(100000)
+print("Steps with ternary :", timeit.default_timer() - starttime)
+
+starttime = timeit.default_timer()
+steps_if(100000)
+print("Steps with if/else :", timeit.default_timer() - starttime)
diff --git a/exercises/practice/collatz-conjecture/.articles/performance/content.md b/exercises/practice/collatz-conjecture/.articles/performance/content.md
new file mode 100644
index 0000000000..c5c62aacef
--- /dev/null
+++ b/exercises/practice/collatz-conjecture/.articles/performance/content.md
@@ -0,0 +1,32 @@
+# Performance
+
+In this approach, we'll find out how to most efficiently calculate Collatz Conjecture in Python.
+
+The [approaches page][approaches] lists three approaches to this exercise:
+
+1. [Using recursion][approach-recursion]
+2. [Using the ternary operator][approach-ternary-operator]
+3. [Using the if/else][approach-if-else]
+
+## Benchmarks
+
+To benchmark the approaches, we wrote a [small benchmark application][benchmark-application] using the [`timeit`][timeit] library.
+These test where run in windows 11, using Python 3.11.1.
+
+```
+Steps with recursion : 0.00015059998258948326
+Steps with ternary : 1.8699909560382366e-05
+Steps with if/else : 1.8799910321831703e-05
+```
+
+## Conclusion
+
+The fastest approach is the one using the ternary operator or the if/else statement.
+The slowest approach is the one using recursion, that is because Python isn't optimized for recursion.
+
+[approaches]: https://exercism.org/tracks/python/exercises/collatz-conjecture/approaches
+[approach-if-else]: https://exercism.org/tracks/python/exercises/collatz-conjecture/approaches/if-else
+[approach-ternary-operator]: https://exercism.org/tracks/python/exercises/collatz-conjecture/approaches/ternary-operator
+[approach-recursion]: https://exercism.org/tracks/python/exercises/collatz-conjecture/approaches/recursion
+[benchmark-application]: https://github.com/exercism/python/blob/main/exercises/practice/collatz-conjecture/.articles/performance/code/Benchmark.py
+[timeit]: https://docs.python.org/3/library/timeit.html
diff --git a/exercises/practice/collatz-conjecture/.articles/performance/snippet.md b/exercises/practice/collatz-conjecture/.articles/performance/snippet.md
new file mode 100644
index 0000000000..d79a676214
--- /dev/null
+++ b/exercises/practice/collatz-conjecture/.articles/performance/snippet.md
@@ -0,0 +1,5 @@
+```
+Steps with recursion : 0.00015059998258948326
+Steps with ternary : 1.8699909560382366e-05
+Steps with if/else : 1.8799910321831703e-05
+```
From 4771a453e84f0c756a150c7d64bd4be4b1742864 Mon Sep 17 00:00:00 2001
From: Carl
Date: Fri, 30 Dec 2022 19:09:18 +0100
Subject: [PATCH 321/826] fix-snippet
---
.../.approaches/if-else/snippet.txt | 19 ++++++++-----------
.../.approaches/ternary-operator/snippet.txt | 2 +-
2 files changed, 9 insertions(+), 12 deletions(-)
diff --git a/exercises/practice/collatz-conjecture/.approaches/if-else/snippet.txt b/exercises/practice/collatz-conjecture/.approaches/if-else/snippet.txt
index eee0d4a3dc..f16ae89210 100644
--- a/exercises/practice/collatz-conjecture/.approaches/if-else/snippet.txt
+++ b/exercises/practice/collatz-conjecture/.approaches/if-else/snippet.txt
@@ -1,11 +1,8 @@
-def steps(number):
- if number <= 0:
- raise ValueError("Only positive integers are allowed")
- counter = 0
- while number != 1:
- if number % 2 == 0:
- number /= 2
- else:
- number = number * 3 + 1
- counter += 1
- return counter
+counter = 0
+while number != 1:
+ if number % 2 == 0:
+ number /= 2
+ else:
+ number = number * 3 + 1
+ counter += 1
+return counter
\ No newline at end of file
diff --git a/exercises/practice/collatz-conjecture/.approaches/ternary-operator/snippet.txt b/exercises/practice/collatz-conjecture/.approaches/ternary-operator/snippet.txt
index 5e6fcd8c03..cc87d02e23 100644
--- a/exercises/practice/collatz-conjecture/.approaches/ternary-operator/snippet.txt
+++ b/exercises/practice/collatz-conjecture/.approaches/ternary-operator/snippet.txt
@@ -5,4 +5,4 @@ def steps(number):
while number != 1:
number = number / 2 if number % 2 == 0 else number * 3 + 1
counter += 1
- return counter
+ return counter
\ No newline at end of file
From 4e047cd722b1512722a01fc344dacf103982408f Mon Sep 17 00:00:00 2001
From: Carl
Date: Fri, 30 Dec 2022 20:08:15 +0100
Subject: [PATCH 322/826] fix
---
.../.approaches/if-else/content.md | 6 +++---
.../.approaches/introduction.md | 16 ++++++++--------
.../.approaches/recursion/content.md | 11 ++++++-----
.../.approaches/ternary-operator/content.md | 12 +++++++-----
.../.articles/performance/content.md | 2 +-
5 files changed, 25 insertions(+), 22 deletions(-)
diff --git a/exercises/practice/collatz-conjecture/.approaches/if-else/content.md b/exercises/practice/collatz-conjecture/.approaches/if-else/content.md
index c78fc06a7e..040b6bccfc 100644
--- a/exercises/practice/collatz-conjecture/.approaches/if-else/content.md
+++ b/exercises/practice/collatz-conjecture/.approaches/if-else/content.md
@@ -16,14 +16,14 @@ def steps(number):
This approach starts with checking if the number is less than or equal to zero.
If it is, then it raises a [`ValueError`][value-error].
-After that we declare a counter variable and set it to zero.
+After that, we declare a counter variable and set it to zero.
Then we start a [`while` loop][while-loop] that will run until the number is equal to one.
-Meaning the loop wont run if the number is already one.
+Meaning the loop won't run if the number is already one.
Inside the loop we check if the number is even.
If it is, then we divide it by two.
If it isn't, then we multiply it by three and add one.
-After that we increment the counter by one.
+After that, we increment the counter by one.
After the loop is done, we return the counter variable.
We have to use a `while` loop here because we don't know how many times the loop will run.
diff --git a/exercises/practice/collatz-conjecture/.approaches/introduction.md b/exercises/practice/collatz-conjecture/.approaches/introduction.md
index 20fbe6f90f..0ff3a741bd 100644
--- a/exercises/practice/collatz-conjecture/.approaches/introduction.md
+++ b/exercises/practice/collatz-conjecture/.approaches/introduction.md
@@ -2,17 +2,17 @@
There are various approaches to solving the Collatz Conjecture exercise in Python.
You can for example use a while loop or a recursive function.
-You can use if and else statements or the ternary operator.
+You can also solve it by using if and else statements or the ternary operator.
## General guidance
The key to this exercise is to check if the number and then do the correct operation.
-Under this process you are suppose to count how many steps it takes to get to one.
+Under this process you are supposed to count how many steps it takes to get to one.
## Approach: If/Else
This is a good way to solve the exercise, it is easy to understand and it is very readable.
-The reason to why you might not want to use this approach is because it is longer than the other approaches.
+The reason why you might not want to use this approach is because it is longer than the other approaches.
```python
def steps(number):
@@ -53,10 +53,10 @@ For more information, check the [Ternary operator approach][approach-ternary-ope
In this approach we use a recursive function.
A recursive function is a function that calls itself.
-The reason to why you want to use this approach is because it is more concise than the other approaches.
+The reason why you want to use this approach is because it is more concise than the other approaches.
Since it doesn't need a counter variable.
-The reason to why you might not want to use this approach is that python has a limit on how many times a function can call itself.
+The reason why you might not want to use this approach is that python has a limit on how many times a function can call itself.
This limit is 1000 times.
```python
@@ -74,9 +74,9 @@ For more information, check the [Recursion approach][approach-recursion].
## Benchmarks
To get a better understanding of the performance of the different approaches, we have created benchmarks.
-For more information, check the [Performance article].
+For more information, check the [Performance article][performance-article].
-[performance article]: https://exercism.org/tracks/python/exercises/collatz-conjecture/articles/performance
[approach-if-else]: https://exercism.org/tracks/python/exercises/collatz-conjecture/approaches/if-else
-[approach-ternary-operator]: https://exercism.org/tracks/python/exercises/collatz-conjecture/approaches/ternary-operator
[approach-recursion]: https://exercism.org/tracks/python/exercises/collatz-conjecture/approaches/recursion
+[approach-ternary-operator]: https://exercism.org/tracks/python/exercises/collatz-conjecture/approaches/ternary-operator
+[performance-article]: https://exercism.org/tracks/python/exercises/collatz-conjecture/articles/performance
diff --git a/exercises/practice/collatz-conjecture/.approaches/recursion/content.md b/exercises/practice/collatz-conjecture/.approaches/recursion/content.md
index b452045ed8..feeb2fc832 100644
--- a/exercises/practice/collatz-conjecture/.approaches/recursion/content.md
+++ b/exercises/practice/collatz-conjecture/.approaches/recursion/content.md
@@ -12,13 +12,13 @@ def steps(number):
This approach uses [concept:python/recursion]() to solve the problem.
Recursion is a programming technique where a function calls itself.
-Recursion is a powerful technique, but can be more tricky.
+It is a powerful technique, but can be more tricky to implement than a while loop.
Recursion isn't that common in Python, it is more common in functional programming languages, like: [Elixir][elixir], [Haskell][haskell], and [Clojure][clojure].
This approach starts with checking if the number is less than or equal to zero.
If it is, then it raises a [`ValueError`][value-error].
-After that we check if the number is equal to one.
+After that, we check if the number is equal to one.
If it is, then we return zero.
Then we use the same ternary operator as in the [ternary operator][ternary-operator] approach.
@@ -27,16 +27,17 @@ The ternary operator checks if the number is even.
If it is, then we divide it by two.
If it isn't, then we multiply it by three and add one.
-After that we return one plus the result of calling the `steps` function with the new number.
+After that, we return one plus the result of calling the `steps` function with the new number.
This is the recursion part.
Doing this exercise with recursion removes the need for a "counter" variable.
Since what we do is that if the number is not one we do `1 + steps(number)`.
-Then that function can do the same thing.
+Then that function can do the same thing again.
Meaning we can get a long chain of `1 + steps(number)` until we reach one and add 0.
That will translate to something like this: `1 + 1 + 1 + 1 + 0`.
-In python we can't have a function call itself more than 1000 times.
+In Python, we can't have a function call itself more than 1000 times.
+Which mean problems that require a lot of recursion will fail.
[clojure]: https://exercism.org/tracks/clojure
[elixir]: https://exercism.org/tracks/elixir
diff --git a/exercises/practice/collatz-conjecture/.approaches/ternary-operator/content.md b/exercises/practice/collatz-conjecture/.approaches/ternary-operator/content.md
index 518455461c..32f0ad8dd1 100644
--- a/exercises/practice/collatz-conjecture/.approaches/ternary-operator/content.md
+++ b/exercises/practice/collatz-conjecture/.approaches/ternary-operator/content.md
@@ -11,20 +11,22 @@ def steps(number):
return counter
```
+This approach starts with checking if the number is less than or equal to zero.
If it is, then it raises a [`ValueError`][value-error].
-After that we declare a counter variable and set it to zero.
+After that, we declare a counter variable and set it to zero.
Then we start a `while` loop that will run until the number is equal to one.
-Meaning the loop wont run if the number is already one.
+Meaning the loop won't run if the number is already one.
-Inside of the loop we have a [ternary operator][ternary-operator].
-The ternary operator is a one-line `if` and `else` statement.
+Inside the loop we have a [ternary operator][ternary-operator].
+A ternary operator is a one-line `if` and `else` statement.
That means that we can make the code more concise by using it.
+
We declare that number is equal to the result of the ternary operator.
The ternary operator checks if the number is even.
If it is, then we divide it by two.
If it isn't, then we multiply it by three and add one.
-After that we increment the counter by one.
+After that, we increment the counter by one.
After the loop is done, we return the counter variable.
[ternary-operator]: https://www.pythontutorial.net/python-basics/python-ternary-operator/
diff --git a/exercises/practice/collatz-conjecture/.articles/performance/content.md b/exercises/practice/collatz-conjecture/.articles/performance/content.md
index c5c62aacef..eccade40cb 100644
--- a/exercises/practice/collatz-conjecture/.articles/performance/content.md
+++ b/exercises/practice/collatz-conjecture/.articles/performance/content.md
@@ -26,7 +26,7 @@ The slowest approach is the one using recursion, that is because Python isn't op
[approaches]: https://exercism.org/tracks/python/exercises/collatz-conjecture/approaches
[approach-if-else]: https://exercism.org/tracks/python/exercises/collatz-conjecture/approaches/if-else
-[approach-ternary-operator]: https://exercism.org/tracks/python/exercises/collatz-conjecture/approaches/ternary-operator
[approach-recursion]: https://exercism.org/tracks/python/exercises/collatz-conjecture/approaches/recursion
+[approach-ternary-operator]: https://exercism.org/tracks/python/exercises/collatz-conjecture/approaches/ternary-operator
[benchmark-application]: https://github.com/exercism/python/blob/main/exercises/practice/collatz-conjecture/.articles/performance/code/Benchmark.py
[timeit]: https://docs.python.org/3/library/timeit.html
From 85cd543f5f44f44a1ed3ea0aba970774eeb7849a Mon Sep 17 00:00:00 2001
From: Carl
Date: Fri, 30 Dec 2022 22:10:17 +0100
Subject: [PATCH 323/826] fix
---
.../collatz-conjecture/.articles/performance/content.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/exercises/practice/collatz-conjecture/.articles/performance/content.md b/exercises/practice/collatz-conjecture/.articles/performance/content.md
index eccade40cb..b18aaba87a 100644
--- a/exercises/practice/collatz-conjecture/.articles/performance/content.md
+++ b/exercises/practice/collatz-conjecture/.articles/performance/content.md
@@ -11,7 +11,7 @@ The [approaches page][approaches] lists three approaches to this exercise:
## Benchmarks
To benchmark the approaches, we wrote a [small benchmark application][benchmark-application] using the [`timeit`][timeit] library.
-These test where run in windows 11, using Python 3.11.1.
+These test were run in windows 11, using Python 3.11.1.
```
Steps with recursion : 0.00015059998258948326
From 9b7c79897d1fbb76f69bb7af8dd0a746b726a0dc Mon Sep 17 00:00:00 2001
From: Carl
Date: Sat, 31 Dec 2022 15:54:08 +0100
Subject: [PATCH 324/826] Re-run the test on python 3.11.1 instead of python
3.10
---
.../collatz-conjecture/.articles/performance/content.md | 8 ++++----
.../collatz-conjecture/.articles/performance/snippet.md | 6 +++---
2 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/exercises/practice/collatz-conjecture/.articles/performance/content.md b/exercises/practice/collatz-conjecture/.articles/performance/content.md
index b18aaba87a..7e68380391 100644
--- a/exercises/practice/collatz-conjecture/.articles/performance/content.md
+++ b/exercises/practice/collatz-conjecture/.articles/performance/content.md
@@ -14,15 +14,15 @@ To benchmark the approaches, we wrote a [small benchmark application][benchmark-
These test were run in windows 11, using Python 3.11.1.
```
-Steps with recursion : 0.00015059998258948326
-Steps with ternary : 1.8699909560382366e-05
-Steps with if/else : 1.8799910321831703e-05
+Steps with recursion : 4.1499966755509377e-05
+Steps with ternary : 2.1900050342082977e-05
+Steps with if/else : 2.0900042727589607e-05
```
## Conclusion
The fastest approach is the one using the ternary operator or the if/else statement.
-The slowest approach is the one using recursion, that is because Python isn't optimized for recursion.
+The slowest approach is the one using recursion, that is because Python isn't as optimized for recursion.
[approaches]: https://exercism.org/tracks/python/exercises/collatz-conjecture/approaches
[approach-if-else]: https://exercism.org/tracks/python/exercises/collatz-conjecture/approaches/if-else
diff --git a/exercises/practice/collatz-conjecture/.articles/performance/snippet.md b/exercises/practice/collatz-conjecture/.articles/performance/snippet.md
index d79a676214..b95418a512 100644
--- a/exercises/practice/collatz-conjecture/.articles/performance/snippet.md
+++ b/exercises/practice/collatz-conjecture/.articles/performance/snippet.md
@@ -1,5 +1,5 @@
```
-Steps with recursion : 0.00015059998258948326
-Steps with ternary : 1.8699909560382366e-05
-Steps with if/else : 1.8799910321831703e-05
+Steps with recursion : 4.1499966755509377e-05
+Steps with ternary : 2.1900050342082977e-05
+Steps with if/else : 2.0900042727589607e-05
```
From 707bba1658f878daeb48d12bf84fbcf72a090670 Mon Sep 17 00:00:00 2001
From: meatball <69751659+meatball133@users.noreply.github.com>
Date: Tue, 10 Jan 2023 22:11:53 +0100
Subject: [PATCH 325/826] Apply suggestions from code review
Co-authored-by: BethanyG
---
.../.approaches/if-else/content.md | 4 +--
.../.approaches/introduction.md | 12 ++++----
.../.approaches/recursion/content.md | 30 +++++++++++--------
.../.approaches/ternary-operator/content.md | 20 ++++++-------
.../.articles/performance/content.md | 8 ++---
5 files changed, 38 insertions(+), 36 deletions(-)
diff --git a/exercises/practice/collatz-conjecture/.approaches/if-else/content.md b/exercises/practice/collatz-conjecture/.approaches/if-else/content.md
index 040b6bccfc..56cc8e4257 100644
--- a/exercises/practice/collatz-conjecture/.approaches/if-else/content.md
+++ b/exercises/practice/collatz-conjecture/.approaches/if-else/content.md
@@ -24,9 +24,9 @@ Inside the loop we check if the number is even.
If it is, then we divide it by two.
If it isn't, then we multiply it by three and add one.
After that, we increment the counter by one.
-After the loop is done, we return the counter variable.
+After the loop completes, we return the counter variable.
-We have to use a `while` loop here because we don't know how many times the loop will run.
+We use a `while` loop here because we don't know exactly how many times the loop will run.
[value-error]: https://docs.python.org/3/library/exceptions.html#ValueError
[while-loop]: https://realpython.com/python-while-loop/
diff --git a/exercises/practice/collatz-conjecture/.approaches/introduction.md b/exercises/practice/collatz-conjecture/.approaches/introduction.md
index 0ff3a741bd..4e9c155c35 100644
--- a/exercises/practice/collatz-conjecture/.approaches/introduction.md
+++ b/exercises/practice/collatz-conjecture/.approaches/introduction.md
@@ -32,9 +32,8 @@ For more information, check the [if/else approach][approach-if-else].
## Approach: Ternary operator
-In this approach we use the ternary operator.
-It allows us to write a one-line if and else statement.
-This makes the code more concise.
+In this approach we replace the `if/else` multi-line construction with a [conditional expression][conditional-expression], sometimes called a _ternary operator_.
+This syntax allows us to write a one-line `if/ else` check, making the code more concise.
```python
def steps(number):
@@ -53,11 +52,9 @@ For more information, check the [Ternary operator approach][approach-ternary-ope
In this approach we use a recursive function.
A recursive function is a function that calls itself.
-The reason why you want to use this approach is because it is more concise than the other approaches.
-Since it doesn't need a counter variable.
+This approach can be more concise than other approaches, and may also be more readable for some audiences.
-The reason why you might not want to use this approach is that python has a limit on how many times a function can call itself.
-This limit is 1000 times.
+The reason why you might not want to use this approach is that Python has a [recursion limit][recursion-limit] with a default of 1000.
```python
def steps(number):
@@ -79,4 +76,5 @@ For more information, check the [Performance article][performance-article].
[approach-if-else]: https://exercism.org/tracks/python/exercises/collatz-conjecture/approaches/if-else
[approach-recursion]: https://exercism.org/tracks/python/exercises/collatz-conjecture/approaches/recursion
[approach-ternary-operator]: https://exercism.org/tracks/python/exercises/collatz-conjecture/approaches/ternary-operator
+[conditional-expression]: https://docs.python.org/3/reference/expressions.html#conditional-expressions
[performance-article]: https://exercism.org/tracks/python/exercises/collatz-conjecture/articles/performance
diff --git a/exercises/practice/collatz-conjecture/.approaches/recursion/content.md b/exercises/practice/collatz-conjecture/.approaches/recursion/content.md
index feeb2fc832..cdfa25335c 100644
--- a/exercises/practice/collatz-conjecture/.approaches/recursion/content.md
+++ b/exercises/practice/collatz-conjecture/.approaches/recursion/content.md
@@ -21,27 +21,31 @@ If it is, then it raises a [`ValueError`][value-error].
After that, we check if the number is equal to one.
If it is, then we return zero.
-Then we use the same ternary operator as in the [ternary operator][ternary-operator] approach.
-We declare that number is equal to the result of the ternary operator.
-The ternary operator checks if the number is even.
-If it is, then we divide it by two.
-If it isn't, then we multiply it by three and add one.
+We then use the same conditional expression/ternary operator as the [ternary operator][ternary-operator] approach does.
+We assign **number** to the result of the conditional expression.
+The expression checks if the number is even.
+If the number is even, we divide it by two.
+If it isn't, we multiply it by three and add one.
-After that, we return one plus the result of calling the `steps` function with the new number.
+After that, we `return` one plus the result of calling the `steps` function with the new number value.
This is the recursion part.
-Doing this exercise with recursion removes the need for a "counter" variable.
-Since what we do is that if the number is not one we do `1 + steps(number)`.
-Then that function can do the same thing again.
-Meaning we can get a long chain of `1 + steps(number)` until we reach one and add 0.
-That will translate to something like this: `1 + 1 + 1 + 1 + 0`.
+Solving this exercise with recursion removes the need for a "counter" variable and the instantiation of a `loop`.
+If the number is not equal to one, we call `1 + steps(number)`.
+Then the `steps` function can execute the same code again with new values.
+Meaning we can get a long chain or stack of `1 + steps(number)` until the number reaches one and the code adds 0.
+That translates to something like this: `1 + 1 + 1 + 1 + 0`.
-In Python, we can't have a function call itself more than 1000 times.
-Which mean problems that require a lot of recursion will fail.
+In Python, we can't have a function call itself more than 1000 times by default.
+Code that exceeds this recursion limit will throw a [RecursionError][recursion-error].
+While it is possible to adjust the [recursion limit][recursion-limit], doing so risks crashing Python and may also crash your system.
+Casually raising the recursion limit is not recommended.
[clojure]: https://exercism.org/tracks/clojure
[elixir]: https://exercism.org/tracks/elixir
[haskell]: https://exercism.org/tracks/haskell
[recursion]: https://realpython.com/python-thinking-recursively/
+[recursion-error]: https://docs.python.org/3/library/exceptions.html#RecursionError
+[recursion-limit]: https://docs.python.org/3/library/sys.html#sys.setrecursionlimit
[ternary-operator]: https://exercism.org/tracks/python/exercises/collatz-conjecture/approaches/ternary-operator
[value-error]: https://docs.python.org/3/library/exceptions.html#ValueError
diff --git a/exercises/practice/collatz-conjecture/.approaches/ternary-operator/content.md b/exercises/practice/collatz-conjecture/.approaches/ternary-operator/content.md
index 32f0ad8dd1..7a1368cf6e 100644
--- a/exercises/practice/collatz-conjecture/.approaches/ternary-operator/content.md
+++ b/exercises/practice/collatz-conjecture/.approaches/ternary-operator/content.md
@@ -12,22 +12,22 @@ def steps(number):
```
This approach starts with checking if the number is less than or equal to zero.
-If it is, then it raises a [`ValueError`][value-error].
+If it is, then a [`ValueError`][value-error] is raised.
-After that, we declare a counter variable and set it to zero.
+After that, a counter variable is assigned to zero.
Then we start a `while` loop that will run until the number is equal to one.
Meaning the loop won't run if the number is already one.
-Inside the loop we have a [ternary operator][ternary-operator].
-A ternary operator is a one-line `if` and `else` statement.
-That means that we can make the code more concise by using it.
+Inside the loop we have a [ternary operator][ternary-operator] or [conditional expression][conditional-expression].
+A ternary operator/conditional expression can be viewed as a one-line `if/else` statement.
+Using a one-line construct can make the code more concise.
-We declare that number is equal to the result of the ternary operator.
-The ternary operator checks if the number is even.
+We assign the number value to the result of the ternary operator.
+The ternary operator/conditional expression checks if the number is even.
If it is, then we divide it by two.
-If it isn't, then we multiply it by three and add one.
-After that, we increment the counter by one.
-After the loop is done, we return the counter variable.
+If the number is not even, we multiply by three and add one.
+Then the counter is incremented by one.
+When the loop completes, we return the counter value.
[ternary-operator]: https://www.pythontutorial.net/python-basics/python-ternary-operator/
[value-error]: https://docs.python.org/3/library/exceptions.html#ValueError
diff --git a/exercises/practice/collatz-conjecture/.articles/performance/content.md b/exercises/practice/collatz-conjecture/.articles/performance/content.md
index 7e68380391..5b1c3d43fd 100644
--- a/exercises/practice/collatz-conjecture/.articles/performance/content.md
+++ b/exercises/practice/collatz-conjecture/.articles/performance/content.md
@@ -1,6 +1,6 @@
# Performance
-In this approach, we'll find out how to most efficiently calculate Collatz Conjecture in Python.
+In this approach, we'll find out how to most efficiently calculate the Collatz Conjecture in Python.
The [approaches page][approaches] lists three approaches to this exercise:
@@ -11,7 +11,7 @@ The [approaches page][approaches] lists three approaches to this exercise:
## Benchmarks
To benchmark the approaches, we wrote a [small benchmark application][benchmark-application] using the [`timeit`][timeit] library.
-These test were run in windows 11, using Python 3.11.1.
+These tests were run in windows 11, using Python 3.11.1.
```
Steps with recursion : 4.1499966755509377e-05
@@ -21,8 +21,8 @@ Steps with if/else : 2.0900042727589607e-05
## Conclusion
-The fastest approach is the one using the ternary operator or the if/else statement.
-The slowest approach is the one using recursion, that is because Python isn't as optimized for recursion.
+The fastest approach is the one using the `if/else` statement, followed by the one using the ternary operator/conditional expression.
+The slowest approach is the one using recursion, probably because Python isn't as optimized for recursion as it is for iteration.
[approaches]: https://exercism.org/tracks/python/exercises/collatz-conjecture/approaches
[approach-if-else]: https://exercism.org/tracks/python/exercises/collatz-conjecture/approaches/if-else
From 957c260db2da549ec4af1191bf366f3b59a92edf Mon Sep 17 00:00:00 2001
From: meatball <69751659+meatball133@users.noreply.github.com>
Date: Tue, 10 Jan 2023 22:16:49 +0100
Subject: [PATCH 326/826] Update
exercises/practice/collatz-conjecture/.approaches/introduction.md
Co-authored-by: BethanyG
---
.../practice/collatz-conjecture/.approaches/introduction.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/exercises/practice/collatz-conjecture/.approaches/introduction.md b/exercises/practice/collatz-conjecture/.approaches/introduction.md
index 4e9c155c35..6a6d487f26 100644
--- a/exercises/practice/collatz-conjecture/.approaches/introduction.md
+++ b/exercises/practice/collatz-conjecture/.approaches/introduction.md
@@ -75,6 +75,7 @@ For more information, check the [Performance article][performance-article].
[approach-if-else]: https://exercism.org/tracks/python/exercises/collatz-conjecture/approaches/if-else
[approach-recursion]: https://exercism.org/tracks/python/exercises/collatz-conjecture/approaches/recursion
+[recursion-limit]: https://docs.python.org/3/library/sys.html#sys.setrecursionlimit
[approach-ternary-operator]: https://exercism.org/tracks/python/exercises/collatz-conjecture/approaches/ternary-operator
[conditional-expression]: https://docs.python.org/3/reference/expressions.html#conditional-expressions
[performance-article]: https://exercism.org/tracks/python/exercises/collatz-conjecture/articles/performance
From 04edbac62469cf1ed3d4ab49bce356b522328d37 Mon Sep 17 00:00:00 2001
From: Carl
Date: Tue, 10 Jan 2023 23:39:35 +0100
Subject: [PATCH 327/826] Fix uuid
---
.../practice/collatz-conjecture/.approaches/config.json | 6 +++---
exercises/practice/collatz-conjecture/.articles/config.json | 2 +-
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/exercises/practice/collatz-conjecture/.approaches/config.json b/exercises/practice/collatz-conjecture/.approaches/config.json
index 2ba3bf4c5b..5730c94d97 100644
--- a/exercises/practice/collatz-conjecture/.approaches/config.json
+++ b/exercises/practice/collatz-conjecture/.approaches/config.json
@@ -5,21 +5,21 @@
},
"approaches": [
{
- "uuid": "7b3aeaef-11ef-427c-9086-d2c6d4c022f1",
+ "uuid": "d92adc98-36fd-49bb-baf5-e4588387841c",
"slug": "if-else",
"title": "If/Else",
"blurb": "Use if and else",
"authors": ["bethanyg", "meatball133"]
},
{
- "uuid": "a2d8742b-9516-44c8-832d-111874430de0",
+ "uuid": "d7703aef-1510-4ec8-b6ce-ca608b5b8f70",
"slug": "ternary-operator",
"title": "Ternary operator",
"blurb": "Use a ternary operator",
"authors": ["bethanyg", "meatball133"]
},
{
- "uuid": "01da60d9-c133-41de-90fd-afbba7df2980",
+ "uuid": "b1220645-124a-4994-96c4-3b2b710fd562",
"slug": "recursion",
"title": "Recursion",
"blurb": "Use recursion",
diff --git a/exercises/practice/collatz-conjecture/.articles/config.json b/exercises/practice/collatz-conjecture/.articles/config.json
index afb7c6f9e6..1bab002169 100644
--- a/exercises/practice/collatz-conjecture/.articles/config.json
+++ b/exercises/practice/collatz-conjecture/.articles/config.json
@@ -1,7 +1,7 @@
{
"articles": [
{
- "uuid": "c6435e29-fd40-42a4-95ba-7c06b9b48f7a",
+ "uuid": "c37be489b-791a-463a-94c0-564ca0277da2",
"slug": "performance",
"title": "Performance deep dive",
"blurb": "Deep dive to find out the most performant approach for collatz conjecture.",
From 759ec3f804a409abbafa5c0882d4fa4c25e5e45b Mon Sep 17 00:00:00 2001
From: Carl
Date: Tue, 10 Jan 2023 23:43:16 +0100
Subject: [PATCH 328/826] fix uuid
---
exercises/practice/collatz-conjecture/.articles/config.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/exercises/practice/collatz-conjecture/.articles/config.json b/exercises/practice/collatz-conjecture/.articles/config.json
index 1bab002169..a9341a5153 100644
--- a/exercises/practice/collatz-conjecture/.articles/config.json
+++ b/exercises/practice/collatz-conjecture/.articles/config.json
@@ -1,7 +1,7 @@
{
"articles": [
{
- "uuid": "c37be489b-791a-463a-94c0-564ca0277da2",
+ "uuid": "f5071a22-f13a-4650-afea-b7aaee8f2b12",
"slug": "performance",
"title": "Performance deep dive",
"blurb": "Deep dive to find out the most performant approach for collatz conjecture.",
From d92309043536511b4fce63971f6aee2cc7fb8167 Mon Sep 17 00:00:00 2001
From: Carl
Date: Tue, 10 Jan 2023 00:40:42 +0100
Subject: [PATCH 329/826] Started
---
.../.approaches/Dictionary/content.md | 51 ++++++++
.../.approaches/Dictionary/snippet.txt | 2 +
.../.approaches/Enum/content.md | 44 +++++++
.../.approaches/Enum/snippet.txt | 8 ++
.../scrabble-score/.approaches/config.json | 36 ++++++
.../.approaches/introduction.md | 115 ++++++++++++++++++
.../.approaches/nested-tuple/content.md | 32 +++++
.../.approaches/nested-tuple/snippet.txt | 8 ++
.../.approaches/two-sequences/content.md | 25 ++++
.../.approaches/two-sequences/snippet.txt | 5 +
10 files changed, 326 insertions(+)
create mode 100644 exercises/practice/scrabble-score/.approaches/Dictionary/content.md
create mode 100644 exercises/practice/scrabble-score/.approaches/Dictionary/snippet.txt
create mode 100644 exercises/practice/scrabble-score/.approaches/Enum/content.md
create mode 100644 exercises/practice/scrabble-score/.approaches/Enum/snippet.txt
create mode 100644 exercises/practice/scrabble-score/.approaches/config.json
create mode 100644 exercises/practice/scrabble-score/.approaches/introduction.md
create mode 100644 exercises/practice/scrabble-score/.approaches/nested-tuple/content.md
create mode 100644 exercises/practice/scrabble-score/.approaches/nested-tuple/snippet.txt
create mode 100644 exercises/practice/scrabble-score/.approaches/two-sequences/content.md
create mode 100644 exercises/practice/scrabble-score/.approaches/two-sequences/snippet.txt
diff --git a/exercises/practice/scrabble-score/.approaches/Dictionary/content.md b/exercises/practice/scrabble-score/.approaches/Dictionary/content.md
new file mode 100644
index 0000000000..55e7b1357f
--- /dev/null
+++ b/exercises/practice/scrabble-score/.approaches/Dictionary/content.md
@@ -0,0 +1,51 @@
+# Dictionary
+
+```python
+LETTER_SCORES = {
+ 'A': 1,
+ 'E': 1,
+ 'I': 1,
+ 'O': 1,
+ 'U': 1,
+ 'L': 1,
+ 'N': 1,
+ 'R': 1,
+ 'S': 1,
+ 'T': 1,
+ 'D': 2,
+ 'G': 2,
+ 'B': 3,
+ 'C': 3,
+ 'M': 3,
+ 'P': 3,
+ 'F': 4,
+ 'H': 4,
+ 'V': 4,
+ 'W': 4,
+ 'Y': 4,
+ 'K': 5,
+ 'J': 8,
+ 'X': 8,
+ 'Q': 10,
+ 'Z': 10
+}
+
+def score(word):
+ return sum(LETTER_SCORES[letter.upper()] for letter in word)
+```
+
+The code starts with initializing a constant that is a [dictionary][dictionary] that holds all the letters as different key and their respective score as a value.
+Then it defines a function that takes a word as an argument.
+
+The function returns the built in function [`sum`][sum] that takes a [generator expression][generator-expersion] that iterates over the letters in the word.
+What a generator expression does is that it generates the values on the fly.
+Meaning that it doesn't have to use a lot of memory since it uses the last value and generates the next value.
+
+Under the generation a letter is given from the string, then it is converted to upcase, and then being looked up at inside of the dictionary and the value is returned.
+
+There is also a very similar approach that uses a dictionary transposition.
+Although that approach requires more computational calculation therefore is this approach more efficient.
+
+[dictionary]: https://docs.python.org/3/library/stdtypes.html#mapping-types-dict
+[generator-expersion]: https://peps.python.org/pep-0289/
+[sum]: https://docs.python.org/3/library/functions.html#sum
diff --git a/exercises/practice/scrabble-score/.approaches/Dictionary/snippet.txt b/exercises/practice/scrabble-score/.approaches/Dictionary/snippet.txt
new file mode 100644
index 0000000000..c0b4b3f970
--- /dev/null
+++ b/exercises/practice/scrabble-score/.approaches/Dictionary/snippet.txt
@@ -0,0 +1,2 @@
+def score(word):
+ return sum(LETTER_SCORES[letter.upper()] for letter in word)
\ No newline at end of file
diff --git a/exercises/practice/scrabble-score/.approaches/Enum/content.md b/exercises/practice/scrabble-score/.approaches/Enum/content.md
new file mode 100644
index 0000000000..a9fd97b97c
--- /dev/null
+++ b/exercises/practice/scrabble-score/.approaches/Enum/content.md
@@ -0,0 +1,44 @@
+# Enum
+
+```python
+from enum import IntEnum
+
+class Scrabble(IntEnum):
+ A = E = I = O = U = L = N = R = S = T = 1
+ D = G = 2
+ B = C = M = P = 3
+ F = H = V = W = Y = 4
+ K = 5
+ J = X = 8
+ Q = Z = 10
+
+def score(word):
+ return sum(Scrabble[char.upper()] for char in word)
+```
+
+This approach uses an [`enum`][enum] to define the score of each letter.
+An `enum` or known as a enumerations is sets of named constant and is immutable.
+`enum` was added to python standard library also known as stdlib in python 3.4.
+
+This approach uses an [`intEnum`][int-enum] it works very similar to a normal `enum` but it has the added benefit that the values are integers.
+Thereby acts like integers.
+
+To use an `intEnum` you need to [import][import] it using: `from enum import IntEnum`.
+Then you can define the `enum` class.
+
+The `enum` class is defined by using the [`class`][classes] keyword.
+Then you need to specify the name of the class.
+
+After that is constant declared by giving the constant capital letters and the value is assigned by using the `=` operator.
+This approach works by giving all the letters as constants and then value of the constant is the score of the letter.
+After the `enum` is defined, the `score` function is defined.
+
+The `score` function takes a word as a parameter.
+And uses the same [generator expression][generator-expersion] as the [dictionary approach][dictionary-approach].
+But instead of looking up the value in a dictionary it looks it up in the `enum` class.
+
+[classes]: https://docs.python.org/3/tutorial/classes.html
+[enum]: https://docs.python.org/3/library/enum.html
+[generator-expersion]: https://peps.python.org/pep-0289/
+[int-enum]: https://docs.python.org/3/library/enum.html#enum.IntEnum
+[import]: https://docs.python.org/3/reference/import.html
diff --git a/exercises/practice/scrabble-score/.approaches/Enum/snippet.txt b/exercises/practice/scrabble-score/.approaches/Enum/snippet.txt
new file mode 100644
index 0000000000..155b62b9c3
--- /dev/null
+++ b/exercises/practice/scrabble-score/.approaches/Enum/snippet.txt
@@ -0,0 +1,8 @@
+class Scrabble(IntEnum):
+ A = E = I = O = U = L = N = R = S = T = 1
+ D = G = 2
+ B = C = M = P = 3
+ F = H = V = W = Y = 4
+ K = 5
+ J = X = 8
+ Q = Z = 10
\ No newline at end of file
diff --git a/exercises/practice/scrabble-score/.approaches/config.json b/exercises/practice/scrabble-score/.approaches/config.json
new file mode 100644
index 0000000000..0fef67cf03
--- /dev/null
+++ b/exercises/practice/scrabble-score/.approaches/config.json
@@ -0,0 +1,36 @@
+{
+ "introduction": {
+ "authors": ["meatball133", "bethanyg"],
+ "contributors": []
+ },
+ "approaches": [
+ {
+ "uuid": "62f556c0-27a6-43e5-80ea-d7abd921f0e7",
+ "slug": "enum",
+ "title": "Enum",
+ "blurb": "Define a enum to solve the problem",
+ "authors": ["meatball133", "bethanyg"]
+ },
+ {
+ "uuid": "21c0fe14-585f-4e14-9f31-1d294a8a622c",
+ "slug": "dictionary",
+ "title": "Dictionary",
+ "blurb": "Dictionary approach",
+ "authors": ["meatball133", "bethanyg"]
+ },
+ {
+ "uuid": "34911050-b424-4289-9d3f-aa5f0e8f1630",
+ "slug": "nested-tuple",
+ "title": "Nested Tuple",
+ "blurb": "Nested Tuple approach",
+ "authors": ["meatball133", "bethanyg"]
+ },
+ {
+ "uuid": "e9b8e10b-23c7-4c2a-afbe-3c3499468919",
+ "slug": "two-sequences",
+ "title": "Two Sequences",
+ "blurb": "Two Sequences approach",
+ "authors": ["meatball133", "bethanyg"]
+ }
+ ]
+}
diff --git a/exercises/practice/scrabble-score/.approaches/introduction.md b/exercises/practice/scrabble-score/.approaches/introduction.md
new file mode 100644
index 0000000000..4a29440827
--- /dev/null
+++ b/exercises/practice/scrabble-score/.approaches/introduction.md
@@ -0,0 +1,115 @@
+# Introduction
+
+There are various ways to solve `scrabble-score`.
+This approaches document shows different strategies to solve this exercise
+
+## General guidance
+
+The goal of this exercise is to write a function that calculates the scrabble score for a given word.
+The problem is that
+
+## Approach: Using a single dictionary
+
+Using a single dictionary is an approach, it is simple and fast.
+It is also very pythonic.
+
+```python
+LETTER_SCORES = {
+ 'A': 1,
+ 'E': 1,
+ 'I': 1,
+ 'O': 1,
+ 'U': 1,
+ 'L': 1,
+ 'N': 1,
+ 'R': 1,
+ 'S': 1,
+ 'T': 1,
+ 'D': 2,
+ 'G': 2,
+ 'B': 3,
+ 'C': 3,
+ 'M': 3,
+ 'P': 3,
+ 'F': 4,
+ 'H': 4,
+ 'V': 4,
+ 'W': 4,
+ 'Y': 4,
+ 'K': 5,
+ 'J': 8,
+ 'X': 8,
+ 'Q': 10,
+ 'Z': 10
+}
+
+def score(word):
+ return sum(LETTER_SCORES[letter.upper()] for letter in word)
+```
+
+For more information, check the [Dictionary Approach][dictionary-approach].
+
+## Approach: Using two sequences
+
+Using two sequences is an approach, it is fast.
+Although the reason you might not want to do this is that it is hard to read.
+
+```python
+KEYS = "AEIOULNRSTDGBCMPFHVWYKJXQZ"
+SCORES = [1] * 10 + [2] * 2 + [3] * 4 + [4] * 5 + [5] * 1 + [8] * 2 +[10] * 2
+
+def score(word):
+ return sum(SCORES[KEYS.index(letter.upper())] for letter in word)
+```
+
+For more information, check the [Two Sequences Approach][two-sequences-approach].
+
+## Approach: Enum
+
+Using an `enum` is an approach, it is short and easy to read.
+Although it is more complicated since it uses a class.
+
+```python
+from enum import IntEnum
+
+class Scrabble(IntEnum):
+ A = E = I = O = U = L = N = R = S = T = 1
+ D = G = 2
+ B = C = M = P = 3
+ F = H = V = W = Y = 4
+ K = 5
+ J = X = 8
+ Q = Z = 10
+
+def score(word):
+ return sum(Scrabble[char.upper()] for char in word)
+```
+
+You can read more about how to achieve this optimization in: [Enum Approach][enum-approach].
+
+## Approach: Using a nested tuple
+
+Tuples in python is more memory efficent than using a dictonary in python.
+Although this solution since it is iterating over a tuple for every letter so is it slower.
+
+```python
+LETTERS_OF_SCORE = (
+ ("AEIOULNRST", 1),
+ ("DG", 2),
+ ("BCMP", 3),
+ ("FHVWY", 4),
+ ("K", 5),
+ ("JX", 8),
+ ("QZ", 10),
+)
+
+def score(word):
+ return sum(for character in word for letters, score in LETTERS_OF_SCORE if character in letters)
+```
+
+For more information, check the [Nested Tuple Approach][nested-tuple-approach].
+
+[dictionary-approach]: https://exercism.org/tracks/python/exercises/scrabble-score/approaches/dictionary
+[enum-approach]: https://exercism.org/tracks/python/exercises/scrabble-score/approaches/enum
+[nested-tuple-approach]: https://exercism.org/tracks/python/exercises/scrabble-score/approaches/nested-tuple
+[two-sequences-approach]: https://exercism.org/tracks/python/exercises/scrabble-score/approaches/two-sequences
diff --git a/exercises/practice/scrabble-score/.approaches/nested-tuple/content.md b/exercises/practice/scrabble-score/.approaches/nested-tuple/content.md
new file mode 100644
index 0000000000..ff92681738
--- /dev/null
+++ b/exercises/practice/scrabble-score/.approaches/nested-tuple/content.md
@@ -0,0 +1,32 @@
+# Nested Tuple
+
+```python
+LETTERS_OF_SCORE = (
+ ("AEIOULNRST", 1),
+ ("DG", 2),
+ ("BCMP", 3),
+ ("FHVWY", 4),
+ ("K", 5),
+ ("JX", 8),
+ ("QZ", 10),
+)
+
+def score(word):
+ return sum(score for character in word for letters, score in LETTERS_OF_SCORE if character.upper() in letters)
+```
+
+The code starts with initializing a constant with a [tuple][tuple] of tuples.
+Inside of the inner tuples there is 2 values, the first value is a string of letters and the second value is the score of the letters.
+
+Then it defines a function that takes a word as an argument.
+The function returns a [generator expression][generator-expersion] similar to the [dictionary approach][dictionary-approach].
+The difference is that it uses a nested [for loop][for-loop] to iterate over the letters and the tuples.
+There we iterate over the characters in the word and then iterate over the tuples.
+There the tuple is unpacked into the letters and the score.
+You can read more about unpacking in the [concept:python/unpacking-and-multiple-assignment]().
+
+Then we check if the character is in the letters and if it is we return the score.
+
+[tuple]: https://docs.python.org/3/tutorial/datastructures.html#tuples-and-sequences
+[generator-expersion]: https://peps.python.org/pep-0289/
+[for-loop]: https://realpython.com/python-for-loop/
diff --git a/exercises/practice/scrabble-score/.approaches/nested-tuple/snippet.txt b/exercises/practice/scrabble-score/.approaches/nested-tuple/snippet.txt
new file mode 100644
index 0000000000..d2b19ebbbf
--- /dev/null
+++ b/exercises/practice/scrabble-score/.approaches/nested-tuple/snippet.txt
@@ -0,0 +1,8 @@
+LETTERS_OF_SCORE = (
+ ("AEIOULNRST", 1),
+ ("DG", 2),
+ ("BCMP", 3),
+ ("FHVWY", 4),
+ ("K", 5),
+ ("JX", 8),
+ ("QZ", 10),)
\ No newline at end of file
diff --git a/exercises/practice/scrabble-score/.approaches/two-sequences/content.md b/exercises/practice/scrabble-score/.approaches/two-sequences/content.md
new file mode 100644
index 0000000000..f677bb4b43
--- /dev/null
+++ b/exercises/practice/scrabble-score/.approaches/two-sequences/content.md
@@ -0,0 +1,25 @@
+# Two sequences
+
+```python
+KEYS = "AEIOULNRSTDGBCMPFHVWYKJXQZ"
+SCORES = [1] * 10 + [2] * 2 + [3] * 4 + [4] * 5 + [5] * 1 + [8] * 2 + [10] * 2
+
+def score(word):
+ return sum(SCORES[KEYS.index(letter.upper())] for letter in word)
+```
+
+This approach uses a string and a [list][list], both of these data types belongs to the data type [sequences][sequence].
+It has a constant with a string with letters and then it has a constant of a list with corresponding score for the same index as the string.
+
+The `score` function takes a word as a parameter.
+And uses the same [generator expression][generator-expersion] as the [dictionary approach][dictionary-approach].
+
+The difference is that instead of using a [dictionary][dictionary] and looked up the score inside.
+This approach gets the index of the letter in the KEYS constant and then then looks up the value for that index in SCORES list.
+Then takes that value and return that to the generator expression.
+
+[dictionary]: https://docs.python.org/3/library/stdtypes.html#mapping-types-dict
+[dictionary-approach]: https://exercism.org/tracks/python/exercises/scrabble-score/approaches/dictionary
+[list]: https://docs.python.org/3/tutorial/datastructures.html#more-on-lists
+[sequence]: https://docs.python.org/3/library/stdtypes.html#sequence-types-list-tuple-range
+[generator-expersion]: https://peps.python.org/pep-0289/
diff --git a/exercises/practice/scrabble-score/.approaches/two-sequences/snippet.txt b/exercises/practice/scrabble-score/.approaches/two-sequences/snippet.txt
new file mode 100644
index 0000000000..fd37416b39
--- /dev/null
+++ b/exercises/practice/scrabble-score/.approaches/two-sequences/snippet.txt
@@ -0,0 +1,5 @@
+KEYS = "AEIOULNRSTDGBCMPFHVWYKJXQZ"
+SCORES = [1] * 10 + [2] * 2 + [3] * 4 + [4] * 5 + [5] * 1 + [8] * 2 +[10] * 2
+
+def score(word):
+ return sum(SCORES[KEYS.index(letter.upper())] for letter in word)
From 85865aa8929a50c52b8fbecd7097ccd0aae20c6f Mon Sep 17 00:00:00 2001
From: Carl
Date: Tue, 10 Jan 2023 00:44:01 +0100
Subject: [PATCH 330/826] Fix dictonary name
---
.../.approaches/{Dictionary => dictionary}/content.md | 0
.../.approaches/{Dictionary => dictionary}/snippet.txt | 0
.../practice/scrabble-score/.approaches/{Enum => enum}/content.md | 0
.../scrabble-score/.approaches/{Enum => enum}/snippet.txt | 0
4 files changed, 0 insertions(+), 0 deletions(-)
rename exercises/practice/scrabble-score/.approaches/{Dictionary => dictionary}/content.md (100%)
rename exercises/practice/scrabble-score/.approaches/{Dictionary => dictionary}/snippet.txt (100%)
rename exercises/practice/scrabble-score/.approaches/{Enum => enum}/content.md (100%)
rename exercises/practice/scrabble-score/.approaches/{Enum => enum}/snippet.txt (100%)
diff --git a/exercises/practice/scrabble-score/.approaches/Dictionary/content.md b/exercises/practice/scrabble-score/.approaches/dictionary/content.md
similarity index 100%
rename from exercises/practice/scrabble-score/.approaches/Dictionary/content.md
rename to exercises/practice/scrabble-score/.approaches/dictionary/content.md
diff --git a/exercises/practice/scrabble-score/.approaches/Dictionary/snippet.txt b/exercises/practice/scrabble-score/.approaches/dictionary/snippet.txt
similarity index 100%
rename from exercises/practice/scrabble-score/.approaches/Dictionary/snippet.txt
rename to exercises/practice/scrabble-score/.approaches/dictionary/snippet.txt
diff --git a/exercises/practice/scrabble-score/.approaches/Enum/content.md b/exercises/practice/scrabble-score/.approaches/enum/content.md
similarity index 100%
rename from exercises/practice/scrabble-score/.approaches/Enum/content.md
rename to exercises/practice/scrabble-score/.approaches/enum/content.md
diff --git a/exercises/practice/scrabble-score/.approaches/Enum/snippet.txt b/exercises/practice/scrabble-score/.approaches/enum/snippet.txt
similarity index 100%
rename from exercises/practice/scrabble-score/.approaches/Enum/snippet.txt
rename to exercises/practice/scrabble-score/.approaches/enum/snippet.txt
From 12e692f032154f13fc610549dd7396807b41b42e Mon Sep 17 00:00:00 2001
From: meatball
Date: Tue, 10 Jan 2023 10:10:16 +0100
Subject: [PATCH 331/826] Fixes
---
.../.approaches/dictionary/content.md | 4 ++--
.../scrabble-score/.approaches/enum/content.md | 12 ++++++------
.../scrabble-score/.approaches/introduction.md | 12 ++++++------
.../.approaches/nested-tuple/content.md | 14 ++++++++------
.../.approaches/two-sequences/content.md | 9 +++++----
5 files changed, 27 insertions(+), 24 deletions(-)
diff --git a/exercises/practice/scrabble-score/.approaches/dictionary/content.md b/exercises/practice/scrabble-score/.approaches/dictionary/content.md
index 55e7b1357f..b046481804 100644
--- a/exercises/practice/scrabble-score/.approaches/dictionary/content.md
+++ b/exercises/practice/scrabble-score/.approaches/dictionary/content.md
@@ -34,8 +34,8 @@ def score(word):
return sum(LETTER_SCORES[letter.upper()] for letter in word)
```
-The code starts with initializing a constant that is a [dictionary][dictionary] that holds all the letters as different key and their respective score as a value.
-Then it defines a function that takes a word as an argument.
+The code starts with initializing a constant that is a [dictionary][dictionary] there each letter is a key and their respective score as a value.
+Then a function is defined that takes a word as an argument.
The function returns the built in function [`sum`][sum] that takes a [generator expression][generator-expersion] that iterates over the letters in the word.
What a generator expression does is that it generates the values on the fly.
diff --git a/exercises/practice/scrabble-score/.approaches/enum/content.md b/exercises/practice/scrabble-score/.approaches/enum/content.md
index a9fd97b97c..cad5b6bdec 100644
--- a/exercises/practice/scrabble-score/.approaches/enum/content.md
+++ b/exercises/practice/scrabble-score/.approaches/enum/content.md
@@ -13,12 +13,12 @@ class Scrabble(IntEnum):
Q = Z = 10
def score(word):
- return sum(Scrabble[char.upper()] for char in word)
+ return sum(Scrabble[character.upper()] for character in word)
```
This approach uses an [`enum`][enum] to define the score of each letter.
-An `enum` or known as a enumerations is sets of named constant and is immutable.
-`enum` was added to python standard library also known as stdlib in python 3.4.
+An `enum` or known as an **enumeration** is sets of named constant and is immutable.
+`enum` was added to python standard library (_also known as stdlib_) in python 3.4.
This approach uses an [`intEnum`][int-enum] it works very similar to a normal `enum` but it has the added benefit that the values are integers.
Thereby acts like integers.
@@ -29,13 +29,13 @@ Then you can define the `enum` class.
The `enum` class is defined by using the [`class`][classes] keyword.
Then you need to specify the name of the class.
-After that is constant declared by giving the constant capital letters and the value is assigned by using the `=` operator.
+After that is the constant in the enum declared by giving the constant capital letters and the value is assigned by using the `=` operator.
This approach works by giving all the letters as constants and then value of the constant is the score of the letter.
After the `enum` is defined, the `score` function is defined.
The `score` function takes a word as a parameter.
-And uses the same [generator expression][generator-expersion] as the [dictionary approach][dictionary-approach].
-But instead of looking up the value in a dictionary it looks it up in the `enum` class.
+And uses the same [generator expression][generator-expersion] as the [dictionary approach][dictionary-approach] but with a slight modification.
+Which is that instead of looking up the value in a dictionary it looks it up in the `enum` class.
[classes]: https://docs.python.org/3/tutorial/classes.html
[enum]: https://docs.python.org/3/library/enum.html
diff --git a/exercises/practice/scrabble-score/.approaches/introduction.md b/exercises/practice/scrabble-score/.approaches/introduction.md
index 4a29440827..2891e55a6d 100644
--- a/exercises/practice/scrabble-score/.approaches/introduction.md
+++ b/exercises/practice/scrabble-score/.approaches/introduction.md
@@ -1,12 +1,12 @@
# Introduction
There are various ways to solve `scrabble-score`.
-This approaches document shows different strategies to solve this exercise
+This approache document shows different strategies to solve this exercise.
## General guidance
The goal of this exercise is to write a function that calculates the scrabble score for a given word.
-The problem is that
+The problem is that the scrabble score is calculated by the sum of the scores of each letter in the word.
## Approach: Using a single dictionary
@@ -51,7 +51,7 @@ For more information, check the [Dictionary Approach][dictionary-approach].
## Approach: Using two sequences
-Using two sequences is an approach, it is fast.
+Using two sequences is an approach, it removes the need of using a nested data structure or a dictonary.
Although the reason you might not want to do this is that it is hard to read.
```python
@@ -67,7 +67,7 @@ For more information, check the [Two Sequences Approach][two-sequences-approach]
## Approach: Enum
Using an `enum` is an approach, it is short and easy to read.
-Although it is more complicated since it uses a class.
+Although it is more complicated since it uses a oop (object oriented programmering) elements.
```python
from enum import IntEnum
@@ -82,10 +82,10 @@ class Scrabble(IntEnum):
Q = Z = 10
def score(word):
- return sum(Scrabble[char.upper()] for char in word)
+ return sum(Scrabble[letter.upper()] for letter in word)
```
-You can read more about how to achieve this optimization in: [Enum Approach][enum-approach].
+For more information, check the [Enum Approach][enum-approach].
## Approach: Using a nested tuple
diff --git a/exercises/practice/scrabble-score/.approaches/nested-tuple/content.md b/exercises/practice/scrabble-score/.approaches/nested-tuple/content.md
index ff92681738..4498d1841f 100644
--- a/exercises/practice/scrabble-score/.approaches/nested-tuple/content.md
+++ b/exercises/practice/scrabble-score/.approaches/nested-tuple/content.md
@@ -15,13 +15,15 @@ def score(word):
return sum(score for character in word for letters, score in LETTERS_OF_SCORE if character.upper() in letters)
```
-The code starts with initializing a constant with a [tuple][tuple] of tuples.
-Inside of the inner tuples there is 2 values, the first value is a string of letters and the second value is the score of the letters.
+The code starts with initializing a constant with a [tuple][tuple] of tuples (_also known as a nested tuple_).
+Inside of the inner tuples there is 2 values, the first value is a string of letters and the second value is the score for the letters.
-Then it defines a function that takes a word as an argument.
-The function returns a [generator expression][generator-expersion] similar to the [dictionary approach][dictionary-approach].
-The difference is that it uses a nested [for loop][for-loop] to iterate over the letters and the tuples.
-There we iterate over the characters in the word and then iterate over the tuples.
+Then a function is defined that takes a word as an argument.
+The function returns a [generator expression][generator-expersion] similar to the [dictionary approach][dictionary-approach] but has some slight modifcations.
+
+The difference is that this one uses a nested [for loop][for-loop] to iterate over the letters and the tuples.
+We first iterate over the characters in the word and then iterate over the tuples.
+Which means that for each letter are we iterating over all of the tuples.
There the tuple is unpacked into the letters and the score.
You can read more about unpacking in the [concept:python/unpacking-and-multiple-assignment]().
diff --git a/exercises/practice/scrabble-score/.approaches/two-sequences/content.md b/exercises/practice/scrabble-score/.approaches/two-sequences/content.md
index f677bb4b43..944da04298 100644
--- a/exercises/practice/scrabble-score/.approaches/two-sequences/content.md
+++ b/exercises/practice/scrabble-score/.approaches/two-sequences/content.md
@@ -8,13 +8,14 @@ def score(word):
return sum(SCORES[KEYS.index(letter.upper())] for letter in word)
```
-This approach uses a string and a [list][list], both of these data types belongs to the data type [sequences][sequence].
-It has a constant with a string with letters and then it has a constant of a list with corresponding score for the same index as the string.
+This approach uses a string and a [list][list], both of these data types belongs to the parent data type [sequences][sequence].
+The code starts with defining a string constant with letters.
+Then another constant is definded which is a list with corresponding score for the same index as the string.
The `score` function takes a word as a parameter.
-And uses the same [generator expression][generator-expersion] as the [dictionary approach][dictionary-approach].
+And uses the same [generator expression][generator-expersion] as the [dictionary approach][dictionary-approach] with some slight modifications.
-The difference is that instead of using a [dictionary][dictionary] and looked up the score inside.
+The difference is that instead of using a [dictionary][dictionary] and looking up the score inside of a dictonary.
This approach gets the index of the letter in the KEYS constant and then then looks up the value for that index in SCORES list.
Then takes that value and return that to the generator expression.
From 906676a8d06090b7bcb6222603c7b6ae456044d9 Mon Sep 17 00:00:00 2001
From: meatball <69751659+meatball133@users.noreply.github.com>
Date: Wed, 11 Jan 2023 21:42:05 +0100
Subject: [PATCH 332/826] Apply suggestions from code review
Co-authored-by: BethanyG
---
.../.approaches/dictionary/content.md | 76 ++++++++++---------
.../.approaches/enum/content.md | 44 ++++++-----
.../.approaches/introduction.md | 64 ++++++----------
.../.approaches/nested-tuple/content.md | 24 +++---
.../.approaches/two-sequences/content.md | 18 ++---
5 files changed, 107 insertions(+), 119 deletions(-)
diff --git a/exercises/practice/scrabble-score/.approaches/dictionary/content.md b/exercises/practice/scrabble-score/.approaches/dictionary/content.md
index b046481804..d220150eb3 100644
--- a/exercises/practice/scrabble-score/.approaches/dictionary/content.md
+++ b/exercises/practice/scrabble-score/.approaches/dictionary/content.md
@@ -2,50 +2,52 @@
```python
LETTER_SCORES = {
- 'A': 1,
- 'E': 1,
- 'I': 1,
- 'O': 1,
- 'U': 1,
- 'L': 1,
- 'N': 1,
- 'R': 1,
- 'S': 1,
- 'T': 1,
- 'D': 2,
- 'G': 2,
- 'B': 3,
- 'C': 3,
- 'M': 3,
- 'P': 3,
- 'F': 4,
- 'H': 4,
- 'V': 4,
- 'W': 4,
- 'Y': 4,
- 'K': 5,
- 'J': 8,
- 'X': 8,
- 'Q': 10,
- 'Z': 10
+ 'A': 1, 'E': 1, 'I': 1, 'O': 1, 'U': 1,
+ 'L': 1, 'N': 1, 'R': 1, 'S': 1, 'T': 1,
+ 'D': 2, 'G': 2, 'B': 3, 'C': 3, 'M': 3,
+ 'P': 3, 'F': 4, 'H': 4, 'V': 4, 'W': 4,
+ 'Y': 4, 'K': 5, 'J': 8, 'X': 8, 'Q': 10, 'Z': 10
}
-
def score(word):
- return sum(LETTER_SCORES[letter.upper()] for letter in word)
+ return sum(LETTER_SCORES[letter] for letter in word.upper())
```
-The code starts with initializing a constant that is a [dictionary][dictionary] there each letter is a key and their respective score as a value.
-Then a function is defined that takes a word as an argument.
+This code starts with defining a constant LETTER_SCORES as a dictionary ([concept:python/dicts](https://docs.python.org/3/library/stdtypes.html#mapping-types-dict)) where each letter is a key and the corresponding score is a value.
+Then the `score` function is defined, which takes a `` as an argument.
+
+The function returns the total score for the word using the built-in function [`sum`][sum].
+ Sum is passed a [generator expression][generator-expression] that iterates over the letters in the word, looking up each score in LETTER_SCORES.
+The generator expression produces the score values on the fly.
+This means that it doesn't use memory to store all the values from LETTER_SCORES.
+Instead, each value is looked up as needed by `sum`.
+
+Within the generator expression, the word is converted from lower to uppercase.
+Each letter of the word is looked up in LETTER_SCORES, and the score value is yielded to `sum` as `sum` iterates over the expression.
+This is almost exactly the same process as using a `list comprehension`.
+However, a `list comprehension` would look up the values and save them into a `list` in memory.
+`sum` would then "unpack" or iterate over the `list`.
+
+A variation on this dictionary approach is to use a dictionary transposition.
+
+```python
+LETTER_SCORES = {
+ 1: {'A', 'E', 'I', 'O', 'U', 'L', 'N', 'R', 'S', 'T'},
+ 2: {'D', 'G'},
+ 3: {'B', 'C', 'M', 'P'},
+ 4: {'F', 'H', 'V', 'W', 'Y'},
+ 5: {'K'},
+ 8: {'J', 'X'},
+ 10: {'Q', 'Z'}
+}
+
+def score(word):
+ return sum(next(score for score, letters in LETTER_SCORES.items() if character in letters) for character in word.upper())
-The function returns the built in function [`sum`][sum] that takes a [generator expression][generator-expersion] that iterates over the letters in the word.
-What a generator expression does is that it generates the values on the fly.
-Meaning that it doesn't have to use a lot of memory since it uses the last value and generates the next value.
-Under the generation a letter is given from the string, then it is converted to upcase, and then being looked up at inside of the dictionary and the value is returned.
+However, transposing the dictionary so that the keys are the score and the values are the letters requires more computational calculation (_a loop within a loop_) and is harder to read.
+ Therefore, arranging the dictionary by letter is both more efficient and easier to understand.
-There is also a very similar approach that uses a dictionary transposition.
-Although that approach requires more computational calculation therefore is this approach more efficient.
[dictionary]: https://docs.python.org/3/library/stdtypes.html#mapping-types-dict
-[generator-expersion]: https://peps.python.org/pep-0289/
+[generator-expression]: https://peps.python.org/pep-0289/
[sum]: https://docs.python.org/3/library/functions.html#sum
diff --git a/exercises/practice/scrabble-score/.approaches/enum/content.md b/exercises/practice/scrabble-score/.approaches/enum/content.md
index cad5b6bdec..188f54000a 100644
--- a/exercises/practice/scrabble-score/.approaches/enum/content.md
+++ b/exercises/practice/scrabble-score/.approaches/enum/content.md
@@ -13,32 +13,40 @@ class Scrabble(IntEnum):
Q = Z = 10
def score(word):
- return sum(Scrabble[character.upper()] for character in word)
-```
+ return sum(Scrabble[character] for character in word.upper())
-This approach uses an [`enum`][enum] to define the score of each letter.
-An `enum` or known as an **enumeration** is sets of named constant and is immutable.
-`enum` was added to python standard library (_also known as stdlib_) in python 3.4.
+This approach uses an [`Enum`][enum] to define the score of each letter.
+An [`Enum`][enum] (_also known as an **enumeration**_) is an object with named attributes assigned unique values.
+These attributes are referred to as the enumeration _members_.
+`Enum`s can be iterated over to return their members in definition order.
+Values can be accessed via index syntax using the member name (_similar to how a dictionary lookup works_) .
+`Enum`s are immutable, and their members function as constants.
+The `enum` module was added to python standard library (_also known as stdlib_) in Python 3.4.
-This approach uses an [`intEnum`][int-enum] it works very similar to a normal `enum` but it has the added benefit that the values are integers.
-Thereby acts like integers.
+This approach uses an [`IntEnum`][int-enum].
+An `IntEnum` is very similar to an `Enum`, but restricts assigned values to `int`s.
+This allows the `IntEnum` to act as a collection of integers.
+In fact, `IntEnum`s are considered subclasses of `int`s.
-To use an `intEnum` you need to [import][import] it using: `from enum import IntEnum`.
-Then you can define the `enum` class.
+To use an `IntEnum` you need to first [import][import] it using: `from enum import IntEnum`.
+Then you can define your `IntEnum` subclass.
-The `enum` class is defined by using the [`class`][classes] keyword.
-Then you need to specify the name of the class.
+The `IntEnum` subclass is defined by using the [`class`][classes] keyword, followed by the name you are using for the class, and then the `IntEnum` class you are subclassing in parenthesis:
-After that is the constant in the enum declared by giving the constant capital letters and the value is assigned by using the `=` operator.
-This approach works by giving all the letters as constants and then value of the constant is the score of the letter.
-After the `enum` is defined, the `score` function is defined.
+```python
+class ClassName(IntEnum):
+
+Member names are declared as constants (ALL CAPS) and assigned values using the `=` operator.
+
+This approach works by creating all the uppercase letters as members with their values being the score.
+After the `IntEnum` is defined, the `score` function is defined.
-The `score` function takes a word as a parameter.
-And uses the same [generator expression][generator-expersion] as the [dictionary approach][dictionary-approach] but with a slight modification.
-Which is that instead of looking up the value in a dictionary it looks it up in the `enum` class.
+The `score` function takes a word as an argument.
+The `score` function uses the same [generator expression][generator-expression] as the [dictionary approach][dictionary-approach], but with a slight modification.
+Instead of looking up the value in a _dictionary_, it looks up the `InEnum` class member value.
[classes]: https://docs.python.org/3/tutorial/classes.html
[enum]: https://docs.python.org/3/library/enum.html
-[generator-expersion]: https://peps.python.org/pep-0289/
+[generator-expression]: https://peps.python.org/pep-0289/
[int-enum]: https://docs.python.org/3/library/enum.html#enum.IntEnum
[import]: https://docs.python.org/3/reference/import.html
diff --git a/exercises/practice/scrabble-score/.approaches/introduction.md b/exercises/practice/scrabble-score/.approaches/introduction.md
index 2891e55a6d..1b94ede3fe 100644
--- a/exercises/practice/scrabble-score/.approaches/introduction.md
+++ b/exercises/practice/scrabble-score/.approaches/introduction.md
@@ -1,73 +1,52 @@
# Introduction
There are various ways to solve `scrabble-score`.
-This approache document shows different strategies to solve this exercise.
+This approaches document shows different strategies to solve this exercise.
## General guidance
The goal of this exercise is to write a function that calculates the scrabble score for a given word.
-The problem is that the scrabble score is calculated by the sum of the scores of each letter in the word.
+The challenge is that the scrabble score is calculated by summing the scores of individual letters in a word.
+The student needs to find an efficient and easily accessed way to store individual letter scores for lookup when processing different words.
## Approach: Using a single dictionary
-Using a single dictionary is an approach, it is simple and fast.
-It is also very pythonic.
+Using a single dictionary for letter lookup is simple and fast.
+It is also very pythonic, and could be considered the canonical approach to this exercise.
```python
LETTER_SCORES = {
- 'A': 1,
- 'E': 1,
- 'I': 1,
- 'O': 1,
- 'U': 1,
- 'L': 1,
- 'N': 1,
- 'R': 1,
- 'S': 1,
- 'T': 1,
- 'D': 2,
- 'G': 2,
- 'B': 3,
- 'C': 3,
- 'M': 3,
- 'P': 3,
- 'F': 4,
- 'H': 4,
- 'V': 4,
- 'W': 4,
- 'Y': 4,
- 'K': 5,
- 'J': 8,
- 'X': 8,
- 'Q': 10,
- 'Z': 10
+ 'A': 1, 'E': 1, 'I': 1, 'O': 1, 'U': 1,
+ 'L': 1, 'N': 1, 'R': 1, 'S': 1, 'T': 1,
+ 'D': 2, 'G': 2, 'B': 3, 'C': 3, 'M': 3,
+ 'P': 3, 'F': 4, 'H': 4, 'V': 4, 'W': 4,
+ 'Y': 4, 'K': 5, 'J': 8, 'X': 8, 'Q': 10, 'Z': 10
}
def score(word):
- return sum(LETTER_SCORES[letter.upper()] for letter in word)
-```
+ return sum(LETTER_SCORES[letter] for letter in word.upper())
For more information, check the [Dictionary Approach][dictionary-approach].
## Approach: Using two sequences
-Using two sequences is an approach, it removes the need of using a nested data structure or a dictonary.
-Although the reason you might not want to do this is that it is hard to read.
+Using two sequences removes the need to use a nested data structure or a dictionary.
+Although you might not want to use this approach because it is hard to read and maintain.
```python
KEYS = "AEIOULNRSTDGBCMPFHVWYKJXQZ"
SCORES = [1] * 10 + [2] * 2 + [3] * 4 + [4] * 5 + [5] * 1 + [8] * 2 +[10] * 2
def score(word):
- return sum(SCORES[KEYS.index(letter.upper())] for letter in word)
+ return sum(SCORES[KEYS.index(letter] for letter in word.upper())
```
For more information, check the [Two Sequences Approach][two-sequences-approach].
## Approach: Enum
-Using an `enum` is an approach, it is short and easy to read.
-Although it is more complicated since it uses a oop (object oriented programmering) elements.
+Using an `Enum` is is short and easy to read.
+Although creating an `Enum` can be more complicated since it uses OOP (object oriented programming).
```python
from enum import IntEnum
@@ -82,15 +61,16 @@ class Scrabble(IntEnum):
Q = Z = 10
def score(word):
- return sum(Scrabble[letter.upper()] for letter in word)
+ return sum(Scrabble[letter] for letter in word.upper())
```
For more information, check the [Enum Approach][enum-approach].
## Approach: Using a nested tuple
-Tuples in python is more memory efficent than using a dictonary in python.
-Although this solution since it is iterating over a tuple for every letter so is it slower.
+Using a tuple in Python is generally more memory efficient than using a dictionary.
+However, this solution requires iterating over the entire `tuple` for every letter in order to score a full word.
+This makes the solution slower than the dictionary approach.
```python
LETTERS_OF_SCORE = (
@@ -104,8 +84,8 @@ LETTERS_OF_SCORE = (
)
def score(word):
- return sum(for character in word for letters, score in LETTERS_OF_SCORE if character in letters)
-```
+ return sum(score for character in word.upper() for
+ letters, score in LETTERS_OF_SCORE if character in letters)
For more information, check the [Nested Tuple Approach][nested-tuple-approach].
diff --git a/exercises/practice/scrabble-score/.approaches/nested-tuple/content.md b/exercises/practice/scrabble-score/.approaches/nested-tuple/content.md
index 4498d1841f..113c385f0a 100644
--- a/exercises/practice/scrabble-score/.approaches/nested-tuple/content.md
+++ b/exercises/practice/scrabble-score/.approaches/nested-tuple/content.md
@@ -12,23 +12,23 @@ LETTERS_OF_SCORE = (
)
def score(word):
- return sum(score for character in word for letters, score in LETTERS_OF_SCORE if character.upper() in letters)
-```
+ return sum(score for character in word.upper() for
+ letters, score in LETTERS_OF_SCORE if character in letters)
-The code starts with initializing a constant with a [tuple][tuple] of tuples (_also known as a nested tuple_).
-Inside of the inner tuples there is 2 values, the first value is a string of letters and the second value is the score for the letters.
+The code starts with defining a constant, LETTERS_OF_SCORE as a [`tuple`][tuple] of tuples (_also known as a nested tuple_).
+Inside of the inner tuples are 2 values, the first value is a string of letters and the second value is the score for those letters.
-Then a function is defined that takes a word as an argument.
-The function returns a [generator expression][generator-expersion] similar to the [dictionary approach][dictionary-approach] but has some slight modifcations.
+Next, the `score` function is defined, taking a word as an argument.
+The `score` function uses a [generator expression][generator-expression] similar to the [dictionary approach][dictionary-approach] with some slight modifications.
-The difference is that this one uses a nested [for loop][for-loop] to iterate over the letters and the tuples.
-We first iterate over the characters in the word and then iterate over the tuples.
-Which means that for each letter are we iterating over all of the tuples.
-There the tuple is unpacked into the letters and the score.
+This particular approach uses a _nested_ [for loop][for-loop] to iterate over the letters and the tuples.
+We first iterate over the characters in the word and then the tuples.
+Which means that for **_each letter_** we iterate over **all** of the tuples.
+Each iteration, the tuple is unpacked into the letters and their corresponding score.
You can read more about unpacking in the [concept:python/unpacking-and-multiple-assignment]().
-Then we check if the character is in the letters and if it is we return the score.
+Then the code checks if the character is in the unpacked letters and if it is we return its score.
[tuple]: https://docs.python.org/3/tutorial/datastructures.html#tuples-and-sequences
-[generator-expersion]: https://peps.python.org/pep-0289/
+[generator-expression]: https://peps.python.org/pep-0289/
[for-loop]: https://realpython.com/python-for-loop/
diff --git a/exercises/practice/scrabble-score/.approaches/two-sequences/content.md b/exercises/practice/scrabble-score/.approaches/two-sequences/content.md
index 944da04298..83f0ea1359 100644
--- a/exercises/practice/scrabble-score/.approaches/two-sequences/content.md
+++ b/exercises/practice/scrabble-score/.approaches/two-sequences/content.md
@@ -5,19 +5,17 @@ KEYS = "AEIOULNRSTDGBCMPFHVWYKJXQZ"
SCORES = [1] * 10 + [2] * 2 + [3] * 4 + [4] * 5 + [5] * 1 + [8] * 2 + [10] * 2
def score(word):
- return sum(SCORES[KEYS.index(letter.upper())] for letter in word)
-```
+ return sum(SCORES[KEYS.index(letter)] for letter in word.upper())
-This approach uses a string and a [list][list], both of these data types belongs to the parent data type [sequences][sequence].
-The code starts with defining a string constant with letters.
-Then another constant is definded which is a list with corresponding score for the same index as the string.
+This approach uses a string and a [list][list], both of which are [sequence][sequence] types.
+The code begins by defining a string constant with letters.
+Then another constant is defined as a list with the corresponding letter score at the same index as the letter in the string.
-The `score` function takes a word as a parameter.
-And uses the same [generator expression][generator-expersion] as the [dictionary approach][dictionary-approach] with some slight modifications.
+The `score` function takes a word as an argument.
+And uses the same [generator expression][generator-expression] as the [dictionary approach][dictionary-approach] with some slight modifications.
-The difference is that instead of using a [dictionary][dictionary] and looking up the score inside of a dictonary.
-This approach gets the index of the letter in the KEYS constant and then then looks up the value for that index in SCORES list.
-Then takes that value and return that to the generator expression.
+Instead of using a [dictionary][dictionary] and looking up the score, this approach looks up the index of the letter in the KEYS constant and then then looks up the value for that index in SCORES list within the generator expression.
+These values are then added up by `sum`.
[dictionary]: https://docs.python.org/3/library/stdtypes.html#mapping-types-dict
[dictionary-approach]: https://exercism.org/tracks/python/exercises/scrabble-score/approaches/dictionary
From b3f23ce0ae879ce3fc4710ee864322ae2c514101 Mon Sep 17 00:00:00 2001
From: Carl
Date: Wed, 11 Jan 2023 21:48:49 +0100
Subject: [PATCH 333/826] Fixed spacing
---
.../scrabble-score/.approaches/dictionary/content.md | 9 ++++-----
.../scrabble-score/.approaches/enum/content.md | 8 +++++---
.../scrabble-score/.approaches/introduction.md | 10 ++++++----
.../scrabble-score/.approaches/nested-tuple/content.md | 7 ++++---
.../.approaches/two-sequences/content.md | 1 +
5 files changed, 20 insertions(+), 15 deletions(-)
diff --git a/exercises/practice/scrabble-score/.approaches/dictionary/content.md b/exercises/practice/scrabble-score/.approaches/dictionary/content.md
index d220150eb3..f87cce0ec1 100644
--- a/exercises/practice/scrabble-score/.approaches/dictionary/content.md
+++ b/exercises/practice/scrabble-score/.approaches/dictionary/content.md
@@ -16,10 +16,10 @@ This code starts with defining a constant LETTER_SCORES as a dictionary ([concep
Then the `score` function is defined, which takes a `` as an argument.
The function returns the total score for the word using the built-in function [`sum`][sum].
- Sum is passed a [generator expression][generator-expression] that iterates over the letters in the word, looking up each score in LETTER_SCORES.
+Sum is passed a [generator expression][generator-expression] that iterates over the letters in the word, looking up each score in LETTER_SCORES.
The generator expression produces the score values on the fly.
This means that it doesn't use memory to store all the values from LETTER_SCORES.
-Instead, each value is looked up as needed by `sum`.
+Instead, each value is looked up as needed by `sum`.
Within the generator expression, the word is converted from lower to uppercase.
Each letter of the word is looked up in LETTER_SCORES, and the score value is yielded to `sum` as `sum` iterates over the expression.
@@ -42,11 +42,10 @@ LETTER_SCORES = {
def score(word):
return sum(next(score for score, letters in LETTER_SCORES.items() if character in letters) for character in word.upper())
-
+```
However, transposing the dictionary so that the keys are the score and the values are the letters requires more computational calculation (_a loop within a loop_) and is harder to read.
- Therefore, arranging the dictionary by letter is both more efficient and easier to understand.
-
+Therefore, arranging the dictionary by letter is both more efficient and easier to understand.
[dictionary]: https://docs.python.org/3/library/stdtypes.html#mapping-types-dict
[generator-expression]: https://peps.python.org/pep-0289/
diff --git a/exercises/practice/scrabble-score/.approaches/enum/content.md b/exercises/practice/scrabble-score/.approaches/enum/content.md
index 188f54000a..5c2ad3a18a 100644
--- a/exercises/practice/scrabble-score/.approaches/enum/content.md
+++ b/exercises/practice/scrabble-score/.approaches/enum/content.md
@@ -14,6 +14,7 @@ class Scrabble(IntEnum):
def score(word):
return sum(Scrabble[character] for character in word.upper())
+```
This approach uses an [`Enum`][enum] to define the score of each letter.
An [`Enum`][enum] (_also known as an **enumeration**_) is an object with named attributes assigned unique values.
@@ -24,7 +25,7 @@ Values can be accessed via index syntax using the member name (_similar to how a
The `enum` module was added to python standard library (_also known as stdlib_) in Python 3.4.
This approach uses an [`IntEnum`][int-enum].
-An `IntEnum` is very similar to an `Enum`, but restricts assigned values to `int`s.
+An `IntEnum` is very similar to an `Enum`, but restricts assigned values to `int`s.
This allows the `IntEnum` to act as a collection of integers.
In fact, `IntEnum`s are considered subclasses of `int`s.
@@ -35,15 +36,16 @@ The `IntEnum` subclass is defined by using the [`class`][classes] keyword, follo
```python
class ClassName(IntEnum):
+```
Member names are declared as constants (ALL CAPS) and assigned values using the `=` operator.
-This approach works by creating all the uppercase letters as members with their values being the score.
+This approach works by creating all the uppercase letters as members with their values being the score.
After the `IntEnum` is defined, the `score` function is defined.
The `score` function takes a word as an argument.
The `score` function uses the same [generator expression][generator-expression] as the [dictionary approach][dictionary-approach], but with a slight modification.
-Instead of looking up the value in a _dictionary_, it looks up the `InEnum` class member value.
+Instead of looking up the value in a _dictionary_, it looks up the `InEnum` class member value.
[classes]: https://docs.python.org/3/tutorial/classes.html
[enum]: https://docs.python.org/3/library/enum.html
diff --git a/exercises/practice/scrabble-score/.approaches/introduction.md b/exercises/practice/scrabble-score/.approaches/introduction.md
index 1b94ede3fe..8539d59d43 100644
--- a/exercises/practice/scrabble-score/.approaches/introduction.md
+++ b/exercises/practice/scrabble-score/.approaches/introduction.md
@@ -25,6 +25,7 @@ LETTER_SCORES = {
def score(word):
return sum(LETTER_SCORES[letter] for letter in word.upper())
+```
For more information, check the [Dictionary Approach][dictionary-approach].
@@ -38,7 +39,7 @@ KEYS = "AEIOULNRSTDGBCMPFHVWYKJXQZ"
SCORES = [1] * 10 + [2] * 2 + [3] * 4 + [4] * 5 + [5] * 1 + [8] * 2 +[10] * 2
def score(word):
- return sum(SCORES[KEYS.index(letter] for letter in word.upper())
+ return sum(SCORES[KEYS.index(letter)] for letter in word.upper())
```
For more information, check the [Two Sequences Approach][two-sequences-approach].
@@ -46,7 +47,7 @@ For more information, check the [Two Sequences Approach][two-sequences-approach]
## Approach: Enum
Using an `Enum` is is short and easy to read.
-Although creating an `Enum` can be more complicated since it uses OOP (object oriented programming).
+Although creating an `Enum` can be more complicated since it uses OOP (object oriented programming).
```python
from enum import IntEnum
@@ -84,8 +85,9 @@ LETTERS_OF_SCORE = (
)
def score(word):
- return sum(score for character in word.upper() for
- letters, score in LETTERS_OF_SCORE if character in letters)
+ return sum(score for character in word.upper() for
+ letters, score in LETTERS_OF_SCORE if character in letters)
+```
For more information, check the [Nested Tuple Approach][nested-tuple-approach].
diff --git a/exercises/practice/scrabble-score/.approaches/nested-tuple/content.md b/exercises/practice/scrabble-score/.approaches/nested-tuple/content.md
index 113c385f0a..70dc860a0a 100644
--- a/exercises/practice/scrabble-score/.approaches/nested-tuple/content.md
+++ b/exercises/practice/scrabble-score/.approaches/nested-tuple/content.md
@@ -12,10 +12,11 @@ LETTERS_OF_SCORE = (
)
def score(word):
- return sum(score for character in word.upper() for
+ return sum(score for character in word.upper() for
letters, score in LETTERS_OF_SCORE if character in letters)
+```
-The code starts with defining a constant, LETTERS_OF_SCORE as a [`tuple`][tuple] of tuples (_also known as a nested tuple_).
+The code starts with defining a constant, `LETTERS_OF_SCORE` as a [`tuple`][tuple] of tuples (_also known as a nested tuple_).
Inside of the inner tuples are 2 values, the first value is a string of letters and the second value is the score for those letters.
Next, the `score` function is defined, taking a word as an argument.
@@ -29,6 +30,6 @@ You can read more about unpacking in the [concept:python/unpacking-and-multiple-
Then the code checks if the character is in the unpacked letters and if it is we return its score.
-[tuple]: https://docs.python.org/3/tutorial/datastructures.html#tuples-and-sequences
[generator-expression]: https://peps.python.org/pep-0289/
[for-loop]: https://realpython.com/python-for-loop/
+[tuple]: https://docs.python.org/3/tutorial/datastructures.html#tuples-and-sequences
diff --git a/exercises/practice/scrabble-score/.approaches/two-sequences/content.md b/exercises/practice/scrabble-score/.approaches/two-sequences/content.md
index 83f0ea1359..6cbb6c7694 100644
--- a/exercises/practice/scrabble-score/.approaches/two-sequences/content.md
+++ b/exercises/practice/scrabble-score/.approaches/two-sequences/content.md
@@ -6,6 +6,7 @@ SCORES = [1] * 10 + [2] * 2 + [3] * 4 + [4] * 5 + [5] * 1 + [8] * 2 + [10] * 2
def score(word):
return sum(SCORES[KEYS.index(letter)] for letter in word.upper())
+```
This approach uses a string and a [list][list], both of which are [sequence][sequence] types.
The code begins by defining a string constant with letters.
From 220fce386d72aea29a4813268ce72ec9b75676d4 Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Thu, 26 Jan 2023 15:23:04 -0800
Subject: [PATCH 334/826] Updated Exercise docs, toml files, test files and
example solutions for: (#3329)
* Palindrom Products (toml and tests)
* Roman Numerals (tests)
* Phone Number (toml, tests, and example solution)
* Resistor Color Trio (intro, toml, tests, and example solution)
---
.../palindrome-products/.meta/tests.toml | 24 +++++++++----
.../palindrome_products_test.py | 5 +++
.../phone-number/.docs/instructions.append.md | 4 +--
.../practice/phone-number/.meta/example.py | 4 +--
.../practice/phone-number/.meta/tests.toml | 10 ++++++
.../phone-number/phone_number_test.py | 4 +--
.../resistor-color-trio/.docs/instructions.md | 8 +++--
.../resistor-color-trio/.meta/example.py | 36 +++++++++++++------
.../resistor-color-trio/.meta/tests.toml | 15 ++++++++
.../resistor_color_trio_test.py | 15 ++++++++
.../roman-numerals/roman_numerals_test.py | 30 ++++++++--------
11 files changed, 113 insertions(+), 42 deletions(-)
diff --git a/exercises/practice/palindrome-products/.meta/tests.toml b/exercises/practice/palindrome-products/.meta/tests.toml
index b34cb0d475..a3bc41750a 100644
--- a/exercises/practice/palindrome-products/.meta/tests.toml
+++ b/exercises/practice/palindrome-products/.meta/tests.toml
@@ -1,12 +1,19 @@
-# This is an auto-generated file. Regular comments will be removed when this
-# file is regenerated. Regenerating will not touch any manually added keys,
-# so comments can be added in a "comment" key.
+# This is an auto-generated file.
+#
+# Regenerating this file via `configlet sync` will:
+# - Recreate every `description` key/value pair
+# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications
+# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion)
+# - Preserve any other key/value pair
+#
+# As user-added comments (using the # character) will be removed when this file
+# is regenerated, comments can be added via a `comment` key.
[5cff78fe-cf02-459d-85c2-ce584679f887]
-description = "finds the smallest palindrome from single digit factors"
+description = "find the smallest palindrome from single digit factors"
[0853f82c-5fc4-44ae-be38-fadb2cced92d]
-description = "finds the largest palindrome from single digit factors"
+description = "find the largest palindrome from single digit factors"
[66c3b496-bdec-4103-9129-3fcb5a9063e1]
description = "find the smallest palindrome from double digit factors"
@@ -15,13 +22,13 @@ description = "find the smallest palindrome from double digit factors"
description = "find the largest palindrome from double digit factors"
[cecb5a35-46d1-4666-9719-fa2c3af7499d]
-description = "find smallest palindrome from triple digit factors"
+description = "find the smallest palindrome from triple digit factors"
[edab43e1-c35f-4ea3-8c55-2f31dddd92e5]
description = "find the largest palindrome from triple digit factors"
[4f802b5a-9d74-4026-a70f-b53ff9234e4e]
-description = "find smallest palindrome from four digit factors"
+description = "find the smallest palindrome from four digit factors"
[787525e0-a5f9-40f3-8cb2-23b52cf5d0be]
description = "find the largest palindrome from four digit factors"
@@ -37,3 +44,6 @@ description = "error result for smallest if min is more than max"
[eeeb5bff-3f47-4b1e-892f-05829277bd74]
description = "error result for largest if min is more than max"
+
+[16481711-26c4-42e0-9180-e2e4e8b29c23]
+description = "smallest product does not use the smallest factor"
diff --git a/exercises/practice/palindrome-products/palindrome_products_test.py b/exercises/practice/palindrome-products/palindrome_products_test.py
index 2ff69adc2d..0fd7eaece3 100644
--- a/exercises/practice/palindrome-products/palindrome_products_test.py
+++ b/exercises/practice/palindrome-products/palindrome_products_test.py
@@ -71,5 +71,10 @@ def test_error_result_for_largest_if_min_is_more_than_max(self):
self.assertEqual(type(err.exception), ValueError)
self.assertEqual(err.exception.args[0], "min must be <= max")
+ def test_smallest_product_does_not_use_the_smallest_factor(self):
+ value, factors = smallest(min_factor=3215, max_factor=4000)
+ self.assertEqual(value, 10988901)
+ self.assertFactorsEqual(factors, [[3297, 3333]])
+
def assertFactorsEqual(self, actual, expected):
self.assertEqual(set(map(frozenset, actual)), set(map(frozenset, expected)))
diff --git a/exercises/practice/phone-number/.docs/instructions.append.md b/exercises/practice/phone-number/.docs/instructions.append.md
index 1001c9dc7e..662c8c8762 100644
--- a/exercises/practice/phone-number/.docs/instructions.append.md
+++ b/exercises/practice/phone-number/.docs/instructions.append.md
@@ -10,10 +10,10 @@ To raise a `ValueError` with a message, write the message as an argument to the
```python
# if a phone number has less than 10 digits.
-raise ValueError("incorrect number of digits")
+raise ValueError("must not be fewer than 10 digits")
# if a phone number has more than 11 digits.
-raise ValueError("more than 11 digits")
+raise ValueError("must not be greater than 11 digits")
# if a phone number has 11 digits, but starts with a number other than 1.
raise ValueError("11 digits must start with 1")
diff --git a/exercises/practice/phone-number/.meta/example.py b/exercises/practice/phone-number/.meta/example.py
index 02b8e13b8b..d23102a01e 100644
--- a/exercises/practice/phone-number/.meta/example.py
+++ b/exercises/practice/phone-number/.meta/example.py
@@ -25,10 +25,10 @@ def _clean(self, number):
def _normalize(self, number):
if len(number) < 10:
- raise ValueError('incorrect number of digits')
+ raise ValueError('must not be fewer than 10 digits')
if len(number) > 11:
- raise ValueError('more than 11 digits')
+ raise ValueError('must not be greater than 11 digits')
if len(number) == 10 or len(number) == 11 and number.startswith('1'):
if number[-10] == '0':
diff --git a/exercises/practice/phone-number/.meta/tests.toml b/exercises/practice/phone-number/.meta/tests.toml
index ee308c3e59..24dbf07a76 100644
--- a/exercises/practice/phone-number/.meta/tests.toml
+++ b/exercises/practice/phone-number/.meta/tests.toml
@@ -20,6 +20,11 @@ description = "cleans numbers with multiple spaces"
[598d8432-0659-4019-a78b-1c6a73691d21]
description = "invalid when 9 digits"
+include = false
+
+[2de74156-f646-42b5-8638-0ef1d8b58bc2]
+description = "invalid when 9 digits"
+reimplements = "598d8432-0659-4019-a78b-1c6a73691d21"
[57061c72-07b5-431f-9766-d97da7c4399d]
description = "invalid when 11 digits does not start with a 1"
@@ -32,6 +37,11 @@ description = "valid when 11 digits and starting with 1 even with punctuation"
[c6a5f007-895a-4fc5-90bc-a7e70f9b5cad]
description = "invalid when more than 11 digits"
+include = false
+
+[4a1509b7-8953-4eec-981b-c483358ff531]
+description = "invalid when more than 11 digits"
+reimplements = "c6a5f007-895a-4fc5-90bc-a7e70f9b5cad"
[63f38f37-53f6-4a5f-bd86-e9b404f10a60]
description = "invalid with letters"
diff --git a/exercises/practice/phone-number/phone_number_test.py b/exercises/practice/phone-number/phone_number_test.py
index dfbb9f851a..72ff3b099a 100644
--- a/exercises/practice/phone-number/phone_number_test.py
+++ b/exercises/practice/phone-number/phone_number_test.py
@@ -24,7 +24,7 @@ def test_invalid_when_9_digits(self):
with self.assertRaises(ValueError) as err:
PhoneNumber("123456789")
self.assertEqual(type(err.exception), ValueError)
- self.assertEqual(err.exception.args[0], "incorrect number of digits")
+ self.assertEqual(err.exception.args[0], "must not be fewer than 10 digits")
def test_invalid_when_11_digits_does_not_start_with_a_1(self):
with self.assertRaises(ValueError) as err:
@@ -44,7 +44,7 @@ def test_invalid_when_more_than_11_digits(self):
with self.assertRaises(ValueError) as err:
PhoneNumber("321234567890")
self.assertEqual(type(err.exception), ValueError)
- self.assertEqual(err.exception.args[0], "more than 11 digits")
+ self.assertEqual(err.exception.args[0], "must not be greater than 11 digits")
def test_invalid_with_letters(self):
with self.assertRaises(ValueError) as err:
diff --git a/exercises/practice/resistor-color-trio/.docs/instructions.md b/exercises/practice/resistor-color-trio/.docs/instructions.md
index fcc76958a5..4ad2aede37 100644
--- a/exercises/practice/resistor-color-trio/.docs/instructions.md
+++ b/exercises/practice/resistor-color-trio/.docs/instructions.md
@@ -46,9 +46,11 @@ So an input of `"orange", "orange", "black"` should return:
> "33 ohms"
-When we get more than a thousand ohms, we say "kiloohms".
-That's similar to saying "kilometer" for 1000 meters, and "kilograms" for 1000 grams.
+When we get to larger resistors, a [metric prefix][metric-prefix] is used to indicate a larger magnitude of ohms, such as "kiloohms".
+That is similar to saying "2 kilometers" instead of "2000 meters", or "2 kilograms" for "2000 grams".
-So an input of `"orange", "orange", "orange"` should return:
+For example, an input of `"orange", "orange", "orange"` should return:
> "33 kiloohms"
+
+[metric-prefix]: https://en.wikipedia.org/wiki/Metric_prefix
diff --git a/exercises/practice/resistor-color-trio/.meta/example.py b/exercises/practice/resistor-color-trio/.meta/example.py
index a6588ea5a6..69554592d0 100644
--- a/exercises/practice/resistor-color-trio/.meta/example.py
+++ b/exercises/practice/resistor-color-trio/.meta/example.py
@@ -1,18 +1,32 @@
COLORS = [
- 'black',
- 'brown',
- 'red',
- 'orange',
- 'yellow',
- 'green',
- 'blue',
- 'violet',
- 'grey',
- 'white'
+ 'black',
+ 'brown',
+ 'red',
+ 'orange',
+ 'yellow',
+ 'green',
+ 'blue',
+ 'violet',
+ 'grey',
+ 'white'
]
def label(colors):
value = 10 * COLORS.index(colors[0]) + COLORS.index(colors[1])
value *= 10 ** COLORS.index(colors[2])
- return str(value) + ' ohms' if value < 1000 else str(value // 1000) + ' kiloohms'
+ label = str(value)
+
+ if len(label) < 4 :
+ unit = 'ohms'
+ elif len(label) < 7:
+ label = str(value//1000)
+ unit = 'kiloohms'
+ elif len(label) <= 8 :
+ label = str(value//1000000)
+ unit = 'megaohms'
+ elif len(label) >= 9:
+ label = str(value//1000000000)
+ unit = 'gigaohms'
+
+ return f'{value if value < 1000 else label} {unit}'
diff --git a/exercises/practice/resistor-color-trio/.meta/tests.toml b/exercises/practice/resistor-color-trio/.meta/tests.toml
index dc6077e54f..b7d45fa5d5 100644
--- a/exercises/practice/resistor-color-trio/.meta/tests.toml
+++ b/exercises/practice/resistor-color-trio/.meta/tests.toml
@@ -23,3 +23,18 @@ description = "Green and brown and orange"
[f5d37ef9-1919-4719-a90d-a33c5a6934c9]
description = "Yellow and violet and yellow"
+
+[5f6404a7-5bb3-4283-877d-3d39bcc33854]
+description = "Blue and violet and blue"
+
+[7d3a6ab8-e40e-46c3-98b1-91639fff2344]
+description = "Minimum possible value"
+
+[ca0aa0ac-3825-42de-9f07-dac68cc580fd]
+description = "Maximum possible value"
+
+[0061a76c-903a-4714-8ce2-f26ce23b0e09]
+description = "First two colors make an invalid octal number"
+
+[30872c92-f567-4b69-a105-8455611c10c4]
+description = "Ignore extra colors"
diff --git a/exercises/practice/resistor-color-trio/resistor_color_trio_test.py b/exercises/practice/resistor-color-trio/resistor_color_trio_test.py
index 302df179ef..ddfdfb6930 100644
--- a/exercises/practice/resistor-color-trio/resistor_color_trio_test.py
+++ b/exercises/practice/resistor-color-trio/resistor_color_trio_test.py
@@ -22,3 +22,18 @@ def test_green_and_brown_and_orange(self):
def test_yellow_and_violet_and_yellow(self):
self.assertEqual(label(["yellow", "violet", "yellow"]), "470 kiloohms")
+
+ def test_blue_and_violet_and_blue(self):
+ self.assertEqual(label(["blue", "violet", "blue"]), "67 megaohms")
+
+ def test_minimum_possible_value(self):
+ self.assertEqual(label(["black", "black", "black"]), "0 ohms")
+
+ def test_maximum_possible_value(self):
+ self.assertEqual(label(["white", "white", "white"]), "99 gigaohms")
+
+ def test_first_two_colors_make_an_invalid_octal_number(self):
+ self.assertEqual(label(["black", "grey", "black"]), "8 ohms")
+
+ def test_ignore_extra_colors(self):
+ self.assertEqual(label(["blue", "green", "yellow", "orange"]), "650 kiloohms")
diff --git a/exercises/practice/roman-numerals/roman_numerals_test.py b/exercises/practice/roman-numerals/roman_numerals_test.py
index f85422cc5e..59e5e697fa 100644
--- a/exercises/practice/roman-numerals/roman_numerals_test.py
+++ b/exercises/practice/roman-numerals/roman_numerals_test.py
@@ -27,6 +27,9 @@ def test_6_is_vi(self):
def test_9_is_ix(self):
self.assertEqual(roman(9), "IX")
+ def test_16_is_xvi(self):
+ self.assertEqual(roman(16), "XVI")
+
def test_27_is_xxvii(self):
self.assertEqual(roman(27), "XXVII")
@@ -39,6 +42,9 @@ def test_49_is_xlix(self):
def test_59_is_lix(self):
self.assertEqual(roman(59), "LIX")
+ def test_66_is_lxvi(self):
+ self.assertEqual(roman(66), "LXVI")
+
def test_93_is_xciii(self):
self.assertEqual(roman(93), "XCIII")
@@ -48,36 +54,30 @@ def test_141_is_cxli(self):
def test_163_is_clxiii(self):
self.assertEqual(roman(163), "CLXIII")
+ def test_166_is_clxvi(self):
+ self.assertEqual(roman(166), "CLXVI")
+
def test_402_is_cdii(self):
self.assertEqual(roman(402), "CDII")
def test_575_is_dlxxv(self):
self.assertEqual(roman(575), "DLXXV")
+ def test_666_is_dclxvi(self):
+ self.assertEqual(roman(666), "DCLXVI")
+
def test_911_is_cmxi(self):
self.assertEqual(roman(911), "CMXI")
def test_1024_is_mxxiv(self):
self.assertEqual(roman(1024), "MXXIV")
- def test_3000_is_mmm(self):
- self.assertEqual(roman(3000), "MMM")
-
- def test_16_is_xvi(self):
- self.assertEqual(roman(16), "XVI")
-
- def test_66_is_lxvi(self):
- self.assertEqual(roman(66), "LXVI")
-
- def test_166_is_clxvi(self):
- self.assertEqual(roman(166), "CLXVI")
-
- def test_666_is_dclxvi(self):
- self.assertEqual(roman(666), "DCLXVI")
-
def test_1666_is_mdclxvi(self):
self.assertEqual(roman(1666), "MDCLXVI")
+ def test_3000_is_mmm(self):
+ self.assertEqual(roman(3000), "MMM")
+
def test_3001_is_mmmi(self):
self.assertEqual(roman(3001), "MMMI")
From afc8e4f025774027a6668b3be12e1435e77dbd35 Mon Sep 17 00:00:00 2001
From: Bob Hoeppner <32035397+bobahop@users.noreply.github.com>
Date: Thu, 26 Jan 2023 06:55:54 -0600
Subject: [PATCH 335/826] Update introduction.md
---
exercises/practice/grains/.approaches/introduction.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/exercises/practice/grains/.approaches/introduction.md b/exercises/practice/grains/.approaches/introduction.md
index 13f56d33be..c02d5b8ba6 100644
--- a/exercises/practice/grains/.approaches/introduction.md
+++ b/exercises/practice/grains/.approaches/introduction.md
@@ -19,7 +19,7 @@ You can see that the exponent, or power, that `2` is raised by is always one les
| 1 | 0 | 2 to the power of 0 = 1 |
| 2 | 1 | 2 to the power of 1 = 2 |
| 3 | 2 | 2 to the power of 2 = 4 |
-| 4 | 3 | 2 to the power of 4 = 8 |
+| 4 | 3 | 2 to the power of 3 = 8 |
## Approach: Bit-shifting
From bf818b5b1c4705903a9e0fa69c891b936e077204 Mon Sep 17 00:00:00 2001
From: Carl
Date: Wed, 11 Jan 2023 21:44:03 +0100
Subject: [PATCH 336/826] start
---
.../.approaches/ascii-values/content.md | 45 ++++++++
.../.approaches/ascii-values/snippet.txt | 5 +
.../rotational-cipher/.approaches/config.json | 22 ++++
.../.approaches/introduction.md | 109 ++++++++++++++++++
.../nested-for-loop-optimized/content.md | 92 +++++++++++++++
.../nested-for-loop-optimized/snippet.txt | 5 +
6 files changed, 278 insertions(+)
create mode 100644 exercises/practice/rotational-cipher/.approaches/ascii-values/content.md
create mode 100644 exercises/practice/rotational-cipher/.approaches/ascii-values/snippet.txt
create mode 100644 exercises/practice/rotational-cipher/.approaches/config.json
create mode 100644 exercises/practice/rotational-cipher/.approaches/introduction.md
create mode 100644 exercises/practice/rotational-cipher/.approaches/nested-for-loop-optimized/content.md
create mode 100644 exercises/practice/rotational-cipher/.approaches/nested-for-loop-optimized/snippet.txt
diff --git a/exercises/practice/rotational-cipher/.approaches/ascii-values/content.md b/exercises/practice/rotational-cipher/.approaches/ascii-values/content.md
new file mode 100644
index 0000000000..0daf501c6a
--- /dev/null
+++ b/exercises/practice/rotational-cipher/.approaches/ascii-values/content.md
@@ -0,0 +1,45 @@
+# Ascii
+
+```python
+def rotate(text, key)
+ result = ""
+ for letter in text:
+ if letter.isalpha():
+ if letter.isupper():
+ result += chr((ord(letter) - 65 + key) % 26 + 65)
+ else:
+ result += chr((ord(letter) - 97 + key) % 26 + 97)
+ else:
+ result += letter
+ return result
+```
+
+This approach uses [ascii values][ascii], ascii stands for American Standard Code for Information Interchange.
+It is a character encoding standard for electronic communication.
+It is a 7-bit code, which means that it can represent 128 different characters.
+The system uses numbers to represent various characters, symbols, and other entities.
+
+In ascii can you find all the downcased letter in the range between 97 and 123.
+While the upcased letters are in the range between 65 and 91.
+
+The reason why you might not want to do this approach is that it only supports the english alphabet.
+
+The approach starts with defining the function `rotate`, then a variable `result` is defined with the value of an empty string.
+Then is all the letters from the text iterated over through a `for loop`.
+Then is checked if the letter is a letter, if it is a letter then is checked if it is a uppercased letter.
+If it is a uppercased letter then is `ord` used which converts a letter to an ascii and is then added with the key and the ascii value of the letter subtracted with 65.
+Then is the result of that modulo 26 added with 65.
+
+That is because we want to know which index in the alphabet the letter is.
+And if the number is over 26 we want to make sure that it is in the range of 0-26.
+So we use modulo to make sure it is in that range.
+To use modulo for a range we have to make sure that it starts at zero, thereby are we subtracting the ascii value of the letter with 65.
+After that to get the back to a letter we add 65 and use the `chr` method which converts an ascii value to a letter.
+After that is the new letter added to the result.
+
+If the letter is a lowercased letter then is the same done but with the ascii value of 97 subtracted with the letter.
+
+If the letter is not a letter then is the letter added to the result.
+When the loop is finished we return the result.
+
+[ascii]: https://en.wikipedia.org/wiki/ASCII
diff --git a/exercises/practice/rotational-cipher/.approaches/ascii-values/snippet.txt b/exercises/practice/rotational-cipher/.approaches/ascii-values/snippet.txt
new file mode 100644
index 0000000000..f678cd824a
--- /dev/null
+++ b/exercises/practice/rotational-cipher/.approaches/ascii-values/snippet.txt
@@ -0,0 +1,5 @@
+ for number_a in range(min_factor, max_factor+1):
+ for number_b in range(min_factor, max_factor+1):
+ if number_a * number_b >= result:
+ test_value = str(number_a * number_b)
+ if test_value == test_value[::-1]:
diff --git a/exercises/practice/rotational-cipher/.approaches/config.json b/exercises/practice/rotational-cipher/.approaches/config.json
new file mode 100644
index 0000000000..3882c1cb97
--- /dev/null
+++ b/exercises/practice/rotational-cipher/.approaches/config.json
@@ -0,0 +1,22 @@
+{
+ "introduction": {
+ "authors": ["meatball133", "bethanyg"],
+ "contributors": []
+ },
+ "approaches": [
+ {
+ "uuid": "ec09a4e1-6bc3-465b-a366-8ccdd2dbe093",
+ "slug": "ascii-values",
+ "title": "ASCII values",
+ "blurb": "Use letters ascii value to rotate the alphabet",
+ "authors": ["meatball133", "bethanyg"]
+ },
+ {
+ "uuid": "6eb99523-b12b-4e72-8ed8-3444b635b9e5",
+ "slug": "nested-for-loop-optimized",
+ "title": "Nested for loop optimized",
+ "blurb": "Nested for loop optimized edition",
+ "authors": ["meatball133", "bethanyg"]
+ }
+ ]
+}
diff --git a/exercises/practice/rotational-cipher/.approaches/introduction.md b/exercises/practice/rotational-cipher/.approaches/introduction.md
new file mode 100644
index 0000000000..5c63d42303
--- /dev/null
+++ b/exercises/practice/rotational-cipher/.approaches/introduction.md
@@ -0,0 +1,109 @@
+# Introduction
+
+There are various ways to solve `palindrome-products`.
+This approaches document shows 2 _common_ strategies, with one being a lot more efficient than the other.
+That being said, neither approach here is considered canonical, and other "pythonic" approaches could be added/expanded on in the future.
+
+## General guidance
+
+The goal of this exercise is to generate the largest and smallest palindromes from a given range of numbers.
+
+## Approach: Using ascii values
+
+This approach is very simple and easy to understand.
+it uses the ascii value of the letters to rotate them.
+There the numbers 65-91 in the ascii range represent downcased letters.
+While 97-123 represent upcased letters.
+
+The reason why you might not want to do this approach is that it only supports the english alphabet.
+Say we want to use the scandivanian letter: **ä**, then this approach will not work.
+Since **ä** has the ascii value of 132.
+
+```python
+def rotate(text, key)
+ result = ""
+ for letter in text:
+ if letter.isalpha():
+ if letter.isupper():
+ result += chr((ord(letter) - 65 + key) % 26 + 65)
+ else:
+ result += chr((ord(letter) - 97 + key) % 26 + 97)
+ else:
+ result += letter
+ return result
+```
+
+## Approach: Alphabet
+
+This approach is similar to the previous one, but instead of using the ascii values, it uses the index of the letter in the alphabet.
+It requires the storing of a string and unless you are using two strings you have to convert the letters from upper to lower case.
+
+What this approach although give is the posiblity to use any alphabet.
+Say we want to use the scandivanian letter: **ä**, then we just add it where we want it:
+`abcdefghijklmnopqrstuvwxyzä` and it will rotate correctley around that.
+
+```python
+AlPHABET = "abcdefghijklmnopqrstuvwxyz"
+
+def rotate(text, key):
+ result = ""
+ for letter in text:
+ if letter.isalpha():
+ if letter.isupper():
+ result += AlPHABET[(AlPHABET.index(letter.lower()) + key) % 26].upper()
+ else:
+ result += AlPHABET[(AlPHABET.index(letter) + key) % 26]
+ else:
+ result += letter
+ return result
+```
+
+For more information, check the [Nested for loop approach][approach-nested-for-loop].
+
+## Approach: Str translate
+
+This approach is similar to the previous one, but instead of using the index of the letter in the alphabet, it uses the `str.translate` method.
+The benefit of this approach is that it has no visable loop, thereby the code becomes more concise.
+What to note is that the `str.translate` still loops over the `string` thereby even if it is no visable loop, it doesn't mean that a method is not looping.
+
+```python
+AlPHABET = "abcdefghijklmnopqrstuvwxyz"
+
+def rotate(text, key):
+ translator = AlPHABET[key:] + AlPHABET[:key]
+ return text.translate(str.maketrans(AlPHABET + AlPHABET.upper(), translator + translator.upper()))
+```
+
+You can read more about how to achieve this optimization in: [Nested for loop optimized][approach-nested-for-loop-optimized].
+
+## Approach: Recursion
+
+In this approach we use a recursive function.
+A recursive function is a function that calls itself.
+This approach can be more concise than other approaches, and may also be more readable for some audiences.
+
+The reason why you might not want to use this approach is that Python has a [recursion limit][recursion-limit] with a default of 1000.
+
+```python
+AlPHABET = "abcdefghijklmnopqrstuvwxyz"
+
+def rotate(text, key):
+ if text == "":
+ return ""
+ first_letter, rest = text[0], text[1:]
+ if first_letter.isalpha():
+ if first_letter.isupper():
+ return AlPHABET[(AlPHABET.index(first_letter.lower()) + key) % 26].upper() + rotate(rest, key)
+ else:
+ return AlPHABET[(AlPHABET.index(first_letter) + key) % 26] + rotate(rest, key)
+ else:
+ return first_letter + rotate(rest, key)
+```
+
+## Benchmark
+
+For more information, check the [Performance article][article-performance].
+
+[approach-nested-for-loop]: https://exercism.org/tracks/python/exercises/palindrome-products/approaches/nested-for-loop
+[approach-nested-for-loop-optimized]: https://exercism.org/tracks/python/exercises/palindrome-products/approaches/nested-for-loop-optimized
+[article-performance]: https://exercism.org/tracks/python/exercises/palindrome-products/articles/performance
diff --git a/exercises/practice/rotational-cipher/.approaches/nested-for-loop-optimized/content.md b/exercises/practice/rotational-cipher/.approaches/nested-for-loop-optimized/content.md
new file mode 100644
index 0000000000..2bc2718391
--- /dev/null
+++ b/exercises/practice/rotational-cipher/.approaches/nested-for-loop-optimized/content.md
@@ -0,0 +1,92 @@
+# Nested For Loop Optimized
+
+This approach shows that just a few changes can improve the running time of the solution significantly.
+
+```python
+def largest(min_factor, max_factor):
+ if min_factor > max_factor:
+ raise ValueError("min must be <= max")
+ result = 0
+ answer = []
+ for number_a in range(max_factor, min_factor - 1,-1):
+ was_bigger = False
+ for number_b in range(max_factor, number_a - 1, -1):
+ if number_a * number_b >= result:
+ was_bigger = True
+ test_value = str(number_a * number_b)
+ if test_value == test_value[::-1]:
+ if number_a * number_b > result:
+ answer = []
+ result = int(test_value)
+ answer.append([number_a, number_b])
+ if not was_bigger:
+ break
+ if result == 0:
+ result = None
+ return (result, answer)
+
+
+def smallest(min_factor, max_factor):
+ if min_factor > max_factor:
+ raise ValueError("min must be <= max")
+ result = 0
+ answer = []
+ for number_a in range(min_factor, max_factor+1):
+ was_smaller = False
+ for number_b in range(min_factor, max_factor+1):
+ if number_a * number_b <= result or result == 0:
+ was_smaller = True
+ test_value = str(number_a * number_b)
+ if test_value == test_value[::-1]:
+ if number_a * number_b < result:
+ answer = []
+ result = int(test_value)
+ answer.append([number_a, number_b])
+ if not was_smaller:
+ break
+ if result == 0:
+ result = None
+ return (result, answer)
+```
+
+This approach is very similar to the [nested for loop approach][approach-nested-for-loop], but it has a few optimizations.
+To optimize the `largest` function, we have to start the inner loop from the maximum factor and proceed down to the minimum factor.
+This allows us to stop the inner loop earlier than before.
+We also set the minimum value in the _inner_ loop to the current value of the _outer_ loop.
+
+Here is an example of how the algorithm works and why the loops need to be modified.
+Say we take maximum to be 99 and the minimum 10.
+In the first round:
+
+```
+x = [99 , 98, 97 ...]
+y = [99]
+```
+
+And already we have our result: `9009[91,99]`
+Although the loops have to continue to make us sure there are no higher values.
+
+```
+x = [98, 97, 96 ...]
+y = [99, 98]
+...
+x = [90, 89, 88 ...]
+y = [99, 98,97,96,95,94,93,92,91,90]
+
+Here we can see that the highest value for this "run" is 90 \* 99 = 8910.
+Meaning that running beyond this point won't give us any values higher than 9009.
+
+That is why we introduce the `was_bigger` variable.
+With `was_bigger`, we can check if the inner loop has a bigger value than the current result.
+If there has not been a bigger value, we can stop the inner loop and stop the outer loop.
+We do that by using the [`break`][break] statement.
+
+If we hadn't modified the direction of the inner loop, it would have started from the minimum factor and gone up to the maximum factor.
+This would mean that for every new run in the outer loop, the values would be bigger than the previous run.
+
+The `smallest` function is optimized in a similar way as `largest` function compared to the original approach.
+The only difference is that we have to start the inner loop and outer loop from the minimum factor and go up to the maximum factor.
+Since what we want is the smallest value, we have to start from the smallest value and go up to the biggest value.
+
+[approach-nested-for-loop]: https://exercism.org/tracks/python/exercises/palindrome-products/approaches/nested-for-loop
+[break]: https://docs.python.org/3/tutorial/controlflow.html#break-and-continue-statements-and-else-clauses-on-loops
diff --git a/exercises/practice/rotational-cipher/.approaches/nested-for-loop-optimized/snippet.txt b/exercises/practice/rotational-cipher/.approaches/nested-for-loop-optimized/snippet.txt
new file mode 100644
index 0000000000..bf91dd6753
--- /dev/null
+++ b/exercises/practice/rotational-cipher/.approaches/nested-for-loop-optimized/snippet.txt
@@ -0,0 +1,5 @@
+for number_a in range(max_factor, min_factor - 1,-1):
+ was_bigger = False
+ for number_b in range(max_factor, number_a - 1, -1):
+ if number_a * number_b >= result:
+ was_bigger = True
From 9580dd8ad684b8ff705a7e98e637862f679ac812 Mon Sep 17 00:00:00 2001
From: Carl
Date: Thu, 12 Jan 2023 22:21:29 +0100
Subject: [PATCH 337/826] Added preformance article and a bunch of content to
approaches
---
.../.approaches/alphabet/content.md | 46 ++++++++++
.../.approaches/alphabet/snippet.txt | 8 ++
.../.approaches/ascii-values/content.md | 21 +++--
.../.approaches/ascii-values/snippet.txt | 13 ++-
.../rotational-cipher/.approaches/config.json | 20 +++-
.../.approaches/introduction.md | 41 +++++----
.../nested-for-loop-optimized/content.md | 92 -------------------
.../nested-for-loop-optimized/snippet.txt | 5 -
.../.approaches/recursion/content.md | 42 +++++++++
.../.approaches/recursion/snippet.txt | 8 ++
.../.approaches/str-translate/content.md | 54 +++++++++++
.../.approaches/str-translate/snippet.txt | 5 +
.../rotational-cipher/.articles/config.json | 11 +++
.../.articles/performance/code/Benchmark.py | 90 ++++++++++++++++++
.../.articles/performance/content.md | 43 +++++++++
.../.articles/performance/snippet.md | 6 ++
16 files changed, 377 insertions(+), 128 deletions(-)
create mode 100644 exercises/practice/rotational-cipher/.approaches/alphabet/content.md
create mode 100644 exercises/practice/rotational-cipher/.approaches/alphabet/snippet.txt
delete mode 100644 exercises/practice/rotational-cipher/.approaches/nested-for-loop-optimized/content.md
delete mode 100644 exercises/practice/rotational-cipher/.approaches/nested-for-loop-optimized/snippet.txt
create mode 100644 exercises/practice/rotational-cipher/.approaches/recursion/content.md
create mode 100644 exercises/practice/rotational-cipher/.approaches/recursion/snippet.txt
create mode 100644 exercises/practice/rotational-cipher/.approaches/str-translate/content.md
create mode 100644 exercises/practice/rotational-cipher/.approaches/str-translate/snippet.txt
create mode 100644 exercises/practice/rotational-cipher/.articles/config.json
create mode 100644 exercises/practice/rotational-cipher/.articles/performance/code/Benchmark.py
create mode 100644 exercises/practice/rotational-cipher/.articles/performance/content.md
create mode 100644 exercises/practice/rotational-cipher/.articles/performance/snippet.md
diff --git a/exercises/practice/rotational-cipher/.approaches/alphabet/content.md b/exercises/practice/rotational-cipher/.approaches/alphabet/content.md
new file mode 100644
index 0000000000..75d85c531e
--- /dev/null
+++ b/exercises/practice/rotational-cipher/.approaches/alphabet/content.md
@@ -0,0 +1,46 @@
+# Alphabet
+
+```python
+AlPHABET = "abcdefghijklmnopqrstuvwxyz"
+
+def rotate(text, key):
+ result = ""
+ for letter in text:
+ if letter.isalpha():
+ if letter.isupper():
+ result += AlPHABET[(AlPHABET.index(letter.lower()) + key) % 26].upper()
+ else:
+ result += AlPHABET[(AlPHABET.index(letter) + key) % 26]
+ else:
+ result += letter
+ return result
+```
+
+The approach starts with defining the a constant which holds the whole alphabets lowercase letters.
+After that the function `rotate` is declared, then a variable `result` is defined with the value of an empty string.
+
+Then is all the letters from the text argument iterated over through a [`for loop`][for-loop].
+Then is checked if the letter is a letter, if it is a letter then is checked if it is a uppercased letter.
+
+If it is a uppercased letter then it is converted to lowe case and finds its index in the `AlPHABET` constant.
+Then is the key added to the index and [modulo (`%`)][modulo] 26 is used on the result.
+Then is the letter at the index found in the `AlPHABET` constant and the letter is converted to upcase.
+
+If the letter is a lowercased letter then it does the same process but don't convert the letter to downcase and then to uppercase.
+
+If the letter is not a letter then is the letter added to the result.
+When the loop is finished we return the result.
+
+If you only want to use english letters so could you import the alphabet instead of defining it yourself.
+Since in the [`string`][string] module there is a constant called [`ascii_lowercase`][ascii_lowercase] which holds the lowercased alphabet.
+
+```python
+import string
+
+AlPHABET = string.ascii_lowercase
+```
+
+[ascii_lowercase]: https://docs.python.org/3/library/string.html#string.ascii_letters
+[for-loop]: https://realpython.com/python-for-loop/
+[modulo]: https://docs.python.org/3/reference/expressions.html#binary-arithmetic-operations
+[string]: https://docs.python.org/3/library/string.html
diff --git a/exercises/practice/rotational-cipher/.approaches/alphabet/snippet.txt b/exercises/practice/rotational-cipher/.approaches/alphabet/snippet.txt
new file mode 100644
index 0000000000..ade372000b
--- /dev/null
+++ b/exercises/practice/rotational-cipher/.approaches/alphabet/snippet.txt
@@ -0,0 +1,8 @@
+for letter in text:
+ if letter.isalpha():
+ if letter.isupper():
+ result += AlPHABET[(AlPHABET.index(letter.lower()) + key) % 26].upper()
+ else:
+ result += AlPHABET[(AlPHABET.index(letter) + key) % 26]
+ else:
+ result += letter
\ No newline at end of file
diff --git a/exercises/practice/rotational-cipher/.approaches/ascii-values/content.md b/exercises/practice/rotational-cipher/.approaches/ascii-values/content.md
index 0daf501c6a..cb637198a9 100644
--- a/exercises/practice/rotational-cipher/.approaches/ascii-values/content.md
+++ b/exercises/practice/rotational-cipher/.approaches/ascii-values/content.md
@@ -1,7 +1,7 @@
# Ascii
```python
-def rotate(text, key)
+def rotate(text, key):
result = ""
for letter in text:
if letter.isalpha():
@@ -25,16 +25,20 @@ While the upcased letters are in the range between 65 and 91.
The reason why you might not want to do this approach is that it only supports the english alphabet.
The approach starts with defining the function `rotate`, then a variable `result` is defined with the value of an empty string.
-Then is all the letters from the text iterated over through a `for loop`.
+Then is all the letters from the text argument iterated over through a [`for loop`][for-loop].
Then is checked if the letter is a letter, if it is a letter then is checked if it is a uppercased letter.
-If it is a uppercased letter then is `ord` used which converts a letter to an ascii and is then added with the key and the ascii value of the letter subtracted with 65.
-Then is the result of that modulo 26 added with 65.
+
+Python has a built in function called `ord` that converts a [unicode][unicode] symbol to an integer.
+The unicode's first 128 characters are the same as ascii.
+
+If it is a uppercased letter then is [`ord`][ord] used to convert the letters to an integer and is then added with the key and then subtracted with 65.
+Then is the result of that [modulo (`%`)][modulo] 26 added with 65.
That is because we want to know which index in the alphabet the letter is.
And if the number is over 26 we want to make sure that it is in the range of 0-26.
So we use modulo to make sure it is in that range.
-To use modulo for a range we have to make sure that it starts at zero, thereby are we subtracting the ascii value of the letter with 65.
-After that to get the back to a letter we add 65 and use the `chr` method which converts an ascii value to a letter.
+To use modulo for a range we have to make sure that it starts at zero, thereby are we subtracting the integer value of the letter with 65.
+After that to get the back to a letter we add 65 and use the [`chr`][chr] method which converts an an unicode value to a letter.
After that is the new letter added to the result.
If the letter is a lowercased letter then is the same done but with the ascii value of 97 subtracted with the letter.
@@ -43,3 +47,8 @@ If the letter is not a letter then is the letter added to the result.
When the loop is finished we return the result.
[ascii]: https://en.wikipedia.org/wiki/ASCII
+[chr]: https://docs.python.org/3/library/functions.html#chr
+[for-loop]: https://realpython.com/python-for-loop/
+[modulo]: https://realpython.com/python-modulo-operator/
+[ord]: https://docs.python.org/3/library/functions.html#ord
+[unicode]: https://en.wikipedia.org/wiki/Unicode
diff --git a/exercises/practice/rotational-cipher/.approaches/ascii-values/snippet.txt b/exercises/practice/rotational-cipher/.approaches/ascii-values/snippet.txt
index f678cd824a..ede3b5c989 100644
--- a/exercises/practice/rotational-cipher/.approaches/ascii-values/snippet.txt
+++ b/exercises/practice/rotational-cipher/.approaches/ascii-values/snippet.txt
@@ -1,5 +1,8 @@
- for number_a in range(min_factor, max_factor+1):
- for number_b in range(min_factor, max_factor+1):
- if number_a * number_b >= result:
- test_value = str(number_a * number_b)
- if test_value == test_value[::-1]:
+for letter in text:
+ if letter.isalpha():
+ if letter.isupper():
+ result += chr((ord(letter) - 65 + key) % 26 + 65)
+ else:
+ result += chr((ord(letter) - 97 + key) % 26 + 97)
+ else:
+ result += letter
\ No newline at end of file
diff --git a/exercises/practice/rotational-cipher/.approaches/config.json b/exercises/practice/rotational-cipher/.approaches/config.json
index 3882c1cb97..5cf51697a6 100644
--- a/exercises/practice/rotational-cipher/.approaches/config.json
+++ b/exercises/practice/rotational-cipher/.approaches/config.json
@@ -13,9 +13,23 @@
},
{
"uuid": "6eb99523-b12b-4e72-8ed8-3444b635b9e5",
- "slug": "nested-for-loop-optimized",
- "title": "Nested for loop optimized",
- "blurb": "Nested for loop optimized edition",
+ "slug": "alphabet",
+ "title": "Alphabet",
+ "blurb": "Using the alphabet to rotate the alphabet",
+ "authors": ["meatball133", "bethanyg"]
+ },
+ {
+ "uuid": "e539d1a5-f497-402b-a232-7e889f4323c1",
+ "slug": "str-translate",
+ "title": "Str Translate",
+ "blurb": "Using str.translate to rotate the alphabet",
+ "authors": ["meatball133", "bethanyg"]
+ },
+ {
+ "uuid": "0c74890e-d96e-47a2-a8bf-93c45dd67f94",
+ "slug": "recursion",
+ "title": "Recursion",
+ "blurb": "Using Recursion to rotate the alphabet",
"authors": ["meatball133", "bethanyg"]
}
]
diff --git a/exercises/practice/rotational-cipher/.approaches/introduction.md b/exercises/practice/rotational-cipher/.approaches/introduction.md
index 5c63d42303..90b8d6fa8a 100644
--- a/exercises/practice/rotational-cipher/.approaches/introduction.md
+++ b/exercises/practice/rotational-cipher/.approaches/introduction.md
@@ -1,12 +1,11 @@
# Introduction
-There are various ways to solve `palindrome-products`.
-This approaches document shows 2 _common_ strategies, with one being a lot more efficient than the other.
-That being said, neither approach here is considered canonical, and other "pythonic" approaches could be added/expanded on in the future.
+There are various ways to solve `rotational-cipher`.
+You can for example use a [ascii values][ascii], alphabet, recursion, and `str.translate`.
## General guidance
-The goal of this exercise is to generate the largest and smallest palindromes from a given range of numbers.
+The goal of this exercise is to rotate the letters in a string by a given key.
## Approach: Using ascii values
@@ -16,11 +15,11 @@ There the numbers 65-91 in the ascii range represent downcased letters.
While 97-123 represent upcased letters.
The reason why you might not want to do this approach is that it only supports the english alphabet.
-Say we want to use the scandivanian letter: **ä**, then this approach will not work.
-Since **ä** has the ascii value of 132.
+Say we want to use the scandinavian letter: **å**, then this approach will not work.
+Since **å** has the ascii value of 132.
```python
-def rotate(text, key)
+def rotate(text, key):
result = ""
for letter in text:
if letter.isalpha():
@@ -33,14 +32,16 @@ def rotate(text, key)
return result
```
+For more information, check the [ascii values approach][approach-ascii-values].
+
## Approach: Alphabet
This approach is similar to the previous one, but instead of using the ascii values, it uses the index of the letter in the alphabet.
It requires the storing of a string and unless you are using two strings you have to convert the letters from upper to lower case.
-What this approach although give is the posiblity to use any alphabet.
-Say we want to use the scandivanian letter: **ä**, then we just add it where we want it:
-`abcdefghijklmnopqrstuvwxyzä` and it will rotate correctley around that.
+What this approach although give is the possibility to use any alphabet.
+Say we want to use the scandinavian letter: **å**, then we just add it where we want it:
+`abcdefghijklmnopqrstuvwxyzå` and it will rotate correctly around that.
```python
AlPHABET = "abcdefghijklmnopqrstuvwxyz"
@@ -58,13 +59,13 @@ def rotate(text, key):
return result
```
-For more information, check the [Nested for loop approach][approach-nested-for-loop].
+For more information, check the [Alphabet approach][approach-alphabet].
## Approach: Str translate
This approach is similar to the previous one, but instead of using the index of the letter in the alphabet, it uses the `str.translate` method.
-The benefit of this approach is that it has no visable loop, thereby the code becomes more concise.
-What to note is that the `str.translate` still loops over the `string` thereby even if it is no visable loop, it doesn't mean that a method is not looping.
+The benefit of this approach is that it has no visible loop, thereby the code becomes more concise.
+What to note is that the `str.translate` still loops over the `string` thereby even if it is no visible loop, it doesn't mean that a method is not looping.
```python
AlPHABET = "abcdefghijklmnopqrstuvwxyz"
@@ -74,7 +75,7 @@ def rotate(text, key):
return text.translate(str.maketrans(AlPHABET + AlPHABET.upper(), translator + translator.upper()))
```
-You can read more about how to achieve this optimization in: [Nested for loop optimized][approach-nested-for-loop-optimized].
+For more information, check the [Str translate approach][approach-str-translate].
## Approach: Recursion
@@ -100,10 +101,16 @@ def rotate(text, key):
return first_letter + rotate(rest, key)
```
+For more information, check the [Recursion approach][approach-recursion].
+
## Benchmark
For more information, check the [Performance article][article-performance].
-[approach-nested-for-loop]: https://exercism.org/tracks/python/exercises/palindrome-products/approaches/nested-for-loop
-[approach-nested-for-loop-optimized]: https://exercism.org/tracks/python/exercises/palindrome-products/approaches/nested-for-loop-optimized
-[article-performance]: https://exercism.org/tracks/python/exercises/palindrome-products/articles/performance
+[ascii]: https://en.wikipedia.org/wiki/ASCII
+[approach-recursion]: https://exercism.org/tracks/python/exercises/rotational-cipher/approaches/recursion
+[approach-str-translate]: https://exercism.org/tracks/python/exercises/rotational-cipher/approaches/str-translate
+[approach-ascii-values]: https://exercism.org/tracks/python/exercises/rotational-cipher/approaches/ascii-values
+[approach-alphabet]: https://exercism.org/tracks/python/exercises/rotational-cipher/approaches/alphabet
+[article-performance]: https://exercism.org/tracks/python/exercises/rotational-cipher/articles/performance
+[recursion-limit]: https://docs.python.org/3/library/sys.html#sys.setrecursionlimit
diff --git a/exercises/practice/rotational-cipher/.approaches/nested-for-loop-optimized/content.md b/exercises/practice/rotational-cipher/.approaches/nested-for-loop-optimized/content.md
deleted file mode 100644
index 2bc2718391..0000000000
--- a/exercises/practice/rotational-cipher/.approaches/nested-for-loop-optimized/content.md
+++ /dev/null
@@ -1,92 +0,0 @@
-# Nested For Loop Optimized
-
-This approach shows that just a few changes can improve the running time of the solution significantly.
-
-```python
-def largest(min_factor, max_factor):
- if min_factor > max_factor:
- raise ValueError("min must be <= max")
- result = 0
- answer = []
- for number_a in range(max_factor, min_factor - 1,-1):
- was_bigger = False
- for number_b in range(max_factor, number_a - 1, -1):
- if number_a * number_b >= result:
- was_bigger = True
- test_value = str(number_a * number_b)
- if test_value == test_value[::-1]:
- if number_a * number_b > result:
- answer = []
- result = int(test_value)
- answer.append([number_a, number_b])
- if not was_bigger:
- break
- if result == 0:
- result = None
- return (result, answer)
-
-
-def smallest(min_factor, max_factor):
- if min_factor > max_factor:
- raise ValueError("min must be <= max")
- result = 0
- answer = []
- for number_a in range(min_factor, max_factor+1):
- was_smaller = False
- for number_b in range(min_factor, max_factor+1):
- if number_a * number_b <= result or result == 0:
- was_smaller = True
- test_value = str(number_a * number_b)
- if test_value == test_value[::-1]:
- if number_a * number_b < result:
- answer = []
- result = int(test_value)
- answer.append([number_a, number_b])
- if not was_smaller:
- break
- if result == 0:
- result = None
- return (result, answer)
-```
-
-This approach is very similar to the [nested for loop approach][approach-nested-for-loop], but it has a few optimizations.
-To optimize the `largest` function, we have to start the inner loop from the maximum factor and proceed down to the minimum factor.
-This allows us to stop the inner loop earlier than before.
-We also set the minimum value in the _inner_ loop to the current value of the _outer_ loop.
-
-Here is an example of how the algorithm works and why the loops need to be modified.
-Say we take maximum to be 99 and the minimum 10.
-In the first round:
-
-```
-x = [99 , 98, 97 ...]
-y = [99]
-```
-
-And already we have our result: `9009[91,99]`
-Although the loops have to continue to make us sure there are no higher values.
-
-```
-x = [98, 97, 96 ...]
-y = [99, 98]
-...
-x = [90, 89, 88 ...]
-y = [99, 98,97,96,95,94,93,92,91,90]
-
-Here we can see that the highest value for this "run" is 90 \* 99 = 8910.
-Meaning that running beyond this point won't give us any values higher than 9009.
-
-That is why we introduce the `was_bigger` variable.
-With `was_bigger`, we can check if the inner loop has a bigger value than the current result.
-If there has not been a bigger value, we can stop the inner loop and stop the outer loop.
-We do that by using the [`break`][break] statement.
-
-If we hadn't modified the direction of the inner loop, it would have started from the minimum factor and gone up to the maximum factor.
-This would mean that for every new run in the outer loop, the values would be bigger than the previous run.
-
-The `smallest` function is optimized in a similar way as `largest` function compared to the original approach.
-The only difference is that we have to start the inner loop and outer loop from the minimum factor and go up to the maximum factor.
-Since what we want is the smallest value, we have to start from the smallest value and go up to the biggest value.
-
-[approach-nested-for-loop]: https://exercism.org/tracks/python/exercises/palindrome-products/approaches/nested-for-loop
-[break]: https://docs.python.org/3/tutorial/controlflow.html#break-and-continue-statements-and-else-clauses-on-loops
diff --git a/exercises/practice/rotational-cipher/.approaches/nested-for-loop-optimized/snippet.txt b/exercises/practice/rotational-cipher/.approaches/nested-for-loop-optimized/snippet.txt
deleted file mode 100644
index bf91dd6753..0000000000
--- a/exercises/practice/rotational-cipher/.approaches/nested-for-loop-optimized/snippet.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-for number_a in range(max_factor, min_factor - 1,-1):
- was_bigger = False
- for number_b in range(max_factor, number_a - 1, -1):
- if number_a * number_b >= result:
- was_bigger = True
diff --git a/exercises/practice/rotational-cipher/.approaches/recursion/content.md b/exercises/practice/rotational-cipher/.approaches/recursion/content.md
new file mode 100644
index 0000000000..d4e4e01908
--- /dev/null
+++ b/exercises/practice/rotational-cipher/.approaches/recursion/content.md
@@ -0,0 +1,42 @@
+# Recursion
+
+```python
+AlPHABET = "abcdefghijklmnopqrstuvwxyz"
+
+def rotate(text, key):
+ if text == "":
+ return ""
+ first_letter, rest = text[0], text[1:]
+ if first_letter.isalpha():
+ if first_letter.isupper():
+ return AlPHABET[(AlPHABET.index(first_letter.lower()) + key) % 26].upper() + rotate(rest, key)
+ else:
+ return AlPHABET[(AlPHABET.index(first_letter) + key) % 26] + rotate(rest, key)
+ else:
+ return first_letter + rotate(rest, key)
+```
+
+This approach uses a very similar approach to alphabet.
+And uses the same logic but instead of a loop so does this approach use [concept:python/recursion]() to solve the problem.
+
+Recursion is a programming technique where a function calls itself.
+It is a powerful technique, but can be more tricky to implement than a while loop.
+Recursion isn't that common in Python, it is more common in functional programming languages, like: [Elixir][elixir], [Haskell][haskell], and [Clojure][clojure].
+
+Solving this exercise with recursion removes the need for a "result" variable and the instantiation of a `loop`.
+If the number is not equal to one, we call ` + rotate(rest)`.
+Then the `rotate` function can execute the same code again with new values.
+Meaning we can get a long chain or stack of ` + rotate(rest)` until the rest variable is exhausted and then the code adds `""`.
+That translates to something like this: ` + + + + ""`.
+
+In Python, we can't have a function call itself more than 1000 times by default.
+Code that exceeds this recursion limit will throw a [RecursionError][recursion-error].
+While it is possible to adjust the [recursion limit][recursion-limit], doing so risks crashing Python and may also crash your system.
+Casually raising the recursion limit is not recommended.
+
+[clojure]: https://exercism.org/tracks/clojure
+[elixir]: https://exercism.org/tracks/elixir
+[haskell]: https://exercism.org/tracks/haskell
+[recursion]: https://realpython.com/python-thinking-recursively/
+[recursion-error]: https://docs.python.org/3/library/exceptions.html#RecursionError
+[recursion-limit]: https://docs.python.org/3/library/sys.html#sys.setrecursionlimit
diff --git a/exercises/practice/rotational-cipher/.approaches/recursion/snippet.txt b/exercises/practice/rotational-cipher/.approaches/recursion/snippet.txt
new file mode 100644
index 0000000000..098c419fe7
--- /dev/null
+++ b/exercises/practice/rotational-cipher/.approaches/recursion/snippet.txt
@@ -0,0 +1,8 @@
+first_letter, rest = text[0], text[1:]
+if first_letter.isalpha():
+ if first_letter.isupper():
+ return AlPHABET[(AlPHABET.index(first_letter.lower()) + key) % 26].upper() + rotate(rest, key)
+ else:
+ return AlPHABET[(AlPHABET.index(first_letter) + key) % 26] + rotate(rest, key)
+else:
+ return first_letter + rotate(rest, key)
\ No newline at end of file
diff --git a/exercises/practice/rotational-cipher/.approaches/str-translate/content.md b/exercises/practice/rotational-cipher/.approaches/str-translate/content.md
new file mode 100644
index 0000000000..e38f8359c7
--- /dev/null
+++ b/exercises/practice/rotational-cipher/.approaches/str-translate/content.md
@@ -0,0 +1,54 @@
+# Str Translate
+
+```python
+AlPHABET = "abcdefghijklmnopqrstuvwxyz"
+
+def rotate(text, key):
+ translator = AlPHABET[key:] + AlPHABET[:key]
+ return text.translate(str.maketrans(AlPHABET + AlPHABET.upper(), translator + translator.upper()))
+```
+
+This approach uses the [`.translate`][translate] method to solve the problem.
+`translate` takes a translation table as an argument.
+To create a translation table we use [str.`makestrans`][maketrans].
+
+The approach starts with defining the a constant which holds the whole alphabets lowercase letters.
+Then the function `rotate` is declared.
+Then is the `translator` variable defined with the value of the `AlPHABET` constant [sliced][slicing] from the key to the end and then the `AlPHABET` constant sliced from the start to the key.
+
+This is done so we have 2 strings which are the same but shifted by the key.
+Say we have the `AlPHABET` constant with the value of `abcdefghijklmnopqrstuvwxyz` and the key is 3.
+Then the `translator` variable will have the value of `defghijklmnopqrstuvwxyzabc`.
+
+Then is the `translate` method called on the `text` argument.
+The `translate` method takes a translation table as an argument.
+To create a translation table we use str.`makestrans`maketrans.
+
+The `makestrans` method takes 2 arguments.
+The first argument is the string which holds the characters which should be translated.
+The second argument is the string which holds the characters which the characters from the first argument should be translated to.
+
+The first argument is the `AlPHABET` constant and the `AlPHABET` constant uppercased.
+The second argument is the `translator` variable and the `translator` variable uppercased.
+
+What the `makestrans` does is that it takes the first argument and maps it to the second argument.
+It does that by creating a [dictionary], and converting the letter to [unicode][unicode].
+
+```python
+>>> str.maketrans("abc", "def")
+{97: 100, 98: 101, 99: 102}
+```
+
+The `translate` method takes the dictionary created by the `makestrans` method and uses it to translate the characters in the `text` argument.
+
+```python
+>>> "abc".translate({97: 100, 98: 101, 99: 102})
+'def'
+```
+
+When the loop is finished we return the result.
+
+[maketrans]: https://docs.python.org/3/library/stdtypes.html#str.maketrans
+[slicing]: https://www.w3schools.com/python/python_strings_slicing.asp
+[translate]: https://docs.python.org/3/library/stdtypes.html#str.translate
+[unicode]: https://en.wikipedia.org/wiki/Unicode
diff --git a/exercises/practice/rotational-cipher/.approaches/str-translate/snippet.txt b/exercises/practice/rotational-cipher/.approaches/str-translate/snippet.txt
new file mode 100644
index 0000000000..75350ae406
--- /dev/null
+++ b/exercises/practice/rotational-cipher/.approaches/str-translate/snippet.txt
@@ -0,0 +1,5 @@
+AlPHABET = "abcdefghijklmnopqrstuvwxyz"
+
+def rotate(text, key):
+ translator = AlPHABET[key:] + AlPHABET[:key]
+ return text.translate(str.maketrans(AlPHABET + AlPHABET.upper(), translator + translator.upper()))
\ No newline at end of file
diff --git a/exercises/practice/rotational-cipher/.articles/config.json b/exercises/practice/rotational-cipher/.articles/config.json
new file mode 100644
index 0000000000..fe3d6dc2a2
--- /dev/null
+++ b/exercises/practice/rotational-cipher/.articles/config.json
@@ -0,0 +1,11 @@
+{
+ "articles": [
+ {
+ "uuid": "8ab0e53c-0e9f-4525-a8ad-dea838e17d8c",
+ "slug": "performance",
+ "title": "Performance deep dive",
+ "blurb": "Deep dive to find out the performance between different approaches",
+ "authors": ["meatball133", "bethanyg"]
+ }
+ ]
+}
diff --git a/exercises/practice/rotational-cipher/.articles/performance/code/Benchmark.py b/exercises/practice/rotational-cipher/.articles/performance/code/Benchmark.py
new file mode 100644
index 0000000000..2919024a1f
--- /dev/null
+++ b/exercises/practice/rotational-cipher/.articles/performance/code/Benchmark.py
@@ -0,0 +1,90 @@
+import timeit
+import sys
+import itertools
+
+
+print(sys.version)
+
+
+AlPHABET = "abcdefghijklmnopqrstuvwxyz"
+COMBINATIONS = itertools.combinations_with_replacement(f"{AlPHABET[:13]}{AlPHABET[:13].upper()} 12,", 2)
+TEST_TEST = "".join([element for sublist in COMBINATIONS for element in sublist])
+
+def rotate_ascii(text, key):
+ result = ""
+ for letter in text:
+ if letter.isalpha():
+ if letter.isupper():
+ result += chr((ord(letter) - 65 + key) % 26 + 65)
+ else:
+ result += chr((ord(letter) - 97 + key) % 26 + 97)
+ else:
+ result += letter
+ return result
+
+
+def rotate_alphabet(text, key):
+ result = ""
+ for letter in text:
+ if letter.isalpha():
+ if letter.isupper():
+ result += AlPHABET[(AlPHABET.index(letter.lower()) + key) % 26].upper()
+ else:
+ result += AlPHABET[(AlPHABET.index(letter) + key) % 26]
+ else:
+ result += letter
+ return result
+
+
+def rotate_translate(text, key):
+ translator = AlPHABET[key:] + AlPHABET[:key]
+ return text.translate(str.maketrans(AlPHABET + AlPHABET.upper(), translator + translator.upper()))
+
+
+def rotate_recursion(text, key):
+ if text == "":
+ return ""
+ first_letter, rest = text[0], text[1:]
+ if first_letter.isalpha():
+ if first_letter.isupper():
+ return AlPHABET[(AlPHABET.index(first_letter.lower()) + key) % 26].upper() + rotate_recursion(rest, key)
+ else:
+ return AlPHABET[(AlPHABET.index(first_letter) + key) % 26] + rotate_recursion(rest, key)
+ else:
+ return first_letter + rotate_recursion(rest, key)
+
+
+
+start_time = timeit.default_timer()
+rotate_ascii(TEST_TEST, 25)
+print("rotate ascii long :", timeit.default_timer() - start_time)
+
+start_time = timeit.default_timer()
+rotate_alphabet(TEST_TEST, 25)
+print("rotate alphabet long :", timeit.default_timer() - start_time)
+
+start_time = timeit.default_timer()
+rotate_translate(TEST_TEST, 25)
+print("rotate translate long :", timeit.default_timer() - start_time)
+
+start_time = timeit.default_timer()
+rotate_recursion(TEST_TEST, 25)
+print("rotate recursion long :", timeit.default_timer() - start_time)
+
+
+
+start_time = timeit.default_timer()
+rotate_ascii("abcABC -12", 11)
+print("rotate ascii short :", timeit.default_timer() - start_time)
+
+start_time = timeit.default_timer()
+rotate_alphabet("abcABC -12", 11)
+print("rotate alphabet short :", timeit.default_timer() - start_time)
+
+start_time = timeit.default_timer()
+rotate_translate("abcABC -12", 11)
+print("rotate translate short :", timeit.default_timer() - start_time)
+
+start_time = timeit.default_timer()
+rotate_recursion("abcABC -12", 11)
+print("rotate recursion short :", timeit.default_timer() - start_time)
diff --git a/exercises/practice/rotational-cipher/.articles/performance/content.md b/exercises/practice/rotational-cipher/.articles/performance/content.md
new file mode 100644
index 0000000000..851180f56f
--- /dev/null
+++ b/exercises/practice/rotational-cipher/.articles/performance/content.md
@@ -0,0 +1,43 @@
+# Performance
+
+In this article, we'll examine the performance difference between approaches for `rotational cipher` in Python.
+
+The [approaches page][approaches] lists two approaches to this exercise:
+
+1. [Using recursion][approach-recursion]
+2. [Using `str.translate`][approach-str-translate]
+3. [Using ascii values][approach-ascii-values]
+4. [Using the alphabet][approach-alphabet]
+
+## Benchmarks
+
+To benchmark the approaches, we wrote a [small benchmark application][benchmark-application] using the [`timeit`][timeit] library.
+These tests were run in windows 11, using Python 3.11.1.
+Your system results may vary.
+
+```
+rotate ascii long : 0.000189200000022538
+rotate alphabet long : 0.0002604000037536025
+rotate translate long : 1.3999990187585354e-05
+rotate recursion long : 0.001309900006162934
+
+rotate ascii short : 4.999994416721165e-06
+rotate alphabet short : 3.6999990697950125e-06
+rotate translate short : 1.0200004908256233e-05
+rotate recursion short : 5.4000120144337416e-06
+```
+
+## Conclusion
+
+For a long string as input, is the translate approach the fastest, followed by ascii, alphabet and last recursion.
+For a short string as input, is the alphabet approach the fastest, followed by ascii, recursion and last translate.
+
+This means that if you know the input is a short string, the fastest approach is to use the alphabet approach.
+On the other hand, if you know the input is a long string, the fastest approach is to use the translate approach.
+
+[approach-recursion]: https://exercism.org/tracks/python/exercises/rotational-cipher/approaches/recursion
+[approach-str-translate]: https://exercism.org/tracks/python/exercises/rotational-cipher/approaches/str-translate
+[approach-ascii-values]: https://exercism.org/tracks/python/exercises/rotational-cipher/approaches/ascii-values
+[approach-alphabet]: https://exercism.org/tracks/python/exercises/rotational-cipher/approaches/alphabet
+[benchmark-application]: https://github.com/exercism/python/blob/main/exercises/practice/rotational-cipher/.articles/performance/code/Benchmark.py
+[timeit]: https://docs.python.org/3/library/timeit.html
diff --git a/exercises/practice/rotational-cipher/.articles/performance/snippet.md b/exercises/practice/rotational-cipher/.articles/performance/snippet.md
new file mode 100644
index 0000000000..0d4f9baba4
--- /dev/null
+++ b/exercises/practice/rotational-cipher/.articles/performance/snippet.md
@@ -0,0 +1,6 @@
+```
+rotate ascii long : 0.000189200000022538
+rotate alphabet long : 0.0002604000037536025
+rotate translate long : 1.3999990187585354e-05
+rotate recursion long : 0.001309900006162934
+```
From b6185ec43ced29cdabd9bdaa7ee84945c9f6864b Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Mon, 6 Feb 2023 10:17:58 -0800
Subject: [PATCH 338/826] Changes and Corrections for rotational-cipher
Edits, spelling, and grammer nits.
---
.../.approaches/alphabet/content.md | 28 ++++----
.../.approaches/ascii-values/content.md | 53 ++++++++-------
.../.approaches/introduction.md | 65 +++++++++++++------
.../.approaches/recursion/content.md | 20 +++---
.../.approaches/str-translate/content.md | 31 ++++-----
.../.articles/performance/content.md | 12 ++--
6 files changed, 120 insertions(+), 89 deletions(-)
diff --git a/exercises/practice/rotational-cipher/.approaches/alphabet/content.md b/exercises/practice/rotational-cipher/.approaches/alphabet/content.md
index 75d85c531e..601a3afc2d 100644
--- a/exercises/practice/rotational-cipher/.approaches/alphabet/content.md
+++ b/exercises/practice/rotational-cipher/.approaches/alphabet/content.md
@@ -16,28 +16,28 @@ def rotate(text, key):
return result
```
-The approach starts with defining the a constant which holds the whole alphabets lowercase letters.
-After that the function `rotate` is declared, then a variable `result` is defined with the value of an empty string.
+The approach starts with defining the constant `ALPHABET` which is a string of all lowercase letters.
+The function `rotate()` is then declared, and a variable `result` is defined as an empty string.
-Then is all the letters from the text argument iterated over through a [`for loop`][for-loop].
-Then is checked if the letter is a letter, if it is a letter then is checked if it is a uppercased letter.
+The text argument is then iterated over via a [`for loop`][for-loop].
+Each element is checked to make sure it is a letter, and subsequently checked if it is uppercase or lowercase.
+Uppercase letters are converted to lowercase.
+Then index of each letter is found in the `AlPHABET` constant.
+The numeric key value is added to the letter index and [modulo (`%`)][modulo] 26 is used on the result.
+Finally, the new number is used as an index into the `AlPHABET` constant, and the resulting letter is converted back to uppercase.
-If it is a uppercased letter then it is converted to lowe case and finds its index in the `AlPHABET` constant.
-Then is the key added to the index and [modulo (`%`)][modulo] 26 is used on the result.
-Then is the letter at the index found in the `AlPHABET` constant and the letter is converted to upcase.
+Lowercase letters follow the same process without the conversion steps.
-If the letter is a lowercased letter then it does the same process but don't convert the letter to downcase and then to uppercase.
+If the element is not a letter (for example, space or punctuation) then it is added directly to the result string.
+The result string is returned once the loop finishes.
-If the letter is not a letter then is the letter added to the result.
-When the loop is finished we return the result.
+If only English letters are needed, the constant [`string.ascii_lowercase`][ascii_lowercase] can be imported from the [`string`][string] module.
-If you only want to use english letters so could you import the alphabet instead of defining it yourself.
-Since in the [`string`][string] module there is a constant called [`ascii_lowercase`][ascii_lowercase] which holds the lowercased alphabet.
```python
-import string
+from string import ascii_lowercase
-AlPHABET = string.ascii_lowercase
+AlPHABET = ascii_lowercase
```
[ascii_lowercase]: https://docs.python.org/3/library/string.html#string.ascii_letters
diff --git a/exercises/practice/rotational-cipher/.approaches/ascii-values/content.md b/exercises/practice/rotational-cipher/.approaches/ascii-values/content.md
index cb637198a9..5b846ac8d4 100644
--- a/exercises/practice/rotational-cipher/.approaches/ascii-values/content.md
+++ b/exercises/practice/rotational-cipher/.approaches/ascii-values/content.md
@@ -14,37 +14,44 @@ def rotate(text, key):
return result
```
-This approach uses [ascii values][ascii], ascii stands for American Standard Code for Information Interchange.
-It is a character encoding standard for electronic communication.
-It is a 7-bit code, which means that it can represent 128 different characters.
-The system uses numbers to represent various characters, symbols, and other entities.
+This approach uses [ascii values][ascii].
+ASCII stands for **A**merican **S**tandard **C**ode for **I**nformation **I**nterchange.
+It is a 7-bit character encoding standard for electronic communication first described in 1969, becoming a formal standard in 2015.
+It uses numbers to represent 128 different entities including carriage returns, whitespace characters, box characters, alphabetic characters, punctuation, and the numbers 0-9.
-In ascii can you find all the downcased letter in the range between 97 and 123.
-While the upcased letters are in the range between 65 and 91.
+In ascii, all the lowercase English letters appear between 97 and 123.
+While the uppercase letters are in the range between 65 and 91.
-The reason why you might not want to do this approach is that it only supports the english alphabet.
+~~~~exercism/caution
-The approach starts with defining the function `rotate`, then a variable `result` is defined with the value of an empty string.
-Then is all the letters from the text argument iterated over through a [`for loop`][for-loop].
-Then is checked if the letter is a letter, if it is a letter then is checked if it is a uppercased letter.
+This approach only supports the English alphabet.
+Non-English alphabets are not contiguous in their ascii number ranges, and are not consistently defined across platforms.
+For example, the Scandinavian letter: **å** has the extended ascii value of 132, but is used in combination with Latin characters that appear in the 65-91 and 97-123 ranges.
+This means that a shift for an extended ascii word containing **å** won't result in an accurate alphabet position for a Scandinavian language.
-Python has a built in function called `ord` that converts a [unicode][unicode] symbol to an integer.
-The unicode's first 128 characters are the same as ascii.
+~~~~
-If it is a uppercased letter then is [`ord`][ord] used to convert the letters to an integer and is then added with the key and then subtracted with 65.
-Then is the result of that [modulo (`%`)][modulo] 26 added with 65.
-That is because we want to know which index in the alphabet the letter is.
-And if the number is over 26 we want to make sure that it is in the range of 0-26.
-So we use modulo to make sure it is in that range.
-To use modulo for a range we have to make sure that it starts at zero, thereby are we subtracting the integer value of the letter with 65.
-After that to get the back to a letter we add 65 and use the [`chr`][chr] method which converts an an unicode value to a letter.
-After that is the new letter added to the result.
+The approach starts with defining the function `rotate()`, with a variable `result` is assigned to an empty string.
+The elements of the text argument are then iterated over using a [`for loop`][for-loop].
+Each element is checked to see if it is a letter, and then is checked if it is an uppercase letter.
-If the letter is a lowercased letter then is the same done but with the ascii value of 97 subtracted with the letter.
+Python has a builtin function called `ord` that converts a [unicode][unicode] symbol to an integer representation.
+Unicode's first 128 code points have the same numbers as their ascii counterparts.
-If the letter is not a letter then is the letter added to the result.
-When the loop is finished we return the result.
+If the element is an uppercase letter, [`ord`][ord] is used to convert the letter to an integer.
+The integer is added to the numeric key and then 65 is subtracted from the total.
+Finally, the result is [modulo (`%`)][modulo] 26 (_to put the value within the 2) and 65 is added back.
+
+This is because we want to know which letter of the alphabet the number will become.
+And if the new number is over 26 we want to make sure that it "wraps around" to remain in the range of 0-26.
+To properly use modulo for a range we have to make sure that it starts at zero, so we subtract 65.
+To get back to a letter in the asciii range we add 65 and use the [`chr`][chr] method to convert the value to a letter.
+
+The process is the same for a lowercase letter, but with 97 subtracted to put the letter in the lowercase ascii range of 97 - 123.
+
+Any element that is not a letter (_whitespace or punctuation_) is added directly to the result string.
+When all the elements have been looped over, the full result is returned.
[ascii]: https://en.wikipedia.org/wiki/ASCII
[chr]: https://docs.python.org/3/library/functions.html#chr
diff --git a/exercises/practice/rotational-cipher/.approaches/introduction.md b/exercises/practice/rotational-cipher/.approaches/introduction.md
index 90b8d6fa8a..4b3e1889bf 100644
--- a/exercises/practice/rotational-cipher/.approaches/introduction.md
+++ b/exercises/practice/rotational-cipher/.approaches/introduction.md
@@ -1,22 +1,30 @@
# Introduction
There are various ways to solve `rotational-cipher`.
-You can for example use a [ascii values][ascii], alphabet, recursion, and `str.translate`.
+You can for example use [ascii values][ascii], an alphabet `str` or `list`, recursion, or `str.translate`.
+
## General guidance
-The goal of this exercise is to rotate the letters in a string by a given key.
+The goal of this exercise is to shift the letters in a string by a given integer key between 0 and 26.
+The letter in the encrypted string is shifted for as many values (or "positions") as the value of the key.
## Approach: Using ascii values
-This approach is very simple and easy to understand.
-it uses the ascii value of the letters to rotate them.
-There the numbers 65-91 in the ascii range represent downcased letters.
-While 97-123 represent upcased letters.
+This approach is straightforward to understand.
+It uses the ascii value of the letters to rotate them within the message.
+The numbers 65-91 in the ascii range represent lowercase Latin letters, while 97-123 represent uppercase Latin letters.
+
+
+~~~~exercism/caution
+
+This approach only supports the English alphabet.
+Non-English alphabets are not contiguous in their ascii number ranges, and are not consistently defined across platforms.
+For example, the Scandinavian letter: **å** has the extended ascii value of 132, but is used in combination with Latin characters that appear in the 65-91 and 97-123 ranges.
+This means that a shift for an extended ascii word containing **å** won't result in an accurate alphabet position for a Scandinavian language.
+
+~~~~
-The reason why you might not want to do this approach is that it only supports the english alphabet.
-Say we want to use the scandinavian letter: **å**, then this approach will not work.
-Since **å** has the ascii value of 132.
```python
def rotate(text, key):
@@ -34,16 +42,20 @@ def rotate(text, key):
For more information, check the [ascii values approach][approach-ascii-values].
+
## Approach: Alphabet
-This approach is similar to the previous one, but instead of using the ascii values, it uses the index of the letter in the alphabet.
-It requires the storing of a string and unless you are using two strings you have to convert the letters from upper to lower case.
+This approach is similar to the ascii one, but it uses the index number of each letter in an alphabet string.
+It requires making a string for all the letters in an alphabet.
+And unless two strings are used, you will have to convert individual letters from lower to upper case (or vice-versa).
+
+The big advantage of this approach is the ability to use any alphabet (_although there are some issues with combining characters in Unicode._).
+Here, if we want to use the scandinavian letter: **å**, we can simply insert it into our string where we want it:
+`abcdefghijklmnopqrstuvwxyzå` and the rotation will work correctly.
-What this approach although give is the possibility to use any alphabet.
-Say we want to use the scandinavian letter: **å**, then we just add it where we want it:
-`abcdefghijklmnopqrstuvwxyzå` and it will rotate correctly around that.
```python
+# This only uses English characters
AlPHABET = "abcdefghijklmnopqrstuvwxyz"
def rotate(text, key):
@@ -61,14 +73,19 @@ def rotate(text, key):
For more information, check the [Alphabet approach][approach-alphabet].
+
## Approach: Str translate
-This approach is similar to the previous one, but instead of using the index of the letter in the alphabet, it uses the `str.translate` method.
-The benefit of this approach is that it has no visible loop, thereby the code becomes more concise.
-What to note is that the `str.translate` still loops over the `string` thereby even if it is no visible loop, it doesn't mean that a method is not looping.
+This approach uses the [`str.translate`][str-translate] method to create a mapping from input to shifted string instead of using the index of an alphabet string to calculate the shift.
+The benefit of this approach is that it has no visible loop, making the code more concise.
+
+~~~~exercism/note
+`str.translate` **still loops over the `string`** even if it is not visibly doing so.
+~~~~
+
```python
-AlPHABET = "abcdefghijklmnopqrstuvwxyz"
+AlPHABET = "abcdefghijklmnopqrstuvwxyz
def rotate(text, key):
translator = AlPHABET[key:] + AlPHABET[:key]
@@ -77,13 +94,19 @@ def rotate(text, key):
For more information, check the [Str translate approach][approach-str-translate].
+
## Approach: Recursion
-In this approach we use a recursive function.
+This approach uses a recursive function.
A recursive function is a function that calls itself.
This approach can be more concise than other approaches, and may also be more readable for some audiences.
-The reason why you might not want to use this approach is that Python has a [recursion limit][recursion-limit] with a default of 1000.
+
+~~~~exercism/caution
+Python does not have any tail-call optimization and has a default [recursion limit][recursion-limit] of 1000 calls on the stack.
+Calculate your base case carefully to avoid errors.
+~~~~
+
```python
AlPHABET = "abcdefghijklmnopqrstuvwxyz"
@@ -103,6 +126,7 @@ def rotate(text, key):
For more information, check the [Recursion approach][approach-recursion].
+
## Benchmark
For more information, check the [Performance article][article-performance].
@@ -114,3 +138,4 @@ For more information, check the [Performance article][article-performance].
[approach-alphabet]: https://exercism.org/tracks/python/exercises/rotational-cipher/approaches/alphabet
[article-performance]: https://exercism.org/tracks/python/exercises/rotational-cipher/articles/performance
[recursion-limit]: https://docs.python.org/3/library/sys.html#sys.setrecursionlimit
+[str-translate]: https://docs.python.org/3/library/stdtypes.html?highlight=str%20translate#str.translate
diff --git a/exercises/practice/rotational-cipher/.approaches/recursion/content.md b/exercises/practice/rotational-cipher/.approaches/recursion/content.md
index d4e4e01908..ca7bf2393d 100644
--- a/exercises/practice/rotational-cipher/.approaches/recursion/content.md
+++ b/exercises/practice/rotational-cipher/.approaches/recursion/content.md
@@ -16,23 +16,25 @@ def rotate(text, key):
return first_letter + rotate(rest, key)
```
-This approach uses a very similar approach to alphabet.
-And uses the same logic but instead of a loop so does this approach use [concept:python/recursion]() to solve the problem.
+This approach uses the same logic as that of the `alphabet` approach, but instead of a `loop`, [concept:python/recursion]() is used to parse the text and solve the problem.
Recursion is a programming technique where a function calls itself.
-It is a powerful technique, but can be more tricky to implement than a while loop.
-Recursion isn't that common in Python, it is more common in functional programming languages, like: [Elixir][elixir], [Haskell][haskell], and [Clojure][clojure].
+It is powerful, but can be more tricky to implement than a `while loop` or `for loop`.
+Recursive techniques are more common in functional programming languages like [Elixir][elixir], [Haskell][haskell], and [Clojure][clojure] than they are in Python.
-Solving this exercise with recursion removes the need for a "result" variable and the instantiation of a `loop`.
+Solving this exercise with recursion removes the need for a `result` variable and the instantiation of a `loop`.
If the number is not equal to one, we call ` + rotate(rest)`.
Then the `rotate` function can execute the same code again with new values.
-Meaning we can get a long chain or stack of ` + rotate(rest)` until the rest variable is exhausted and then the code adds `""`.
+We can build a long chain or "stack" of ` + rotate(rest)` calls until the `rest` variable is exhausted and the code adds `""`.
That translates to something like this: ` + + + + ""`.
-In Python, we can't have a function call itself more than 1000 times by default.
-Code that exceeds this recursion limit will throw a [RecursionError][recursion-error].
-While it is possible to adjust the [recursion limit][recursion-limit], doing so risks crashing Python and may also crash your system.
+
+~~~~exercism/note
+By default, we can't have a function call itself more than 1000 times.
+Code that exceeds this recursion limit will throw a RecursionError.
+While it is possible to adjust the recursion limit, doing so risks crashing Python and may also crash your system.
Casually raising the recursion limit is not recommended.
+~~~~
[clojure]: https://exercism.org/tracks/clojure
[elixir]: https://exercism.org/tracks/elixir
diff --git a/exercises/practice/rotational-cipher/.approaches/str-translate/content.md b/exercises/practice/rotational-cipher/.approaches/str-translate/content.md
index e38f8359c7..b95f5139b1 100644
--- a/exercises/practice/rotational-cipher/.approaches/str-translate/content.md
+++ b/exercises/practice/rotational-cipher/.approaches/str-translate/content.md
@@ -8,45 +8,42 @@ def rotate(text, key):
return text.translate(str.maketrans(AlPHABET + AlPHABET.upper(), translator + translator.upper()))
```
-This approach uses the [`.translate`][translate] method to solve the problem.
+This approach uses the [`.translate`][translate] method.
`translate` takes a translation table as an argument.
To create a translation table we use [str.`makestrans`][maketrans].
-The approach starts with defining the a constant which holds the whole alphabets lowercase letters.
-Then the function `rotate` is declared.
-Then is the `translator` variable defined with the value of the `AlPHABET` constant [sliced][slicing] from the key to the end and then the `AlPHABET` constant sliced from the start to the key.
+This approach starts with defining a constant of all the lowercase letters in the alphabet.
+Then the function `rotate()` is declared.
+A `translator` variable defined with the value of the `AlPHABET` constant [sliced][slicing] from the key to the end and then sliced from the start to the key.
-This is done so we have 2 strings which are the same but shifted by the key.
+This is done so we have 2 strings which are the same but shifted by the key value.
Say we have the `AlPHABET` constant with the value of `abcdefghijklmnopqrstuvwxyz` and the key is 3.
Then the `translator` variable will have the value of `defghijklmnopqrstuvwxyzabc`.
-Then is the `translate` method called on the `text` argument.
-The `translate` method takes a translation table as an argument.
-To create a translation table we use str.`makestrans`maketrans.
+`str.translate` is then called on the `text` argument.
+`str.translate` takes a translation table mapping start values to transformed values as an argument.
+To create a translation table, `str.makestrans` is used.
+`makestrans` takes 2 arguments: the first is the string to be translated, and the second is the string the first argument should be translated to.
-The `makestrans` method takes 2 arguments.
-The first argument is the string which holds the characters which should be translated.
-The second argument is the string which holds the characters which the characters from the first argument should be translated to.
+For our solution, the first argument is the `AlPHABET` constant + the `AlPHABET` constant in uppercase.
+The second argument is the `translator` variable + uppercase `translator` variable.
-The first argument is the `AlPHABET` constant and the `AlPHABET` constant uppercased.
-The second argument is the `translator` variable and the `translator` variable uppercased.
+`makestrans` does is that it takes the [Unicode][unicode] values of the first argument and maps them to the corresponding Unicode values in the second argument, creating a `dict`.
-What the `makestrans` does is that it takes the first argument and maps it to the second argument.
-It does that by creating a [dictionary], and converting the letter to [unicode][unicode].
```python
>>> str.maketrans("abc", "def")
{97: 100, 98: 101, 99: 102}
```
-The `translate` method takes the dictionary created by the `makestrans` method and uses it to translate the characters in the `text` argument.
+`str.translate` takes the `dict` created by `str.makestrans` and uses it to translate the characters in the `text` argument.
```python
>>> "abc".translate({97: 100, 98: 101, 99: 102})
'def'
```
-When the loop is finished we return the result.
+Once the `str.translate` loop completes, we return the `result`.
[maketrans]: https://docs.python.org/3/library/stdtypes.html#str.maketrans
[slicing]: https://www.w3schools.com/python/python_strings_slicing.asp
diff --git a/exercises/practice/rotational-cipher/.articles/performance/content.md b/exercises/practice/rotational-cipher/.articles/performance/content.md
index 851180f56f..9833401155 100644
--- a/exercises/practice/rotational-cipher/.articles/performance/content.md
+++ b/exercises/practice/rotational-cipher/.articles/performance/content.md
@@ -2,7 +2,7 @@
In this article, we'll examine the performance difference between approaches for `rotational cipher` in Python.
-The [approaches page][approaches] lists two approaches to this exercise:
+The [approaches page][approaches] lists four approaches to this exercise:
1. [Using recursion][approach-recursion]
2. [Using `str.translate`][approach-str-translate]
@@ -12,7 +12,7 @@ The [approaches page][approaches] lists two approaches to this exercise:
## Benchmarks
To benchmark the approaches, we wrote a [small benchmark application][benchmark-application] using the [`timeit`][timeit] library.
-These tests were run in windows 11, using Python 3.11.1.
+These tests were run in `Windows 11`, using Python `3.11.1`.
Your system results may vary.
```
@@ -29,11 +29,11 @@ rotate recursion short : 5.4000120144337416e-06
## Conclusion
-For a long string as input, is the translate approach the fastest, followed by ascii, alphabet and last recursion.
-For a short string as input, is the alphabet approach the fastest, followed by ascii, recursion and last translate.
+For a long string as input, the `str.translate` approach the fastest, followed by ascii, alphabet, and finally recursion.
+For a short string as input, is the alphabet approach the is the fastest, followed by ascii, recursion and finally `str.translate`.
-This means that if you know the input is a short string, the fastest approach is to use the alphabet approach.
-On the other hand, if you know the input is a long string, the fastest approach is to use the translate approach.
+This means that if you know the input is a short string, the fastest approach is to use the alphabet, and forgo the overhead of making and saving a translation dictionary.
+On the other hand, if the input is a long string, the overhead of making a dictionary is amortized over the length of the text to be translated, and the fastest approach becomes `str.translate`.
[approach-recursion]: https://exercism.org/tracks/python/exercises/rotational-cipher/approaches/recursion
[approach-str-translate]: https://exercism.org/tracks/python/exercises/rotational-cipher/approaches/str-translate
From 2f190cd045a8a7023c93cb1e4a95e005a955c04e Mon Sep 17 00:00:00 2001
From: Carl
Date: Mon, 6 Feb 2023 19:36:14 +0100
Subject: [PATCH 339/826] Updated formating
---
.../.approaches/alphabet/content.md | 1 -
.../.approaches/ascii-values/content.md | 7 +++---
.../.approaches/introduction.md | 23 +++++--------------
.../.approaches/recursion/content.md | 5 ++--
.../.approaches/str-translate/content.md | 3 +--
.../.articles/performance/content.md | 2 +-
6 files changed, 13 insertions(+), 28 deletions(-)
diff --git a/exercises/practice/rotational-cipher/.approaches/alphabet/content.md b/exercises/practice/rotational-cipher/.approaches/alphabet/content.md
index 601a3afc2d..e79625dbdf 100644
--- a/exercises/practice/rotational-cipher/.approaches/alphabet/content.md
+++ b/exercises/practice/rotational-cipher/.approaches/alphabet/content.md
@@ -33,7 +33,6 @@ The result string is returned once the loop finishes.
If only English letters are needed, the constant [`string.ascii_lowercase`][ascii_lowercase] can be imported from the [`string`][string] module.
-
```python
from string import ascii_lowercase
diff --git a/exercises/practice/rotational-cipher/.approaches/ascii-values/content.md b/exercises/practice/rotational-cipher/.approaches/ascii-values/content.md
index 5b846ac8d4..0b9fb07718 100644
--- a/exercises/practice/rotational-cipher/.approaches/ascii-values/content.md
+++ b/exercises/practice/rotational-cipher/.approaches/ascii-values/content.md
@@ -22,15 +22,14 @@ It uses numbers to represent 128 different entities including carriage returns,
In ascii, all the lowercase English letters appear between 97 and 123.
While the uppercase letters are in the range between 65 and 91.
-~~~~exercism/caution
+```exercism/caution
This approach only supports the English alphabet.
Non-English alphabets are not contiguous in their ascii number ranges, and are not consistently defined across platforms.
For example, the Scandinavian letter: **å** has the extended ascii value of 132, but is used in combination with Latin characters that appear in the 65-91 and 97-123 ranges.
This means that a shift for an extended ascii word containing **å** won't result in an accurate alphabet position for a Scandinavian language.
-~~~~
-
+```
The approach starts with defining the function `rotate()`, with a variable `result` is assigned to an empty string.
The elements of the text argument are then iterated over using a [`for loop`][for-loop].
@@ -41,7 +40,7 @@ Unicode's first 128 code points have the same numbers as their ascii counterpart
If the element is an uppercase letter, [`ord`][ord] is used to convert the letter to an integer.
The integer is added to the numeric key and then 65 is subtracted from the total.
-Finally, the result is [modulo (`%`)][modulo] 26 (_to put the value within the 2) and 65 is added back.
+Finally, the result is [modulo (`%`)][modulo] 26 (_to put the value within the 2_) and 65 is added back.
This is because we want to know which letter of the alphabet the number will become.
And if the new number is over 26 we want to make sure that it "wraps around" to remain in the range of 0-26.
diff --git a/exercises/practice/rotational-cipher/.approaches/introduction.md b/exercises/practice/rotational-cipher/.approaches/introduction.md
index 4b3e1889bf..67a6264dfd 100644
--- a/exercises/practice/rotational-cipher/.approaches/introduction.md
+++ b/exercises/practice/rotational-cipher/.approaches/introduction.md
@@ -3,7 +3,6 @@
There are various ways to solve `rotational-cipher`.
You can for example use [ascii values][ascii], an alphabet `str` or `list`, recursion, or `str.translate`.
-
## General guidance
The goal of this exercise is to shift the letters in a string by a given integer key between 0 and 26.
@@ -15,16 +14,14 @@ This approach is straightforward to understand.
It uses the ascii value of the letters to rotate them within the message.
The numbers 65-91 in the ascii range represent lowercase Latin letters, while 97-123 represent uppercase Latin letters.
-
-~~~~exercism/caution
+```exercism/caution
This approach only supports the English alphabet.
Non-English alphabets are not contiguous in their ascii number ranges, and are not consistently defined across platforms.
For example, the Scandinavian letter: **å** has the extended ascii value of 132, but is used in combination with Latin characters that appear in the 65-91 and 97-123 ranges.
This means that a shift for an extended ascii word containing **å** won't result in an accurate alphabet position for a Scandinavian language.
-~~~~
-
+```
```python
def rotate(text, key):
@@ -42,7 +39,6 @@ def rotate(text, key):
For more information, check the [ascii values approach][approach-ascii-values].
-
## Approach: Alphabet
This approach is similar to the ascii one, but it uses the index number of each letter in an alphabet string.
@@ -53,7 +49,6 @@ The big advantage of this approach is the ability to use any alphabet (_although
Here, if we want to use the scandinavian letter: **å**, we can simply insert it into our string where we want it:
`abcdefghijklmnopqrstuvwxyzå` and the rotation will work correctly.
-
```python
# This only uses English characters
AlPHABET = "abcdefghijklmnopqrstuvwxyz"
@@ -73,16 +68,14 @@ def rotate(text, key):
For more information, check the [Alphabet approach][approach-alphabet].
-
## Approach: Str translate
This approach uses the [`str.translate`][str-translate] method to create a mapping from input to shifted string instead of using the index of an alphabet string to calculate the shift.
The benefit of this approach is that it has no visible loop, making the code more concise.
-~~~~exercism/note
+```exercism/note
`str.translate` **still loops over the `string`** even if it is not visibly doing so.
-~~~~
-
+```
```python
AlPHABET = "abcdefghijklmnopqrstuvwxyz
@@ -94,19 +87,16 @@ def rotate(text, key):
For more information, check the [Str translate approach][approach-str-translate].
-
## Approach: Recursion
This approach uses a recursive function.
A recursive function is a function that calls itself.
This approach can be more concise than other approaches, and may also be more readable for some audiences.
-
-~~~~exercism/caution
+```exercism/caution
Python does not have any tail-call optimization and has a default [recursion limit][recursion-limit] of 1000 calls on the stack.
Calculate your base case carefully to avoid errors.
-~~~~
-
+```
```python
AlPHABET = "abcdefghijklmnopqrstuvwxyz"
@@ -126,7 +116,6 @@ def rotate(text, key):
For more information, check the [Recursion approach][approach-recursion].
-
## Benchmark
For more information, check the [Performance article][article-performance].
diff --git a/exercises/practice/rotational-cipher/.approaches/recursion/content.md b/exercises/practice/rotational-cipher/.approaches/recursion/content.md
index ca7bf2393d..1addd0849f 100644
--- a/exercises/practice/rotational-cipher/.approaches/recursion/content.md
+++ b/exercises/practice/rotational-cipher/.approaches/recursion/content.md
@@ -28,13 +28,12 @@ Then the `rotate` function can execute the same code again with new values.
We can build a long chain or "stack" of ` + rotate(rest)` calls until the `rest` variable is exhausted and the code adds `""`.
That translates to something like this: ` + + + + ""`.
-
-~~~~exercism/note
+```exercism/note
By default, we can't have a function call itself more than 1000 times.
Code that exceeds this recursion limit will throw a RecursionError.
While it is possible to adjust the recursion limit, doing so risks crashing Python and may also crash your system.
Casually raising the recursion limit is not recommended.
-~~~~
+```
[clojure]: https://exercism.org/tracks/clojure
[elixir]: https://exercism.org/tracks/elixir
diff --git a/exercises/practice/rotational-cipher/.approaches/str-translate/content.md b/exercises/practice/rotational-cipher/.approaches/str-translate/content.md
index b95f5139b1..bee4885a7b 100644
--- a/exercises/practice/rotational-cipher/.approaches/str-translate/content.md
+++ b/exercises/practice/rotational-cipher/.approaches/str-translate/content.md
@@ -30,13 +30,12 @@ The second argument is the `translator` variable + uppercase `translator` variab
`makestrans` does is that it takes the [Unicode][unicode] values of the first argument and maps them to the corresponding Unicode values in the second argument, creating a `dict`.
-
```python
>>> str.maketrans("abc", "def")
{97: 100, 98: 101, 99: 102}
```
-`str.translate` takes the `dict` created by `str.makestrans` and uses it to translate the characters in the `text` argument.
+`str.translate` takes the `dict` created by `str.makestrans` and uses it to translate the characters in the `text` argument.
```python
>>> "abc".translate({97: 100, 98: 101, 99: 102})
diff --git a/exercises/practice/rotational-cipher/.articles/performance/content.md b/exercises/practice/rotational-cipher/.articles/performance/content.md
index 9833401155..92d6c1f14d 100644
--- a/exercises/practice/rotational-cipher/.articles/performance/content.md
+++ b/exercises/practice/rotational-cipher/.articles/performance/content.md
@@ -30,7 +30,7 @@ rotate recursion short : 5.4000120144337416e-06
## Conclusion
For a long string as input, the `str.translate` approach the fastest, followed by ascii, alphabet, and finally recursion.
-For a short string as input, is the alphabet approach the is the fastest, followed by ascii, recursion and finally `str.translate`.
+For a short string as input, is the alphabet approach the is the fastest, followed by ascii, recursion and finally `str.translate`.
This means that if you know the input is a short string, the fastest approach is to use the alphabet, and forgo the overhead of making and saving a translation dictionary.
On the other hand, if the input is a long string, the overhead of making a dictionary is amortized over the length of the text to be translated, and the fastest approach becomes `str.translate`.
From 3d714863bf0deb53d18885b9b84eecd14895d37c Mon Sep 17 00:00:00 2001
From: Carl
Date: Mon, 6 Feb 2023 19:45:50 +0100
Subject: [PATCH 340/826] Added back tilda
---
.../.approaches/ascii-values/content.md | 6 +++---
.../rotational-cipher/.approaches/introduction.md | 12 ++++++------
.../.approaches/recursion/content.md | 4 ++--
3 files changed, 11 insertions(+), 11 deletions(-)
diff --git a/exercises/practice/rotational-cipher/.approaches/ascii-values/content.md b/exercises/practice/rotational-cipher/.approaches/ascii-values/content.md
index 0b9fb07718..5b37134ed0 100644
--- a/exercises/practice/rotational-cipher/.approaches/ascii-values/content.md
+++ b/exercises/practice/rotational-cipher/.approaches/ascii-values/content.md
@@ -22,14 +22,14 @@ It uses numbers to represent 128 different entities including carriage returns,
In ascii, all the lowercase English letters appear between 97 and 123.
While the uppercase letters are in the range between 65 and 91.
-```exercism/caution
+~~~~exercism/caution
This approach only supports the English alphabet.
Non-English alphabets are not contiguous in their ascii number ranges, and are not consistently defined across platforms.
For example, the Scandinavian letter: **å** has the extended ascii value of 132, but is used in combination with Latin characters that appear in the 65-91 and 97-123 ranges.
This means that a shift for an extended ascii word containing **å** won't result in an accurate alphabet position for a Scandinavian language.
-```
+~~~~
The approach starts with defining the function `rotate()`, with a variable `result` is assigned to an empty string.
The elements of the text argument are then iterated over using a [`for loop`][for-loop].
@@ -40,7 +40,7 @@ Unicode's first 128 code points have the same numbers as their ascii counterpart
If the element is an uppercase letter, [`ord`][ord] is used to convert the letter to an integer.
The integer is added to the numeric key and then 65 is subtracted from the total.
-Finally, the result is [modulo (`%`)][modulo] 26 (_to put the value within the 2_) and 65 is added back.
+Finally, the result is [modulo (%)][modulo] 26 (_to put the value within the 0-26 range_) and 65 is added back.
This is because we want to know which letter of the alphabet the number will become.
And if the new number is over 26 we want to make sure that it "wraps around" to remain in the range of 0-26.
diff --git a/exercises/practice/rotational-cipher/.approaches/introduction.md b/exercises/practice/rotational-cipher/.approaches/introduction.md
index 67a6264dfd..f8c5e95ed0 100644
--- a/exercises/practice/rotational-cipher/.approaches/introduction.md
+++ b/exercises/practice/rotational-cipher/.approaches/introduction.md
@@ -14,14 +14,14 @@ This approach is straightforward to understand.
It uses the ascii value of the letters to rotate them within the message.
The numbers 65-91 in the ascii range represent lowercase Latin letters, while 97-123 represent uppercase Latin letters.
-```exercism/caution
+~~~~exercism/caution
This approach only supports the English alphabet.
Non-English alphabets are not contiguous in their ascii number ranges, and are not consistently defined across platforms.
For example, the Scandinavian letter: **å** has the extended ascii value of 132, but is used in combination with Latin characters that appear in the 65-91 and 97-123 ranges.
This means that a shift for an extended ascii word containing **å** won't result in an accurate alphabet position for a Scandinavian language.
-```
+~~~~
```python
def rotate(text, key):
@@ -73,9 +73,9 @@ For more information, check the [Alphabet approach][approach-alphabet].
This approach uses the [`str.translate`][str-translate] method to create a mapping from input to shifted string instead of using the index of an alphabet string to calculate the shift.
The benefit of this approach is that it has no visible loop, making the code more concise.
-```exercism/note
+~~~~exercism/note
`str.translate` **still loops over the `string`** even if it is not visibly doing so.
-```
+~~~~
```python
AlPHABET = "abcdefghijklmnopqrstuvwxyz
@@ -93,10 +93,10 @@ This approach uses a recursive function.
A recursive function is a function that calls itself.
This approach can be more concise than other approaches, and may also be more readable for some audiences.
-```exercism/caution
+~~~~exercism/caution
Python does not have any tail-call optimization and has a default [recursion limit][recursion-limit] of 1000 calls on the stack.
Calculate your base case carefully to avoid errors.
-```
+~~~~
```python
AlPHABET = "abcdefghijklmnopqrstuvwxyz"
diff --git a/exercises/practice/rotational-cipher/.approaches/recursion/content.md b/exercises/practice/rotational-cipher/.approaches/recursion/content.md
index 1addd0849f..689f8aaed7 100644
--- a/exercises/practice/rotational-cipher/.approaches/recursion/content.md
+++ b/exercises/practice/rotational-cipher/.approaches/recursion/content.md
@@ -28,12 +28,12 @@ Then the `rotate` function can execute the same code again with new values.
We can build a long chain or "stack" of ` + rotate(rest)` calls until the `rest` variable is exhausted and the code adds `""`.
That translates to something like this: ` + + + + ""`.
-```exercism/note
+~~~~exercism/note
By default, we can't have a function call itself more than 1000 times.
Code that exceeds this recursion limit will throw a RecursionError.
While it is possible to adjust the recursion limit, doing so risks crashing Python and may also crash your system.
Casually raising the recursion limit is not recommended.
-```
+~~~~
[clojure]: https://exercism.org/tracks/clojure
[elixir]: https://exercism.org/tracks/elixir
From 3c2f97b27cd43e99cd082ec164cebeaa992375cf Mon Sep 17 00:00:00 2001
From: Carl
Date: Mon, 6 Feb 2023 19:51:32 +0100
Subject: [PATCH 341/826] fix
---
.../practice/rotational-cipher/.articles/performance/content.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/exercises/practice/rotational-cipher/.articles/performance/content.md b/exercises/practice/rotational-cipher/.articles/performance/content.md
index 92d6c1f14d..0b40eb7cde 100644
--- a/exercises/practice/rotational-cipher/.articles/performance/content.md
+++ b/exercises/practice/rotational-cipher/.articles/performance/content.md
@@ -30,7 +30,7 @@ rotate recursion short : 5.4000120144337416e-06
## Conclusion
For a long string as input, the `str.translate` approach the fastest, followed by ascii, alphabet, and finally recursion.
-For a short string as input, is the alphabet approach the is the fastest, followed by ascii, recursion and finally `str.translate`.
+For a short string as input, is the alphabet approach the fastest, followed by ascii, recursion and finally `str.translate`.
This means that if you know the input is a short string, the fastest approach is to use the alphabet, and forgo the overhead of making and saving a translation dictionary.
On the other hand, if the input is a long string, the overhead of making a dictionary is amortized over the length of the text to be translated, and the fastest approach becomes `str.translate`.
From c63546060c307e160a8a0d9b2e6a3643bc6e368d Mon Sep 17 00:00:00 2001
From: Carl
Date: Mon, 6 Feb 2023 19:56:53 +0100
Subject: [PATCH 342/826] fix
---
.../rotational-cipher/.approaches/ascii-values/content.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/exercises/practice/rotational-cipher/.approaches/ascii-values/content.md b/exercises/practice/rotational-cipher/.approaches/ascii-values/content.md
index 5b37134ed0..a126898526 100644
--- a/exercises/practice/rotational-cipher/.approaches/ascii-values/content.md
+++ b/exercises/practice/rotational-cipher/.approaches/ascii-values/content.md
@@ -45,7 +45,7 @@ Finally, the result is [modulo (%)][modulo] 26 (_to put the value within the 0-2
This is because we want to know which letter of the alphabet the number will become.
And if the new number is over 26 we want to make sure that it "wraps around" to remain in the range of 0-26.
To properly use modulo for a range we have to make sure that it starts at zero, so we subtract 65.
-To get back to a letter in the asciii range we add 65 and use the [`chr`][chr] method to convert the value to a letter.
+To get back to a letter in the ascii range we add 65 and use the [`chr`][chr] method to convert the value to a letter.
The process is the same for a lowercase letter, but with 97 subtracted to put the letter in the lowercase ascii range of 97 - 123.
From 588058183e1c976982e458344351fcf7d5c14ded Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Mon, 6 Feb 2023 11:02:42 -0800
Subject: [PATCH 343/826] Apply suggestions from code review
Two final nits...
---
.../rotational-cipher/.approaches/ascii-values/content.md | 2 +-
.../rotational-cipher/.approaches/str-translate/content.md | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/exercises/practice/rotational-cipher/.approaches/ascii-values/content.md b/exercises/practice/rotational-cipher/.approaches/ascii-values/content.md
index a126898526..c3fba5945a 100644
--- a/exercises/practice/rotational-cipher/.approaches/ascii-values/content.md
+++ b/exercises/practice/rotational-cipher/.approaches/ascii-values/content.md
@@ -47,7 +47,7 @@ And if the new number is over 26 we want to make sure that it "wraps around" to
To properly use modulo for a range we have to make sure that it starts at zero, so we subtract 65.
To get back to a letter in the ascii range we add 65 and use the [`chr`][chr] method to convert the value to a letter.
-The process is the same for a lowercase letter, but with 97 subtracted to put the letter in the lowercase ascii range of 97 - 123.
+The process is the same for a lowercase letter, but with 97 subtracted/added to put the letter in the lowercase ascii range of 97 - 123.
Any element that is not a letter (_whitespace or punctuation_) is added directly to the result string.
When all the elements have been looped over, the full result is returned.
diff --git a/exercises/practice/rotational-cipher/.approaches/str-translate/content.md b/exercises/practice/rotational-cipher/.approaches/str-translate/content.md
index bee4885a7b..b80da8d2ec 100644
--- a/exercises/practice/rotational-cipher/.approaches/str-translate/content.md
+++ b/exercises/practice/rotational-cipher/.approaches/str-translate/content.md
@@ -28,7 +28,7 @@ To create a translation table, `str.makestrans` is used.
For our solution, the first argument is the `AlPHABET` constant + the `AlPHABET` constant in uppercase.
The second argument is the `translator` variable + uppercase `translator` variable.
-`makestrans` does is that it takes the [Unicode][unicode] values of the first argument and maps them to the corresponding Unicode values in the second argument, creating a `dict`.
+`str.makestrans` takes the [Unicode][unicode] values of the first argument and maps them to the corresponding Unicode values in the second argument, creating a `dict`.
```python
>>> str.maketrans("abc", "def")
From c8967079f25f9864d9eef143a77554180c1322be Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Mon, 6 Feb 2023 11:04:42 -0800
Subject: [PATCH 344/826] Update
exercises/practice/rotational-cipher/.articles/performance/content.md
---
.../practice/rotational-cipher/.articles/performance/content.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/exercises/practice/rotational-cipher/.articles/performance/content.md b/exercises/practice/rotational-cipher/.articles/performance/content.md
index 0b40eb7cde..8401b40e25 100644
--- a/exercises/practice/rotational-cipher/.articles/performance/content.md
+++ b/exercises/practice/rotational-cipher/.articles/performance/content.md
@@ -29,7 +29,7 @@ rotate recursion short : 5.4000120144337416e-06
## Conclusion
-For a long string as input, the `str.translate` approach the fastest, followed by ascii, alphabet, and finally recursion.
+For a long string as input, the `str.translate` approach is the fastest, followed by ascii, alphabet, and finally recursion.
For a short string as input, is the alphabet approach the fastest, followed by ascii, recursion and finally `str.translate`.
This means that if you know the input is a short string, the fastest approach is to use the alphabet, and forgo the overhead of making and saving a translation dictionary.
From 5e8951a574473436bad39e9f10ebf6ee9fe45b2d Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Thu, 9 Feb 2023 16:41:40 -0800
Subject: [PATCH 345/826] Addressed issues 3181 and 3161 for lasagna test file.
---
.../guidos-gorgeous-lasagna/lasagna_test.py | 24 +++++++++++++------
1 file changed, 17 insertions(+), 7 deletions(-)
diff --git a/exercises/concept/guidos-gorgeous-lasagna/lasagna_test.py b/exercises/concept/guidos-gorgeous-lasagna/lasagna_test.py
index 3a10c9db6b..aeacc53b07 100644
--- a/exercises/concept/guidos-gorgeous-lasagna/lasagna_test.py
+++ b/exercises/concept/guidos-gorgeous-lasagna/lasagna_test.py
@@ -1,29 +1,32 @@
import unittest
import pytest
-
+# For this first exercise, it is really important to be clear about how we are importing names for tests.
+# To that end, we are putting a try/catch around imports and throwing specific messages to help students
+# decode that they need to create and title their constants and functions in a specific way.
try:
from lasagna import (EXPECTED_BAKE_TIME,
bake_time_remaining,
preparation_time_in_minutes,
elapsed_time_in_minutes)
-
+# Here, we are separating the constant import errors from the function name import errors
except ImportError as import_fail:
message = import_fail.args[0].split('(', maxsplit=1)
item_name = import_fail.args[0].split()[3]
- if 'EXPECTED_BAKE_TIME' in message:
+ if 'EXPECTED_BAKE_TIME' in item_name:
# pylint: disable=raise-missing-from
- raise ImportError(f'We can not find or import the constant {item_name} in your'
- " 'lasagna.py' file. Did you mis-name or forget to define it?")
+ raise ImportError(f'\n\nMISSING CONSTANT --> \nWe can not find or import the constant {item_name} in your'
+ " 'lasagna.py' file.\nDid you mis-name or forget to define it?") from None
else:
item_name = item_name[:-1] + "()'"
# pylint: disable=raise-missing-from
- raise ImportError("In your 'lasagna.py' file, we can not find or import the"
- f' function named {item_name}. Did you mis-name or forget to define it?')
+ raise ImportError("\n\nMISSING FUNCTION --> In your 'lasagna.py' file, we can not find or import the"
+ f' function named {item_name}. \nDid you mis-name or forget to define it?') from None
+# Here begins the formal test cases for the exercise.
class LasagnaTest(unittest.TestCase):
@pytest.mark.task(taskno=1)
@@ -64,9 +67,16 @@ def test_elapsed_time_in_minutes(self):
@pytest.mark.task(taskno=5)
def test_docstrings_were_written(self):
+ """Validate function.__doc__ exists for each function.
+ Check the attribute dictionary of each listed function
+ for the presence of a __doc__ key.
+
+ :return: unexpectedly None error when __doc__ key is missing.
+ """
functions = [bake_time_remaining, preparation_time_in_minutes, elapsed_time_in_minutes]
for variant, function in enumerate(functions, start=1):
with self.subTest(f'variation #{variant}', function=function):
failure_msg = f'Expected a docstring for `{function.__name__}`, but received `None` instead.'
+ # Check that the __doc__ key is populated for the function.
self.assertIsNotNone(function.__doc__, msg=failure_msg)
From ba8dbecf2acdb42cc8697443a1ce3bf499bc2a4d Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Wed, 22 Feb 2023 18:41:49 -0800
Subject: [PATCH 346/826] Apply suggestions from code review
Co-authored-by: Victor Goff
---
exercises/concept/guidos-gorgeous-lasagna/lasagna_test.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/exercises/concept/guidos-gorgeous-lasagna/lasagna_test.py b/exercises/concept/guidos-gorgeous-lasagna/lasagna_test.py
index aeacc53b07..d200cb926a 100644
--- a/exercises/concept/guidos-gorgeous-lasagna/lasagna_test.py
+++ b/exercises/concept/guidos-gorgeous-lasagna/lasagna_test.py
@@ -18,7 +18,7 @@
if 'EXPECTED_BAKE_TIME' in item_name:
# pylint: disable=raise-missing-from
raise ImportError(f'\n\nMISSING CONSTANT --> \nWe can not find or import the constant {item_name} in your'
- " 'lasagna.py' file.\nDid you mis-name or forget to define it?") from None
+ " 'lasagna.py' file.\nDid you misname or forget to define it?") from None
else:
item_name = item_name[:-1] + "()'"
# pylint: disable=raise-missing-from
From 27fa70430bc15ae8966b3d6cc48a7c4eee65e771 Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Wed, 22 Feb 2023 18:42:16 -0800
Subject: [PATCH 347/826] Apply suggestions from code review
Co-authored-by: Victor Goff
---
exercises/concept/guidos-gorgeous-lasagna/lasagna_test.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/exercises/concept/guidos-gorgeous-lasagna/lasagna_test.py b/exercises/concept/guidos-gorgeous-lasagna/lasagna_test.py
index d200cb926a..7d0a7d9f1b 100644
--- a/exercises/concept/guidos-gorgeous-lasagna/lasagna_test.py
+++ b/exercises/concept/guidos-gorgeous-lasagna/lasagna_test.py
@@ -23,7 +23,7 @@
item_name = item_name[:-1] + "()'"
# pylint: disable=raise-missing-from
raise ImportError("\n\nMISSING FUNCTION --> In your 'lasagna.py' file, we can not find or import the"
- f' function named {item_name}. \nDid you mis-name or forget to define it?') from None
+ f' function named {item_name}. \nDid you misname or forget to define it?') from None
# Here begins the formal test cases for the exercise.
From 2f73fb34ce49d2e1814c134fb41ec395c9ca001d Mon Sep 17 00:00:00 2001
From: BethanyG
Date: Sat, 18 Feb 2023 01:05:28 -0800
Subject: [PATCH 348/826] Additional edits and adjustments to introduction and
related files for clarity.
---
.../guidos-gorgeous-lasagna/.docs/hints.md | 15 +-
.../.docs/instructions.md | 2 +-
.../.docs/introduction.md | 189 +++++++++++-------
.../guidos-gorgeous-lasagna/lasagna.py | 46 ++++-
4 files changed, 165 insertions(+), 87 deletions(-)
diff --git a/exercises/concept/guidos-gorgeous-lasagna/.docs/hints.md b/exercises/concept/guidos-gorgeous-lasagna/.docs/hints.md
index 41e3c669f0..351daef986 100644
--- a/exercises/concept/guidos-gorgeous-lasagna/.docs/hints.md
+++ b/exercises/concept/guidos-gorgeous-lasagna/.docs/hints.md
@@ -4,6 +4,7 @@
- [The Python Tutorial][the python tutorial] can be a great introduction.
- [Numbers][numbers] in Python can be integers, floats, or complex.
+- [PEP 8][PEP8] is the Python code style guide.
## 1. Define expected bake time in minutes
@@ -33,13 +34,13 @@
- Clearly [commenting][comments] and [documenting][docstrings] your code according to [PEP257][PEP257] is always recommended.
-[the python tutorial]: https://docs.python.org/3/tutorial/introduction.html
-[numbers]: https://docs.python.org/3/tutorial/introduction.html#numbers
-[naming]: https://realpython.com/python-variables/
+[PEP257]: https://www.python.org/dev/peps/pep-0257/
[assignment]: https://docs.python.org/3/reference/simple_stmts.html#grammar-token-assignment-stmt
-[defining functions]: https://docs.python.org/3/tutorial/controlflow.html#defining-functions
-[return]: https://docs.python.org/3/reference/simple_stmts.html#return
-[python as a calculator]: https://docs.python.org/3/tutorial/introduction.html#using-python-as-a-calculator
[comments]: https://realpython.com/python-comments-guide/
+[defining functions]: https://docs.python.org/3/tutorial/controlflow.html#defining-functions
[docstrings]: https://docs.python.org/3/tutorial/controlflow.html#tut-docstrings
-[PEP257]: https://www.python.org/dev/peps/pep-0257/
\ No newline at end of file
+[naming]: https://realpython.com/python-variables/
+[numbers]: https://docs.python.org/3/tutorial/introduction.html#numbers
+[python as a calculator]: https://docs.python.org/3/tutorial/introduction.html#using-python-as-a-calculator
+[return]: https://docs.python.org/3/reference/simple_stmts.html#return
+[the python tutorial]: https://docs.python.org/3/tutorial/introduction.html
diff --git a/exercises/concept/guidos-gorgeous-lasagna/.docs/instructions.md b/exercises/concept/guidos-gorgeous-lasagna/.docs/instructions.md
index 321e77761b..f4b9480e50 100644
--- a/exercises/concept/guidos-gorgeous-lasagna/.docs/instructions.md
+++ b/exercises/concept/guidos-gorgeous-lasagna/.docs/instructions.md
@@ -49,7 +49,7 @@ This function should return the total number of minutes you've been cooking, or
## 5. Update the recipe with notes
-Go back through the recipe, adding notes and documentation.
+Go back through the recipe, adding notes in the form of function docstrings.
```python
def elapsed_time_in_minutes(number_of_layers, elapsed_bake_time):
diff --git a/exercises/concept/guidos-gorgeous-lasagna/.docs/introduction.md b/exercises/concept/guidos-gorgeous-lasagna/.docs/introduction.md
index 1703d43082..e9d5b96db6 100644
--- a/exercises/concept/guidos-gorgeous-lasagna/.docs/introduction.md
+++ b/exercises/concept/guidos-gorgeous-lasagna/.docs/introduction.md
@@ -1,29 +1,59 @@
# Introduction
-[Python][python docs] is a [dynamic and strongly][dynamic typing in python] typed [object-oriented][object oriented programming] programming language.
+Python is a [dynamic and strongly][dynamic typing in python] typed [object-oriented][object oriented programming] programming language.
It employs both [duck typing][duck typing] and [gradual typing][gradual typing], via [type hints][type hints].
-Programming across paradigms is fully _supported_ -- but internally, [everything in Python is an object][everythings an object].
+Python supports Imperative, declarative (e.g. functional), and object oriented programming _styles_, but internally [everything in Python is an object][everythings an object].
-Python puts a strong emphasis on code readability and (_similar to Haskell_) uses [significant indentation][significant indentation] for function, method, and class definitions.
-[The Zen of Python (PEP 20)][the zen of python] and [_What is Pythonic?_][what is pythonic] lay out additional philosophies.
+This exercise introduces 4 major Python language features: Names (_variables and constants_), Functions (_and the return keyword_), Comments, and Docstrings.
-Objects are [assigned][assignment statements] to [names][naming and binding] via the _assignment operator_, `=`.
-[Variables][variables] are written in [`snake_case`][snake case], and _constants_ usually in `SCREAMING_SNAKE_CASE`.
-A `name` (_variable or constant_) is not itself typed, and can be attached or re-attached to different objects over its lifetime.
-For extended naming conventions and advice, see [PEP 8][pep8].
+~~~~exercism/note
+
+In general, content, tests, and analyzer tooling for the Python track follow the style conventions outlined in [PEP 8](https://www.python.org/dev/peps/pep-0008/) and [PEP 257](https://www.python.org/dev/peps/pep-0257/) for Python code style, with the additional (strong) suggestion that there be no single letter variable names.
+
+~~~~
+
+
+## Name Assignment and Re-assignment
+
+
+There are no keywords in Python to define variables or constants and there is no difference in the way Python treats them.
+Both are considered [_names_][facts-and-myths-about-python-names] that help programmers reference values (_objects_) in a program and are written differently only by convention.
+On Exercism, [variables][variables] are always written in [`snake_case`][snake case], and _constants_ in `SCREAMING_SNAKE_CASE`.
+
+Names are assigned to values via `=`, or the [_assignment operator_][assignment statements]: ` = `.
+A name (_variable or constant_) can be assigned or re-assigned over its lifetime to different values/different object types.
+For example, `my_first_variable` can be assigned and re-assigned many times using `=`, and can refer to different object types with each re-assignment:
```python
+# Assigning my_first_variable to a numeric value.
>>> my_first_variable = 1
->>> my_first_variable = "Last one, I promise"
+>>> print(type(my_first_variable))
+...
+
+
>>> print(my_first_variable)
...
-"Last one, I promise"
+1
+
+# Reassigning my_first_variable to a new string value.
+>>> my_first_variable = "Now, I'm a string."
+>>> print(type(my_first_variable))
+...
+
+
+>>> print(my_first_variable)
+...
+"Now, I'm a string."
```
-Constants are typically defined on a [module][module] or _global_ level, and although they _can_ be changed, they are _intended_ to be named only once.
-Their `SCREAMING_SNAKE_CASE` is a message to other developers that the assignment should not be altered:
+### Constants
+
+Constants are typically defined at a [module][module] level, being values that are accessible outside function or class scope.
+Constant names **_can be reassigned to new values_**, but they are _intended_ to be named only once.
+Using `SCREAMING_SNAKE_CASE` warns other programmers that these names should not be mutated or reassigned.
+
```python
# All caps signal that this is intended as a constant.
@@ -31,23 +61,43 @@ MY_FIRST_CONSTANT = 16
# Re-assignment will be allowed by the compiler & interpreter,
# but this is VERY strongly discouraged.
-# Please don't do: MY_FIRST_CONSTANT = "Some other value"
+# Please don't: MY_FIRST_CONSTANT = "Some other value"
```
+
+## Functions
+
The keyword `def` begins a [function definition][function definition].
It must be followed by the function name and a parenthesized list of zero or more formal [parameters][parameters].
Parameters can be of several different varieties, and can even [vary][more on functions] in length.
The `def` line is terminated with a colon.
+```python
+# function definition
+def my_function_name(parameter, second_parameter):
+
+
+```
+
Statements for the _body_ of the function begin on the line following `def` and must be _indented in a block_.
There is no strict indentation amount (_either space **OR** [tab] characters are acceptable_), but [indentation][indentation] must be _consistent for all indented statements_.
+
+```python
+# Function definition on first line.
+def add_two_numbers(number_one, number_two):
+ print(number_one + number_two) # Prints the sum of the numbers, and is indented by 2 spaces.
+
+>>> add_two_numbers(3, 4)
+7
+```
+
Functions explicitly return a value or object via the [`return`][return] keyword.
```python
# Function definition on first line.
def add_two_numbers(number_one, number_two):
- return number_one + number_two # Returns the sum of the numbers, and is indented by 2 spaces.
+ return number_one + number_two # Returns the sum of the numbers.
>>> add_two_numbers(3, 4)
7
@@ -64,33 +114,28 @@ def add_two_numbers(number_one, number_two):
None
```
-Inconsistent indentation will raise an error:
+While you may choose any indentation depth, _inconsistent_ indentation in your code blocks will raise an error:
```python
# The return statement line does not match the first line indent.
>>> def add_three_numbers_misformatted(number_one, number_two, number_three):
... result = number_one + number_two + number_three # Indented by 4 spaces.
... return result #this was only indented by 3 spaces
+...
+...
File "", line 3
return result
^
IndentationError: unindent does not match any outer indentation level
```
-Functions are [_called_][calls] using their name followed by `()`.
-The number of arguments passed in the parentheses must match the number of parameters in the original function definition unless [default arguments][default arguments] have been used:
+### Calling Functions
+
+Functions are [_called_][calls] or invoked using their name followed by `()`.
+The number of arguments passed in the parentheses must match the number of parameters in the original function definition..
```python
->>> def number_to_the_power_of(number_one, number_two):
- """Raise a number to an arbitrary power.
-
- :param number_one: int the base number.
- :param number_two: int the power to raise the base number to.
- :return: int - number raised to power of second number
-
- Takes number_one and raises it to the power of number_two, returning the result.
- """
-
+>>> def number_to_the_power_of(number_one, number_two):
return number_one ** number_two
...
@@ -98,7 +143,7 @@ The number of arguments passed in the parentheses must match the number of param
27
```
-A mis-match between parameters and arguments will raise an error:
+A mis-match between the number of parameters and the number of arguments will raise an error:
```python
>>> number_to_the_power_of(4,)
@@ -109,48 +154,34 @@ TypeError: number_to_the_power_of() missing 1 required positional argument: 'num
```
-Adding a [default value][default arguments] for a parameter can defend against such errors:
+Calling functions defined inside a class (_class methods_) use `.(