Quiz 2: Solutions

August 16th, 2021


Key Statistics

  • Median: 131.0
  • Mean: 117.49
  • Maximum: 155.0

You can find your answers on Gradescope. Let Tara know if you do not have access to Gradescope.


1. Grid (20 points)

Reminder of Grid features:

  grid = Grid.build([['row', '0', '0'], ['row', '1', '1']])
  # Grid properties: grid.width == 3, grid.height == 2
  grid.in_bounds(x, y) - True if in bounds
  grid.get(x, y) - returns contents at that x,y, or None if empty
  grid.set(x, y, value) - sets new value into grid at x,y

Implement the function diagonal_test(grid, x, y, d) as follows: The function takes in a grid, an in bounds x and y, and an int n. The square diagonally up and to the right of (x,y) is d=1. The square diagonally up and to the right of that one is d=2 and so on. Given an in-bounds x,y and an int d that is 1 or more, return True if the diagonal d square is in bounds and contains the same thing as the square at x,y. Three doctests are shown below to give an example of the behavior of the function. There is also a diagram showing locations of d values for given x,y.

>>> gird = Grid.build([[None, 's', 's'], ['r', 'r', 's'], ['s', None, 'r']])
>>> diagonal_test(grid, 0, 2, 2) # in bound and same character
True 
>>> diagonal_test(grid, 0, 2, 1) # Not the same characters
False 
>>> diagonal_test(grid, 0, 0, 1) # d=1 is out of bounds
False 

Below is a diagram showing the different diagonals for two squares in the grid.

Starter Code

  
def diagonal_test(grid, x, y, d):
    pass
  

Solution Code

  
def diagonal_test(grid, x, y, d):
    val = grid.get(x, y)
    if grid.inbounds(x+d, y-d):
        d_val = grid.get(x+d, y-d)
        if val == d_val:
            return True
    return False
  

2. Crypto (20 points)

This problem is similar to the Crypto homework.

For this problem, there is a source list and a slug list which is twice as long as the source. Compute and return the encrypted form of a char as follows: if the lowercase version of the char does not appear in the source list, return the char unchanged. If the lowercase version of the char appears in the source list, consider its case. (a) If ch is lower case, return the char at the same index in the slug. (b) If ch is uppercase, return the upper case version of the character at the corresponding index in the second half of the slug

Say we have this len-5 source and len-10 slug:

source = ['a', 'b', 'c', 'd', 'y']
slug = ['j', 'k', 'l', 'm', 'n', 'p', 'q', 'r', 's', 't']

len(source) = 5 
len(slug) = 10

index in source:    corresponding index in second half of slug:
0                   5
1                   6
2                   7
3                   8
4                   9
>>> source = ['a', 'b', 'c', 'd', 'y']
>>> slug = ['j', 'k', 'l', 'm', 'n', 'p', 'q', 'r', 's', 't']
>>> ch = 'A'
>>> encryption(source, slug, ch)
P
>>> ch = 'b'
encryption(source, slug, ch)
k

Starter Code

  
def encryption(source, slug, ch):
    pass
  

Solution Code

  
def encryption(source, slug, ch):
    low = ch.lower()
    if low in source:
        indx = source.index(low)
        if ch.islower():
            return slug[indx]
        else:
            return slug[indx+len(source)].upper() 
    return ch
  

3. String (25 points)

You are given a string s which has a secret code somewhere in the string. The code is as follows: In the string s, there may or may not be a

‘$$’
substring. If there is not a
‘$$’
substring in s, there is not a secret code in the string and your function should return None. If there is a
‘$$’
in s, it will be followed by some number of digits and then a series of upper case letters. Find and return this series of upper case letters. For example:
>>> s = ‘xxxx$$106CSCSat54xx’ 
>>> parse_code(s)
‘CSCS’
>>>
>>> s = 'xx$$ABCxx'
>>> parse_code(s) 
'ABC'
>>> s = ‘Today is a great day to buy a $1 lemonade’
>>> parse_code(s)
None

Starter Code

  
def parse_code(s):
    pass

