CS106A Midterm

Stanford, Fall 2023-24

alt:midterm distr

Instructions

# CS106A Exam Reference Reminder
# [square brackets] denote functions listed here that we have not used yet
# Exam questions will not depend on functions we have not used yet
Bit:
  bit = Bit(filename)
  bit.front_clear() bit.left_clear() bit.right_clear()
  bit.get_color()
  bit.move() bit.left() bit.right()
  bit.paint('red') [bit.erase()]
General functions:
  len() int() str() range() [list() sorted()]
String functions:
  isalpha() isdigit() isspace() isupper() islower()
  find() upper() lower() strip()
List functions:
  append() index() [extend() pop()  insert()]
SimpleImage:
  # read filename
  image = SimpleImage(filename)
  # create blank image
  image = SimpleImage.blank(width, height)

  # foreach loop
  for pixel in image:

  # range/y/x loop
  for y in range(image.height):
      for x in range(image.width):
          pixel = image.get_pixel(x, y)
Grid 2D:
  grid = Grid.build([['row', '0'], ['row', '1']])
  grid.width, grid.height - properties
  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

1. Short Answer (6 points)

a. By each ???, write what value comes from the Python expression:

>>> 1 + 2 * 3 - 4 - 1
???


>>> 26 % 5
???


>>> s = 'Python'
>>> s[:2]
???


>>> s[3:]
???


b. What 3 numbers print when the caller() function runs? This code runs without error.

def foo(a, b):
    a = 2 * a
    return a + b


def caller():
    a = 3
    b = 4
    c = foo(b, 1)
    print(a, b, c)
# What 3 numbers print ???

2. Bit (6 points)

For this problem, the add_green() function is provided. The precondition for add_green() is that bit is facing a lone black block, a block with clear squares all around it. The add_green() function moves bit to the opposite side of the block, paints that square green, and finally moves bit back to the initial start square and facing direction. Assume that this function is correct and can be called from the function you implement below.

def add_green(bit):
    ...

Write code for the green_above() function, calling the add_green() function: Bit is facing the right side of the world. There are lone black blocks in the row above bit's path. Move bit forward until blocked. For each moved-to square, if there is a block immediately above bit, paint the square on the opposite side of the block green.

Bit before and after:
alt: bit before and after green_row

def green_above(filename):
    bit = Bit(filename)
    # Your code here




3. Image (20 points)

Given an image filename and ints a and b which are 1 or more. Produce a new out image with three features: (1) at the top, a blank stripe 10 pixels high forming the top of the output image. (2) a vertically flipped copy of the original image, with a blank margin area a pixels wide on both sides. (3) At the bottom of the output, a yellow stripe b pixels high running across the width of the output. Add your code in the 3 indicated places to complete the function.

alt: blank stripe, flipped image, yellow rect

1. Write one line of code to create the out image.

2. Almost all of the code to write the flipped image is provided — looping over the original image, writing the colors at each "pixel" to "pixel_out". Write the one line of code which sets the "pixel_out" variable used by the later lines in this loop.

3. Write the loops and other needed code to produce the yellow rectangle. These are separate loops, not nested inside the earlier loops. You can make a white pixel yellow by setting its blue value to 0. We'll assume there is a return out as the last line.

def do_image(filename, a, b):
    image = SimpleImage(filename)
    #### 1. Create "out" image (Write one-line below)


    # Write flipped image to out
    for y in range(image.height):
        for x in range(image.width):
            pixel = image.get_pixel(x, y)
            #### 2. Set "pixel_out" (Write one-line below


            pixel_out.red = pixel.red
            pixel_out.green = pixel.green
            pixel_out.blue = pixel.blue

    #### 3. Fill yellow rectangle
    # (Your code here)

4. Strings (24 points)

a. Given a string s, return a new string made of all the chars from s which are 'd' or 'o' or 'g' (not case sensitive).

'xdxggy' -> 'dgg'  |  def doggy(s):
'DOggy' -> 'DOgg'  |
'abdGxo' -> 'dGo'  |
'xyz' -> ''        |







b. Given a string s. Find the first '@' in s. If there is a digit 3 chars after the '@', such as the '6' in 'x@106', then return the numeric value of that digit doubled. If there is no '@' or no digit, then return -1. Use the s.find() function.

'x@106x'  -> 12  |  def digit2x(s):
'xx@ab51' -> 10  |
'x@abx' -> -1    |
'abc@'  -> -1    |
'abc'   -> -1    |







c. Given a string s, find the first double left-bracket '[[' in s, and the first double right-bracket ']]'. If the double left-brackets are before the double right-brackets, then return a new string where the text between the brackets is replaced by the string 'NOPE'. If the left or right double brackets are missing or they are in the wrong order, then return the empty string. Do not use loops. Use s.find() and slices.

'xx[[hi there!]]zzz'  ->  |  def nope(s):
  'xx[[NOPE]]zzz'         |
'a[[]]b'  -> 'a[[NOPE]]b' |
'xx[[abc'  -> ''          |
'xx]]abc'  -> ''          |
'x]]ab[['  -> ''          |






5. Grid Squirrel-Magic (24 points)

