primeiro commit com exercicios de Exercism realizados até esta data

This commit is contained in:
2026-10-17 22:10:27 +01:00
commit 58fccbe27a
708 changed files with 35038 additions and 0 deletions
@@ -0,0 +1,24 @@
{
"authors": [
"PaulT89",
"BethanyG"
],
"contributors": [
"DjangoFett",
"kotp",
"IsaacG"
],
"files": {
"solution": [
"classes.py"
],
"test": [
"classes_test.py"
],
"exemplar": [
".meta/exemplar.py"
]
},
"icon": "character-study",
"blurb": "Learn about classes by creating an Alien for Ellen's game."
}
@@ -0,0 +1 @@
{"track":"python","exercise":"ellens-alien-game","id":"93900682dcdf4ccd8e1eb9ff17859ab7","url":"https://exercism.org/tracks/python/exercises/ellens-alien-game","handle":"sanctushck","is_requester":true,"auto_approve":false}
+130
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 classes.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.
+42
View File
@@ -0,0 +1,42 @@
# Hints
## 1. Create the Alien Class
- Remember that `object methods` are always passed `self` as the first parameter.
- Remember the double underscores on _both_ sides of `__init__()`.
- Instance variables are unique to the `class` instance (_object_) that possesses them.
- Class variables are the same across all instances of a `class`.
## 2. The `hit` Method
- Remember that `object methods` are always passed `self` as the first parameter.
- You can choose to allow the Alien's health to fall below zero, or require that it does not.
## 3. The `is_alive` Method
- Remember that `object methods` are always passed `self` as the first parameter.
- 0 may not be the only 'dead' condition, depending on how `hit()` is implemented.
## 4. The `teleport` Method
- Remember that `object methods` are always passed `self` as the first parameter.
- Instance attributes can be updated from a method by using `self.<attribute>` = `<new attribute value>`.
## 5. The `collision_detection` Method
- Remember that `object methods` are always passed `self` as the first parameter.
- This method seems like an excellent place to use some kind of placeholder…
## 6. Alien Counter
- Class attributes are the same across all instances of a `class`.
- Ideally, this counter would increment whenever someone _made an new Alien_.
- Class attributes can be changed from an instance method by using the _class name_: `Alien.<class attribute name>`.
- `__init__()` is considered an instance method since it _initializes a new object_.
## 7. Object Creation
- A `tuple` would be a _single_ parameter.
- The Alien constructor takes _2 parameters_.
- Unpacking what is _inside_ the tuple would yield two parameters.
- The standalone function is outside of the `class`
+415
View File
@@ -0,0 +1,415 @@
# Ellen's Alien Game
Welcome to Ellen's Alien Game 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
## Object Oriented Programming in Python
If you have been programming in a [functional][functional], [declarative][declarative], or [imperative][imperative] style, shifting focus to [object oriented programming][oop] (OOP) may feel a bit foreign.
An OOP approach asks the programmer to think about modeling a problem as one or more `objects` that interact with one another, keep state, and act upon data.
Objects can represent real world entities (_such as cars or cats_) - or more abstract concepts (_such as integers, vehicles, or mammals_).
Each object becomes a unique instance in computer memory and represents some part of the overall model.
## Classes
`Classes` are the definitions of new object types, and from which new `instances` of objects are created.
They often bundle data with code or functions that operate on that data.
In this sense, classes are _blueprints_ or sets of instructions from which many objects of a similar type can be built and used.
A complex program can have many classes, each building many different flavors of objects.
The process of building an object from a class is known as `instantiation` (_or creating an instance of the class_).
A class definition in Python is straightforward:
```python
class MyClass:
# Class body goes here
```
### Class Attributes
Class fields (_otherwise known as `properties`, `attributes`, `data members`, or `variables`_) can be added to the body of the class:
```python
class MyClass:
number = 5
string = "Hello!"
```
An instance (_object_) of `MyClass` can be created and bound to a name by [_calling_][calling] the class (_in the same way a function is called_):
```python
>>> new_object = MyClass()
# Class is instantiated and resulting object is bound to the "new_object" name.
# Note: the object address 'at 0x15adc55b0' will vary by computer.
>>> new_object
<__main__.MyClass at 0x15adc55b0>
```
`Class attributes` are shared across all objects (_or instances_) created from a class, and can be accessed via [`dot notation`][dot notation] - a `.` placed after the object name and before the attribute name:
```python
>>> new_object = MyClass()
# Accessing the class attribute "number" via dot-notation.
>>> new_object.number
5
# Accessing the class attribute "string" via dot-notation.
>>> new_object.string
'Hello!'
# Instantiating an additional object and binding it to the "second_new_object" name.
>>> second_new_object = MyClass()
>>> second_new_object
# Note: the object address "at 0x15ad99970" will vary by computer.
<__main__.MyClass at 0x15ad99970>
# Second_new_object shares the same class attributes as new_object.
>>> new_object.number == second_new_object.number
True
```
Class attributes are defined in the body of the class itself, before any other methods.
They are owned by the class - allowing them to be shared across instances of the class.
Because these attributes are shared, their value can be accessed and manipulated from the class _directly_.
Altering the value of class attributes alters the value _**for all objects instantiated from the class**_:
```python
>>> obj_one = MyClass()
>>> obj_two = MyClass()
# Accessing a class attribute from an object.
>>> obj_two.number
5
# Accessing the class attribute from the class itself.
>>> MyClass.number
5
# Modifying the value of the "number" class attribute.
>>> MyClass.number = 27
# Modifying the "number" class attribute changes the "number" attribute for all objects.
>>> obj_one.number
27
>>> obj_two.number
27
```
Having a bunch of objects with synchronized data at all times is not particularly useful.
Fortunately, objects created from a class can be customized with their own `instance attributes` (_or instance properties, variables, or fields_).
As their name suggests, instance attributes are unique to each object, and can be modified independently.
## Customizing Object Instantiation with `__init__()`
The special ["dunder method"][dunder] (_short for "double underscore method"_) `__init__()` is used to customize class instances, and can be used to initialize instance attributes or properties for objects.
For its role in initializing instance attributes, `__init__()` is also referred to as a `class constructor` or `initializer`.
`__init__()` takes one required parameter called `self`, which refers to the newly initialized or created object.
Data for instance attributes or properties can then be passed as arguments of `__init__()`, following the `self` parameter.
Below, `MyClass` now has instance attributes called `location_x` and `location_y`.
As you can see, the two attributes have been assigned to the first and second indexes of the `location` (_a tuple_) argument that has been passed to `__init__()`.
The `location_x` and `location_y` attributes for a class instance will now be initialized when you instantiate the class, and an object is created:
```python
class MyClass:
# These are class attributes, variables, or fields.
number = 5
string = "Hello!"
# This is the class "constructor", with a "location" parameter that is a tuple.
def __init__(self, location):
# This is an instance or object property, attribute, or variable.
# Note that we are unpacking the tuple argument into two separate instance variables.
self.location_x = location[0]
self.location_y = location[1]
# Create a new object "new_object_one", with object property (1, 2).
>>> new_object_one = MyClass((1, 2))
# Create a second new object "new_object_two" with object property (-8, -9).
>>> new_object_two = MyClass((-8, -9))
# Note that new_object_one.location_x and new_object_two.location_x two different values.
>>> new_object_one.location_x
1
>>> new_object_two.location_x
-8
```
Note that you only need to pass one argument when initializing `MyClass` above -- Python takes care of passing `self` when the class is called.
## Methods
A `method` is a `function` that is bound to either the class itself (_known as a [class method][class method], which will be discussed in a later exercise_) or an _instance_ of the class (object).
Methods that operate on an object (instance) need to be defined with `self` as the first parameter.
You can then define the rest of the parameters as you would for a "normal" or non-bound function:
```python
class MyClass:
number = 5
string = "Hello!"
# Class constructor.
def __init__(self, location):
# Instance properties
self.location_x = location[0]
self.location_y = location[1]
# Instance method. Note "self" as first parameter.
def change_location(self, amount):
self.location_x += amount
self.location_y += amount
return self.location_x, self.location_y
```
Like attribute access, calling a method simply requires putting a `.` after the object name, and before the method name.
The called method does not need a copy of the object as a first parameter -- Python fills in `self` automatically:
```python
class MyClass:
number = 5
string = "Hello!"
def __init__(self, location):
self.location_x = location[0]
self.location_y = location[1]
def change_location(self, amount):
self.location_x += amount
self.location_y += amount
return self.location_x, self.location_y
# Make a new test_object with location (3,7)
>>> test_object = MyClass((3,7))
>>> (test_object.location_x, test_object.location_y)
(3,7)
# Call change_location to increase location_x and location_y by 7.
>>> test_object.change_location(7)
(10, 14)
```
Class attributes can be accessed from within instance methods in the same way that they are accessed outside of the class:
```python
class MyClass:
number = 5
string = "Hello!"
def __init__(self, location):
self.location_x = location[0]
self.location_y = location[1]
# Alter instance variable location_x and location_y
def change_location(self, amount):
self.location_x += amount
self.location_y += amount
return self.location_x, self.location_y
# Alter class variable number for all instances from within an instance.
def increment_number(self):
# Increment the 'number' class variable by 1.
MyClass.number += 1
>>> test_object_one = MyClass((0,0))
>>> test_object_one.number
5
>>> test_object_two = MyClass((13, -3))
>>> test_object_two.increment_number()
>>> test_object_one.number
6
```
## Placeholding or Stubbing Implementation with Pass
In previous concept exercises and practice exercise stubs, you will have seen the `pass` keyword used within the body of functions in place of actual code.
The `pass` keyword is a syntactically valid placeholder - it prevents Python from throwing a syntax error for an empty function or class definition.
Essentially, it is a way to say to the Python interpreter, 'Don't worry! I _will_ put code here eventually, I just haven't done it yet.'
```python
class MyClass:
number = 5
string = "Hello!"
def __init__(self, location):
self.location_x = location[0]
self.location_y = location[1]
# Alter instance variable location_x and location_y
def change_location(self, amount):
self.location_x += amount
self.location_y += amount
return self.location_x, self.location_y
# Alter class variable number for all instances
def increment_number(self):
# Increment the 'number' class variable by 1.
MyClass.number += 1
# This will compile and run without error, but has no current functionality.
def pending_functionality(self):
# Stubbing or placholding the body of this method.
pass
```
[calling]: https://www.pythonmorsels.com/topics/calling-a-function
[class method]: https://stackoverflow.com/questions/17134653/difference-between-class-and-instance-methods
[dunder]: https://mathspp.com/blog/pydonts/dunder-methods
[imperative]: https://en.wikipedia.org/wiki/Imperative_programming
[declarative]: https://en.wikipedia.org/wiki/Declarative_programming
[oop]: https://www.digitalocean.com/community/tutorials/how-to-construct-classes-and-define-objects-in-python-3
[functional]: https://en.wikipedia.org/wiki/Functional_programming
[dot notation]: https://stackoverflow.com/questions/45179186/understanding-the-dot-notation-in-python
## Instructions
Ellen is making a game where the player has to fight aliens.
She has just learned about Object Oriented Programming (OOP) and is eager to take advantage of what using `classes` could offer her program.
To Ellen's delight, you have offered to help and she has given you the task of programming the aliens that the player has to fight.
## 1. Create the Alien Class
Define the Alien class with a constructor that accepts two parameters `<x_coordinate>` and `<y_coordinate>`, putting them into `x_coordinate` and `y_coordinate` instance variables.
Every alien will also start off with a health level of 3, so the `health` variable should be initialized as well.
```python
>>> alien = Alien(2, 0)
>>> alien.x_coordinate
2
>>> alien.y_coordinate
0
>>> alien.health
3
```
Now, each alien should be able to internally track its own position and health.
## 2. The `hit` Method
Ellen would like the Alien `class` to have a `hit` method that decrements the health of an alien object by 1 when called.
This way, she can simply call `<alien>.hit()` instead of having to manually change an alien's health.
It is up to you if `hit()` takes healths points _to_ or _below_ zero.
```python
>>> alien = Alien(0, 0)
>>> alien.health
# Initialized health value.
3
# Decrements health by 1 point.
>>> alien.hit()
>>> alien.health
2
```
## 3. The `is_alive` Method
You realize that if the health keeps decreasing, at some point it will probably hit 0 (_or even less!_).
It would be a good idea to add an `is_alive` method that Ellen can quickly call to check if the alien is... well... alive. 😉
`<alien>.is_alive()` should return a boolean.
```python
>>> alien.health
1
>>> alien.is_alive()
True
>>> alien.hit()
>>> alien.health
0
>>> alien.is_alive()
False
```
## 4. The `teleport` Method
In Ellen's game, the aliens have the ability to teleport!
You will need to write a `teleport` method that takes new `x_coordinate` and `y_coordinate` values, and changes the alien's coordinates accordingly.
```python
>>> alien.teleport(5, -4)
>>> alien.x_coordinate
5
>>> alien.y_coordinate
-4
```
## 5. The `collision_detection` Method
Obviously, if the aliens can be hit by something, then they need to be able to detect when such a collision has occurred.
However, collision detection algorithms can be tricky, and you do not yet know how to implement one.
Ellen has said that she will do it later, but she would still like the `collision_detection` method to appear in the class as a reminder to build out the functionality.
It will need to take a variable of some kind (probably another object), but that's really all you know.
You will need to make sure that putting the method definition into the class doesn't cause any errors when called:
```python
>>> alien.collision_detection(other_object)
>>>
```
## 6. Alien Counter
Ellen has come back with a new request for you.
She wants to keep track of how many aliens have been created over the game's lifetime.
She says that it's got something to do with the scoring system.
For example:
```python
>>> alien_one = Alien(5, 1)
>>> alien_one.total_aliens_created
1
>>> alien_two = Alien(3, 0)
>>> alien_two.total_aliens_created
2
>>> alien_one.total_aliens_created
2
>>> Alien.total_aliens_created
# Accessing the variable from the class directly
2
```
## 7. Creating a List of Aliens
Ellen loves what you've done so far, but she has one more favor to ask.
She would like a standalone (_outside the `Alien()` class_) function that creates a `list` of `Alien()` objects, given a list of positions (as `tuples`).
For example:
```python
>>> alien_start_positions = [(4, 7), (-1, 0)]
>>> aliens = new_aliens_collection(alien_start_positions)
...
>>> for alien in aliens:
print(alien.x_coordinate, alien.y_coordinate)
(4, 7)
(-1, 0)
```
## Source
### Created by
- @PaulT89
- @BethanyG
### Contributed to by
- @DjangoFett
- @kotp
- @IsaacG
+54
View File
@@ -0,0 +1,54 @@
"""Solution to Ellen's Alien Game exercise."""
class Alien:
"""Create an Alien object with location x_coordinate and y_coordinate.
Attributes
----------
(class)total_aliens_created: int
x_coordinate: int - Position on the x-axis.
y_coordinate: int - Position on the y-axis.
health: int - Number of health points.
Methods
-------
hit(): Decrement Alien health by one point.
is_alive(): Return a boolean for if Alien is alive (if health is > 0).
teleport(new_x_coordinate, new_y_coordinate): Move Alien object to new coordinates.
collision_detection(other): Implementation TBD.
"""
total_aliens_created: int = 0
x_coordinate: int
y_coordinate: int
health: int
def __init__(self, x_coordinate: int, y_coordinate: int) -> None:
self.x_coordinate = x_coordinate
self.y_coordinate = y_coordinate
self.health = 3
Alien.total_aliens_created += 1
def hit(self):
self.health -= 1
def is_alive(self) -> bool:
return self.health > 0
def teleport(self, new_x_coordinate: int, new_y_coordinate: int):
self.x_coordinate = new_x_coordinate
self.y_coordinate = new_y_coordinate
def collision_detection(self, other):
pass
pass
# TODO: create the new_aliens_collection() function below to call your Alien class with a list of coordinates.
def new_aliens_collection(list_positions: list[tuple[int, int]]) -> list[Alien]:
list_of_aliens: list[Alien] = []
for alien_position_x, alien_position_y in list_positions:
list_of_aliens.append(Alien(alien_position_x, alien_position_y))
return list_of_aliens
+212
View File
@@ -0,0 +1,212 @@
import unittest
import pytest
try:
from classes import Alien
except ImportError as import_fail:
# pylint: disable=raise-missing-from
raise ImportError("\n\nMISSING CLASS --> We tried to import the 'Alien' class from "
"your classes.py file, but could not find it."
"Did you misname or forget to create it?") from None
try:
from classes import new_aliens_collection
except ImportError as err:
raise ImportError("\n\nMISSING FUNCTION --> We tried to import the "
"new_aliens_collection() function "
"from your classes.py file, but could not find it. "
"Did you misname or forget to create it?") from None
class ClassesTest(unittest.TestCase):
@pytest.mark.task(taskno=1)
def test_alien_has_correct_initial_coordinates(self):
"""Test that the Alien class gets correctly initialised."""
alien = Alien(2, -1)
error_message = (f'Created a new Alien by calling Alien(2, -1). '
f'The Alien was initialized to position '
f'{(alien.x_coordinate, alien.y_coordinate)}, but the tests expected '
f'the object to be at position (2, -1)')
self.assertEqual((2, -1), (alien.x_coordinate, alien.y_coordinate), msg=error_message)
@pytest.mark.task(taskno=1)
def test_alien_has_health(self):
alien = Alien(0, 0)
error_message = (f'Created a new Alien by calling Alien(0, 0). '
f'The new Alien has a health of {alien.health}, '
f'but the tests expect health = 3')
self.assertEqual(3, alien.health, msg=error_message)
@pytest.mark.task(taskno=1)
def test_alien_instance_variables(self):
"""Test instance variables are unique to specific instances."""
alien_one = Alien(-8, -1)
alien_two = Alien(2, 5)
coord_x_error = (f'Created two new Aliens by assigning '
f'alien_one = Alien(-8, -1) and alien_two = Alien(2, 5). '
f'Both Aliens x coordinates were {alien_two.x_coordinate}, '
f'but the tests expect alien_one and alien_two to have '
f'different x positions.')
coord_y_error = (f'Created two new Aliens by assigning '
f'alien_one = Alien(-8, -1) and alien_two = Alien(2, 5). '
f'Both Aliens y coordinates were {alien_two.y_coordinate}, '
f'but the tests expect alien_one and alien_two to have '
f'different y positions.')
self.assertFalse(alien_one.x_coordinate == alien_two.x_coordinate, msg=coord_x_error)
self.assertFalse(alien_one.y_coordinate == alien_two.y_coordinate, msg=coord_y_error)
@pytest.mark.task(taskno=2)
def test_alien_hit_method(self):
"""Test class methods work as specified.
There are two valid interpretations for this method/task.
`self.health -= 1` and `self.health = max(0, self.health - 1)`
The tests for this task reflect this ambiguity.
"""
test_data = [1, 2, 3, 4, 5, 6]
result_data = [(2,), (1,), (0,), (0, -1), (0, -2), (0, -3)]
for variant, (iterations, expected) in enumerate(zip(test_data, result_data), start=1):
alien = Alien(2, 2)
with self.subTest(f'variation #{variant}',
iterations=iterations,
expected=expected):
for _ in range(iterations):
alien.hit()
error_message = (f'Called hit() {iterations} time(s) '
f'on a newly created Alien. The Aliens health '
f'is now {alien.health}, but the tests expected '
f'it to be in {expected} after decrementing 1 health '
f'point {iterations} time(s).')
self.assertIn(alien.health, expected, msg=error_message)
@pytest.mark.task(taskno=3)
def test_alien_is_alive_method(self):
alien = Alien(0, 1)
alive_error = ('Created a new Alien and called hit(). '
'The function is_alive() is returning False (dead) '
'while alien.health is greater than 0.')
dead_error = ('Created a new Alien and called hit(). '
'The function is_alive() is returning True (alive) '
'while alien.health is less than or equal to 0.')
for _ in range(5):
alien.hit()
if alien.health > 0:
self.assertTrue(alien.is_alive(), msg=alive_error)
else:
self.assertFalse(alien.is_alive(), msg=dead_error)
@pytest.mark.task(taskno=4)
def test_alien_teleport_method(self):
alien = Alien(0, 0)
alien.teleport(-1, -4)
error_message = ('Called alien.teleport(-1,-4) on a newly created Alien. '
'The Alien was found at position '
f'{(alien.x_coordinate, alien.y_coordinate)}, but the '
'tests expected it at position (-1, -4).')
self.assertEqual((-1, -4), (alien.x_coordinate, alien.y_coordinate), msg=error_message)
@pytest.mark.task(taskno=5)
def test_alien_collision_detection_method(self):
alien = Alien(7, 3)
error_message = ('Created a new Alien at (7,3) and called '
'alien.collision_detection(Alien(7, 2)). '
f'The method returned {alien.collision_detection(Alien(7, 2))}, '
'but the tests expected None. ')
self.assertIsNone(alien.collision_detection(Alien(7, 2)), msg=error_message)
@pytest.mark.task(taskno=6)
def test_alien_class_variable(self):
"""Test class attribute/variables are identical across instances."""
alien_one, alien_two = Alien(0, 2), Alien(-6, -1)
Alien.health = 6
created_error_message = ('Created two new Aliens and requested the '
'total_aliens_created attribute for each one. '
f'Received {alien_one.total_aliens_created, alien_two.total_aliens_created} '
f'for total_aliens_created, but the tests expect '
f'the class attributes for each newly created Alien to be identical. ')
health_error_message = ('Created two new Aliens and requested the '
f'health attribute for each one. Received {alien_one.health, alien_two.health} '
'for health, but the tests expect the class '
'attributes for each newly created Alien to be identical. ')
self.assertEqual(alien_two.total_aliens_created,
alien_one.total_aliens_created,
msg=created_error_message)
self.assertEqual(alien_two.health,
alien_one.health,
msg=health_error_message)
@pytest.mark.task(taskno=6)
def test_alien_total_aliens_created(self):
"""Test total_aliens_created class variable increments upon object instantiation."""
Alien.total_aliens_created = 0
aliens = [Alien(-2, 6)]
error_message = ('Created a new Alien and called total_aliens_created for it. '
f'{aliens[0].total_aliens_created} was returned, but '
'the tests expected that total_aliens_created would equal 1.')
self.assertEqual(1, aliens[0].total_aliens_created, msg=error_message)
aliens.append(Alien(3, 5))
aliens.append(Alien(-5, -5))
def error_text(alien, variable):
return ('Created two additional Aliens for the session.'
f"Alien number {alien}'s total_aliens_created variable "
f"is equal to {variable}, but the tests expected all "
'total_aliens_created variables for all instances to be '
'equal to number of alien instances created (i.e. 3).')
self.assertEqual(3, aliens[0].total_aliens_created, msg=error_text(1, aliens[0]))
self.assertEqual(3, aliens[1].total_aliens_created, msg=error_text(2, aliens[1]))
self.assertEqual(3, aliens[2].total_aliens_created, msg=error_text(3, aliens[2]))
@pytest.mark.task(taskno=7)
def test_new_aliens_collection(self):
"""Test that the user knows how to create objects themselves."""
test_data = [(-2, 6), (1, 5), (-4, -3)]
actual_result = new_aliens_collection(test_data)
error_message = "new_aliens_collection() must return a list of Alien objects."
for obj in actual_result:
self.assertIsInstance(obj, Alien, msg=error_message)
for position, obj in zip(test_data, actual_result):
position_error = (f'After calling new_aliens_collection({test_data}), '
f'found {obj} initialized to position {(obj.x_coordinate, obj.y_coordinate)}, '
f'but the tests expected {obj} to be at position {position} instead.')
self.assertEqual(position, (obj.x_coordinate, obj.y_coordinate), msg=position_error)