mirror of
https://codeberg.org/andyscott/exercism.git
synced 2024-12-21 22:33:11 -05:00
Compare commits
2 commits
11966b4315
...
bbd7c562bd
Author | SHA1 | Date | |
---|---|---|---|
bbd7c562bd | |||
d336d146db |
14 changed files with 1349 additions and 0 deletions
24
python/card-games/.exercism/config.json
Normal file
24
python/card-games/.exercism/config.json
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
{
|
||||||
|
"authors": [
|
||||||
|
"itamargal",
|
||||||
|
"isaacg",
|
||||||
|
"bethanyg"
|
||||||
|
],
|
||||||
|
"contributors": [
|
||||||
|
"valentin-p",
|
||||||
|
"pranasziaukas"
|
||||||
|
],
|
||||||
|
"files": {
|
||||||
|
"solution": [
|
||||||
|
"lists.py"
|
||||||
|
],
|
||||||
|
"test": [
|
||||||
|
"lists_test.py"
|
||||||
|
],
|
||||||
|
"exemplar": [
|
||||||
|
".meta/exemplar.py"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"icon": "poker",
|
||||||
|
"blurb": "Learn about lists by tracking hands in card games."
|
||||||
|
}
|
1
python/card-games/.exercism/metadata.json
Normal file
1
python/card-games/.exercism/metadata.json
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{"track":"python","exercise":"card-games","id":"47aa83e6f3eb45219f2ef38dc238d4e2","url":"https://exercism.org/tracks/python/exercises/card-games","handle":"Chomp1295","is_requester":true,"auto_approve":false}
|
130
python/card-games/HELP.md
Normal file
130
python/card-games/HELP.md
Normal 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 lists.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.
|
47
python/card-games/HINTS.md
Normal file
47
python/card-games/HINTS.md
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
# Hints
|
||||||
|
|
||||||
|
## General
|
||||||
|
|
||||||
|
## 1. Tracking Poker Rounds
|
||||||
|
|
||||||
|
- Lists in Python may be [constructed][constructed] in multiple ways.
|
||||||
|
- This function should [return][return] a `list`.
|
||||||
|
|
||||||
|
## 2. Keeping all Rounds in the Same Place
|
||||||
|
|
||||||
|
- Sequence types such as `list` support [common operations][common sequence operations].
|
||||||
|
- This function should [return][return] a `list`.
|
||||||
|
|
||||||
|
## 3. Finding Prior Rounds
|
||||||
|
|
||||||
|
- Sequence types such as `list` support a few [common operations][common sequence operations].
|
||||||
|
- This function should [return][return] a `bool`.
|
||||||
|
|
||||||
|
## 4. Averaging Card Values
|
||||||
|
|
||||||
|
- To get the average, this function should count how many items are in the `list` and sum up their values. Then, return the sum divided by the count.
|
||||||
|
|
||||||
|
## 5. Alternate Averages
|
||||||
|
|
||||||
|
- Sequence types such as `list` support a few [common operations][common sequence operations].
|
||||||
|
- To access an element, use the square brackets (`<list>[]`) notation.
|
||||||
|
- Remember that the first element of the `list` is at index 0 from the **left-hand** side.
|
||||||
|
- In Python, negative indexing starts at -1 from the **right-hand** side. This means that you can find the last element of a `list` by using `<list>[-1]`.
|
||||||
|
- Think about how you could reuse the code from the functions that you have already implemented.
|
||||||
|
|
||||||
|
## 6. More Averaging Techniques
|
||||||
|
|
||||||
|
- Sequence types such as `list` already support a few [common operations][common sequence operations].
|
||||||
|
- Think about reusing the code from the functions that you just implemented.
|
||||||
|
- The slice syntax supports a _step value_ (`<list>[<start>:<stop>:<step>]`).
|
||||||
|
|
||||||
|
## 7. Bonus Round Rules
|
||||||
|
|
||||||
|
- Lists are _mutable_. Once a `list` is created, you can modify, delete or add any type of element you wish.
|
||||||
|
- Python provides a wide range of [ways to modify `lists`][ways to modify `lists`].
|
||||||
|
|
||||||
|
|
||||||
|
[common sequence operations]: https://docs.python.org/3/library/stdtypes.html#sequence-types-list-tuple-range
|
||||||
|
[constructed]: https://docs.python.org/3/library/stdtypes.html#list
|
||||||
|
[return]: https://www.w3schools.com/python/ref_keyword_return.asp
|
||||||
|
[ways to modify `lists`]: https://realpython.com/python-lists-tuples/#lists-are-mutable
|
393
python/card-games/README.md
Normal file
393
python/card-games/README.md
Normal file
|
@ -0,0 +1,393 @@
|
||||||
|
# Card Games
|
||||||
|
|
||||||
|
Welcome to Card Games 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
|
||||||
|
|
||||||
|
A [`list`][list] is a mutable collection of items in _sequence_.
|
||||||
|
Like most collections (_see the built-ins [`tuple`][tuple], [`dict`][dict] and [`set`][set]_), lists can hold reference to any (or multiple) data type(s) - including other lists.
|
||||||
|
Like any [sequence][sequence type], items can be accessed via `0-based index` number from the left and `-1-based index` from the right.
|
||||||
|
Lists can be copied in whole or in part via [slice notation][slice notation] or `<list>.copy()`.
|
||||||
|
|
||||||
|
Lists support both [common][common sequence operations] and [mutable][mutable sequence operations] sequence operations such as `min()`/`max()`, `<list>.index()`, `<list>.append()` and `<list>.reverse()`.
|
||||||
|
List elements can be iterated over using the `for item in <list>` construct.
|
||||||
|
`for index, item in enumerate(<list>)` can be used when both the element index and the element value are needed.
|
||||||
|
|
||||||
|
Under the hood, `lists` are implemented as [dynamic arrays][dynamic array] -- similar to Java's [`ArrayList`][arraylist] type, and are most often used to store groups of similar data (_strings, numbers, sets etc._) of unknown length.
|
||||||
|
Lists are an extremely flexible and useful data structure and many built-in methods and operations in Python produce lists as their output.
|
||||||
|
|
||||||
|
|
||||||
|
## Construction
|
||||||
|
|
||||||
|
A `list` can be declared as a _literal_ with square `[]` brackets and commas between elements:
|
||||||
|
|
||||||
|
|
||||||
|
```python
|
||||||
|
>>> no_elements = []
|
||||||
|
|
||||||
|
>>> no_elements
|
||||||
|
[]
|
||||||
|
|
||||||
|
>>> one_element = ["Guava"]
|
||||||
|
|
||||||
|
>>> one_element
|
||||||
|
['Guava']
|
||||||
|
|
||||||
|
>>> elements_separated_with_commas = ["Parrot", "Bird", 334782]
|
||||||
|
|
||||||
|
>>> elements_separated_with_commas
|
||||||
|
['Parrot', 'Bird', 334782]
|
||||||
|
```
|
||||||
|
|
||||||
|
For readability, line breaks can be used when there are many elements or nested data structures within a `list`:
|
||||||
|
|
||||||
|
|
||||||
|
```python
|
||||||
|
>>> lots_of_entries = [
|
||||||
|
"Rose",
|
||||||
|
"Sunflower",
|
||||||
|
"Poppy",
|
||||||
|
"Pansy",
|
||||||
|
"Tulip",
|
||||||
|
"Fuchsia",
|
||||||
|
"Cyclamen",
|
||||||
|
"Lavender"
|
||||||
|
]
|
||||||
|
|
||||||
|
>>> lots_of_entries
|
||||||
|
['Rose', 'Sunflower', 'Poppy', 'Pansy', 'Tulip', 'Fuchsia', 'Cyclamen', 'Lavender']
|
||||||
|
|
||||||
|
# Each data structure is on its own line to help clarify what they are.
|
||||||
|
>>> nested_data_structures = [
|
||||||
|
{"fish": "gold", "monkey": "brown", "parrot": "grey"},
|
||||||
|
("fish", "mammal", "bird"),
|
||||||
|
['water', 'jungle', 'sky']
|
||||||
|
]
|
||||||
|
|
||||||
|
>>> nested_data_structures
|
||||||
|
[{'fish': 'gold', 'monkey': 'brown', 'parrot': 'grey'}, ('fish', 'mammal', 'bird'), ['water', 'jungle', 'sky']]
|
||||||
|
```
|
||||||
|
|
||||||
|
The `list()` constructor can be used empty or with an _iterable_ as an argument.
|
||||||
|
Elements in the iterable are cycled through by the constructor and added to the `list` in order:
|
||||||
|
|
||||||
|
|
||||||
|
```python
|
||||||
|
>>> no_elements = list()
|
||||||
|
|
||||||
|
>>> no_elements
|
||||||
|
[]
|
||||||
|
|
||||||
|
# The tuple is unpacked and each element is added.
|
||||||
|
>>> multiple_elements_from_tuple = list(("Parrot", "Bird", 334782))
|
||||||
|
|
||||||
|
>>> multiple_elements_from_tuple
|
||||||
|
['Parrot', 'Bird', 334782]
|
||||||
|
|
||||||
|
# The set is unpacked and each element is added.
|
||||||
|
>>> multiple_elements_from_set = list({2, 3, 5, 7, 11})
|
||||||
|
|
||||||
|
>>> multiple_elements_from_set
|
||||||
|
[2, 3, 5, 7, 11]
|
||||||
|
```
|
||||||
|
|
||||||
|
Results when using a `list` constructor with a `string` or a `dict` may be surprising:
|
||||||
|
|
||||||
|
|
||||||
|
```python
|
||||||
|
# String elements (Unicode code points) are iterated through and added *individually*.
|
||||||
|
>>> multiple_elements_string = list("Timbuktu")
|
||||||
|
|
||||||
|
>>> multiple_elements_string
|
||||||
|
['T', 'i', 'm', 'b', 'u', 'k', 't', 'u']
|
||||||
|
|
||||||
|
# Unicode separators and positioning code points are also added *individually*.
|
||||||
|
>>> multiple_code_points_string = list('अभ्यास')
|
||||||
|
|
||||||
|
>>> multiple_code_points_string
|
||||||
|
['अ', 'भ', '्', 'य', 'ा', 'स']
|
||||||
|
|
||||||
|
# The iteration default for dictionaries is over the keys, so only key data is inserted into the list.
|
||||||
|
>>> source_data = {"fish": "gold", "monkey": "brown"}
|
||||||
|
|
||||||
|
>>> multiple_elements_dict_1 = list(source_data)
|
||||||
|
['fish', 'monkey']
|
||||||
|
```
|
||||||
|
|
||||||
|
Because the `list` constructor will only take _iterables_ (or nothing) as arguments, objects that are _not_ iterable will throw a type error.
|
||||||
|
Consequently, it is much easier to create a one-item `list` via the literal method.
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Numbers are not iterable, and so attempting to create a list with a number passed to the constructor fails.
|
||||||
|
>>> one_element = list(16)
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "<stdin>", line 1, in <module>
|
||||||
|
TypeError: 'int' object is not iterable
|
||||||
|
|
||||||
|
# Tuples *are* iterable, so passing a one-element tuple to the constructor does work, but it's awkward
|
||||||
|
>>> one_element_from_iterable = list((16,))
|
||||||
|
|
||||||
|
>>> one_element_from_iterable
|
||||||
|
[16]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Accessing elements
|
||||||
|
|
||||||
|
Items inside lists (_as well as items in other sequence types `str` & `tuple`_) can be accessed via `0-based index` and _bracket notation_.
|
||||||
|
Indexes can be from **`left`** --> **`right`** (_starting at zero_) or **`right`** --> **`left`** (_starting at -1_).
|
||||||
|
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td style="vertical-align: top"> index from left ⟹<br><br><br><br><br><br><br></td><td style="vertical-align: middle">
|
||||||
|
|
||||||
|
| 0<br>👇🏾 | 1<br>👇🏾 | 2<br>👇🏾 | 3<br>👇🏾 | 4<br>👇🏾 | 5<br>👇🏾 |
|
||||||
|
|:--------: |:--------: |:--------: |:--------: |:--------: |:--------: |
|
||||||
|
| P | y | t | h | o | n |
|
||||||
|
| 👆🏾<br>-6 | 👆🏾<br>-5 | 👆🏾<br>-4 | 👆🏾<br>-3 | 👆🏾<br>-2 | 👆🏾<br>-1 |
|
||||||
|
</td><td style="vertical-align: bottom"><br><br><br><br><br>⟸ index from right</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
|
||||||
|
```python
|
||||||
|
>>> breakfast_foods = ["Oatmeal", "Fruit Salad", "Eggs", "Toast"]
|
||||||
|
|
||||||
|
# Oatmeal is at index 0 or index -4.
|
||||||
|
>>> breakfast_foods[0]
|
||||||
|
'Oatmeal'
|
||||||
|
|
||||||
|
>>> breakfast_foods[-4]
|
||||||
|
'Oatmeal'
|
||||||
|
|
||||||
|
# Eggs are at index -2 or 2
|
||||||
|
>>> breakfast_foods[-2]
|
||||||
|
'Eggs'
|
||||||
|
|
||||||
|
>>> breakfast_foods[2]
|
||||||
|
'Eggs'
|
||||||
|
|
||||||
|
# Toast is at -1
|
||||||
|
>>> breakfast_foods[-1]
|
||||||
|
'Toast'
|
||||||
|
```
|
||||||
|
|
||||||
|
A section of the elements inside a `list` can be accessed via _slice notation_ (`<list>[start:stop]`).
|
||||||
|
A _slice_ is defined as an element sequence at position `index`, such that `start <= index < stop`.
|
||||||
|
_Slicing_ returns a copy of the "sliced" items and does not modify the original `list`.
|
||||||
|
|
||||||
|
|
||||||
|
A `step` parameter can also be used `[start:stop:step]` to "skip over" or filter the `list` elements (_for example, a `step` of 2 will select every other element in the range_):
|
||||||
|
|
||||||
|
|
||||||
|
```python
|
||||||
|
>>> colors = ["Red", "Purple", "Green", "Yellow", "Orange", "Pink", "Blue", "Grey"]
|
||||||
|
|
||||||
|
# If there is no step parameter, the step is assumed to be 1.
|
||||||
|
>>> middle_colors = colors[2:6]
|
||||||
|
|
||||||
|
>>> middle_colors
|
||||||
|
['Green', 'Yellow', 'Orange', 'Pink']
|
||||||
|
|
||||||
|
# If the start or stop parameters are omitted, the slice will
|
||||||
|
# start at index zero, and will stop at the end of the list.
|
||||||
|
>>> primary_colors = colors[::3]
|
||||||
|
|
||||||
|
>>> primary_colors
|
||||||
|
['Red', 'Yellow', 'Blue']
|
||||||
|
```
|
||||||
|
|
||||||
|
## Working with lists
|
||||||
|
|
||||||
|
The usage of the built-in `sum()` function on a list will return the sum of all the numbers in the list:
|
||||||
|
|
||||||
|
```python
|
||||||
|
>>> number_list = [1, 2, 3, 4]
|
||||||
|
>>> sum(number_list)
|
||||||
|
10
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also get the _length_ of a list by using the `len()` function:
|
||||||
|
|
||||||
|
```python
|
||||||
|
>>> long_list = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J"]
|
||||||
|
>>> len(long_list)
|
||||||
|
10
|
||||||
|
```
|
||||||
|
|
||||||
|
Lists can be also combined in various ways:
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Using the plus + operator unpacks each list and creates a new list, but it is not efficient.
|
||||||
|
>>> new_via_concatenate = ["George", 5] + ["cat", "Tabby"]
|
||||||
|
|
||||||
|
>>> new_via_concatenate
|
||||||
|
['George', 5, 'cat', 'Tabby']
|
||||||
|
|
||||||
|
# Likewise, using the multiplication operator * is the equivalent of using + n times.
|
||||||
|
>>> first_group = ["cat", "dog", "elephant"]
|
||||||
|
>>> multiplied_group = first_group * 3
|
||||||
|
|
||||||
|
>>> multiplied_group
|
||||||
|
['cat', 'dog', 'elephant', 'cat', 'dog', 'elephant', 'cat', 'dog', 'elephant']
|
||||||
|
```
|
||||||
|
|
||||||
|
Lists supply an _iterator_, and can be looped through/over in the same manner as other _sequence types_.
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Looping through the list and printing out each element.
|
||||||
|
>>> colors = ["Orange", "Green", "Grey", "Blue"]
|
||||||
|
|
||||||
|
>>> for item in colors:
|
||||||
|
... print(item)
|
||||||
|
...
|
||||||
|
Orange
|
||||||
|
Green
|
||||||
|
Grey
|
||||||
|
Blue
|
||||||
|
```
|
||||||
|
|
||||||
|
_For a more in-depth explanation, of `loops` and `iterators`, complete the `loops` concept._
|
||||||
|
|
||||||
|
[arraylist]: https://beginnersbook.com/2013/12/java-arraylist/
|
||||||
|
[common sequence operations]: https://docs.python.org/3/library/stdtypes.html#common-sequence-operations
|
||||||
|
[dict]: https://docs.python.org/3/library/stdtypes.html#dict
|
||||||
|
[dynamic array]: https://en.wikipedia.org/wiki/Dynamic_array
|
||||||
|
[list]: https://docs.python.org/3/library/stdtypes.html#list
|
||||||
|
[mutable sequence operations]: https://docs.python.org/3/library/stdtypes.html#typesseq-mutable
|
||||||
|
[sequence type]: https://docs.python.org/3/library/stdtypes.html#sequence-types-list-tuple-range
|
||||||
|
[set]: https://docs.python.org/3/library/stdtypes.html#set
|
||||||
|
[slice notation]: https://docs.python.org/3/reference/expressions.html#slicings
|
||||||
|
[tuple]: https://docs.python.org/3/library/stdtypes.html#tuple
|
||||||
|
|
||||||
|
## Instructions
|
||||||
|
|
||||||
|
Elyse is really looking forward to playing some poker (and other card games) during her upcoming trip to Vegas.
|
||||||
|
Being a big fan of "self-tracking" she wants to put together some small functions that will help her with tracking tasks and has asked for your help thinking them through.
|
||||||
|
|
||||||
|
## 1. Tracking Poker Rounds
|
||||||
|
|
||||||
|
Elyse is especially fond of poker, and wants to track how many rounds she plays - and _which rounds_ those are.
|
||||||
|
Every round has its own number, and every table shows the round number currently being played.
|
||||||
|
Elyse chooses a table and sits down to play her first round. She plans on playing three rounds.
|
||||||
|
|
||||||
|
Implement a function `get_rounds(<round_number>)` that takes the current round number and returns a single `list` with that round and the _next two_ that are coming up:
|
||||||
|
|
||||||
|
```python
|
||||||
|
>>> get_rounds(27)
|
||||||
|
[27, 28, 29]
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. Keeping all Rounds in the Same Place
|
||||||
|
|
||||||
|
Elyse played a few rounds at the first table, then took a break and played some more rounds at a second table ... but ended up with a different list for each table!
|
||||||
|
She wants to put the two lists together, so she can track all of the poker rounds in the same place.
|
||||||
|
|
||||||
|
Implement a function `concatenate_rounds(<rounds_1>, <rounds_2>)` that takes two lists and returns a single `list` consisting of all the rounds in the first `list`, followed by all the rounds in the second `list`:
|
||||||
|
|
||||||
|
```python
|
||||||
|
>>> concatenate_rounds([27, 28, 29], [35, 36])
|
||||||
|
[27, 28, 29, 35, 36]
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3. Finding Prior Rounds
|
||||||
|
|
||||||
|
Talking about some of the prior Poker rounds, another player remarks how similarly two of them played out.
|
||||||
|
Elyse is not sure if she played those rounds or not.
|
||||||
|
|
||||||
|
Implement a function `list_contains_round(<rounds>, <round_number>)` that takes two arguments, a list of rounds played and a round number.
|
||||||
|
The function will return `True` if the round is in the list of rounds played, `False` if not:
|
||||||
|
|
||||||
|
```python
|
||||||
|
>>> list_contains_round([27, 28, 29, 35, 36], 29)
|
||||||
|
True
|
||||||
|
|
||||||
|
>>> list_contains_round([27, 28, 29, 35, 36], 30)
|
||||||
|
False
|
||||||
|
```
|
||||||
|
|
||||||
|
## 4. Averaging Card Values
|
||||||
|
|
||||||
|
Elyse wants to try out a new game called Black Joe.
|
||||||
|
It's similar to Black Jack - where your goal is to have the cards in your hand add up to a target value - but in Black Joe the goal is to get the _average_ of the card values to be 7.
|
||||||
|
The average can be found by summing up all the card values and then dividing that sum by the number of cards in the hand.
|
||||||
|
|
||||||
|
Implement a function `card_average(<hand>)` that will return the average value of a hand of Black Joe.
|
||||||
|
|
||||||
|
```python
|
||||||
|
>>> card_average([5, 6, 7])
|
||||||
|
6.0
|
||||||
|
```
|
||||||
|
|
||||||
|
## 5. Alternate Averages
|
||||||
|
|
||||||
|
In Black Joe, speed is important. Elyse is going to try and find a faster way of finding the average.
|
||||||
|
|
||||||
|
She has thought of two ways of getting an _average-like_ number:
|
||||||
|
|
||||||
|
- Take the average of the _first_ and _last_ number in the hand.
|
||||||
|
- Using the median (middle card) of the hand.
|
||||||
|
|
||||||
|
Implement the function `approx_average_is_average(<hand>)`, given `hand`, a list containing the values of the cards in your hand.
|
||||||
|
|
||||||
|
Return `True` if either _one_ `or` _both_ of the, above named, strategies result in a number _equal_ to the _actual average_.
|
||||||
|
|
||||||
|
Note: _The length of all hands are odd, to make finding a median easier._
|
||||||
|
|
||||||
|
```python
|
||||||
|
>>> approx_average_is_average([1, 2, 3])
|
||||||
|
True
|
||||||
|
|
||||||
|
>>> approx_average_is_average([2, 3, 4, 8, 8])
|
||||||
|
True
|
||||||
|
|
||||||
|
>>> approx_average_is_average([1, 2, 3, 5, 9])
|
||||||
|
False
|
||||||
|
```
|
||||||
|
|
||||||
|
## 6. More Averaging Techniques
|
||||||
|
|
||||||
|
Intrigued by the results of her averaging experiment, Elyse is wondering if taking the average of the cards at the _even_ positions versus the average of the cards at the _odd_ positions would give the same results.
|
||||||
|
Time for another test function!
|
||||||
|
|
||||||
|
Implement a function `average_even_is_average_odd(<hand>)` that returns a Boolean indicating if the average of the cards at even indexes is the same as the average of the cards at odd indexes.
|
||||||
|
|
||||||
|
```python
|
||||||
|
>>> average_even_is_average_odd([1, 2, 3])
|
||||||
|
True
|
||||||
|
|
||||||
|
>>> average_even_is_average_odd([1, 2, 3, 4])
|
||||||
|
False
|
||||||
|
```
|
||||||
|
|
||||||
|
## 7. Bonus Round Rules
|
||||||
|
|
||||||
|
Every 11th hand in Black Joe is a bonus hand with a bonus rule: if the last card you draw is a Jack, you double its value.
|
||||||
|
|
||||||
|
Implement a function `maybe_double_last(<hand>)` that takes a hand and checks if the last card is a Jack (11).
|
||||||
|
If the last card **is** a Jack (11), double its value before returning the hand.
|
||||||
|
|
||||||
|
```python
|
||||||
|
>>> hand = [5, 9, 11]
|
||||||
|
>>> maybe_double_last(hand)
|
||||||
|
[5, 9, 22]
|
||||||
|
|
||||||
|
>>> hand = [5, 9, 10]
|
||||||
|
>>> maybe_double_last(hand)
|
||||||
|
[5, 9, 10]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Source
|
||||||
|
|
||||||
|
### Created by
|
||||||
|
|
||||||
|
- @itamargal
|
||||||
|
- @isaacg
|
||||||
|
- @bethanyg
|
||||||
|
|
||||||
|
### Contributed to by
|
||||||
|
|
||||||
|
- @valentin-p
|
||||||
|
- @pranasziaukas
|
81
python/card-games/lists.py
Normal file
81
python/card-games/lists.py
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
"""Functions for tracking poker hands and assorted card tasks.
|
||||||
|
|
||||||
|
Python list documentation: https://docs.python.org/3/tutorial/datastructures.html
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def get_rounds(number):
|
||||||
|
"""Create a list containing the current and next two round numbers.
|
||||||
|
|
||||||
|
:param number: int - current round number.
|
||||||
|
:return: list - current round and the two that follow.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return [number, number + 1, number + 2]
|
||||||
|
|
||||||
|
|
||||||
|
def concatenate_rounds(rounds_1, rounds_2):
|
||||||
|
"""Concatenate two lists of round numbers.
|
||||||
|
|
||||||
|
:param rounds_1: list - first rounds played.
|
||||||
|
:param rounds_2: list - second set of rounds played.
|
||||||
|
:return: list - all rounds played.
|
||||||
|
"""
|
||||||
|
|
||||||
|
return rounds_1 + rounds_2
|
||||||
|
|
||||||
|
|
||||||
|
def list_contains_round(rounds, number):
|
||||||
|
"""Check if the list of rounds contains the specified number.
|
||||||
|
|
||||||
|
:param rounds: list - rounds played.
|
||||||
|
:param number: int - round number.
|
||||||
|
:return: bool - was the round played?
|
||||||
|
"""
|
||||||
|
return number in rounds
|
||||||
|
|
||||||
|
|
||||||
|
def card_average(hand):
|
||||||
|
"""Calculate and returns the average card value from the list.
|
||||||
|
|
||||||
|
:param hand: list - cards in hand.
|
||||||
|
:return: float - average value of the cards in the hand.
|
||||||
|
"""
|
||||||
|
return sum(hand) / len(hand)
|
||||||
|
|
||||||
|
|
||||||
|
def approx_average_is_average(hand):
|
||||||
|
"""Return if the (average of first and last card values) OR ('middle' card) == calculated average.
|
||||||
|
|
||||||
|
:param hand: list - cards in hand.
|
||||||
|
:return: bool - does one of the approximate averages equal the `true average`?
|
||||||
|
"""
|
||||||
|
|
||||||
|
size = len(hand)
|
||||||
|
actual_avg = card_average(hand)
|
||||||
|
first_last = (hand[0] + hand[size - 1]) / 2
|
||||||
|
median = hand[size // 2]
|
||||||
|
|
||||||
|
return actual_avg in (first_last, median)
|
||||||
|
|
||||||
|
|
||||||
|
def average_even_is_average_odd(hand):
|
||||||
|
"""Return if the (average of even indexed card values) == (average of odd indexed card values).
|
||||||
|
|
||||||
|
:param hand: list - cards in hand.
|
||||||
|
:return: bool - are even and odd averages equal?
|
||||||
|
"""
|
||||||
|
return card_average(hand[::2]) == card_average(hand[1::2])
|
||||||
|
|
||||||
|
|
||||||
|
def maybe_double_last(hand):
|
||||||
|
"""Multiply a Jack card value in the last index position by 2.
|
||||||
|
|
||||||
|
:param hand: list - cards in hand.
|
||||||
|
:return: list - hand with Jacks (if present) value doubled.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if hand[-1] == 11:
|
||||||
|
hand[-1] *= 2
|
||||||
|
|
||||||
|
return hand
|
137
python/card-games/lists_test.py
Normal file
137
python/card-games/lists_test.py
Normal file
|
@ -0,0 +1,137 @@
|
||||||
|
import unittest
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from lists import (
|
||||||
|
get_rounds,
|
||||||
|
concatenate_rounds,
|
||||||
|
list_contains_round,
|
||||||
|
card_average,
|
||||||
|
approx_average_is_average,
|
||||||
|
average_even_is_average_odd,
|
||||||
|
maybe_double_last,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class CardGamesTest(unittest.TestCase):
|
||||||
|
|
||||||
|
@pytest.mark.task(taskno=1)
|
||||||
|
def test_get_rounds(self):
|
||||||
|
|
||||||
|
input_data = [0, 1, 10, 27, 99, 666]
|
||||||
|
result_data = [[0, 1, 2], [1, 2, 3],
|
||||||
|
[10, 11, 12], [27, 28, 29],
|
||||||
|
[99, 100, 101], [666, 667, 668]]
|
||||||
|
|
||||||
|
for variant, (number, expected) in enumerate(zip(input_data, result_data), start=1):
|
||||||
|
with self.subTest(f'variation #{variant}', number=number, expected=expected):
|
||||||
|
actual_result = get_rounds(number)
|
||||||
|
error_message = (f'Called get_rounds({number}). '
|
||||||
|
f'The function returned {actual_result}, '
|
||||||
|
f'but the tests expected rounds {expected} '
|
||||||
|
f'given the current round {number}.')
|
||||||
|
|
||||||
|
self.assertEqual(actual_result, expected, msg=error_message)
|
||||||
|
|
||||||
|
@pytest.mark.task(taskno=2)
|
||||||
|
def test_concatenate_rounds(self):
|
||||||
|
|
||||||
|
input_data = [([], []), ([0, 1], []), ([], [1, 2]),
|
||||||
|
([1], [2]), ([27, 28, 29], [35, 36]),
|
||||||
|
([1, 2, 3], [4, 5, 6])]
|
||||||
|
|
||||||
|
result_data = [[], [0, 1], [1, 2], [1, 2],
|
||||||
|
[27, 28, 29, 35, 36],
|
||||||
|
[1, 2, 3, 4, 5, 6]]
|
||||||
|
|
||||||
|
for variant, ((rounds_1, rounds_2), expected) in enumerate(zip(input_data, result_data), start=1):
|
||||||
|
with self.subTest(f'variation #{variant}', rounds_1=rounds_1, rounds_2=rounds_2, expected=expected):
|
||||||
|
actual_result = concatenate_rounds(rounds_1, rounds_2)
|
||||||
|
error_message = (f'Called concatenate_rounds({rounds_1}, {rounds_2}). '
|
||||||
|
f'The function returned {actual_result}, but the tests '
|
||||||
|
f'expected {expected} as the concatenation '
|
||||||
|
f'of {rounds_1} and {rounds_2}.')
|
||||||
|
|
||||||
|
self.assertEqual(actual_result, expected, msg=error_message)
|
||||||
|
|
||||||
|
@pytest.mark.task(taskno=3)
|
||||||
|
def test_list_contains_round(self):
|
||||||
|
|
||||||
|
input_data = [([], 1), ([1, 2, 3], 0),
|
||||||
|
([27, 28, 29, 35, 36], 30),
|
||||||
|
([1], 1), ([1, 2, 3], 1),
|
||||||
|
([27, 28, 29, 35, 36], 29)]
|
||||||
|
result_data = [False, False, False, True, True, True]
|
||||||
|
|
||||||
|
for variant, ((rounds, round_number), expected) in enumerate(zip(input_data, result_data), start=1):
|
||||||
|
with self.subTest(f'variation #{variant}', rounds=rounds, round_number=round_number, expected=expected):
|
||||||
|
actual_result = list_contains_round(rounds, round_number)
|
||||||
|
error_message = (f'Called list_contains_round({rounds}, {round_number}). '
|
||||||
|
f'The function returned {actual_result}, but round {round_number} '
|
||||||
|
f'{"is" if expected else "is not"} in {rounds}.')
|
||||||
|
|
||||||
|
self.assertEqual(actual_result, expected, msg=error_message)
|
||||||
|
|
||||||
|
@pytest.mark.task(taskno=4)
|
||||||
|
def test_card_average(self):
|
||||||
|
|
||||||
|
input_data = [[1], [5, 6, 7], [1, 2, 3, 4], [1, 10, 100]]
|
||||||
|
result_data = [1.0, 6.0, 2.5, 37.0]
|
||||||
|
|
||||||
|
for variant, (hand, expected) in enumerate(zip(input_data, result_data), start=1):
|
||||||
|
with self.subTest(f'variation #{variant}', hand=hand, expected=expected):
|
||||||
|
actual_result = card_average(hand)
|
||||||
|
error_message = (f'Called card_average({hand}). '
|
||||||
|
f'The function returned {actual_result}, but '
|
||||||
|
f'the tests expected {expected} as the average of {hand}.')
|
||||||
|
|
||||||
|
self.assertEqual(actual_result, expected, msg=error_message)
|
||||||
|
|
||||||
|
@pytest.mark.task(taskno=5)
|
||||||
|
def test_approx_average_is_average(self):
|
||||||
|
|
||||||
|
input_data = [[0, 1, 5], [3, 6, 9, 12, 150], [1, 2, 3, 5, 9],
|
||||||
|
[2, 3, 4, 7, 8], [1, 2, 3], [2, 3, 4],
|
||||||
|
[2, 3, 4, 8, 8], [1, 2, 4, 5, 8]]
|
||||||
|
|
||||||
|
result_data = [False, False, False, False, True, True, True, True]
|
||||||
|
|
||||||
|
for variant, (hand, expected) in enumerate(zip(input_data, result_data), start=1):
|
||||||
|
with self.subTest(f'variation #{variant}', hand=hand, expected=expected):
|
||||||
|
actual_result = approx_average_is_average(hand)
|
||||||
|
error_message = (f'Called approx_average_is_average({hand}). '
|
||||||
|
f'The function returned {actual_result}, but '
|
||||||
|
f'the hand {hand} {"does" if expected else "does not"} '
|
||||||
|
f'yield the same approximate average.')
|
||||||
|
|
||||||
|
self.assertEqual(actual_result, expected, msg=error_message)
|
||||||
|
|
||||||
|
@pytest.mark.task(taskno=6)
|
||||||
|
def test_average_even_is_average_odd(self):
|
||||||
|
|
||||||
|
input_data = [[5, 6, 8], [1, 2, 3, 4], [1, 2, 3], [5, 6, 7], [1, 3, 5, 7, 9]]
|
||||||
|
result_data = [False, False, True, True, True]
|
||||||
|
|
||||||
|
for variant, (input_hand, expected) in enumerate(zip(input_data, result_data), start=1):
|
||||||
|
with self.subTest(f'variation #{variant}', input_hand=input_hand, expected=expected):
|
||||||
|
actual_result = average_even_is_average_odd(input_hand)
|
||||||
|
error_message = (f'Called average_even_is_average_odd({input_hand}). '
|
||||||
|
f'The function returned {actual_result}, but '
|
||||||
|
f'the hand {"does" if expected else "does not"} '
|
||||||
|
f'yield the same odd-even average.')
|
||||||
|
|
||||||
|
self.assertEqual(actual_result, expected, msg=error_message)
|
||||||
|
|
||||||
|
@pytest.mark.task(taskno=7)
|
||||||
|
def test_maybe_double_last(self):
|
||||||
|
|
||||||
|
input_data = [(1, 2, 11), (5, 9, 11), (5, 9, 10), (1, 2, 3), (1, 11, 8)]
|
||||||
|
result_data = [[1, 2, 22], [5, 9, 22], [5, 9, 10], [1, 2, 3], [1, 11, 8]]
|
||||||
|
|
||||||
|
for variant, (hand, expected) in enumerate(zip(input_data, result_data), start=1):
|
||||||
|
with self.subTest(f'variation #{variant}', hand=list(hand), expected=expected):
|
||||||
|
actual_result = maybe_double_last(list(hand))
|
||||||
|
error_message = (f'Called maybe_double_last({list(hand)}). '
|
||||||
|
f'The function returned {actual_result}, but '
|
||||||
|
f'the tests expected {expected} as the maybe-doubled version of {list(hand)}.')
|
||||||
|
|
||||||
|
self.assertEqual(actual_result, expected, msg=error_message)
|
22
python/meltdown-mitigation/.exercism/config.json
Normal file
22
python/meltdown-mitigation/.exercism/config.json
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"authors": [
|
||||||
|
"sachsom95",
|
||||||
|
"BethanyG"
|
||||||
|
],
|
||||||
|
"contributors": [
|
||||||
|
"kbuc"
|
||||||
|
],
|
||||||
|
"files": {
|
||||||
|
"solution": [
|
||||||
|
"conditionals.py"
|
||||||
|
],
|
||||||
|
"test": [
|
||||||
|
"conditionals_test.py"
|
||||||
|
],
|
||||||
|
"exemplar": [
|
||||||
|
".meta/exemplar.py"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"icon": "circular-buffer",
|
||||||
|
"blurb": "Learn about conditionals and avoid a meltdown by developing a simple control system for a Nuclear Reactor."
|
||||||
|
}
|
1
python/meltdown-mitigation/.exercism/metadata.json
Normal file
1
python/meltdown-mitigation/.exercism/metadata.json
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{"track":"python","exercise":"meltdown-mitigation","id":"b1c401666a194150aa374afe91089205","url":"https://exercism.org/tracks/python/exercises/meltdown-mitigation","handle":"Chomp1295","is_requester":true,"auto_approve":false}
|
130
python/meltdown-mitigation/HELP.md
Normal file
130
python/meltdown-mitigation/HELP.md
Normal 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 conditionals.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.
|
50
python/meltdown-mitigation/HINTS.md
Normal file
50
python/meltdown-mitigation/HINTS.md
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
# Hints
|
||||||
|
|
||||||
|
## General
|
||||||
|
|
||||||
|
- The Python Docs on [Control Flow Tools][control flow tools] and the Real Python tutorial on [conditionals][real python conditionals] are great places to start.
|
||||||
|
- The Python Docs on [Boolean Operations][boolean operations] can be a great refresher on `bools`, as can the Real Python tutorial on [booleans][python booleans].
|
||||||
|
- The Python Docs on [Comparisons][comparisons] and [comparisons examples][python comparisons examples] can be a great refresher for comparisons.
|
||||||
|
|
||||||
|
## 1. Check for criticality
|
||||||
|
|
||||||
|
- Comparison operators ([comparisons][comparisons review]) and boolean operations ([concept:python/bools]()) can be combined and used with conditionals.
|
||||||
|
- Conditional expressions must evaluate to `True` or `False`.
|
||||||
|
- `else` can be used for a code block that will execute when all conditional tests return `False`.
|
||||||
|
|
||||||
|
```python
|
||||||
|
>>> item = 'blue'
|
||||||
|
>>> item_2 = 'green'
|
||||||
|
|
||||||
|
>>> if len(item) >= 3 and len(item_2) < 5:
|
||||||
|
print('Both pass the test!')
|
||||||
|
elif len(item) >= 3 or len(item_2) < 5:
|
||||||
|
print('One passes the test!')
|
||||||
|
else:
|
||||||
|
print('None pass the test!')
|
||||||
|
...
|
||||||
|
One passes the test!
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. Determine the Power output range
|
||||||
|
|
||||||
|
- Comparison operators can be combined and used with conditionals.
|
||||||
|
- Any number of `elif` statements can be used as decision "branches".
|
||||||
|
- Each "branch" can have a separate `return`, although it might be considered "bad form" by linting tools.
|
||||||
|
- If the linter complains, consider assigning the output of a branch to a common variable, and then `return`ing that variable.
|
||||||
|
|
||||||
|
## 3. Fail Safe Mechanism
|
||||||
|
|
||||||
|
- Comparison operators can be combined and used with conditionals.
|
||||||
|
- Any number of `elif` statements can be used as decision "branches".
|
||||||
|
- Each "branch" can have a separate `return`, although it might be considered "bad form" by linting tools.
|
||||||
|
- If the linter complains, consider assigning the output of a branch to a common variable, and then `return`ing that variable.
|
||||||
|
|
||||||
|
|
||||||
|
[boolean operations]: https://docs.python.org/3/library/stdtypes.html#boolean-operations-and-or-not
|
||||||
|
[comparisons review]: https://www.learnpython.dev/02-introduction-to-python/090-boolean-logic/20-comparisons/
|
||||||
|
[comparisons]: https://docs.python.org/3/library/stdtypes.html#comparisons
|
||||||
|
[control flow tools]: https://docs.python.org/3/tutorial/controlflow.html
|
||||||
|
[python booleans]: https://realpython.com/python-boolean/
|
||||||
|
[python comparisons examples]: https://www.tutorialspoint.com/python/comparison_operators_example.htm
|
||||||
|
[real python conditionals]: https://realpython.com/python-conditional-statements/
|
172
python/meltdown-mitigation/README.md
Normal file
172
python/meltdown-mitigation/README.md
Normal file
|
@ -0,0 +1,172 @@
|
||||||
|
# Meltdown Mitigation
|
||||||
|
|
||||||
|
Welcome to Meltdown Mitigation 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
|
||||||
|
|
||||||
|
In Python, [`if`][if statement], `elif` (_a contraction of 'else and if'_) and `else` statements are used to [control the flow][control flow tools] of execution and make decisions in a program.
|
||||||
|
Unlike many other programming languages, Python versions 3.9 and below do not offer a formal case-switch statement, instead using multiple `elif` statements to serve a similar purpose.
|
||||||
|
|
||||||
|
Python 3.10 introduces a variant case-switch statement called `structural pattern matching`, which will be covered separately in another concept.
|
||||||
|
|
||||||
|
Conditional statements use expressions that must resolve to `True` or `False` -- either by returning a `bool` type directly, or by evaluating as ["truthy" or "falsy"][truth value testing].
|
||||||
|
|
||||||
|
```python
|
||||||
|
x = 10
|
||||||
|
y = 5
|
||||||
|
|
||||||
|
# The comparison '>' returns the bool 'True',
|
||||||
|
# so the statement is printed.
|
||||||
|
if x > y:
|
||||||
|
print("x is greater than y")
|
||||||
|
...
|
||||||
|
>>> x is greater than y
|
||||||
|
```
|
||||||
|
|
||||||
|
When paired with `if`, an optional `else` code block will execute when the original `if` condition evaluates to `False`:
|
||||||
|
|
||||||
|
```python
|
||||||
|
x = 5
|
||||||
|
y = 10
|
||||||
|
|
||||||
|
# The comparison '>' here returns the bool 'False',
|
||||||
|
# so the 'else' block is executed instead of the 'if' block.
|
||||||
|
if x > y:
|
||||||
|
print("x is greater than y")
|
||||||
|
else:
|
||||||
|
print("y is greater than x")
|
||||||
|
...
|
||||||
|
>>> y is greater than x
|
||||||
|
```
|
||||||
|
|
||||||
|
`elif` allows for multiple evaluations/branches.
|
||||||
|
|
||||||
|
```python
|
||||||
|
x = 5
|
||||||
|
y = 10
|
||||||
|
z = 20
|
||||||
|
|
||||||
|
# The 'elif' statement allows for the checking of more conditions.
|
||||||
|
if x > y:
|
||||||
|
print("x is greater than y and z")
|
||||||
|
elif y > z:
|
||||||
|
print("y is greater than x and z")
|
||||||
|
else:
|
||||||
|
print("z is greater than x and y")
|
||||||
|
...
|
||||||
|
>>> z is greater than x and y
|
||||||
|
```
|
||||||
|
|
||||||
|
[Boolean operations][boolean operations] and [comparisons][comparisons] can be combined with conditionals for more complex testing:
|
||||||
|
|
||||||
|
```python
|
||||||
|
>>> def classic_fizzbuzz(number):
|
||||||
|
if number % 3 == 0 and number % 5 == 0:
|
||||||
|
say = 'FizzBuzz!'
|
||||||
|
elif number % 5 == 0:
|
||||||
|
say = 'Buzz!'
|
||||||
|
elif number % 3 == 0:
|
||||||
|
say = 'Fizz!'
|
||||||
|
else:
|
||||||
|
say = str(number)
|
||||||
|
|
||||||
|
return say
|
||||||
|
|
||||||
|
>>> classic_fizzbuzz(15)
|
||||||
|
'FizzBuzz!'
|
||||||
|
|
||||||
|
>>> classic_fizzbuzz(13)
|
||||||
|
'13'
|
||||||
|
```
|
||||||
|
|
||||||
|
[boolean operations]: https://docs.python.org/3/library/stdtypes.html#boolean-operations-and-or-not
|
||||||
|
[comparisons]: https://docs.python.org/3/library/stdtypes.html#comparisons
|
||||||
|
[control flow tools]: https://docs.python.org/3/tutorial/controlflow.html#more-control-flow-tools
|
||||||
|
[if statement]: https://docs.python.org/3/reference/compound_stmts.html#the-if-statement
|
||||||
|
[truth value testing]: https://docs.python.org/3/library/stdtypes.html#truth-value-testing
|
||||||
|
|
||||||
|
## Instructions
|
||||||
|
|
||||||
|
In this exercise, we'll develop a simple control system for a nuclear reactor.
|
||||||
|
|
||||||
|
For a reactor to produce the power it must be in a state of _criticality_.
|
||||||
|
If the reactor is in a state less than criticality, it can become damaged.
|
||||||
|
If the reactor state goes beyond criticality, it can overload and result in a meltdown.
|
||||||
|
We want to mitigate the chances of meltdown and correctly manage reactor state.
|
||||||
|
|
||||||
|
The following three tasks are all related to writing code for maintaining ideal reactor state.
|
||||||
|
|
||||||
|
## 1. Check for criticality
|
||||||
|
|
||||||
|
The first thing a control system has to do is check if the reactor is balanced in criticality.
|
||||||
|
A reactor is said to be critical if it satisfies the following conditions:
|
||||||
|
|
||||||
|
- The temperature is less than 800 K.
|
||||||
|
- The number of neutrons emitted per second is greater than 500.
|
||||||
|
- The product of temperature and neutrons emitted per second is less than 500000.
|
||||||
|
|
||||||
|
Implement the function `is_criticality_balanced()` that takes `temperature` measured in kelvin and `neutrons_emitted` as parameters, and returns `True` if the criticality conditions are met, `False` if not.
|
||||||
|
|
||||||
|
```python
|
||||||
|
>>> is_criticality_balanced(750, 600)
|
||||||
|
True
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. Determine the Power output range
|
||||||
|
|
||||||
|
Once the reactor has started producing power its efficiency needs to be determined.
|
||||||
|
Efficiency can be grouped into 4 bands:
|
||||||
|
|
||||||
|
1. `green` -> efficiency of 80% or more,
|
||||||
|
2. `orange` -> efficiency of less than 80% but at least 60%,
|
||||||
|
3. `red` -> efficiency below 60%, but still 30% or more,
|
||||||
|
4. `black` -> less than 30% efficient.
|
||||||
|
|
||||||
|
The percentage value can be calculated as `(generated_power/theoretical_max_power)*100`
|
||||||
|
where `generated_power` = `voltage` * `current`.
|
||||||
|
Note that the percentage value is usually not an integer number, so make sure to consider the
|
||||||
|
proper use of the `<` and `<=` comparisons.
|
||||||
|
|
||||||
|
Implement the function `reactor_efficiency(<voltage>, <current>, <theoretical_max_power>)`, with three parameters: `voltage`,
|
||||||
|
`current`, and `theoretical_max_power`.
|
||||||
|
This function should return the efficiency band of the reactor : 'green', 'orange', 'red', or 'black'.
|
||||||
|
|
||||||
|
```python
|
||||||
|
>>> reactor_efficiency(200,50,15000)
|
||||||
|
'orange'
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3. Fail Safe Mechanism
|
||||||
|
|
||||||
|
Your final task involves creating a fail-safe mechanism to avoid overload and meltdown.
|
||||||
|
This mechanism will determine if the reactor is below, at, or above the ideal criticality threshold.
|
||||||
|
Criticality can then be increased, decreased, or stopped by inserting (or removing) control rods into the reactor.
|
||||||
|
|
||||||
|
Implement the function called `fail_safe()`, which takes 3 parameters: `temperature` measured in kelvin,
|
||||||
|
`neutrons_produced_per_second`, and `threshold`, and outputs a status code for the reactor.
|
||||||
|
|
||||||
|
- If `temperature * neutrons_produced_per_second` < 90% of `threshold`, output a status code of 'LOW'
|
||||||
|
indicating that control rods must be removed to produce power.
|
||||||
|
|
||||||
|
- If the value `temperature * neutrons_produced_per_second` is within 10% of the `threshold` (so either 0-10% less than the threshold, at the threshold, or 0-10% greater than the threshold), the reactor is in _criticality_ and the status code of 'NORMAL' should be output, indicating that the reactor is in optimum condition and control rods are in an ideal position.
|
||||||
|
|
||||||
|
- If `temperature * neutrons_produced_per_second` is not in the above-stated ranges, the reactor is
|
||||||
|
going into meltdown and a status code of 'DANGER' must be passed to immediately shut down the reactor.
|
||||||
|
|
||||||
|
```python
|
||||||
|
>>> fail_safe(temperature=1000, neutrons_produced_per_second=30, threshold=5000)
|
||||||
|
'DANGER'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Source
|
||||||
|
|
||||||
|
### Created by
|
||||||
|
|
||||||
|
- @sachsom95
|
||||||
|
- @BethanyG
|
||||||
|
|
||||||
|
### Contributed to by
|
||||||
|
|
||||||
|
- @kbuc
|
79
python/meltdown-mitigation/conditionals.py
Normal file
79
python/meltdown-mitigation/conditionals.py
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
"""Functions to prevent a nuclear meltdown."""
|
||||||
|
|
||||||
|
|
||||||
|
def is_criticality_balanced(temperature, neutrons_emitted):
|
||||||
|
"""Verify criticality is balanced.
|
||||||
|
|
||||||
|
:param temperature: int or float - temperature value in kelvin.
|
||||||
|
:param neutrons_emitted: int or float - number of neutrons emitted per second.
|
||||||
|
:return: bool - is criticality balanced?
|
||||||
|
|
||||||
|
A reactor is said to be critical if it satisfies the following conditions:
|
||||||
|
- The temperature is less than 800 K.
|
||||||
|
- The number of neutrons emitted per second is greater than 500.
|
||||||
|
- The product of temperature and neutrons emitted per second is less than 500000.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if (
|
||||||
|
temperature < 800
|
||||||
|
and neutrons_emitted > 500
|
||||||
|
and temperature * neutrons_emitted < 500000
|
||||||
|
):
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def reactor_efficiency(voltage, current, theoretical_max_power):
|
||||||
|
"""Assess reactor efficiency zone.
|
||||||
|
|
||||||
|
:param voltage: int or float - voltage value.
|
||||||
|
:param current: int or float - current value.
|
||||||
|
:param theoretical_max_power: int or float - power that corresponds to a 100% efficiency.
|
||||||
|
:return: str - one of ('green', 'orange', 'red', or 'black').
|
||||||
|
|
||||||
|
Efficiency can be grouped into 4 bands:
|
||||||
|
|
||||||
|
1. green -> efficiency of 80% or more,
|
||||||
|
2. orange -> efficiency of less than 80% but at least 60%,
|
||||||
|
3. red -> efficiency below 60%, but still 30% or more,
|
||||||
|
4. black -> less than 30% efficient.
|
||||||
|
|
||||||
|
The percentage value is calculated as
|
||||||
|
(generated power/ theoretical max power)*100
|
||||||
|
where generated power = voltage * current
|
||||||
|
"""
|
||||||
|
|
||||||
|
efficiency = ((voltage * current) / theoretical_max_power) * 100
|
||||||
|
|
||||||
|
if efficiency >= 80:
|
||||||
|
return "green"
|
||||||
|
if efficiency >= 60:
|
||||||
|
return "orange"
|
||||||
|
if efficiency >= 30:
|
||||||
|
return "red"
|
||||||
|
|
||||||
|
return "black"
|
||||||
|
|
||||||
|
|
||||||
|
def fail_safe(temperature, neutrons_produced_per_second, threshold):
|
||||||
|
"""Assess and return status code for the reactor.
|
||||||
|
|
||||||
|
:param temperature: int or float - value of the temperature in kelvin.
|
||||||
|
:param neutrons_produced_per_second: int or float - neutron flux.
|
||||||
|
:param threshold: int or float - threshold for category.
|
||||||
|
:return: str - one of ('LOW', 'NORMAL', 'DANGER').
|
||||||
|
|
||||||
|
1. 'LOW' -> `temperature * neutrons per second` < 90% of `threshold`
|
||||||
|
2. 'NORMAL' -> `temperature * neutrons per second` +/- 10% of `threshold`
|
||||||
|
3. 'DANGER' -> `temperature * neutrons per second` is not in the above-stated ranges
|
||||||
|
"""
|
||||||
|
|
||||||
|
if temperature * neutrons_produced_per_second < threshold * 0.9:
|
||||||
|
return "LOW"
|
||||||
|
|
||||||
|
if temperature * neutrons_produced_per_second <= threshold * 1.1:
|
||||||
|
return "NORMAL"
|
||||||
|
|
||||||
|
if temperature * neutrons_produced_per_second > threshold * 1.1:
|
||||||
|
return "DANGER"
|
82
python/meltdown-mitigation/conditionals_test.py
Normal file
82
python/meltdown-mitigation/conditionals_test.py
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
import unittest
|
||||||
|
import pytest
|
||||||
|
from conditionals import (is_criticality_balanced,
|
||||||
|
reactor_efficiency,
|
||||||
|
fail_safe)
|
||||||
|
|
||||||
|
|
||||||
|
class MeltdownMitigationTest(unittest.TestCase):
|
||||||
|
"""Test cases for Meltdown mitigation exercise.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@pytest.mark.task(taskno=1)
|
||||||
|
def test_is_criticality_balanced(self):
|
||||||
|
"""Testing border cases around typical points.
|
||||||
|
|
||||||
|
T, n == (800, 500), (625, 800), (500, 1000), etc.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
test_data = ((750, 650, True), (799, 501, True), (500, 600, True),
|
||||||
|
(1000, 800, False), (800, 500, False), (800, 500.01, False),
|
||||||
|
(799.99, 500, False), (500.01, 999.99, False), (625, 800, False),
|
||||||
|
(625.99, 800, False), (625.01, 799.99, False), (799.99, 500.01, True),
|
||||||
|
(624.99, 799.99, True), (500, 1000, False), (500.01, 1000, False),
|
||||||
|
(499.99, 1000, True))
|
||||||
|
|
||||||
|
for variant, data in enumerate(test_data, start=1):
|
||||||
|
temp, neutrons_emitted, expected = data
|
||||||
|
with self.subTest(f'variation #{variant}', temp=temp, neutrons_emitted=neutrons_emitted, expected=expected):
|
||||||
|
|
||||||
|
# pylint: disable=assignment-from-no-return
|
||||||
|
actual_result = is_criticality_balanced(temp, neutrons_emitted)
|
||||||
|
failure_message = (f'Called is_criticality_balanced({temp}, {neutrons_emitted}). '
|
||||||
|
f' The function returned {actual_result}, '
|
||||||
|
f'but the test expected {expected} as the return value.')
|
||||||
|
|
||||||
|
self.assertEqual(actual_result, expected, failure_message)
|
||||||
|
|
||||||
|
@pytest.mark.task(taskno=2)
|
||||||
|
def test_reactor_efficiency(self):
|
||||||
|
voltage = 10
|
||||||
|
theoretical_max_power = 10000
|
||||||
|
|
||||||
|
# The numbers are chosen so that current == 10 x percentage
|
||||||
|
test_data = ((1000, 'green'), (999, 'green'), (800, 'green'),
|
||||||
|
(799, 'orange'), (700, 'orange'), (600, 'orange'),
|
||||||
|
(599, 'red'), (560, 'red'), (400, 'red'), (300, 'red'),
|
||||||
|
(299, 'black'), (200, 'black'), (0, 'black'))
|
||||||
|
|
||||||
|
for variant, data in enumerate(test_data, start=1):
|
||||||
|
current, expected = data
|
||||||
|
with self.subTest(f'variation #{variant}', voltage=voltage, current=current,
|
||||||
|
theoretical_max_power=theoretical_max_power, expected=expected):
|
||||||
|
|
||||||
|
# pylint: disable=assignment-from-no-return
|
||||||
|
actual_result = reactor_efficiency(voltage, current, theoretical_max_power)
|
||||||
|
failure_message =(f'Called reactor_efficiency({voltage}, {current}, {theoretical_max_power}). '
|
||||||
|
f'The function returned {actual_result}, '
|
||||||
|
f'but the test expected {expected} as the return value.')
|
||||||
|
|
||||||
|
self.assertEqual(actual_result, expected, failure_message)
|
||||||
|
|
||||||
|
@pytest.mark.task(taskno=3)
|
||||||
|
def test_fail_safe(self):
|
||||||
|
temp = 10
|
||||||
|
threshold = 10000
|
||||||
|
test_data = ((399, 'LOW'), (300, 'LOW'), (1, 'LOW'),
|
||||||
|
(0, 'LOW'), (901, 'NORMAL'), (1000, 'NORMAL'),
|
||||||
|
(1099, 'NORMAL'), (899, 'LOW'), (700, 'LOW'),
|
||||||
|
(400, 'LOW'), (1101, 'DANGER'), (1200, 'DANGER'))
|
||||||
|
|
||||||
|
for variant, (neutrons_per_second, expected) in enumerate(test_data, start=1):
|
||||||
|
with self.subTest(f'variation #{variant}', temp=temp, neutrons_per_second=neutrons_per_second,
|
||||||
|
threshold=threshold, expected=expected):
|
||||||
|
|
||||||
|
# pylint: disable=assignment-from-no-return
|
||||||
|
actual_result = fail_safe(temp, neutrons_per_second, threshold)
|
||||||
|
failure_message = (f'Called fail_safe({temp}, {neutrons_per_second}, {threshold}). '
|
||||||
|
f'The function returned {actual_result}, '
|
||||||
|
f'but the test expected {expected} as the return value.')
|
||||||
|
|
||||||
|
self.assertEqual(actual_result, expected, failure_message)
|
Loading…
Reference in a new issue