diff --git a/exercises/practice/darts/.approaches/booleans-as-ints/content.md b/exercises/practice/darts/.approaches/booleans-as-ints/content.md new file mode 100644 index 0000000000..d3c1541d2a --- /dev/null +++ b/exercises/practice/darts/.approaches/booleans-as-ints/content.md @@ -0,0 +1,36 @@ +# Using Boolean Values as Integers + + +```python +def score(x_coord, y_coord): + radius = (x_coord**2 + y_coord**2) + return (radius<=1)*5 + (radius<=25)*4 + (radius<=100)*1 +``` + + +In Python, the [Boolean values `True` and `False` are _subclasses_ of `int`][bools-as-ints] and can be interpreted as `0` (False) and `1` (True) in a mathematical context. +This approach leverages that interpretation by checking which areas the throw falls into and multiplying each Boolean `int` by a scoring multiple. +For example, a throw that lands on the 25 (_or 5 if using `math.sqrt(x**2 + y**2)`_) circle should have a score of 5: + +```python +>>> (False)*5 + (True)*4 + (True)*1 +5 +``` + + +This makes for very compact code and has the added boost of not requiring any `loops` or additional data structures. +However, it is considered bad form to rely on Boolean interpretation. +Instead, the Python documentation recommends an explicit conversion to `int`: + + +```python +def score(x_coord, y_coord): + radius = (x_coord**2 + y_coord**2) + return int(radius<=1)*5 + int(radius<=25)*4 + int(radius<=100)*1 +``` + +Beyond that recommendation, the terseness of this approach might be harder to reason about or decode — especially if a programmer is coming from a programming langauge that does not treat Boolean values as `ints`. +Despite the "radius" variable name, it is also more difficult to relate the scoring "rings" of the Dartboard to the values being checked and calculated in the `return` statement. +If using this code in a larger program, it would be strongly recommended that a docstring be provided to explain the Dartboard rings, scoring rules, and the corresponding scores. + +[bools-as-ints]: https://docs.python.org/3/library/stdtypes.html#boolean-type-bool \ No newline at end of file diff --git a/exercises/practice/darts/.approaches/booleans-as-ints/snippet.txt b/exercises/practice/darts/.approaches/booleans-as-ints/snippet.txt new file mode 100644 index 0000000000..ec7dcfabbf --- /dev/null +++ b/exercises/practice/darts/.approaches/booleans-as-ints/snippet.txt @@ -0,0 +1,3 @@ +def score(x_coord, y_coord): + radius = (x_coord**2 + y_coord**2) + return (radius<=1)*5 + (radius<=25)*4 +(radius<=100)*1 \ No newline at end of file diff --git a/exercises/practice/darts/.approaches/config.json b/exercises/practice/darts/.approaches/config.json new file mode 100644 index 0000000000..77f331bfce --- /dev/null +++ b/exercises/practice/darts/.approaches/config.json @@ -0,0 +1,50 @@ +{ + "introduction": { + "authors": ["bethanyg"], + "contributors": [] + }, + "approaches": [ + { + "uuid": "7d78f598-8b4c-4f7f-89e1-e8644e934a4c", + "slug": "if-statements", + "title": "Use If Statements", + "blurb": "Use if-statements to check scoring boundaries for a dart throw.", + "authors": ["bethanyg"] + }, + { + "uuid": "f8f5533a-09d2-4b7b-9dec-90f268bfc03b", + "slug": "tuple-and-loop", + "title": "Use a Tuple & Loop through Scores", + "blurb": "Score the Dart throw by looping through a tuple of scores.", + "authors": ["bethanyg"] + }, + { + "uuid": "a324f99e-15bb-43e0-9181-c1652094bc4f", + "slug": "match-case", + "title": "Use Structural Pattern Matching ('Match-Case')", + "blurb": "Use a Match-Case (Structural Pattern Matching) to score the dart throw.)", + "authors": ["bethanyg"] + }, + { + "uuid": "966bd2dd-c4fd-430b-ad77-3a304dedd82e", + "slug": "dict-and-generator", + "title": "Use a Dictionary with a Generator Expression", + "blurb": "Use a generator expression looping over a scoring dictionary, getting the max score for the dart throw.", + "authors": ["bethanyg"] + }, + { + "uuid": "5b087f50-31c5-4b84-9116-baafd3a30ed6", + "slug": "booleans-as-ints", + "title": "Use Boolean Values as Integers", + "blurb": "Use True and False as integer values to calculate the score of the dart throw.", + "authors": ["bethanyg"] + }, + { + "uuid": "0b2dbcd3-f0ac-45f7-af75-3451751fd21f", + "slug": "dict-and-dict-get", + "title": "Use a Dictionary with dict.get", + "blurb": "Loop over a dictionary and retrieve score via dct.get.", + "authors": ["bethanyg"] + } + ] +} \ No newline at end of file diff --git a/exercises/practice/darts/.approaches/dict-and-dict-get/content.md b/exercises/practice/darts/.approaches/dict-and-dict-get/content.md new file mode 100644 index 0000000000..a3c5bc2ac5 --- /dev/null +++ b/exercises/practice/darts/.approaches/dict-and-dict-get/content.md @@ -0,0 +1,72 @@ +# Using a Dictionary and `dict.get()` + + +```python +def score(x_coord, y_coord): + point = (x_coord**2 + y_coord**2) + scores = { + point <= 100: 1, + point <= 25: 5, + point <= 1: 10 + } + + return scores.get(True, 0) +``` + +At first glance, this approach looks similar to the [Booleans as Integers][approach-boolean-values-as-integers] approach, due to the Boolean evaluation used in the dictionary keys. +However, this approach is **not** interpreting Booleans as integers and is instead exploiting three key properties of [dictionaries][dicts]: + + +1. [Keys must be hashable][hashable-keys] — in other words, keys have to be _unique_. +2. Insertion order is preserved (_as of `Python 3.7`_), and evaluation/iteration happens in insertion order. +3. Duplicate keys _overwrite_ existing keys. + If the first key is `True` and the third key is `True`, the _value_ from the third key will overwrite the value from the first key. + +Finally, the `return` line uses [`dict.get()`][dict-get] to `return` a default value of 0 when a throw is outside the existing circle radii. +To see this in action, you can view this code on [Python Tutor][dict-get-python-tutor]. + + +Because of the listed dictionary qualities, **_order matters_**. +This approach depends on the outermost scoring circle containing all smaller circles and that +checks proceed from largest --> smallest circle. +Iterating in the opposite direction will not resolve to the correct score. +The following code variations do not pass the exercise tests: + + +```python + +def score(x_coord, y_coord): + point = (x_coord**2 + y_coord**2) + scores = { + point <= 1: 10, + point <= 25: 5, + point <= 100: 1, + } + + return scores.get(True, 0) + + #OR# + +def score(x_coord, y_coord): + point = (x_coord**2 + y_coord**2) + scores = { + point <= 25: 5, + point <= 1: 10, + point <= 100: 1, + } + + return scores.get(True, 0) + +``` + +While this approach is a _very clever_ use of dictionary properties, it is likely to be very hard to reason about for those who are not deeply knowledgeable. +Even those experienced in Python might take longer than usual to figure out what is happening in the code. +Extensibility could also be error-prone due to needing a strict order for the `dict` keys. + +This approach offers no space or speed advantages over using `if-statements` or other strategies, so is not recommended for use beyond a learning context. + +[approach-boolean-values-as-integers]: https://exercism.org/tracks/python/exercises/darts/approaches/boolean-values-as-integers +[dicts]: https://docs.python.org/3/library/stdtypes.html#mapping-types-dict +[dict-get]: https://docs.python.org/3/library/stdtypes.html#dict.get +[dict-get-python-tutor]: https://pythontutor.com/render.html#code=def%20score%28x_coord,%20y_coord%29%3A%0A%20%20%20%20point%20%3D%20%28x_coord**2%20%2B%20y_coord**2%29%0A%20%20%20%20scores%20%3D%20%7B%0A%20%20%20%20%20%20%20%20point%20%3C%3D%20100%3A%201,%0A%20%20%20%20%20%20%20%20point%20%3C%3D%2025%3A%205,%0A%20%20%20%20%20%20%20%20point%20%3C%3D%201%3A%2010%0A%20%20%20%20%7D%0A%20%20%20%20%0A%20%20%20%20return%20scores.get%28True,%200%29%0A%20%20%20%20%0Aprint%28score%281,3%29%29&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=311&rawInputLstJSON=%5B%5D&textReferences=false +[hashable-keys]: https://www.pythonmorsels.com/what-are-hashable-objects/#dictionary-keys-must-be-hashable diff --git a/exercises/practice/darts/.approaches/dict-and-dict-get/snippet.txt b/exercises/practice/darts/.approaches/dict-and-dict-get/snippet.txt new file mode 100644 index 0000000000..6d496f54d3 --- /dev/null +++ b/exercises/practice/darts/.approaches/dict-and-dict-get/snippet.txt @@ -0,0 +1,5 @@ +def score(x_coord, y_coord): + point = (x_coord**2 + y_coord**2) + scores = {point <= 100: 1, point <= 25: 5, point <= 1: 10} + + return scores.get(True, 0) \ No newline at end of file diff --git a/exercises/practice/darts/.approaches/dict-and-generator/content.md b/exercises/practice/darts/.approaches/dict-and-generator/content.md new file mode 100644 index 0000000000..30ffeac1eb --- /dev/null +++ b/exercises/practice/darts/.approaches/dict-and-generator/content.md @@ -0,0 +1,69 @@ +# Use a Dictionary and a Generator Expression + +```python +def score(x_coord, y_coord): + throw = x_coord**2 + y_coord**2 + rules = {1: 10, 25: 5, 100: 1, 200: 0} + + return max(point for distance, point in + rules.items() if throw <= distance) +``` + + +This approach is very similar to the [tuple and loop][approach-tuple-and-loop] approach, but iterates over [`dict.items()`][dict-items] and writes the `loop` as a [`generator-expression`][generator-expression] inside `max()`. +In cases where the scoring circles overlap, `max()` will return the maximum score available for the throw. +The generator expression inside `max()` is the equivalent of using a `for-loop` and a variable to determine the max score: + + +```python +def score(x_coord, y_coord): + throw = x_coord**2 + y_coord**2 + rules = {1: 10, 25: 5, 100: 1} + max_score = 0 + + for distance, point in rules.items(): + if throw <= distance and point > max_score: + max_score = point + return max_score +``` + + +A `list` or `tuple` can also be used in place of `max()`, but then requires an index to return the max score: + +```python +def score(x_coord, y_coord): + throw = x_coord**2 + y_coord**2 + rules = {1: 10, 25: 5, 100: 1, 200: 0} + + return [point for distance, point in + rules.items() if throw <= distance][0] #<-- have to specify index 0. + +#OR# + +def score(x_coord, y_coord): + throw = x_coord**2 + y_coord**2 + rules = {1: 10, 25: 5, 100: 1, 200: 0} + + return tuple(point for distance, point in + rules.items() if throw <= distance)[0] +``` + + +This solution can even be reduced to a "one-liner". +However, this is not performant, and is difficult to read: + +```python +def score(x_coord, y_coord): + return max(point for distance, point in + {1: 10, 25: 5, 100: 1, 200: 0}.items() if + (x_coord**2 + y_coord**2) <= distance) +``` + +While all of these variations do pass the tests, they suffer from even more over-engineering/performance caution than the earlier tuple and loop approach (_although for the data in this problem, the performance hit is slight_). +Additionally, the dictionary will take much more space in memory than using a `tuple` of tuples to hold scoring values. +In some circumstances, these variations might also be harder to reason about for those not familiar with `generator-expressions` or `list comprehensions`. + + +[approach-tuple-and-loop]: https://exercism.org/tracks/python/exercises/darts/approaches/tuple-and-loop +[dict-items]: https://docs.python.org/3/library/stdtypes.html#dict.items +[generator-expression]: https://dbader.org/blog/python-generator-expressions diff --git a/exercises/practice/darts/.approaches/dict-and-generator/snippet.txt b/exercises/practice/darts/.approaches/dict-and-generator/snippet.txt new file mode 100644 index 0000000000..f6649cf3a9 --- /dev/null +++ b/exercises/practice/darts/.approaches/dict-and-generator/snippet.txt @@ -0,0 +1,8 @@ +def score(x_coord, y_coord): + length = x_coord**2 + y_coord**2 + rules = {1.0: 10, 25.0: 5, 100.0: 1, 200: 0} + score = max(point for + distance, point in + rules.items() if length <= distance) + + return score \ No newline at end of file diff --git a/exercises/practice/darts/.approaches/if-statements/content.md b/exercises/practice/darts/.approaches/if-statements/content.md new file mode 100644 index 0000000000..40e2886ddb --- /dev/null +++ b/exercises/practice/darts/.approaches/if-statements/content.md @@ -0,0 +1,73 @@ +# Use `if-statements` + + +```python +import math + +# Checks scores from the center --> edge. +def score(x_coord, y_coord): + distance = math.sqrt(x_coord**2 + y_coord**2) + + if distance <= 1: return 10 + if distance <= 5: return 5 + if distance <= 10: return 1 + + return 0 +``` + +This approach uses [concept:python/conditionals]() to check the boundaries for each scoring ring, returning the corresponding score. +Calculating the euclidian distance is assigned to the variable "distance" to avoid having to re-calculate it for every if check. +Because the `if-statements` are simple and readable, they're written on one line to shorten the function body. +Zero is returned if no other check is true. + + +To avoid importing the `math` module (_for a very very slight speedup_), (x**2 +y**2) can be calculated instead, and the scoring rings can be adjusted to 1, 25, and 100: + + +```python +# Checks scores from the center --> edge. +def score(x_coord, y_coord): + distance = x_coord**2 + y_coord**2 + + if distance <= 1: return 10 + if distance <= 25: return 5 + if distance <= 100: return 1 + + return 0 +``` + + +# Variation 1: Check from Edge to Center Using Upper and Lower Bounds + + +```python +import math + +# Checks scores from the edge --> center +def score(x_coord, y_coord): + distance = math.sqrt(x_coord**2 + y_coord**2) + + if distance > 10: return 0 + if 5 < distance <= 10: return 1 + if 1 < distance <= 5: return 5 + + return 10 +``` + +This variant checks from the edge moving inward, checking both a lower and upper bound due to the overlapping scoring circles in this direction. + +Scores for any of these solutions can also be assigned to a variable to avoid multiple `returns`, but this isn't really necessary: + +```python +# Checks scores from the edge --> center +def score(x_coord, y_coord): + distance = x_coord**2 + y_coord**2 + points = 10 + + if distance > 100: points = 0 + if 25 < distance <= 100: points = 1 + if 1 < distance <= 25: points = 5 + + return points +``` + diff --git a/exercises/practice/darts/.approaches/if-statements/snippet.txt b/exercises/practice/darts/.approaches/if-statements/snippet.txt new file mode 100644 index 0000000000..18537416e2 --- /dev/null +++ b/exercises/practice/darts/.approaches/if-statements/snippet.txt @@ -0,0 +1,8 @@ +import math + +def score(x_coord, y_coord): + distance = math.sqrt(x_coord**2 + y_coord**2) + if distance <= 1: return 10 + if distance <= 5: return 5 + if distance <= 10: return 1 + return 0 \ No newline at end of file diff --git a/exercises/practice/darts/.approaches/introduction.md b/exercises/practice/darts/.approaches/introduction.md new file mode 100644 index 0000000000..588c266184 --- /dev/null +++ b/exercises/practice/darts/.approaches/introduction.md @@ -0,0 +1,146 @@ +# Introduction + + +There are multiple Pythonic ways to solve the Darts exercise. +Among them are: + +- Using `if-statements` +- Using a `tuple` (or `list` or `dict`) and a `for-loop` +- Using a `dict` (or `tuple` or `list`) and a `generator-expression` +- Using `boolean` values as `ints` +- Using a `dict` and `dict.get()` +- Using `match/case` (_Python 3.10+ only_) + +
+ +## General guidance + +The goal of the Darts exercise is to score a single throw in a Darts game. +The scoring areas are _concentric circles_, so boundary values need to be checked in order to properly score a throw. +The key is to determine how far from the center the dart lands (_by calculating sqrt(x**2 + y**2), or a variation_) and then determine what scoring ring it falls into. + + +**_Order matters_** - each bigger target circle contains all the smaller circles, so the most straightforward solution is to check the smallest circle first. +Otherwise, you must box your scoring by checking both a _lower bound_ and an _upper bound_. + + +Darts that fall on a _boundary_ are scored based on the area below the line (_closer to center_), so checking `<=` or `>=` is advised. + + +## Approach: Using `if` statements + + +```python +import math + +# Checks scores from the center --> edge. +def score(x_coord, y_coord): + distance = math.sqrt(x_coord**2 + y_coord**2) + + if distance <= 1: return 10 + if distance <= 5: return 5 + if distance <= 10: return 1 + + return 0 +``` + + +This approach uses [concept:python/conditionals]() to check the boundaries for each scoring ring, returning the corresponding score. +For more details, see the [if statements][approach-if-statements] approach. + + +## Approach: Using a `tuple` and a `loop` + +```python +def score(x_coord, y_coord): + throw = x_coord**2 + y_coord**2 + rules = (1, 10), (25, 5), (100, 1), (200, 0) + + for distance, points in rules: + if throw <= distance: + return points +``` + + +This approach uses a loop to iterate through the _rules_ `tuple`, unpacking each `distance` and corresponding`score`. +For more details, see the [tuple and loop][approach-tuple-and-loop] approach. + + +## Approach: Using a `dict` with a `generator-expression` + +```python +def score(x_coord, y_coord): + throw = x_coord**2 + y_coord**2 + rules = {1: 10, 25: 5, 100: 1, 200: 0} + + return max(point for distance, point in + rules.items() if throw <= distance) +``` + +This approach is very similar to the [tuple and loop][approach-tuple-and-loop] approach, but iterates over [`dict.items()`][dict-items]. +For more information, see the [dict with generator-expression][approach-dict-with-generator-expression] approach. + + +## Approach: Using Boolean Values as Integers + +```python +def score(x_coord, y_coord): + radius = (x_coord**2 + y_coord**2) + return (radius<=1)*5 + (radius<=25)*4 +(radius<=100)*1 +``` + + +This approach exploits the fact that Boolean values are an integer subtype in Python. +For more information, see the [boolean values as integers][approach-boolean-values-as-integers] approach. + + +## Approach: Using a `Dictionary` and `dict.get()` + +```python +def score(x_coord, y_coord): + point = (x_coord**2 + y_coord**2) + scores = { + point <= 100: 1, + point <= 25: 5, + point <= 1: 10 + } + + return scores.get(True, 0) +``` + +This approach uses a dictionary to hold the distance --> scoring mappings and `dict.get()` to retrieve the correct points value. +For more details, read the [`Dictionary and dict.get()`][approach-dict-and-dict-get] approach. + + +## Approach: Using `match/case` (structural pattern matching) + +```python +from math import hypot, ceil + + +def score(x, y): + match ceil(hypot(x, y)): + case 0 | 1: return 10 + case 2 | 3 | 4 | 5: return 5 + case 6 | 7 | 8 | 9 | 10: return 1 + case _: return 0 +``` + + +This approach uses `Python 3.10`'s structural pattern matching with `return` values on the same line as `case`. +A fallthrough case (`_`) is used if the dart throw is outside the outer circle of the target (_greater than 10_). +For more details, see the [structural pattern matching][approach-struct-pattern-matching] approach. + + +## Which approach to use? + +Many of these approaches are a matter of personal preference - there are not significant memory or performance differences. +Although a strong argument could be made for simplicity and clarity — many listed solutions (_while interesting_) are harder to reason about or are over-engineered for the current scope of the exercise. + +[approach-boolean-values-as-integers]: https://exercism.org/tracks/python/exercises/darts/approaches/boolean-values-as-integers +[approach-dict-and-dict-get]: https://exercism.org/tracks/python/exercises/darts/approaches/dict-and-dict-get +[approach-dict-with-generator-expression]: https://exercism.org/tracks/python/exercises/darts/approaches/dict-with-gnerator-expresson +[approach-if-statements ]: https://exercism.org/tracks/python/exercises/darts/approaches/if-statements +[approach-struct-pattern-matching]: https://exercism.org/tracks/python/exercises/darts/approaches/struct-pattern-matching +[approach-tuple-and-loop]: https://exercism.org/tracks/python/exercises/darts/approaches/tuple-and-loop +[dict-items]: https://docs.python.org/3/library/stdtypes.html#dict.items diff --git a/exercises/practice/darts/.approaches/match-case/content.md b/exercises/practice/darts/.approaches/match-case/content.md new file mode 100644 index 0000000000..04430a5dc5 --- /dev/null +++ b/exercises/practice/darts/.approaches/match-case/content.md @@ -0,0 +1,85 @@ +# Use `match/case` (Structural Pattern Matching) + + +```python +from math import hypot, ceil + + +def score(x, y): + throw = ceil(hypot(x, y)) + + match throw: + case 0 | 1: return 10 + case 2 | 3 | 4 | 5: return 5 + case 6 | 7 | 8 | 9 | 10: return 1 + case _: return 0 + +#OR# + +def score(x, y): + match ceil(hypot(x, y)): + case 0 | 1: return 10 + case 2 | 3 | 4 | 5: return 5 + case 6 | 7 | 8 | 9 | 10: return 1 + case _: return 0 +``` + +This approach uses `Python 3.10`'s [`structural pattern matching`][structural-pattern-matching] with `return` values on the same line as `case`. +Because the match is numeric, each case explicitly lists allowed values using the `|` (OR) operator. +A fallthrough case (`_`) is used if the dart throw is greater than 10 (_the outer circle radius of the target_). +This is equivalent to using `if-statements` to check throw values although some might argue it is clearer to read. +An `if-statement` equivalent would be: + +```python +from math import hypot, ceil + + +def score(x, y): + throw = ceil(hypot(x, y)) + + if throw in (0, 1): return 10 + if throw in (2, 3, 4, 5): return 5 + if throw in (6, 7, 8, 9, 10): return 1 + + return 0 +``` + +One can also use `<`, `>`, or `<=` and `>=` in structural pattern matching, although the syntax becomes almost identical to using them with `if-statements`, but more verbose: + + +```python +from math import hypot, ceil + + +def score(x, y): + throw = ceil(hypot(x, y)) + + match throw: + case throw if throw <= 1: return 10 + case throw if throw <= 5: return 5 + case throw if throw <= 10: return 1 + case _: return 0 +``` + + +Finally, one can use an [assignment expression][assignment-expression] or [walrus operator][walrus] to calculate the throw value rather than calculating and assigning a variable on a separate line. +This isn't necessary (_the first variations shows this clearly_) and might be harder to reason about/understand for some programmers: + + +```python +from math import hypot, ceil + +def score(x, y): + match throw := ceil(hypot(x, y)): + case throw if throw <= 1: return 10 + case throw if throw <=5: return 5 + case throw if throw <=10: return 1 + case _: return 0 +``` + +Using structural pattern matching for this exercise doesn't offer any clear performance advantages over the `if-statement`, but might be "cleaner", more "organized looking", or easier for others to scan/read. + + +[assignment-expression]: https://docs.python.org/3/reference/expressions.html#grammar-token-python-grammar-assignment_expression +[structural-pattern-matching]: https://peps.python.org/pep-0636/ +[walrus]: https://peps.python.org/pep-0572/ diff --git a/exercises/practice/darts/.approaches/match-case/snippet.txt b/exercises/practice/darts/.approaches/match-case/snippet.txt new file mode 100644 index 0000000000..e66b5382b2 --- /dev/null +++ b/exercises/practice/darts/.approaches/match-case/snippet.txt @@ -0,0 +1,8 @@ +from math import hypot, ceil + +def score(x, y): + match ceil(hypot(x, y)): + case 0 | 1: return 10 + case 2 | 3 | 4 | 5: return 5 + case 6 | 7 | 8 | 9 | 10: return 1 + case _: return 0 \ No newline at end of file diff --git a/exercises/practice/darts/.approaches/tuple-and-loop/content.md b/exercises/practice/darts/.approaches/tuple-and-loop/content.md new file mode 100644 index 0000000000..042b9e88ae --- /dev/null +++ b/exercises/practice/darts/.approaches/tuple-and-loop/content.md @@ -0,0 +1,55 @@ +# Use a tuple with a loop + +```python +def score(x_coord, y_coord): + throw = x_coord**2 + y_coord**2 + rules = (1, 10), (25, 5), (100, 1), (200, 0) + + for distance, points in rules: + if throw <= distance: + return points +``` + +This approach uses a loop to iterate through the _rules_ `tuple`, unpacking each (`distance`, `points`) pair (_For a little more on unpacking, see [Tuple Unpacking Improves Python Code Readability][tuple-unpacking]_). +If the calculated distance of the throw is less than or equal to a given distance, the score for that region is returned. +A `list` of `lists`, a `list` of `tuples`, or a dictionary could be used here to the same effect: + +```python +def score(x_coord, y_coord): + throw = x_coord**2 + y_coord**2 + rules = [[1, 10], [25, 5], [100, 1]] + + for distance, points in rules: + if throw <= distance: + return points + + return 0 + +#OR# + +def score(x_coord, y_coord): + throw = x_coord**2 + y_coord**2 + rules = [(1, 10), (25, 5), (100, 1), (200, 0)] + + for distance, points in rules: + if throw <= distance: + return points + +#OR# + +def score(x_coord, y_coord): + throw = x_coord**2 + y_coord**2 + rules = {1: 10, 25: 5, 100: 1} + + for distance, points in rules.items(): + if throw <= distance: + return points + + return 0 +``` + +This approach would work nicely in a scenario where you expect to be adding more scoring "rings", since it is cleaner to edit the data structure than to add additional `if-statements` as you would have to in the [`if-statement` approach][approach-if-statements ]. +For the three rings as defined by the current exercise, it is a bit over-engineered to use a data structure + `loop`, and results in a slight (_**very** slight_) slowdown over using `if-statements`. + +[tuple-unpacking]: https://treyhunner.com/2018/03/tuple-unpacking-improves-python-code-readability/#Unpacking_in_a_for_loop +[approach-if-statements ]: https://exercism.org/tracks/python/exercises/darts/approaches/if-statements diff --git a/exercises/practice/darts/.approaches/tuple-and-loop/snippet.txt b/exercises/practice/darts/.approaches/tuple-and-loop/snippet.txt new file mode 100644 index 0000000000..ad50500526 --- /dev/null +++ b/exercises/practice/darts/.approaches/tuple-and-loop/snippet.txt @@ -0,0 +1,7 @@ +def score(x_coord, y_coord): + distance = x_coord**2 + y_coord**2 + rules = (1.0, 10), (25.0, 5), (100.0, 1), (200.0, 0) + + for distance, point in rules: + if length <= distance: + return point \ No newline at end of file