This problem has two parts, and you can get full credit for each part regardless of what you did on the other part. We have a Grid representing Stanford campus and every square is either a squirrel 's', a tree 't', an acorn 'a', or empty None. The function squirrel_magic(grid, x, y, n) will be called with an in-bounds x, y, to attempt a squirrel "move" to make an acorn disappear. (a.) Write code for the move with these steps:

1. There must be a squirrel at x, y for the move to proceed.

2. If there is an acorn in the square immediately below the squirrel, then the acorn disappears and the move is over.

3. Otherwise, if there is an acorn in the row immediately above the squirrel and exactly n squares to the left of the squirrel, then the acorn disappears and the move is over. The parameter n will be an int value 1 or greater.

In all cases, the function should return the grid (changed or not) when done.

def squirrel_magic(grid, x, y, n):













(b.) Write one Doctest for a call to squirrel_magic() where an acorn disappears. As a reminder of the syntax, here is a working Doctest from the movie scroll_left() lecture example which shifts each letter one square to the left.

>>> grid = Grid.build([['a', 'b', 'c'], ['d', None, None]])
>>> scroll_left(grid)
[['b', 'c', None], [None, None, None]]
# squirrel_magic() Doctests:


6. Encryption (20 points)

This problem uses an encryption scheme somewhat like the homework. As a simplification, assume the char to encrypt is not uppercase. The encryption uses 3 lists of lowercase chars: source1, source2, and slug (the example shows the alphabetic chars 'a'-'d' as a simplification):

source1 = ['a', 'b', 'c', 'd']
source2 = ['d', 'c', 'b', 'a']
slug    = ['c', 'd', 'a', 'b']
indexes     0    1    2    3

Write code to encrypt a char with the following algorithm:

1. To use the slug, the char (ch) must be in both source1 and source2. If it is not in both lists, its encryption is simply the char itself, e.g. '$' -> '$'

2. Consider the index of ch in both source1 and source2. If the index in source1 is smaller or equal to the index in source2, then the encryption will use the index in source1: the encryption is the slug char at the source1 index. e.g. 'b' -> 'd' and 'a' -> 'c'

3. Otherwise the index in source2 is the smaller index. In that case, the encryption is the slug char at the source2 index, converted to uppercase, e.g. 'd' -> 'C' and 'c' -> 'D'

def encrypt_char(source1, source2, slug, ch):
    # Your code here
    

Solutions


#### 1 Short Answer

2
1
Py
hon

3 4 9


#### 2 Bit

# key idea: using helper properly
# to add green to each block

def green_row(filename):
    bit = Bit(filename)
    while bit.front_clear():
        bit.move()
        if not bit.left_clear():  # detect block
            bit.left()      # pre-fn
            add_green(bit)  # call helper
            bit.right()     # post-fn


#### 3 Image

# (1) out image
out = SimpleImage.blank(image.width + 2 * a, image.height + 10 + b)

# (2) vertical flipped image
pixel_out = out.get_pixel(a + x, 10 + image.height - 1 - y)
# or
pixel_out = out.get_pixel(a + x, out.height - b - 1 - y)

# (3) yellow rectangle
for y in range(b)
    for x in range(out.width): # or (image.width + 2 * a)
        pixel_yellow = out.get_pixel(x, 10 + image.height + y)
        pixel_yellow.blue = 0


#### 4 Strings

def doggy(s):
    result = ''
    for ch in s:  # for/i/range also fine
        low = ch.lower() 
        if low == 'd' or low == 'o' or low == 'g':
            result += ch
    return result


def digit2x(s):
    at = s.find('@')
    if at == -1:
        return -1
    if at + 3 < len(s) and s[at + 3].isdigit():
        return int(s[at + 3]) * 2
    return -1


def nope(s):
    left = s.find('[[')
    right = s.find(']]')
    if left == -1 or right == -1:
        return ''
    # Right before left = wrong order
    if right < left:
        return ''
    
    # Slice for each side, 'NOPE' in the middle!
    return s[:left + 2] + 'NOPE' + s[right:]



#### 5 Grid


def squirrel_magic(grid, x, y, n):
    if grid.get(x, y) != 's':
        return grid

    # Acorn below
    if grid.in_bounds(x, y + 1) and grid.get(x, y + 1) == 'a':
        grid.set(x, y + 1, None)
        return grid

    # Acorn - up and to the left
    if grid.in_bounds(x - n, y - 1) and grid.get(x - n, y - 1) == 'a':
        grid.set(x - n, y - 1, None)
        return grid
    
    # No acorn
    return grid


# squirrel first row, acorn below it (smaller grid fine too)
>>> grid = Grid.build([[None, 's', None], [None, 'a', None]]);
>>> super_snack(grid, 1, 0, 1)           # Squirrel at 1,0
[[None, 's', None], [None, None, None]]  # Acorn gone


#### 6 Encryption

def encrypt_char(source1, source2, slug, ch):
    if ch in source1 and ch in source2:
        # Index numbers in each source
        i1 = source1.index(ch)
        i2 = source2.index(ch)
        # source1 index smaller -> use it
        if i1 <= i2:
            return slug[i1]
        # Otherwise the other index, uppercased
        return slug[i2].upper()
    return ch