Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Card Games]: Modified Test Error Messages & Touched Up Docs #3528

Merged
merged 1 commit into from
Nov 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 13 additions & 9 deletions concepts/lists/about.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Accessing elements, checking for membership via `in`, or appending items to the
For a similar data structure that supports memory efficient `appends`/`pops` from both sides, see [`collections.deque`][deque], which has approximately the same O(1) performance in either direction.


Because lists are mutable and can contain references to arbitrary objects, they also take up more space in memory than a fixed-size [`array.array`][array.array] type of the same apparent length.
Because lists are mutable and can contain references to arbitrary Python objects, they also take up more space in memory than an [`array.array`][array.array] or a [`tuple`][tuple] (_which is immutable_) of the same apparent length.
Despite this, lists are an extremely flexible and useful data structure and many built-in methods and operations in Python produce lists as their output.


Expand Down Expand Up @@ -135,7 +135,8 @@ TypeError: 'int' object is not iterable

## Accessing elements

Items inside lists (_as well as elements in other sequence types such as [`str`][string] & [`tuple`][tuple]_), can be accessed using _bracket notation_. Indexes can be from **`left`** --> **`right`** (_starting at zero_) or **`right`** --> **`left`** (_starting at -1_).
Items inside lists (_as well as elements in other sequence types such as [`str`][string] & [`tuple`][tuple]_), can be accessed using _bracket notation_.
Indexes can be from **`left`** --> **`right`** (_starting at zero_) or **`right`** --> **`left`** (_starting at -1_).


<table>
Expand Down Expand Up @@ -173,9 +174,11 @@ Items inside lists (_as well as elements in other sequence types such as [`str`]
'Toast'
```

A section of 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_][slice notation] returns a copy of the "sliced" items and does not modify the original `list`.
A section of 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_][slice notation] returns a copy of the "sliced" items and does not modify the original `list`.

A `step` parameter can also be used in the slice (`[start:stop:step]`) to "skip over" or filter the returned elements (_for example, a `step` of 2 will select every other element in the section_):
A `step` parameter can also be used in the slice (`<list>[<start>:<stop>:<step>]`) to "skip over" or filter the returned elements (_for example, a `step` of 2 will select every other element in the section_):

```python
>>> colors = ["Red", "Purple", "Green", "Yellow", "Orange", "Pink", "Blue", "Grey"]
Expand Down Expand Up @@ -269,7 +272,7 @@ Lists can also be combined via various techniques:
>>> first_one
['George', 5, 'cat', 'Tabby']

# This loops through the first list and appends it's items to the end of the second list.
# This loops through the first list and appends its items to the end of the second list.
>>> first_one = ["cat", "Tabby"]
>>> second_one = ["George", 5]

Expand All @@ -284,7 +287,7 @@ Lists can also be combined via various techniques:
## Some cautions

Recall that variables in Python are _labels_ that point to _underlying objects_.
`lists` add one more layer as _container objects_ -- they hold object references for their collected items.
`lists` add one more layer as _container objects_ -- they hold object _references_ for their collected items.
This can lead to multiple potential issues when working with lists, if not handled properly.


Expand All @@ -305,21 +308,22 @@ A `shallow_copy` will create a new `list` object, but **will not** create new ob

# Altering the list via the new name is the same as altering the list via the old name.
>>> same_list.append("Clarke")
>>> same_list
["Tony", "Natasha", "Thor", "Bruce", "Clarke"]

>>> actual_names
["Tony", "Natasha", "Thor", "Bruce", "Clarke"]

# Likewise, altering the data in the list via the original name will also alter the data under the new name.
>>> actual_names[0] = "Wanda"
>>> same_list
['Wanda', 'Natasha', 'Thor', 'Bruce', 'Clarke']

# If you copy the list, there will be two separate list objects which can be changed independently.
>>> copied_list = actual_names.copy()
>>> copied_list[0] = "Tony"

>>> actual_names
['Wanda', 'Natasha', 'Thor', 'Bruce', 'Clarke']

>>> copied_list
["Tony", "Natasha", "Thor", "Bruce", "Clarke"]
```
Expand Down Expand Up @@ -455,4 +459,4 @@ The collections module also provides a `UserList` type that can be customized to
[set]: https://docs.python.org/3/library/stdtypes.html#set
[slice notation]: https://docs.python.org/3/reference/expressions.html#slicings
[string]: https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str
[tuple]: https://docs.python.org/3/library/stdtypes.html#tuple
[tuple]: https://docs.python.org/3/library/stdtypes.html#tuple
21 changes: 10 additions & 11 deletions exercises/concept/card-games/.docs/hints.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,45 +4,44 @@

## 1. Tracking Poker Rounds

- Lists in Python may be [constructed][constructed] in several ways.
- 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` already support [common operations][common sequence operations].
- 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` already support a few [common operations][common sequence operations].
- 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 sum/count.
- 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` already 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.
- In Python, negative indexing starts the count from the right-hand side. This mean that you can find the last element of a `list` at `index -1`.
- 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.
- 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.
- 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
[iterate over a list in python]: https://www.geeksforgeeks.org/iterate-over-a-list-in-python/
[return]: https://www.w3schools.com/python/ref_keyword_return.asp
[ways to modify `lists`]: https://realpython.com/python-lists-tuples/#lists-are-mutable
2 changes: 1 addition & 1 deletion exercises/concept/card-games/.docs/introduction.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

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-base index` from the right.
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()`.
Expand Down
124 changes: 77 additions & 47 deletions exercises/concept/card-games/lists_test.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import unittest
import pytest

from lists import (
get_rounds,
concatenate_rounds,
Expand All @@ -16,92 +17,121 @@ class CardGamesTest(unittest.TestCase):
@pytest.mark.task(taskno=1)
def test_get_rounds(self):

input_vars = [0, 1, 10, 27, 99, 666]
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]]

