Repository: exercism/python
Pull request #3423 (2023-05-18 16:34:44)

Synced `instructions.md` & `introduction.md` Files from Problem Specifications
Synced the `introduction.md` and `instruction.md` files for the following exercises:

- [x] accumulate
- [x] binary
- [x] binary-search
- [x] book-store
- [x] bowling
- [x] darts
- [x] error-handling
- [x] gigasecond
- [x] hexadecimal
- [x] killer-sudoku-helper
- [x] linked-list
- [x] nucleotide-count
- [x] octal
- [x] pangram
- [x] parallel-letter-frequency
- [x] queen-attack
- [x] roman-numerals
- [x] saddle-points
- [x] secret-handshake
- [x] sieve
- [x] strain
- [x] sum-of-multiples
- [x] trinary
- [x] two-fer

ErikSchierboom: I'm holding off on approving as this will have the same issue mentioned in https://github.com/exercism/python/pull/3373#issuecomment-1542318581
BethanyG: It will?!!?  AFAK, none of these include re-generated test files -- these are all problem descriptions and  introductions which shouldn't trigger any re-submissions.
BethanyG: I'll put a hold label on this, though.  😄  

Patch:
diff --git a/concepts/binary-octal-hexadecimal/.meta/config.json b/concepts/binary-octal-hexadecimal/.meta/config.json
new file mode 100644
index 00000000..6e2a15b6
--- /dev/null
+++ b/concepts/binary-octal-hexadecimal/.meta/config.json
@@ -0,0 +1,4 @@
+{
+  "blurb": "Other numerical systems in Python: binary (0b11), octal (0o71), and hex (0xFF)",
+  "authors": ["BethanyG", "meatball133"]
+}
diff --git a/concepts/binary-octal-hexadecimal/about.md b/concepts/binary-octal-hexadecimal/about.md
new file mode 100644
index 00000000..667f3eb6
--- /dev/null
+++ b/concepts/binary-octal-hexadecimal/about.md
@@ -0,0 +1,224 @@
+# Binary, Octal, and Hexadecimal
+
+Binary, octal, and hexadecimal (_also known as hex_) are different [numeral systems][numeral-systems] with different bases.
+Binary is base 2, octal is base 8, and hexadecimal is base 16.
+Normal integers are base 10 in python.
+Binary, octal, and hexadecimal are all representations of integers.
+Which means that they represent positive and negative numbers (_including zero_) without fractions or decimals, and support all the operations that we can do with integers.
+
+## Binary
+
+[Binary][binary] is a base 2 numeral system, using only the digits 0 and 1.
+It commonly represents the 0 ("off") and 1 ("on") states of electrical flow through transistors and switches in computers, as well as the positive and negative charges in magnetic storage media.
+Binary can represent all the integers that are used in base 10.
+
+A snippet from the base 2 system looks like this, although it continues infinitely and doesn't stop at 128:
+
+| 128      | 64       | 32       | 16       | 8        | 4        | 2        | 1        |
+| -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- |
+| 2 \*\* 7 | 2 \*\* 6 | 2 \*\* 5 | 2 \*\* 4 | 2 \*\* 3 | 2 \*\* 2 | 2 \*\* 1 | 2 \*\* 0 |
+
+So if we want to represent the number 6, it would in binary be: 110
+
+| Place value   | 4   | 2   | 1   |
+| ------------- | --- | --- | --- |
+| Binary number | 1   | 1   | 0   |
+
+And the operation would be: `4 + 2 + 0 = 6`
+
+Another example: 19
+
+| Place value   | 16  | 8   | 4   | 2   | 1   |
+| ------------- | --- | --- | --- | --- | --- |
+| Binary number | 1   | 0   | 0   | 1   | 1   |
+
+The binary number would be: 10011
+And the operation would be: `16 + 0 + 0 + 2 + 1 = 19`
+
+## Binary in Python
+
+In Python, we can represent binary literals using the `0b` prefix.
+If we write `0b10011`, Python will interpret it as a binary number and convert it to base 10.
+
+```python
+# 0b10011
+>>> 0b10011
+19
+
+>>> type(0b10011)
+<class 'int'>
+```
+
+Binary in Python is just a different way of writing an integer and so the binary representation **is an integer** for all mathematical operations.
+
+If you write a number with a `0b` prefix that is not in the binary system, it will raise a `SyntaxError`.
+
+```python
+Traceback (most recent call last):
+  File "c:\binary.py", line 1, in <module>
+    0b10211
+SyntaxError: invalid digit '2' in binary literal
+```
+
+### Operations with Binary Numbers
+
+Since binary numbers are integers, we can perform all operations on them that we can with integers.
+
+```python
+# addition
+>>> 0b10011 + 0b10011
+38
+
+# multiplication
+>>> 0b10011 * 0b10011
+361
+```
+
+We can also perform operations between both binary and integer representations.
+However, the usual mathematical operator rules apply:  dividing two binary numbers or integer numbers will return a `float`, even if the division does not result in a decimal portion.
+
+```python
+>>> 0b10011 + 19
+38
+
+>>> 0b10011/0b10011
+1.0
+
+>>> 0b10011/3
+6.333333333333333
+
+### Converting to and from Binary Representation
+
+Python will automatically convert a binary literal into `int`.
+ To convert an `int` into a binary representation, use the built-in [`bin()`][bin] function.
+`bin()` will return a `str` of the binary equivalent with the prefix `0b` .
+
+```python
+>>> bin(19)
+'0b10011'
+```
+
+To convert a binary literal to an integer, we can use the built-in `int()` function, and pass a string of the binary representation and a base argument:
+
+```python
+>>> int("0b10011", 2)
+19
+```
+
+Giving the wrong base (_or an invalid binary representation_) will raise a `ValueError`:
+
+```python
+Traceback (most recent call last):
+  File "c:\binary.py", line 4, in <module>
+    int("0b10011", 3)
+ValueError: invalid literal for int() with base 3: '0b10011'
+```
+
+### Binary Methods
+
+There are also some special [methods][numeral-systems] that we can use on binary numbers.
+
+
+[`<int>.bit_length()`][bit_length] will return the number of bits that are needed to represent the number:
+
+```python
+>>> 0b11011.bit_length()
+5
+```
+
+
+[`<int>.bit_count()`][bit_count] will return the number of **ones** in the binary number.
+For example, `bit_count()` on '0b11011' will return 4:
+
+```python
+>>> 0b11011.bit_count()
+4
+~~~~exercism/note
+If you are working locally, `bit_count()` requires at least Python 3.10.
+The Exercism online editor currently supports all features through Python 3.11.
+~~~~ 
+
+
+## Octal
+
+[Octal][octal] is a base 8 numeral system.
+It uses the digits 0, 1, 2, 3, 4, 5, 6, and 7.
+
+In Python, we can represent octal numbers using the `0o` prefix.
+As with binary, Python automatically converts an octal representation to an `int`.
+
+```python
+# 0o123
+>>> 0o123
+83
+```
+
+As with binary, octal numbers **are ints** and support all integer operations.
+Prefixing a number with `0o` that is not in the octal system will raise a `SyntaxError`.
+
+ ### Converting to and from Octal Representation
+ 
+
+To convert an `int` into an octal representation, you can use the built-in [`oct()`][oct] function.
+This acts similarly to the `bin()` function, returning a string:
+
+```python
+>>> oct(83)
+'0o123'
+
+To convert an octal number to an integer, we can use the `int()` function, passing an octal string representation and the base (8) as arguments:
+
+```python
+>>> int("0o123", 8)
+83
+```
+
+As with binary, giving the wrong base will raise a `ValueError`.
+
+### Hexadecimal
+
+[Hexadecimal][hexadecimal] is a base 16 numeral system.
+It uses the digits 0 - 9 and the letters A, B, C, D, E, and F.
+A is 10, B is 11, C is 12, D is 13, E is 14, and F is 15.
+
+We can represent hexadecimal numbers in Python using the `0x` prefix.
+As with binary and octal, Python will automatically convert hexadecimal literals to `int`.
+
+```python
+# 0x123
+>>> 0x123
+291
+```
+
+As with binary and octal - hexidecimal literals **are ints**, and you can perform all integer operations.  
+Prefixing a non-hexidecimal number with `0x` will raise a `SyntaxError`.
+
+
+###  Converting to and from Hexadecimal Representation
+
+To convert an `int` into a hexadecimal representation, you can use the built-in [`hex()`][hex] function.
+This acts similarly to the `bin()` function, returning a string:
+
+```python
+>>> hex(291)
+'0x123'
+
+To convert a hexadecimal representation to an integer, we can use the `int()` function, passing a hexadecimal string with the base (16) as arguments:
+
+```python
+>>> int("0x123", 16)
+291
+```
+
+As with binary and octal, giving the wrong base will raise a `ValueError`.
+
+
+[binary]: https://en.wikipedia.org/wiki/Binary_number
+[bit_count]:  https://docs.python.org/3/library/stdtypes.html#int.bit_count
+[bit_length]: https://docs.python.org/3/library/stdtypes.html#int.bit_length
+[bit_count]:  https://docs.python.org/3/library/stdtypes.html#int.bit_count
+[bit_length]: https://docs.python.org/3/library/stdtypes.html#int.bit_length
+[hexadecimal]: https://en.wikipedia.org/wiki/Hexadecimal
+[methods-int]: https://docs.python.org/3/library/stdtypes.html#additional-methods-on-integer-types
+[numeral-systems]: https://en.wikipedia.org/wiki/Numeral_system
+[octal]: https://en.wikipedia.org/wiki/Octal
diff --git a/concepts/binary-octal-hexadecimal/introduction.md b/concepts/binary-octal-hexadecimal/introduction.md
new file mode 100644
index 00000000..820aac33
--- /dev/null
+++ b/concepts/binary-octal-hexadecimal/introduction.md
@@ -0,0 +1,11 @@
+# binary, octal, hexadecimal
+
+Binary, octal, and hexadecimal (_also known as hex_) are different [numeral systems][numeral-systems] with different bases.
+Binary is base 2, octal is base 8, and hexadecimal is base 16.
+Normal integers are base 10 in python.
+Binary, octal, and hexadecimal literals are all considered `int` subtypes and Python automatically converts between them.
+This means that they can only represent zero, positive, and negative numbers that do not have a fractional or decimal part.
+Binary, octal, and hexidecimal numbers support all integer operations.
+However, division (_as with ints_) will return a `float`.
+
+[numeral-systems]: https://en.wikipedia.org/wiki/Numeral_system
diff --git a/concepts/binary-octal-hexadecimal/links.json b/concepts/binary-octal-hexadecimal/links.json
new file mode 100644
index 00000000..8826182c
--- /dev/null
+++ b/concepts/binary-octal-hexadecimal/links.json
@@ -0,0 +1,10 @@
+[
+  {
+    "url": "https://towardsdatascience.com/binary-hex-and-octal-in-python-20222488cee1",
+    "description": "Binary, octal, hex in python"
+  },
+  {
+    "url": "https://en.wikipedia.org/wiki/Numeral_system",
+    "description": "Numeral system"
+  }
+]
diff --git a/concepts/comparisons/about.md b/concepts/comparisons/about.md
index 568b6603..c2f5faaa 100644
--- a/concepts/comparisons/about.md
+++ b/concepts/comparisons/about.md
@@ -82,7 +82,7 @@ False
 Strings (`str`) are compared [_lexicographically_][lexographic order], using their individual Unicode code points (_the result of passing each code point in the `str` to the built-in function [`ord()`][ord], which returns an `int`_).
 If all code points in both strings match and are _**in the same order**_, the two strings are considered equal.
 This comparison is done in a 'pair-wise' fashion - first-to-first, second-to-second, etc.
-Unlike in Python 2.x, in Python 3.x, `str` and `bytes` cannot be directly coerced/compared.
+In Python 3.x, `str` and `bytes` cannot be directly coerced/compared.
 
 ```python
 >>> 'Python' > 'Rust'
diff --git a/concepts/dict-methods/about.md b/concepts/dict-methods/about.md
index e36fc708..d910d3e9 100644
--- a/concepts/dict-methods/about.md
+++ b/concepts/dict-methods/about.md
@@ -1,74 +1,75 @@
 # Dictionary Methods in Python
 
-A dictionary (`dict`) in Python is a data structure that associates [hashable][term-hashable] _keys_ to _values_ and is known in other programming languages as a [hash table or hashmap][hashtable-wikipedia].
-In Python, it's considered a [mapping type][mapping-types-dict].
-`dicts` enable the retrieval of a value in constant time (on average), given the key.
-
-Compared to searching for a value within a list or array (_without knowing the index position_), a dictionary uses significantly more memory, but has very rapid retrieval.
-It's especially useful in scenarios where the collection of items is large and must be accessed/updated frequently.
-
-## Dictionary Methods
-
 The `dict` class in Python provides many useful [methods][dict-methods] for working with dictionaries.
 Some were introduced in the concept for `dicts`.
-Here are a few more - along with some techniques for iterating through and manipulating `dicts`.
+Here we cover a few more - along with some techniques for iterating through and manipulating dictionaries.
 
-To quickly populate a dictionary with various `keys` and default values, the _class method_ [`dict.fromkeys(iterable, <default value>)`][fromkeys] will iterate through the `keys` and create a new `dict`.  All `values` will be set to the `default` value provided.
+- `dict.setdefault()` automatically adds keys without throwing a KeyError.
+- `dict.fromkeys(iterable, <default value>)` creates a new `dict` from any number of iterables.
+- `.keys()`, `.values()`, and `.items()` provide convenient iterators.
+- `sorted(<dict>.items())`. can easily re-order entries in a `dict`.
+- `dict_one.update(<dict_two>)` updates one `dict` with overlapping values from another `dict`.
+- `dict | other_dict` and `dict |= other_dict` merges or updates two `dict`s via operators.
+- `reversed(dict.keys())`, `reversed(dict.values())`, or `reversed(dict.items())` produce _reversed_ views.
+- `<dict>.popitem()` removes and returns a `key`, `value` pair.
 
-```python
->>> new_dict = dict.fromkeys(['Grassy Green', 'Purple Mountains Majesty', 'Misty Mountain Pink'], 'fill in hex color here')
->>> new_dict
-{'Grassy Green': 'fill in hex color here',
- 'Purple Mountains Majesty': 'fill in hex color here',
- 'Misty Mountain Pink': 'fill in hex color here'}
-```
 
-`dict.clear()` will removed all `key:value` pairs from the dictionary, leaving it empty and ready for new entries.
+## `setdefault()` for Error-Free Insertion
 
-```python
->>> pallette_II = {'Factory Stone Purple': '#7c677f', 'Green Treeline': '#478559', 'Purple baseline': '#161748'}
->>> pallette_II.clear()
->>> pallette_II
-{}
-```
+The dictionary concept previously covered that `.get(key, <default value>)` returns an existing `value` or the `default value` if a `key` is not found in a dictionary, thereby avoiding a `KeyError`.
+This works well in situations where you would rather not have extra error handling but cannot trust that a looked-for `key` will be present.
 
-`dict.get(key, <default return value>)` works similarly to `dict[key]` -- but it will return the `default` if the `key` is not in the dictionary.
-If no `default` is given, the method will return `None`.
+For a similarly "safe" (_without KeyError_) insertion operation, there is the `.setdefault(key, <default value>)` method.
+`setdefault(key, <default value>)` will return the `value` if the `key` is found in the dictionary.
+If the key is **not** found, it will _insert_ the (`key`, `default value`) pair and return the `default value` for use.
 
 ```python
 >>> palette_I = {'Grassy Green': '#9bc400', 'Purple Mountains Majesty': '#8076a3', 'Misty Mountain Pink': '#f9c5bd'}
->>> palette_I['Factory Stone Purple']
-Traceback (most recent call last):
 
-  line 1, in <module>
-    palette_I['Factory Stone Purple']
+# Looking for the value associated with key "Rock Brown".The key does not exist, 
+# so it is added with the default value, and the value is returned.
+>>> palette.setdefault('Rock Brown', '#694605')
+'#694605'
 
-KeyError: 'Factory Stone Purple'
+# The (key, default value) pair has now been added to the dictionary.
+>>> palette_I
+{'Grassy Green': '#9bc400', 'Purple Mountains Majesty': '#8076a3', 'Misty Mountain Pink': '#f9c5bd', 'Rock Brown': '#694605'}
+```
 
->>> palette_I.get('Factory Stone Purple', 'That color was not found.')
-'That color was not found.'
+## `fromkeys()` to Populate a Dictionary from an Iterable
 
->>> palette_I.get('Factory Stone Purple', False)
-False
+To quickly populate a dictionary with various `keys` and default values, the _class method_ [`fromkeys(iterable, <default value>)`][fromkeys] will iterate through an iterable of `keys` and create a new `dict`.
+All `values` will be set to the `default value` provided:
 
->>> palette_I.get('Factory Stone Purple')
-None
+```python
+>>> new_dict = dict.fromkeys(['Grassy Green', 'Purple Mountains Majesty', 'Misty Mountain Pink'], 'fill in hex color here')
+
+{'Grassy Green': 'fill in hex color here',
+ 'Purple Mountains Majesty': 'fill in hex color here',
+ 'Misty Mountain Pink': 'fill in hex color here'}
 ```
 
-`dict.popitem()`  removes & returns a single `key:value` pair from the `dict`.
-Pairs are returned in Last-in-First-out (LIFO) order.
-If the dictionary is empty, calling `.dict.popitem` will raise a `KeyError`.
+## Remove and Return a (key, value) Pair With `.popitem()`
+
+`.popitem()` removes & returns a single (`key`, `value`) pair from a dictionary.
+Pairs are returned in Last-in-First-out (`LIFO`) order.
+If the dictionary is empty, calling `popitem()` will raise a `KeyError`:
 
 ```python
->>> palette_I = {'Grassy Green': '#9bc400', 'Purple Mountains Majesty': '#8076a3', 'Misty Mountain Pink': '#f9c5bd'}
+>>> palette_I = {'Grassy Green': '#9bc400', 
+                 'Purple Mountains Majesty': '#8076a3', 
+                 'Misty Mountain Pink': '#f9c5bd'}
 >>> palette_I.popitem()
 ('Misty Mountain Pink', '#f9c5bd')
+
 >>> palette_I.popitem()
 ('Purple Mountains Majesty', '#8076a3')
+
 >>> palette_I.popitem()
 ('Grassy Green', '#9bc400')
->>> palette_I.popitem()
 
+# All (key, value) pairs have been removed.
+>>> palette_I.popitem()
 Traceback (most recent call last):
 
   line 1, in <module>
@@ -77,25 +78,38 @@ Traceback (most recent call last):
 KeyError: 'popitem(): dictionary is empty'
 ```
 
-While `dict.clear()` and `dict.popitem()` are  _destructive_ actions, the  `.keys()`, `.values()`, and `.items()` methods return [_iterable views_][dict-views].
-These views can be used for looping over `dict` content without altering it and are  _dynamic_ -- when underlying dictionary data changes, the associated view object will reflect the change.
+## Iterating Over Entries in a Dictionary Via Views
+
+The `.keys()`, `.values()`, and `.items()` methods return [_iterable views_][dict-views] of a dictionary.
+
+These views can be used to easily loop over entries without altering them.
+Views are also _dynamic_ -- when underlying dictionary data changes, the associated `view object` will reflect the change:
 
 ```python
->>> palette_I = {'Grassy Green': '#9bc400', 'Purple Mountains Majesty': '#8076a3', 'Misty Mountain Pink': '#f9c5bd'}
+>>> palette_I = {'Grassy Green': '#9bc400', 
+                 'Purple Mountains Majesty': '#8076a3', 
+                 'Misty Mountain Pink': '#f9c5bd'}
+
+# Using .keys() returns a list of keys.
 >>> palette_I.keys()
 dict_keys(['Grassy Green', 'Purple Mountains Majesty', 'Misty Mountain Pink'])
 
+# Using .values() returns a list of values.
 >>> palette_I.values()
 dict_values(['#9bc400', '#8076a3', '#f9c5bd'])
 
+# Using .items() returns a list of (key, value) tuples.
 >>> palette_I.items()
 dict_items([('Grassy Green', '#9bc400'), ('Purple Mountains Majesty', '#8076a3'), ('Misty Mountain Pink', '#f9c5bd')])
 
+# Views are dynamic.  Changing values in the dict 
+# changes all of the associated views.
 >>> palette_I['Purple Mountains Majesty'] = (128, 118, 163)
+>>> palette_I['Deep Red'] = '#932432'
+
 >>> palette_I.values()
-dict_values(['#9bc400', (128, 118, 163), '#f9c5bd'])
+dict_values(['#9bc400', (128, 118, 163), '#f9c5bd', '#932432'])
 
->>> palette_I['Deep Red'] = '#932432'
 >>> palette_I.keys()
 dict_keys(['Grassy Green', 'Purple Mountains Majesty', 'Misty Mountain Pink', 'Deep Red'])
 
