Section 2. Variables & Control Flow


Brahm Capoor, Juliette Woodrow, Parth Sarin, Kara Eng & Tori Qiu

Here is the starter code:

Online IDE Using Ed

Tracing

The following is code for an interactive console program that performs a type of calculation that is probably familiar. Examing the code. What is the role of the SENTINEL constant? How do each of the four variables-a, b, x and y-change over time? Overall, what common task does this program do?

"""
File: mystery_calculation.py 
----------------------------
It's your job to figure out what this program does!
"""

SENTINEL = -1

def main():
    a = int(input("Enter a value for a: "))
    b = int(input("Enter a value for b: "))
    x = int(input("Enter a value for x: "))
    while x != SENTINEL:
        y = a * x + b
        print("Result for x = " + str(x) + " is " + str(y))
        x = int(input("Enter a value for x: "))
        

if __name__ == "__main__":
    main()      

This program calculates output (y) values for input (x) values along a line with slope a and intercept b. First, it prompts the user for a slope a and intercept term b (remember that a line has an equation of the form y = ax + b). Then, the program prompts the user for x values until the user enters the SENTINEL (the value of which is specified using a named constant). For each entered number, it prints the y value that corresponds to the user's entered x value according to y = ax + b. The values for a and b do not change after the user initially enters them, while x and y change with each iteration of the while loop.

Here is a sample run of the program, with SENTINEL = -1 (user input is italicized).

Enter a value for a: 2
Enter a value for b: 4
Enter a value for x: 5
Result for x = 5 is 14 
Enter a value for x: 1
Result for x = 1 is 6
Enter a value for x: -1         

The program works properly regardless of the value of SENTINEL.


What's That in Dog Years?

Everyone knows that our furry friends age at a different rate to humans. Write a program that takes asks the user for a human age (expressed as a whole number) and prints the equivalent dog age using the fact that there are seven dog years per human year. Consider defining a constant DOG_YEARS_PER_HUMAN_YEAR. You can assume the user types in an integer age, but not necessarily that the integer is positive. If it isn't, print an error message.

Your program should continuously ask the user for human ages until the user types 0, at which the program should end.

Here is an example of what one run of your program should look like (user input is [italicized]{style="font-style: italic;"}):

Enter an age in human years: -12
Sorry, please enter a positive number or 0 to exit
Enter an age in human years: 13
The age in dog years is 91
Enter an age in human years: 0          
SENTINEL = 0
DOG_YEARS_PER_HUMAN_YEAR = 7

def main():
    while True:
        user_input = int(input("Enter an age in human years: "))
        if user_input < 0:
            print("Sorry, please enter a positive number or 0 to exit")
        elif user_input == SENTINEL:
            break
        else:
            dog_age = user_input * DOG_YEARS_PER_HUMAN_YEAR
            print("The age in dog years is " + str(dog_age))

if __name__ == "__main__":
    main()          

Finding Factors

Implement a program that asks the user to enter an integer, then print out all the factors of the given number, one by one. Your function should check that the entered number is greater than 0. The program should keep asking for numbers until 0 is entered.

Here is an example of what one run of your program should look like (user input is [italicized]{style="font-style: italic;"}):

Your number: -10
Please input a positive number
Your number: 42
1
2
3
6
7
14
21
42
Your number: 53
1
53
Your number: 0                
SENTINEL = 0

def main():
    while True:
        user_in = int(input("Enter a positive number to get its factors, or enter 0 to stop:"))
        if user_in < 0: 
            print("Please input a positive number")
        elif user_in == SENTINEL:
            break
        else:
            for i in range(1, user_in+1): 
                if user_in % i == 0: 
                    print(str(i))

Rock, Paper, Scissors

In 1997, a computer named Deep Blue beat world chess champion Gary Kasparov at a game of chess. In 2020, IBM has findally gained the confidence to expand its repertoire of human vs. computer games and enlisted you to write a program that allows a human player and computer to spar over a game of Rock Paper Scissors.