Solution Code

  
def parse_code(s):
    money_loc = s.find(‘$$’)
    if money_loc == -1:
        return None

    start = money_loc + 2
    while start < len(s) and s[start].isdigit():
        start += 1

    end = start # or start + 1
    while end < len(s) and s[end].isupper():
        end += 1

    return s[start:end]

4. Drawing (25 points)

Use the draw_fn style of drawing as you did on HW5 for this problem

Reminder: the canvas draw_rect function:

canvas.draw_rect(x, y, width, height) - draws a rectangle with the upper left corner at x,y with the given width and height

Implement a function, draw_rects(canvas, width, height, n) as follows:

The function takes in a canvas object, the width of the canvas, the height of the canvas, and the number of rectangles to draw. The function should draw n concentric rectangles. Rectangles are said to be concentric if they share the same midpoint and axes (see figure for example). The outer most rectangle should start at (0, 0) and have width width and height height.

Each of the following rectangles should be evenly spaced such that the last rectangle is a dot in the center of the canvas. In other words, each rectangle should be equidistant from the one before it and the one after it in both the x and y directions. As a hint, think of how you can use the x_add/y_add strategy to get the starting points for each rectangle.

In the second figure, this distance is represented by the red lines (equidistant in x direction) and blue lines (equidistant in y direction). You are not responsible for drawing the red and blue lines. Those are just to show you how to space out the rectangles.

Example of what your output should look like for n=5

Diagram showing what we mean by equidistant. All of the red lines are the same length and all of the blue lines are the same length. As mentioned above, you should not draw the red and blue lines. These are just to help break down the problem.

Starter Code

  
def draw_rects(canvas, width, height, n):
    pass