@@ -103,32 +117,69 @@ dict_keys(['Grassy Green', 'Purple Mountains Majesty', 'Misty Mountain Pink', 'D
 dict_items([('Grassy Green', '#9bc400'), ('Purple Mountains Majesty', (128, 118, 163)), ('Misty Mountain Pink', '#f9c5bd'), ('Deep Red', '#932432')])
 ```
 
-`dict_one.update(<dict_two>)` can be used to _combine_ two dictionaries.
-This method will take the `key:value` pairs of `dict_two` and write them into `dict_one`.
+## More on `.keys()`, `.values()`, and `.items()`
+
+In Python 3.7+, `dicts` preserve the order in which entries are inserted allowing First-in, First-out (_`FIFO`_),  iteration when using `.keys()`, `.values()`, or `.items()`.
+
+In Python 3.8+, views are also _reversible_.
+This allows keys, values, or (`key`, `value`) pairs to be iterated over in Last-in, First-out (`LIFO`) order by using `reversed(<dict>.keys())`, `reversed(<dict>.values())`, or `reversed(<dict>.items())`:
 
 ```python
->>> palette_I = {'Grassy Green': '#9bc400', 'Purple Mountains Majesty': '#8076a3', 'Misty Mountain Pink': '#f9c5bd'}
->>> palette_II = {'Factory Stone Purple': '#7c677f', 'Green Treeline': '#478559', 'Purple baseline': '#161748'}
->>> palette_I.update(palette_II)
->>> palette_I
+>>> palette_II = {'Factory Stone Purple': '#7c677f', 
+                  'Green Treeline': '#478559', 
+                  'Purple baseline': '#161748'}
+>>> for item in palette_II.items():
+...     print(item)
 ...
+('Factory Stone Purple', '#7c677f')
+('Green Treeline', '#478559')
+('Purple baseline', '#161748')
 
-{'Grassy Green': '#9bc400',
- 'Purple Mountains Majesty': '#8076a3',
- 'Misty Mountain Pink': '#f9c5bd',
- 'Factory Stone Purple': '#7c677f',
- 'Green Treeline': '#478559', 
- 'Purple baseline': '#161748'}
+>>> for item in reversed(palette_II.items()):
+...    print (item)
+...
+('Purple baseline', '#161748')
+('Green Treeline', '#478559')
+('Factory Stone Purple', '#7c677f')
 ```
 
-Where keys in the two dictionaries _overlap_, the `value` in `dict_one` will be _overwritten_ by the corresponding `value` from `dict_two`.
+## Combine Dictionaries with `.update()`
+
+`<dict_one>.update(<dict_two>)` can be used to _combine_ two dictionaries.
+This method will take the (`key`,`value`) pairs of `<dict_two>` and write them into `<dict_one>`:
 
 ```python
->>> palette_I =   {'Grassy Green': '#9bc400', 'Purple Mountains Majesty': '#8076a3', 'Misty Mountain Pink': '#f9c5bd', 
-                   'Factory Stone Purple': '#7c677f', 'Green Treeline': '#478559', 'Purple baseline': '#161748'}
->>> palette_III = {'Grassy Green': (155, 196, 0), 'Purple Mountains Majesty': (128, 118, 163),
+>>> palette_I = {'Grassy Green': '#9bc400',
+                 'Purple Mountains Majesty': '#8076a3',
+                  'Misty Mountain Pink': '#f9c5bd'}
+>>> palette_II = {'Factory Stone Purple': '#7c677f',
+                  'Green Treeline': '#478559',
+                  'Purple Baseline': '#161748'}
+
+>>> palette_I.update(palette_II)
+
+# Note that new items from palette_II are added.
+>>> palette_I
+{'Grassy Green': '#9bc400', 'Purple Mountains Majesty': '#8076a3', 'Misty Mountain Pink': '#f9c5bd', 'Factory Stone Purple': '#7c677f', 'Green Treeline': '#478559', 'Purple Baseline': '#161748'}
+```
+
+Where keys in the two dictionaries _overlap_, the `value` in `dict_one` will be _overwritten_ by the corresponding `value` from `dict_two`:
+
+```python
+>>> palette_I =   {'Grassy Green': '#9bc400', 
+                   'Purple Mountains Majesty': '#8076a3', 
+                   'Misty Mountain Pink': '#f9c5bd', 
+                   'Factory Stone Purple': '#7c677f', 
+                   'Green Treeline': '#478559', 
+                   'Purple baseline': '#161748'}
+                   
+>>> palette_III = {'Grassy Green': (155, 196, 0), 
+                   'Purple Mountains Majesty': (128, 118, 163),
                    'Misty Mountain Pink': (249, 197, 189)}
 >>> palette_I.update(palette_III)
+
+# Overlapping values in palette_I are replaced with 
+# values from palette_III
 >>> palette_I
 {'Grassy Green': (155, 196, 0),
   'Purple Mountains Majesty': (128, 118, 163), 
@@ -137,14 +188,21 @@ Where keys in the two dictionaries _overlap_, the `value` in `dict_one` will be
   'Green Treeline': '#478559', 'Purple baseline': '#161748'}
 ```
 
-Python 3.9 introduces a different means of merging `dicts`:  the `union` operators.
-`dict | other_dict` will create a **new** `dict`, made up of the `key:value` pairs of `dict` and `other_dict`.
-When both dictionaries share keys, the `other_dict` values will take precedence.
-`dict |= other` will behave similar to `dict.update()`, but in this case, `other` can be either a `dict` or an iterable of `key:value` pairs.
+## Merge or Update Dictionaries Via the Union (`|`) Operators
+
+Python 3.9 introduces a different means of merging `dicts`: the `union` operators.
+`dict_one | dict_two` will create a **new dictionary**, made up of the (`key`, `value`) pairs of `dict_one` and `dict_two`.
+When both dictionaries share keys, `dict_two` values take precedence.
 
 ```python
->>> palette_I = {'Grassy Green': '#9bc400', 'Purple Mountains Majesty': '#8076a3', 'Misty Mountain Pink': '#f9c5bd'}
->>> palette_II = {'Factory Stone Purple': '#7c677f', 'Green Treeline': '#478559', 'Purple baseline': '#161748'}
+>>> palette_I = {'Grassy Green': '#9bc400', 
+                 'Purple Mountains Majesty': '#8076a3', 
+                 'Misty Mountain Pink': '#f9c5bd'}
+
+>>> palette_II = {'Factory Stone Purple': '#7c677f', 
+                  'Green Treeline': '#478559', 
+                  'Purple baseline': '#161748'}
+
 >>> new_dict = palette_I | palette_II
 >>> new_dict
 ...
@@ -154,44 +212,34 @@ When both dictionaries share keys, the `other_dict` values will take precedence.
  'Factory Stone Purple': '#7c677f',
  'Green Treeline': '#478559',
  'Purple baseline': '#161748'}
- 
- >>> palette_III = {'Grassy Green': (155, 196, 0), 'Purple Mountains Majesty': (128, 118, 163), 'Misty Mountain Pink': (249, 197, 189)}
- >>> new_dict |= palette_III
- >>> new_dict
- ...
- {'Grassy Green': (155, 196, 0),
- 'Purple Mountains Majesty': (128, 118, 163),
- 'Misty Mountain Pink': (249, 197, 189),
- 'Factory Stone Purple': '#7c677f',
- 'Green Treeline': '#478559',
- 'Purple baseline': '#161748'}
 ```
 
-## Tips and Tricks
-
-As of Python 3.6, `dicts` preserve the order in which items are inserted, allowing ordered iteration using `.items()`.  As of Python 3.8, `dict` _views_ are reversible, allowing keys, values or items to be iterated over reverse of insertion order by using `reversed(dict.keys())`, `reversed(dict.values())`, or `reversed(dict.items())`.
+`dict_one |= other` behaves similar to `<dict_one>.update(<dict_two>)`, but in this case, `other` can be either a `dict` or an iterable of (`key`, `value`) pairs:
 
 ```python
->>> palette_II = {'Factory Stone Purple': '#7c677f', 'Green Treeline': '#478559', 'Purple baseline': '#161748'}
->>> for item in palette_II.items():
-...     print(item)
-...
-('Factory Stone Purple', '#7c677f')
-('Green Treeline', '#478559')
-('Purple baseline', '#161748')
-
->>> for item in reversed(palette_II.items()):
-...    print (item)
+>>> palette_III = {'Grassy Green': (155, 196, 0),
+                   'Purple Mountains Majesty': (128, 118, 163),
+                   'Misty Mountain Pink': (249, 197, 189)}
+>>> new_dict |= palette_III
+>>> new_dict
 ...
-('Purple baseline', '#161748')
-('Green Treeline', '#478559')
-('Factory Stone Purple', '#7c677f')
-
+{'Grassy Green': (155, 196, 0),
+'Purple Mountains Majesty': (128, 118, 163),
+'Misty Mountain Pink': (249, 197, 189),
+'Factory Stone Purple': '#7c677f',
+'Green Treeline': '#478559',
+'Purple baseline': '#161748'}
 ```
 
-While `dict` does not have a built-in sorting method, it is possible to sort a dictionary _view_ by keys or values using the built-in `sorted()` with `dict.items()`.  The sorted view can then be used to create a new, sorted dictionary.  Unless a _sort key_ is specified, the default sort is over dictionary keys.
+## Sorting a Dictionary
+
+Dictionaries do not have a built-in sorting method.
+However, it is possible to sort a `dict` _view_ using the built-in function `sorted()` with `.items()`.
+The sorted view can then be used to create a new dictionary.
+Unless a _sort key_ is specified, the default sort is over dictionary `keys`.
 
 ```python
+# Default ordering for a dictionary is last in, first out (LIFO).
 >>> color_palette = {'Grassy Green': '#9bc400', 
                     'Purple Mountains Majesty': '#8076a3', 
                     'Misty Mountain Pink': '#f9c5bd', 
@@ -200,6 +248,7 @@ While `dict` does not have a built-in sorting method, it is possible to sort a d
                     'Purple baseline': '#161748'}
  
  
+# The default sort order for a dictionary uses the keys.
 >>> sorted_palette = dict(sorted(color_palette.items()))
 >>> sorted_palette
 {'Factory Stone Purple': '#7c677f',
@@ -208,7 +257,10 @@ While `dict` does not have a built-in sorting method, it is possible to sort a d
  'Misty Mountain Pink': '#f9c5bd',
  'Purple Mountains Majesty': '#8076a3',
  'Purple baseline': '#161748'}
- 
+
+
+# A sort key can be provided in the form
+# of an anonymous function (lambda). 
 >>> value_sorted_palette = dict(sorted(color_palette.items(), key=lambda color: color[1]))
 >>> value_sorted_palette
 {'Purple baseline': '#161748',
@@ -217,110 +269,93 @@ While `dict` does not have a built-in sorting method, it is possible to sort a d
  'Purple Mountains Majesty': '#8076a3',
  'Grassy Green': '#9bc400',
  'Misty Mountain Pink': '#f9c5bd'} 
-
 ```
 
-Swapping keys and values reliably in a dictionary takes a little more work, but can be accomplished via a loop using `dict.items()`.  But if the values stored in the `dict` are not unique, extra checks are required.  Both methods assume that `dict` keys and values are _hashable_.
-
-```python
+## Transposing a Dictionaries Keys and Values
 
+Swapping keys and values reliably in a dictionary takes a little work, but can be accomplished via a `loop` using `dict.items()` or in a dictionary comprehension.
+Safe swapping assumes that `dict` keys and values are both _hashable_.
 
+```python
 color_reference = {'Purple Mountains Majesty': '#8076a3',
                    'Misty Mountain Pink': '#f9c5bd',
                    'Factory Stone Purple': '#7c677f',
                    'Green Treeline': '#478559',
-                   'Purple baseline': '#161748',
-                   'Pink highlight': '#f95d9b',
-                   'Bluewater lowlight': '#39a0ca',
-                   'Bright Red': '#DE354C',
-                   'Deep Red': '#932432',
-                   'Pure Purple': '#3C1874',
-                   'Purple Tinged Grey': '#283747',
-                   'Cloud': '#F3F3F3'}
-
->>> reversed_color_reference = {}
+                   'Purple baseline': '#161748'}
+
+# Creating a new dictionary to hold the swapped entries.
+>>> swapped_color_reference = {}
+
+# Iterating through the dictionary, using values as keys.
 >>> for key, value in color_reference.items():
-...     reversed_color_reference[value] = key
+...     swapped_color_reference[value] = key
+
+>>> swapped_color_reference
+{'#8076a3': 'Purple Mountains Majesty',
+ '#f9c5bd': 'Misty Mountain Pink',
+ '#7c677f': 'Factory Stone Purple',
+ '#478559': 'Green Treeline',
+ '#161748': 'Purple baseline'}
 
->>> reversed_color_reference
+ 
+# A dictionary comprehension can also be used to swap entries.
+>>> swapped = {value: key for key, value in
+               color_reference.items()}
+>>> swapped
 {'#8076a3': 'Purple Mountains Majesty',
  '#f9c5bd': 'Misty Mountain Pink',
  '#7c677f': 'Factory Stone Purple',
  '#478559': 'Green Treeline',
- '#161748': 'Purple baseline',
- '#f95d9b': 'Pink highlight',
- '#39a0ca': 'Bluewater lowlight',
- '#DE354C': 'Bright Red',
- '#932432': 'Deep Red',
- '#3C1874': 'Pure Purple',
- '#283747': 'Purple Tinged Grey',
- '#F3F3F3': 'Cloud'}
-    
-
->>> extended_color_reference = {'#8076a3': 'Purple Mountains Majesty',(128, 118, 163): 'Purple Mountains Majesty',
-                                (21, 28, 0, 36): 'Purple Mountains Majesty','#f9c5bd': 'Misty Mountain Pink',
-                                (249, 197, 189): 'Misty Mountain Pink',(0, 21, 24, 2): 'Misty Mountain Pink',
-                                '#7c677f': 'Factory Stone Purple',(124, 103, 127): 'Factory Stone Purple',
-                                (2, 19, 0, 50): 'Factory Stone Purple','#478559': 'Green Treeline',
-                                (71, 133, 89): 'Green Treeline',(47, 0, 33, 48): 'Green Treeline',
-                                '#161748': 'Purple baseline',(22, 23, 72): 'Purple baseline',
-                                (69, 68, 0, 72): 'Purple baseline','#f95d9b': 'Pink highlight',
-                                (249, 93, 155): 'Pink highlight',(0, 63, 38, 2): 'Pink highlight',
-                                '#39a0ca': 'Bluewater lowlight',(57, 160, 202): 'Bluewater lowlight',
-                                (72, 21, 0, 21): 'Bluewater lowlight','#DE354C': 'Bright Red',
-                                (222, 53, 76): 'Bright Red',(0, 76, 66, 13): 'Bright Red',
-                                '#932432': 'Deep Red',(147, 36, 50): 'Deep Red',
-                                (0, 76, 66, 42): 'Deep Red','#3C1874': 'Pure Purple',
-                                (60, 24, 116): 'Pure Purple',(48, 79, 0, 55): 'Pure Purple',
-                                '#283747': 'Purple Tinged Grey',(40, 55, 71): 'Purple Tinged Grey',
-                                (44, 23, 0, 72): 'Purple Tinged Grey','#F3F3F3': 'Cloud',
-                                (243, 243, 243): 'Cloud',(0, 0, 0, 5): 'Cloud'}
+ '#161748': 'Purple baseline'}
+```
 
+If the values stored in the `dict` are not unique, extra checks become necessary before key and value swapping can happen:
+
+```python
+# Things become more complicated if there are duplicates in 
+# potential key values.This dict is arranged by hex, RGB, and HSL
+# keys, but values repeat.
+>>> extended_colors = {'#8076a3': 'Purple Mountains Majesty',
+                       (128, 118, 163): 'Purple Mountains Majesty',
+                       (21, 28, 0, 36): 'Purple Mountains Majesty',
+                       '#f9c5bd': 'Misty Mountain Pink',
+                       (249, 197, 189): 'Misty Mountain Pink',
+                       (0, 21, 24, 2): 'Misty Mountain Pink',
+                       '#7c677f': 'Factory Stone Purple',
+                       (124, 103, 127): 'Factory Stone Purple',
+                       (2, 19, 0, 50): 'Factory Stone Purple',
+                       '#478559': 'Green Treeline',
+                       (71, 133, 89): 'Green Treeline',
+                       (47, 0, 33, 48): 'Green Treeline'}
+                                
+# New empty dictionary for holding swapped entries.
 >>> consolidated_colors = {}
+
+# Iterating over (key, value) pairs using .items()
 >>> for key, value in extended_color_reference.items():
-...    if value in consolidated_colors:
+...    if value in consolidated_colors: #Check if key has already been created.
 ...        consolidated_colors[value].append(key)
 ...    else:
-...        consolidated_colors[value] = [key]
+...        consolidated_colors[value] = [key]  #Create a value list with the former key in it.
 
 >>> consolidated_colors
 {'Purple Mountains Majesty': ['#8076a3', (128, 118, 163), (21, 28, 0, 36)],
  'Misty Mountain Pink': ['#f9c5bd', (249, 197, 189), (0, 21, 24, 2)],
  'Factory Stone Purple': ['#7c677f', (124, 103, 127), (2, 19, 0, 50)],
- 'Green Treeline': ['#478559', (71, 133, 89), (47, 0, 33, 48)],
- 'Purple baseline': ['#161748', (22, 23, 72), (69, 68, 0, 72)],
- 'Pink highlight': ['#f95d9b', (249, 93, 155), (0, 63, 38, 2)],
- 'Bluewater lowlight': ['#39a0ca', (57, 160, 202), (72, 21, 0, 21)],
- 'Bright Red': ['#DE354C', (222, 53, 76), (0, 76, 66, 13)],
- 'Deep Red': ['#932432', (147, 36, 50), (0, 76, 66, 42)],
- 'Pure Purple': ['#3C1874', (60, 24, 116), (48, 79, 0, 55)],
- 'Purple Tinged Grey': ['#283747', (40, 55, 71), (44, 23, 0, 72)],
- 'Cloud': ['#F3F3F3', (243, 243, 243), (0, 0, 0, 5)]}
-        
+ 'Green Treeline': ['#478559', (71, 133, 89), (47, 0, 33, 48)]}  
 ```
 
 For a detailed explanation of dictionaries and methods for working with them, the [official tutorial][dicts-docs] and the [official library reference][mapping-types-dict] are excellent starting places.
-For more on sorting, see the [Sorting HOW TO][sorting-howto] in the python docs.
- [Real Python][how-to-dicts] and [Finxter][fi-dict-guide] also have very thorough articles on Python dictionaries.
 
-## Extending Dictionaries: The collections module
+[Real Python][how-to-dicts] and [Finxter][fi-dict-guide] also have very thorough articles on Python dictionaries.
 
-The [`collections`][collections-docs] module adds more functionality to Python's standard collection-based datatypes (`dictionary`, `set`, `list`, `tuple`).
-A popular `dict`-oriented member of this module is the [`Counter`][counter-dicts], which automatically counts items and returns them a `dict` with the items as keys and their counts as values.
-There is also the [`OrderedDict`][ordered-dicts-docs], which has methods specialized for re-arranging the order of a dictionary.
-Finally, there is the [`defaultdict`][default-dicts], a subclass of the built-in `dict` module that, based on a factory method, sets a default value if a key is not found when trying to retrieve or assign the value.
+For more on sorting, see the [Sorting HOW TO][sorting-howto] in the Python docs.
 
-[term-hashable]: https://docs.python.org/3/glossary.html#term-hashable
-[hashtable-wikipedia]: https://en.wikipedia.org/wiki/Hash_table
-[mapping-types-dict]: https://docs.python.org/3/library/stdtypes.html#mapping-types-dict
-[dicts-docs]: https://docs.python.org/3/tutorial/datastructures.html#dictionaries
-[how-to-dicts]: https://www.w3schools.com/python/python_dictionaries.asp
-[fromkeys]: https://docs.python.org/3/library/stdtypes.html#dict.fromkeys
-[collections-docs]: https://docs.python.org/3/library/collections.html
-[counter-dicts]: https://docs.python.org/3/library/collections.html#collections.Counter
-[ordered-dicts-docs]: https://docs.python.org/3/library/collections.html#collections.OrderedDict
-[default-dicts]: https://docs.python.org/2/library/collections.html#collections.defaultdict
-[dict-views]: https://docs.python.org/3/library/stdtypes.html#dict-views
 [dict-methods]: https://docs.python.org/3/library/stdtypes.html#dict
+[dict-views]: https://docs.python.org/3/library/stdtypes.html#dict-views
+[dicts-docs]: https://docs.python.org/3/tutorial/datastructures.html#dictionaries
 [fi-dict-guide]: https://blog.finxter.com/python-dictionary
+[fromkeys]: https://docs.python.org/3/library/stdtypes.html#dict.fromkeys
+[how-to-dicts]: https://www.w3schools.com/python/python_dictionaries.asp
+[mapping-types-dict]: https://docs.python.org/3/library/stdtypes.html#mapping-types-dict
 [sorting-howto]: https://docs.python.org/3/howto/sorting.html
diff --git a/concepts/dict-methods/introduction.md b/concepts/dict-methods/introduction.md
index 52868299..c15fbc11 100644
--- a/concepts/dict-methods/introduction.md
+++ b/concepts/dict-methods/introduction.md
@@ -1,17 +1,16 @@
 # Dictionary Methods in Python
 
-A dictionary (`dict`) in Python is a data structure that associates [hashable][term-hashable] _keys_ to _values_ and is known in other programming languages as a [hash table or hashmap][hashtable-wikipedia].
-In Python, it's considered a [mapping type][mapping-types-dict].
-`dicts` enable the retrieval of a value in constant time (on average), given the key.
+The `dict` class in Python provides many useful [methods][dict-methods], some of which are introduced in the concept exercise for dictionaries.
 
-Compared to searching for a value within a list or array (_without knowing the index position_), a dictionary uses significantly more memory, but has very rapid retrieval.
-It's especially useful in scenarios where the collection of items is large and must be accessed/updated frequently.
+This concept tackles a few more:
 
-The `dict` class in Python provides many useful [methods][dict-methods] for working with dictionaries.
-Some are introduced in the concept exercise for `dicts`.
-This concept tackles a few more - along with some techniques for iterating through and manipulating `dicts`.
+- `dict.setdefault()` automatically adds keys without throwing a `KeyError`.
+- `dict.fromkeys(iterable, <default value>)` creates a new `dict` from any number of iterables.
+- `.keys()`, `.values()`, and `.items()` provide convenient iterators.
+- `sorted(<dict>.items())`. can easily re-order entries in a `dict`.
+- `dict_one.update(<dict_two>)` updates one `dict` with overlapping values from another `dict`.
+- `dict | other_dict` and `dict |= other_dict` merges or updates two `dict`s via operators.
+- `reversed(dict.keys())`, `reversed(dict.values())`, or `reversed(dict.items())` produce _reversed_ views.
+- `<dict>.popitem()` removes and returns a `key`, `value` pair.
 
-[mapping-types-dict]: https://docs.python.org/3/library/stdtypes.html#mapping-types-dict
-[hashtable-wikipedia]: https://en.wikipedia.org/wiki/Hash_table
-[term-hashable]: https://docs.python.org/3/glossary.html#term-hashable
 [dict-methods]: https://docs.python.org/3/library/stdtypes.html#dict
diff --git a/concepts/dicts/about.md b/concepts/dicts/about.md
index fb43067e..c34160b2 100644
--- a/concepts/dicts/about.md
+++ b/concepts/dicts/about.md
@@ -1,55 +1,292 @@
 # About
 
+A dictionary (`dict`) in Python is a data structure that associates [hashable][term-hashable] _keys_ to _values_ and is known in other programming languages as a resizable [hash table][hashtable-wikipedia], hashmap, or [associative array][associative-array].
+Dictionaries are Python's only built-in [mapping type][mapping-types-dict].
 
-A dictionary (`dict`) is a [mapping type][mapping-types-dict] data structure that associates [hashable][term-hashable] `keys` to `values` -- known in other programming languages as a resizable [hash table or hashmap][hashtable-wikipedia].
-  `Keys` can include `numbers`, `str`, `tuples` (of _immutable_ values), or `frozensets`, but must be hashable and unique across the dictionary.
-  `keys` are _immutable_ - once added to a `dict`, they can only be removed, they cannot be updated.
-  `values` can be of any or multiple data type(s) or structures, including other dictionaries, built-in types, custom types, or even objects like functions or classes.
-   `values` associated with any `key` are _mutable_, and can be replaced, updated or altered as long as the `key` entry exists.
-  Dictionaries enable the retrieval of a `value` in (on average) constant O(1) time, given the `key`.
+`Keys` must be hashable and unique across the dictionary.
+Key types can include `numbers`, `str`, or `tuples` (of _immutable_ values).
+They cannot contain _mutable_ data structures such as `lists`, `dict`s, or `set`s.
+As of Python 3.7, `dict` key order is guaranteed to be the order in which entries are inserted.
 
-  Compared to searching for a value within a `list` or `array` (_without knowing the `index` position_), a `dict` uses significantly more space in memory, but has significantly more rapid retrieval.
- Dictionaries are especially useful in scenarios where the collection of items is large and must be accessed and/or updated frequently.
+`values` can be of any data type or structure.
+ Values can also nest _arbitrarily_, so they can include lists-of-lists, sub-dictionaries, and other custom or compound data structures.
 
-## Dictionary creation
+Given a `key`, dictionaries can retrieve a `value` in (on average) constant time (_independent of the number of entries_).
+Compared to searching for a value within a `list` or `array` (_without knowing the `index` position_), a `dict` uses significantly more memory, but has very rapid retrieval.
 
-A simple `dict` can be declared using the literal form `{<key_1>: <value_1>, <key_2>: <value_2>}`:
+Dictionaries are especially useful in scenarios where the collection of items is large and must be accessed and updated frequently.
 
- ```python
 
+## Dictionary Construction
 
+Dictionaries can be created in many different ways, including:
+ - Using the [`fromkeys()`][fromkeys] classmethod
+ - Creating [dictionary comprehensions][dict-comprehensions]
+ - Merging two dictionaries via unpacking (`**`)
+ - Merging dictionaries via the `|` (_update_) operator
+ - Using a loop to iteratively add entries to a previously created empty `dict`.
 
+The two most straightforward methods are the dictionary _constructor_ and the dictionary _literal_.
 
+### The Dictionary Constructor
+
+`dict()` (_the constructor for the `dict` class_) can be used with any iterable of `key`, `value` pairs.
+ It can also be called with a series of `<name>=<value>` _arguments_:
+
+```python
+# Passing a list of key,value tuples.
+>>> wombat = dict([('name', 'Wombat'),('speed', 23),
+                   ('land_animal', True)])
+{'name': 'Wombat', 'speed': 23, 'land_animal': True}
+
+
+# Using key=value arguments.
+>>> bear = dict(name="Black Bear", speed=40, land_animal=True)
+{'name': 'Black Bear', 'speed': 40, 'land_animal': True}
+```
+
+The [documentation on `dicts`][dicts-docs] outlines additional variations and options in constructor use.
+
+
+### Dictionary Literals
+
+A dictionary can also be directly entered as a _dictionary literal_, using curly brackets (`{}`) enclosing `key : value` pairs.
+Entries that are enclosed in the `{}` can also appear on separate lines:
+
+```python
+>>> whale = {"name": "Blue Whale", 
+             "speed": 35, 
+             "land_animal": False}
+{'name': 'Blue Whale', 'speed': 35, 'land_animal': False}
+
+>>> wombat = {'name': 'Wombat',
+              'speed': 23,
+              'land_animal': True,
+              'color': 'Brindle'}
+
+>>> wombat
+{'name': 'Wombat', 'speed': 23, 'land_animal': True, 'color': 'Brindle'}
+```
+
+### Nested Dictionaries
+
+Dictionaries can be arbitrarily nested:
+
+```python
+animals = {
+            "Real" : {
+                "Winged" : {
+                            "Sparrow" : {'name': 'sparrow','speed': 12, 'land_animal': True},
+                            "Kestrel" : {'name': 'kestrel', 'speed': 15, 'land_animal': True}
+                           },
+                "Legged" : {
+                            "Wombat" : {'name': 'Wombat', 'speed': 23, 'land_animal': True},
+                            "Black Bear": {'name': 'Black Bear', 'speed': 40, 'land_animal': True},
+                            "Polecat" : {'name': 'Polecat', 'speed': 15, 'land_animal': True}
+                           },
+                "Other" :  {
+                            "Whale" : {'name': 'Blue Whale', 'speed': 35, 'land_animal': False},
+                            "Orca" : {'name': 'Orca', 'speed': 45, 'land_animal': False},
+                            "Snake" : {'name': 'Python', 'speed': 25, 'land_animal': True}
+                            }
+                },
+        
+        "Imaginary": {
+                "Winged" : {
+                            "Dragon" : {'name': 'Fire Dragon','speed': 100, 'land_animal': True},
+                            "Phoenix" : {'name': 'Phoenix', 'speed': 1500, 'land_animal': True}
+                            },
+                "Legged" : {
+                            "Sphinx" : {'name': 'Sphinx','speed': 10, 'land_animal': True},
+                            "Minotaur" : {'name': 'Minotaur', 'speed': 5, 'land_animal': True}
+                            },
+                "Other" :  {}
+                }
+       }
+```
+
+## Accessing Values in a `dict`
+
+You can access a `value` in a dictionary using a _key_ in square brackets.
+If a key does not exist, a `KeyError` is thrown:
+
+```python
+>>> bear["speed"]
+40
+
+>>> bear["color"]
+Traceback (most recent call last):
+  File "<stdin>", line 1, in <module>
+KeyError: 'color'
+```
+
+Accessing an entry via the `get(<key>, <default value>)` method can avoid the `KeyError`:
+
+```python
+>>> bear.get("color", 'not found')
+'not found'
+```
+
+### Accessing Nested Dictionary Entries
+
+To access entries in nested dictionaries, use successive brackets.
+If a given key is missing, the usual KeyError will be thrown:
+
+```python
+# Using the animals nested dictionary.
+>>> animals["Real"]["winged"]["Kestrel"]["speed"]
+15
+
+>>> animals["Imaginary"]["winged"]["Kestrel"]["speed"]
+Traceback (most recent call last):
+  File "<stdin>", line 1, in <module>
+KeyError: 'Kestrel'
+```
+
+To avoid the `KeyError`, `.get()` can be used, but the calls to `.get()` must be _chained_:
+
+```python
+# Using the animals nested dictionary.
+# Note the use of parenthesis to enable placing the 
+# .get() calls on separate lines.
+>>> (animals.get("Imaginary", {})
+            .get("Legged", {})
+            .get("Sphinx", {})
+            .get("Color", "I have no idea!"))
+'I have no idea!'
+```
+
+## Changing or Adding Dictionary Values
+
+You can change an entry `value` by assigning to its _key_:
+
+```python
+# Assigning the value "Grizzly Bear" to the name key.
+>>> bear["name"] = "Grizzly Bear"
+{'name': 'Grizzly Bear', 'speed': 40, 'land_animal': True}
+
+>>> whale["speed"] = 25
+{'name': 'Blue Whale', 'speed': 25, 'land_animal': False}
+```
+
+New `key`:`value` pairs can be _added_ in the same fashion:
+
+```python
+# Adding an new "color" key with a new "tawney" value.
+>>> bear["color"] = 'tawney'
+{'name': 'Grizzly Bear', 'speed': 40, 'land_animal': True, 'color': 'tawney'}
+
+>>> whale["blowholes"] = 1
+{'name': 'Blue Whale', 'speed': 25, 'land_animal': False, 'blowholes': 1}
+```
+
+
+## Removing (Pop-ing and del) Dictionary Entries
+
+You can use the `.pop(<key>)` method to delete a dictionary entry.
+`.pop()` removes the (`key`, `value`) pair and returns the `value` for use.
+Like `.get()`, `.pop(<key>)` accepts second argument (_`dict.pop(<key>, <default value>)`_) that will be returned if the `key` is not found.
+This prevents a `KeyError` being raised:
+
+```python
+# Using .pop() removes both the key and value, returning the value.
+>>> bear.pop("name")
+'Grizzly Bear'
+
+
+# The "name" key is now removed from the dictionary.
+# Attempting .pop() a second time will throw a KeyError.
+>>> bear.pop("name")
+Traceback (most recent call last):
+  File "<stdin>", line 1, in <module>
+KeyError: 'name'
+
+
+# Using a default argument with .pop() will 
+# prevent a KeyError from a missing key.
+>>> bear.pop("name", "Unknown")
+'Unknown'
 ```
 
- The dictionary  constructor `dict(<key_1>=<value_1>, <key_2>=<value_2>)`, but there are many more ways of creating and initializing dictionaries including the use of a _dict comprehension_ or passing additional constructor parameters as illustrated in the [Python docs][mapping-types-dict].
+You can also use the `del` statement to remove a single or multiple entries.
+A `KeError` is raised if the entry to be removed is not found in the dictionary:
+
+```python
+>>> wombat = {'name': 'Wombat',
+              'speed': 23,
+              'land_animal': True,
+              'color': 'Brindle',
+              'talent': 'Singing',
+              'size': 'small'}
+
+# Remove a single entry from the dictionary.
+>>> del wombat["color"]
+>>> wombat
+{'name': 'Wombat', 'speed': 23, 'land_animal': True, 'talent': 'Singing', 'size': 'small'}
+
+
+# Remove multiple entries from the dictionary.
+>>> del wombat["talent"], wombat["size"]
+>>> wombat
+{'name': 'Wombat', 'speed': 23, 'land_animal': True}
 
 
+# Attempting a deletion of a non-existent key raises a KeyError
+>>> del wombat["number_of_legs"]
+Traceback (most recent call last):
+  File "<stdin>", line 1, in <module>
+KeyError: 'number_of_legs'
+```
 
-Inserting a new `key`:`value` pair can be done with `dict[key] = value` and the value can be retrieved by using `retrieved_value = dict[key]`.
+## Looping Through/Iterating over a Dictionary
 
-## Methods
+Looping through a dictionary using `for item in dict` or `while item` will iterate over the _keys_ by default.
+You can access _values_ within the same loop by using _square brackets_:
 
-`dicts` implement various methods to allow easy initialization, updating and viewing.
+```python
+>>> for key in bear:
+>>>     print((key, bear[key])) #this prints a tuple of (key, value)
+('name', 'Black Bear')
+('speed', 40)
+('land_animal', True)
+```
 
-Some useful `dict` methods:
+You can also use the `.items()` method, which returns (`key`, `value`) tuples:
 
-- Retrieve a value "safely" from a dictionary by using the `.get(key, [default])` method. `.get(key, [default])` returns the value for the key **or** the _default value_ if the key is not found, instead of raising a `KeyError`. This works well in situations where you would rather not have extra error handling but cannot trust that a looked-for key will be present.
-- Retrieve a value "safely" or insert a default _value_ if the key is not found using the `.setdefault(key, [default])` method. `setdefault(key, [default])` will insert the default value in the dictionary **only** if the key is not found, then it will retrieve either the **newly inserted** default value if the key was not found or the **unchanged** existing value if the key was found.
-- Return various _iterable_ views of your `dict` with `.keys()`, `.values()`, `.items()` (_an iterable of (key, value) `tuples`_).
+```python
+# dict.items() forms (key, value tuples) that can be 
+# unpacked and iterated over.
+>>> for key, value in whale.items():
+>>>     print(key, ":", value)
+name : Blue Whale
+speed : 25
+land_animal : False
+blowholes : 1
+```
+
+Likewise, `.keys()` will return the `keys` and `.values()` will return the `values`.
 
 For a detailed explanation of dictionaries in Python, the [official documentation][dicts-docs] is an excellent starting place, or you can also check out the [W3-Schools][how-to-dicts] tutorial.
 
-## Extending Dictionaries: The collections module
 
-The [`collections`][collections-docs] module adds more functionality to Python's standard collection-based datatypes (`dictionary`, `set`, `list`, `tuple`). A popular `dict`-oriented member of this module is the [`Counter`][counter-dicts], which automatically counts items and returns them a `dict` with the items as keys and their counts as values. There is also the [`OrderedDict`][ordered-dicts-docs], which has methods specialized for re-arranging the order of a dictionary. Finally, there is the [`defaultdict`][default-dicts], a subclass of the built-in `dict` module that, based on a factory method, sets a default value if a key is not found when trying to retrieve or assign the value.
+## Extending Dictionary Functionality: The Collections Module
 
-[term-hashable]: https://docs.python.org/3/glossary.html#term-hashable
-[hashtable-wikipedia]: https://en.wikipedia.org/wiki/Hash_table
-[mapping-types-dict]: https://docs.python.org/3/library/stdtypes.html#mapping-types-dict
-[dicts-docs]: https://docs.python.org/3/tutorial/datastructures.html#dictionaries
-[how-to-dicts]: https://www.w3schools.com/python/python_dictionaries.asp
+The [`collections`][collections-docs] module adds specialized functionality to Python's standard collection-based datatypes (`dictionary`, `set`, `list`, `tuple`).
+Three of the most useful dictionary-based classes are:
+
+- [`Counter`][counter-dicts] automatically counts items and returns them in a `dict` with the items as keys and their counts as values.
+- [`OrderedDict`][ordered-dicts-docs], has methods specialized for arranging the order of dictionary entries.
+- [`defaultdict`][default-dicts] uses a factory method to set a default value if a `key` is not found when trying to retrieve or assign to a dictionary entry.
+
+[associative-array]: https://en.wikipedia.org/wiki/Associative_array#:~:text=In%20computer%20science%2C%20an%20associative,a%20function%20with%20finite%20domain.
 [collections-docs]: https://docs.python.org/3/library/collections.html
 [counter-dicts]: https://docs.python.org/3/library/collections.html#collections.Counter
-[ordered-dicts-docs]: https://docs.python.org/3/library/collections.html#collections.OrderedDict
 [default-dicts]: https://docs.python.org/2/library/collections.html#collections.defaultdict
+[dict-comprehensions]: https://www.learnbyexample.org/python-dictionary-comprehension/
+[dicts-docs]: https://docs.python.org/3/tutorial/datastructures.html#dictionaries
+[fromkeys]: https://www.w3schools.com/python/ref_dictionary_fromkeys.asp
+[hashtable-wikipedia]: https://en.wikipedia.org/wiki/Hash_table
+[how-to-dicts]: https://www.w3schools.com/python/python_dictionaries.asp
+[mapping-types-dict]: https://docs.python.org/3/library/stdtypes.html#mapping-types-dict
+[ordered-dicts-docs]: https://docs.python.org/3/library/collections.html#collections.OrderedDict
+[term-hashable]: https://docs.python.org/3/glossary.html#term-hashable
diff --git a/concepts/dicts/introduction.md b/concepts/dicts/introduction.md
index 9a887ccf..5c8a7724 100644
--- a/concepts/dicts/introduction.md
+++ b/concepts/dicts/introduction.md
@@ -1,15 +1,24 @@
 # Introduction
 
+A dictionary (`dict`) in Python is a data structure that associates [hashable][term-hashable] _keys_ to _values_ and is known in other programming languages as a resizable [hash table][hashtable-wikipedia], hashmap, or [associative array][associative-array].
+Dictionaries are Python's only built-in [mapping type][mapping-types-dict].
 
-A dictionary (`dict`) is a [mapping type][mapping-types-dict] data structure that associates [hashable][term-hashable] `keys` to `values` -- known in other programming languages as a resizable [hash table or hashmap][hashtable-wikipedia].
-  `Keys` can include `numbers`, `str`, `tuples` (of _immutable_ values), or `frozensets`, but must be hashable and unique across the dictionary.
-  `values` can be of any or multiple data type(s) or structures, including other dictionaries, built-in types, custom types, or even objects like functions or classes.
-  Dictionaries enable the retrieval of a `value` in (on average) constant O(1) time, given the `key`.
 
-  Compared to searching for a value within a `list` or `array` (_without knowing the `index` position_), a `dict` uses significantly more space in memory, but has significantly more rapid retrieval.
- Dictionaries are especially useful in scenarios where the collection of items is large and must be accessed and/or updated frequently.
+`Keys` must be hashable and unique across the dictionary.
+Key types can include `numbers`, `str`, or `tuples` (of _immutable_ values).
+They cannot contain _mutable_ data structures such as `lists`, `dict`s, or `set`s.
+As of Python 3.7, `dict` key order is guaranteed to be the order in which entries are inserted.
 
+`values` can be of any data type or structure.
+ Values can also nest _arbitrarily_, so they can include lists-of-lists, sub-dictionaries, and other custom or compound data structures.
 
+Given a `key`, dictionaries can retrieve a `value` in (on average) constant time (_independent of the number of entries_).
+Compared to searching for a value within a `list` or `array` (_without knowing the `index` position_), a `dict` uses significantly more memory, but has very rapid retrieval.
+
+Dictionaries are especially useful in scenarios where the collection of items is large and must be accessed and updated frequently.
+
+
+[associative-array]: https://en.wikipedia.org/wiki/Associative_array#:~:text=In%20computer%20science%2C%20an%20associative,a%20function%20with%20finite%20domain.
 [hashtable-wikipedia]: https://en.wikipedia.org/wiki/Hash_table
 [mapping-types-dict]: https://docs.python.org/3/library/stdtypes.html#mapping-types-dict
 [term-hashable]: https://docs.python.org/3/glossary.html#term-hashable
diff --git a/concepts/generators/.meta/config.json b/concepts/generators/.meta/config.json
index 2204a700..6c29169d 100644
--- a/concepts/generators/.meta/config.json
+++ b/concepts/generators/.meta/config.json
@@ -1,5 +1,5 @@
 {
-  "blurb": "Learn about generators by assigning seats to passengers.",
+  "blurb": "Generator are functions that returns a lazy iterator, lazy iterator is an iterator like: list, tuple, etc. But doesn't need store its content in memory",
   "authors": ["J08K"],
   "contributors": []
 }
diff --git a/concepts/generators/about.md b/concepts/generators/about.md
index f1b97291..9a26ab55 100644
--- a/concepts/generators/about.md
+++ b/concepts/generators/about.md
@@ -4,65 +4,63 @@
 
 Generators are constructed much like other looping or recursive functions, but require a [`yield` expression](#the-yield-expression), which we will explore in depth a bit later.
 
-
 An example is a function that returns the _squares_ from a given list of numbers.  
-As currently written, all input must be processed before any values can be returned:  
-
+As currently written, all input must be processed before any values can be returned:
 
 ```python
 >>> def squares(list_of_numbers):
->>>     squares = []
->>>     for number in list_of_numbers:
->>>         squares.append(number ** 2)
->>>     return squares
+...     squares = []
+...     for number in list_of_numbers:
+...         squares.append(number ** 2)
+...     return squares
 ```
 
 You can convert that function into a generator like this:
 
 ```python
-def squares(list_of_numbers):
-    for number in list_of_number:
-        yield number ** 2
+>>> def squares_generator(list_of_numbers):
+...     for number in list_of_numbers:
+...         yield number ** 2
 ```
 
 The rationale behind this is that you use a generator when you do not need all the values _at once_.
 
 This saves memory and processing power, since only the value you are _currently working on_ is calculated.
 
-
 ## Using a generator
 
-Generators may be used in place of most `iterables` in Python. This includes  _functions_ or _objects_ that require an `iterable`/`iterator` as an argument.
+Generators may be used in place of most `iterables` in Python. This includes _functions_ or _objects_ that require an `iterable`/`iterator` as an argument.
 
-To use the `squares()` generator:
+To use the `squares_generator()` generator:
 
 ```python
->>> squared_numbers = squares([1, 2, 3, 4])
+>>> squared_numbers = squares_generator([1, 2, 3, 4])
 
 >>> for square in squared_numbers:
->>>     print(square)
+...     print(square)
+...
 1
 4
 9
 16
 ```
 
-Values within a generator can also be produced/accessed via the `next()` function. 
+Values within a generator can also be produced/accessed via the `next()` function.
 `next()` calls the `__next__()` method of a generator object, "advancing" or evaluating the generator code up to its `yield` expression, which then "yields" or returns the value.
 
 ```python
-square_generator = squares([1, 2])
+>>> squared_numbers = squares_generator([1, 2])
 
->>> next(square_generator)
+>>> next(squared_numbers)
 1
->>> next(square_generator)
+>>> next(squared_numbers)
 4
 ```
 
 When a `generator` is fully consumed and has no more values to return, it throws a `StopIteration` error.
 
 ```python
->>> next(square_generator)
+>>> next(squared_numbers)
 Traceback (most recent call last):
   File "<stdin>", line 1, in <module>
 StopIteration
@@ -72,12 +70,12 @@ StopIteration
 
 Generators are a special sub-set of _iterators_.
 `Iterators` are the mechanism/protocol that enables looping over _iterables_.
-Generators and and the iterators returned by common Python (`iterables`)[https://wiki.python.org/moin/Iterator] act very similarly, but there are some important differences to note:
-
+Generators and the iterators returned by common Python [`iterables`][iterables] act very similarly, but there are some important differences to note:
 
 - Generators are _one-way_; there is no "backing up" to a previous value.
 
 - Iterating over generators consume the returned values; no resetting.
+
 - Generators (_being lazily evaluated_) are not sortable and can not be reversed.
 
 - Generators do _not_ have `indexes`, so you can't reference a previous or future value using addition or subtraction.
@@ -88,7 +86,7 @@ Generators and and the iterators returned by common Python (`iterables`)[https:/
 
 ## The yield expression
 
-The [yield expression](https://docs.python.org/3.8/reference/expressions.html#yield-expressions) is very similar to the `return` expression.
+The [yield expression][yield expression] is very similar to the `return` expression.
 
 _Unlike_ the `return` expression, `yield` gives up values to the caller at a _specific point_, suspending evaluation/return of any additional values until they are requested.
 
@@ -96,14 +94,16 @@ When `yield` is evaluated, it pauses the execution of the enclosing function and
 
 The function then _stays in scope_, and when `__next__()` is called, execution resumes until `yield` is encountered again.
 
-Note: _Using `yield` expressions is prohibited outside of functions._
+```exercism/note
+Using `yield` expressions is prohibited outside of functions.
+```
 
 ```python
 >>> def infinite_sequence():
->>>     current_number = 0
->>>     while True:
->>>         yield current_number
->>>         current_number += 1
+...     current_number = 0
+...     while True:
+...         yield current_number
+...         current_number += 1
 
 >>> lets_try = infinite_sequence()
 >>> lets_try.__next__()
@@ -123,10 +123,13 @@ Generators are also very helpful when a process or calculation is _complex_, _ex
 
 ```python
 >>> def infinite_sequence():
->>>     current_number = 0
->>>     while True:
->>>         yield current_number
->>>         current_number += 1
+...     current_number = 0
+...     while True:
+...         yield current_number
+...         current_number += 1
 ```
 
 Now whenever `__next__()` is called on the `infinite_sequence` object, it will return the _previous number_ + 1.
+
+[iterables]: https://wiki.python.org/moin/Iterator
+[yield expression]: https://docs.python.org/3.11/reference/expressions.html#yield-expressions
diff --git a/concepts/generators/introduction.md b/concepts/generators/introduction.md
index 2c148371..82a686d1 100644
--- a/concepts/generators/introduction.md
+++ b/concepts/generators/introduction.md
@@ -1,5 +1,7 @@
 # Introduction
 
-A generator in Python is a _callable function_ that returns a [lazy iterator](https://en.wikipedia.org/wiki/Lazy_evaluation).
+A generator in Python is a _callable function_ that returns a [lazy iterator][lazy iterator].
 
 _Lazy iterators_ are similar to `lists`, and other `iterators`, but with one key difference: They do not store their `values` in memory, but _generate_ their values when needed.
+
+[lazy iterator]: https://en.wikipedia.org/wiki/Lazy_evaluation
diff --git a/concepts/generators/links.json b/concepts/generators/links.json
index b8ae2f7b..972bbe7a 100644
--- a/concepts/generators/links.json
+++ b/concepts/generators/links.json
@@ -1,10 +1,14 @@
 [
   {
-    "url": "https://docs.python.org/3.8/reference/expressions.html#yield-expressions",
-    "description": "Official Python 3.8 docs for the yield expression."
+    "url": "https://docs.python.org/3.10/reference/expressions.html#yield-expressions",
+    "description": "Official Python 3.10 docs for the yield expression."
   },
   {
     "url": "https://en.wikipedia.org/wiki/Lazy_evaluation",
     "description": "Wikipedia page about lazy evaluation"
+  },
+  {
+    "url": "https://realpython.com/introduction-to-python-generators/",
+    "description": "Real python, introduction to generators and yield"
   }
 ]
diff --git a/concepts/sets/about.md b/concepts/sets/about.md
index dd567f07..5f272402 100644
--- a/concepts/sets/about.md
+++ b/concepts/sets/about.md
@@ -12,7 +12,7 @@ _Unlike_ sequence types (_`string`, `list` & `tuple`_), `sets` are **neither ord
 They're also used for fast membership testing, finding supersets & subsets of items, and performing "set math" (_calculating union, intersection, difference & symmetric difference between groups of items._).
 
 Checking membership in a `set` has only O(1) time complexity versus checking for membership in a `list` or `string`, which has worst-case O(n) time complexity.
-Operations such as `<set>.union()`, `<set>.intersection()`, or `<set>.diference()` have an average O(n) time complexity.
+Operations such as `<set>.union()`, `<set>.intersection()`, or `<set>.difference()` have an average O(n) time complexity.
 
 ## Construction
 
@@ -52,7 +52,7 @@ set()
 # The list is unpacked and each distinct element is added.
 >>> multiple_elements_from_list = set([2, 3, 2, 3, 3, 3, 5, 7, 11, 7, 11, 13, 13])
 >>> multiple_elements_from_set
-{2, 3, 5, 7, 11}
+{2, 3, 5, 7, 11, 13}
 ```
 
 Results when using a set constructor with a string or dictionary may be surprising:
diff --git a/concepts/string-formatting/about.md b/concepts/string-formatting/about.md
index 07306a8d..f3b2756b 100644
--- a/concepts/string-formatting/about.md
+++ b/concepts/string-formatting/about.md
@@ -2,18 +2,18 @@
 
 ## String Formatting in Python
 
+String formatting is the process of converting values to strings and inserting them into a string template.
 The [Zen of Python][zen-of-python] asserts there should be "one _obvious_ way to do something in Python".
-But when it comes to string formatting, things are a little .... _less zen_.
+But when it comes to string formatting, things are a little ... _less zen_.
 It can be surprising to find out that there are **four** main ways to perform string formatting in Python - each for a different scenario.
 Some of this is due to Python's long history and some of it is due to considerations like internationalization or input sanitation.
 We will start with the most recent additions to the string formatting toolbox and work our way backward to "old style" or "printf() style" string formatting.
 
+## literal string interpolation: The `f-string`
 
-## literal string interpolation:  The `f-string`
-
- Introduced in [Python 3.6][pep-0498], [`f-strings`][f-string] (_short for "formatted-strings"_) or [literal string interpolation][string interpolation] are a way of quickly and efficiently evaluating and formatting expressions and strings to a `str` type using the `f` (or `F`) prefix before the brackets (_like so `f'{object}'`_).
- They can be used with all enclosing string types as: single quote `'`, double quote `"` and with multi-lines and escaping triple quotes `'''` or `"""`.
- Any variables, expressions, or other types placed inside the `{}` are first evaluated, then converted to a `str`, then concatenated with any `str` outside the curly braces.
+Introduced in [Python 3.6][pep-0498], [`f-strings`][f-string] (_short for "formatted-strings"_) or [literal string interpolation][string interpolation] are a way of quickly and efficiently evaluating and formatting expressions and strings to a `str` type using the `f` (or `F`) prefix before the brackets (_like so `f'{object}'`_).
+They can be used with all enclosing string types as: single-line `'` or `"` and with multi-lines `'''` or `"""`.
+Any variables, expressions, or other types placed inside the `{}` are first evaluated, then converted to a `str`, then concatenated with any `str` outside the curly braces.
 
 In this example, we insert two variable values in the sentence: one `str` and one `float`:
 
@@ -23,8 +23,8 @@ In this example, we insert two variable values in the sentence: one `str` and on
 ...
 # The f-string, using the two values.
 # The .2f format code truncates so the value displays as 0.12.
->>> print(f'An {name} is approximately {value:.2f}.')
-'An eighth is approximately 0.12.' 
+>>> f'An {name} is approximately {value:.2f}.'
+'An eighth is approximately 0.12.'
 ```
 
 The expressions evaluated can be almost anything.
@@ -37,16 +37,16 @@ Some examples:
 >>> waves = {'water': 1, 'light': 3, 'sound': 5}
 
 # Using the name waves in an f-string.
->>> print(f'"A dict can be represented with f-string: {waves}."')
+>>> f'"A dict can be represented with f-string: {waves}."'
 '"A dict can be represented with f-string: {\'water\': 1, \'light\': 3, \'sound\': 5}."'
 
 # Here, we pull a value from the dictionary by using the key
->>> print(f'Tenfold the value of "light" is {waves["light"]*10}.')
+>>> f'Tenfold the value of "light" is {waves["light"] * 10}.'
 'Tenfold the value of "light" is 30.'
 ```
 
 Replacement fields (_the `{}` in the f-string_) support output control mechanisms such as width, alignment, precision.
- This is the same [format specification mini-language][format-mini-language] that is used by the `str.format()` method.
+This specification is started in the [format specification mini-language][format-mini-language].
 
 A more complex example of an `f-string` that includes output control:
 
@@ -61,24 +61,21 @@ A more complex example of an `f-string` that includes output control:
 
 # This example includes a function, str, a nested f-string, an arithmetic expression, 
 # precision formatting, bracket escaping and object formatting.
->>> message = f'"Have a {"NICE".lower()} day, I will {verb} you after {f"{30e8*111_000:6.{precision}e}"} light-years."{{{the_end}}}'
-...
->>> print(message)
+>>> f'"Have a {"NICE".lower()} day, I will {verb} you after {f"{30e8 * 111_000:6.{precision}e}"} light-years."{{{the_end}}}'
 '"Have a nice day, I will meet you after 3.330e+14 light-years."{[\'end\', \'of\', \'transmission\']}'
-
 ```
 
 There are a few limitations to be aware of.
-`f-string` expressions cannot be empty, they cannot contain comments, and for Python versions earlier than Python 3.7, they cannot contain `await` or `async for` clauses:
+`f-string` expressions cannot be empty, they cannot contain comments.
 
 ```python
->>> print(f"An empty expression will error: {}")
+>>> f"An empty expression will error: {}"
 SyntaxError: f-string: empty expression not allowed
 
 >>> word = 'word'
->>> print(f"""A comment in a triple quoted f-string will error: {
+>>> f"""A comment in a triple quoted f-string will error: {
     word # I chose a nice variable
-}""")
+}"""
 SyntaxError: f-string expression part cannot include '#'
 ```
 
@@ -92,17 +89,16 @@ Also keep in mind that using expressions inside the `f-string` brackets `{}` is
 ## The `str.format()` Method
 
 The [`str.format()`][str-format] method replaces placeholders within the string with values fed as arguments to the function.
- The placeholders are identified with named (`{price}`), numbered (`{0}` or indexed) or even empty (_positional_) placeholders `{}`.
+The placeholders are identified with named (`{price}`), numbered (`{0}` or indexed) or even empty (_positional_) placeholders `{}`.
 For example:
 
 ```python
 # A named placeholder and a positional placeholder.
->>> print('My text: {placeholder_1} and {}.'.format(12, placeholder_1='named placeholder'))
-...
+>>> 'My text: {placeholder_1} and {}.'.format(12, placeholder_1='named placeholder')
 'My text: named placeholder and 12.'
 ```
 
-As with `f-strings`, Pythons  `str.format()` supports a whole range of [mini language format specifier][format-mini-language] that can be used to align text, convert, etc.
+As with `f-strings`, Pythons `str.format()` supports a whole range of [mini language format specifier][format-mini-language] that can be used to align text, convert, etc.
 
 The complete formatting specifier pattern is `{[<name>][!<conversion>][:<format_specifier>]}`:
 
@@ -115,25 +111,24 @@ Example of conversions for a diacritical letter:
 
 ```python
 # Fills in the object at index zero, converted to a string.
->>> print('An e with an umlaut: {0!s}'.format('ë'))
-An e with an umlaut: ë
-...
+>>> 'An e with an umlaut: {0!s}'.format('ë')
+'An e with an umlaut: ë'
+
 
 # Fills in the object at index zero, converted to a repr.
->>> print('An e with an umlaut object representation: {0!r}'.format('ë'))
-An e with an umlaut object representation: 'ë'
+>>> 'An e with an umlaut object representation: {0!r}'.format('ë')
+"An e with an umlaut object representation: 'ë'"
 
 ...
 
 # Fills in the object at index zero, converted to ascii
->>> print('An e with an umlaut converted into ascii: {0!a}'.format('ë'))
-An e with an umlaut converted into ascii: '\xeb'
+>>> 'An e with an umlaut converted into ascii: {0!a}'.format('ë')
+"An e with an umlaut converted into ascii: '\xeb'"
 
-...
 
 # Fills in the object in the first position.
 # Then fills in the object in the second position formatted as a repr
->>> print('She said her name is not {} but {!r}.'.format('Chloe', 'Zoë'))
+>>> 'She said her name is not {} but {!r}.'.format('Chloe', 'Zoë')
 "She said her name is not Chloe but 'Zoë'."
 ```
 
@@ -142,31 +137,28 @@ Example of using format specifiers:
 ```python
 # Formats the object at index 0 as a decimal with zero places, 
 # then as a right-aligned binary number in an 8 character wide field.
->>> print("The number {0:d} has a representation in binary: '{0: >8b}'.".format(42))
-The number 42 has a representation in binary: '  101010'.
+>>> "The number {0:d} has a representation in binary: '{0: >8b}'.".format(42)
+"The number 42 has a representation in binary: '  101010'."
 ```
 
 More examples are shown at the end of [this documentation][summary-string-format].
 
-
 ## `%` Formatting, or `printf()` Style Formatting
 
 Use of the `%` operator for formatting is the oldest method of string formatting in Python.
 It comes from the C language and allows the use of positional arguments to build a `str`.
 
 This method has been superseded by both `f-strings` and `str.format()`, which is why the nickname for `%` formatting is _'Old Style'_.
-It can be still found in python 2 and/or legacy code.
+It can be still found in Python 2 and/or legacy code.
 While using this method will work in Python 3.x, `%` formatting is usually avoided because it can be error-prone, is less efficient, has fewer options available, and any placeholder-argument mismatch can raise an exception.
- Using the `%` operator is similar to [`printf()`][printf-style-docs], so it is also sometimes called _printf formatting_.
-
+Using the `%` operator is similar to [`printf()`][printf-style-docs], so it is also sometimes called _printf formatting_.
 
 ```python
 # Assigning a variable.
->> name = "Anna-conda"
+>>> name = "Anna-conda"
 
 # Building a string using %
->> print("The snake's name is %s." % name)
-...
+>>> "The snake's name is %s." % name
 "The snake's name is Anna-conda."
 ```
 
@@ -179,33 +171,30 @@ If you want to add multiple variables to a string, you need to supply a [tuple][
 >>> fruit = "grapes"
 
 # Building a string using %
->>> print("Surprisingly, %ss favorite snack was %s." %(name, fruit))
-Surprisingly, Billy the Kids favorite snack was grapes.
+>>> "Surprisingly, %ss favorite snack was %s." %(name, fruit)
+"Surprisingly, Billy the Kids favorite snack was grapes."
 ```
 
-
 ## Template Strings
 
 [`string.Template()`][string.Template()] is a class from the `string` module (_as opposed to the built-in `str` type_), which is part of the Python standard library, but has to be imported for use.
 Template strings support `$`-based substitution and are much simpler and less capable than the other options mentioned here, but can be very useful for when complicated internationalization is needed, or outside inputs need to be sanitized.
 
-
 ```python
->> from string import Template
+>>> from string import Template
 
->>> snake_name = "Anna-Conda"
+>>> name = "Anna-Conda"
 
 # Creating a Template() with placeholder text
->> template_string = Template("The snake called `$snake_name` has escaped!")
+>>> template_string = Template("The snake called `$snake_name` has escaped!")
 
 # Calling .substitute() to replace the placeholder with a value.
->> template_string.substitute(snake_name=name)
+>>> template_string.substitute(snake_name=name)
 'The snake called `Anna-Conda` has escaped!'
 ```
 
 More information about `Template` string can be found in the Python [documentation][template-string].
 
-
 ## How Do You Choose which Formatting Method to Use?
 
 With all these options and mini-languages, how do you decide what to reach for when formatting Python strings?
diff --git a/config.json b/config.json
index 1d118f79..f386ef39 100644
--- a/config.json
+++ b/config.json
@@ -144,6 +144,14 @@
         "prerequisites": ["loops", "lists", "tuples"],
         "status": "beta"
       },
+      {
+        "slug": "mecha-munch-management",
+        "name": "Mecha Munch Management",
+        "uuid": "5ac0c40c-4038-47b8-945b-8480e4a3f44c",
+        "concepts": ["dict-methods"],
+        "prerequisites": ["dicts"],
+        "status": "wip"
+      },
       {
         "slug": "locomotive-engineer",
         "name": "Locomotive Engineer",
@@ -184,7 +192,7 @@
         "name": "Plane Tickets",
         "uuid": "3ba3fc89-3e1b-48a5-aff0-5aeaba8c8810",
         "concepts": ["generators"],
-        "prerequisites": ["conditionals", "dicts", "lists", "loops"],
+        "prerequisites": ["conditionals", "dicts", "lists", "loops", "classes"],
         "status": "wip"
       },
       {
@@ -970,6 +978,21 @@
         ],
         "difficulty": 3
       },
+      {
+        "slug": "resistor-color-expert",
+        "name": "Resistor Color Expert",
+        "uuid": "8a738365-0efa-444f-9466-a757ddaddcdb",
+        "practices": ["list-methods"],
+        "prerequisites": [
+          "basics",
+          "bools",
+          "lists",
+          "numbers",
+          "strings",
+          "comparisons"
+        ],
+        "difficulty": 3
+      },
       {
         "slug": "matrix",
         "name": "Matrix",
@@ -1247,14 +1270,6 @@
         ],
         "difficulty": 3
       },
-      {
-        "slug": "diffie-hellman",
-        "name": "Diffie-Hellman",
-        "uuid": "92e2d5f8-7d8a-4e81-a55c-52fa6be80c74",
-        "practices": ["numbers"],
-        "prerequisites": ["basics", "bools", "numbers"],
-        "difficulty": 3
-      },
       {
         "slug": "connect",
         "name": "Connect",
@@ -2201,6 +2216,15 @@
         "difficulty": 3,
         "status": "deprecated"
       },
+      {
+        "slug": "diffie-hellman",
+        "name": "Diffie-Hellman",
+        "uuid": "92e2d5f8-7d8a-4e81-a55c-52fa6be80c74",
+        "practices": [],
+        "prerequisites": [],
+        "difficulty": 3,
+        "status": "deprecated"
+      },
       {
         "slug": "trinary",
         "name": "Trinary",
@@ -2234,6 +2258,11 @@
       "slug": "binary-data",
       "name": "Binary Data"
     },
+    {
+      "uuid": "78dbf248-a1e5-48cb-ba53-def4d92bf2a8",
+      "slug": "binary-octal-hexadecimal",
+      "name": "Binary, Octal, and Hexadecimal"
+    },
     {
       "uuid": "f8e96e60-f746-4c7a-8dc9-df6b77eefdfa",
       "slug": "bitflags",
diff --git a/docs/ABOUT.md b/docs/ABOUT.md
index 05c56799..f0d52389 100644
--- a/docs/ABOUT.md
+++ b/docs/ABOUT.md
@@ -22,7 +22,7 @@ The [zen of Python (PEP 20)][the zen of python] and [What is Pythonic?][what is
 
 Tests and tooling for this track currently support `3.7` - `3.10.6` (_tests_) and [`Python 3.11`][311-new-features] (_tooling_).
 It is highly recommended that students upgrade to at least `Python 3.8`, as some features used by this track may not be supported in earlier versions.
-That being said, most of the exercises will work with `Python 3.6+`, and many are compatible with `Python 2.7+`.
+That being said, most of the exercises will work with `Python 3.6+`.
 But we don't guarantee support for versions not listed under [Active Python Releases][active-python-releases].
 We will try to note when a feature is only available in a certain version.
 
diff --git a/docs/INSTALLATION.md b/docs/INSTALLATION.md
index 8bf42a04..c8eb68ca 100644
--- a/docs/INSTALLATION.md
+++ b/docs/INSTALLATION.md
@@ -6,7 +6,7 @@ Real Python also offers a [nice guide][helpful guide] to installation on various
 Finally, these posts by Brett Cannon [A quick-and-dirty guide][quick-and-dirty] and [Why you should use `python -m pip`][python-m-pip], give very helpful advice on how to manage Python installations and packages.
 
 **Note for MacOS users:**  prior to MacOS Monterey (12.3), `Python 2.7` came pre-installed with the operating system.
-Using `Python 2.7` with Exercism or most other programs is not recommended.
+Using `Python 2.7` with Exercism or most other programs is not supported.
 You should instead install [Python 3][Python-three downloads] via one of the methods detailed below.
 As of MacOS Monterey (12.3), no version of Python will be pre-installed via MacOS.
 
@@ -20,7 +20,7 @@ Some quick links into the documentation by operating system:
 
 Exercism tests and tooling currently support `3.7` - `3.10.6` (_tests_) and [`Python 3.11`][311-new-features] (_tooling_).
 Exceptions to this support are noted where they occur.
-Most of the exercises will work with `Python 3.6+`, and many are compatible with `Python 2.7+`.
+Most of the exercises will work with `Python 3.6+`.
 But we don't guarantee support for versions not listed under [Active Python Releases][active-python-releases].
 
 
diff --git a/exercises/concept/black-jack/.docs/introduction.md b/exercises/concept/black-jack/.docs/introduction.md
index b79091f4..20722935 100644
--- a/exercises/concept/black-jack/.docs/introduction.md
+++ b/exercises/concept/black-jack/.docs/introduction.md
@@ -80,7 +80,7 @@ False
 Unlike numbers, strings (`str`) are compared [_lexicographically_][lexographic order], using their individual Unicode code points (_the result of passing each code point in the `str` to the built-in function [`ord()`][ord], which returns an `int`_).
 If all code points in both strings match and are _**in the same order**_, the two strings are considered equal.
 This comparison is done in a 'pair-wise' fashion - first-to-first, second-to-second, etc.
-Unlike in Python 2.x, in Python 3.x, `str` and `bytes` cannot be directly coerced/compared.
+In Python 3.x, `str` and `bytes` cannot be directly coerced/compared.
 
 ```python
 >>> 'Python' > 'Rust'
diff --git a/exercises/concept/cater-waiter/.docs/introduction.md b/exercises/concept/cater-waiter/.docs/introduction.md
index fa29f056..905504a6 100644
--- a/exercises/concept/cater-waiter/.docs/introduction.md
+++ b/exercises/concept/cater-waiter/.docs/introduction.md
@@ -50,7 +50,7 @@ set()
 # The list is unpacked and each distinct element is added.
 >>> multiple_elements_from_list = set([2, 3, 2, 3, 3, 3, 5, 7, 11, 7, 11, 13, 13])
 >>> multiple_elements_from_set
-{2, 3, 5, 7, 11}
+{2, 3, 5, 7, 11, 13}
 ```
 
 Sets can hold heterogeneous datatypes, but all `set` elements must be _hashable_:
diff --git a/exercises/concept/inventory-management/.docs/instructions.md b/exercises/concept/inventory-management/.docs/instructions.md
index e1c1806c..718b91d3 100644
--- a/exercises/concept/inventory-management/.docs/instructions.md
+++ b/exercises/concept/inventory-management/.docs/instructions.md
@@ -4,13 +4,17 @@ In this exercise, you will be managing an inventory system.
 
 The inventory should be organized by the item name and it should keep track of the number of items available.
 
-You will have to handle adding items to an inventory. Each time an item appears in a given list, increase the item's quantity by `1` in the inventory. Then, you will have to handle deleting items from an inventory.
+You will have to handle adding items to an inventory.
+Each time an item appears in a given list, the item's quantity should be increased by `1` in the inventory.
+You will also have to handle deleting items from an inventory by decreasing quantities by `1` when requested.
+
+Finally, you will need to implement a function that will return all the key-value pairs in a given inventory as a `list` of `tuples`.
 
-To finish, you will have to implement a function which returns all the key-value pairs in an inventory as a list of `tuples`.
 
 ## 1. Create an inventory based on a list
 
-Implement the `create_inventory()` function that creates an "inventory" from a list of items. It should return a `dict` containing each item name paired with their respective quantity.
+Implement the `create_inventory(<input list>)` function that creates an "inventory" from an input list of items.
+It should return a `dict` containing each item name paired with their respective quantity.
 
 ```python
 >>> create_inventory(["coal", "wood", "wood", "diamond", "diamond", "diamond"])
@@ -19,7 +23,7 @@ Implement the `create_inventory()` function that creates an "inventory" from a l
 
 ## 2. Add items from a list to an existing dictionary
 
-Implement the `add_items()` function that adds a list of items to an inventory:
+Implement the `add_items(<inventory dict>, <item list>)` function that adds a list of items to the passed-in inventory:
 
 ```python
 >>> add_items({"coal":1}, ["wood", "iron", "coal", "wood"])
@@ -28,23 +32,26 @@ Implement the `add_items()` function that adds a list of items to an inventory:
 
 ## 3. Decrement items from the inventory
 
-Implement the `decrement_items(<items>)` function that takes a `list` of items. The function should remove one from the available count in the inventory for each time an item appears on the `list`:
+Implement the `decrement_items(<inventory dict>, <items list>)` function that takes a `list` of items.
+Your function should remove `1` from an item count for each time that item appears on the `list`:
 
 ```python
 >>> decrement_items({"coal":3, "diamond":1, "iron":5}, ["diamond", "coal", "iron", "iron"])
 {"coal":2, "diamond":0, "iron":3}
 ```
 
-Item counts in the inventory should not fall below 0. If the number of times an item appears on the list exceeds the count available, the quantity listed for that item should remain at 0 and additional requests for removing counts should be ignored.
+Item counts in the inventory should not be allowed to fall below 0.
+ If the number of times an item appears on the input `list` exceeds the count available, the quantity listed for that item should remain at 0.
+ Additional requests for removing counts should be ignored once the count falls to zero.
 
 ```python
 >>> decrement_items({"coal":2, "wood":1, "diamond":2}, ["coal", "coal", "wood", "wood", "diamond"])
 {"coal":0, "wood":0, "diamond":1}
 ```
 
-## 4. Remove an item entirely from the inventory
+## 4. Remove an entry entirely from the inventory
 
-Implement the `remove_item(<inventory>, <item>)` function that removes an item and its count entirely from an inventory:
+Implement the `remove_item(<inventory dict>, <item>)` function that removes an item and its count entirely from an inventory:
 
 ```python
 >>> remove_item({"coal":2, "wood":1, "diamond":2}, "coal")
@@ -58,9 +65,10 @@ If the item is not found in the inventory, the function should return the origin
 {"coal":2, "wood":1, "diamond":2}
 ```
 
-## 5. Return the inventory content
+## 5. Return the entire content of the inventory
 
-Implement the `list_inventory()` function that takes an inventory and returns a list of `(item, quantity)` tuples. The list should only include the available items (with a quantity greater than zero):
+Implement the `list_inventory(<inventory dict>)` function that takes an inventory and returns a list of `(item, quantity)` tuples.
+The list should only include the _available_ items (_with a quantity greater than zero_):
 
 ```python
 >>> list_inventory({"coal":7, "wood":11, "diamond":2, "iron":7, "silver":0})
diff --git a/exercises/concept/inventory-management/.docs/introduction.md b/exercises/concept/inventory-management/.docs/introduction.md
index 1b4a15dc..2b9ef011 100644
--- a/exercises/concept/inventory-management/.docs/introduction.md
+++ b/exercises/concept/inventory-management/.docs/introduction.md
@@ -1,50 +1,79 @@
 # Introduction
 
-A _**dictionary**_ is Python's primary mapping type that associates a _hashable key_ with a value. The lookup by key is more efficient than searching through an array, but does require more memory.
+A dictionary (`dict`) in Python is a data structure that associates [hashable][term-hashable] _keys_ to _values_ and is known in other programming languages as a resizable [hash table][hashtable-wikipedia], hashmap, or [associative array][associative-array].
+Dictionaries are Python's only built-in [mapping type][mapping-types-dict].
 
-## Dict construction
 
-Dictionaries can be created in various ways. Two simple options are the use the `dict()` class constructor or the dict literal declaration with key-value pairs.
+`Keys` must be hashable and unique across the dictionary.
+Key types can include `numbers`, `str`, or `tuples` (of _immutable_ values).
+They cannot contain _mutable_ data structures such as `lists`, `dict`s, or `set`s.
+As of Python 3.7, `dict` key order is guaranteed to be the order in which entries are inserted.
 
-### Use the `dict()` constructor
+`values` can be of any data type or structure.
+ Values can also nest _arbitrarily_, so they can include lists-of-lists, sub-dictionaries, and other custom or compound data structures.
+
+Given a `key`, dictionaries can retrieve a `value` in (on average) constant time (_independent of the number of entries_).
+Compared to searching for a value within a `list` or `array` (_without knowing the `index` position_), a `dict` uses significantly more memory, but has very rapid retrieval.
+Dictionaries are especially useful in scenarios where the collection of items is large and must be accessed and updated frequently.
+
+
+## Dictionary Construction
+
+Dictionaries can be created in many ways.
+The two most straightforward are using the `dict()`constructor or declaring a `dict` _literal_.
+
+### The `dict()` Class Constructor
+
+`dict()` (_the constructor for the dictionary class_) can be used with any iterable of `key`, `value` pairs or with a series of `<name>=<value>` _arguments_:
 
 ```python
+#Passing a list of key,value tuples.
+>>> wombat = dict([('name', 'Wombat'),('speed', 23),('land_animal', True)])
+{'name': 'Wombat', 'speed': 23, 'land_animal': True}
+
+
+#Using key=value arguments.
 >>> bear = dict(name="Black Bear", speed=40, land_animal=True)
 {'name': 'Black Bear', 'speed': 40, 'land_animal': True}
 ```
 
-### Declare a _dict_ literal
+### Dictionary Literals
+
+A `dict` can also be directly entered as a _dictionary literal_, using curly brackets (`{}`) enclosing `key : value` pairs:
 
 ```python
 >>> whale = {"name": "Blue Whale", "speed": 35, "land_animal": False}
 {'name': 'Blue Whale', 'speed': 35, 'land_animal': False}
 ```
 
-With the dict literal declaration keep in mind that _keys_ are of _data types_ `str` and the colon `:` is used instead of an equal sign `=`.
+## Accessing Values in a Dictionary
 
-## Accessing values
-
-You can access an item in a dictionary using the _key_ of the value.
-
-### Using _square brackets_ after the dict object
+You can access an entry in a dictionary using a _key_ in square (`[]`) brackets.
+If a `key` does not exist n the `dict`, a `KeyError` is thrown:
 
 ```python
 >>> bear["speed"]
 40
+
+>>> bear["color"]
+Traceback (most recent call last):
+  File "<stdin>", line 1, in <module>
+KeyError: 'color'
 ```
 
-### Using `.get()`
+Accessing an entry via the `.get(<key>, <default value>)` method can avoid the `KeyError`:
 
 ```python
->>> whale.get("name")
-'Blue Whale'
+>>> bear.get("color", 'not found')
+'not found'
 ```
 
-## Changing values
+## Changing or Adding Dictionary Values
 
-You can easily change a value of an item using its _key_.
+You can change an entry `value` by assigning to its _key_:
 
 ```python
+#Assigning the value "Grizzly Bear" to the name key.
 >>> bear["name"] = "Grizzly Bear"
 {'name': 'Grizzly Bear', 'speed': 40, 'land_animal': True}
 
@@ -52,29 +81,71 @@ You can easily change a value of an item using its _key_.
 {'name': 'Blue Whale', 'speed': 25, 'land_animal': False}
 ```
 
-## Deleting values using keys
+New `key`:`value` pairs can be _added_ in the same fashion:
+
+```python
+# Adding an new "color" key with a new "tawney" value.
+>>> bear["color"] = 'tawney'
+{'name': 'Grizzly Bear', 'speed': 40, 'land_animal': True, 'color': 'tawney'}
 
-You can delete an item from a dictionary using `dict.pop(<key>)`. This will remove the (`key`, `value`) pair from the dictionary and return the `value` for use. `dict.pop(<key>)` accepts second argument, `default` that is returned if the `key` is not found  (`dict.pop(<key>, <default>)`). Otherwise, a `KeyError` will be raised for any `key` that is missing.
+>>> whale["blowholes"] = 1
+{'name': 'Blue Whale', 'speed': 25, 'land_animal': False, 'blowholes': 1}
+```
+
+## Removing (Pop-ing) Dictionary Entries
+
+You can use the `.pop(<key>)` method to delete a dictionary entry.
+`.pop()` removes the (`key`, `value`) pair and returns the `value` for use.
+Like `.get()`, `.pop(<key>)` accepts second argument (_`dict.pop(<key>, <default value>)`_) that will be returned if the `key` is not found.
+This prevents a `KeyError` being raised:
 
 ```python
+#Using .pop() removes both the key and value, returning the value.
 >>> bear.pop("name")
 'Grizzly Bear'
->>> bear.pop("name", "Unknown")
-'Unknown'
+
+
+#The "name" key is now removed from the dictionary.
+#Attempting .pop() a second time will throw a KeyError.
 >>> bear.pop("name")
 Traceback (most recent call last):
   File "<stdin>", line 1, in <module>
 KeyError: 'name'
+
+
+#Using a default argument with .pop() will prevent a KeyError from a missing key.
+>>> bear.pop("name", "Unknown")
+'Unknown'
 ```
 
-## Looping through a dictionary
+## Looping through/Iterating over a Dictionary
 
-Looping through a dictionary using `for item in dict` will iterate over the _keys_, but you can access the _values_ by using _square brackets_.
+Looping through a dictionary using `for item in dict` or `while item` will iterate over only the _keys _ by default.
+You can access the _values_ within the same loop by using _square brackets_:
 
 ```python
 >>> for key in bear:
->>>     (key, bear[key])
+>>>     print((key, bear[key])) #this forms a tuple of (key, value) and prints it.
 ('name', 'Black Bear')
 ('speed', 40)
 ('land_animal', True)
 ```
+
+You can also use the `.items()` method, which returns (`key`, `value`) tuples automatically:
+
+```python
+#dict.items() forms (key, value tuples) that can be unpacked and iterated over.
+>>> for key, value in whale.items():
+>>>     print(key, ":", value)
+name : Blue Whale
+speed : 25
+land_animal : False
+blowholes : 1
+```
+
+Likewise, the `.keys()` method will return `keys` and the `.values()` method will return the `values`.
+
+[associative-array]: https://en.wikipedia.org/wiki/Associative_array#:~:text=In%20computer%20science%2C%20an%20associative,a%20function%20with%20finite%20domain.
+[hashtable-wikipedia]: https://en.wikipedia.org/wiki/Hash_table
+[mapping-types-dict]: https://docs.python.org/3/library/stdtypes.html#mapping-types-dict
+[term-hashable]: https://docs.python.org/3/glossary.html#term-hashable
diff --git a/exercises/concept/mecha-munch-management/.docs/hints.md b/exercises/concept/mecha-munch-management/.docs/hints.md
new file mode 100644
index 00000000..9edd70ab
--- /dev/null
+++ b/exercises/concept/mecha-munch-management/.docs/hints.md
@@ -0,0 +1,56 @@
+# Hints
+
+## General
+
+Remember, this is an [MVP][mvp].
+That means you don't need to get too fancy with error handling or different "edge case" scenarios.
+It's OK to be simple and direct with the functions you are writing.
+
+The dictionary section of the [official tutorial][dicts-docs] and the mapping type [official library reference][mapping-types-dict] are excellent places to look for more help with all these methods.
+
+
+## 1. Add Item(s) to the Users Shopping Cart
+
+- You will need to iterate through each item in `items_to_add`.
+- You can avoid a `KeyError` when a key is missing by using a `dict` [method][set-default] that takes a _default value_ as one of its arguments.
+- It is also possible to accomplish the same thing manually in the `loop` by using some checking and error handling, but the `dict` method is easier.
+
+## 2. Read in Items Listed in the Users Notes App
+
+- Remember, Python's got a method for _everything_. This one is a _classmethod_ that's an easy way to [populate a `dict`][fromkeys] with keys.
+- This `dict` method returns a _new dictionary_, populated with default values. If no value is given, the default value will become `None`
+
+## 3. Update Recipe "Ideas" Section
+
+- Don't overthink this one!  This can be solved in **one** `dict` method call.
+- The key word here is .... [_update_][update].
+
+## 4. Sort the Items in the User Cart
+
+- What method would you call to get an [iterable view of items][items] in the dictionary?
+- If you had a `list` or a `tuple`, what [`built-in`][builtins] function might you use to sort them?
+- The built-in function you want is the one that returns a _copy_, and doesn't mutate the original.
+
+## 5. Send User Shopping Cart to Store for Fulfillment
+
+- Having a fresh, empty dictionary here as the `fulfillment_cart` might be handy for adding in items.
+- `Looping` through the members of the cart might be the most direct way of accessing things here.
+- What method would you call to get an [iterable view of just the keys][keys] of the dictionary?
+- Remember that you can get the `value` of a given key by using `<dict name>[<key_name>]` syntax.
+- If you had a `list` or a `tuple`, what [`built-in`][builtins] function might you use to sort them?
+- Remember that the `built-in` function can take an optional `reversed=true` argument.
+
+## 6. Update the Store Inventory to Reflect what a User Has Ordered.
+
+- There is a method that will give you an iterable view of (`key`, `value`) pairs from the dictionary.
+- You can access an item in a _nested tuple_ using _bracket notation_:  `<tuple>[<nested_tuple>][<index_of_value>]`
+- Don't forget to check if an inventory count falls to zero, you'll need to add in the "Out of Stock" message.
+
+[builtins]: https://docs.python.org/3/library/functions.html
+[dicts-docs]: https://docs.python.org/3/tutorial/datastructures.html#dictionaries
+[fromkeys]: https://docs.python.org/3/library/stdtypes.html#dict.fromkeys
+[items]: https://docs.python.org/3/library/stdtypes.html#dict.items
+[mapping-types-dict]: https://docs.python.org/3/library/stdtypes.html#mapping-types-dict
+[mvp]: https://en.wikipedia.org/wiki/Minimum_viable_product
+[set-default]: https://docs.python.org/3/library/stdtypes.html#dict.setdefault
+[update]: https://docs.python.org/3/library/stdtypes.html#dict.update
diff --git a/exercises/concept/mecha-munch-management/.docs/instructions.md b/exercises/concept/mecha-munch-management/.docs/instructions.md
new file mode 100644
index 00000000..f5fa6f79
--- /dev/null
+++ b/exercises/concept/mecha-munch-management/.docs/instructions.md
@@ -0,0 +1,129 @@
+# Instructions
+
+Mecha Munch™, a grocery shopping automation company has just hired you to work on their ordering app.
+Your team is tasked with building an MVP (_[minimum viable product][mvp]_) that manages all the basic shopping cart activities, allowing users to add, remove, and sort their grocery orders.
+Thankfully, a different team is handling all the money and check-out functions!
+
+## 1. Add Item(s) to the Users Shopping Cart
+
+The MVP should allow the user to add items to their shopping cart.
+This could be a single item or multiple items at once.
+Since this is an MVP, item quantity is indicated by _repeats_.
+If a user wants to add 2 Oranges, 'Oranges' will appear twice in the input iterable.
+If the user already has the item in their cart, the cart quantity should be increased by 1.
+If the item is _new_ to the cart, it should be added with a quantity of 1.
+
+Create the function `add_items(<current_cart>, <items_to_add>)` that takes a cart dictionary and any list-like iterable of items to add as arguments.
+It should return a new/updated shopping cart dictionary for the user.
+
+```python
+>>> add_items({'Banana': 3, 'Apple': 2, 'Orange': 1}, 
+              ('Apple', 'Apple', 'Orange', 'Apple', 'Banana'))
+{'Banana': 4, 'Apple': 5, 'Orange': 2}
+
+>>> add_items({'Banana': 3, 'Apple': 2, 'Orange': 1}, 
+              ['Banana', 'Orange', 'Blueberries', 'Banana'])
+{'Banana': 5, 'Apple': 2, 'Orange': 2, 'Blueberries': 1}
+```
+
+## 2. Read in Items Listed in the Users Notes App
+
+Uh-oh.
+Looks like the product team is engaging in [feature creep][feature creep].
+They want to add extra functionality to the MVP.
+The application now has to create a shopping cart by reading items off a users notes app.
+Convenient for the users, but slightly more work for the team.
+
+Create the function `read_notes(<notes>)` that can take any list-like iterable as an argument.
+The function should parse the items and create a user shopping cart/dictionary.
+Each item should be added with a quantity of 1.
+The new user cart should then be returned.
+
+```python
+>>> read_notes(('Banana','Apple', 'Orange'))
+{'Banana': 1, 'Apple': 1, 'Orange': 1}
+
+>>> read_notes(['Blueberries', 'Pear', 'Orange', 'Banana', 'Apple'])
+{'Blueberries' : 1, 'Pear' : 1, 'Orange' : 1, 'Banana' : 1, 'Apple' : 1}
+```
+
+## 3. Update Recipe "Ideas" Section
+
+The app has an "ideas" section that's filled with finished recipes from various cuisines.
+The user can select any one of these recipes and have all its ingredients added to their shopping cart automatically.
+The project manager has asked you create a way to edit these "ideas" recipes, since the content team keeps changing around ingredients and quantities.
+
+Create the function `update_recipes(<ideas>, <recipe_updates>)` that takes an "ideas" dictionary and an iterable of recipe updates as arguments.
+The function should return the new/updated "ideas" dictionary.
+
+```python
+>>> update_recipes({'Banana Bread' : {'Banana': 1, 'Apple': 1, 'Walnuts': 1, 'Flour': 1, 'Eggs': 2, 'Butter': 1},
+                    'Raspberry Pie' : {'Raspberry': 1, 'Orange': 1, 'Pie Crust': 1, 'Cream Custard': 1}},
+(('Banana Bread', {'Banana': 4,  'Walnuts': 2, 'Flour': 1, 'Eggs': 2, 'Butter': 1, 'Milk': 2, 'Eggs': 3}),))
+...
+
+{'Banana Bread' : {'Banana': 4,  'Walnuts': 2, 'Flour': 1, 'Eggs': 2, 'Butter': 1, 'Milk': 2, 'Eggs': 3},
+ 'Raspberry Pie' : {'Raspberry': 1, 'Orange': 1, 'Pie Crust': 1, 'Cream Custard': 1}}
+
+>>> update_recipes({'Banana Bread' : {'Banana': 1, 'Apple': 1, 'Walnuts': 1, 'Flour': 1, 'Eggs': 2, 'Butter': 1},
+                    'Raspberry Pie' : {'Raspberry': 1, 'Orange': 1, 'Pie Crust': 1, 'Cream Custard': 1},
+                    'Pasta Primavera': {'Eggs': 1, 'Carrots': 1, 'Spinach': 2, 'Tomatoes': 3, 'Parmesan': 2, 'Milk': 1, 'Onion': 1}},
+[('Raspberry Pie', {'Raspberry': 3, 'Orange': 1, 'Pie Crust': 1, 'Cream Custard': 1, 'Whipped Cream': 2}),
+('Pasta Primavera', {'Eggs': 1, 'Mixed Veggies': 2, 'Parmesan': 2, 'Milk': 1, 'Spinach': 1, 'Bread Crumbs': 1}),
+('Blueberry Crumble', {'Blueberries': 2, 'Whipped Creme': 2, 'Granola Topping': 2, 'Yogurt': 3})])
+...
+
+{'Banana Bread': {'Banana': 1, 'Apple': 1, 'Walnuts': 1, 'Flour': 1, 'Eggs': 2, 'Butter': 1},
+ 'Raspberry Pie': {'Raspberry': 3, 'Orange': 1, 'Pie Crust': 1, 'Cream Custard': 1, 'Whipped Cream': 2},
+ 'Pasta Primavera': {'Eggs': 1, 'Mixed Veggies': 2, 'Parmesan': 2, 'Milk': 1, 'Spinach': 1, 'Bread Crumbs': 1},
+ 'Blueberry Crumble': {'Blueberries': 2, 'Whipped Creme': 2, 'Granola Topping': 2, 'Yogurt': 3}}
+```
+
+## 4. Sort the Items in the User Cart
+
+Once a user has started a cart, the app allows them to sort their items alphabetically.
+This makes things easier to find, and helps when there are data-entry errors like having 'potatoes' and 'Potato' in the database.
+
+Create the function `sort_entries(<cart>)` that takes a shopping cart/dictionary as an argument and returns a new, alphabetically sorted one.
+
+```python
+>>> sort_entries({'Banana': 3, 'Apple': 2, 'Orange': 1})
+{'Apple': 2, 'Banana':3, 'Orange': 1}
+```
+
+## 5. Send User Shopping Cart to Store for Fulfillment
+
+The app needs to send a given users cart to the store for fulfillment.
+However, the shoppers in the store need to know which store isle the item can be found in and if the item needs refrigeration.
+So (_rather arbitrarily_) the "fulfillment cart" needs to be sorted in reverse alphabetical order with item quantities combined with location and refrigeration information.
+
+Create the function `send_to_store(<cart>, <isle_mapping>)` that takes a user shopping cart and a dictionary that has store isle number and a `True`/`False` for refrigeration needed for each item.
+The function should `return` a combined "fulfillment cart" that has (quantity, isle, and refrigeration) for each item the customer is ordering.
+Items should appear in _reverse_ alphabetical order.
+
+```python
+>>> send_to_store({'Banana': 3, 'Apple': 2, 'Orange': 1, 'Milk': 2}, 
+                  {'Banana': ['Isle 5', False], 'Apple': ['Isle 4', False], 'Orange': ['Isle 4', False], 'Milk': ['Isle 2', True]})
+{'Orange': [1, 'Isle 4', False], 'Milk': [2, 'Isle 2', True], 'Banana': [3, 'Isle 5', False], 'Apple': [2, 'Isle 4', False]}
+```
+
+## 6. Update the Store Inventory to Reflect what a User Has Ordered.
+
+The app can't just place customer orders endlessly.
+Eventually, the store is going to run out of various products.
+So your app MVP needs to update the store inventory every time a user sends their order to the store.
+Otherwise, customers will order products that aren't actually available.
+
+Create the function `update_store_inventory(<fulfillment_cart>, <store_inventory>)` that takes a "fulfillment cart" and a store inventory.
+The function should reduce the store inventory amounts by the number "ordered" in the "fulfillment cart" and then return the updated store inventory.
+Where a store item count falls to 0, the count should be replaced by the message 'Out of Stock'.
+
+```python
+>>> update_store_inventory({'Orange': [1, 'Isle 4', False], 'Milk': [2, 'Isle 2', True], 'Banana': [3, 'Isle 5', False], 'Apple': [2, 'Isle 4', False]}, 
+{'Banana': [15, 'Isle 5', False], 'Apple': [12, 'Isle 4', False], 'Orange': [1, 'Isle 4', False], 'Milk': [4, 'Isle 2', True]})
+
+{'Banana': [12, 'Isle 5', False], 'Apple': [10, 'Isle 4', False], 'Orange': ['Out of Stock', 'Isle 4', False], 'Milk': [2, 'Isle 2', True]}
+```
+
+[feature creep]: https://en.wikipedia.org/wiki/Feature_creep
+[mvp]: https://en.wikipedia.org/wiki/Minimum_viable_product
diff --git a/exercises/concept/mecha-munch-management/.docs/introduction.md b/exercises/concept/mecha-munch-management/.docs/introduction.md
new file mode 100644
index 00000000..f1c5744e
--- /dev/null
+++ b/exercises/concept/mecha-munch-management/.docs/introduction.md
@@ -0,0 +1,221 @@
+# Dictionary Methods in Python
+
+The `dict` class in Python provides many useful [methods][dict-methods] for working with dictionaries.
+Some were introduced in the concept for `dicts`.
+Here we cover a few more - along with some techniques for iterating through and manipulating dictionaries.
+
+### Use `setdefault()` for Error-Free Insertion
+
+The dictionary concept previously covered that `.get(key, <default value>)` returns an existing `value` or the `default value` if a `key` is not found in a dictionary, thereby avoiding a `KeyError`.
+This works well in situations where you would rather not have extra error handling but cannot trust that a looked-for `key` will be present.
+
+For a similarly "safe" (_without KeyError_) insertion operation, there is the `.setdefault(key, <default value>)` method.
+`setdefault(key, <default value>)` will return the `value` if the `key` is found in the dictionary.
+If the key is **not** found, it will _insert_ the (`key`, `default value`) pair and return the `default value` for use.
+
+```python
+>>> palette_I = {'Grassy Green': '#9bc400', 'Purple Mountains Majesty': '#8076a3', 'Misty Mountain Pink': '#f9c5bd'}
+
+# Looking for the value associated with key "Rock Brown".
+# The key does not exist, so it is added with the default value, and the value is returned.
+>>> palette.setdefault('Rock Brown', '#694605')
+'#694605'
+
+# The (key, default value) pair has now been added to the dictionary.
+>>> palette_I
+{'Grassy Green': '#9bc400', 'Purple Mountains Majesty': '#8076a3', 'Misty Mountain Pink': '#f9c5bd', 'Rock Brown': '#694605'}
+```
+
+## Use `fromkeys()` to Populate a Dictionary from an Iterable
+
+To quickly populate a dictionary with various `keys` and default values, the _class method_ [`fromkeys(iterable, <default value>)`][fromkeys] will iterate through an iterable of `keys` and create a new `dict`.
+All `values` will be set to the `default value` provided:
+
+```python
+>>> new_dict = dict.fromkeys(['Grassy Green', 'Purple Mountains Majesty', 'Misty Mountain Pink'], 'fill in hex color here')
+
+{'Grassy Green': 'fill in hex color here',
+ 'Purple Mountains Majesty': 'fill in hex color here',
+ 'Misty Mountain Pink': 'fill in hex color here'}
+```
+
+## Iterating Over Entries in a Dictionary Via Views
+
+The `.keys()`, `.values()`, and `.items()` methods return [_iterable views_][dict-views] of a dictionary.
+
+These views can be used to easily loop over entries without altering them.
+Views are also _dynamic_ -- when underlying dictionary data changes, the associated `view object` will reflect the change:
+
+```python
+>>> palette_I = {'Grassy Green': '#9bc400',
+                 'Purple Mountains Majesty': '#8076a3',
+                  'Misty Mountain Pink': '#f9c5bd'}
+
+# Using .keys() returns a list of keys.
+>>> palette_I.keys()
+dict_keys(['Grassy Green', 'Purple Mountains Majesty', 'Misty Mountain Pink'])
+
+# Using .values() returns a list of values.
+>>> palette_I.values()
+dict_values(['#9bc400', '#8076a3', '#f9c5bd'])
+
+# Using .items() returns a list of (key, value) tuples.
+>>> palette_I.items()
+dict_items([('Grassy Green', '#9bc400'), ('Purple Mountains Majesty', '#8076a3'), ('Misty Mountain Pink', '#f9c5bd')])
+
+# Views are dynamic.  Changing values in the dict changes all of the associated views.
+>>> palette_I['Purple Mountains Majesty'] = (128, 118, 163)
+>>> palette_I['Deep Red'] = '#932432'
+
+>>> palette_I.values()
+dict_values(['#9bc400', (128, 118, 163), '#f9c5bd', '#932432'])
+
+>>> palette_I.keys()
+dict_keys(['Grassy Green', 'Purple Mountains Majesty', 'Misty Mountain Pink', 'Deep Red'])
+
+>>> palette_I.items()
+dict_items([('Grassy Green', '#9bc400'), ('Purple Mountains Majesty', (128, 118, 163)), ('Misty Mountain Pink', '#f9c5bd'), ('Deep Red', '#932432')])
+```
+
+## More on `.keys()`, `.values()`, and `.items()`
+
+In Python 3.7+, `dicts` preserve the order in which entries are inserted allowing First-in, First-out (_`FIFO`_),  iteration when using `.keys()`, `.values()`, or `.items()`.
+
+In Python 3.8+, views are also _reversible_.
+This allows keys, values, or (`key`, `value`) pairs to be iterated over in Last-in, First-out (`LIFO`) order by using `reversed(<dict>.keys())`, `reversed(<dict>.values())`, or `reversed(<dict>.items())`:
+
+```python
+>>> palette_II = {'Factory Stone Purple': '#7c677f', 'Green Treeline': '#478559', 'Purple baseline': '#161748'}
+
+# Iterating in insertion order 
+>>> for item in palette_II.items():
+...     print(item)
+...
+('Factory Stone Purple', '#7c677f')
+('Green Treeline', '#478559')
+('Purple baseline', '#161748')
+
+
+# Iterating in the reverse direction.
+>>> for item in reversed(palette_II.items()):
+...    print (item)
+...
+('Purple baseline', '#161748')
+('Green Treeline', '#478559')
+('Factory Stone Purple', '#7c677f')
+```
+
+## Sorting a Dictionary
+
+Dictionaries do not have a built-in sorting method.
+However, it is possible to sort a `dict` _view_ using the built-in function `sorted()` with `.items()`.
+The sorted view can then be used to create a new dictionary.
+Like iteration, the default sort is over dictionary `keys`.
+
+```python
+# Default ordering for a dictionary is last in, first out (LIFO).
+>>> color_palette = {'Grassy Green': '#9bc400', 
+                    'Purple Mountains Majesty': '#8076a3', 
+                    'Misty Mountain Pink': '#f9c5bd', 
+                    'Factory Stone Purple': '#7c677f', 
+                    'Green Treeline': '#478559', 
+                    'Purple baseline': '#161748'}
+ 
+ 
+# The default sort order for a dictionary uses the keys.
+>>> sorted_palette = dict(sorted(color_palette.items()))
+>>> sorted_palette
+{'Factory Stone Purple': '#7c677f',
+ 'Grassy Green': '#9bc400',
+ 'Green Treeline': '#478559',
+ 'Misty Mountain Pink': '#f9c5bd',
+ 'Purple Mountains Majesty': '#8076a3',
+ 'Purple baseline': '#161748'}
+```
+
+## Combining Dictionaries with `.update()`
+
+`<dict_one>.update(<dict_two>)` can be used to _combine_ two dictionaries.
+This method will take the (`key`,`value`) pairs of `<dict_two>` and write them into `<dict_one>`:
+
+```python
+>>> palette_I = {'Grassy Green': '#9bc400',
+                 'Purple Mountains Majesty': '#8076a3',
+                  'Misty Mountain Pink': '#f9c5bd'}
+>>> palette_II = {'Factory Stone Purple': '#7c677f',
+                  'Green Treeline': '#478559',
+                  'Purple Baseline': '#161748'}
+
+>>> palette_I.update(palette_II)
+
+# Note that new items from palette_II are added.
+>>> palette_I
+{'Grassy Green': '#9bc400', 'Purple Mountains Majesty': '#8076a3', 'Misty Mountain Pink': '#f9c5bd', 'Factory Stone Purple': '#7c677f', 'Green Treeline': '#478559', 'Purple Baseline': '#161748'}
+```
+
+Where keys in the two dictionaries _overlap_, the `value` in `dict_one` will be _overwritten_ by the corresponding `value` from `dict_two`:
+
+```python
+>>> palette_I =   {'Grassy Green': '#9bc400', 'Purple Mountains Majesty': '#8076a3', 'Misty Mountain Pink': '#f9c5bd', 
+                   'Factory Stone Purple': '#7c677f', 'Green Treeline': '#478559', 'Purple baseline': '#161748'}
+>>> palette_III = {'Grassy Green': (155, 196, 0), 'Purple Mountains Majesty': (128, 118, 163),
+                   'Misty Mountain Pink': (249, 197, 189)}
+>>> palette_I.update(palette_III)
+
+# Overlapping values in palette_I are replaced with values from palette_III
+>>> palette_I
+{'Grassy Green': (155, 196, 0),
+  'Purple Mountains Majesty': (128, 118, 163), 
+  'Misty Mountain Pink': (249, 197, 189), 
+  'Factory Stone Purple': '#7c677f', 
+  'Green Treeline': '#478559', 'Purple baseline': '#161748'}
+```
+
+## Merge or Update Dictionaries Via the Union (`|`) Operators
+
+Python 3.9 introduces a different means of merging `dicts`: the `union` operators.
+`dict_one | dict_two` will create a **new dictionary**, made up of the (`key`, `value`) pairs of `dict_one` and `dict_two`.
+When both dictionaries share keys, `dict_two` values take precedence.
+
+```python
+>>> palette_I = {'Grassy Green': '#9bc400', 'Purple Mountains Majesty': '#8076a3', 'Misty Mountain Pink': '#f9c5bd'}
+>>> palette_II = {'Factory Stone Purple': '#7c677f', 'Green Treeline': '#478559', 'Purple baseline': '#161748'}
+>>> new_dict = palette_I | palette_II
+>>> new_dict
+...
+{'Grassy Green': '#9bc400',
+ 'Purple Mountains Majesty': '#8076a3',
+ 'Misty Mountain Pink': '#f9c5bd',
+ 'Factory Stone Purple': '#7c677f',
+ 'Green Treeline': '#478559',
+ 'Purple baseline': '#161748'}
+```
+
+`dict_one |= other` behaves similar to `<dict_one>.update(<dict_two>)`, but in this case, `other` can be either a `dict` or an iterable of (`key`, `value`) pairs:
+
+```python
+>>> palette_III = {'Grassy Green': (155, 196, 0),
+                   'Purple Mountains Majesty': (128, 118, 163),
+                   'Misty Mountain Pink': (249, 197, 189)}
+>>> new_dict |= palette_III
+>>> new_dict
+...
+{'Grassy Green': (155, 196, 0),
+'Purple Mountains Majesty': (128, 118, 163),
+'Misty Mountain Pink': (249, 197, 189),
+'Factory Stone Purple': '#7c677f',
+'Green Treeline': '#478559',
+'Purple baseline': '#161748'}
+```
+
+For a detailed explanation of dictionaries and methods for working with them, the [official tutorial][dicts-docs] and the [official library reference][mapping-types-dict] are excellent starting places.
+
+[Real Python][how-to-dicts] and [Finxter][fi-dict-guide] also have very thorough articles on Python dictionaries.
+
+[dict-methods]: https://docs.python.org/3/library/stdtypes.html#dict
+[dict-views]: https://docs.python.org/3/library/stdtypes.html#dict-views
+[dicts-docs]: https://docs.python.org/3/tutorial/datastructures.html#dictionaries
+[fi-dict-guide]: https://blog.finxter.com/python-dictionary
+[fromkeys]: https://docs.python.org/3/library/stdtypes.html#dict.fromkeys
+[how-to-dicts]: https://www.w3schools.com/python/python_dictionaries.asp
+[mapping-types-dict]: https://docs.python.org/3/library/stdtypes.html#mapping-types-dict
diff --git a/exercises/concept/mecha-munch-management/.meta/config.json b/exercises/concept/mecha-munch-management/.meta/config.json
new file mode 100644
index 00000000..f09d0f29
--- /dev/null
+++ b/exercises/concept/mecha-munch-management/.meta/config.json
@@ -0,0 +1,21 @@
+{
+  "authors": [
+    "meatball133",
+    "BethanyG"
+  ],
+  "contributors": [
+  ],
+  "files": {
+    "solution": [
+      "dict_methods.py"
+    ],
+    "test": [
+      "dict_methods_test.py"
+    ],
+    "exemplar": [
+      ".meta/exemplar.py"
+    ]
+  },
+  "icon": "gross-store",
+  "blurb": "Learn about dictionary methods by building a shopping cart MVP for the Mecha Munch grocery app."
+}
diff --git a/exercises/concept/mecha-munch-management/.meta/design.md b/exercises/concept/mecha-munch-management/.meta/design.md
new file mode 100644
index 00000000..4c3c52a0
--- /dev/null
+++ b/exercises/concept/mecha-munch-management/.meta/design.md
@@ -0,0 +1,59 @@
+## Learning objectives
+
+Cover useful `dict` methods and a few techniques for operating on/manipulating `dicts`.
+
+- `dict.setdefault()` for automatically adding keys when needed.
+- `dict.fromkeys(iterable, <default value>)` for creating a new `dict` from any number of iterables.
+- `dict.keys()`, `dict.values()`, and `dict.items()` for convenient iterators.
+- `reversed(dict.keys())`, `reversed(dict.values())`, or `reversed(dict.items())` for reversed views.
+- `sorted()` with `dict.items()`. for re-ordering entries in a `dict`.
+- `dict_one.update(<dict_two>)` for updating one `dict` with overlapping values from another `dict`.
+- `dict | other_dict` and `dict |= other_dict` for merging or updating two `dict`s via operators.
+- `dict.popitem()` for removing and returning a key, value pair.
+
+- Working more with  the `dict` views `items()` , `keys()` or `values()`.  (e.g, by sorting information using `sorted()` or by swapping `keys` and `values`, etc.)
+- Knowing that Dictionaries can be _nested_, _-- e.g._ ' a dictionary of dictionaries'.
+- Considerations when `updating()` or using `union` with dictionaries.
+
+## Out of scope
+
+Please take a look at the `dicts` concept exercise [design.md file](https://github.com/exercism/python/edit/main/exercises/concept/inventory-management/.meta/design.md) for `dict` features  taught thus far.
+While those methods can be used for solutions to this exercise, it  isn't necessary to cover them again in detail.  Additionally, the following is out of scope:
+
+- Dictionary comprehensions
+- Built-in functions as they relate to this data structure (*e.g.* `len()`, or `enumerate()`
+- Considerations of Mutability
+- `copy()` vs `deepcopy()`
+- Memory and performance characteristics.
+- Related `collections` module with `Counter()` and `defaultdict()`
+
+## Concepts
+
+- `dicts`
+- `dict-methods`
+
+## Prerequisites
+
+These are the concepts/concept exercises the student needs to complete/understand before solving this concept exercise.
+
+- `basics`
+- `bools`
+- `conditionals`
+- `comparisons`
+- `dicts`
+- `lists`
+- `loops`
+- `numbers`
+- `strings`
+- `tuples`
+
+
+## Resources to refer to
+
+- [Python docs:  Tutorial - Dictionaries](https://docs.python.org/3/tutorial/datastructures.html#dictionaries)
+- [Python docs:  Mapping Type `dict`](https://docs.python.org/3/library/stdtypes.html#mapping-types-dict)
+- [Real Python:  Dicts](https://realpython.com/python-dicts/)
+- [Digital Ocean:  Understanding dictionaries in python 3](https://www.digitalocean.com/community/tutorials/understanding-dictionaries-in-python-3)
+- [Stack Overflow: exchanging keys with values in a `dict` in Python](https://stackoverflow.com/questions/1031851/how-do-i-exchange-keys-with-values-in-a-dictionary)
+- [kite:  how to sort a dictionary by key in python](https://www.kite.com/python/answers/how-to-sort-a-dictionary-by-key-in-python)
+- [medium:  16 Python Dictionary Tips](https://medium.com/python-in-plain-english/16-intermediate-level-python-dictionary-tips-tricks-and-shortcuts-1376859e1adc)  _**note:** this is a good resource for ideas and writing this exericse, but is a subscription-based service, so not the best for linking to_
\ No newline at end of file
diff --git a/exercises/concept/mecha-munch-management/.meta/exemplar.py b/exercises/concept/mecha-munch-management/.meta/exemplar.py
new file mode 100644
index 00000000..0390944d
--- /dev/null
+++ b/exercises/concept/mecha-munch-management/.meta/exemplar.py
@@ -0,0 +1,79 @@
+"""Functions to manage a users shopping cart items."""
+
+
+def add_item(current_cart, items_to_add):
+    """Add items to shopping cart.
+
+    :param current_cart: dict - the current shopping cart.
+    :param items_to_add: iterable - items to add to the cart.
+    :return: dict - the updated user cart dictionary.
+    """
+
+    for item in items_to_add:
+        current_cart.setdefault(item, 0)
+        current_cart[item] += 1
+
+    return current_cart
+
+
+def read_notes(notes):
+    """Create user cart from an iterable notes entry.
+
+    :param notes: iterable of items to add to cart.
+    :return: dict - a user shopping cart dictionary.
+    """
+
+    return dict.fromkeys(notes, 1)
+
+
+def update_recipes(ideas, recipe_updates):
+    """Update the recipe ideas dictionary.
+
+    :param ideas: dict - The "recipe ideas" dict.
+    :param recipe_updates: dict - dictionary with updates for the ideas section.
+    :return: dict - updated "recipe ideas" dict.
+    """
+
+    ideas.update(recipe_updates)
+    return ideas
+
+
+def sort_entries(cart):
+    """Sort a users shopping cart in alphabetically order.
+
+    :param cart: dict - a users shopping cart dictionary.
+    :return: dict - users shopping cart sorted in alphabetical order.
+    """
+
+    return dict(sorted(cart.items()))
+
+
+def send_to_store(cart, isle_mapping):
+    """Combine users order to isle and refrigeration information.
+
+    :param cart: dict - users shopping cart dictionary.
+    :param isle_mapping: dict - isle and refrigeration information dictionary.
+    :return: dict - fulfillment dictionary ready to send to store.
+    """
+    fulfillment_cart = {}
+
+    for key in cart.keys():
+        fulfillment_cart[key] = [cart[key]] + isle_mapping[key]
+
+    return dict(sorted(fulfillment_cart.items(), reverse=True))
+
+
+def update_store_inventory(fulfillment_cart, store_inventory):
+    """Update store inventory levels with user order.
+
+    :param fulfillment cart: dict - fulfillment cart to send to store.
+    :param store_inventory: dict - store available inventory
+    :return: dict - store_inventory updated.
+    """
+
+    for key, values in fulfillment_cart.items():
+        store_inventory[key][0] = store_inventory[key][0] - values[0]
+        if store_inventory[key][0] == 0:
+            store_inventory[key][0] = 'Out of Stock'
+
+    return store_inventory
diff --git a/exercises/concept/mecha-munch-management/dict_methods.py b/exercises/concept/mecha-munch-management/dict_methods.py
new file mode 100644
index 00000000..d443c8bc
--- /dev/null
+++ b/exercises/concept/mecha-munch-management/dict_methods.py
@@ -0,0 +1,65 @@
+"""Functions to manage a users shopping cart items."""
+
+
+def add_item(current_cart, items_to_add):
+    """Add items to shopping cart.
+
+    :param current_cart: dict - the current shopping cart.
+    :param items_to_add: iterable - items to add to the cart.
+    :return: dict - the updated user cart dictionary.
+    """
+
+    pass
+
+
+def read_notes(notes):
+    """Create user cart from an iterable notes entry.
+
+    :param notes: iterable of items to add to cart.
+    :return: dict - a user shopping cart dictionary.
+    """
+
+    pass
+
+
+def update_recipes(ideas, recipe_updates):
+    """Update the recipe ideas dictionary.
+
+    :param ideas: dict - The "recipe ideas" dict.
+    :param recipe_updates: dict - dictionary with updates for the ideas section.
+    :return: dict - updated "recipe ideas" dict.
+    """
+
+    pass
+
+
+def sort_entries(cart):
+    """Sort a users shopping cart in alphabetically order.
+
+    :param cart: dict - a users shopping cart dictionary.
+    :return: dict - users shopping cart sorted in alphabetical order.
+    """
+
+    pass
+
+
+def send_to_store(cart, isle_mapping):
+    """Combine users order to isle and refrigeration information.
+
+    :param cart: dict - users shopping cart dictionary.
+    :param isle_mapping: dict - isle and refrigeration information dictionary.
+    :return: dict - fulfillment dictionary ready to send to store.
+    """
+
+    pass
+
+
+def update_store_inventory(fulfillment_cart, store_inventory):
+    """Update store inventory levels with user order.
+
+    :param fulfillment cart: dict - fulfillment cart to send to store.
+    :param store_inventory: dict - store available inventory
+    :return: dict - store_inventory updated.
+    """
+
+    pass
diff --git a/exercises/concept/plane-tickets/.docs/hints.md b/exercises/concept/plane-tickets/.docs/hints.md
index 72d814b2..11508ee3 100644
--- a/exercises/concept/plane-tickets/.docs/hints.md
+++ b/exercises/concept/plane-tickets/.docs/hints.md
@@ -1,11 +1,24 @@
 # Hints
 
-## 1. Generate an amount of seats
+## 1. Generate seat letters
+
+- The returned value should be of _type_ `generator`.
+- You can have a sequence of letters from `A` to `D` and cycle through them.
+- And use `yield` to return the next letter.
+
+## 2. Generate an amount of seats
 
 - The returned value should be of _type_ `generator`.
 - Row `13` should be skipped, so go from `12` to `14`.
 - Keep in mind that the returned values should be ordered from low to high. `1A, 1B, 2A, ...`
+- It might be good to reuse or call other functions you have already completed here.
 
-## 2. Assign seats to passengers
+## 3. Assign seats to passengers
 
 - Make sure your seat numbers do not have any space in them.
+- It might be good to reuse or call other functions you have already completed here.
+
+## 4. Ticket codes
+
+- You can use `len()` to get the length of a string.
+- You can use `"<string>" * <int>` to repeat a string.
diff --git a/exercises/concept/plane-tickets/.docs/instructions.md b/exercises/concept/plane-tickets/.docs/instructions.md
index 290aad2e..b22a84f2 100644
--- a/exercises/concept/plane-tickets/.docs/instructions.md
+++ b/exercises/concept/plane-tickets/.docs/instructions.md
@@ -2,32 +2,51 @@
 
 Conda airlines is the programming-world's biggest airline, with over 10.000 flights a day!
 
-They are currently assigning all seats to passengers by hand, this will need to automated.
+They are currently assigning all seats to passengers by hand, this will need to be automated.
 
-They have asked _you_ to create software to automate the assigning of seats to passengers. They require your software to be memory efficient and performant.
+They have asked _you_ to create software to automate the assigning of seats to passengers.
+They require your software to be memory efficient and performant.
 
-Conda's airplanes have up to _4 seats_ in each row, and each airplane has many rows.
+## 1. Generate seat letters
 
-While the rows are defined using numbers, seats in each row are defined using letters from the alphabet, with `seat A` being the first _seat_ in the row.
+Conda wants to generate seat letters for their airplanes.
+An airplane is made of rows of seats.
+Each row has _4 seats_.
+The rows seats has the same naming: `A`, `B`, `C`, `D`.
+Meaning the first seat in the row is `A`, the second seat in the row is `B`, and so on.
+After reaching `D` it should start again with `A`.
 
-You can use this table as a guide:
+Implement a function `generate_seat_letters()` that accepts an `int` that holds how many seat letters to be generated.
+The function should then return an _iterable_ of seat letters.
 
-| x             | 1         | 2     |
-| :----:        | :----:    | :----:|
-| Row           | 5         | 21    |
-| Seat letter   | A         | D     |
-| Result        | 5A        | 21D   |
+```python
+>>> letters = generate_seat_letters(4)
+>>> next(letters)
+"A"
+>>> next(letters)
+"B"
+```
 
-## 1. Generate an amount of seats
+## 2. Generate an amount of seats
 
-Implement the `generate_seats()` function that returns an _iterable_ of seats given the following variable:
+Conda wants a system that can generate an amount of seats for their airplanes.
+Each airplane has _4 seats_ in each row.
+The rows are defined using numbers, starting from `1` and going up.
+The seats should be ordered, like: `1A`, `1B`, `1C`, `1D`, `2A`, `2B`, `2C`, `2D`, `3A`, `3B`, `3C`, `3D`, ...
 
-`amount`: The amount of seats to be generated.
+Here is an example:
+
+|      x      |  1  |  2  |
+| :---------: | :-: | :-: |
+|     Row     |  5  | 21  |
+| Seat letter |  A  |  D  |
+|   Result    | 5A  | 21D |
 
 Many airlines do not have _row_ number 13 on their flights, due to superstition amongst passengers.
 Conda Airlines also follows this convention, so make sure you _don't_ generate seats for _row_ number 13.
 
-_Note: The returned seats should be ordered, like: 1A 1B 1C._
+Implement a function `generate_seats()` that accepts an `int` that holds how many seats to be generated.
+The function should then return an _iterable_ of seats given.
 
 ```python
 >>> seats = generate_seats(10)
@@ -37,29 +56,32 @@ _Note: The returned seats should be ordered, like: 1A 1B 1C._
 "1B"
 ```
 
-## 2. Assign seats to passengers
+## 3. Assign seats to passengers
 
-Implement the `assign_seats()` function that returns a _dictionary_ of `passenger` as _key_, and `seat_number` as _value_. Given is the following _list_:
+Now that you have a function that generates seats, you can use it to assign seats to passengers.
 
-`passengers`: A list containing passenger names.
+Implement a function `assign_seats()` that accepts a `list` of passenger names.
+The function should then return a _dictionary_ of `passenger` as _key_, and `seat_number` as _value_.
 
 ```python
->>> passengers = ['Jerimiah', 'Eric', 'Bethaney', 'Byte', 'SqueekyBoots', 'Bob']
+>>> passengers = ['Jerimiah', 'Eric', 'Bethany', 'Byte', 'SqueekyBoots', 'Bob']
 
 >>> assign_seats(passengers)
-{'Jerimiah': '1A', 'Eric': '1B', 'Bethaney': '1C', 'Byte': '1D', 'SqueekyBoots': '2A', 'Bob': '2B'}
+{'Jerimiah': '1A', 'Eric': '1B', 'Bethany': '1C', 'Byte': '1D', 'SqueekyBoots': '2A', 'Bob': '2B'}
 ```
 
-## 3. Ticket codes
-
-Each ticket has a _12_ character long string code for identification.
+## 4. Ticket codes
 
-This code begins with the `assigned_seat` followed by the `flight_id`. The rest of the code is appended by `0s`.
+Conda Airlines would like to have a unique code for each ticket.
+Since they are a big airline, they have a lot of flights.
+Meaning that there are multiple flights with the same seat number.
+They want you to create a system that creates a unique ticket that is _12_ characters long string code for identification.
 
-Implement a `generator` that yields a `ticket_number` given the following arguments:
+This code begins with the `assigned_seat` followed by the `flight_id`.
+The rest of the code is appended by `0s`.
 
-`seat_numbers`: A _list_ of *seat_numbers*.
-`flight_id`: A string containing the flight identification.
+Implement a function `generate_codes()` that accepts a `list` of `seat_numbers` and a `string` with the flight number.
+The function should then return a `generator` that yields a `ticket_number`.
 
 ```python
 >>> seat_numbers = ['1A', '17D']
diff --git a/exercises/concept/plane-tickets/.docs/introduction.md b/exercises/concept/plane-tickets/.docs/introduction.md
index 1b557b44..5ab9d6d2 100644
--- a/exercises/concept/plane-tickets/.docs/introduction.md
+++ b/exercises/concept/plane-tickets/.docs/introduction.md
@@ -1,5 +1,135 @@
-# Introduction
+# About
 
-A generator in Python is a _callable function_ that returns a [lazy iterator](https://en.wikipedia.org/wiki/Lazy_evaluation).
+## Constructing a generator
 
-_Lazy iterators_ are similar to iterables such as `lists`, and other types of `iterators` in Python -- but with one key difference: `generators` do not store their `values` in memory, but _generate_ their values as needed or when called.
+Generators are constructed much like other looping or recursive functions, but require a [`yield` expression](#the-yield-expression), which we will explore in depth a bit later.
+
+An example is a function that returns the _squares_ from a given list of numbers.  
+As currently written, all input must be processed before any values can be returned:
+
+```python
+>>> def squares(list_of_numbers):
+...     squares = []
+...     for number in list_of_numbers:
+...         squares.append(number ** 2)
+...     return squares
+```
+
+You can convert that function into a generator like this:
+
+```python
+>>> def squares_generator(list_of_numbers):
+...     for number in list_of_numbers:
+...         yield number ** 2
+```
+
+The rationale behind this is that you use a generator when you do not need all the values _at once_.
+
+This saves memory and processing power, since only the value you are _currently working on_ is calculated.
+
+## Using a generator
+
+Generators may be used in place of most `iterables` in Python. This includes _functions_ or _objects_ that require an `iterable`/`iterator` as an argument.
+
+To use the `squares_generator()` generator:
+
+```python
+>>> squared_numbers = squares_generator([1, 2, 3, 4])
+
+>>> for square in squared_numbers:
+...     print(square)
+...
+1
+4
+9
+16
+```
+
+Values within a generator can also be produced/accessed via the `next()` function.
+`next()` calls the `__next__()` method of a generator object, "advancing" or evaluating the generator code up to its `yield` expression, which then "yields" or returns the value.
+
+```python
+>>> squared_numbers = squares_generator([1, 2])
+
+>>> next(squared_numbers)
+1
+>>> next(squared_numbers)
+4
+```
+
+When a `generator` is fully consumed and has no more values to return, it throws a `StopIteration` error.
+
+```python
+>>> next(squared_numbers)
+Traceback (most recent call last):
+  File "<stdin>", line 1, in <module>
+StopIteration
+```
+
+### Difference between iterables and generators
+
+Generators are a special sub-set of _iterators_.
+`Iterators` are the mechanism/protocol that enables looping over _iterables_.
+Generators and the iterators returned by common Python [`iterables`][iterables] act very similarly, but there are some important differences to note:
+
+- Generators are _one-way_; there is no "backing up" to a previous value.
+
+- Iterating over generators consume the returned values; no resetting.
+
+- Generators (_being lazily evaluated_) are not sortable and can not be reversed.
+
+- Generators do _not_ have `indexes`, so you can't reference a previous or future value using addition or subtraction.
+
+- Generators cannot be used with the `len()` function.
+
+- Generators can be _finite_ or _infinite_, be careful when collecting all values from an _infinite_ generator.
+
+## The yield expression
+
+The [yield expression][yield expression] is very similar to the `return` expression.
+
+_Unlike_ the `return` expression, `yield` gives up values to the caller at a _specific point_, suspending evaluation/return of any additional values until they are requested.
+
+When `yield` is evaluated, it pauses the execution of the enclosing function and returns any values of the function _at that point in time_.
+
+The function then _stays in scope_, and when `__next__()` is called, execution resumes until `yield` is encountered again.
+
+```exercism/note
+Using `yield` expressions is prohibited outside of functions.
+```
+
+```python
+>>> def infinite_sequence():
+...     current_number = 0
+...     while True:
+...         yield current_number
+...         current_number += 1
+
+>>> lets_try = infinite_sequence()
+>>> lets_try.__next__()
+0
+>>> lets_try.__next__()
+1
+```
+
+## Why generators?
+
+Generators are useful in a lot of applications.
+
+When working with a large collection, you might not want to put all of its values into `memory`.
+A generator can be used to work on larger data piece-by-piece, saving memory and improving performance.
+
+Generators are also very helpful when a process or calculation is _complex_, _expensive_, or _infinite_:
+
+```python
+>>> def infinite_sequence():
+...     current_number = 0
+...     while True:
+...         yield current_number
+...         current_number += 1
+```
+
+Now whenever `__next__()` is called on the `infinite_sequence` object, it will return the _previous number_ + 1.
+
+[iterables]: https://wiki.python.org/moin/Iterator
+[yield expression]: https://docs.python.org/3.11/reference/expressions.html#yield-expressions
diff --git a/exercises/concept/plane-tickets/.meta/config.json b/exercises/concept/plane-tickets/.meta/config.json
index 016ff0ce..26981888 100644
--- a/exercises/concept/plane-tickets/.meta/config.json
+++ b/exercises/concept/plane-tickets/.meta/config.json
@@ -16,6 +16,6 @@
       ".meta/exemplar.py"
     ]
   },
-  "icon": "poker",
+  "icon": "new-passport",
   "blurb": "Learn about generators by assigning seats to passengers."
 }
diff --git a/exercises/concept/plane-tickets/.meta/design.md b/exercises/concept/plane-tickets/.meta/design.md
index 58542aab..96a4cc3c 100644
--- a/exercises/concept/plane-tickets/.meta/design.md
+++ b/exercises/concept/plane-tickets/.meta/design.md
@@ -2,16 +2,15 @@ This issue describes how to implement the `generators` concept exercise for the
 
 ## Goal
 
-The goal of this exercise is to teach the syntax and use of `generators` in Python. 
+The goal of this exercise is to teach the syntax and use of `generators` in Python.
 
 ## Learning objectives
 
--  Understand what generators are and how/when to use them
--  Understand how generators relate to `loops` and `iterators`
--  Understand how to use the `yield` keyword
--  Understand the `__next__()` method
--  Create a generator
-
+- Understand what generators are and how/when to use them
+- Understand how generators relate to `loops` and `iterators`
+- Understand how to use the `yield` keyword
+- Understand the `__next__()` method
+- Create a generator
 
 ## Out of scope
 
@@ -22,32 +21,29 @@ The goal of this exercise is to teach the syntax and use of `generators` in Pyth
 - `yield from`
 - `generators` used as coroutines
 
-
 ## Concepts covered
 
--  `generators`
--  `yield`
--   `__next__()`
--  `iterators`
-
+- `generators`
+- `yield`
+- `__next__()`
+- `iterators`
 
 ## Prerequisites
 
--  `conditionals`
--  `dicts`
--  `functions`
--  `higher-order-functions`
--  `lists`
--  `loops`
--  `iteration`
--  `iterators`
--  `sequences`
-
-
+- `conditionals`
+- `dicts`
+- `functions`
+- `higher-order-functions`
+- `lists`
+- `loops`
+- `iteration`
+- `iterators`
+- `sequences`
+- `classes`
 
 ## Resources to refer to
 
--  [Generators (Python official docs)](https://docs.python.org/3/howto/functional.html#generators)
+- [Generators (Python official docs)](https://docs.python.org/3/howto/functional.html#generators)
 - [generator (Python official docs glossary)](https://docs.python.org/3/glossary.html#term-generator)
 - [The yield statement (Python official docs)](https://docs.python.org/3/reference/simple_stmts.html#the-yield-statement)
 - [Yield expressions (Python official docs)](https://docs.python.org/3/reference/expressions.html#yieldexpr)
@@ -57,21 +53,20 @@ The goal of this exercise is to teach the syntax and use of `generators` in Pyth
 
 ### Hints
 
--  Referring to one or more of the resources linked above, or analogous resources from a trusted source.
-- `Generators` section of the Python Docs Functional How to tutorial:  [Generators](https://docs.python.org/3/howto/functional.html#generators)
-
+- Referring to one or more of the resources linked above, or analogous resources from a trusted source.
+- `Generators` section of the Python Docs Functional How to tutorial: [Generators](https://docs.python.org/3/howto/functional.html#generators)
 
-## Concept Description 
+## Concept Description
 
-(_a variant of this can be used for the `v3/languages/python/concepts/<concept>/about.md` doc and this exercises  `introduction.md` doc._)
+(_a variant of this can be used for the `v3/languages/python/concepts/<concept>/about.md` doc and this exercises `introduction.md` doc._)
 
 _**Concept Description Needs to Be Filled In Here/Written**_
 
 _Some "extras" that we might want to include as notes in the concept description, or as links in `links.json`:_
 
--  Additional `Generator-iterator methods`, such as `generator.send()` and `generator.throw()`
+- Additional `Generator-iterator methods`, such as `generator.send()` and `generator.throw()`
 - `generator expressions`
--  Asynchronous generator functions
+- Asynchronous generator functions
 - `generators` used as coroutines
 
 ## Implementing
@@ -80,10 +75,8 @@ The general Python track concept exercise implantation guide can be found [here]
 
 Tests should be written using `unittest.TestCase` and the test file named `generators_test.py`.
 
-Code in the `.meta/example.py` file should **only use syntax & concepts introduced in this exercise or one of its prerequisites.** Please do not use comprehensions, generator expressions, or other syntax not previously covered.  Please also follow [PEP8](https://www.python.org/dev/peps/pep-0008/) guidelines.
+Code in the `.meta/example.py` file should **only use syntax & concepts introduced in this exercise or one of its prerequisites.** Please do not use comprehensions, generator expressions, or other syntax not previously covered. Please also follow [PEP8](https://www.python.org/dev/peps/pep-0008/) guidelines.
 
 ## Help
 
 If you have any questions while implementing the exercise, please post the questions as comments in this issue, or contact one of the maintainers on our Slack channel.
-
-
diff --git a/exercises/concept/plane-tickets/.meta/exemplar.py b/exercises/concept/plane-tickets/.meta/exemplar.py
index fa8927d7..9a3d7024 100644
--- a/exercises/concept/plane-tickets/.meta/exemplar.py
+++ b/exercises/concept/plane-tickets/.meta/exemplar.py
@@ -1,15 +1,32 @@
-"""Plane Tickets Exercise"""
+"""Functions to automate Conda airlines ticketing system."""
 
 import math
 
 SEATS_IN_ROW = ['A', 'B', 'C', 'D']
 
-def generate_seats(amount):
 
+def generate_seat_letters(amount):
+    """Generate a series of seat letters for airline boarding.
+
+    :param amount: int - amount of seat letters to be generated.
+    :return: generator - generator that yields seat letters.
+
+    Seat letters are generated from A to D.
+    After D it should start again with A.
+
+    Example: A, B, C, D
+
+    """
+
+    for seat in range(amount):
+        yield SEATS_IN_ROW[seat % 4]
+
+
+def generate_seats(amount):
     """Generate a series of seat numbers for airline boarding.
 
-    :param amount: Amount of seats to be generated. (int)
-    :return: Generator that generates seat numbers. (generator)
+    :param amount: int - Amount of seats to be generated.
+    :return: generator - generator that yields seat numbers.
 
     There should be no row 13
 
@@ -21,19 +38,17 @@ def generate_seats(amount):
     """
 
     amount = amount + 4 if amount >= 13 else amount
-
+    letters = generate_seat_letters(amount)
     for seat in range(amount):
         row_number = math.ceil((seat+1) / 4)
         if row_number != 13:
-            seat_letter = SEATS_IN_ROW[seat % 4]
-            yield f'{str(row_number)}{seat_letter}'
+            yield f'{str(row_number)}{next(letters)}'
 
 def assign_seats(passengers):
+    """Assign seats to passengers.
 
-    """Assign seats to passenger.
-
-    :param passengers: A list of strings containing names of passengers. (list[str])
-    :return: A dictionary type object containing the names of the passengers as keys and seat numbers as values.
+    :param passengers: list[str] - A list of strings containing names of passengers.
+    :return: dict - with the names of the passengers as keys and seat numbers as values.
 
     Example output: {"Foo": "1A", "Bar": "1B"}
 
@@ -46,12 +61,11 @@ def assign_seats(passengers):
     return output
 
 def generate_codes(seat_numbers, flight_id):
-
     """Generate codes for a ticket.
 
-    :param seat_numbers: A list of seat numbers. (list[str])
-    :param flight_id: A string containing the flight identification. (str)
-    :return: Generator that generates 12 character long strings. (generator[str])
+    :param seat_numbers: list[str] - list of seat numbers.
+    :param flight_id: str - string containing the flight identification.
+    :return: generator - generator that yields 12 character long strings.
 
     """
 
diff --git a/exercises/concept/plane-tickets/plane_tickets.py b/exercises/concept/plane-tickets/plane_tickets.py
index 44d68553..4399791b 100644
--- a/exercises/concept/plane-tickets/plane_tickets.py
+++ b/exercises/concept/plane-tickets/plane_tickets.py
@@ -1,11 +1,26 @@
-"""Plane Tickets Exercise"""
+"""Functions to automate Conda airlines ticketing system."""
 
-def generate_seats(amount):
+def generate_seat_letters(amount):
+    """ Generate a series of seat letters for airline boarding.
+
+    :param amount: int - amount of seat letters to be generated.
+    :return: generator - generator that yields seat letters.
+
+    Seat letters are generated from A to D.
+    After D it should start again with A.
+
+    Example: A, B, C, D
+
+    """
 
+    pass
+
+
+def generate_seats(amount):
     """ Generate a series of seat numbers for airline boarding.
 
-    :param amount: Amount of seats to be generated. (int)
-    :return: Generator that yields seat numbers.
+    :param amount: int - Amount of seats to be generated.
+    :return: generator - generator that yields seat numbers.
 
     There should be no row 13
 
@@ -19,11 +34,10 @@ def generate_seats(amount):
     pass
 
 def assign_seats(passengers):
-
     """ Assign seats to passengers.
 
-    :param passengers: A list of strings containing names of passengers. (list[str])
-    :return: A dictionary type object containing the names of the passengers as keys and seat numbers as values.
+    :param passengers: list[str] - A list of strings containing names of passengers.
+    :return: dict - with the names of the passengers as keys and seat numbers as values.
 
     Example output: {"Foo": "1A", "Bar": "1B"}
 
@@ -32,11 +46,10 @@ def assign_seats(passengers):
     pass
 
 def generate_codes(seat_numbers, flight_id):
-
     """Generate codes for a ticket.
 
-    :param seat_numbers: A list of seat numbers. (list[str])
-    :param flight_id: A string containing the flight identification. (str)
-    :return: Generator that generates 12 character long strings. (generator[str])
+    :param seat_numbers: list[str] - list of seat numbers.
+    :param flight_id: str - string containing the flight identification.
+    :return: generator - generator that yields 12 character long strings.
 
     """
diff --git a/exercises/practice/accumulate/.docs/instructions.md b/exercises/practice/accumulate/.docs/instructions.md
index 435e0b32..c25a03fa 100644
--- a/exercises/practice/accumulate/.docs/instructions.md
+++ b/exercises/practice/accumulate/.docs/instructions.md
@@ -1,9 +1,6 @@
 # Instructions
 
-Implement the `accumulate` operation, which, given a collection and an
-operation to perform on each element of the collection, returns a new
-collection containing the result of applying that operation to each element of
-the input collection.
+Implement the `accumulate` operation, which, given a collection and an operation to perform on each element of the collection, returns a new collection containing the result of applying that operation to each element of the input collection.
 
 Given the collection of numbers:
 
@@ -21,6 +18,5 @@ Check out the test suite to see the expected function signature.
 
 ## Restrictions
 
-Keep your hands off that collect/map/fmap/whatchamacallit functionality
-provided by your standard library!
+Keep your hands off that collect/map/fmap/whatchamacallit functionality provided by your standard library!
 Solve this one yourself using other basic tools instead.
diff --git a/exercises/practice/binary-search/.docs/instructions.md b/exercises/practice/binary-search/.docs/instructions.md
index d7f1c899..aa1946cf 100644
--- a/exercises/practice/binary-search/.docs/instructions.md
+++ b/exercises/practice/binary-search/.docs/instructions.md
@@ -11,7 +11,7 @@ Binary search only works when a list has been sorted.
 
 The algorithm looks like this:
 
-- Find the middle element of a sorted list and compare it with the item we're looking for.
+- Find the middle element of a *sorted* list and compare it with the item we're looking for.
 - If the middle element is our item, then we're done!
 - If the middle element is greater than our item, we can eliminate that element and all the elements **after** it.
 - If the middle element is less than our item, we can eliminate that element and all the elements **before** it.
diff --git a/exercises/practice/binary/.docs/instructions.md b/exercises/practice/binary/.docs/instructions.md
index 3cfde215..046fd3e0 100644
--- a/exercises/practice/binary/.docs/instructions.md
+++ b/exercises/practice/binary/.docs/instructions.md
@@ -2,9 +2,9 @@
 
 Convert a binary number, represented as a string (e.g. '101010'), to its decimal equivalent using first principles.
 
-Implement binary to decimal conversion. Given a binary input
-string, your program should produce a decimal output. The
-program should handle invalid inputs.
+Implement binary to decimal conversion.
+Given a binary input string, your program should produce a decimal output.
+The program should handle invalid inputs.
 
 ## Note
 
@@ -15,8 +15,7 @@ program should handle invalid inputs.
 
 Decimal is a base-10 system.
 
-A number 23 in base 10 notation can be understood
-as a linear combination of powers of 10:
+A number 23 in base 10 notation can be understood as a linear combination of powers of 10:
 
 - The rightmost digit gets multiplied by 10^0 = 1
 - The next number gets multiplied by 10^1 = 10
diff --git a/exercises/practice/binary/.meta/config.json b/exercises/practice/binary/.meta/config.json
index 7170a4ab..b9f5b075 100644
--- a/exercises/practice/binary/.meta/config.json
+++ b/exercises/practice/binary/.meta/config.json
@@ -26,5 +26,5 @@
     ]
   },
   "source": "All of Computer Science",
-  "source_url": "http://www.wolframalpha.com/input/?i=binary&a=*C.binary-_*MathWorld-"
+  "source_url": "https://www.wolframalpha.com/examples/mathematics/numbers/base-conversions"
 }
diff --git a/exercises/practice/book-store/.docs/instructions.md b/exercises/practice/book-store/.docs/instructions.md
index 341ad01f..906eb587 100644
--- a/exercises/practice/book-store/.docs/instructions.md
+++ b/exercises/practice/book-store/.docs/instructions.md
@@ -12,9 +12,9 @@ If you buy 4 different books, you get a 20% discount.
 
 If you buy all 5, you get a 25% discount.
 
-Note: that if you buy four books, of which 3 are different titles, you get a 10% discount on the 3 that form part of a set, but the fourth book still costs $8.
+Note that if you buy four books, of which 3 are different titles, you get a 10% discount on the 3 that form part of a set, but the fourth book still costs $8.
 
-Your mission is to write a piece of code to calculate the price of any conceivable shopping basket (containing only books of the same series), giving as big a discount as possible.
+Your mission is to write code to calculate the price of any conceivable shopping basket (containing only books of the same series), giving as big a discount as possible.
 
 For example, how much does this basket of books cost?
 
@@ -26,36 +26,36 @@ For example, how much does this basket of books cost?
 
 One way of grouping these 8 books is:
 
-- 1 group of 5 --> 25% discount (1st,2nd,3rd,4th,5th)
-- +1 group of 3 --> 10% discount (1st,2nd,3rd)
+- 1 group of 5 (1st, 2nd,3rd, 4th, 5th)
+- 1 group of 3 (1st, 2nd, 3rd)
 
 This would give a total of:
 
 - 5 books at a 25% discount
-- +3 books at a 10% discount
+- 3 books at a 10% discount
 
 Resulting in:
 
-- 5 × (8 - 2.00) = 5 × 6.00 = $30.00
-- +3 × (8 - 0.80) = 3 × 7.20 = $21.60
+- 5 × (100% - 25%) * $8 = 5 × $6.00 = $30.00, plus
+- 3 × (100% - 10%) * $8 = 3 × $7.20 = $21.60
 
-For a total of $51.60
+Which equals $51.60.
 
 However, a different way to group these 8 books is:
 
-- 1 group of 4 books --> 20% discount  (1st,2nd,3rd,4th)
-- +1 group of 4 books --> 20% discount  (1st,2nd,3rd,5th)
+- 1 group of 4 books (1st, 2nd, 3rd, 4th)
+- 1 group of 4 books (1st, 2nd, 3rd, 5th)
 
 This would give a total of:
 
 - 4 books at a 20% discount
-- +4 books at a 20% discount
+- 4 books at a 20% discount
 
 Resulting in:
 
-- 4 × (8 - 1.60) = 4 × 6.40 = $25.60
-- +4 × (8 - 1.60) = 4 × 6.40 = $25.60
+- 4 × (100% - 20%) * $8 = 4 × $6.40 = $25.60, plus
+- 4 × (100% - 20%) * $8 = 4 × $6.40 = $25.60
 
-For a total of $51.20
+Which equals $51.20.
 
 And $51.20 is the price with the biggest discount.
diff --git a/exercises/practice/bowling/.docs/instructions.md b/exercises/practice/bowling/.docs/instructions.md
index 48a2fedc..ddce7ee4 100644
--- a/exercises/practice/bowling/.docs/instructions.md
+++ b/exercises/practice/bowling/.docs/instructions.md
@@ -36,7 +36,7 @@ Frame 3 is (9 + 0) = 9
 This means the current running total is 48.
 
 The tenth frame in the game is a special case.
-If someone throws a strike or a spare then they get a fill ball.
+If someone throws a spare or a strike then they get one or two fill balls respectively.
 Fill balls exist to calculate the total of the 10th frame.
 Scoring a strike or spare on the fill ball does not give the player more fill balls.
 The total value of the 10th frame is the total number of pins knocked down.
diff --git a/exercises/practice/circular-buffer/.meta/example.py b/exercises/practice/circular-buffer/.meta/example.py
index 8438b983..538cc7bc 100644
--- a/exercises/practice/circular-buffer/.meta/example.py
+++ b/exercises/practice/circular-buffer/.meta/example.py
@@ -25,7 +25,7 @@ class CircularBuffer:
         self.read_point = 0
         self.write_point = 0
 
-    # (protected) helper method to support python 2/3
+    # (protected) helper method
     def _update_buffer(self, data):
         try:
             self.buffer[self.write_point] = data
diff --git a/exercises/practice/darts/.docs/instructions.md b/exercises/practice/darts/.docs/instructions.md
index 7af7428a..70f0e53d 100644
--- a/exercises/practice/darts/.docs/instructions.md
+++ b/exercises/practice/darts/.docs/instructions.md
@@ -12,7 +12,7 @@ In our particular instance of the game, the target rewards 4 different amounts o
 - If the dart lands in the inner circle of the target, player earns 10 points.
 
 The outer circle has a radius of 10 units (this is equivalent to the total radius for the entire target), the middle circle a radius of 5 units, and the inner circle a radius of 1.
-Of course, they are all centered at the same point (that is, the circles are [concentric][] defined by the coordinates (0, 0).
+Of course, they are all centered at the same point — that is, the circles are [concentric][] defined by the coordinates (0, 0).
 
 Write a function that given a point in the target (defined by its [Cartesian coordinates][cartesian-coordinates] `x` and `y`, where `x` and `y` are [real][real-numbers]), returns the correct amount earned by a dart landing at that point.
 
diff --git a/exercises/practice/error-handling/.docs/instructions.md b/exercises/practice/error-handling/.docs/instructions.md
index 7bc1a085..25dd4d29 100644
--- a/exercises/practice/error-handling/.docs/instructions.md
+++ b/exercises/practice/error-handling/.docs/instructions.md
@@ -2,9 +2,7 @@
 
 Implement various kinds of error handling and resource management.
 
-An important point of programming is how to handle errors and close
-resources even if errors occur.
+An important point of programming is how to handle errors and close resources even if errors occur.
 
-This exercise requires you to handle various errors. Because error handling
-is rather programming language specific you'll have to refer to the tests
-for your track to see what's exactly required.
+This exercise requires you to handle various errors.
+Because error handling is rather programming language specific you'll have to refer to the tests for your track to see what's exactly required.
diff --git a/exercises/practice/etl/.meta/config.json b/exercises/practice/etl/.meta/config.json
index 4d99e631..32aab8ba 100644
--- a/exercises/practice/etl/.meta/config.json
+++ b/exercises/practice/etl/.meta/config.json
@@ -27,7 +27,7 @@
       ".meta/example.py"
     ]
   },
-  "blurb": "We are going to do the `Transform` step of an Extract-Transform-Load.",
-  "source": "Exercise by the JumpstartLab team for students at The Turing School of Software and Design.",
+  "blurb": "Change the data format for scoring a game to more easily add other languages.",
+  "source": "Based on an exercise by the JumpstartLab team for students at The Turing School of Software and Design.",
   "source_url": "https://turing.edu"
 }
diff --git a/exercises/practice/gigasecond/.docs/introduction.md b/exercises/practice/gigasecond/.docs/introduction.md
index 74afaa99..18a3dc20 100644
--- a/exercises/practice/gigasecond/.docs/introduction.md
+++ b/exercises/practice/gigasecond/.docs/introduction.md
@@ -13,7 +13,7 @@ Then we can use metric system prefixes for writing large numbers of seconds in m
 - Perhaps you and your family would travel to somewhere exotic for two megaseconds (that's two million seconds).
 - And if you and your spouse were married for _a thousand million_ seconds, you would celebrate your one gigasecond anniversary.
 
-```exercism/note
+~~~~exercism/note
 If we ever colonize Mars or some other planet, measuring time is going to get even messier.
 If someone says "year" do they mean a year on Earth or a year on Mars?
 
@@ -21,4 +21,4 @@ The idea for this exercise came from the science fiction novel ["A Deepness in t
 In it the author uses the metric system as the basis for time measurements.
 
 [vinge-novel]: https://www.tor.com/2017/08/03/science-fiction-with-something-for-everyone-a-deepness-in-the-sky-by-vernor-vinge/
-```
+~~~~
diff --git a/exercises/practice/hexadecimal/.docs/instructions.md b/exercises/practice/hexadecimal/.docs/instructions.md
index 3e5afa77..a3c648e3 100644
--- a/exercises/practice/hexadecimal/.docs/instructions.md
+++ b/exercises/practice/hexadecimal/.docs/instructions.md
@@ -2,7 +2,6 @@
 
 Convert a hexadecimal number, represented as a string (e.g. "10af8c"), to its decimal equivalent using first principles (i.e. no, you may not use built-in or external libraries to accomplish the conversion).
 
-On the web we use hexadecimal to represent colors, e.g. green: 008000,
-teal: 008080, navy: 000080).
+On the web we use hexadecimal to represent colors, e.g. green: 008000, teal: 008080, navy: 000080).
 
 The program should handle invalid hexadecimal strings.
diff --git a/exercises/practice/hexadecimal/.meta/config.json b/exercises/practice/hexadecimal/.meta/config.json
index 352ca16d..b61e1a2c 100644
--- a/exercises/practice/hexadecimal/.meta/config.json
+++ b/exercises/practice/hexadecimal/.meta/config.json
@@ -26,5 +26,5 @@
     ]
   },
   "source": "All of Computer Science",
-  "source_url": "http://www.wolframalpha.com/examples/NumberBases.html"
+  "source_url": "https://www.wolframalpha.com/examples/mathematics/numbers/base-conversions"
 }
diff --git a/exercises/practice/killer-sudoku-helper/.docs/instructions.md b/exercises/practice/killer-sudoku-helper/.docs/instructions.md
index 93469a09..2347ab26 100644
--- a/exercises/practice/killer-sudoku-helper/.docs/instructions.md
+++ b/exercises/practice/killer-sudoku-helper/.docs/instructions.md
@@ -56,8 +56,8 @@ The screenshots above have been generated using [F-Puzzles.com](https://www.f-pu
 
 [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
+[one-solution-img]: https://exercism-v3-icons.s3.eu-west-2.amazonaws.com/images/exercises/killer-sudoku-helper/example1.png
+[four-solutions-img]: https://exercism-v3-icons.s3.eu-west-2.amazonaws.com/images/exercises/killer-sudoku-helper/example2.png
+[not-possible-img]: https://exercism-v3-icons.s3.eu-west-2.amazonaws.com/images/exercises/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/linked-list/.docs/instructions.md b/exercises/practice/linked-list/.docs/instructions.md
index a47942d7..edf4055b 100644
--- a/exercises/practice/linked-list/.docs/instructions.md
+++ b/exercises/practice/linked-list/.docs/instructions.md
@@ -13,7 +13,7 @@ Sometimes a station gets closed down, and in that case the station needs to be r
 
 The size of a route is measured not by how far the train travels, but by how many stations it stops at.
 
-```exercism/note
+~~~~exercism/note
 The linked list is a fundamental data structure in computer science, often used in the implementation of other data structures.
 As the name suggests, it is a list of nodes that are linked together.
 It is a list of "nodes", where each node links to its neighbor or neighbors.
@@ -23,4 +23,4 @@ In a **doubly linked list** each node links to both the node that comes before,
 If you want to dig deeper into linked lists, check out [this article][intro-linked-list] that explains it using nice drawings.
 
 [intro-linked-list]: https://medium.com/basecs/whats-a-linked-list-anyway-part-1-d8b7e6508b9d
-```
+~~~~
diff --git a/exercises/practice/nucleotide-count/.docs/instructions.md b/exercises/practice/nucleotide-count/.docs/instructions.md
index 57667134..548d9ba5 100644
--- a/exercises/practice/nucleotide-count/.docs/instructions.md
+++ b/exercises/practice/nucleotide-count/.docs/instructions.md
@@ -1,10 +1,12 @@
 # Instructions
 
-Each of us inherits from our biological parents a set of chemical instructions known as DNA that influence how our bodies are constructed. All known life depends on DNA!
+Each of us inherits from our biological parents a set of chemical instructions known as DNA that influence how our bodies are constructed.
+All known life depends on DNA!
 
 > Note: You do not need to understand anything about nucleotides or DNA to complete this exercise.
 
-DNA is a long chain of other chemicals and the most important are the four nucleotides, adenine, cytosine, guanine and thymine. A single DNA chain can contain billions of these four nucleotides and the order in which they occur is important!
+DNA is a long chain of other chemicals and the most important are the four nucleotides, adenine, cytosine, guanine and thymine.
+A single DNA chain can contain billions of these four nucleotides and the order in which they occur is important!
 We call the order of these nucleotides in a bit of DNA a "DNA sequence".
 
 We represent a DNA sequence as an ordered collection of these four nucleotides and a common way to do that is with a string of characters such as "ATTACG" for a DNA sequence of 6 nucleotides.
diff --git a/exercises/practice/nucleotide-count/.meta/config.json b/exercises/practice/nucleotide-count/.meta/config.json
index 4e91bb7e..e0c108f7 100644
--- a/exercises/practice/nucleotide-count/.meta/config.json
+++ b/exercises/practice/nucleotide-count/.meta/config.json
@@ -28,5 +28,5 @@
     ]
   },
   "source": "The Calculating DNA Nucleotides_problem at Rosalind",
-  "source_url": "http://rosalind.info/problems/dna/"
+  "source_url": "https://rosalind.info/problems/dna/"
 }
diff --git a/exercises/practice/octal/.docs/instructions.md b/exercises/practice/octal/.docs/instructions.md
index 81f10838..65ce135c 100644
--- a/exercises/practice/octal/.docs/instructions.md
+++ b/exercises/practice/octal/.docs/instructions.md
@@ -1,11 +1,9 @@
 # Instructions
 
-Convert an octal number, represented as a string (e.g. '1735263'), to its
-decimal equivalent using first principles (i.e. no, you may not use built-in or
-external libraries to accomplish the conversion).
+Convert an octal number, represented as a string (e.g. '1735263'), to its decimal equivalent using first principles (i.e. no, you may not use built-in or external libraries to accomplish the conversion).
 
-Implement octal to decimal conversion.  Given an octal input
-string, your program should produce a decimal output.
+Implement octal to decimal conversion.
+Given an octal input string, your program should produce a decimal output.
 
 ## Note
 
diff --git a/exercises/practice/octal/.meta/config.json b/exercises/practice/octal/.meta/config.json
index e39e3fb6..4fe6c11b 100644
--- a/exercises/practice/octal/.meta/config.json
+++ b/exercises/practice/octal/.meta/config.json
@@ -26,5 +26,5 @@
     ]
   },
   "source": "All of Computer Science",
-  "source_url": "http://www.wolframalpha.com/input/?i=base+8"
+  "source_url": "https://www.wolframalpha.com/examples/mathematics/numbers/base-conversions"
 }
