Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
21 changes: 21 additions & 0 deletions exercises/practice/yacht/.approaches/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"introduction": {
"authors": ["safwansamsudeen"]
},
"approaches": [
{
"uuid": "3593cfe3-5cab-4141-b0a2-329148a66bb6",
"slug": "functions",
"title": "Functions",
"blurb": "Use functions",
"authors": ["safwansamsudeen"]
},
{
"uuid": "eccd1e1e-6c88-4823-9b25-944eccaa92e7",
"slug": "if-structure",
"title": "If structure",
"blurb": "Use an if structure",
"authors": ["safwansamsudeen"]
}
]
}
66 changes: 66 additions & 0 deletions exercises/practice/yacht/.approaches/functions/content.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
## Approach: functions
Each bit of functionality for each category can be encoded in a function, and the constant set to that function.
We use `lambda`s as _all_ the functions can be written in one line.
In `score`, we call the category (as it's now a function) passing in `dice`.

```python
def digits(n):
return lambda dice: dice.count(n) * n

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 s: 4 * sorted(s)[1] if len(set(s)) < 3 and s.count(s[0]) in (1, 4, 5) 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 idiomatic way to solve the exercise, although some one-liners get a little long.
The [ternary operator][ternary-operator] is crucial in solving the exercise this way.
Instead of `lambda`s, functions could be created and the constants set to them.
This will remove the need for one-liners. If interested, read more on [lamdas][lambdas].
```python
def yacht(dice):
if dice.count(dice[0]) == len(dice):
return 50
return 0
YACHT = yacht
# and so on
# or even, though not recommended
def YACHT(dice):
if dice.count(dice[0]) == len(dice):
return 50
return 0
```

Instead of setting each constant in `ONES` through `SIXES` to a separate `lambda`, we create a function that returns a `lambda`, 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(d) == 20 and len(set(d)) == 5` (15 in `LITTLE_STRAIGHT`).
In `CHOICE`, `lambda x: sum(x)` is shortened to just `sum`.

The essence of the one-liners in `FULL_HOUSE` and `FOUR_OF_A_KIND` is in creating `set`s to remove the duplicates and checking their lengths.
`FOUR_OF_A_KIND` is commonly the hardest function to put in one line, and it's valid to declare it in a less complicated function:
```python
FOUR_OF_A_KIND = four_of_a_kind
def four_of_a_kind(x):
four_times_elements = [dice for dice in set(x) if x.count(dice) >= 4]
return 4 * four_times_elements[0] if len(four_times_elements) > 0 else 0
```
This approach can be done in one line using the [walrus operator][walrus] which exists in Python since 3.8 (done slightly differently to show the possible variations):
```python
FOUR_OF_A_KIND = lambda dice: four_elem[0] * 4 if (four_elem := [i for i in dice if dice.count(i) >= 4]) else 0
```


[closures]: https://www.programiz.com/python-programming/closure
[ternary-operator]: https://www.tutorialspoint.com/ternary-operator-in-python
[lambdas]: https://www.w3schools.com/python/python_lambda.asp
[walrus]: https://realpython.com/python-walrus-operator/
7 changes: 7 additions & 0 deletions exercises/practice/yacht/.approaches/functions/snippet.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
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)
56 changes: 56 additions & 0 deletions exercises/practice/yacht/.approaches/if-structure/content.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# 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
YACHT = 'YACHT'
ONES = 'ONES'
TWOS = 'TWOS'
THREES = 'THREES'
FOURS = 'FOURS'
FIVES = 'FIVES'
SIXES = 'SIXES'
FULL_HOUSE = 'FULL_HOUSE'
FOUR_OF_A_KIND = 'FOUR_OF_A_KIND'
LITTLE_STRAIGHT = 'LITTLE_STRAIGHT'
BIG_STRAIGHT = 'BIG_STRAIGHT'
CHOICE = 'CHOICE'