Solution Code

  
def draw_rects(canvas, width, height, n):
    for i in range(num_bars):
        x_add = (i/(num_bars-1)) * (width//2)
        y_add = (i/(num_bars-1)) * (height//2)
        canvas.draw_rect(x_add, y_add, width - 2*x_add, height-2*y_add)
  

5. One-liners (20 Points)

Do not need to write a def for these questions. For each part, write a 1-line expression to compute the indicated value with: map() or sorted() or min() or max() or a comprehension. You do not need to call list() for these.

# a. Given a list of strings
# Call map() to produce a result where each string is replaced by the first 
# character in the string
# (or equivalently write a comprehension)
>>> strs = ['I', 'love', 'CS106A']
# yields ['I', 'l', 'C']
# your expression:

map(lambda s: s[0], strs)


# b. Given a list of (x,y) tuples like this:
# points = [(3045, 100), (42, 66), (800, 32)]
# Call sorted() to produce a list of these tuples sorted in decreasing 
# order based on the product of the two values in the tuple (x multiplied by y) 
# your expression:

sorted(points, key=lambda point: point[0] * point[1], reverse=True)

# c. Given a non-empty list of (country, total_medal_count, gold_medal_count) tuples
# like the following: [(‘United States’, 98, 31), (‘China’, 79, 36) (‘ROC’, 62, 17)]
# Call max() or min() to return the tuple with the highest gold medal count.
# your expression:

# they can use whatever name they want for the list of tuples

max(lst, key=lambda data: data[2])

6. Dict (20 points)

Suppose we have a "possible meals" dict, where each key is a food, and its value is a list of all meals at which this food is typically eaten, like this:

poss_meals = {
    'mac and cheese': ['brunch', 'lunch', 'snack', 'dinner'],
    'cereal': ['breakfast', 'brunch', 'snack'],
    'milkshake': ['dessert']
}

We also have a "meals data" dict where each key is a meal, and its value is a len-2 list which has the sum of the tasty scores of all foods possibly eaten at that meal as the first value in the list and the sum of the healthy scores of all foods possibly eaten at that meal as the second value in the list.

meal_data = {
    'breakfast': [5, 3]
    'brunch': [5, 3]
    'dessert': [10, 0]
}

Implement a function update_meal_data(food_info, poss_meals, meal_data) as follows: The function takes in food_info (which is a len-3 tuple with the name of a food, its tasty score, and its healthy score), a poss_meals dict as described above, and a meal_data dict as described above. The function should update the meal_data dictionary with the information in the food_info tuple by increasing the tasty and healthy scores for all meals at wich the given food is typically eaten.

Input food_info, poss_meals, and meal_data:
food_info = ('mac and cheese', 10, 2)
poss_meals = {
    'mac and cheese': ['brunch', 'lunch', 'snack', 'dinner'],
    'cereal': ['breakfast', 'brunch', 'snack'],
    'milkshake': ['dessert']
}
meal_data = {
    'breakfast': [5, 3]
    'brunch': [5, 3]
    'dessert': [10, 0]
}

After a call to update_meal_data(food_info, poss_meals, meal_data) meal_data should look like:
meal_data = {
    'breakfast': [5, 3]
    'brunch': [15, 5]
    'dessert': [10, 0]
    'lunch': [10, 2]
    'snack': [10, 2]
    'dinner': [10, 2]
}

Starter Code

  
def update_meal_data(food_info, poss_meals, meal_data):
    pass
  

Solution Code

  
def update_meal_data(food_info, poss_meals, meal_data):
    # decomp by var
    food = food_info[0]
    tasty = food_info[1]
    healthy = food_info[2]
    
    meals = poss_meals[food]
    for meal in meals:
        if meal not in meal_data:
            meal_data[meal] = [0, 0]
        scores = meal_data[meal]
        scores[0] += tasty
        scores[1] += healthy
  

7. Dict-File (25 points)

Suppose we have an "athlete country" dict where each key is the name of an athlete and its value is the name of the country that althete is representing in the Olympic Games, like this:

athlete_country = {
    'Dora Varella': 'Brazil',
    'Rebecca Andrade': 'Brazil',
    'Sakura Andrade': 'Japan',
    'Kokona Hiraki': 'Japan',
    'Keegan Palmer': 'Australia',
    'Emma McKeon': 'Australia',
    'Cate Campbell': 'Australia',
    'Matthew Temple': 'Australia'
}

Suppose we also have a file named results.txt where each line is like: athlete,medal_count,event
where athlete is the athletes name, medal_count is 0 if they did not win a medal in that event and 1 if they did win a medal in that event, and event is the event that athlete competed in. An example results.txt file:

Dora Varella,0,womens-skateboarding-park
Rebecca Andrade,1,Womens gymnastics all-around
Sakura Yosozumi,1,womens-skateboarding-park
Kokona Hiraki,1,womens-skateboarding-park
Keegan Palmer,1,mens-skateboarding-park
Emma McKeon,1,womens-100-freestyle
Cate Campbell,1,womens-100-freestyle
Matthew Temple,1,mens-100-butterfly

Implement a function, make_olympic_info(athlete_country, filename) which processes all the lines in the file and builds and returns a nested dictionary where the outer key is a country and the outer value is a dictionary. The inner dicionary key is an event and the inner dictionary value is the integer number of medals won for that country in that event.

A call to make_olympic_info(athlete_country, 'results.txt') would return:

{
    'Brazil': {
        'womens-skateboarding-park': 0, # Brazil competed in this event, but didn't win a medal
        'womens-gymnastics-all-around': 1
    }
    'Japan': {
        'womens-skateboarding-park': 2 # Japan won two medals in this event 
        'mens-surfing': 1
    }
    'Australia': {
        'womens-100-freestyle': 2,
        'mens-skateboarding-park': 1,
        'mens-100-butterfly': 0 
    }
}

Starter Code

  
def make_olympic_info(athlete_country, filename):
    country_results = {}
    with open(filename) as f:
        for line in f:
            pass


    return country_results
  

Solution Code

  
def make_olympic_info(athlete_country, filename):
    country_results = {}
    with open(filename) as f:
        for line in f:
            line = line.strip() # it is fine if this is not here
            splits = line.split(',')
            athlete = splits[0]
            medal_count = int(splits[1])
            event = splits[2]

            country = athlete_country[athlete]
            if country not in result:
                result[country] = {}
            country_data = result[country]

            if event not in country_data:
                country_data[event] = 0
            country_data[event] += medal_count


    return country_results