diff --git a/exercises/practice/pangram/.docs/instructions.md b/exercises/practice/pangram/.docs/instructions.md
index d5698bc2..817c872d 100644
--- a/exercises/practice/pangram/.docs/instructions.md
+++ b/exercises/practice/pangram/.docs/instructions.md
@@ -5,4 +5,4 @@ Your task is to figure out if a sentence is a pangram.
 A pangram is a sentence using every letter of the alphabet at least once.
 It is case insensitive, so it doesn't matter if a letter is lower-case (e.g. `k`) or upper-case (e.g. `K`).
 
-For this exercise we only use the basic letters used in the English alphabet: `a` to `z`.
+For this exercise, a sentence is a pangram if it contains each of the 26 letters in the English alphabet.
diff --git a/exercises/practice/pangram/.docs/introduction.md b/exercises/practice/pangram/.docs/introduction.md
index d38fa341..32b6f1fc 100644
--- a/exercises/practice/pangram/.docs/introduction.md
+++ b/exercises/practice/pangram/.docs/introduction.md
@@ -7,10 +7,10 @@ To give a comprehensive sense of the font, the random sentences should use **all
 They're running a competition to get suggestions for sentences that they can use.
 You're in charge of checking the submissions to see if they are valid.
 
-```exercism/note
+~~~~exercism/note
 Pangram comes from Greek, παν γράμμα, pan gramma, which means "every letter".
 
 The best known English pangram is:
 
 > The quick brown fox jumps over the lazy dog.
-```
+~~~~
diff --git a/exercises/practice/parallel-letter-frequency/.docs/instructions.md b/exercises/practice/parallel-letter-frequency/.docs/instructions.md
index a5b936c5..85abcf86 100644
--- a/exercises/practice/parallel-letter-frequency/.docs/instructions.md
+++ b/exercises/practice/parallel-letter-frequency/.docs/instructions.md
@@ -2,7 +2,6 @@
 
 Count the frequency of letters in texts using parallel computation.
 