Each game consists of 5 rounds of Rock, Paper, Scissors. Each round consists of you--the user--choosing whether to play Rock, Paper or Scissors, and the computer doing the same. In this game, the user will type 1 if they wish to play Rock, 2 if they wish to play Paper and 3 if they wish to play Scissors.

In each round, you should follow the following steps:

  1. Prompt the user to enter their move (you can assume they type '1', '2' or '3'.)
  2. Randomly choose either rock, paper, or scissors as the computer's move using the random module (remember, calling `random.randint(a, b)` will give you back an integer between a and b, inclusive).
  3. Determine who wins the round. As a reminder, rock beats scissors, scissors beats paper, and paper, somewhat inexplicably, beats rock.
  4. Print a message reporting whether the human player won or lost and which moves each player made.

At the end of the program, you should print a message saying how many rounds you won.

Here is an example of what one run of your program should look like (user input is [italicized]{style="font-style: italic;"}):

Your move (1, 2, or 3): 1
It's a tie!
Your move (1, 2, or 3): 3
You Win! Scissors cuts paper
Your move (1, 2, or 3): 2
You Lose! Scissors cuts paper
Your move (1, 2, or 3): 2
It's a tie!
Your move (1, 2, or 3): 1
You Win! Rock crushes scissors
You won 2 rounds!       
import random 

NUM_ROUNDS = 5

def main():
    num_human_wins = 0

    for i in range(NUM_ROUNDS):
        user_choice = int(input("Your move (1, 2, or 3): "))
        computer_choice = random.randint(1, 3)
        
        if computer_choice == user_choice:
            print("It's a tie!")
        else:
            if user_choice == 1:
                if computer_choice == 3:
                    print("You Win! Rock crushes scissors")
                    num_human_wins += 1
                else:
                    print("You Lose! Paper covers rock")

            if user_choice == 3:
                if computer_choice == 2:
                    print("You Win! Scissors cuts paper")
                    num_human_wins += 1
                else:
                    print("You Lose! Rock crushes scissors")
            
            if user_choice == 2:
                if computer_choice == 1:
                    print("You Win! Paper covers rock")
                    num_human_wins += 1
                else:
                    print("You Lose! Scissors cuts paper")

    print("You won " + str(num_human_wins) + " rounds!")

if __name__ == "__main__":
    main()

Estimating (π)

Write a function, estimate_pi, that estimates the value of \(\pi\) using Python!

