Compare commits

..

2 commits

Author SHA1 Message Date
9cfecd833c
Python: completed Black Jack 2024-09-28 12:12:21 -04:00
9750e8d9ef
Python: completed Little Sister's Essay 2024-09-28 12:12:02 -04:00
14 changed files with 1339 additions and 0 deletions

View file

@ -0,0 +1,27 @@
{
"authors": [
"Ticktakto",
"Yabby1997",
"limm-jk",
"OMEGA-Y",
"wnstj2007",
"pranasziaukas",
"bethanyG"
],
"contributors": [
"PaulT89"
],
"files": {
"solution": [
"black_jack.py"
],
"test": [
"black_jack_test.py"
],
"exemplar": [
".meta/exemplar.py"
]
},
"icon": "poker",
"blurb": "Learn about comparisons by implementing some Black Jack judging rules."
}

View file

@ -0,0 +1 @@
{"track":"python","exercise":"black-jack","id":"c39a445c45914cf69f9cd468976225b8","url":"https://exercism.org/tracks/python/exercises/black-jack","handle":"Chomp1295","is_requester":true,"auto_approve":false}

130
python/black-jack/HELP.md Normal file
View file

@ -0,0 +1,130 @@
# Help
## Running the tests
We use [pytest][pytest: Getting Started Guide] as our website test runner.
You will need to install `pytest` on your development machine if you want to run tests for the Python track locally.
You should also install the following `pytest` plugins:
- [pytest-cache][pytest-cache]
- [pytest-subtests][pytest-subtests]
Extended information can be found in our website [Python testing guide][Python track tests page].
### Running Tests
To run the included tests, navigate to the folder where the exercise is stored using `cd` in your terminal (_replace `{exercise-folder-location}` below with your path_).
Test files usually end in `_test.py`, and are the same tests that run on the website when a solution is uploaded.
Linux/MacOS
```bash
$ cd {path/to/exercise-folder-location}
```
Windows
```powershell
PS C:\Users\foobar> cd {path\to\exercise-folder-location}
```
<br>
Next, run the `pytest` command in your terminal, replacing `{exercise_test.py}` with the name of the test file:
Linux/MacOS
```bash
$ python3 -m pytest -o markers=task {exercise_test.py}
==================== 7 passed in 0.08s ====================
```
Windows
```powershell
PS C:\Users\foobar> py -m pytest -o markers=task {exercise_test.py}
==================== 7 passed in 0.08s ====================
```
### Common options
- `-o` : override default `pytest.ini` (_you can use this to avoid marker warnings_)
- `-v` : enable verbose output.
- `-x` : stop running tests on first failure.
- `--ff` : run failures from previous test before running other test cases.
For additional options, use `python3 -m pytest -h` or `py -m pytest -h`.
### Fixing warnings
If you do not use `pytest -o markers=task` when invoking `pytest`, you might receive a `PytestUnknownMarkWarning` for tests that use our new syntax:
```bash
PytestUnknownMarkWarning: Unknown pytest.mark.task - is this a typo? You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/mark.html
```
To avoid typing `pytest -o markers=task` for every test you run, you can use a `pytest.ini` configuration file.
We have made one that can be downloaded from the top level of the Python track directory: [pytest.ini][pytest.ini].
You can also create your own `pytest.ini` file with the following content:
```ini
[pytest]
markers =
task: A concept exercise task.
```
Placing the `pytest.ini` file in the _root_ or _working_ directory for your Python track exercises will register the marks and stop the warnings.
More information on pytest marks can be found in the `pytest` documentation on [marking test functions][pytest: marking test functions with attributes] and the `pytest` documentation on [working with custom markers][pytest: working with custom markers].
Information on customizing pytest configurations can be found in the `pytest` documentation on [configuration file formats][pytest: configuration file formats].
### Extending your IDE or Code Editor
Many IDEs and code editors have built-in support for using `pytest` and other code quality tools.
Some community-sourced options can be found on our [Python track tools page][Python track tools page].
[Pytest: Getting Started Guide]: https://docs.pytest.org/en/latest/getting-started.html
[Python track tools page]: https://exercism.org/docs/tracks/python/tools
[Python track tests page]: https://exercism.org/docs/tracks/python/tests
[pytest-cache]:http://pythonhosted.org/pytest-cache/
[pytest-subtests]:https://github.com/pytest-dev/pytest-subtests
[pytest.ini]: https://github.com/exercism/python/blob/main/pytest.ini
[pytest: configuration file formats]: https://docs.pytest.org/en/6.2.x/customize.html#configuration-file-formats
[pytest: marking test functions with attributes]: https://docs.pytest.org/en/6.2.x/mark.html#raising-errors-on-unknown-marks
[pytest: working with custom markers]: https://docs.pytest.org/en/6.2.x/example/markers.html#working-with-custom-markers
## Submitting your solution
You can submit your solution using the `exercism submit black_jack.py` command.
This command will upload your solution to the Exercism website and print the solution page's URL.
It's possible to submit an incomplete solution which allows you to:
- See how others have completed the exercise
- Request help from a mentor
## Need to get help?
If you'd like help solving the exercise, check the following pages:
- The [Python track's documentation](https://exercism.org/docs/tracks/python)
- The [Python track's programming category on the forum](https://forum.exercism.org/c/programming/python)
- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5)
- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs)
Should those resources not suffice, you could submit your (incomplete) solution to request mentoring.
Below are some resources for getting help if you run into trouble:
- [The PSF](https://www.python.org) hosts Python downloads, documentation, and community resources.
- [The Exercism Community on Discord](https://exercism.org/r/discord)
- [Python Community on Discord](https://pythondiscord.com/) is a very helpful and active community.
- [/r/learnpython/](https://www.reddit.com/r/learnpython/) is a subreddit designed for Python learners.
- [#python on Libera.chat](https://www.python.org/community/irc/) this is where the core developers for the language hang out and get work done.
- [Python Community Forums](https://discuss.python.org/)
- [Free Code Camp Community Forums](https://forum.freecodecamp.org/)
- [CodeNewbie Community Help Tag](https://community.codenewbie.org/t/help)
- [Pythontutor](http://pythontutor.com/) for stepping through small code snippets visually.
Additionally, [StackOverflow](http://stackoverflow.com/questions/tagged/python) is a good spot to search for your problem/question to see if it has been answered already.
If not - you can always [ask](https://stackoverflow.com/help/how-to-ask) or [answer](https://stackoverflow.com/help/how-to-answer) someone else's question.

View file

@ -0,0 +1,45 @@
# Hints
[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 value of a card
- You can use the equality comparison operator `==` to determine if a card is an ace card: `card == 'A'`.
- You can use the containment operator `in` to determine if a substring is contained inside a string: `'Q' in 'KJQ'`.
- You can use the [`int` constructor][int constructor] to convert a `str` of an `int` to an `int`: `int('13')`.
## 2. Determine which card has a higher value
- Once you have defined the `value_of_card` function, you can call it from other functions.
- You can use the value comparison operators `>` and `<` to determine if specific cards are _greater than_ or _less than_ a given value: `3 < 12`.
- You can use the equality comparison operator `==` to determine if two values are equal to one another.
## 3. Calculate the value of an ace
- Once you have defined the `value_of_card` function, you can call it from other functions.
- You can use the order comparison operator `>` to decide the appropriate course of action here.
## 4. Determine Blackjack
- Remember, you can use the [`if`/`elif`/`else` syntax][if syntax] to handle different combinations of cards.
- You can chain BOTH comparison operators and boolean operators _arbitrarily_: `y < z < x` or `(y or z) and (x or z)`
- You can reuse the already implemented `value_of_card` function.
## 5. Splitting pairs
- You can reuse the already implemented `value_of_card` function.
- You can handle the `A` case (when at least one of the cards in an ace) separately.
## 6. Doubling down
- An `A` scored at 11 will never allow doubling down if there are two cards in the hand.
- Given the first point, you _should_ be able to reuse the already implemented `value_of_card` function.
- You can chain comparison operators _arbitrarily_: `y < z < x`.
- You can use the [conditional expression][conditional expression] (_sometimes called a "ternary operator"_)
to shorten simple `if`/`else` statements: `13 if letter == 'M' else 3`.
[conditional expression]: https://docs.python.org/3/reference/expressions.html#conditional-expressions
[if syntax]: https://docs.python.org/3/tutorial/controlflow.html#if-statements
[int constructor]: https://docs.python.org/3/library/functions.html#int
[python comparisons examples]: https://www.tutorialspoint.com/python/comparison_operators_example.htm
[python comparisons tutorial]: https://docs.python.org/3/reference/expressions.html#comparisons

395
python/black-jack/README.md Normal file
View file

@ -0,0 +1,395 @@
# Black Jack
Welcome to Black Jack on Exercism's Python Track.
If you need help running the tests or submitting your code, check out `HELP.md`.
If you get stuck on the exercise, check out `HINTS.md`, but try and solve it without using those first :)
## Introduction
## Comparisons
Python supports the following basic comparison operators:
| Operator | Operation | Description |
| -------- | -------------------------- | ------------------------------------------------------------------------- |
| `>` | "greater than" | `a > b` is `True` if `a` is **strictly** greater in value than `b` |
| `<` | "less than" | `a < b` is `True` if `a` is **strictly** less in value than `b` |
| `==` | "equal to" | `a == b` is `True` if `a` is **strictly** equal to `b` in value |
| `>=` | "greater than or equal to" | `a >= b` is `True` if `a > b` OR `a == b` in value |
| `<=` | "less than or equal to" | `a <= b` is `True` if `a < b` or `a == b` in value |
| `!=` | "not equal to" | `a != b` is `True` if `a == b` is `False` |
| `is` | "identity" | `a is b` is `True` if **_and only if_** `a` and `b` are the same _object_ |
| `is not` | "negated identity" | `a is not b` is `True` if `a` and `b` are **not** the same _object_ |
| `in` | "containment test" | `a in b` is `True` if `a` is member, subset, or element of `b` |
| `not in` | "negated containment test" | `a not in b` is `True` if `a` is not a member, subset, or element of `b` |
They all have the same priority (_which is higher than that of [Boolean operations][boolean operations], but lower than that of arithmetic or bitwise operations_).
## Comparison between different data types
Objects that are different types (_except numeric types_) never compare equal by default.
Non-identical instances of a `class` will also _**not**_ compare as equal unless the `class` defines special [rich comparison][rich comparisons] methods that customize the default `object` comparison behavior.
Customizing via `rich comparisons` will be covered in a follow-on exercise.
For (much) more detail on this topic, see [Value comparisons][value comparisons] in the Python documentation.
Numeric types are (mostly) an exception to this type matching rule.
An `integer` **can** be considered equal to a `float` (_or an [`octal`][octal] equal to a [`hexadecimal`][hex]_), as long as the types can be implicitly converted for comparison.
For the other numeric types in the Python standard library ([complex][complex numbers], [decimal][decimal numbers], [fractions][rational numbers]), comparison operators are defined where they "make sense" (_where implicit conversion does not change the outcome_), but throw a `TypeError` if the underlying objects cannot be accurately converted for comparison.
For more information on the rules that python uses for _numeric conversion_, see [arithmetic conversions][arithmetic conversions] in the Python documentation.
```python
>>> import fractions
# A string cannot be converted to an int.
>>> 17 == '17'
False
# An int can be converted to float for comparison.
>>> 17 == 17.0
True
# The fraction 6/3 can be converted to the int 2
# The int 2 can be converted to 0b10 in binary.
>>> 6/3 == 0b10
True
# An int can be converted to a complex number with a 0 imaginary part.
>>> 17 == complex(17)
True
# The fraction 2/5 can be converted to the float 0.4
>>> 0.4 == 2/5
True
>>> complex(2/5, 1/2) == complex(0.4, 0.5)
True
```
Any ordered comparison of a number to a `NaN` (_not a number_) type is `False`.
A confusing side effect of Python's `NaN` definition is that `NaN` never compares equal to `NaN`.
```python
>>> x = float('NaN')
>>> 3 < x
False
>>> x < 3
False
# NaN never compares equal to NaN
>>> x == x
False
```
## Comparing Strings
Unlike numbers, strings (`str`) are compared [_lexicographically_][lexographic order], using their individual Unicode code points (_the result of passing each code point in the `str` to the built-in function [`ord()`][ord], which returns an `int`_).
If all code points in both strings match and are _**in the same order**_, the two strings are considered equal.
This comparison is done in a 'pair-wise' fashion - first-to-first, second-to-second, etc.
In Python 3.x, `str` and `bytes` cannot be directly coerced/compared.
```python
>>> 'Python' > 'Rust'
False
>>> 'Python' > 'JavaScript'
True
# Examples with Mandarin.
# hello < goodbye
>>> '你好' < '再见'
True
# ord() of first characters
>>> ord('你'), ord('再')
(20320, 20877)
# ord() of second characters
>>> ord('好'), ord('见')
(22909, 35265)
# And with Korean words.
# Pretty < beautiful.
>>> '예쁜' < '아름다운'
False
>>> ord('예'), ord('아')
(50696, 50500)
```
## Comparison Chaining
Comparison operators can be chained _arbitrarily_ -- meaning that they can be used in any combination of any length.
Note that the evaluation of an expression takes place from `left` to `right`.
As an example, `x < y <= z` is equivalent to `x < y` `and` `y <= z`, except that `y` is evaluated **only once**.
In both cases, `z` is _not_ evaluated **at all** when `x < y` is found to be `False`.
This is often called `short-circuit evaluation` - the evaluation stops if the truth value of the expression has already been determined.
`Short circuiting` is supported by various boolean operators, functions, and also by comparison chaining in Python.
Unlike many other programming languages, including `C`, `C++`, `C#`, and `Java`, chained expressions like `a < b < c` in Python have a conventional [mathematical interpretation][three way boolean comparison] and precedence.
```python
>>> x = 2
>>> y = 5
>>> z = 10
>>> x < y < z
True
>>> x < y > z
False
>>> x > y < z
False
```
## Comparing object identity
The operators `is` and `is not` test for object [_identity_][object identity], as opposed to object _value_.
An object's identity never changes after creation and can be found by using the [`id()`][id function] function.
`<apple> is <orange>` evaluates to `True` if _**and only if**_ `id(<apple>)` == `id(<orange>)`.
`<apple> is not <orange>` yields the inverse.
Due to their singleton status, `None` and `NotImplemented` should always be compared to items using `is` and `is not`.
See the Python reference docs on [value comparisons][value comparisons none] and [PEP8][pep8 programming recommendations] for more details on this convention.
```python
>>> my_fav_numbers = [1, 2, 3]
>>> your_fav_numbers = my_fav_numbers
>>> my_fav_numbers is your_fav_numbers
True
# The returned id will differ by system and python version.
>>> id(my_fav_numbers)
4517478208
# your_fav_numbers is only an alias pointing to the original my_fav_numbers object.
# Assigning a new name does not create a new object.
>>> id(your_fav_numbers)
4517478208
>>> my_fav_numbers is not your_fav_numbers
False
>>> my_fav_numbers is not None
True
>>> my_fav_numbers is NotImplemented
False
```
## Membership comparisons
The operators `in` and `not in` test for _membership_.
`<fish> in <soup>` evaluates to `True` if `<fish>` is a member of `<soup>` (_if `<fish>` is a subset of or is contained within `<soup>`_), and evaluates `False` otherwise.
`<fish> not in <soup>` returns the negation, or _opposite of_ `<fish> in <soup>`.
For string and bytes types, `<name> in <fullname>` is `True` _**if and only if**_ `<name>` is a substring of `<fullname>`.
```python
# A set of lucky numbers.
>>> lucky_numbers = {11, 22, 33}
>>> 22 in lucky_numbers
True
>>> 44 in lucky_numbers
False
# A dictionary of employee information.
>>> employee = {'name': 'John Doe',
'id': 67826, 'age': 33,
'title': 'ceo'}
# Checking for the membership of certain keys.
>>> 'age' in employee
True
>>> 33 in employee
False
>>> 'lastname' not in employee
True
# Checking for substring membership
>>> name = 'Super Batman'
>>> 'Bat' in name
True
>>> 'Batwoman' in name
False
```
[arithmetic conversions]: https://docs.python.org/3/reference/expressions.html?highlight=number%20conversion#arithmetic-conversions
[boolean operations]: https://docs.python.org/3/library/stdtypes.html#boolean-operations-and-or-not
[complex numbers]: https://docs.python.org/3/library/functions.html#complex
[decimal numbers]: https://docs.python.org/3/library/decimal.html
[hex]: https://docs.python.org/3/library/functions.html?highlight=hex#hex
[id function]: https://docs.python.org/3/library/functions.html#id
[lexographic order]: https://en.wikipedia.org/wiki/Lexicographic_order
[object identity]: https://docs.python.org/3/reference/datamodel.html
[octal]: https://docs.python.org/3/library/functions.html?#oct
[ord]: https://docs.python.org/3/library/functions.html#ord
[pep8 programming recommendations]: https://pep8.org/#programming-recommendations
[rational numbers]: https://docs.python.org/3/library/fractions.html
[rich comparisons]: https://docs.python.org/3/reference/datamodel.html#object.__lt__
[three way boolean comparison]: https://en.wikipedia.org/wiki/Three-way_comparison
[value comparisons none]: https://docs.python.org/3/reference/expressions.html?highlight=none#value-comparisons
[value comparisons]: https://docs.python.org/3/reference/expressions.html?highlight=nan#value-comparisons
## Instructions
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 ace, _`J`_ means jack, _`Q`_ means queen, and _`K`_ means king.
Jokers are discarded.
A [standard French-suited 52-card deck][standard_deck] is assumed, but in most versions, several decks are shuffled together for play.
## 1. Calculate the value of a card
In Blackjack, it is up to each individual player if an ace is worth 1 or 11 points (_more on that later_).
Face cards (`J`, `Q`, `K`) are scored at 10 points and any other card is worth its "pip" (_numerical_) value.
Define the `value_of_card(<card>)` function with parameter `card`.
The function should return the _numerical value_ of the passed-in card string.
Since an ace can take on multiple values (1 **or** 11), this function should fix the value of an ace card at 1 for the time being.
Later on, you will implement a function to determine the value of an ace card, given an existing hand.
```python
>>> value_of_card('K')
10
>>> value_of_card('4')
4
>>> value_of_card('A')
1
```
## 2. Determine which card has a higher value
Define the `higher_card(<card_one>, <card_two>)` function having parameters `card_one` and `card_two`.
For scoring purposes, the value of `J`, `Q` or `K` is 10.
The function should return which card has the higher value for scoring.
If both cards have an equal value, return both.
Returning both cards can be done by using a comma in the `return` statement:
```python
# Using a comma in a return creates a Tuple. Tuples will be covered in a later exercise.
>>> def returning_two_values(value_one, value_two):
return value_one, value_two
>>> returning_two_values('K', '3')
('K', '3')
```
An ace can take on multiple values, so we will fix `A` cards to a value of 1 for this task.
```python
>>> higher_card('K', '10')
('K', '10')
>>> higher_card('4', '6')
'6'
>>> higher_card('K', 'A')
'K'
```
## 3. Calculate the value of an ace
As mentioned before, an ace can be worth _either_ 1 **or** 11 points.
Players try to get as close as possible to a score of 21, without going _over_ 21 (_going "bust"_).
Define the `value_of_ace(<card_one>, <card_two>)` function with parameters `card_one` and `card_two`, which are a pair of cards already in the hand _before_ getting an ace card.
Your function will have to decide if the upcoming ace will get a value of 1 or a value of 11, and return that value.
Remember: the value of the hand with the ace needs to be as high as possible _without_ going over 21.
**Hint**: if we already have an ace in hand, then the value for the upcoming ace would be 1.
```python
>>> value_of_ace('6', 'K')
1
>>> value_of_ace('7', '3')
11
```
## 4. Determine a "Natural" or "Blackjack" Hand
If a player is dealt an ace (`A`) and a ten-card (10, `K`, `Q`, or `J`) as their first two cards, then the player has a score of 21.
This is known as a **blackjack** hand.
Define the `is_blackjack(<card_one>, <card_two>)` function with parameters `card_one` and `card_two`, which are a pair of cards.
Determine if the two-card hand is a **blackjack**, and return the boolean `True` if it is, `False` otherwise.
**Note** : The score _calculation_ can be done in many ways.
But if possible, we'd like you to check if there is an ace and a ten-card **_in_** the hand (_or at a certain position_), as opposed to _summing_ the hand values.
```python
>>> is_blackjack('A', 'K')
True
>>> is_blackjack('10', '9')
False
```
## 5. Splitting pairs
If the players 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(<card_one>, <card_two>)` 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.
If the hand can be split, return the boolean `True` otherwise, return `False`
```python
>>> can_split_pairs('Q', 'K')
True
>>> can_split_pairs('10', 'A')
False
```
## 6. Doubling down
When the original two cards dealt total 9, 10, or 11 points, a player can place an additional bet equal to their original bet.
This is known as "doubling down".
Define the `can_double_down(<card_one>, <card_two>)` function with parameters `card_one` and `card_two`, which are a pair of cards.
Determine if the two-card hand can be "doubled down", and return the boolean `True` if it can, `False` otherwise.
```python
>>> can_double_down('A', '9')
True
>>> can_double_down('10', '2')
False
```
[blackjack]: https://bicyclecards.com/how-to-play/blackjack/
[standard_deck]: https://en.wikipedia.org/wiki/Standard_52-card_deck
## Source
### Created by
- @Ticktakto
- @Yabby1997
- @limm-jk
- @OMEGA-Y
- @wnstj2007
- @pranasziaukas
- @bethanyG
### Contributed to by
- @PaulT89

View file

@ -0,0 +1,110 @@
"""Functions to help play and score a game of blackjack.
How to play blackjack: https://bicyclecards.com/how-to-play/blackjack/
"Standard" playing cards: https://en.wikipedia.org/wiki/Standard_52-card_deck
"""
def value_of_card(card):
"""Determine the scoring value of a card.
:param card: str - given card.
:return: int - value of a given card. See below for values.
1. 'J', 'Q', or 'K' (otherwise known as "face cards") = 10
2. 'A' (ace card) = 1
3. '2' - '10' = numerical value.
"""
res = 0
if card >= "J":
res = 10
elif card == "A":
res = 1
else:
res = int(card)
return res
def higher_card(card_one, card_two):
"""Determine which card has a higher value in the hand.
:param card_one, card_two: str - cards dealt in hand. See below for values.
:return: str or tuple - resulting Tuple contains both cards if they are of equal value.
1. 'J', 'Q', or 'K' (otherwise known as "face cards") = 10
2. 'A' (ace card) = 1
3. '2' - '10' = numerical value.
"""
card_one_val = value_of_card(card_one)
card_two_val = value_of_card(card_two)
if card_one_val == card_two_val:
return (card_one, card_two)
if card_one_val > card_two_val:
return card_one
return card_two
def value_of_ace(card_one, card_two):
"""Calculate the most advantageous value for the ace card.
:param card_one, card_two: str - card dealt. See below for values.
:return: int - either 1 or 11 value of the upcoming ace card.
1. 'J', 'Q', or 'K' (otherwise known as "face cards") = 10
2. 'A' (ace card) = 11 (if already in hand)
3. '2' - '10' = numerical value.
"""
if (value_of_card(card_one) + value_of_card(card_two) >= 11) or "A" in (
card_one,
card_two,
):
return 1
return 11
def is_blackjack(card_one, card_two):
"""Determine if the hand is a 'natural' or 'blackjack'.
:param card_one, card_two: str - card dealt. See below for values.
:return: bool - is the hand is a blackjack (two cards worth 21).
1. 'J', 'Q', or 'K' (otherwise known as "face cards") = 10
2. 'A' (ace card) = 11 (if already in hand)
3. '2' - '10' = numerical value.
"""
if card_one != "A" and card_two != "A":
return False
if card_one == "A":
return value_of_card(card_two) + 11 == 21
return value_of_card(card_one) + 11 == 21
def can_split_pairs(card_one, card_two):
"""Determine if a player can split their hand into two hands.
:param card_one, card_two: str - cards dealt.
:return: bool - can the hand be split into two pairs? (i.e. cards are of the same value).
"""
return value_of_card(card_one) == value_of_card(card_two)
def can_double_down(card_one, card_two):
"""Determine if a blackjack player can place a double down bet.
:param card_one, card_two: str - first and second cards in hand.
:return: bool - can the hand can be doubled down? (i.e. totals 9, 10 or 11 points).
"""
return 9 <= value_of_card(card_one) + value_of_card(card_two) <= 11

View file

@ -0,0 +1,114 @@
import unittest
import pytest
from black_jack import (
value_of_card,
higher_card,
value_of_ace,
is_blackjack,
can_split_pairs,
can_double_down
)
class BlackJackTest(unittest.TestCase):
@pytest.mark.task(taskno=1)
def test_value_of_card(self):
test_data = [('2', 2), ('5', 5), ('8', 8),
('A', 1), ('10', 10), ('J', 10),
('Q', 10), ('K', 10)]
for variant, (card, expected) in enumerate(test_data, 1):
with self.subTest(f'variation #{variant}', card=card, expected=expected):
actual_result = value_of_card(card)
error_msg = (f'Called value_of_card({card}). '
f'The function returned {actual_result} as the value of the {card} card, '
f'but the test expected {expected} as the {card} card value.')
self.assertEqual(actual_result, expected, msg=error_msg)
@pytest.mark.task(taskno=2)
def test_higher_card(self):
test_data = [('A', 'A', ('A', 'A')),
('10', 'J', ('10', 'J')),
('3', 'A', '3'),
('3', '6', '6'),
('Q', '10', ('Q', '10')),
('4', '4', ('4', '4')),
('9', '10', '10'),
('6', '9', '9'),
('4', '8', '8')]
for variant, (card_one, card_two, expected) in enumerate(test_data, 1):
with self.subTest(f'variation #{variant}', card_one=card_one, card_two=card_two, expected=expected):
actual_result = higher_card(card_one, card_two)
error_msg = (f'Called higher_card({card_one}, {card_two}). '
f'The function returned {actual_result}, '
f'but the test expected {expected} as the result for the cards {card_one, card_two}.')
self.assertEqual(actual_result, expected, msg=error_msg)
@pytest.mark.task(taskno=3)
def test_value_of_ace(self):
test_data = [('2', '3', 11), ('3', '6', 11), ('5', '2', 11),
('8', '2', 11), ('5', '5', 11), ('Q', 'A', 1),
('10', '2', 1), ('7', '8', 1), ('J', '9', 1),
('K', 'K', 1), ('2', 'A', 1), ('A', '2', 1)]
for variant, (card_one, card_two, ace_value) in enumerate(test_data, 1):
with self.subTest(f'variation #{variant}', card_one=card_one, card_two=card_two, ace_value=ace_value):
actual_result = value_of_ace(card_one, card_two)
error_msg = (f'Called value_of_ace({card_one}, {card_two}). '
f'The function returned {actual_result}, '
f'but the test expected {ace_value} as the value of an ace card '
f'when the hand includes {card_one, card_two}.')
self.assertEqual(value_of_ace(card_one, card_two), ace_value, msg=error_msg)
@pytest.mark.task(taskno=4)
def test_is_blackjack(self):
test_data = [(('A', 'K'), True), (('10', 'A'), True),
(('10', '9'), False), (('A', 'A'), False),
(('4', '7'), False), (('9', '2'), False),
(('Q', 'K'), False)]
for variant, (hand, expected) in enumerate(test_data, 1):
with self.subTest(f'variation #{variant}', hand=hand, expected=expected):
actual_result = is_blackjack(*hand)
error_msg = (f'Called is_blackjack({hand[0]}, {hand[1]}). '
f'The function returned {actual_result}, '
f'but hand {hand} {"is" if expected else "is not"} a blackjack.')
self.assertEqual(actual_result, expected, msg=error_msg)
@pytest.mark.task(taskno=5)
def test_can_split_pairs(self):
test_data = [(('Q', 'K'), True), (('6', '6'), True),
(('A', 'A'), True),(('10', 'A'), False),
(('10', '9'), False)]
for variant, (hand, expected) in enumerate(test_data, 1):
with self.subTest(f'variation #{variant}', input=hand, expected=expected):
actual_result = can_split_pairs(*hand)
error_msg = (f'Called can_split_pairs({hand[0]}, {hand[1]}). '
f'The function returned {actual_result}, '
f'but hand {hand} {"can" if expected else "cannot"} be split into pairs.')
self.assertEqual(actual_result, expected, msg=error_msg)
@pytest.mark.task(taskno=6)
def test_can_double_down(self):
test_data = [(('A', '9'), True), (('K', 'A'), True),
(('4', '5'), True),(('A', 'A'), False),
(('10', '2'), False), (('10', '9'), False)]
for variant, (hand, expected) in enumerate(test_data, 1):
with self.subTest(f'variation #{variant}', hand=hand, expected=expected):
actual_result = can_double_down(*hand)
error_msg = (f'Called can_double_down({hand[0]}, {hand[1]}). '
f'The function returned {actual_result}, '
f'but hand {hand} {"can" if expected else "cannot"} be doubled down.')
self.assertEqual(actual_result, expected, msg=error_msg)

View file

@ -0,0 +1,22 @@
{
"authors": [
"kimolivia"
],
"contributors": [
"valentin-p",
"BethanyG"
],
"files": {
"solution": [
"string_methods.py"
],
"test": [
"string_methods_test.py"
],
"exemplar": [
".meta/exemplar.py"
]
},
"icon": "anagrams",
"blurb": "Learn about string methods while improving your little sister's school essay."
}

View file

@ -0,0 +1 @@
{"track":"python","exercise":"little-sisters-essay","id":"68b15f3f00a4425db54965e6136896c1","url":"https://exercism.org/tracks/python/exercises/little-sisters-essay","handle":"Chomp1295","is_requester":true,"auto_approve":false}

View file

@ -0,0 +1,130 @@
# Help
## Running the tests
We use [pytest][pytest: Getting Started Guide] as our website test runner.
You will need to install `pytest` on your development machine if you want to run tests for the Python track locally.
You should also install the following `pytest` plugins:
- [pytest-cache][pytest-cache]
- [pytest-subtests][pytest-subtests]
Extended information can be found in our website [Python testing guide][Python track tests page].
### Running Tests
To run the included tests, navigate to the folder where the exercise is stored using `cd` in your terminal (_replace `{exercise-folder-location}` below with your path_).
Test files usually end in `_test.py`, and are the same tests that run on the website when a solution is uploaded.
Linux/MacOS
```bash
$ cd {path/to/exercise-folder-location}
```
Windows
```powershell
PS C:\Users\foobar> cd {path\to\exercise-folder-location}
```
<br>
Next, run the `pytest` command in your terminal, replacing `{exercise_test.py}` with the name of the test file:
Linux/MacOS
```bash
$ python3 -m pytest -o markers=task {exercise_test.py}
==================== 7 passed in 0.08s ====================
```
Windows
```powershell
PS C:\Users\foobar> py -m pytest -o markers=task {exercise_test.py}
==================== 7 passed in 0.08s ====================
```
### Common options
- `-o` : override default `pytest.ini` (_you can use this to avoid marker warnings_)
- `-v` : enable verbose output.
- `-x` : stop running tests on first failure.
- `--ff` : run failures from previous test before running other test cases.
For additional options, use `python3 -m pytest -h` or `py -m pytest -h`.
### Fixing warnings
If you do not use `pytest -o markers=task` when invoking `pytest`, you might receive a `PytestUnknownMarkWarning` for tests that use our new syntax:
```bash
PytestUnknownMarkWarning: Unknown pytest.mark.task - is this a typo? You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/mark.html
```
To avoid typing `pytest -o markers=task` for every test you run, you can use a `pytest.ini` configuration file.
We have made one that can be downloaded from the top level of the Python track directory: [pytest.ini][pytest.ini].
You can also create your own `pytest.ini` file with the following content:
```ini
[pytest]
markers =
task: A concept exercise task.
```
Placing the `pytest.ini` file in the _root_ or _working_ directory for your Python track exercises will register the marks and stop the warnings.
More information on pytest marks can be found in the `pytest` documentation on [marking test functions][pytest: marking test functions with attributes] and the `pytest` documentation on [working with custom markers][pytest: working with custom markers].
Information on customizing pytest configurations can be found in the `pytest` documentation on [configuration file formats][pytest: configuration file formats].
### Extending your IDE or Code Editor
Many IDEs and code editors have built-in support for using `pytest` and other code quality tools.
Some community-sourced options can be found on our [Python track tools page][Python track tools page].
[Pytest: Getting Started Guide]: https://docs.pytest.org/en/latest/getting-started.html
[Python track tools page]: https://exercism.org/docs/tracks/python/tools
[Python track tests page]: https://exercism.org/docs/tracks/python/tests
[pytest-cache]:http://pythonhosted.org/pytest-cache/
[pytest-subtests]:https://github.com/pytest-dev/pytest-subtests
[pytest.ini]: https://github.com/exercism/python/blob/main/pytest.ini
[pytest: configuration file formats]: https://docs.pytest.org/en/6.2.x/customize.html#configuration-file-formats
[pytest: marking test functions with attributes]: https://docs.pytest.org/en/6.2.x/mark.html#raising-errors-on-unknown-marks
[pytest: working with custom markers]: https://docs.pytest.org/en/6.2.x/example/markers.html#working-with-custom-markers
## Submitting your solution
You can submit your solution using the `exercism submit string_methods.py` command.
This command will upload your solution to the Exercism website and print the solution page's URL.
It's possible to submit an incomplete solution which allows you to:
- See how others have completed the exercise
- Request help from a mentor
## Need to get help?
If you'd like help solving the exercise, check the following pages:
- The [Python track's documentation](https://exercism.org/docs/tracks/python)
- The [Python track's programming category on the forum](https://forum.exercism.org/c/programming/python)
- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5)
- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs)
Should those resources not suffice, you could submit your (incomplete) solution to request mentoring.
Below are some resources for getting help if you run into trouble:
- [The PSF](https://www.python.org) hosts Python downloads, documentation, and community resources.
- [The Exercism Community on Discord](https://exercism.org/r/discord)
- [Python Community on Discord](https://pythondiscord.com/) is a very helpful and active community.
- [/r/learnpython/](https://www.reddit.com/r/learnpython/) is a subreddit designed for Python learners.
- [#python on Libera.chat](https://www.python.org/community/irc/) this is where the core developers for the language hang out and get work done.
- [Python Community Forums](https://discuss.python.org/)
- [Free Code Camp Community Forums](https://forum.freecodecamp.org/)
- [CodeNewbie Community Help Tag](https://community.codenewbie.org/t/help)
- [Pythontutor](http://pythontutor.com/) for stepping through small code snippets visually.
Additionally, [StackOverflow](http://stackoverflow.com/questions/tagged/python) is a good spot to search for your problem/question to see if it has been answered already.
If not - you can always [ask](https://stackoverflow.com/help/how-to-ask) or [answer](https://stackoverflow.com/help/how-to-answer) someone else's question.

View file

@ -0,0 +1,29 @@
# Hints
## General
- [Python Documentation: String Methods][string-method-docs]
- [Python Documentation Tutorial: Text][tutorial-strings]
## 1. Capitalize the title of the paper
- You can use [string methods][title-method-docs] to capitalize the title properly.
## 2. Check if each sentence ends with a period
- You can use [string methods][endswith-method-docs] to check the ending of a string.
## 3. Clean up spacing
- You can use [string methods][strip-method-docs] to remove whitespace.
## 4. Replace words with a synonym
- You can use [string methods][replace-method-docs] to replace words.
[endswith-method-docs]: https://docs.python.org/3/library/stdtypes.html#str.endswith
[replace-method-docs]: https://docs.python.org/3/library/stdtypes.html#str.replace
[string-method-docs]: https://docs.python.org/3/library/stdtypes.html#string-methods
[strip-method-docs]: https://docs.python.org/3/library/stdtypes.html#str.strip
[title-method-docs]: https://docs.python.org/3/library/stdtypes.html#str.title
[tutorial-strings]: https://docs.python.org/3/tutorial/introduction.html#text

View file

@ -0,0 +1,194 @@
# Little Sister's Essay
Welcome to Little Sister's Essay on Exercism's Python Track.
If you need help running the tests or submitting your code, check out `HELP.md`.
If you get stuck on the exercise, check out `HINTS.md`, but try and solve it without using those first :)
## Introduction
The `str` class offers [many useful methods][str methods] for working with and composing strings.
These include searching, cleaning, splitting, transforming, translating, and many other techniques.
Strings are [immutable sequences][text sequence] of [Unicode code points][unicode code points] -- individual "characters" or code points (_strings of length 1_) can be referenced by `0-based index` number from the left, or `-1-based index` number from the right.
Strings can be iterated through using `for item in <str>` or `for index, item in enumerate(<str>)` syntax.
They can be concatenated using the `+` operator or via `<string>.join(<iterable>)` and implement all [common sequence operations][common sequence operations].
Strings are _immutable_, meaning the value of a `str` object in memory cannot change.
Functions or methods that operate on a `str` (_like the ones we are learning about here_) will return a new `instance` of that `str` object instead of modifying the original `str`.
Following is a small selection of Python string methods.
For a complete list, see the [str class][str methods] in the Python docs.
[`<str>.title()`][str-title] parses a string and capitalizes the first "character" of each "word" found.
In Python, this is very dependent on the [language codec][codecs] used and how the particular language represents words and characters.
There may also be [locale][locale] rules in place for a language or character set.
```python
man_in_hat_th = 'ผู้ชายใส่หมวก'
man_in_hat_ru = 'мужчина в шляпе'
man_in_hat_ko = '모자를 쓴 남자'
man_in_hat_en = 'the man in the hat.'
>>> man_in_hat_th.title()
'ผู้ชายใส่หมวก'
>>> man_in_hat_ru.title()
'Мужчина В Шляпе'
>>> man_in_hat_ko.title()
'모자를 쓴 남자'
>> man_in_hat_en.title()
'The Man In The Hat.'
```
[`<str>.endswith(<suffix>)`][str-endswith] returns `True` if the string ends with `<suffix>`, `False` otherwise.
```python
>>> 'My heart breaks. 💔'.endswith('💔')
True
>>> 'cheerfulness'.endswith('ness')
True
# Punctuation is part of the string, so needs to be included in any endswith match.
>>> 'Do you want to 💃?'.endswith('💃')
False
>> 'The quick brown fox jumped over the lazy dog.'.endswith('dog')
False
```
[`<str>.strip(<chars>)`][str-strip] returns a copy of the `str` with leading and trailing `<chars>` removed.
The code points specified in `<chars>` are not a prefix or suffix - **all combinations** of the code points will be removed starting from **both ends** of the string.
If nothing is specified for `<chars>`, all combinations of whitespace code points will be removed.
```python
# This will remove "https://", because it can be formed from "/stph:".
>>> 'https://unicode.org/emoji/'.strip('/stph:')
'unicode.org/emoji'
# Removal of all whitespace from both ends of the str.
>>> ' 🐪🐪🐪🌟🐪🐪🐪 '.strip()
'🐪🐪🐪🌟🐪🐪🐪'
>>> justification = 'оправдание'
>>> justification.strip('еина')
'оправд'
# Prefix and suffix in one step.
>>> 'unaddressed'.strip('dnue')
'address'
>>> ' unaddressed '.strip('dnue ')
'address'
```
[`<str>.replace(<substring>, <replacement substring>)`][str-replace] returns a copy of the string with all occurrences of `<substring>` replaced with `<replacement substring>`.
The quote used below is from [The Hunting of the Snark][The Hunting of the Snark] by [Lewis Carroll][Lewis Carroll]
```python
# The Hunting of the Snark, by Lewis Carroll
>>> quote = '''
"Just the place for a Snark!" the Bellman cried,
As he landed his crew with care;
Supporting each man on the top of the tide
By a finger entwined in his hair.
"Just the place for a Snark! I have said it twice:
That alone should encourage the crew.
Just the place for a Snark! I have said it thrice:
What I tell you three times is true."
'''
>>> quote.replace('Snark', '🐲')
...
'\n"Just the place for a 🐲!" the Bellman cried,\n As he landed his crew with care;\nSupporting each man on the top of the tide\n By a finger entwined in his hair.\n\n"Just the place for a 🐲! I have said it twice:\n That alone should encourage the crew.\nJust the place for a 🐲! I have said it thrice:\n What I tell you three times is true."\n'
>>> 'bookkeeper'.replace('kk', 'k k')
'book keeper'
```
[Lewis Carroll]: https://www.poetryfoundation.org/poets/lewis-carroll
[The Hunting of the Snark]: https://www.poetryfoundation.org/poems/43909/the-hunting-of-the-snark
[codecs]: https://docs.python.org/3/library/codecs.html
[common sequence operations]: https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str
[locale]: https://docs.python.org/3/library/locale.html#module-locale
[str methods]: https://docs.python.org/3/library/stdtypes.html#string-methods
[str-endswith]: https://docs.python.org/3/library/stdtypes.html#str.endswith
[str-replace]: https://docs.python.org/3/library/stdtypes.html#str.replace
[str-strip]: https://docs.python.org/3/library/stdtypes.html#str.strip
[str-title]: https://docs.python.org/3/library/stdtypes.html#str.title
[text sequence]: https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str
[unicode code points]: https://stackoverflow.com/questions/27331819/whats-the-difference-between-a-character-a-code-point-a-glyph-and-a-grapheme
## Instructions
In this exercise you are helping your younger sister edit her paper for school. The teacher is looking for correct punctuation, grammar, and excellent word choice.
You have four tasks to clean up and modify strings.
## 1. Capitalize the title of the paper
Any good paper needs a properly formatted title.
Implement the function `capitalize_title(<title>)` which takes a title `str` as a parameter and capitalizes the first letter of each word.
This function should return a `str` in title case.
```python
>>> capitalize_title("my hobbies")
"My Hobbies"
```
## 2. Check if each sentence ends with a period
You want to make sure that the punctuation in the paper is perfect.
Implement the function `check_sentence_ending()` that takes `sentence` as a parameter. This function should return a `bool`.
```python
>>> check_sentence_ending("I like to hike, bake, and read.")
True
```
## 3. Clean up spacing
To make the paper look professional, unnecessary spacing needs to be removed.
Implement the function `clean_up_spacing()` that takes `sentence` as a parameter.
The function should remove extra whitespace at both the beginning and the end of the sentence, returning a new, updated sentence `str`.
```python
>>> clean_up_spacing(" I like to go on hikes with my dog. ")
"I like to go on hikes with my dog."
```
## 4. Replace words with a synonym
To make the paper _even better_, you can replace some of the adjectives with their synonyms.
Write the function `replace_word_choice()` that takes `sentence`, `old_word`, and `new_word` as parameters.
This function should replace all instances of the `old_word` with the `new_word`, and return a new `str` with the updated sentence.
```python
>>> replace_word_choice("I bake good cakes.", "good", "amazing")
"I bake amazing cakes."
```
## Source
### Created by
- @kimolivia
### Contributed to by
- @valentin-p
- @BethanyG

View file

@ -0,0 +1,43 @@
"""Functions to help edit essay homework using string manipulation."""
def capitalize_title(title):
"""Convert the first letter of each word in the title to uppercase if needed.
:param title: str - title string that needs title casing.
:return: str - title string in title case (first letters capitalized).
"""
return title.title()
def check_sentence_ending(sentence):
"""Check the ending of the sentence to verify that a period is present.
:param sentence: str - a sentence to check.
:return: bool - return True if punctuated correctly with period, False otherwise.
"""
return sentence.endswith(".")
def clean_up_spacing(sentence):
"""Verify that there isn't any whitespace at the start and end of the sentence.
:param sentence: str - a sentence to clean of leading and trailing space characters.
:return: str - a sentence that has been cleaned of leading and trailing space characters.
"""
return sentence.strip()
def replace_word_choice(sentence, old_word, new_word):
"""Replace a word in the provided sentence with a new one.
:param sentence: str - a sentence to replace words in.
:param old_word: str - word to replace.
:param new_word: str - replacement word.
:return: str - input sentence with new words in place of old words.
"""
return sentence.replace(old_word, new_word)

View file

@ -0,0 +1,98 @@
import unittest
import pytest
from string_methods import (capitalize_title,
check_sentence_ending,
clean_up_spacing,
replace_word_choice)
class LittleSistersEssayTest(unittest.TestCase):
@pytest.mark.task(taskno=1)
def test_capitalize_word(self):
actual_result = capitalize_title("canopy")
expected = "Canopy"
error_message = (f'Called capitalize_title("canopy"). '
f'The function returned "{actual_result}", '
f'but the tests expected "{expected}" for the title.')
self.assertEqual(actual_result, expected, msg=error_message)
@pytest.mark.task(taskno=1)
def test_capitalize_title(self):
actual_result = capitalize_title("fish are cold blooded")
expected = "Fish Are Cold Blooded"
error_message = (f'Called capitalize_title("fish are cold blooded"). '
f'The function returned "{actual_result}", '
f'but the tests expected "{expected}" for the title.')
self.assertEqual(actual_result, expected, msg=error_message)
@pytest.mark.task(taskno=2)
def test_sentence_ending(self):
actual_result = check_sentence_ending("Snails can sleep for 3 years.")
expected = True
error_message = (f'Called check_sentence_ending("Snails can sleep for 3 years."). '
f'The function returned {actual_result}, '
f'but the tests expected {expected} for a period ending.')
self.assertEqual(actual_result, expected, msg=error_message)
@pytest.mark.task(taskno=2)
def test_sentence_ending_without_period(self):
actual_result = check_sentence_ending("Fittonia are nice")
expected = False
error_message = (f'Called check_sentence_ending("Fittonia are nice"). '
f'The function returned {actual_result}, '
f'but the tests expected {expected} for a period ending.')
self.assertEqual(actual_result, expected, msg=error_message)
@pytest.mark.task(taskno=3)
def test_remove_extra_spaces_only_start(self):
actual_result = clean_up_spacing(" A rolling stone gathers no moss")
expected = "A rolling stone gathers no moss"
error_message = (f'Called clean_up_spacing(" A rolling stone gathers no moss"). '
f'The function returned "{actual_result}", '
f'but the tests expected "{expected}" as a cleaned string.')
self.assertEqual(actual_result, expected, msg=error_message)
@pytest.mark.task(taskno=3)
def test_remove_extra_spaces(self):
actual_result = clean_up_spacing(" Elephants can't jump. ")
expected = "Elephants can't jump."
error_message = ("Called clean_up_spacing(\" Elephants can't jump. \")"
f'The function returned "{actual_result}", '
f'but the tests expected "{expected}" as a cleaned string.')
self.assertEqual(actual_result, expected, msg=error_message)
@pytest.mark.task(taskno=4)
def test_replace_word_choice(self):
actual_result = replace_word_choice("Animals are cool.", "cool", "awesome")
expected = "Animals are awesome."
error_message = ('Called replace_word_choice("Animals are cool.", "cool", "awesome"). '
f'The function returned "{actual_result}", '
f'but the tests expected "{expected}" after the word replacement.')
self.assertEqual(actual_result, expected, msg=error_message)
@pytest.mark.task(taskno=4)
def test_replace_word_not_exist(self):
actual_result = replace_word_choice("Animals are cool.", "small", "tiny")
expected = "Animals are cool."
error_message = ('Called replace_word_choice("Animals are cool.", "small", "tiny"). '
f'The function returned "{actual_result}", '
f'but the tests expected "{expected}", because the word '
'to be replaced is not in the sentence.')
self.assertEqual(actual_result, expected, msg=error_message)