def score(dice, category):
if category == 'ONES':
return dice.count(1)
elif category == 'TWOS':
return dice.count(2) * 2
elif category == 'THREES':
return dice.count(3) * 3
elif category == 'FOURS':
return dice.count(4) * 4
elif category == 'FIVES':
return dice.count(5) * 5
elif category == 'SIXES':
return dice.count(6) * 6
elif category == 'FULL_HOUSE':
for i in dice:
for j in dice:
if dice.count(i) == 3 and dice.count(j) == 2:
return i * 3 + j * 2
elif category == 'FOUR_OF_A_KIND':
for j in dice:
if dice.count(j) >= 4:
return j * 4
elif category == 'LITTLE_STRAIGHT':
if dice.count(1) == 1 and dice.count(2) == 1 and dice.count(3) == 1 and dice.count(4) == 1 and dice.count(5) == 1:
return 30
elif category == 'BIG_STRAIGHT':
if dice.count(6) == 1 and dice.count(2) == 1 and dice.count(3) == 1 and dice.count(4) == 1 and dice.count(5) == 1:
return 30
elif category == 'YACHT':
if all(i == dice[0] for i 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 is not an ideal way to solve the exercise, as the provided constants are used as dummies, and the code is rather long and winded. It remains, however, valid, and does have its advantages, such as the lack of repetition in returning 0 that we had in the `lambda` approach.
8 changes: 8 additions & 0 deletions exercises/practice/yacht/.approaches/if-structure/snippet.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
YACHT = 'YACHT'
CHOICE = 'CHOICE'
def score(dice, category):
if category == 'ONES':
...
elif category == 'FULL_HOUSE':
...
return 0
87 changes: 87 additions & 0 deletions exercises/practice/yacht/.approaches/introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# Introduction
Yacht in Python can be solved in many ways. The most intuitive approach is to use an `if` structure. More idiomatically, 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 to a function or a standalone piece of code. While map generally reminds us of `dict`s, here the constants in the stub file are global. This indicates that the most idiomatic approach is not using a `dict`. Adhering to the principles of DRY too 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 set to that function. We use `lambda`s as _all_ of the functions can be written in one line. In `score`, we call the category (as it's now a function) passing in `dice`. We use `lambda`s as _all_ of the functions can be written in one line.
```python
def digits(n):
return lambda x: x.count(n) * n

YACHT = lambda x: 50 if x.count(x[0]) == len(x) else 0
ONES = digits(1)
TWOS = digits(2)
THREES = digits(3)
FOURS = digits(4)
FIVES = digits(5)
SIXES = digits(6)
FULL_HOUSE = lambda x: sum(x) if len(set(x)) == 2 and x.count(x[0]) in [2, 3] else 0
FOUR_OF_A_KIND = lambda s: 4 * sorted(s)[1] if len(set(s)) < 3 and s.count(s[0]) in (1, 4, 5) else 0
LITTLE_STRAIGHT = lambda x: 30 if sorted(x) == [1, 2, 3, 4, 5] else 0
BIG_STRAIGHT = lambda x: 30 if sorted(x) == [2, 3, 4, 5, 6] else 0
CHOICE = sum

def score(dice, category):
return category(dice)
```
This is a very idiomatic way to solve the exercise, although some one-liners get a little long.
The repetitive code is minimized using a seperate function `digits` that returns a function (closure). For more information on this approach, read [this document][approach-functions].
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The repetitive code is minimized using a seperate function `digits` that returns a function (closure). For more information on this approach, read [this document][approach-functions].
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
YACHT = 'YACHT'
ONES = 'ONES'
TWOS = 'TWOS'
THREES = 'THREES'
FOURS = 'FOURS'
FIVES = 'FIVES'
SIXES = 'SIXES'
FULL_HOUSE = 'FULL_HOUSE'
FOUR_OF_A_KIND = 'FOUR_OF_A_KIND'
LITTLE_STRAIGHT = 'LITTLE_STRAIGHT'
BIG_STRAIGHT = 'BIG_STRAIGHT'
CHOICE = 'CHOICE'

def score(dice, category):
if category == 'ONES':
return dice.count(1)
elif category == 'TWOS':
return dice.count(2) * 2
elif category == 'THREES':
return dice.count(3) * 3
elif category == 'FOURS':
return dice.count(4) * 4
elif category == 'FIVES':
return dice.count(5) * 5
elif category == 'SIXES':
return dice.count(6) * 6
elif category == 'FULL_HOUSE':
for i in dice:
for j in dice:
if dice.count(i) == 3 and dice.count(j) == 2:
return i * 3 + j * 2
elif category == 'FOUR_OF_A_KIND':
for j in dice:
if dice.count(j) >= 4:
return j * 4
elif category == 'LITTLE_STRAIGHT':
if dice.count(1) == 1 and dice.count(2) == 1 and dice.count(3) == 1 and dice.count(4) == 1 and dice.count(5) == 1:
return 30
elif category == 'BIG_STRAIGHT':
if dice.count(6) == 1 and dice.count(2) == 1 and dice.count(3) == 1 and dice.count(4) == 1 and dice.count(5) == 1:
return 30
elif category == 'YACHT':
if all(i == dice[0] for i 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