-Parallelism is about doing things in parallel that can also be done
-sequentially. A common example is counting the frequency of letters.
-Create a function that returns the total frequency of each letter in a
-list of texts and that employs parallelism.
+Parallelism is about doing things in parallel that can also be done sequentially.
+A common example is counting the frequency of letters.
+Create a function that returns the total frequency of each letter in a list of texts and that employs parallelism.
diff --git a/exercises/practice/point-mutations/.meta/config.json b/exercises/practice/point-mutations/.meta/config.json
index 2d8d765c..b02f3706 100644
--- a/exercises/practice/point-mutations/.meta/config.json
+++ b/exercises/practice/point-mutations/.meta/config.json
@@ -26,5 +26,5 @@
     ]
   },
   "source": "The Calculating Point Mutations problem at Rosalind",
-  "source_url": "http://rosalind.info/problems/hamm/"
+  "source_url": "https://rosalind.info/problems/hamm/"
 }
diff --git a/exercises/practice/pythagorean-triplet/.meta/template.j2 b/exercises/practice/pythagorean-triplet/.meta/template.j2
index b3836a54..e52cda4e 100644
--- a/exercises/practice/pythagorean-triplet/.meta/template.j2
+++ b/exercises/practice/pythagorean-triplet/.meta/template.j2
@@ -2,9 +2,6 @@
 
 {{ macros.header() }}
 
