Note: Any FAQs or clarifications relevant to the assignment will be posted here. This post will be continually updated (with newer updates at the bottom of the page), so make sure to check on it regularly – click the “Watch” button in the top-right corner to receive notifications about this thread. If you have a question which is not already addressed on this page, create a new thread to post your question on Ed.
honour_code.txt
file included in the starter files on
MarkUs (more below) which gives an overview of common mistakes students
make in regards to academic integrity.Assignments are meant to evaluate not just the basic knowledge and skills you’ve learned, but also your ability to apply your learning to new domains, and to synthesize multiple concepts and skills to solve more complex problems.
Warning: we don’t recommend trying to complete assignments in one sitting. You’ll want to make sure you take breaks so that you don’t burn yourself out, and often you might get stuck on something and want to return to it in a few days, perhaps after having asked questions on our discussion board or during office hours. Instead you should schedule time to work on the assignment (put it in your calendar!). You’ll typically have around two weeks to work on each assignment. Even if you aren’t ready to start an assignment the day it comes out, you can plan out your next two weeks to find time to work on the assignment.
Assignments are the biggest type of assessments you’ll complete in CSC110, and their scale can feel a bit intimidating at first.
To help you, we typically break assignments down into a few different parts, where each part consists of a number of questions/problems to solve.
As you schedule time to work on the assignment, you might find it useful to set a deadline for each part separately.
For example, for Assignment 1 you might say:
To obtain the starter files for this assignment:
csc110/assignments/
folder.a1
folder that contains the
assignment’s starter files!You can then see these files in your PyCharm csc110
project.
Your programming question work should be completed in the provided starter files.
We have provided code at the bottom of some of these files for running doctest examples (we’ll discuss in lecture what this code does, and why it’s useful for you!) and running a program called PythonTA. This program checks your work for common logical, design, and style errors, and the feedback from PythonTA will be used to determine a portion of your grade for all programming parts of this assignment.
Tip: You already installed PythonTA on your own computer if you followed the software installation guide. Run PythonTA as you are working on your assignment to help identify issues and fix them as they come up to ensure that you get a perfect PythonTA grade on your final submission for this assignment. For instructions on how to run PythonTA, please check out our Assignment 1 PythonTA Guide.
Imagine this scenario: Paul is trying to decide where he should go Black Friday shopping. He has his eyes set on a Nintendo Switch 2 ($700), a new pair of shoes ($120), an air fryer ($150), and a new couch ($600). Different malls offer different discounts depending on the item. And, because Paul’s time is limited, he is trying to figure out how much money he can save by shopping at only one mall.
He calculates his average savings by multiplying the discount with the price of the 4 things he is trying to purchase. For example, if a mall’s discounts were 10%, 25%, 50% and 30% on the items above, respectively, his total savings would be: \[ 0.1 \times 700 + 0.25 \times 120 + 0.5 \times 150 + 0.3 \times 600\], or $355.00.
Paul writes a small program to perform this calculation, with the “main” function:
def summarize_average_savings(mall_discounts: dict) -> dict:
"""Return a mapping of malls to the total savings on the items in Paul's
shopping list for each of the malls in mall_discounts.
Each value in mall_discounts is itself a list, containing four floats
representing the discounts for the particular category of the 4 things
Paul is trying to purchase.
...
"""
He also writes three tests using pytest
for this
function. You can find the code for his program and tests in the starter
file a1_part1.py
.
When Paul runs pytest
to test his work, he sees the
following output (some extraneous text has been removed):
============================= test session starts =============================
collecting ... collected 3 items
a1_part1.py::test_calculation_one_mall_no_discount PASSED [ 33%]
a1_part1.py::test_calculation_many_malls_same_discount FAILED [ 66%]
a1_part1.py::test_calculation_many_malls_different_discounts FAILED [100%]
================================== FAILURES ===================================
__________________ test_calculation_many_malls_same_discount __________________
def test_calculation_many_malls_same_discount() -> None:
"""Test summarize_average_savings with many malls that offer the same
discounts.
"""
same_discount = [15.0, 25.0, 50.0, 0.0]
discounts = {
'Eaton Centre': same_discount,
'Fairview': same_discount,
'Sherway': same_discount,
'Yorkdale': same_discount,
}
# DO NOT MODIFY ANY OF THE BELOW LINES IN THIS TEST
expected = 210.0
actual = summarize_average_savings(discounts)
> assert math.isclose(actual['Eaton Centre'], expected)
E assert False
E + where False = <built-in function isclose>(2100.0, 210.0)
E + where <built-in function isclose> = math.isclose
a1_part1.py:111: AssertionError
_______________ test_calculation_many_malls_different_discounts _______________
def test_calculation_many_malls_different_discounts() -> None:
"""Test calculate_average_savings with many malls that offer different
discounts.
"""
discounts = {
'Eaton Centre': [30.0, 50.0, 40.0, 10.0],
'Fairview': [20.0, 70.0, 40.0, 35.0],
'Sherway': [15.0, 40.0, 10.0, 25.0],
'Yorkdale': [15.0, 15.0, 25.0, 30.0],
}
# DO NOT MODIFY ANY OF THE BELOW LINES IN THIS TEST
actual = summarize_average_savings(discounts)
expected = 390.0
> assert math.isclose(actual['Eaton Centre'], expected)
E assert False
E + where False = <built-in function isclose>(3800.0, 390.0)
E + where <built-in function isclose> = math.isclose
a1_part1.py:132: AssertionError
=========================== short test summary info ===========================
FAILED a1_part1.py::test_calculation_many_malls_same_discount - assert False
FAILED a1_part1.py::test_calculation_many_malls_different_discounts - assert ...
========================= 2 failed, 1 passed in 1.50s =========================
In your a1.tex
file, answer the following questions
about this program and pytest
report:
Looking at the pytest
report, identify which tests,
if any, passed, and which tests failed. You can just state the name of
the tests and whether they passed/failed, and do not need to give
explanations here.
For each failing test from the pytest
report, there is exactly one statement of code per test causing
it to fail (Note that in Paul’s code, there are some single statements
that are broken up into multiple lines for readability purposes – this
still counts as a single statement of code despite the multiple
lines).
The incorrect code statement may occur either within the test function itself, or Paul’s original functions.
For each failing test:
Explain in 2-3 sentences (plain English, not code) what error is causing the test to fail and why.
Edit a1_part1.py
by fixing the function code and/or
the tests so that all tests pass. Each failing test should be fixed by
changing exactly one statement of code per test. The
changes must be to fix errors only; the original purpose of the
functions and tests must remain the same. Rule: Do not modify any
of the expected
, actual
or assert
lines for any test. These lines are all
correct.
For each test that passed on the original code (before your changes in question 2), explain why that test passed even though there were errors in the Python file.
Write your responses in Part 1 of a1.tex
, filling in the
places marked with TODOs.
In Section 1.8 of the Course Notes, we discuss how to represent colours as tuples of three integers \((r, g, b)\), representing values for the amount of red, green, and blue used to form the colour.
Some examples are shown in the table below.
RGB Value | Colour |
---|---|
(0, 0, 0) | |
(255, 0, 0) | |
(0, 255, 0) | |
(0, 0, 255) | |
(181, 57, 173) | |
(255, 255, 255) |
In this part of the assignment, you’ll apply your knowledge of Python comprehensions to process collections of RGB colour tuples in interesting ways, and eventually build up a small Python program that allows you to manipulate real images!
First, we’ll define a colour row (or pixel row) to be a list of RGB triples, represented in Python as a list of lists, where each inner list has exactly three elements.
Here is an example of a colour row as a Python expression:
0, 255, 200], [1, 2, 3], [100, 100, 100], [181, 57, 173], [33, 0, 197]] [[
As the name “pixels” suggests, we’ll see later how an image is composed of multiple colour rows, but in this part we’ll keep things simple and write code that operates on just a single colour row.
Warmup. Open the a1_part2.py
starter file. At the top of the file we’ve provided a function
warmup1
and instructions on how to complete it. Follow the
instructions and then run the file in the Python Console, and call
warmup1()
. You should see a Pygame window appear with the
colours you specified!
Note: the Python interpreter will wait for you to close the Pygame window before returning from the function. This means you won’t be able to do anything else in the Python console until you close the Pygame window!
Tip: as you complete the functions in this part of the
assignment, you’ll want to check your work! You can use the doctest
examples we provided, your own examples in the Python console, and/or
call the provided a1_helpers.show_colours_pygame
function
(similar to how we use it in warmup1
) to visualize your
colour rows. Enjoy!
Cropping rows. Now, complete the functions
crop_row
, crop_row_border_single
, and
crop_row_border_multiple
. Each of these functions will
require you to take a colour row and return just part of that row.
Hint: recall Exercise 3 of the Lecture 2A worksheet.
While this is possible to do using list slicing (a Python feature we’ll see more of, later in this course), for this assignment you must implement these functions using comprehensions (and without list slicing), as this is what we’re evaluating on this assignment!
Changing colours. Next, let’s look at how we can
transform colour rows to create new rows. Implement the functions
remove_red_in_row
, greyscale
, and
sepia
again using comprehensions as well as any built-in
functions you may think would be useful (e.g. sum
,
min
, max
, etc.).
Hint: if you have an RGB tuple colour
, you can
access its individual red, green, and blue values by the indexing
expressions colour[0]
, colour[1]
, and
colour[2]
, respectively.
Distorting colours (pixelate_row). Finally,
implement the function pixelate_row
. This is the most
challenging functions on this assignment, and you’ll need to combine the
techniques you used in the previous two questions to complete them.
We’ve given you some hints and examples in the function docstring, so
please read them carefully.
In the previous part, you completed a series of functions that transform a row of colours. While we hope you had fun visualizing your results using Pygame, this may have felt a little unsatisfying: coloured squares is one thing, but what about real images? In this part of Assignment 1, you’ll apply the functions you wrote in Part 1 to work on real image data!
Every image we see on a computer screen is made up of small squares of various colours called pixels. More precisely, every image is a rectangular grid of pixels; typical image dimensions (e.g., “256-by-256” or “1920-by-1080”) describe the number of columns and rows of pixels in the image—i.e., the image’s width and height.
This is the key insight we’ll use to extend our work from Part 4 to two-dimensional images: because images can be decomposed into rows of pixels, we can use comprehensions to apply our “colour row” functions to every row in an image!
Warmup. Open the a1_part3.py
starter file. At the top of the file we’ve provided a function
warmup2
, which will return the pixel data for a small
30-by-15 image file we’ve provided under
images/spiderman.png
.
First, try running this file in the Python console and then
calling warmup2()
. You should see a very long list of
colour tuples! In fact, \(30 \times 15 =
450\) tuples are displayed in total.
>>> warmup2()
125, 125, 125], [81, 87, 78], [103, 114, 117], [144, 151, 164], ... [[
Let’s use the Python library pprint
to make the
output look a bit nicer. In the Python console, type in the
following:
>>> image_data = warmup2()
>>> import pprint
>>> pprint.pprint(image_data)
125, 125, 125],
[[[81, 87, 78],
[103, 114, 117],
[144, 151, 164],
[ ...
Better! Notice that the triple [[[
at the start. This
confirms that the image data is a list of colour rows, or list of
list of lists; if you scroll down, you’ll see the “breaks” where
one of the colour rows ends and another begins.
We can check the total number of colour rows contained in
image_data
using the len
function:
>>> len(image_data)
15
This number corresponds to the height of the image.
Because image_data
is a list, we can use list
indexing to access the individual colour rows it contains. Try typing
the following into the Python console to access the topmost colour row
in the image:
>>> image_data[0]
125, 125, 125], [81, 87, 78], [103, 114, 117], [144, 151, 164], ... [[
And because a colour row is itself a list, we can also find its size, which is the number of pixels in a single row of the image—i.e., the image’s width.
>>> len(image_data[0])
30
Finally, inside the body of warmup2
, try
uncommenting (i.e., deleting the #
and space) the
a1_helpers.show_colour_rows.pygame
function call, and then
re-running the file and calling warmup2
again. You should
see a new Pygame window appear showing the individual colours of the
image! If you have the patience and time, you can count to make sure
there are indeed 15 rows displayed, each with 30 squares.
Note: like with warmup1
, the Python interpreter
will wait for you to close the Pygame window before continuing to the
return statement of the function. This means you won’t be able to
see/access the colour rows in the Python console until you close the
Pygame window!
Practice transforming multiple colour rows. Now
that you’ve explored the data types we use to represent image data,
let’s actually get to transforming them! We provide you with five
completed functions that directly rely on your
functions from part 1: remove_red_in_image
,
greyscale_image
, sepia_image
,
pixelate_rows_in_image
, and
crop_rows_in_image
.
Each of these functions use a list comprehension together with one of your functions from Part 1 to apply a transformation to each colour row in image data.
We can use these functions to play around and see how all of your work can be used on some real images!
Near the bottom of a1_part3.py
, we’ve provided the
skeleton of a function (transform_image
) that you can use
to read in an image file, perform some transformations, and then save
the file.
Experiment with this function on the provided files
images/spiderman.png
and
images/sadias_zoo.jpeg
, or copy your own images into the
images/
folder and use them instead!
This part is not for credit (we aren’t grading any changes you make
to the transform_image
function, except checking the code
with PythonTA), so feel free to spend as much or as little time as you
like on it.
But if someone asks you what you’re working on for your first assignment in CSC110, you can use this function to show off images you’ve made! 😎
Generalized cropping. Complete the function
crop_image
, which enables cropping out both columns and
rows. In addition to completing the function body, you are required to
write at least one example doctest for this function, which should
follow our usual guidelines for good function examples. As we saw with
the function design recipe, you should write the example first,
to help you understand what the function should do.
This function follows a similar structure to the transformation functions we already completed for you, but you’ll need to do something a bit different to select only a subset of the rows to keep.
We have provided one constant EXAMPLE_PIXEL_GRID
that
you might find helpful to use in your tests. You can also uncomment the
line in transform_image
which calls this function once you
are done, to test your work out with real images.
Once again, you may NOT use list slicing for this function, or anywhere on this assignment!
Please proofread and test your work carefully before your final submission! As we explain in Assignment Expectations, it is essential that your submitted code not contain syntax errors. Python files that contain syntax errors will receive a grade of 0 on all automated testing components (though they may receive partial or full credit on any TA grading for assignments). You have lots of time to work on this assignment and check your work (and right-click -> “Run in Python Console”), so please make sure to do this regularly and fix syntax errors right away.
honour_code.txt
,
a1_part1.py
, a1_part2.py
,
a1_part3.py
. Please note that MarkUs is picky with
filenames, and so your filenames must match these exactly, including
using lowercase letters.
a1.tex
and a1.pdf
files from
Overleaf.Remember, you can submit your files multiple times before the due date. So you can aim to submit your work early, and if you find an error or a place to improve before the due date, you can still make your changes and resubmit your work.
After you’ve submitted your work, please give yourself a well-deserved pat on the back and go do something fun or look at some art or eat some chocolate or take a nap!