results = [[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}.')

for variant, (number, rounds) in enumerate(zip(input_vars, results), start=1):
error_message = f'Expected rounds {rounds} given the current round {number}.'
with self.subTest(f'variation #{variant}', input=number, output=rounds):
self.assertEqual(rounds, get_rounds(number), msg=error_message)
self.assertEqual(actual_result, expected, msg=error_message)

@pytest.mark.task(taskno=2)
def test_concatenate_rounds(self):

input_vars = [([], []), ([0, 1], []), ([], [1, 2]),
input_data = [([], []), ([0, 1], []), ([], [1, 2]),
([1], [2]), ([27, 28, 29], [35, 36]),
([1, 2, 3], [4, 5, 6])]

results = [[], [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}.')

for variant, ((rounds_1, rounds_2), rounds) in enumerate(zip(input_vars, results), start=1):
error_message = f'Expected {rounds} as the concatenation of {rounds_1} and {rounds_2}.'
with self.subTest(f'variation #{variant}', input=(rounds_1, rounds_2), output=rounds):
self.assertEqual(rounds, concatenate_rounds(rounds_1, rounds_2), msg=error_message)
self.assertEqual(actual_result, expected, msg=error_message)

@pytest.mark.task(taskno=3)
def test_list_contains_round(self):

input_vars = [([], 1), ([1, 2, 3], 0), ([27, 28, 29, 35, 36], 30),
([1], 1), ([1, 2, 3], 1), ([27, 28, 29, 35, 36], 29)]
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]

results = [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}.')

for variant, ((rounds, round_number), contains) in enumerate(zip(input_vars, results), start=1):
error_message = f'Round {round_number} {"is" if contains else "is not"} in {rounds}.'
with self.subTest(f'variation #{variant}', input=(rounds, round_number), output=contains):
self.assertEqual(contains, list_contains_round(rounds, round_number), msg=error_message)
self.assertEqual(actual_result, expected, msg=error_message)

@pytest.mark.task(taskno=4)
def test_card_average(self):

input_vars = [[1], [5, 6, 7], [1, 2, 3, 4], [1, 10, 100]]
input_data = [[1], [5, 6, 7], [1, 2, 3, 4], [1, 10, 100]]
result_data = [1.0, 6.0, 2.5, 37.0]

results = [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}.')

for variant, (hand, average) in enumerate(zip(input_vars, results), start=1):
error_message = f'Expected {average} as the average of {hand}.'
with self.subTest(f'variation #{variant}', input=hand, output=average):
self.assertEqual(average, card_average(hand), msg=error_message)
self.assertEqual(actual_result, expected, msg=error_message)

@pytest.mark.task(taskno=5)
def test_approx_average_is_average(self):

input_vars = [[0, 1, 5], [3, 6, 9, 12, 150], [1, 2, 3, 5, 9],
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]]

results = [False, False, False, False, True, True, True, True]
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.')

for variant, (hand, same) in enumerate(zip(input_vars, results), start=1):
error_message = f'Hand {hand} {"does" if same else "does not"} yield the same approximate average.'
with self.subTest(f'variation #{variant}', input=hand, output=same):
self.assertEqual(same, approx_average_is_average(hand), msg=error_message)
self.assertEqual(actual_result, expected, msg=error_message)

@pytest.mark.task(taskno=6)
def test_average_even_is_average_odd(self):

input_vars = [[5, 6, 8], [1, 2, 3, 4], [1, 2, 3], [5, 6, 7], [1, 3, 5, 7, 9]]
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]

results = [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.')

for variant, (hand, same) in enumerate(zip(input_vars, results), start=1):
error_message = f'Hand {hand} {"does" if same else "does not"} yield the same odd-even average.'
with self.subTest(f'variation #{variant}', input=hand, output=same):
self.assertEqual(same, average_even_is_average_odd(hand), msg=error_message)
self.assertEqual(actual_result, expected, msg=error_message)

@pytest.mark.task(taskno=7)
def test_maybe_double_last(self):

input_vars = [[1, 2, 11], [5, 9, 11], [5, 9, 10], [1, 2, 3]]
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]]

results = [[1, 2, 22], [5, 9, 22], [5, 9, 10], [1, 2, 3]]
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)}.')

for variant, (hand, doubled_hand) in enumerate(zip(input_vars, results), start=1):
error_message = f'Expected {doubled_hand} as the maybe-doubled version of {hand}.'
with self.subTest(f'variation #{variant}', input=hand, output=doubled_hand):
self.assertEqual(doubled_hand, maybe_double_last(hand), msg=error_message)
self.assertEqual(actual_result, expected, msg=error_message)
Loading