-# Python 2/3 compatibility
-if not hasattr(unittest.TestCase, 'assertCountEqual'):
-    unittest.TestCase.assertCountEqual = unittest.TestCase.assertItemsEqual
 
 class {{ exercise | camel_case }}Test(unittest.TestCase):
     {% for case in cases -%}
diff --git a/exercises/practice/queen-attack/.docs/instructions.md b/exercises/practice/queen-attack/.docs/instructions.md
index dce0fc29..ad7ea954 100644
--- a/exercises/practice/queen-attack/.docs/instructions.md
+++ b/exercises/practice/queen-attack/.docs/instructions.md
@@ -21,5 +21,5 @@ So if you are told the white queen is at `c5` (zero-indexed at column 2, row 3)
   a b c d e f g h
 ```
 
-You are also be able to answer whether the queens can attack each other.
+You are also able to answer whether the queens can attack each other.
 In this case, that answer would be yes, they can, because both pieces share a diagonal.
diff --git a/exercises/practice/resistor-color-expert/.docs/instructions.md b/exercises/practice/resistor-color-expert/.docs/instructions.md
new file mode 100644
index 00000000..f0880164
--- /dev/null
+++ b/exercises/practice/resistor-color-expert/.docs/instructions.md
@@ -0,0 +1,76 @@
+# Instructions
+
+In this exercise, you are going to create a helpful program so that you don't have to remember the values of the bands.
+The program will take 1, 4, or 5 colors as input, and outputs the correct value, in ohms.
+The color bands are encoded as follows:
+
+- Black: 0
+- Brown: 1
+- Red: 2
+- Orange: 3
+- Yellow: 4
+- Green: 5
+- Blue: 6
+- Violet: 7
+- Grey: 8
+- White: 9
+
+In `resistor-color trio` you decoded the first three colors.
+For instance: orange-orange-brown translated to the main value `330`.
+In this exercise you will need to add _tolerance_ to the mix.
+Tolerance is the maximum amount that a value can be above or below the main value.
+For example, if the last band is green, the maximum tolerance will be ±0.5%.
+
+The tolerance band will have one of these values:
+
+- Grey - 0.05%
+- Violet - 0.1%
+- Blue - 0.25%
+- Green - 0.5%
+- Brown - 1%
+- Red - 2%
+- Gold - 5%
+- Silver - 10%
+
+The four-band resistor is built up like this:
+
+| Band_1  | Band_2  | Band_3     | band_4    |
+| ------- | ------- | ---------- | --------- |
+| Value_1 | Value_2 | Multiplier | Tolerance |
+
+Meaning
+
+- orange-orange-brown-green would be 330 ohms with a ±0.5% tolerance.
+- orange-orange-red would-grey would be 3300 ohms with ±0.005% tolerance.
+The difference between a four and five-band resistor is that the five-band resistor has an extra band to indicate a more precise value.
+
+| Band_1  | Band_2  | Band_3  | Band_4     | band_5    |
+| ------- | ------- | ------- | ---------- | --------- |
+| Value_1 | Value_2 | Value_3 | Multiplier | Tolerance |
+
+Meaning
+
+- orange-orange-orange-black-green would be 330 ohms with a ±0.5% tolerance.
+There are also one band resistors.
+This type of resistor only has the color black and has a value of 0.
+
+This exercise is about translating the resistor band colors into a label:
+
+"... ohms ...%"
+
+So an input of "orange", "orange", "black, green" should return:
+
+"33 ohms ±0.5%"
+
+When there are more than a thousand ohms, we say "kiloohms".
+ That's similar to saying "kilometer" for 1000 meters, and "kilograms" for 1000 grams.
+
+So an input of "orange", "orange", "orange", grey should return:
+
+"33 kiloohms ±0.05%"
+
+When there are more than a million ohms, we say "megaohms".
+
+So an input of "orange", "orange", "orange", "red" should return:
+
+"33 megaohms ±2%"
diff --git a/exercises/practice/resistor-color-expert/.docs/introduction.md b/exercises/practice/resistor-color-expert/.docs/introduction.md
new file mode 100644
index 00000000..fd9e05ef
--- /dev/null
+++ b/exercises/practice/resistor-color-expert/.docs/introduction.md
@@ -0,0 +1,10 @@
+# Introduction
+
+If you want to build something using a Raspberry Pi, you'll probably use _resistors_.
+Like the previous `Resistor Color Duo` and `Resistor Color Trio` exercises, you will be translating resistor color bands to human-readable labels.
+
+- Each resistor has a resistance value.
+- Resistors are small - so small in fact that if you printed the resistance value on them, it would be hard to read.
+  To get around this problem, manufacturers print color-coded bands onto the resistors to denote their resistance values.
+- Each band acts as a digit of a number.
+  For example, if they printed a brown band (value 1) followed by a green band (value 5), it would translate to the number 15.
diff --git a/exercises/practice/resistor-color-expert/.meta/config.json b/exercises/practice/resistor-color-expert/.meta/config.json
new file mode 100644
index 00000000..edf50b4a
--- /dev/null
+++ b/exercises/practice/resistor-color-expert/.meta/config.json
@@ -0,0 +1,20 @@
+{
+  "authors": [
+    "meatball133",
+    "bethanyg"
+  ],
+  "files": {
+    "solution": [
+      "resistor_color_expert.py"
+    ],
+    "test": [
+      "resistor_color_expert_test.py"
+    ],
+    "example": [
+      ".meta/example.py"
+    ]
+  },
+  "blurb": "Convert color codes as used on resistors with different bands to a human-readable label.",
+  "source": "Based on earlier resistor color exercises made by Erik Schierboom and Maud de Vries",
+  "source_url": "https://github.com/exercism/problem-specifications/issues/1464"
+}
diff --git a/exercises/practice/resistor-color-expert/.meta/example.py b/exercises/practice/resistor-color-expert/.meta/example.py
new file mode 100644
index 00000000..0fd6e42f
--- /dev/null
+++ b/exercises/practice/resistor-color-expert/.meta/example.py
@@ -0,0 +1,49 @@
+COLORS = [
+    'black',
+    'brown',
+    'red',
+    'orange',
+    'yellow',
+    'green',
+    'blue',
+    'violet',
+    'grey',
+    'white'
+]
+
+COLORS_TOLERANCE = {
+    'brown': 1,
+    'red': 2,
+    'green': 0.5,
+    'blue': 0.25,
+    'violet': 0.1,
+    'grey': 0.05,
+    'gold': 5,
+    'silver': 10
+}
+
+
+def resistor_label(colors):
+    if len(colors) == 1:
+        return f'0 ohms'
+    elif len(colors) == 4:
+        value = 10 * COLORS.index(colors[0]) + COLORS.index(colors[1])
+        value *= 10 ** COLORS.index(colors[2])
+        value, unit = color_code(value)
+        value = int(value) if value.is_integer() else value
+        return f'{value} {unit} ±{COLORS_TOLERANCE[colors[3]]}%'
+    else:
+        value = 100 * COLORS.index(colors[0]) + 10 * COLORS.index(colors[1]) + COLORS.index(colors[2])
+        value *= 10 ** COLORS.index(colors[3])
+        value, unit = color_code(value)
+        value = int(value) if value.is_integer() else value
+        return f'{value} {unit} ±{COLORS_TOLERANCE[colors[4]]}%'
+
+
+def color_code(color):
+    if color < 1000:
+        return color / 1, 'ohms'
+    elif color < 1000000:
+        return color / 1000, 'kiloohms'
+    else:
+        return color / 1000000, 'megaohms'
\ No newline at end of file
diff --git a/exercises/practice/resistor-color-expert/resistor_color_expert.py b/exercises/practice/resistor-color-expert/resistor_color_expert.py
new file mode 100644
index 00000000..f36881c5
--- /dev/null
+++ b/exercises/practice/resistor-color-expert/resistor_color_expert.py
@@ -0,0 +1,2 @@
+def resistor_label(colors):
+    pass
diff --git a/exercises/practice/robot-name/.approaches/config.json b/exercises/practice/robot-name/.approaches/config.json
new file mode 100644
index 00000000..c71235ec
--- /dev/null
+++ b/exercises/practice/robot-name/.approaches/config.json
@@ -0,0 +1,21 @@
+{
+  "introduction": {
+    "authors": ["safwansamsudeen"]
+  },
+  "approaches": [
+    {
+      "uuid": "94d82d63-0a20-44df-ad9b-14aba7c076ae",
+      "slug": "mass-name-generation",
+      "title": "Mass Name Generation",
+      "blurb": "Select from all possible names",
+      "authors": ["safwansamsudeen"]
+    },
+    {
+      "uuid": "20ca6d56-ab48-46be-a04e-98736109be0a",
+      "slug": "name-on-the-fly",
+      "title": "Find name on the fly",
+      "blurb": "Generate name and check that it hasn't been used",
+      "authors": ["safwansamsudeen"]
+    }
+  ]
+}
diff --git a/exercises/practice/robot-name/.approaches/introduction.md b/exercises/practice/robot-name/.approaches/introduction.md
new file mode 100644
index 00000000..9dc810ed
--- /dev/null
+++ b/exercises/practice/robot-name/.approaches/introduction.md
@@ -0,0 +1,59 @@
+# Introduction
+Robot Name in Python is an interesting exercise for practising randomness.
+
+## General Guidance
+Two ways immedietely come to mind: generate all the possible names and then return them sequentially, or generate a random name and ensure that it's not been previously used.
+
+Randomness can be a little, well, random, so **it's very easy to have an incorrect solution and still pass the tests**. It's strongly recommended to submit your solution for Code Review.
+
+## Approach: mass name generation
+We'd first have to generate all the possible names, shuffle them, and then use `next` (the simplest way) or maintain a `current_index` and get the name.
+Here's a possible way to do it:
+
+```python
+from itertools import product
+from random import shuffle
+from string import ascii_uppercase as letters
+
+letter_pairs = (''.join(p) for p in product(letters, letters))
+numbers = (str(i).zfill(3) for i in range(1000))
+names = [l + n for l, n in product(letter_pairs, numbers)]
+shuffle(names)
+NAMES = iter(names)
+class Robot(object):
+    def __init__(self):
+        self.reset()
+    def reset(self):
+        self.name = next(NAMES)
+```
+Note that selecting randomly from the list of all names would be incorrect, as there's a possibility of the name being repeated.
+For more detail and explanation of the code, [read here][approach-mass-name-generation].
+
+## Approach: name on the fly
+Another approach is to generate the name on the fly and add it to a cache or a store, and checking if the generated name hasn't been used previously.
+
+
+A possible way to implement this:
+```python
+from string import ascii_uppercase, digits
+from random import choices
+
+
+cache = set()
+
+
+class Robot:
+    def __get_name(self): 
+        return ''.join(choices(ascii_uppercase, k=2) + choices(digits, k=3))
+
+    def reset(self):
+        while (name := self.__get_name()) in cache:
+            pass
+        cache.add(name)
+        self.name = name
+
+    def __init__(self): 
+        self.reset()
+```
+
+For more detail and different ways to implement this, [read here][approach-name-on-the-fly].
\ No newline at end of file
diff --git a/exercises/practice/robot-name/.approaches/mass-name-generation/content.md b/exercises/practice/robot-name/.approaches/mass-name-generation/content.md
new file mode 100644
index 00000000..6e5fad83
--- /dev/null
+++ b/exercises/practice/robot-name/.approaches/mass-name-generation/content.md
@@ -0,0 +1,49 @@
+# Mass Name Generation
+We'd first have to generate all the possible names, shuffle them, and then use `next` (the simplest way) or maintain a `current_index` and get the name.
+Note that selecting randomly from the list of all names would be incorrect, as there's a possibility of the name being repeated.
+
+Here's a possible way to do it:
+
+```python
+from itertools import product
+from random import shuffle
+from string import ascii_uppercase
+
+letter_pairs = (''.join(p) for p in product(ascii_uppercase, ascii_uppercase))
+numbers = (str(i).zfill(3) for i in range(1000))
+names = [l + n for l, n in product(letter_pairs, numbers)]
+
+shuffle(names)
+NAMES = iter(names)
+
+class Robot(object):
+    def __init__(self):
+        self.reset()
+    def reset(self):
+        self.name = next(NAMES)
+```
+
+The first few lines of the mass name generation uses [`itertools.product`][itertools-product].
+The resultant code is a simplification of:
+```python
+letter_pairs = (''.join((l1, l2)) for l1 in ascii_uppercase for l2 in ascii_uppercase)
+numbers = (str(i).zfill(3) for i in range(1000))
+names = [l + n for l in letter_pairs for n in numbers]
+```
+
+After the name generation, the names are shuffled - using the [default `seed`][random-seed] in the `random` module (the current timestamp). 
+When the tests reseed `random`, this has no effect as the names were shuffled before that.
+
+We then set `NAMES` to the iterable of names, and in `reset`, set the robot's name to the  `next(name)`. 
+If you'd like, read more on [`iter` and `next`][iter-and-next].
+
+Unlike the on the fly approach, this has a relatively short "generation" time, because we're merely giving the `next` name instead of generating it.
+However, this has a huge startup memory and time cost, as 676,000 strings have to be calculated and stored. 
+For an approximate calculation, 676,000 strings * 5 characters / string * 1 byte / character gives 3380000 bytes or 3.38 MB of RAM - and that's just the memory aspect of it.
+Sounds small, but it's relatively very expensive at the beginning.
+
+Thus, this approach is inefficient in cases where only a small amount of names are needed _and_ the time to set/reset the robot isn't crucial.
+
+[random-seed]: https://docs.python.org/3/library/random.html#random.seed
+[iter-and-next]: https://www.programiz.com/python-programming/methods/built-in/iter
+[itertools-product]: https://www.hackerrank.com/challenges/itertools-product/problem
\ No newline at end of file
diff --git a/exercises/practice/robot-name/.approaches/mass-name-generation/snippet.txt b/exercises/practice/robot-name/.approaches/mass-name-generation/snippet.txt
new file mode 100644
index 00000000..34123094
--- /dev/null
+++ b/exercises/practice/robot-name/.approaches/mass-name-generation/snippet.txt
@@ -0,0 +1,8 @@
+...
+names = [l + n for l, n in product(letter_pairs, numbers)]
+shuffle(names)
+NAMES = iter(names)
+
+class Robot(object):
+    def reset(self):
+        self.name = next(NAMES)
\ No newline at end of file
diff --git a/exercises/practice/robot-name/.approaches/name-on-the-fly/content.md b/exercises/practice/robot-name/.approaches/name-on-the-fly/content.md
new file mode 100644
index 00000000..0aa9f9a3
--- /dev/null
+++ b/exercises/practice/robot-name/.approaches/name-on-the-fly/content.md
@@ -0,0 +1,52 @@
+# Find name on the fly
+We generate the name on the fly and add it to a cache or a store, and checking if the generated name hasn't been used previously.
+
+A possible way to implement this:
+```python
+from string import ascii_uppercase, digits
+from random import choices
+
+cache = set()
+
+
+class Robot:
+    def __get_name(self): 
+        return ''.join(choices(ascii_uppercase, k=2) + choices(digits, k=3))
+
+    def reset(self):
+        while (name := self.__get_name()) in cache:
+            pass
+        cache.add(name)
+        self.name = name
+
+    def __init__(self): 
+        self.reset()
+```
+We use a `set` for the cache as it has a low access time, and we don't need the preservation of order or the ability to be indexed.
+
+This way is merely one of the many to generate the name. 
+Another way might be to use `randrange` along with `zfill` for the number part, and a double `random.choice` / `random.choice` on `itertools.product` to generate the letter part.
+This is the shortest way, and best utilizes the Python standard library.
+
+As we're using a `while` loop to check for the name generation, it's convenient to store the local `name` using the [walrus operator][walrus-operator].
+It's also possible to find the name before the loop and find it again inside the loop, but that would unnecessary repetition.
+A helper method ([private][private-helper-methods] in this case) makes your code cleaner, but it's equally valid to have the code in the loop itself:
+```python
+def reset(self):
+    while (name := ''.join(choices(ascii_uppercase, k=2) + choices(digits, k=3))) in cache:
+        pass
+    cache.add(name)
+    self.name = name
+```
+
+We call `reset` from `__init__` - it's syntactically valid to do it the other way round, but it's not considered good practice to call [dunder methods][dunder-methods] directly.
+
+This has almost no startup time and memory, apart from declaring an empty `set`.
+Note that the _generation_ time is the same as the mass generation approach, as a similar method is used.
+However, as the name is generated at the time of setting/resetting, the method time itself is higher.
+
+In the long run, if many names are generated, this is inefficient, since collisions will start being generated more often than unique names. 
+
+[walrus-operator]: https://realpython.com/python-walrus-operator/
+[private-helper-methods]: https://www.geeksforgeeks.org/private-methods-in-python/
+[dunder-methods]: https://dbader.org/blog/python-dunder-methods
\ No newline at end of file
diff --git a/exercises/practice/robot-name/.approaches/name-on-the-fly/snippet.txt b/exercises/practice/robot-name/.approaches/name-on-the-fly/snippet.txt
new file mode 100644
index 00000000..e6ab23e7
--- /dev/null
+++ b/exercises/practice/robot-name/.approaches/name-on-the-fly/snippet.txt
@@ -0,0 +1,8 @@
+cache = set()
+class Robot:
+    def reset(self):
+        while (name := ''.join(choices(ascii_uppercase, k=2) + choices(digits, k=3))) in cache:
+            pass
+        cache.add(name)
+        self.name = name
+    def __init__(self): self.reset()
\ No newline at end of file
diff --git a/exercises/practice/roman-numerals/.docs/instructions.md b/exercises/practice/roman-numerals/.docs/instructions.md
index bb7e909d..247ea089 100644
--- a/exercises/practice/roman-numerals/.docs/instructions.md
+++ b/exercises/practice/roman-numerals/.docs/instructions.md
@@ -36,6 +36,6 @@ In Roman numerals 1990 is MCMXC:
 2000=MM
 8=VIII
 
-Learn more about [Roman numberals on Wikipedia][roman-numerals].
+Learn more about [Roman numerals on Wikipedia][roman-numerals].
 
 [roman-numerals]: https://wiki.imperivm-romanvm.com/wiki/Roman_Numerals
diff --git a/exercises/practice/saddle-points/.docs/instructions.md b/exercises/practice/saddle-points/.docs/instructions.md
index d861388e..c585568b 100644
--- a/exercises/practice/saddle-points/.docs/instructions.md
+++ b/exercises/practice/saddle-points/.docs/instructions.md
@@ -5,20 +5,22 @@ Your task is to find the potential trees where you could build your tree house.
 The data company provides the data as grids that show the heights of the trees.
 The rows of the grid represent the east-west direction, and the columns represent the north-south direction.
 
-An acceptable tree will be the the largest in its row, while being the smallest in its column.
+An acceptable tree will be the largest in its row, while being the smallest in its column.
 
 A grid might not have any good trees at all.
 Or it might have one, or even several.
 
 Here is a grid that has exactly one candidate tree.
 
+```text
     1  2  3  4
   |-----------
 1 | 9  8  7  8
 2 | 5  3  2  4  <--- potential tree house at row 2, column 1, for tree with height 5
 3 | 6  6  7  1
