Solutions
Lab 10
lab10.py
def read_courses(filename: str) -> list:
"""
Reads the list of courses from the given filename and returns
a list of lists OR course objects, your choice.
"""
in_file = open(filename, "r")
lines = in_file.readlines()
course_list = []
# starting from the second line (lines[1:])
# because the first line is just the header
for line in lines[1:]:
# to get rid of the new line character at the end of each line
line = line.strip()
course_fields = line.split(",")
course_list.append(course_fields)
in_file.close()
return course_list
def write_courses(filename: str, courses: list) -> None:
"""
Writes the prioritized list of courses to the given filename in the
same format as the original "courses.csv"
"""
out_file = open(filename, "w")
# write the header first
out_file.write("Course,Current Grade,Exam Weight,Exam day\n")
for course in courses:
line = ",".join(course)
out_file.write(line + "\n")
out_file.close()
def calc_priority(current_grade: float, exam_weight: int, exam_day: int) -> float:
"""
Calculates the priority of a course based on the current grade, the exam weight,
and the date of the exam. The formula is:
priority = (100 - current_grade) * exam_weight / (exam_day - STUDY_DAY_ONE)
where STUDY_DAY_ONE is a constant that represents the first day of studying.
The tests assume studying begins on day 11.
"""
return (100 - current_grade) * exam_weight / (exam_day - 11)
def sort_by_priority(courses: list) -> None:
"""
Sorts the list by priority from highest to lowest.
"""
# this is Insertion Sort.
# learn more about it here: https://www.geeksforgeeks.org/insertion-sort-algorithm
# we're changing the list in-place. that's why we're looping over the index, and not value.
# since we're not re-assigning the list, any change to the "courses" parameter will change
# the original list. this is because lists are mutable.
for i in range(1, len(courses)):
current = courses[i] # current course
j = i - 1
# move courses with lower priority to the right
while j >= 0:
next_course = courses[j] # course to be compared with the current one
# calculate priorities for both courses (current and next)
current_course_priority = calc_priority(
float(current[1]), int(current[2]), int(current[3])
)
next_course_priority = calc_priority(
float(next_course[1]), int(next_course[2]), int(next_course[3])
)
if next_course_priority < current_course_priority:
# shift the lower priority course to the right
courses[j + 1] = courses[j]
j -= 1
else:
break # current item is at the correct location. let's go to the next one.
courses[j + 1] = current
def main() -> None:
"""
Prompts the user for the name of a file.
Reads the list of courses from the given filename, sorts them by priority,
and writes the prioritized list to a new file. The new filename should be
the old filename with "_prioritized" appended to it. Don't forget to move the
".csv" to the end of the filename!
"""
filename = input("Enter filename: ")
course_list = read_courses(filename)
sort_by_priority(course_list)
filename = f"{filename.split('.csv')[0]}_prioritized.csv"
write_courses(filename, course_list)
if __name__ == "__main__":
main()
Assignment 04
main.py
from matplotlib import pyplot as plt
ANIMATION_SPEED = 0.01
def view_grid(grid: list, frame_delay: float, step_number: int) -> None:
"""
Shows an image of the current state of the grid
parameters:
grid - list-of-lists representing the current grid. Inner lists use 0s to represent dead cells, and 1s to represent live ones
frame_delay - the program will pause for this many seconds after displaying the image. 0.1s gives a pretty good animation effect
step_number - the step number of the supplied grid (will be displayed above the image)
"""
# check that the grid supplied is not empty
if len(grid) == 0:
raise Exception("grid is empty")
# check that all rows contain the same number of cells
row_lengths = set([len(row) for row in grid])
if len(row_lengths) != 1:
raise Exception(
f"not all grid rows are the same length. Found lengths: {row_lengths}"
)
# check that all rows contain only 0s and 1s
if not all([set(row) <= {0, 1} for row in grid]):
raise Exception("only 0 and 1 are allowed in grid")
# plot the grid
plt.cla()
plt.imshow(grid)
plt.title(f"step {step_number}")
plt.pause(frame_delay)
def number_of_alive_neighbors(grid: list, row: int, col: int) -> int:
"""
Calculates the number of live neighbors for a cell
parameters:
grid: the grid at the current state
row: the row index of the current cell
col: the col index of the current cell
returns:
number of live neighbors for the cell
"""
row_ranges = [-1, 0, 1] # offset of row changes for the neighbors
col_ranges = [-1, 0, 1] # offset of column changes for the neighbors
num_rows = len(grid)
num_cols = len(grid[0])
neighbors = 0
for row_offset in row_ranges:
for col_offset in col_ranges:
if row_offset == 0 and col_offset == 0:
# i don't want to continue, because this is the cell itself and not a neighbor
# go to the next iteration, instead
continue
new_row = row + row_offset
new_col = col + col_offset
if 0 <= new_row < num_rows and 0 <= new_col < num_cols:
# the new coordinate is within bounds
neighbor = grid[new_row][new_col]
if neighbor == 1:
neighbors += 1
return neighbors
def is_cell_alive(grid: list, row: int, col: int) -> bool:
"""
Checks if a cell will be alive or dead in the next step
parameters:
grid: the grid at the current state
row: the row index of the current cell
col: the col index of the current cell
returns:
True if the cell will be alive in the next state; False otherwise
"""
neighbors = number_of_alive_neighbors(grid, row, col)
current_cell = grid[row][col]
if current_cell == 1:
# the cell is alive. we'll see if it will survive in the next step.
if 2 <= neighbors <= 3:
return True # cell will survive
else:
return False # cell will die
else:
# cell is dead. we'll see if it can get born in the next step.
if neighbors == 3:
return True # birth happens
else:
return False # cell remains dead
def create_next_step_grid(grid: list) -> list:
"""
Creates the next step grid based on the two rules
parameters:
grid: the grid at the current state
returns:
next state grid
"""
# initializing a new grid because we don't want to change the original grid yet.
new_grid = []
number_of_rows = len(grid)
number_of_cols = len(grid[0])
for row in range(number_of_rows):
new_row = [] # initializing an empty row
for col in range(number_of_cols):
if is_cell_alive(grid, row, col):
new_row.append(1)
else:
new_row.append(0)
# add the row to the new grid
new_grid.append(new_row)
return new_grid
def write_grid_to_file(grid: list, file_name: str) -> None:
"""
Writes the grid to a file
parameters:
file_name: file to write the final grid to
grid: the grid at the final state
"""
out_file = open(file_name, "w", encoding="UTF-8")
wall_char = "▒"
live_cell_char = "█"
dead_cell_char = " "
number_of_cols = (
len(grid[0]) + 2
) # because we ignored the 2 wall characters when creating the grid.
out_file.write(wall_char * number_of_cols + "\n") # upper wall
for row in grid:
new_row = []
for col in row:
if col == 1:
new_row.append(live_cell_char)
else:
new_row.append(dead_cell_char)
line = wall_char + "".join(new_row) + wall_char + "\n"
out_file.write(line)
out_file.write(wall_char * number_of_cols) # bottom wall
out_file.close()
def main(input_filename: str, output_filename: str, display: bool) -> None:
"""
main function
parameters:
input_filename: file to read the starting configuration from
output_filename: file to write the ending configuration (after 100 steps) to
display: if True, the program should display the grid steps (using the provided view_grid function)
if False, the program should not display the grid steps.
"""
in_file = open(input_filename, "r", encoding="UTF-8")
lines = in_file.readlines()
in_file.close() # you can close the file once you read its contents
grid = [] # initializing an empty grid
for line in lines[1:-1]: # we don't need to process the wall characters
# create a row based on each line
# then append the row to the grid at the end
row = []
for char in line:
if char == " ":
row.append(0)
elif char == "█":
row.append(1)
# row is completely filled and ready to be added to the grid
grid.append(row)
if display:
# the grid is initialized and ready to be shown for the first time.
view_grid(grid, ANIMATION_SPEED, 0)
for step in range(1, 101): # 100 steps (1-100)
next_step_grid = create_next_step_grid(grid)
grid = next_step_grid # make sure the grid is updated for the next step
if display:
view_grid(grid, ANIMATION_SPEED, step)
write_grid_to_file(grid, output_filename)
# test call
main("data/input/a.txt", "data/output/z.txt", False)
Quiz 02
lab09.py
from math import sqrt
def total_distance(directions: list) -> int:
"""
Calculates and returns the total distance walked.
"""
total = 0
for direction in directions:
# split() will return a list of two items.
# first item is the direction, and second item is the distance.
# by using direction.split()[1] I'm doing a split and grabbing
# the second item in one line.
distance = direction.split()[1]
# remember that what you read from a text file is in string format.
# to do calculations, you need to cast it into a proper type.
total += int(distance)
return total
def net_distance(directions: list) -> float:
"""
Calculates and returns the distance between the start and end point.
"""
net_north = 0 # net distance for North and South directions
net_west = 0 # net distance for West and East directions
for direction_distance in directions:
direction = direction_distance.split()[0]
distance = int(direction_distance.split()[1])
if direction == "N":
net_north += distance
elif direction == "S":
net_north -= distance
elif direction == "W":
net_west += distance
else:
net_west -= distance
net_distance = sqrt(net_north**2 + net_west**2)
return net_distance
def main() -> None:
"""
Main program entry point.
"""
f = open("directions.txt", "r")
lines = f.readlines()
t_distance = total_distance(lines)
print(t_distance)
n_distance = net_distance(lines)
print(n_distance)
f.close()
f = open("activity_log.txt", "w")
f.write(f"Total distance: {t_distance}\n")
f.write(f"Net distance: {n_distance:.2f}")
f.close()
main()
Quiz 01
lab08part1.py
def remove_all(courses: list, prefix: str) -> None:
"""
Removes all courses matching prefix from the courses list.
"""
# make a copy of the list so we don't loop over
# and change the size of the original list.
# otherwise, unexpected results happen.
courses_cpy = courses[:]
for course in courses_cpy:
if prefix in course:
courses.remove(course)
def main() -> None:
courses = ["COMP 1501", "GNED 1103", "GNED 1401", "MATH 1505"]
remove_all(courses, "GEOL")
remove_all(courses, "MATH")
remove_all(courses, "GNED")
print(courses)
main()
lab08part2.py
def best_quiz_grade(grades_1: list, grades_2: list) -> list:
max_grades = [] # an empty list to store the maximums
# lengths of both lists are the same
# so doesn't matter which list you use
for index in range(len(grades_1)):
grade_1 = grades_1[index]
grade_2 = grades_2[index]
max_grade = max(grade_1, grade_2)
max_grades.append(max_grade)
return max_grades
def main() -> None:
# test lists
grade_1 = [20, 45.6, 76.1, 100, 200, 300, 400]
grade_2 = [30, 25.6, 86.1, 100, 200, 300, 400]
best_grades = best_quiz_grade(grade_1, grade_2)
print(best_grades)
main()
Assignment 03
import math
def compute_HRV(interval1: float, interval2: float, interval3: float) -> float:
"""
compute heart rate variability
parameters:
interval1, interval2, and interval3: three successive heart beat intervals, in ms
returns:
the heart rate variability
"""
diff1 = interval1 - interval2
diff2 = interval2 - interval3
avg_sq_diff = (diff1**2 + diff2**2) / 2
hrv = math.sqrt(avg_sq_diff)
return hrv
def temperature_is_fever(temperature: float, site: str) -> bool:
"""
determines whether a given temperature and test site represents fever
parameters:
temperature: the recorded temperature in °C
site: either "oral" or "underarm"
returns: True if an oral temp is at least 37.8°, or an underarm temp is at least 37.2°
"""
fever = False
if (temperature >= 37.8 and site == "oral") or (
temperature >= 37.2 and site == "underarm"
):
fever = True
return fever
def has_fever() -> bool:
"""
Asks the user for their body temperature and where it was measured, and
returns True if they have a fever
"""
temperature = float(input("Enter your temperature (°C):"))
site = input(
"Was the temperature measured Orally (O) or Underarm (U)? (enter O or U):"
).lower()
site = "oral" if site == "o" else "underarm"
return temperature_is_fever(temperature, site)
def has_nausea() -> bool:
"""
asks the user if they are experiencing nausea, and
returns their response as a boolean
"""
return input("Are you experiencing nausea? (enter y or n):").lower() == "y"
def has_low_HRV() -> bool:
"""
asks the user to enter three successive heartbeat intervals, in ms
returns True if these intervals represent a HRV less than 50 ms
"""
print("Please enter 3 heartbeat intervals in ms…")
interval1 = float(input("Enter first interval:"))
interval2 = float(input("Enter second interval:"))
interval3 = float(input("Enter third interval:"))
hrv = compute_HRV(interval1, interval2, interval3)
return hrv < 50
def has_high_cortisol() -> bool:
"""
asks the user to enter their cortisol level
returns True if the level is above 25.0
"""
cortisol = float(input("Enter cortisol level in mcg/dL:"))
return cortisol > 25.0
def main() -> None:
"""
Runs the diagnostic program, and prints out the final diagnosis
"""
diagnosis = "error"
if has_fever():
if has_nausea():
diagnosis = "flu"
else:
diagnosis = "infection"
elif has_low_HRV() and has_high_cortisol():
diagnosis = "stress"
else:
diagnosis = "healthy"
print(f"Diagnosis: {diagnosis}")
if __name__ == "__main__":
main()
Assignment 02
# ____CONSTANTS_________
# constants (if any) should go in this section
RATE = 0.014
# ____FUNCTIONS_________
# functions (if used) should go in this section
def compute_retirement_income(
average_income: float,
age: int,
years_service: int,
retirement_age: int,
) -> float:
"""
calculates retirement income by applying the formula
average_income: the average of the 3 highest expected incomes over the person's career
age: current age in years
years_service: number of years of service currently
retirement_age: age in years at retirement
"""
years_service_at_retirement = (retirement_age - age) + years_service
income = average_income * RATE * years_service_at_retirement
return income
# ____INPUTS____________
# collect user inputs in this section
age = int(input("Enter current age in years: "))
service_years = int(input("Enter current years of service: "))
largest_income = float(input("Enter the largest expected annual income: "))
second_largest_income = float(
input("Enter the second-largest expected annual income: ")
)
third_largest_income = float(input("Enter the third-largest expected annual income: "))
# ____PROCESSING_______
# perform processing and calculations in this section
avg_3_incomes = (largest_income + second_largest_income + third_largest_income) / 3
income_55 = compute_retirement_income(avg_3_incomes, age, service_years, 55)
income_60 = compute_retirement_income(avg_3_incomes, age, service_years, 60)
income_65 = compute_retirement_income(avg_3_incomes, age, service_years, 65)
# ____OUTPUT___________
# format and display your output in this section
print() # extra empty line
print(f'{"retirement age":20}{"retirement income":20}')
print(f"{55:<20}${income_55:<20,.2f}")
print(f"{60:<20}${income_60:<20,.2f}")
print(f"{65:<20}${income_65:<20,.2f}")
Lab 07
Part 1
lab07part1.py
:
def sum_all() -> int:
"""
Prompts the user to enter positive integers one at a time, then displays the
number and the running total. Finishes when a negative number is entered.
Returns the final total.
"""
prompt = "Enter a positive integer to sum, or negative integer to finish: "
number = int(input(prompt)) # the priming read
total = 0
while number >= 0:
# Remove the internal read here!!
print(f"You entered {number}")
total += number
print(f"The running sum is {total}")
number = int(input(prompt)) # the internal read
return total
def main():
total = sum_all()
print(f"\nThe final sum is {total}")
main()
Part 2
lab07part2.py
:
def main() -> None:
number_str = input("Enter number: ")
largest = int(number_str)
# Remember that the zero value of any type is evaluated to False
# in conditional expressions. Empty string is the zero value of the
# string type. Therefore, if number_str is an empty string, the while
# condition evaluates to False and the loop terminates. Otherwise,
# the loop runs.
while number_str:
number = int(number_str)
if number > largest:
largest = number
number_str = input("Enter number: ")
print(largest)
main()
Lab 06
Part 2
lab06part2.py
:
# Declare a constant for the amount limit
THRESHOLD = 40
def check_free_delivery(amount: float, delivery_distance: int) -> str:
"""
Takes the total amount of the order and checks
whether the order qualifies for free delivery.
returns a string message.
"""
if delivery_distance < 0:
return "Invalid entry, distance must be positive"
if delivery_distance >= 20:
return "Sorry, you are not eligible for free delivery"
# When delivery 0 <= delivery_distance < 20, we check the amount
if amount < 0:
return "Invalid entry, orders must be positive"
elif amount < THRESHOLD:
remaining = THRESHOLD - amount
return f"Add ${remaining:.2f} to your order to get free delivery"
else:
return "You get free delivery!"
def main() -> None:
"""
Prompts the user for the total amount of the order and calls
check_free_delivery with the user's input.
Prints an appropriate message based on whether the order qualifies
for free delivery.
"""
amount = float(input("What is the dollar amount of the current order? "))
delivery_distance = int(input("Delivery distance? "))
print(check_free_delivery(amount, delivery_distance))
main()
Lab 05
Part 2
lab05.py
:
# importing only the functions we need from the math module
from math import cos, sin, radians
g = 9.81 # gravitational acceleration = (9.81 m/s² on earth)
def calculate_landing_point(angle: float, velocity: float) -> float:
"""
Computes the horizontal distance the projectile will travel.
Parameters:
angle: the angle of the projectile's trajectory in degrees
velocity: the speed of the projectile in m/s
"""
angle = radians(angle)
t = velocity * sin(angle) / (0.5 * g)
x = velocity * cos(angle) * t
return x
def is_hit(distance_1: float, distance_2: float) -> bool:
return -10 < (distance_1 - distance_2) < 10
def main() -> None:
"""
Calls the calculate_landing_point function.
"""
print(calculate_landing_point(80, 120))
print(calculate_landing_point(65, 80))
print(calculate_landing_point(45, 70))
main()
Lab 04
Part 1
ingredients.py
:
def add_ingredient(item: str, quantity: float) -> str:
"""
Returns a string indicating that ingredient type and quantity are added.
"""
return f"Added {quantity} grams of {item}."
def main() -> None:
ingredient = input("What ingredient would you like to add? ")
quantity = float(input(f"How many grams of {ingredient} are needed? "))
print(add_ingredient(ingredient, quantity))
main()
Part 2:
cooking_time.py
:
from math import pi
def area(diameter: float) -> float:
"""
Calculate the area of a circle given its diameter.
Parameters:
diameter (float): The diameter of the circle.
Returns:
float: The area of the circle
"""
return pi * (diameter / 2) ** 2
def cake_time(temp: int, diameter: float) -> int:
"""
Calculate the approximate baking time for a cake based on temperature and cake diameter.
This function estimates the baking time in minutes, rounding to the nearest 5-minute interval.
Parameters:
temp (int): The oven temperature in degrees Fahrenheit.
diameter (float): The diameter of the cake in inches.
Returns:
int: The estimated baking time in minutes, rounded to the nearest 5 minutes.
"""
t = 20 + (350 * (area(diameter) - area(6))) / (2 * temp)
nearest_5 = round(t / 5) * 5
return nearest_5
def main() -> None:
temp = int(input("Enter the temperature in °F (between 300 and 450): "))
diameter = float(
input("Enter the cake pan diameter in inches (between 6 and 12): ")
)
print(f"Your cake should take about {cake_time(temp, diameter)} minutes to bake.")
main()
Lab 03
lab03.py
:
gems = int(input("Enter the number of gems: "))
players = int(input("Enter the number of players: "))
gems_per_player = gems // players
leftover = gems % players
# Create a constant for the output's first column width
FIRST_COL_WIDTH = 17
# Create a constant for the output's second column width
SECOND_COL_WIDTH = 2
# Create a string to print
output = f"""\
{'Gems:':{FIRST_COL_WIDTH}}{gems:>{SECOND_COL_WIDTH}}
{'Players:':{FIRST_COL_WIDTH}}{players:>{SECOND_COL_WIDTH}}
{'Gems per player:':{FIRST_COL_WIDTH}}{gems_per_player:>{SECOND_COL_WIDTH}}
{'Gems left over:':{FIRST_COL_WIDTH}}{leftover:>{SECOND_COL_WIDTH}}
"""
print(output)