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)