+```
 
-- Row 2 has values 5, 3, and 1. The largest value is 5.
+- Row 2 has values 5, 3, 2, and 4. The largest value is 5.
 - Column 1 has values 9, 5, and 6. The smallest value is 5.
 
 So the point at `[2, 1]` (row: 2, column: 1) is a great spot for a tree house.
diff --git a/exercises/practice/saddle-points/.docs/introduction.md b/exercises/practice/saddle-points/.docs/introduction.md
index b582efbd..34b2c77e 100644
--- a/exercises/practice/saddle-points/.docs/introduction.md
+++ b/exercises/practice/saddle-points/.docs/introduction.md
@@ -1,9 +1,11 @@
 # Introduction
 
-You are planning on building a tree house in the woods near your house so that you can watch the sun rise and set.
+You plan to build a tree house in the woods near your house so that you can watch the sun rise and set.
 
-You've obtained data from a local survey company that shows the heights of all the trees in each rectangular section of the map.
-You need to analyze each grid on the map to find the perfect tree for your tree house.
+You've obtained data from a local survey company that show the height of every tree in each rectangular section of the map.
+You need to analyze each grid on the map to find good trees for your tree house.
 
-The best tree will be the tallest tree compared to all the other trees to the east and west, so that you have the best possible view of the sunrises and sunsets.
-You don't like climbing too much, so the perfect tree will also be the shortest among all the trees to the north and to the south.
+A good tree is both:
+
+- taller than every tree to the east and west, so that you have the best possible view of the sunrises and sunsets.
+- shorter than every tree to the north and south, to minimize the amount of tree climbing.
diff --git a/exercises/practice/secret-handshake/.docs/instructions.md b/exercises/practice/secret-handshake/.docs/instructions.md
index 77136cf0..d2120b9b 100644
--- a/exercises/practice/secret-handshake/.docs/instructions.md
+++ b/exercises/practice/secret-handshake/.docs/instructions.md
@@ -43,5 +43,6 @@ jump, double blink
 
 ~~~~exercism/note
 If you aren't sure what binary is or how it works, check out [this binary tutorial][intro-to-binary].
+
 [intro-to-binary]: https://medium.com/basecs/bits-bytes-building-with-binary-13cb4289aafa
 ~~~~
diff --git a/exercises/practice/sieve/.docs/instructions.md b/exercises/practice/sieve/.docs/instructions.md
index ec14620c..3adf1d55 100644
--- a/exercises/practice/sieve/.docs/instructions.md
+++ b/exercises/practice/sieve/.docs/instructions.md
@@ -18,11 +18,11 @@ Then you repeat the following steps:
 You keep repeating these steps until you've gone through every number in your list.
 At the end, all the unmarked numbers are prime.
 
-```exercism/note
+~~~~exercism/note
 [Wikipedia's Sieve of Eratosthenes article][eratosthenes] has a useful graphic that explains the algorithm.
 
 The tests don't check that you've implemented the algorithm, only that you've come up with the correct list of primes.
 A good first test is to check that you do not use division or remainder operations.
 
 [eratosthenes]: https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes
-```
+~~~~
diff --git a/exercises/practice/strain/.docs/instructions.md b/exercises/practice/strain/.docs/instructions.md
index 370eb221..3469ae65 100644
--- a/exercises/practice/strain/.docs/instructions.md
+++ b/exercises/practice/strain/.docs/instructions.md
@@ -1,9 +1,7 @@
 # Instructions
 
-Implement the `keep` and `discard` operation on collections. Given a collection
-and a predicate on the collection's elements, `keep` returns a new collection
-containing those elements where the predicate is true, while `discard` returns
-a new collection containing those elements where the predicate is false.
+Implement the `keep` and `discard` operation on collections.
+Given a collection and a predicate on the collection's elements, `keep` returns a new collection containing those elements where the predicate is true, while `discard` returns a new collection containing those elements where the predicate is false.
 
 For example, given the collection of numbers:
 
@@ -23,12 +21,9 @@ While your discard operation should produce:
 
 Note that the union of keep and discard is all the elements.
 
-The functions may be called `keep` and `discard`, or they may need different
-names in order to not clash with existing functions or concepts in your
-language.
+The functions may be called `keep` and `discard`, or they may need different names in order to not clash with existing functions or concepts in your language.
 
 ## Restrictions
 
-Keep your hands off that filter/reject/whatchamacallit functionality
-provided by your standard library!  Solve this one yourself using other
-basic tools instead.
+Keep your hands off that filter/reject/whatchamacallit functionality provided by your standard library!
+Solve this one yourself using other basic tools instead.
diff --git a/exercises/practice/sublist/.approaches/config.json b/exercises/practice/sublist/.approaches/config.json
new file mode 100644
index 00000000..ce54db9c
--- /dev/null
+++ b/exercises/practice/sublist/.approaches/config.json
@@ -0,0 +1,21 @@
+{
+  "introduction": {
+    "authors": ["safwansamsudeen"]
+  },
+  "approaches": [
+    {
+      "uuid": "db47397a-4551-49e8-8775-7e7aad79a38b",
+      "slug": "list-manipulation",
+      "title": "List manipulation",
+      "blurb": "Manipulate and check lists to solve the exercise",
+      "authors": ["safwansamsudeen"]
+    },
+    {
+      "uuid": "61366160-c859-4d16-9085-171428209b8d",
+      "slug": "using-strings",
+      "title": "Using strings",
+      "blurb": "Convert the lists to string and use string manipulation to solve the exercise",
+      "authors": ["safwansamsudeen"]
+    }
+  ]
+}
diff --git a/exercises/practice/sublist/.approaches/introduction.md b/exercises/practice/sublist/.approaches/introduction.md
new file mode 100644
index 00000000..42f991ef
--- /dev/null
+++ b/exercises/practice/sublist/.approaches/introduction.md
@@ -0,0 +1,59 @@
+# Introduction
+There are two broad ways to solve Sublist. 
+
+## General guidance
+To write the code, you need to branch out (probably with `if`) into the four different possible conditions, and return the appropriate name of the category. 
+
+## Approach: list manipulation
+The direct approach would be to manipulate and check the given lists to solve this.
+This solution uses a helper function, which simplifies things, but the approach can be implemented without it.
+
+```python
+SUBLIST = 1
+SUPERLIST = 2
+EQUAL = 3
+UNEQUAL = 4
+
+def check_sub_sequences(list_one, list_two):
+    n1 = len(list_one)
+    n2 = len(list_two)
+    return any(list_two[i:i+n1] == list_one for i in range(n2 - n1 + 1))
+    
+def sublist(list_one, list_two):
+    if list_one == list_two:
+        return EQUAL
+    if check_sub_sequences(list_one, list_two):
+        return SUBLIST
+    if check_sub_sequences(list_two, list_one):
+        return SUPERLIST
+    return UNEQUAL
+```
+
+Read more on the [detail of this approach][approach-list-manipulation].
+
+## Approach: using strings
+Another seemingly clever approach is to convert the lists to strings and then 
+use the `in` operator to check for sub-sequences.
+**However, this does not work.**
+```python
+SUBLIST = 1
+SUPERLIST = 2
+EQUAL = 3
+UNEQUAL = 4
+
+def sublist(list_one, list_two):
+    list_one_check = (str(list_one).strip("[]") + ",")
+    list_two_check = (str(list_two).strip("[]") + ",")
+
+    if list_one_check == list_two_check:
+        return EQUAL
+    elif list_one_check in list_two_check:
+        return SUBLIST
+    elif list_two_check in list_one_check:
+        return SUPERLIST
+    return UNEQUAL
+```
+To understand more about this approach and **why it fails**, [read here][approach-using-strings].
+
+[approach-list-manipulation]: https://exercism.org/tracks/python/exercises/sublist/approaches/list-manipulation
+[approach-using-strings]: https://exercism.org/tracks/python/exercises/sublist/approaches/using-strings
diff --git a/exercises/practice/sublist/.approaches/list-manipulation/content.md b/exercises/practice/sublist/.approaches/list-manipulation/content.md
new file mode 100644
index 00000000..ac374b73
--- /dev/null
+++ b/exercises/practice/sublist/.approaches/list-manipulation/content.md
@@ -0,0 +1,38 @@
+# List manipulation
+The direct approach would be to manipulate and check the given lists to solve this.
+This solution uses a helper function, which simplifies things, but the approach can be implemented without it.
+
+```python
+SUBLIST = 1
+SUPERLIST = 2
+EQUAL = 3
+UNEQUAL = 4
+
+def check_sub_sequences(list_one, list_two):
+    n1 = len(list_one)
+    n2 = len(list_two)
+    return any(list_two[i:i+n1] == list_one for i in range(n2 - n1 + 1))
+    
+def sublist(list_one, list_two):
+    if list_one == list_two:
+        return EQUAL
+    if check_sub_sequences(list_one, list_two):
+        return SUBLIST
+    if check_sub_sequences(list_two, list_one):
+        return SUPERLIST
+    return UNEQUAL
+```
+
+We first check for equality using the `==` operator, if so, then we return `EQUAL`. 
+A common way to do this differently would be to return `1` directly, but this is better practice as we  [remove magic values][magic values].
+
+After that we call `check_sub_sequences` passing in `list_one` and `list_two`.
+In the helper function, we check if `any` of the possible sub-sequences in `list_two` of length `n1` (the length of the first list) are equal to the first list.
+If so, then we conclude that `list_one` is a `SUBLIST` of `list_two`. 
+
+To find whether `list_one` is a `SUPERLIST` of `list_two`, we just reverse this process - pass in the lists in the opposite order.
+Thus, we check if `any` of the possible sub-sequences in `list_one` of length `n2` (the length of the second list) are equal to the second list.
+
+If none of the above conditions are true, we conclude that the two lists are unequal.
+
+[magic values]: https://stackoverflow.com/questions/47882/what-is-a-magic-number-and-why-is-it-bad
\ No newline at end of file
diff --git a/exercises/practice/sublist/.approaches/list-manipulation/snippet.txt b/exercises/practice/sublist/.approaches/list-manipulation/snippet.txt
new file mode 100644
index 00000000..290f8cdd
--- /dev/null
+++ b/exercises/practice/sublist/.approaches/list-manipulation/snippet.txt
@@ -0,0 +1,8 @@
+def sublist(list_one, list_two):
+    if list_one == list_two:
+        return EQUAL
+    if check_sub_sequences(list_one, list_two):
+        return SUBLIST
+    if check_sub_sequences(list_two, list_one):
+        return SUPERLIST
+    return UNEQUAL
\ No newline at end of file
diff --git a/exercises/practice/sublist/.approaches/using-strings/content.md b/exercises/practice/sublist/.approaches/using-strings/content.md
new file mode 100644
index 00000000..ff960902
--- /dev/null
+++ b/exercises/practice/sublist/.approaches/using-strings/content.md
@@ -0,0 +1,47 @@
+# Using strings
+~~~~exercism/caution
+**This approach does not work, and this document exists to explain that.** 
+Please do not use it in your code.
+~~~~
+
+Another seemingly clever solution is to convert the lists to strings and then 
+use the `in` operator to check for sub-sequences.
+Note that this approach, even if it worked, is not as performant as the 
+previous one.
+```python
+SUBLIST = 1
+SUPERLIST = 2
+EQUAL = 3
+UNEQUAL = 4
+
+def sublist(list_one, list_two):
+    list_one_check = str(list_one).strip("[]")
+    list_two_check = str(list_two).strip("[]")
+
+    if list_one_check == list_two_check:
+        return EQUAL
+    elif list_one_check in list_two_check:
+        return SUBLIST
+    elif list_two_check in list_one_check:
+        return SUPERLIST
+    return UNEQUAL
+```
+Let's parse the code to see what it does.
+In this approach, we convert the lists to strings, so `[1, 2, 3]` becomes `"[1, 2, 3]"`, remove the brackets `"1, 2, 3"`, and add a comma `"1, 2, 3,"`. 
+We check equality and then use the `in` operator to check for `SUBLIST` or `SUPERLIST`, and finally return `UNEQUAL`.
+
+We add a comma because, say, we call `sublist` with `[1, 2]` and `[1, 22]`. `"1, 2" in "1, 22"` evaluates to `True`, so 
+the **function would wrongly mark it as `SUBLIST`**. 
+
+This test can be overridden by changing the code like this:
+```python
+list_one_check = str(list_one).strip("[]") + ','
+list_two_check = str(list_two).strip("[]") + ','
+```
+Yet, the test case (which doesn't exist in the Exercism test suite) `["1", "2"]` and `["5", "'1', '2',", "7"]` would 
+fail.
+
+Students can add any arbitrary string into the representation to try to "defeat" this test - `list_one_check = str
+(list_one) + TOKEN`. The test suite currently test `TOKEN = ''`, but not others.
+
+[gen-exp]: https://www.programiz.com/python-programming/generator
\ No newline at end of file
diff --git a/exercises/practice/sublist/.approaches/using-strings/snippet.txt b/exercises/practice/sublist/.approaches/using-strings/snippet.txt
new file mode 100644
index 00000000..26fc3ec0
--- /dev/null
+++ b/exercises/practice/sublist/.approaches/using-strings/snippet.txt
@@ -0,0 +1,8 @@
+# Failing approach
+def sublist(list_one, list_two):
+    list_one_check = str(list_one).strip("[]")
+    ...
+    elif list_one_check in list_two_check:
+        return SUBLIST
+    ...
+    return UNEQUAL
\ No newline at end of file
diff --git a/exercises/practice/sublist/sublist.py b/exercises/practice/sublist/sublist.py
index 428d3aa6..35410301 100644
--- a/exercises/practice/sublist/sublist.py
+++ b/exercises/practice/sublist/sublist.py
@@ -1,8 +1,7 @@
 """
 This exercise stub and the test suite contain several enumerated constants.
 
-Since Python 2 does not have the enum module, the idiomatic way to write
-enumerated constants has traditionally been a NAME assigned to an arbitrary,
+Enumerated constants can be done with a NAME assigned to an arbitrary,
 but unique value. An integer is traditionally used because it’s memory
 efficient.
 It is a common practice to export both constants and functions that work with
diff --git a/exercises/practice/sum-of-multiples/.docs/instructions.md b/exercises/practice/sum-of-multiples/.docs/instructions.md
index e9a25636..d69f890e 100644
--- a/exercises/practice/sum-of-multiples/.docs/instructions.md
+++ b/exercises/practice/sum-of-multiples/.docs/instructions.md
@@ -7,12 +7,12 @@ The points awarded depend on two things:
 - The level (a number) that the player completed.
 - The base value of each magical item collected by the player during that level.
 
-The energy points are awarded according to the following rules:  
+The energy points are awarded according to the following rules:
 
 1. For each magical item, take the base value and find all the multiples of that value that are less than the level number.
 2. Combine the sets of numbers.
 3. Remove any duplicates.
-4. Calculate the sum of all the numbers that are left.  
+4. Calculate the sum of all the numbers that are left.
 
 Let's look at an example:
 
@@ -24,4 +24,4 @@ To calculate the energy points earned by the player, we need to find all the uni
 - Multiples of 5 less than 20: `{5, 10, 15}`
 - Combine the sets and remove duplicates: `{3, 5, 6, 9, 10, 12, 15, 18}`
 - Sum the unique multiples: `3 + 5 + 6 + 9 + 10 + 12 + 15 + 18 = 78`
-- Therefore, the player earns **78** energy points for completing level 20 and finding the two magical items with base values of 3 and 5
+- Therefore, the player earns **78** energy points for completing level 20 and finding the two magical items with base values of 3 and 5.
diff --git a/exercises/practice/trinary/.docs/instructions.md b/exercises/practice/trinary/.docs/instructions.md
index 3638ddb7..d38e3b5b 100644
--- a/exercises/practice/trinary/.docs/instructions.md
+++ b/exercises/practice/trinary/.docs/instructions.md
@@ -1,15 +1,13 @@
 # Instructions
 
-Convert a trinary number, represented as a string (e.g. '102012'), to its
-decimal equivalent using first principles.
+Convert a trinary number, represented as a string (e.g. '102012'), to its decimal equivalent using first principles.
 
-The program should consider strings specifying an invalid trinary as the
-value 0.
+The program should consider strings specifying an invalid trinary as the value 0.
 
 Trinary numbers contain three symbols: 0, 1, and 2.
 
-The last place in a trinary number is the 1's place. The second to last
-is the 3's place, the third to last is the 9's place, etc.
+The last place in a trinary number is the 1's place.
+The second to last is the 3's place, the third to last is the 9's place, etc.
 
 ```shell
 # "102012"
@@ -18,5 +16,4 @@ is the 3's place, the third to last is the 9's place, etc.
   243 +     0 +    54 +     0 +     3 +     2 =  302
 ```
 
-If your language provides a method in the standard library to perform the
-conversion, pretend it doesn't exist and implement it yourself.
+If your language provides a method in the standard library to perform the conversion, pretend it doesn't exist and implement it yourself.
diff --git a/exercises/practice/trinary/.meta/config.json b/exercises/practice/trinary/.meta/config.json
index 104b505a..da59ec6c 100644
--- a/exercises/practice/trinary/.meta/config.json
+++ b/exercises/practice/trinary/.meta/config.json
@@ -26,5 +26,5 @@
     ]
   },
   "source": "All of Computer Science",
-  "source_url": "http://www.wolframalpha.com/input/?i=binary&a=*C.binary-_*MathWorld-"
+  "source_url": "https://www.wolframalpha.com/examples/mathematics/numbers/base-conversions"
 }
diff --git a/exercises/practice/two-fer/.docs/instructions.md b/exercises/practice/two-fer/.docs/instructions.md
index a9bb4a3c..37aa7529 100644
--- a/exercises/practice/two-fer/.docs/instructions.md
+++ b/exercises/practice/two-fer/.docs/instructions.md
@@ -17,9 +17,9 @@ One for you, one for me.
 
 Here are some examples:
 
-|Name    |Dialogue
-|:-------|:------------------
-|Alice   |One for Alice, one for me.
-|Bohdan  |One for Bohdan, one for me.
-|        |One for you, one for me.
-|Zaphod  |One for Zaphod, one for me.
+| Name   | Dialogue                    |
+| :----- | :-------------------------- |
+| Alice  | One for Alice, one for me.  |
+| Bohdan | One for Bohdan, one for me. |
+|        | One for you, one for me.    |
+| Zaphod | One for Zaphod, one for me. |
diff --git a/exercises/practice/two-fer/.meta/template.j2 b/exercises/practice/two-fer/.meta/template.j2
index 51634eb1..5db1d53c 100644
--- a/exercises/practice/two-fer/.meta/template.j2
+++ b/exercises/practice/two-fer/.meta/template.j2
@@ -10,5 +10,3 @@ class {{ exercise | camel_case }}Test(unittest.TestCase):
         self.assertEqual({{ case["property"] | to_snake }}("{{ case["input"]["name"] }}"), "{{ case["expected"] }}")
         {% endif %}
     {% endfor %}
-
-{{ macros.footer() }}
diff --git a/exercises/practice/word-count/.meta/template.j2 b/exercises/practice/word-count/.meta/template.j2
index 2c91fdf9..7e41be67 100644
--- a/exercises/practice/word-count/.meta/template.j2
+++ b/exercises/practice/word-count/.meta/template.j2
@@ -21,6 +21,3 @@ class {{ exercise | camel_case }}Test(unittest.TestCase):
     {{ test_case(case) }}
     {% endfor %}
     {%- endif %}
-
-
-{{ macros.footer() }}
diff --git a/exercises/practice/word-search/.meta/template.j2 b/exercises/practice/word-search/.meta/template.j2
index 309a2fc1..952da9e9 100644
--- a/exercises/practice/word-search/.meta/template.j2
+++ b/exercises/practice/word-search/.meta/template.j2
@@ -20,6 +20,3 @@ class {{ exercise | camel_case }}Test(unittest.TestCase):
         {%- endfor %}
 
     {% endfor %}
-
-{{ macros.footer() }}
-
diff --git a/exercises/practice/yacht/.approaches/config.json b/exercises/practice/yacht/.approaches/config.json
new file mode 100644
index 00000000..86d37075
--- /dev/null
+++ b/exercises/practice/yacht/.approaches/config.json
@@ -0,0 +1,28 @@
+{
+  "introduction": {
+    "authors": ["safwansamsudeen"]
+  },
+  "approaches": [
+    {
+      "uuid": "3593cfe3-5cab-4141-b0a2-329148a66bb6",
+      "slug": "functions",
+      "title": "Lambdas with Functions",
+      "blurb": "Use lambdas with functions",
+      "authors": ["safwansamsudeen"]
+    },
+    {
+      "uuid": "eccd1e1e-6c88-4823-9b25-944eccaa92e7",
+      "slug": "if-structure",
+      "title": "If structure",
+      "blurb": "Use an if structure",
+      "authors": ["safwansamsudeen"]
+    },
+    {
+      "uuid": "72079791-e51f-4825-ad94-3b7516c631cc",
+      "slug": "structural-pattern-matching",
+      "title": "Structural Pattern Matching",
+      "blurb": "Use structural pattern matching",
+      "authors": ["safwansamsudeen"]
+    }
+  ]
+}
diff --git a/exercises/practice/yacht/.approaches/functions/content.md b/exercises/practice/yacht/.approaches/functions/content.md
new file mode 100644
index 00000000..2c6bfe52
--- /dev/null
+++ b/exercises/practice/yacht/.approaches/functions/content.md
@@ -0,0 +1,72 @@
+## Approach: Using Lambdas with Functions
+Each bit of functionality for each category can be encoded in an anonymous function (otherwise known as a [`lambda` expression][lambda] or lambda form), and the constant name set to that function.
+
+In `score`, we call the category (as it now points to a function) passing in `dice` as an argument.
+
+```python
+def digits(num):
+    return lambda dice: dice.count(num) * num
+
+YACHT = lambda dice: 50 if len(set(dice)) ==  1 else 0
+ONES = digits(1)
+TWOS = digits(2)
+THREES = digits(3)
+FOURS = digits(4)
+FIVES = digits(5)
+SIXES = digits(6)
+FULL_HOUSE = lambda dice: sum(dice) if len(set(dice)) == 2 and dice.count(dice[0]) in [2, 3] else 0
+FOUR_OF_A_KIND = lambda dice: 4 * dice[1] if dice[0] == dice[3] or dice[1] == dice[4] else 0
+LITTLE_STRAIGHT = lambda dice: 30 if sorted(dice) == [1, 2, 3, 4, 5] else 0
+BIG_STRAIGHT = lambda dice: 30 if sorted(dice) == [2, 3, 4, 5, 6] else 0
+CHOICE = sum
+
+def score(dice, category):
+    return category(dice)
+``` 
+
+
+Instead of setting each constant in `ONES` through `SIXES` to a separate function, we create a function `digits` that returns a function, using [closures][closures] transparently.
+
+For `LITTLE_STRAIGHT` and `BIG_STRAIGHT`, we first sort the dice and then check it against the hard-coded value. 
+Another way to solve this would be to check if `sum(dice) == 20 and len(set(dice)) == 5` (15 in `LITTLE_STRAIGHT`).
+In `CHOICE`, `lambda number : sum(number)` is shortened to just `sum`.
+
+In `FULL_HOUSE`, we create a `set` to remove the duplicates and check the set's length along with the individual counts. 
+For `FOUR_OF_A_KIND`, we check if the first and the fourth element are the same or the second and the last element are the same - if so, there are (at least) four of the same number in the array. 
+
+This solution is a succinct way to solve the exercise, although some of the one-liners can get a little long and hard to read. 
+Additionally, [PEP8][pep8] does not recommend assigning constant or variable names to `lambda` expressions, so it is a better practice to use `def`:
+```python
+def digits(num):
+    return lambda dice: dice.count(num) * num
+
+def YACHT(dice): return 50 if len(set(dice)) ==  1 else 0
+ONES = digits(1)
+TWOS = digits(2)
+THREES = digits(3)
+FOURS = digits(4)
+FIVES = digits(5)
+SIXES = digits(6)
+def FULL_HOUSE(dice): return sum(dice) if len(set(dice)) == 2 and dice.count(dice[0]) in [2, 3] else 0
+def FOUR_OF_A_KIND(dice): return 4 * sorted(dice)[1] if len(set(dice)) < 3 and dice.count(dice[0]) in (1, 4, 5) else 0
+def LITTLE_STRAIGHT(dice): return 30 if sorted(dice) == [1, 2, 3, 4, 5] else 0
+def BIG_STRAIGHT(dice): return 30 if sorted(dice) == [2, 3, 4, 5, 6] else 0
+CHOICE = sum
+
+def score(dice, category):
+    return category(dice)
+```
+
+As you can see from the examples, the [ternary operator][ternary-operator] (_or ternary form_) is crucial in solving the exercise using one liners.  
+As functions are being used, it might be a better strategy to spread the code over multiple lines to improve readability.
+```python
+def YACHT(dice):
+    if dice.count(dice[0]) == len(dice):
+        return 50
+    return 0
+```
+
+[closures]: https://www.programiz.com/python-programming/closure
+[ternary-operator]: https://www.tutorialspoint.com/ternary-operator-in-python
+[lambda]: https://docs.python.org/3/howto/functional.html?highlight=lambda#small-functions-and-the-lambda-expression    
+[pep8]: https://peps.python.org/pep-0008/
\ No newline at end of file
diff --git a/exercises/practice/yacht/.approaches/functions/snippet.txt b/exercises/practice/yacht/.approaches/functions/snippet.txt
new file mode 100644
index 00000000..34d270ad
--- /dev/null
+++ b/exercises/practice/yacht/.approaches/functions/snippet.txt
@@ -0,0 +1,8 @@
+def digits(num):
+    return lambda dice: dice.count(num) * num
+YACHT = lambda x: 50 if x.count(x[0]) == len(x) else 0
+ONES = digits(1)
+FULL_HOUSE = lambda x: sum(x) if len(set(x)) == 2 and x.count(x[0]) in [2, 3] else 0
+LITTLE_STRAIGHT = lambda x: 30 if sorted(x) == [1, 2, 3, 4, 5] else 0
+def score(dice, category):
+    return category(dice)
\ No newline at end of file
diff --git a/exercises/practice/yacht/.approaches/if-structure/content.md b/exercises/practice/yacht/.approaches/if-structure/content.md
new file mode 100644
index 00000000..581f31d1
--- /dev/null
+++ b/exercises/practice/yacht/.approaches/if-structure/content.md
@@ -0,0 +1,50 @@
+# If structure
+
+The constants here can be set to random, null, or numeric values, and an `if` structure inside the `score` function can determine the code to be executed. 
+ 
+As one-liners aren't necessary here, we can spread out the code to make it look neater:
+```python
+ONES = 1
+TWOS = 2
+THREES = 3
+FOURS = 4
+FIVES = 5
+SIXES = 6
+FULL_HOUSE = 'FULL_HOUSE'
+FOUR_OF_A_KIND = 'FOUR_OF_A_KIND'
+LITTLE_STRAIGHT = 'LITTLE_STRAIGHT'
+BIG_STRAIGHT = 'BIG_STRAIGHT'
+CHOICE = 'CHOICE'
+YACHT = 'YACHT'
+
+def score(dice, category):
+    if category in (1,2,3,4,5,6):
+         return dice.count(category) * category
+    elif category == 'FULL_HOUSE':
+        if len(set(dice)) == 2 and dice.count(dice[0]) in [2, 3]:
+            return sum(dice) or 0
+    elif category == 'FOUR_OF_A_KIND':
+        if dice[0] == dice[3] or dice[1] == dice[4]:
+            return dice[1] * 4 or 0
+    elif category == 'LITTLE_STRAIGHT':
+        if sorted(dice) == [1, 2, 3, 4, 5]: 
+            return 30 or 0
+    elif category == 'BIG_STRAIGHT':
+        if sorted(dice) == [2, 3, 4, 5, 6]:
+            return 30 or 0
+    elif category == 'YACHT':
+        if all(num == dice[0] for num in dice):
+            return 50
+    elif category == 'CHOICE':
+        return sum(dice)
+    return 0
+```
+Note that the code inside the `if` statements themselves can differ, but the key idea here is to use `if` and `elif` to branch out the code, and return `0` at the end if nothing else has been returned. 
+The `if` condition itself can be different, with people commonly checking if `category == ONES` as opposed to `category == 'ONES'` (or whatever the dummy value is).
+
+This may not be an ideal way to solve the exercise, as the code is rather long and convoluted.
+However, it is a valid (_and fast_) solution.
+Using [structural pattern matching][structural pattern matching], introduced in Python 3.10, could shorten and clarify the code in this situation.
+Pulling some logic out of the `score` function and into additional "helper" functions could also help.
+
+[structural pattern matching]: https://peps.python.org/pep-0636/
\ No newline at end of file
diff --git a/exercises/practice/yacht/.approaches/if-structure/snippet.txt b/exercises/practice/yacht/.approaches/if-structure/snippet.txt
new file mode 100644
index 00000000..fb590cc1
--- /dev/null
+++ b/exercises/practice/yacht/.approaches/if-structure/snippet.txt
@@ -0,0 +1,8 @@
+ONES = 1
+YACHT = 'YACHT'
+def score(dice, category):
+    if category == 'ONES':
+        ...
+    elif category == 'FULL_HOUSE':
+        ...
+    return 0
\ No newline at end of file
diff --git a/exercises/practice/yacht/.approaches/introduction.md b/exercises/practice/yacht/.approaches/introduction.md
new file mode 100644
index 00000000..5a37d168
--- /dev/null
+++ b/exercises/practice/yacht/.approaches/introduction.md
@@ -0,0 +1,76 @@
+# Introduction
+Yacht in Python can be solved in many ways. The most intuitive approach is to use an `if` structure. 
+Alternatively, you can create functions and set their names to the constant names.
+
+## General guidance
+The main thing in this exercise is to map a category (_here defined as constants in the stub file_) to a function or a standalone piece of code. 
+While mapping generally reminds us of dictionaries, here the constants are global.
+This indicates that the most idiomatic approach is not using a `dict`.
+Adhering to the principles of DRY is important - don't repeat yourself if you can help it, especially in the `ONES` through `SIXES` categories!
+
+## Approach: functions
+Each bit of functionality for each category can be encoded in a function, and the constant name set to that function. 
+This can be done by assigning the constant name to a `lambda` or creating a one-line function using the constant as a function name. 
+```python
+def digits(num):
+    return lambda dice: dice.count(num) * num
+YACHT = lambda dice: 50 if dice.count(dice[0]) == len(dice) else 0
+ONES = digits(1)
+TWOS = digits(2)
+THREES = digits(3)
+FOURS = digits(4)
+FIVES = digits(5)
+SIXES = digits(6)
+FULL_HOUSE = lambda dice: sum(dice) if len(set(dice)) == 2 and dice.count(dice[0]) in [2, 3] else 0
+FOUR_OF_A_KIND = lambda dice: 4 * dice[1] if dice[0] == dice[3] or dice[1] == dice[4] else 0
+LITTLE_STRAIGHT = lambda dice: 30 if sorted(dice) == [1, 2, 3, 4, 5] else 0
+BIG_STRAIGHT = lambda dice: 30 if sorted(dice) == [2, 3, 4, 5, 6] else 0
+CHOICE = sum
+def score(dice, category):
+    return category(dice)
+```
+This is a very succinct way to solve the exercise, although some one-liners get a little long. 
+For more information on this approach, read [this document][approach-functions].
+
+## Approach: if structure
+The constants can be set to random, null, or numeric values, and an `if` structure inside `score` determines the code to be executed. 
+As one-liners aren't necessary here, we can spread out the code to make it look neater:
+```python
+ONES = 1
+TWOS = 2
+THREES = 3
+FOURS = 4
+FIVES = 5
+SIXES = 6
+FULL_HOUSE = 'FULL_HOUSE'
+FOUR_OF_A_KIND = 'FOUR_OF_A_KIND'
+LITTLE_STRAIGHT = 'LITTLE_STRAIGHT'
+BIG_STRAIGHT = 'BIG_STRAIGHT'
+CHOICE = 'CHOICE'
+YACHT = 'YACHT'
+def score(dice, category):
+    if category in (1,2,3,4,5,6):
+         return dice.count(category) * category
+    elif category == 'FULL_HOUSE':
+        if len(set(dice)) == 2 and dice.count(dice[0]) in [2, 3]:
+            return sum(dice) or 0
+    elif category == 'FOUR_OF_A_KIND':
+        if dice[0] == dice[3] or dice[1] == dice[4]:
+            return dice[1] * 4 or 0
+    elif category == 'LITTLE_STRAIGHT':
+        if sorted(dice) == [1, 2, 3, 4, 5]: 
+            return 30 or 0
+    elif category == 'BIG_STRAIGHT':
+        if sorted(dice) == [2, 3, 4, 5, 6]:
+            return 30 or 0
+    elif category == 'YACHT':
+        if all(num == dice[0] for num in dice):
+            return 50
+    elif category == 'CHOICE':
+        return sum(dice)
+    return 0
+```
+Read more on this approach [here][approach-if-structure].
+
+[approach-functions]: https://exercism.org/tracks/python/exercises/yacht/approaches/functions
+[approach-if-structure]: https://exercism.org/tracks/python/exercises/yacht/approaches/if-structure
diff --git a/exercises/practice/yacht/.approaches/structural-pattern-matching/content.md b/exercises/practice/yacht/.approaches/structural-pattern-matching/content.md
new file mode 100644
index 00000000..b49bb634
--- /dev/null
+++ b/exercises/practice/yacht/.approaches/structural-pattern-matching/content.md
@@ -0,0 +1,55 @@
+# Structural Pattern Matching
+
+Another very interesting approach is to use [structural pattern matching][structural pattern matching]. 
+Existing in Python since 3.10, this feature allows for neater code than traditional if structures.
+ 
+By and large, we reuse the code from the [if structure approach][approach-if-structure]. 
+We set the constants to random values and check for them in the `match` structure. 
+`category` is the "subject", and in every other line, we check it against a "pattern". 
+```python
+ONES = 1
+TWOS = 2
+THREES = 3
+FOURS = 4
+FIVES = 5
+SIXES = 6
+FULL_HOUSE = 'FULL_HOUSE'
+FOUR_OF_A_KIND = 'FOUR_OF_A_KIND'
+LITTLE_STRAIGHT = 'LITTLE_STRAIGHT'
+BIG_STRAIGHT = 'BIG_STRAIGHT'
+CHOICE = 'CHOICE'
+YACHT = 'YACHT'
+
+def score(dice, category):
+    match category:
+        case 1 | 2 | 3 | 4 | 5 | 6:
+            return dice.count(category) * category
+        case 'FULL_HOUSE' if len(set(dice)) == 2 and dice.count(dice[0]) in [2, 3]:
+            return sum(dice)
+        case 'FOUR_OF_A_KIND' if dice[0] == dice[3] or dice[1] == dice[4]:
+            return dice[1] * 4
+        case 'LITTLE_STRAIGHT' if sorted(dice) == [1, 2, 3, 4, 5]:
+            return 30
+        case 'BIG_STRAIGHT' if sorted(dice) == [2, 3, 4, 5, 6]:
+            return 30 
+        case 'YACHT' if all(num == dice[0] for num in dice):
+            return 50
+        case 'CHOICE':
+            return sum(dice)
+        case _:
+            return 0
+```
+For the first pattern, we utilize "or patterns", using the `|` operator. 
+This checks whether the subject is any of the provided patterns.
+
+In the next five patterns, we check an additional condition along with the pattern matching. 
+Finally, we use the wildcard operator `_` to match anything. 
+As the compiler checks the patterns (`case`s) in order, `return 0` will be executed if none of the other patterns match. 
+
+Note that the conditions might differ, but the patterns must have hard coded values - that is, you can't say `case ONES ...` instead of `case 1 ...`. 
+This will capture the category and lead to unexpected behavior. 
+
+This code is much clenaer than the corresponding `if` structure code.
+
+[structural pattern matching]: https://peps.python.org/pep-0636/
+[approach-if-structure]: https://exercism.org/tracks/python/exercises/yacht/approaches/if-structure
\ No newline at end of file
diff --git a/exercises/practice/yacht/.approaches/structural-pattern-matching/snippet.txt b/exercises/practice/yacht/.approaches/structural-pattern-matching/snippet.txt
new file mode 100644
index 00000000..4ed99824
--- /dev/null
+++ b/exercises/practice/yacht/.approaches/structural-pattern-matching/snippet.txt
@@ -0,0 +1,8 @@
+ONES = 1
+YACHT = 'YACHT'
+def score(dice, category):
+    match category:
+        case 1 | 2 | 3 | 4 | 5 | 6:
+            return dice.count(category) * category
+        case _:
+            return 0
\ No newline at end of file
diff --git a/exercises/practice/yacht/.meta/template.j2 b/exercises/practice/yacht/.meta/template.j2
index 4086f512..80eb31f7 100644
--- a/exercises/practice/yacht/.meta/template.j2
+++ b/exercises/practice/yacht/.meta/template.j2
@@ -16,5 +16,3 @@ class {{ exercise | camel_case }}Test(unittest.TestCase):
             {{ case["expected"] }})
 
     {% endfor %}
-
-{{ macros.footer() }}
\ No newline at end of file
diff --git a/exercises/practice/zebra-puzzle/.meta/template.j2 b/exercises/practice/zebra-puzzle/.meta/template.j2
index 6b068c7e..134ccdbf 100644
--- a/exercises/practice/zebra-puzzle/.meta/template.j2
+++ b/exercises/practice/zebra-puzzle/.meta/template.j2
@@ -6,5 +6,3 @@ class {{ exercise | camel_case }}Test(unittest.TestCase):
     def test_{{ case["description"] | to_snake }}(self):
         self.assertEqual({{ case["property"] | to_snake }}(), "{{ case["expected"] }}")
     {% endfor %}
-
-{{ macros.footer() }}
diff --git a/exercises/practice/zipper/.meta/template.j2 b/exercises/practice/zipper/.meta/template.j2
index 9f239bc1..4e5de995 100644
--- a/exercises/practice/zipper/.meta/template.j2
+++ b/exercises/practice/zipper/.meta/template.j2
@@ -52,5 +52,3 @@ class {{ exercise | camel_case }}Test(unittest.TestCase):
 {%- for case in cases %}
     {{ test_case(case) }}
 {%- endfor %}
-
-{{ macros.footer() }}
diff --git a/pylintrc b/pylintrc
index 745e6f03..09795978 100644
--- a/pylintrc
+++ b/pylintrc
@@ -341,11 +341,6 @@ known-standard-library=
 # Force import order to recognize a module as part of a third party library.
 known-third-party=enchant, absl
 
-# Analyse import fallback blocks. This can be used to support both Python 2 and
-# 3 compatible code, which means that the block might have code that exists
-# only in one or another interpreter, leading to false positives when analysed.
-analyse-fallback-blocks=no
-
 
 [CLASSES]
 
diff --git a/reference/exercise-concepts/rna-transcription.md b/reference/exercise-concepts/rna-transcription.md
index b2b12946..004ded95 100644
--- a/reference/exercise-concepts/rna-transcription.md
+++ b/reference/exercise-concepts/rna-transcription.md
@@ -2,7 +2,7 @@
 
 ## Example implementation
 
-Modified from the existing [example.py](https://github.com/exercism/python/blob/master/exercises/rna-transcription/example.py) to remove Python 2 compatiblity noise:
+Taken from the existing [example.py](https://github.com/exercism/python/blob/main/exercises/practice/rna-transcription/.meta/example.py):
 
 ```python
 DNA_TO_RNA = str.maketrans("AGCT", "UCGA")


Test patch:
diff --git a/exercises/concept/mecha-munch-management/dict_methods_test.py b/exercises/concept/mecha-munch-management/dict_methods_test.py
new file mode 100644
index 00000000..2f5828d6
--- /dev/null
+++ b/exercises/concept/mecha-munch-management/dict_methods_test.py
@@ -0,0 +1,161 @@
+import unittest
+import pytest
+from dict_methods import (add_item,
+                          read_notes,
+                          update_recipes,
+                          sort_entries,
+                          send_to_store,
+                          update_store_inventory)
+
+
+class MechaMunchManagementTest(unittest.TestCase):
+
+    @pytest.mark.task(taskno=1)
+    def test_add_item(self):
+        input_data = [
+                      ({'Apple': 1, 'Banana': 4 }, ('Apple', 'Banana', 'Orange')),
+                      ({'Orange': 1, 'Raspberry': 1, 'Blueberries': 10}, ['Raspberry', 'Blueberries', 'Raspberry']),
+                      ({'Broccoli': 1, 'Banana': 1}, ('Broccoli', 'Kiwi', 'Kiwi', 'Kiwi', 'Melon', 'Apple', 'Banana', 'Banana'))
+                      ]
+        
+        output_data = [{'Apple': 2, 'Banana': 5, 'Orange': 1},
+                       {'Orange': 1, 'Raspberry': 3, 'Blueberries': 11},
+                       {'Broccoli': 2, 'Banana': 3, 'Kiwi': 3, 'Melon': 1, 'Apple': 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 shopping cart.'
+                self.assertEqual(add_item(input_data[0], input_data[1]), output_data, msg=error_msg)
+        
+
+    @pytest.mark.task(taskno=2)
+    def test_read_notes(self):
+        input_data = [('Apple', "Banana"), ('Orange', 'Raspberry', 'Blueberries'),
+                      ['Broccoli', 'Kiwi', 'Melon', 'Apple', 'Banana']]
+
+        output_data = [{'Apple': 1, 'Banana': 1}, {'Orange': 1, 'Raspberry': 1, 'Blueberries': 1},
+                       {'Broccoli': 1, 'Kiwi': 1, 'Melon': 1, 'Apple': 1, 'Banana': 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 shopping cart.'
+                self.assertEqual(read_notes(input_data), output_data, msg=error_msg)
+
+    @pytest.mark.task(taskno=3)
+    def test_update_recipes(self):
+        input_data = [
+                        ({'Banana Bread' : {'Banana': 1, 'Apple': 1, 'Walnuts': 1, 'Flour': 1, 'Eggs': 2, 'Butter': 1},
+                          'Raspberry Pie' : {'Raspberry': 1, 'Orange': 1, 'Pie Crust': 1, 'Cream Custard': 1}},
+                        (('Banana Bread', {'Banana': 4,  'Walnuts': 2, 'Flour': 1, 'Eggs': 2, 'Butter': 1, 'Milk': 2, 'Eggs': 3}),)),
+
+                        ({'Apple Pie': {'Apple': 1, 'Pie Crust': 1, 'Cream Custard': 1},
+                          'Blueberry Pie': {'Blueberries': 1, 'Pie Crust': 1, 'Cream Custard': 1}},
+                        (('Blueberry Pie', {'Blueberries': 2, 'Pie Crust': 1, 'Cream Custard': 1}),
+                         ('Apple Pie', {'Apple': 1, 'Pie Crust': 1, 'Cream Custard': 1}))),
+
+                        ({'Banana Bread' : {'Banana': 1, 'Apple': 1, 'Walnuts': 1, 'Flour': 1, 'Eggs': 2, 'Butter': 1},
+                          'Raspberry Pie' : {'Raspberry': 1, 'Orange': 1, 'Pie Crust': 1, 'Cream Custard': 1},
+                          'Pasta Primavera': {'Eggs': 1, 'Carrots': 1, 'Spinach': 2, 'Tomatoes': 3, 'Parmesan': 2, 'Milk': 1, 'Onion': 1}},
+                        (('Raspberry Pie', {'Raspberry': 3, 'Orange': 1, 'Pie Crust': 1, 'Cream Custard': 1, 'Whipped Cream': 2}),
+                         ('Pasta Primavera', {'Eggs': 1, 'Mixed Veggies': 2, 'Parmesan': 2, 'Milk': 1, 'Spinach': 1, 'Bread Crumbs': 1}),
+                         ('Blueberry Crumble', {'Blueberries': 2, 'Whipped Creme': 2, 'Granola Topping': 2, 'Yogurt': 3})))
+                     ]
+
+        output_data = [
+                        {'Banana Bread': {'Banana': 4,  'Walnuts': 2, 'Flour': 1, 'Eggs': 2, 'Butter': 1, 'Milk': 2, 'Eggs': 3},
+                         'Raspberry Pie': {'Raspberry': 1, 'Orange': 1, 'Pie Crust': 1, 'Cream Custard': 1}},
+                        {'Apple Pie': {'Apple': 1, 'Pie Crust': 1, 'Cream Custard': 1},
+                        'Blueberry Pie': {'Blueberries': 2, 'Pie Crust': 1, 'Cream Custard': 1}},
+                        {'Banana Bread': {'Banana': 1, 'Apple': 1, 'Walnuts': 1, 'Flour': 1, 'Eggs': 2, 'Butter': 1},
+                        'Raspberry Pie': {'Raspberry': 3, 'Orange': 1, 'Pie Crust': 1, 'Cream Custard': 1, 'Whipped Cream': 2},
+                        'Pasta Primavera': {'Eggs': 1, 'Mixed Veggies': 2, 'Parmesan': 2, 'Milk': 1, 'Spinach': 1, 'Bread Crumbs': 1},
+                        'Blueberry Crumble': {'Blueberries': 2, 'Whipped Creme': 2, 'Granola Topping': 2, 'Yogurt': 3}}
+                     ]
+
+        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 ideas instead.'
+                self.assertEqual(update_recipes(input_data[0], input_data[1]), output_data, msg=error_msg)
+
+    @pytest.mark.task(taskno=4)
+    def test_sort_entries(self):
+        input_data = [
+                      {'Banana': 4, 'Apple': 2, 'Orange': 1, 'Pear': 12},
+                      {'Apple': 3, 'Orange': 5, 'Banana': 1, 'Avocado': 2},
+                      {'Orange': 3, 'Banana': 2, 'Apple': 1},
+                      {'Apple': 2, 'Raspberry': 2, 'Blueberries': 5, 'Broccoli' : 2, 'Kiwi': 1, 'Melon': 4}
+                     ]
+
+        output_data = [
+                        {'Apple': 2, 'Banana': 4, 'Orange': 1, 'Pear': 12},
+                        {'Avocado': 2, 'Apple': 3, 'Banana': 1, 'Orange': 5},
+                        {'Apple': 1, 'Orange': 3, 'Banana': 2},
+                        {'Apple' : 2, 'Blueberries': 5, 'Broccoli': 2, 'Kiwi': 1, 'Melon': 4, 'Raspberry': 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 sorted list instead.'
+                self.assertEqual(sort_entries(input_data), output_data, msg=error_msg)
+
+    @pytest.mark.task(taskno=5)
+    def test_send_to_store(self):
+        input_data = [
+                        ({'Banana': 3, 'Apple': 2, 'Orange': 1, 'Milk': 2},
+                         {'Banana': ['Isle 5', False], 'Apple': ['Isle 4', False], 'Orange': ['Isle 4', False], 'Milk': ['Isle 2', True]}),
+
+                        ({'Kiwi': 3, 'Juice': 5, 'Yoghurt': 2, 'Milk': 5},
+                         {'Kiwi': ['Isle 6', False], 'Juice': ['Isle 5', False], 'Yoghurt': ['Isle 2', True], 'Milk': ['Isle 2', True]}),
+
+                        ({'Apple': 2, 'Raspberry': 2, 'Blueberries': 5, 'Broccoli' : 2, 'Kiwi': 1, 'Melon': 4},
+                         {'Apple': ['Isle 1', False], 'Raspberry': ['Isle 6', False], 'Blueberries': ['Isle 6', False],
+                          'Broccoli': ['Isle 3', False], 'Kiwi': ['Isle 6', False], 'Melon': ['Isle 6', False]})
+                      ]
+
+        output_data = [
+                        {'Orange': [1, 'Isle 4', False], 'Milk': [2, 'Isle 2', True], 'Banana': [3, 'Isle 5', False], 'Apple': [2, 'Isle 4', False]},
+                        {'Juice': [5, 'Isle 5', False], 'Yoghurt': [2, 'Isle 2', True], 'Milk': [5, 'Isle 2', True], 'Kiwi': [3, 'Isle 6', False]},
+                        {'Kiwi': [1, 'Isle 6', False], 'Melon': [4, 'Isle 6', False], 'Apple': [2, 'Isle 1', False],
+                         'Raspberry': [2, 'Isle 6', False], 'Blueberries': [5, 'Isle 6', False], 'Broccoli': [2, 'Isle 3', False]}
+                      ]
+
+        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 fulfillment_cart instead.'
+                self.assertEqual(send_to_store(input_data[0], input_data[1]), output_data, msg=error_msg)
+
+    @pytest.mark.task(taskno=6)
+    def test_update_store_inventory(self):
+        input_data = [
+                        ({'Orange': [1, 'Isle 4', False], 'Milk': [2, 'Isle 2', True],
+                          'Banana': [3, 'Isle 5', False], 'Apple': [2, 'Isle 4', False]},
+                         {'Banana': [15, 'Isle 5', False], 'Apple': [12, 'Isle 4', False],
+                          'Orange': [1, 'Isle 4', False], 'Milk': [4, 'Isle 2', True]}),
+
+                        ({'Kiwi': [3, 'Isle 6', False]},{'Kiwi': [3, 'Isle 6', False], 'Juice': [5, 'Isle 5', False],
+                          'Yoghurt': [2, 'Isle 2', True], 'Milk': [5, 'Isle 2', True]}),
+
+                        ({'Kiwi': [1, 'Isle 6', False], 'Melon': [4, 'Isle 6', False], 'Apple': [2, 'Isle 1', False],
+                          'Raspberry': [2, 'Isle 6', False], 'Blueberries': [5, 'Isle 6', False],
+                          'Broccoli': [1, 'Isle 3', False]},
+                         {'Apple': [2, 'Isle 1', False], 'Raspberry': [5, 'Isle 6', False],
+                          'Blueberries': [10, 'Isle 6', False], 'Broccoli': [4, 'Isle 3', False],
+                          'Kiwi': [1, 'Isle 6', False], 'Melon': [8, 'Isle 6', False]})
+                      ]
+
+        output_data = [
+                        {'Banana': [12, 'Isle 5', False], 'Apple': [10, 'Isle 4', False],
+                         'Orange': ['Out of Stock', 'Isle 4', False], 'Milk': [2, 'Isle 2', True]},
+
+                        {'Juice': [5, 'Isle 5', False], 'Yoghurt': [2, 'Isle 2', True],
+                         'Milk': [5, 'Isle 2', True], 'Kiwi': ["Out of Stock", 'Isle 6', False]},
+
+                        {'Kiwi': ['Out of Stock', 'Isle 6', False], 'Melon': [4, 'Isle 6', False],
+                         'Apple': ['Out of Stock', 'Isle 1', False], 'Raspberry': [3, 'Isle 6', False],
+                         'Blueberries': [5, 'Isle 6', False], 'Broccoli': [3, 'Isle 3', False]}
+                      ]
+
+        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 store inventory instead.'
+                self.assertEqual(update_store_inventory(input_data[0], input_data[1]), output_data, msg=error_msg)
diff --git a/exercises/concept/plane-tickets/plane_tickets_test.py b/exercises/concept/plane-tickets/plane_tickets_test.py
index f4255c43..1372e5cd 100644
--- a/exercises/concept/plane-tickets/plane_tickets_test.py
+++ b/exercises/concept/plane-tickets/plane_tickets_test.py
@@ -3,6 +3,7 @@ import unittest
 import pytest
 
 from plane_tickets import (
+    generate_seat_letters,
     generate_seats,
     assign_seats,
     generate_codes
@@ -10,16 +11,31 @@ from plane_tickets import (
 
 class PlaneTicketsTest(unittest.TestCase):
 
-
     @pytest.mark.task(taskno=1)
     def test_task1_is_generator(self):  # * Tests if [Task 1] actually returns a generator.
         input_var = 5
         output_type = Generator
         error_message = f"Expected: {str(output_type)} type, but got a different type."
-        self.assertIsInstance(generate_seats(input_var), output_type, msg=error_message)
+        self.assertIsInstance(generate_seat_letters(input_var), output_type, msg=error_message)
 
     @pytest.mark.task(taskno=1)
     def test_task1_output(self):
+        input_vars = [1, 2, 3, 4, 5]
+        output = [["A"], ["A", "B"], ["A", "B", "C"], ["A", "B", "C", "D"], ["A", "B", "C", "D", "A"]]
+        for variant, (input_var, output) in enumerate(zip(input_vars, output), start=1):
+            error_message = f"Expected: {output}, but something went wrong while generating {input_var} seat(s)."
+            with self.subTest(f"variation #{variant}", input_data=input_var, output_data=output):
+                self.assertEqual(list(generate_seat_letters(input_var)), output, msg=error_message)
+
+    @pytest.mark.task(taskno=2)
+    def test_task2_is_generator(self):  # * Tests if [Task 2] actually returns a generator.
+        input_var = 5
+        output_type = Generator
+        error_message = f"Expected: {str(output_type)} type, but got a different type."
+        self.assertIsInstance(generate_seats(input_var), output_type, msg=error_message)
+
+    @pytest.mark.task(taskno=2)
+    def test_task2_output(self):
         input_vars = [1, 2, 3, 4, 5]
         output = [["1A"], ["1A", "1B"], ["1A", "1B", "1C"], ["1A", "1B", "1C", "1D"], ["1A", "1B", "1C", "1D", "2A"]]
         for variant, (input_var, output) in enumerate(zip(input_vars, output), start=1):
@@ -27,8 +43,8 @@ class PlaneTicketsTest(unittest.TestCase):
             with self.subTest(f"variation #{variant}", input_data=input_var, output_data=output):
                 self.assertEqual(list(generate_seats(input_var)), output, msg=error_message)
 
-    @pytest.mark.task(taskno=1)
-    def test_task1_skips_row_13(self):
+    @pytest.mark.task(taskno=2)
+    def test_task3_skips_row_13(self):
         input_vars = [14 * 4]
         output = [["1A", "1B", "1C", "1D", "2A", "2B", "2C", "2D",
                    "3A", "3B", "3C", "3D", "4A", "4B", "4C", "4D",
@@ -42,8 +58,8 @@ class PlaneTicketsTest(unittest.TestCase):
             with self.subTest(f"variation #{variant}", input_data=input_var, output_data=output):
                 self.assertEqual(list(generate_seats(input_var)), output, msg=error_message)
 
-    @pytest.mark.task(taskno=2)
-    def test_task2(self):
+    @pytest.mark.task(taskno=3)
+    def test_task3(self):
         input_vars = [["Passenger1", "Passenger2", "Passenger3", "Passenger4", "Passenger5"], 
                       ["TicketNo=5644", "TicketNo=2273", "TicketNo=493", "TicketNo=5411", "TicketNo=824"]]
         output = [{"Passenger1": "1A", "Passenger2": "1B", "Passenger3": "1C", "Passenger4": "1D", "Passenger5": "2A"}, 
@@ -53,15 +69,15 @@ class PlaneTicketsTest(unittest.TestCase):
             with self.subTest(f"variation #{variant}", input_data=input_var, output_data=output):
                 self.assertEqual(assign_seats(input_var), output, msg=error_message)
 
-    @pytest.mark.task(taskno=3)
-    def test_task3_is_generator(self):
+    @pytest.mark.task(taskno=4)
+    def test_task4_is_generator(self):
         input_var = ("11B", "HA80085")
         output_type = Generator
         error_message = f"Expected: {str(output_type)} type, but got a different type."
         self.assertIsInstance(generate_codes(input_var[0], input_var[1]), output_type, msg=error_message)
 
-    @pytest.mark.task(taskno=3)
-    def test_task3(self):
+    @pytest.mark.task(taskno=4)
+    def test_task4(self):
         input_vars = [(["12A", "38B", "69C", "102B"],"KL1022"),
                       (["22C", "88B", "33A", "44B"], "DL1002")]
         output = [['12AKL1022000', '38BKL1022000', '69CKL1022000', '102BKL102200'],
diff --git a/exercises/practice/pythagorean-triplet/pythagorean_triplet_test.py b/exercises/practice/pythagorean-triplet/pythagorean_triplet_test.py
index 70d501c4..bb094c90 100644
--- a/exercises/practice/pythagorean-triplet/pythagorean_triplet_test.py
+++ b/exercises/practice/pythagorean-triplet/pythagorean_triplet_test.py
@@ -6,10 +6,6 @@ from pythagorean_triplet import (
 
 # Tests adapted from `problem-specifications//canonical-data.json`
 
-# Python 2/3 compatibility
-if not hasattr(unittest.TestCase, "assertCountEqual"):
-    unittest.TestCase.assertCountEqual = unittest.TestCase.assertItemsEqual
-
 
 class PythagoreanTripletTest(unittest.TestCase):
     def test_triplets_whose_sum_is_12(self):
diff --git a/exercises/practice/resistor-color-expert/.meta/tests.toml b/exercises/practice/resistor-color-expert/.meta/tests.toml
new file mode 100644
index 00000000..4f57723a
--- /dev/null
+++ b/exercises/practice/resistor-color-expert/.meta/tests.toml
@@ -0,0 +1,40 @@
+# 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.
+
+[8c4f9fb6-d477-4250-bc57-b325d2be226f]
+description = "Orange, orange, black, and red"
+
+[d1d4a769-9210-43cc-9a14-6af6ce4c0b00]
+description = "Blue, grey, brown, and violet"
+
+[6af91bc3-8275-4c38-920a-185d30feb5f3]
+description = "Red, black, red, and green"
+
+[9c4630bf-0dda-4212-baca-2f5111530b4d]
+description = "Green, brown, orange, and grey"
+
+[5880ddf1-0dc6-4bd0-b9de-5626117cd2c7]
+description = "One black band"
+
+[a5cfda34-3c02-4bda-b183-726791fb43b2]
+description = "Orange, orange, yellow, black, and brown"
+
+[4f0ad96c-cdab-4c84-95dd-7074e889e001]
+description = "Red, green, yellow, yellow, and brown"
+
+[48c66841-208c-46a7-8992-1e84a2eda9e2]
+description = "Blue, grey, white, brown, and brown"
+
+[4f3aeb0c-fd9d-4cbd-b204-554e61978f73]
+description = "Violet, orange, red, and grey"
+
+[1ae3a17f-8bc0-449c-9831-fddfd2d91c5d]
+description = "Brown, red, orange, green, and blue"
diff --git a/exercises/practice/resistor-color-expert/resistor_color_expert_test.py b/exercises/practice/resistor-color-expert/resistor_color_expert_test.py
new file mode 100644
index 00000000..bcf2052c
--- /dev/null
+++ b/exercises/practice/resistor-color-expert/resistor_color_expert_test.py
@@ -0,0 +1,51 @@
+import unittest
+
+from resistor_color_expert import (
+    resistor_label,
+)
+
+# Tests adapted from `problem-specifications//canonical-data.json`
+
+
+class ResistorColorExpertTest(unittest.TestCase):
+    def test_orange_orange_black_and_red(self):
+        self.assertEqual(resistor_label(["orange", "orange", "black", "red"]), "33 ohms ±2%")
+
+    def test_blue_grey_brown_and_violet(self):
+        self.assertEqual(resistor_label(["blue", "grey", "brown", "violet"]), "680 ohms ±0.1%")
+
+    def test_red_black_red_and_green(self):
+        self.assertEqual(resistor_label(["red", "black", "red", "green"]), "2 kiloohms ±0.5%")
+
+    def test_green_brown_orange_and_grey(self):
+        self.assertEqual(
+            resistor_label(["green", "brown", "orange", "grey"]), "51 kiloohms ±0.05%"
+        )
+
+    def test_one_black_band(self):
+        self.assertEqual(resistor_label(["black"]), "0 ohms")
+
+    def test_orange_orange_yellow_black_and_brown(self):
+        self.assertEqual(
+            resistor_label(["orange", "orange", "yellow", "black", "brown"]), "334 ohms ±1%"
+        )
+
+    def test_red_green_yellow_yellow_and_brown(self):
+        self.assertEqual(
+            resistor_label(["red", "green", "yellow", "yellow", "brown"]), "2.54 megaohms ±1%"
+        )
+
+    def test_blue_grey_white_red_and_brown(self):
+        self.assertEqual(
+            resistor_label(["blue", "grey", "white", "brown", "brown"]), "6.89 kiloohms ±1%"
+        )
+
+    def test_violet_orange_red_and_grey(self):
+        self.assertEqual(
+            resistor_label(["violet", "orange", "red", "grey"]), "7.3 kiloohms ±0.05%"
+        )
+
+    def test_brown_red_orange_green_and_blue(self):
+        self.assertEqual(
+            resistor_label(["brown", "red", "orange", "green", "blue"]), "12.3 megaohms ±0.25%"
+        )
diff --git a/exercises/practice/two-fer/two_fer_test.py b/exercises/practice/two-fer/two_fer_test.py
index 540930da..ea74f116 100644
--- a/exercises/practice/two-fer/two_fer_test.py
+++ b/exercises/practice/two-fer/two_fer_test.py
@@ -16,7 +16,3 @@ class TwoFerTest(unittest.TestCase):
 
     def test_another_name_given(self):
         self.assertEqual(two_fer("Bob"), "One for Bob, one for me.")
-
-
-if __name__ == "__main__":
-    unittest.main()
diff --git a/exercises/practice/word-count/word_count_test.py b/exercises/practice/word-count/word_count_test.py
index b63a9eb3..329301a3 100644
--- a/exercises/practice/word-count/word_count_test.py
+++ b/exercises/practice/word-count/word_count_test.py
@@ -121,7 +121,3 @@ class WordCountTest(unittest.TestCase):
 
     def test_multiple_apostrophes_ignored(self):
         self.assertEqual(count_words("''hey''"), {"hey": 1})
-
-
-if __name__ == "__main__":
-    unittest.main()
diff --git a/exercises/practice/word-search/word_search_test.py b/exercises/practice/word-search/word_search_test.py
index 6ee05041..e5a32cee 100644
--- a/exercises/practice/word-search/word_search_test.py
+++ b/exercises/practice/word-search/word_search_test.py
@@ -310,7 +310,3 @@ class WordSearchTest(unittest.TestCase):
     def test_should_not_wrap_around_vertically_to_find_a_word(self):
         puzzle = WordSearch(["s", "u", "r", "a", "b", "c", "t"])
         self.assertIsNone(puzzle.search("rust"))
-
-
-if __name__ == "__main__":
-    unittest.main()
diff --git a/exercises/practice/yacht/yacht_test.py b/exercises/practice/yacht/yacht_test.py
index 5c262c88..32b92994 100644
--- a/exercises/practice/yacht/yacht_test.py
+++ b/exercises/practice/yacht/yacht_test.py
@@ -92,7 +92,3 @@ class YachtTest(unittest.TestCase):
 
     def test_yacht_as_choice(self):
         self.assertEqual(yacht.score([2, 2, 2, 2, 2], yacht.CHOICE), 10)
-
-
-if __name__ == "__main__":
-    unittest.main()
diff --git a/exercises/practice/zebra-puzzle/zebra_puzzle_test.py b/exercises/practice/zebra-puzzle/zebra_puzzle_test.py
index 034d4ccc..4b35afb7 100644
--- a/exercises/practice/zebra-puzzle/zebra_puzzle_test.py
+++ b/exercises/practice/zebra-puzzle/zebra_puzzle_test.py
@@ -14,7 +14,3 @@ class ZebraPuzzleTest(unittest.TestCase):
 
     def test_resident_who_owns_zebra(self):
         self.assertEqual(owns_zebra(), "Japanese")
-
-
-if __name__ == "__main__":
-    unittest.main()
diff --git a/exercises/practice/zipper/zipper_test.py b/exercises/practice/zipper/zipper_test.py
index 5883e80d..d770c92b 100644
--- a/exercises/practice/zipper/zipper_test.py
+++ b/exercises/practice/zipper/zipper_test.py
@@ -315,7 +315,3 @@ class ZipperTest(unittest.TestCase):
         expected = Zipper.from_tree(final).right().to_tree()
 
         self.assertEqual(result, expected)
-
-
-if __name__ == "__main__":
-    unittest.main()