diff --git a/exercises/concept/black-jack/.docs/hints.md b/exercises/concept/black-jack/.docs/hints.md index 48d5446c44..f91a51a182 100644 --- a/exercises/concept/black-jack/.docs/hints.md +++ b/exercises/concept/black-jack/.docs/hints.md @@ -1,21 +1,33 @@ # General -- [The Python Comparisons Tutorial][the python comparisons tutorial] and [Python comparisons examples][python comparisons examples] can be a great introduction. +[The Python comparisons tutorial][python comparisons tutorial] and [Python comparisons examples][python comparisons examples] are a great introduction covering the content of this exercise. -## 1. Calculate the number of card +## 1. Calculate the value of a card -- You can use the [equality comparison operator][equality comparison operator] to get the number of the card. +- You can use the equality comparison operator `==` to determine specific cards, e.g. `card == 'J'`. +- You can use the [`int` constructor][int constructor] to get an integer number from an integer literal, e.g. `int(card)`. -## 2. Calculate the number of Ace +## 2. Calculate the value of an ace -- You can use the [order comparisons operator][order comparisons operator]to decide the value of ace without the sum of hand exceeding 21. +- You can use the order comparison operator `>` to decide the appropriate course of action, e.g. `hand_value + 11 > 21`. -## 3. Judge Blackjack +## 3. Determine Blackjack -- You can use the [membership test operations][membership test operations] in `if` or `elif` syntax to find black-jack from the first two cards in your hand. +- You can use the [`if`/`elif`/`else` syntax][if syntax] to handle different combinations of cards. +- You can reuse the already implemented `value_of_card` function. -[the python comparisons tutorial]: https://docs.python.org/3/reference/expressions.html#comparisons +## 4. Splitting pairs + +- You can handle the `A` case (when at least one of the cards in an ace) separately. + +## 5. Doubling down + +- You can chain comparison operators, e.g. `9 <= hand_value <= 11`. +- You can use the [conditional expression][conditional expression] (sometimes called a "ternary operator") +to shorten simple `if`/`else` statements, e.g. `1 if card == 'A' else value_of_card(card)`. + +[python comparisons tutorial]: https://docs.python.org/3/reference/expressions.html#comparisons [python comparisons examples]: https://www.tutorialspoint.com/python/comparison_operators_example.htm -[equality comparison operator]: https://docs.python.org/3/reference/expressions.html#comparisons -[order comparisons operator]: https://docs.python.org/3/reference/expressions.html#comparisons -[membership test operations]: https://docs.python.org/3/reference/expressions.html#comparisons \ No newline at end of file +[int constructor]: https://docs.python.org/3/library/functions.html#int +[if syntax]: https://docs.python.org/3/tutorial/controlflow.html#if-statements +[conditional expression]: https://docs.python.org/3/reference/expressions.html#conditional-expressions diff --git a/exercises/concept/black-jack/.docs/instructions.md b/exercises/concept/black-jack/.docs/instructions.md index b906923998..774b35fdb4 100644 --- a/exercises/concept/black-jack/.docs/instructions.md +++ b/exercises/concept/black-jack/.docs/instructions.md @@ -1,50 +1,91 @@ -In this exercise, you're going to implement some rules from [Blackjack][blackjack] +# Instructions -You have some rules to implement for judging the result of the game. +In this exercise you are going to implement some rules of [Blackjack][blackjack], +such as the way the game is played and scored. -**Note** : In this exercise, _A_ means an ace, _J_ means jack, _Q_ means queen, and _K_ means king card. +**Note** : In this exercise, _A_ means ace, _J_ means jack, _Q_ means queen, and _K_ means king cards. +A [standard 52-card deck][standard_deck] is assumed. -## 1. Calculate the number of card +## 1. Calculate the value of a card -Create the `number_of_card()` function with a parameter `card`. The value of _J, Q_ or _K_ is 10. If the `card` is _A_, then just return "ace". +In Blackjack, it is up to each individual player if an ace is worth 1 or 11 points. +Face cards (_J_, _Q_, _K_) are worth 10 points and any other card is worth its pip (numerical) value. + +Define the `value_of_card` function with a parameter `card`. +The value of _J_, _Q_ or _K_ is 10. +Otherwise, return the numerical value of a card. +An ace can take on multiple values so let's ignore _A_ for the time being. ```python ->>> number_of_card('K') +>>> value_of_card('K') 10 ->>> number_of_card('A') -ace +>>> value_of_card('4') +4 ``` -## 2. Calculate the number of Ace - -Create the `number_of_ace()` function with a parameter `hand`. +## 2. Calculate the value of an ace -1. `hand` : the sum of cards in hand with an ace. +As mentioned before, an ace can be worth _either_ 1 or 11 points. +At the same time, players are trying to get as close to 21 as possible, without going _over_ 21. -Ace is 1 or 11. You have to decide the value of ace without the sum of hand exceeding 21. +Define the `value_of_ace()` function with a parameter `hand_value`, +which is the total hand value before getting an ace card. +You now have to decide if the upcoming ace card will be scored as a 1 or 11. +The value of the hand with the ace needs to be as high as possible _without_ going over 21. ```python ->>> number_of_ace(19) +>>> value_of_ace(19) 1 ->>> number_of_ace(7) +>>> value_of_ace(7) 11 ``` -## 3. Judge Blackjack +## 3. Determine Blackjack + +If the first two cards are an ace and a ten-card, giving a count of 21 in two cards, it is known as blackjack. + +Define the `is_blackjack(, )` function with parameters `card_one` and `card_two`, which are a pair of cards. +Determine if the two-card hand is a blackjack. -Create the `blackjack()` function with a parameter `hand`. +**Note** : This calculation can be done in many ways. + If possible, check if there is an ace and a ten-card in the hand. -1. `hand` : first two cards in hand. +```python +>>> is_blackjack('A', 'K') +True +>>> is_blackjack('10', '9') +False +``` + +## 4. Splitting pairs + +If the first two cards are of the same value, +such as two sixes, or a _Q_ and _K_ a player may choose to treat them as two separate hands. +This is known as "splitting pairs". + +Define the `can_split_pairs(, )` function with parameters `card_one` and `card_two`, which are a pair of cards. +Determine if this two-card hand can be split into two pairs. +```python +>>> can_split_pairs('Q', 'K') +True +>>> can_split_pairs('10', 'A') +False +``` -This function should return if the hand is blackjack. There's must be an ace card in `hand`. +## 5. Doubling down -**Note** : If the player has an Ace and a ten-value card, it is called a _Blackjack_. Ten-value cards include _10, J, Q, K_. I think you may have many ways. But if you can, use a way to check if there are an ace and a ten-value in the list. +When the original two cards dealt total 9, 10, or 11 points +a player can place an additional bet equal to the original bet. +This is known as "doubling down". +Define the `can_double_down(, )` function with parameters `card_one` and `card_two`, which are a pair of cards. +Determine if the two-card hand can be "doubled down". ```python ->>> blackjack(['A', 'K']) +>>> can_double_down('A', '9') True ->>> blackjack([10, 9]) +>>> can_double_down('10', '2') False ``` -[blackjack]: https://en.wikipedia.org/wiki/Blackjack \ No newline at end of file +[blackjack]: https://en.wikipedia.org/wiki/Blackjack +[standard_deck]: https://en.wikipedia.org/wiki/Standard_52-card_deck diff --git a/exercises/concept/black-jack/.meta/config.json b/exercises/concept/black-jack/.meta/config.json index e5e5971ea3..c120dafa28 100644 --- a/exercises/concept/black-jack/.meta/config.json +++ b/exercises/concept/black-jack/.meta/config.json @@ -1,6 +1,6 @@ { "blurb": "Learn about comparisons by implementing some Black Jack judging rules.", - "authors": ["Ticktakto", "Yabby1997", "limm-jk", "OMEGA-Y", "wnstj2007"], + "authors": ["Ticktakto", "Yabby1997", "limm-jk", "OMEGA-Y", "wnstj2007", "pranasziaukas"], "icon": "poker", "contributors": ["bethanyg"], "files": { diff --git a/exercises/concept/black-jack/.meta/exemplar.py b/exercises/concept/black-jack/.meta/exemplar.py index 7276260e89..0b6c7b3208 100644 --- a/exercises/concept/black-jack/.meta/exemplar.py +++ b/exercises/concept/black-jack/.meta/exemplar.py @@ -1,23 +1,72 @@ -def number_of_card(card): - if card == 'A': - return "ace" - elif card == 'J' or card == 'Q' or card == 'K': - return 10 +def value_of_card(card): + """ + + :param card: str - given card. + :return: int - value of a given card (J, Q, K = 10, numerical value otherwise). + """ + + if card == 'J' or card == 'Q' or card == 'K': + value = 10 else: - return card + value = int(card) + return value + + +def value_of_ace(hand_value): + """ + :param hand_value: int - current hand value. + :return: int - value of the upcoming ace card (either 1 or 11). + """ -def number_of_ace(hand): - if hand + 11 <= 21: - return 11 + if hand_value + 11 > 21: + value = 1 else: - return 1 + value = 11 + return value -def blackjack(hand): - if 'A' not in hand: - return False - elif 'J' in hand or 'Q' in hand or 'K' in hand or 10 in hand: - return True +def is_blackjack(card_one, card_two): + """ + + :param card_one: str - first card in hand. + :param card_two: str - second card in hand. + :return: bool - if the hand is a blackjack (two cards worth 21). + """ + + if card_one == 'A' and card_two != 'A': + blackjack = value_of_card(card_two) == 10 + elif card_one != 'A' and card_two == 'A': + blackjack = value_of_card(card_one) == 10 else: - False + blackjack = False + return blackjack + + +def can_split_pairs(card_one, card_two): + """ + + :param card_one: str - first card in hand. + :param card_two: str - second card in hand. + :return: bool - if the hand can be split into two pairs (i.e. cards are of the same value). + """ + + if card_one == 'A' or card_two == 'A': + split_pairs = card_one == card_two + else: + split_pairs = value_of_card(card_one) == value_of_card(card_two) + return split_pairs + + +def can_double_down(card_one, card_two): + """ + + :param card_one: str - first card in hand. + :param card_two: str - second card in hand. + :return: bool - if the hand can be doubled down (i.e. totals 9, 10 or 11 points). + """ + + card_one_value = 1 if card_one == 'A' else value_of_card(card_one) + card_two_value = 1 if card_two == 'A' else value_of_card(card_two) + hand_value = card_one_value + card_two_value + return 9 <= hand_value <= 11 diff --git a/exercises/concept/black-jack/black_jack.py b/exercises/concept/black-jack/black_jack.py index 5c954d2d3f..7343184f97 100644 --- a/exercises/concept/black-jack/black_jack.py +++ b/exercises/concept/black-jack/black_jack.py @@ -1,10 +1,51 @@ -def number_of_card(card): +def value_of_card(card): + """ + + :param card: str - given card. + :return: int - value of a given card (J, Q, K = 10, numerical value otherwise). + """ + + pass + + +def value_of_ace(hand_value): + """ + + :param hand_value: int - current hand value. + :return: int - value of the upcoming ace card (either 1 or 11). + """ + + pass + + +def is_blackjack(card_one, card_two): + """ + + :param card_one: str - first card in hand. + :param card_two: str - second card in hand. + :return: bool - if the hand is a blackjack (two cards worth 21). + """ + pass -def number_of_ace(hand): +def can_split_pairs(card_one, card_two): + """ + + :param card_one: str - first card in hand. + :param card_two: str - second card in hand. + :return: bool - if the hand can be split into two pairs (i.e. cards are of the same value). + """ + pass -def blackjack(hand): +def can_double_down(card_one, card_two): + """ + + :param card_one: str - first card in hand. + :param card_two: str - second card in hand. + :return: bool - if the hand can be doubled down (i.e. totals 9, 10 or 11 points). + """ + pass diff --git a/exercises/concept/black-jack/black_jack_test.py b/exercises/concept/black-jack/black_jack_test.py index 0e12d27187..bfef90bff5 100644 --- a/exercises/concept/black-jack/black_jack_test.py +++ b/exercises/concept/black-jack/black_jack_test.py @@ -1,43 +1,109 @@ import unittest import pytest from black_jack import ( - number_of_card, - number_of_ace, - blackjack + value_of_card, + value_of_ace, + is_blackjack, + can_split_pairs, + can_double_down ) class BlackJackTest(unittest.TestCase): @pytest.mark.task(taskno=1) - def test_number_of_card(self): - input_data = ['K', 'A'] - output_data = [10, "ace"] - number_of_variants = range(1, len(input_data) + 1) + def test_value_of_card(self): + data = [ + ('2', 2), + ('5', 5), + ('8', 8), + ('10', 10), + ('J', 10), + ('Q', 10), + ('K', 10), + ] - for variant, input, output in zip(number_of_variants, input_data, output_data): - with self.subTest(f"variation #{variant}", input=input, output=output): - self.assertEqual(number_of_card(input[0], input[1]), output, - msg=f'Expected: {output} but the number of cards was calculated incorrectly.') + for variant, (card, value) in enumerate(data, 1): + with self.subTest(f'variation #{variant}', input=card, output=value): + self.assertEqual( + value, + value_of_card(card), + msg=f'Expected {value} as the value of {card}.' + ) @pytest.mark.task(taskno=2) - def test_number_of_ace(self): - input_data = [19, 7] - output_data = [1, 11] - number_of_variants = range(1, len(input_data) + 1) + def test_value_of_ace(self): + data = [ + (2, 11), + (5, 11), + (7, 11), + (9, 11), + (10, 11), + (11, 1), + (12, 1), + (15, 1), + (19, 1), + (20, 1), + ] - for variant, input, output in zip(number_of_variants, input_data, output_data): - with self.subTest(f"variation #{variant}", input=input, output=output): - self.assertEqual(number_of_ace(input[0], input[1]), output, - msg=f'Expected: {output} but the number of Ace cards was calculated incorrectly.') + for variant, (hand_value, ace_value) in enumerate(data, 1): + with self.subTest(f'variation #{variant}', input=hand_value, output=ace_value): + self.assertEqual( + ace_value, + value_of_ace(hand_value), + msg=f'Expected {ace_value} as the value of ace when the hand is worth {hand_value}.' + ) @pytest.mark.task(taskno=3) - def test_blackjack(self): - input_data = [['A', 'J'], [10, 9]] - output_data = [True, False] - number_of_variants = range(1, len(input_data) + 1) - - for variant, input, output in zip(number_of_variants, input_data, output_data): - with self.subTest(f"variation #{variant}", input=input, output=output): - self.assertEqual(blackjack(input[0], input[1]), output, - msg=f'Expected: {output} but the value returned was incorrect,') + def test_is_blackjack(self): + data = [ + (('A', 'K'), True), + (('10', 'A'), True), + (('10', '9'), False), + (('A', 'A'), False), + ] + + for variant, (hand, blackjack) in enumerate(data, 1): + with self.subTest(f'variation #{variant}', input=hand, output=blackjack): + self.assertEqual( + blackjack, + is_blackjack(*hand), + msg=f'Hand {hand} {"is" if blackjack else "is not"} a blackjack.' + ) + + @pytest.mark.task(taskno=4) + def test_can_split_pairs(self): + data = [ + (('Q', 'K'), True), + (('6', '6'), True), + (('A', 'A'), True), + (('10', 'A'), False), + (('10', '9'), False), + ] + + for variant, (hand, split_pairs) in enumerate(data, 1): + with self.subTest(f'variation #{variant}', input=hand, output=split_pairs): + self.assertEqual( + split_pairs, + can_split_pairs(*hand), + msg=f'Hand {hand} {"can" if split_pairs else "cannot"} be split into pairs.' + ) + + @pytest.mark.task(taskno=5) + def test_can_double_down(self): + data = [ + (('A', '9'), True), + (('K', 'A'), True), + (('4', '5'), True), + (('A', 'A'), False), + (('10', '2'), False), + (('10', '9'), False), + ] + + for variant, (hand, double_down) in enumerate(data, 1): + with self.subTest(f'variation #{variant}', input=hand, output=double_down): + self.assertEqual( + double_down, + can_double_down(*hand), + msg=f'Hand {hand} {"can" if double_down else "cannot"} be doubled down.' + )