Skip to content

Commit 6c77491

Browse files
mrcfpscmccandless
authored andcommitted
yacht: implement exercise (#1368)
* yacht: implement exercise * yacht: use enumeration of score categories * Use enumeration instead of plain strings to represent categories * Improve func `ns` in example solution
1 parent f93813c commit 6c77491

File tree

5 files changed

+273
-0
lines changed

5 files changed

+273
-0
lines changed

config.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,21 @@
148148
"conditionals"
149149
]
150150
},
151+
{
152+
"uuid": "22f937e5-52a7-4956-9dde-61c985251a6b",
153+
"slug": "yacht",
154+
"core": false,
155+
"unlocked_by": null,
156+
"difficulty": 1,
157+
"topics": [
158+
"conditionals",
159+
"equality",
160+
"games",
161+
"integers",
162+
"pattern_matching",
163+
"sequences"
164+
]
165+
},
151166
{
152167
"uuid": "505e7bdb-e18d-45fd-9849-0bf33492efd9",
153168
"slug": "run-length-encoding",

exercises/yacht/README.md

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
# Yacht
2+
3+
# Score a single throw of dice in *Yacht*
4+
5+
The dice game [Yacht](https://en.wikipedia.org/wiki/Yacht_(dice_game)) is from
6+
the same family as Poker Dice, Generala and particularly Yahtzee, of which it
7+
is a precursor. In the game, five dice are rolled and the result can be entered
8+
in any of twelve categories. The score of a throw of the dice depends on
9+
category chosen.
10+
11+
## Scores in Yacht
12+
13+
Category Score Example
14+
Ones 1 × number of ones 1 1 1 4 5 scores 3
15+
Twos 2 × number of twos 2 2 3 4 5 scores 4
16+
Threes 3 × number of threes 3 3 3 3 3 scores 15
17+
Fours 4 × number of fours 1 2 3 3 5 scores 0
18+
Fives 5 × number of fives 5 1 5 2 5 scores 15
19+
Sixes 6 × number of sixes 2 3 4 5 6 scores 6
20+
Full House Total of the dice 3 3 3 5 5 scores 19
21+
Four of a Kind Total of the four dice 4 4 4 4 6 scores 16
22+
Little Straight 30 points 1 2 3 4 5 scores 30
23+
Big Straight 30 points 2 3 4 5 6 scores 30
24+
Choice Sum of the dice 2 3 3 4 6 scores 18
25+
Yacht 50 points 4 4 4 4 4 scores 50
26+
27+
If the dice do not satisfy the requirements of a category, the score is zero.
28+
If, for example, *Four Of A Kind* is entered in the *Yacht* category, zero
29+
points are scored. A *Yacht* scores zero if entered in the *Full House* category.
30+
31+
## Task
32+
Given a list of values for five dice and a category, your solution should return
33+
the score of the dice for that category. If the dice do not satisfy the requirements
34+
of the category your solution should return 0. You can assume that five values
35+
will always be presented, and the value of each will be between one and six
36+
inclusively. You should not assume that the dice are ordered.
37+
38+
## Exception messages
39+
40+
Sometimes it is necessary to raise an exception. When you do this, you should include a meaningful error message to
41+
indicate what the source of the error is. This makes your code more readable and helps significantly with debugging. Not
42+
every exercise will require you to raise an exception, but for those that do, the tests will only pass if you include
43+
a message.
44+
45+
To raise a message with an exception, just write it as an argument to the exception type. For example, instead of
46+
`raise Exception`, you should write:
47+
48+
```python
49+
raise Exception("Meaningful message indicating the source of the error")
50+
```
51+
52+
## Running the tests
53+
54+
To run the tests, run the appropriate command below ([why they are different](https://github.com/pytest-dev/pytest/issues/1629#issue-161422224)):
55+
56+
- Python 2.7: `py.test yacht_test.py`
57+
- Python 3.3+: `pytest yacht_test.py`
58+
59+
Alternatively, you can tell Python to run the pytest module (allowing the same command to be used regardless of Python version):
60+
`python -m pytest yacht_test.py`
61+
62+
### Common `pytest` options
63+
64+
- `-v` : enable verbose output
65+
- `-x` : stop running tests on first failure
66+
- `--ff` : run failures from previous test before running other test cases
67+
68+
For other options, see `python -m pytest -h`
69+
70+
## Submitting Exercises
71+
72+
Note that, when trying to submit an exercise, make sure the solution is in the `$EXERCISM_WORKSPACE/python/yacht` directory.
73+
74+
You can find your Exercism workspace by running `exercism debug` and looking for the line that starts with `Workspace`.
75+
76+
For more detailed information about running tests, code style and linting,
77+
please see the [help page](http://exercism.io/languages/python).
78+
79+
## Source
80+
81+
James Kilfiger, using wikipedia [https://en.wikipedia.org/wiki/Yacht_(dice_game)](https://en.wikipedia.org/wiki/Yacht_(dice_game))
82+
83+
## Submitting Incomplete Solutions
84+
85+
It's possible to submit an incomplete solution so you can see how others have completed the exercise.

exercises/yacht/example.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
from collections import Counter
2+
from functools import partial
3+
4+
YACHT = 0
5+
ONES = 1
6+
TWOS = 2
7+
THREES = 3
8+
FOURS = 4
9+
FIVES = 5
10+
SIXES = 6
11+
FULL_HOUSE = 7
12+
FOUR_OF_A_KIND = 8
13+
LITTLE_STRAIGHT = 9
14+
BIG_STRAIGHT = 10
15+
CHOICE = 11
16+
17+
18+
def ns(number, dice):
19+
return sum(n for n in dice if n == number)
20+
21+
22+
def full_house(dice):
23+
counter = Counter(dice)
24+
return sum(dice) if set(counter.values()) == {3, 2} else 0
25+
26+
27+
def four_of_a_kind(dice):
28+
counter = Counter(dice)
29+
number, count = counter.most_common()[0]
30+
return 4 * number if count >= 4 else 0
31+
32+
33+
def little_straight(dice):
34+
return 30 if set(dice) == {1, 2, 3, 4, 5} else 0
35+
36+
37+
def big_straight(dice):
38+
return 30 if set(dice) == {2, 3, 4, 5, 6} else 0
39+
40+
41+
def yacht(dice):
42+
return 50 if len(set(dice)) == 1 else 0
43+
44+
45+
functions = [
46+
yacht,
47+
partial(ns, 1),
48+
partial(ns, 2),
49+
partial(ns, 3),
50+
partial(ns, 4),
51+
partial(ns, 5),
52+
partial(ns, 6),
53+
full_house,
54+
four_of_a_kind,
55+
little_straight,
56+
big_straight,
57+
sum,
58+
]
59+
60+
61+
def score(dice, category):
62+
try:
63+
return functions[category](dice)
64+
except IndexError:
65+
raise ValueError("no such category")

exercises/yacht/yacht.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Score categories
2+
# Change the values as you see fit
3+
YACHT = None
4+
ONES = None
5+
TWOS = None
6+
THREES = None
7+
FOURS = None
8+
FIVES = None
9+
SIXES = None
10+
FULL_HOUSE = None
11+
FOUR_OF_A_KIND = None
12+
LITTLE_STRAIGHT = None
13+
BIG_STRAIGHT = None
14+
CHOICE = None
15+
16+
17+
def score(dice, category):
18+
pass

exercises/yacht/yacht_test.py

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import unittest
2+
3+
import yacht
4+
from yacht import score
5+
6+
7+
# Tests adapted from `problem-specifications//canonical-data.json` @ v1.0.0
8+
9+
class YachtTests(unittest.TestCase):
10+
def test_yacht(self):
11+
self.assertEqual(score([5, 5, 5, 5, 5], yacht.YACHT), 50)
12+
13+
def test_not_yacht(self):
14+
self.assertEqual(score([1, 3, 3, 2, 5], yacht.YACHT), 0)
15+
16+
def test_ones(self):
17+
self.assertEqual(score([1, 1, 1, 3, 5], yacht.ONES), 3)
18+
19+
def test_ones_out_of_order(self):
20+
self.assertEqual(score([3, 1, 1, 5, 1], yacht.ONES), 3)
21+
22+
def test_no_ones(self):
23+
self.assertEqual(score([4, 3, 6, 5, 5], yacht.ONES), 0)
24+
25+
def test_twos(self):
26+
self.assertEqual(score([2, 3, 4, 5, 6], yacht.TWOS), 2)
27+
28+
def test_fours(self):
29+
self.assertEqual(score([1, 4, 1, 4, 1], yacht.FOURS), 8)
30+
31+
def test_yacht_counted_as_threes(self):
32+
self.assertEqual(score([3, 3, 3, 3, 3], yacht.THREES), 15)
33+
34+
def test_yacht_of_threes_counted_as_fives(self):
35+
self.assertEqual(score([3, 3, 3, 3, 3], yacht.FIVES), 0)
36+
37+
def test_sixes(self):
38+
self.assertEqual(score([2, 3, 4, 5, 6], yacht.SIXES), 6)
39+
40+
def test_full_house_two_small_three_big(self):
41+
self.assertEqual(score([2, 2, 4, 4, 4], yacht.FULL_HOUSE), 16)
42+
43+
def test_full_house_three_small_two_big(self):
44+
self.assertEqual(score([5, 3, 3, 5, 3], yacht.FULL_HOUSE), 19)
45+
46+
def test_two_pair_is_not_a_full_house(self):
47+
self.assertEqual(score([2, 2, 4, 4, 5], yacht.FULL_HOUSE), 0)
48+
49+
def test_yacht_is_not_a_full_house(self):
50+
self.assertEqual(score([2, 2, 2, 2, 2], yacht.FULL_HOUSE), 0)
51+
52+
def test_four_of_a_kind(self):
53+
self.assertEqual(score([6, 6, 4, 6, 6], yacht.FOUR_OF_A_KIND), 24)
54+
55+
def test_yacht_can_be_scored_as_four_of_a_kind(self):
56+
self.assertEqual(score([3, 3, 3, 3, 3], yacht.FOUR_OF_A_KIND), 12)
57+
58+
def test_full_house_is_not_four_of_a_kind(self):
59+
self.assertEqual(score([3, 5, 4, 1, 2], yacht.FOUR_OF_A_KIND), 0)
60+
61+
def test_little_straight(self):
62+
self.assertEqual(score([3, 5, 4, 1, 2], yacht.LITTLE_STRAIGHT), 30)
63+
64+
def test_little_straight_as_big_straight(self):
65+
self.assertEqual(score([1, 2, 3, 4, 5], yacht.BIG_STRAIGHT), 0)
66+
67+
def test_four_in_order_but_not_a_little_straight(self):
68+
self.assertEqual(score([1, 1, 2, 3, 4], yacht.LITTLE_STRAIGHT), 0)
69+
70+
def test_no_pairs_but_not_a_little_straight(self):
71+
self.assertEqual(score([1, 2, 3, 4, 6], yacht.LITTLE_STRAIGHT), 0)
72+
73+
def test_min_1_max_5_but_not_a_little_straight(self):
74+
self.assertEqual(score([1, 1, 3, 4, 5], yacht.LITTLE_STRAIGHT), 0)
75+
76+
def test_big_straight(self):
77+
self.assertEqual(score([4, 6, 2, 5, 3], yacht.BIG_STRAIGHT), 30)
78+
79+
def test_big_straight_as_little_straight(self):
80+
self.assertEqual(score([6, 5, 4, 3, 2], yacht.LITTLE_STRAIGHT), 0)
81+
82+
def test_choice(self):
83+
self.assertEqual(score([3, 3, 5, 6, 6], yacht.CHOICE), 23)
84+
85+
def test_yacht_as_choice(self):
86+
self.assertEqual(score([2, 2, 2, 2, 2], yacht.CHOICE), 10)
87+
88+
89+
if __name__ == '__main__':
90+
unittest.main()

0 commit comments

Comments
 (0)