Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions exercises/practice/darts/.approaches/booleans-as-ints/content.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@

```python
def score(x_coord, y_coord):
radius = (x_coord**2 + y_coord**2)
return (radius<=1)*5 + (radius<=25)*4 + (radius<=100)*1
radius_squared = x_coord**2 + y_coord**2
return (radius_squared<=1)*5 + (radius_squared<=25)*4 + (radius_squared<=100)*1
```


Expand All @@ -25,12 +25,12 @@ 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
radius_squared = x_coord**2 + y_coord**2
return int(radius_squared<=1)*5 + int(radius_squared<=25)*4 + int(radius_squared<=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.
Despite the "radius_squared" 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
Original file line number Diff line number Diff line change
@@ -1,3 +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
radius_squared = x_coord**2 + y_coord**2
return (radius_squared<=1)*5 + (radius_squared<=25)*4 + (radius_squared<=100)*1
22 changes: 14 additions & 8 deletions exercises/practice/darts/.approaches/config.json
Original file line number Diff line number Diff line change
@@ -1,50 +1,56 @@
{
"introduction": {
"authors": ["bethanyg"],
"contributors": []
"contributors": ["yrahcaz7"]
},
"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"]
"authors": ["bethanyg"],
"contributors": ["yrahcaz7"]
},
{
"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"]
"authors": ["bethanyg"],
"contributors": ["yrahcaz7"]
},
{
"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"]
"authors": ["bethanyg"],
"contributors": ["yrahcaz7"]
},
{
"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"]
"authors": ["bethanyg"],
"contributors": ["yrahcaz7"]
},
{
"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"]
"authors": ["bethanyg"],
"contributors": ["yrahcaz7"]
},
{
"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"]
"blurb": "Loop over a dictionary and retrieve score via dict.get.",
"authors": ["bethanyg"],
"contributors": ["yrahcaz7"]
}
]
}
22 changes: 10 additions & 12 deletions exercises/practice/darts/.approaches/dict-and-dict-get/content.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@

```python
def score(x_coord, y_coord):
point = (x_coord**2 + y_coord**2)
point = x_coord**2 + y_coord**2
scores = {
point <= 100: 1,
point <= 25: 5,
point <= 1: 10
point <= 1: 10,
}

return scores.get(True, 0)
Expand All @@ -17,10 +17,10 @@ At first glance, this approach looks similar to the [Booleans as Integers][appro
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.
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].
Expand All @@ -34,29 +34,27 @@ The following code variations do not pass the exercise tests:


```python

def score(x_coord, y_coord):
point = (x_coord**2 + y_coord**2)
point = x_coord**2 + y_coord**2
scores = {
point <= 1: 10,
point <= 25: 5,
point <= 100: 1,
}

return scores.get(True, 0)
#OR#

#OR#

def score(x_coord, y_coord):
point = (x_coord**2 + y_coord**2)
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.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
def score(x_coord, y_coord):
point = (x_coord**2 + y_coord**2)
point = x_coord**2 + y_coord**2
scores = {point <= 100: 1, point <= 25: 5, point <= 1: 10}

return scores.get(True, 0)
37 changes: 21 additions & 16 deletions exercises/practice/darts/.approaches/dict-and-generator/content.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@
```python
def score(x_coord, y_coord):
throw = x_coord**2 + y_coord**2
rules = {1: 10, 25: 5, 100: 1, 200: 0}
rules = {1: 10, 25: 5, 100: 1}

return max(point for distance, point in
rules.items() if throw <= distance)
return max(point for distance, point in
rules.items() if throw <= distance,
default=0)
```


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()`.
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:

Expand All @@ -24,6 +25,7 @@ def score(x_coord, y_coord):
for distance, point in rules.items():
if throw <= distance and point > max_score:
max_score = point

return max_score
```

Expand All @@ -33,37 +35,40 @@ A `list` or `tuple` can also be used in place of `max()`, but then requires an i
```python
def score(x_coord, y_coord):
throw = x_coord**2 + y_coord**2
rules = {1: 10, 25: 5, 100: 1, 200: 0}
rules = {1: 10, 25: 5, 100: 1}

return [point for distance, point in
rules.items() if throw <= distance][0] #<-- have to specify index 0.

return ([point for distance, point in
rules.items() if throw <= distance]
or [0])[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}
rules = {1: 10, 25: 5, 100: 1}

return tuple(point for distance, point in
rules.items() if throw <= distance)[0]
return (tuple(point for distance, point in
rules.items() if throw <= distance)
or (0,))[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)
def score(x_coord, y_coord):
return max(point for distance, point in
{1: 10, 25: 5, 100: 1}.items() if
(x_coord**2 + y_coord**2) <= distance,
default=0)
```

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
[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
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
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
throw = x_coord**2 + y_coord**2
rules = {1: 10, 25: 5, 100: 1}

return max(point for distance, point in
rules.items() if throw <= distance,
default=0)
27 changes: 13 additions & 14 deletions exercises/practice/darts/.approaches/if-statements/content.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ 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
if distance <= 5: return 5
if distance <= 10: return 1

return 0
```
Expand All @@ -21,23 +21,23 @@ Because the `if-statements` are simple and readable, they're written on one line
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:
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
distance_squared = x_coord**2 + y_coord**2

if distance <= 1: return 10
if distance <= 25: return 5
if distance <= 100: return 1
if distance_squared <= 1: return 10
if distance_squared <= 25: return 5
if distance_squared <= 100: return 1

return 0
```


# Variation 1: Check from Edge to Center Using Upper and Lower Bounds
## Variation 1: Check from Edge to Center Using Upper and Lower Bounds


```python
Expand All @@ -56,18 +56,17 @@ def score(x_coord, y_coord):

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:
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
distance_squared = 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
if distance_squared > 100: points = 0
if 25 < distance_squared <= 100: points = 1
if 1 < distance_squared <= 25: points = 5

return points
```

Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ 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
if distance <= 5: return 5
if distance <= 10: return 1
return 0
Loading
Loading