π is a mathematical constant that comes from geometric properties of a circle. Specifically, a circle that has radius r will have area (π r2) and circumference (2 π r). π's value is roughly 3.141… and in this problem, we'll get pretty close to that value (we're shooting for 2 decimal places of accuracy)!

One nice way to estimate π is using the random module. Imagine a 2x2 square drawn on a 2D plane, centered at the origin. We can draw a circle inside this square, with radius 1, centered at the origin. So far, our drawing looks like this:

A circle drawn inside a square. Both shapes are centered at (0, 0) on
the coordinate plane. The circle has radius 1 and the square measures 2
x
2.

The area of the square is 2 x 2 = 4 and the area of the circle is π x 12 = π. The circle takes up a large fraction of the square's area: to be precise, the ratio of the circle's area to the square's area is π/4.

One way to think about this is that if you pick a bunch of points, randomly, inside the square, the fraction of those points that will lie inside the circle is π/4.

The same drawing as above with 100,000 randomly chosen points
displayed in the
square.

The same drawing as above where the randomly chosen points outside the
circle are not
shown.

Your function, estimate_pi should randomly pick 100000 points inside the square (remember that it's good programming style to use constants where appropriate; consider defining a constant NUM_POINTS = 100000).

You can pick a random point in the square by picking a random x coordinate and a random y coordinate (both of which should be real numbers between -1 and 1). Count the number of points that are inside the circle and use that number to calculate the fraction of points that landed inside the circle. That number is approximately π/4, so multiply your ratio by 4 and print out that value as our approximation of π. How close did you get? What happens when you change NUM_POINTS?

Hint: A point (x, y) is inside the circle of radius 1, centered at the origin, if (x2 + y2 <= 1).

Note: Your function will produce a number, not the graphs that you see in this writeup. However, these graphs were actually generated using Python and you'll be able to make images like these by the end of the quarter!

"""
File: solution.py
-----------------

Solutions for the Estimate Pi problem on CS 106A, Section 2, Spring 2020.
"""
import random

NUM_POINTS = 100_000

def estimate_pi():
    """
    Estimates the value of π by randomly choosing points in the square and 
    calculating the percentage of points that lie in the circle. Prints out
    the estimated value of π.
    """
    num_in_circle = 0 # Counter variable

    for i in range(NUM_POINTS):
        """
        Pick a random point in the square. To do this, we need to pick a random
        x coordinate and random y coordinate, each in the interval [-1, 1].
        """
        x = random.uniform(-1, 1)
        y = random.uniform(-1, 1)

        """
        The formula for a circle is x^2 + y^2 = r^2. In this case, since the
        circle has radius 1, it can be described by x^2 + y^2 = 1. Points that
        lie *inside* the circle have x^2 + y^2 <= 1.
        """
        if x ** 2 + y ** 2 <= 1:
            # (x, y) is inside the circle
            num_in_circle += 1

    """
    The area of the square is 4, the area of the circle is π. Thus, the
    percentage of points in the circle is approximately π / 4.
    """
    percent_in_circle = num_in_circle / NUM_POINTS # ~ π / 4
    π = percent_in_circle * 4

    print("π is roughly " + str(π))


if __name__ == '__main__':
    # random.seed(8675309)
    estimate_pi() # => 3.14224 (when seeded with Jenny's phone #)

Part B: Parameters and Return Values

Once we've covered parameters and return values in class, you can re-write your solution to this problem to get some practice with for the diagnostic and calculate a more accurate value of π!

Re-write estimate_pi so it returns the value of π that it computes and write a main function that calls estimate_pi 100 times, in a loop. The main function should average the values that estimate_pi returns and print out "The average of those estimations is: {avg_estimation}" where {avg_estimation} is replaced by the average of values returned by estimate_pi.

How close is your estimation? Our estimation was accurate to four decimal places!

import random

NUM_POINTS = 100_000
NUM_ESTIMATES = 100

def estimate_pi():
    """
    Estimates the value of π by randomly choosing points in the square and 
    calculating the percentage of points that lie in the circle. Prints out
    the estimated value of π.

    Returns
    -------
    float -- The estimated value of π.
    """
    num_in_circle = 0 # Counter variable

    for i in range(NUM_POINTS):
        """
        Pick a random point in the square. To do this, we need to pick a random
        x coordinate and random y coordinate, each in the interval [-1, 1].
        """
        x = random.uniform(-1, 1)
        y = random.uniform(-1, 1)

        """
        The formula for a circle is x^2 + y^2 = r^2. In this case, since the
        circle has radius 1, it can be described by x^2 + y^2 = 1. Points that
        lie *inside* the circle have x^2 + y^2 <= 1.
        """
        if x ** 2 + y ** 2 <= 1:
            # (x, y) is inside the circle
            num_in_circle += 1

    """
    The area of the square is 4, the area of the circle is π. Thus, the
    percentage of points in the circle is approximately π / 4.
    """
    percent_in_circle = num_in_circle / NUM_POINTS # ~ π / 4
    π = percent_in_circle * 4

    print("π is roughly " + str(π))
    return π

def main():
    """
    Calls estimate_pi() 100 times and averages the estimates. Then, prints out
    that value.
    """
    running_total = 0

    for i in range(NUM_ESTIMATES):
        running_total += estimate_pi()

    average_value = running_total / 100

    print("The average of those estimations is: " + str(running_total))


if __name__ == '__main__':
    # random.seed(8675309)
    main() # => 3.142082... (when seeded with Jenny's phone #)