One of the main activities this week will be building a pyramid with the
picture module. This exercise will get you through some of the basic math involved. Here are some examples of the pyramids you’ll be drawing. All three images use a 400 x 400 pixel canvas. The first pyramid has 3 bricks in the base, the second has 17, and the last one has 123. The input of your program will be the number
n of bricks in height and the
width of the canvas.
Notice that the pyramid doesn’t necessarily fill the entire canvas to the right and to the top; if the canvas width is not evenly divisible by the number of bricks, then there will be extra blank space.
Why is there so much blank space in the third example? Seems like you could fit lots of extra bricks both to the right and up top.
For each of the following questions, work on the question yourself for a few minutes, then discuss with your partner.
Suppose you have a canvas that is 7 units wide and 7 units high. It may be useful to imagine that the canvas is made of graph paper and is 7 squares wide by 7 squares tall. Suppose that you want a pyramid that is 3 bricks tall (that is,
n = 3). You’d like the bricks to be as large as possible, but they should all be of the same size, and they must use a whole number of units (i.e., squares on our imaginary graph paper). What is the side-length of each brick? That is, how many units wide and tall is each brick?
How many bricks are:
More generally, suppose your canvas is
size wide and
size high, and that your pyramid is
n bricks tall. What is the side-length
s of a brick, in terms of
n? Use Python’s integer division if necessary.
If our pyramid is
n bricks tall, then it is also
n bricks wide at its base. Thus we have that
How many bricks does row
r contain, in terms of
If our pyramid is
n bricks tall and each brick has side-length
s, then the first brick of row 0 starts at x-coordinate 0. The first brick of row 1 starts at x-coordinate
s//2, and the first brick of row 2 starts at x-coordinate
What is the x-coordinate of the first brick of row
r, in terms of
If our pyramid is
n bricks tall, our canvas is
size wide and tall, and each brick has side-length
s, then the bricks of row 0 have y-coordinate
size - s. The bricks of row 1 have y-coordinate
size - (2*s), and the bricks of row 2 have y-coordinate
size - (3*s).
What is the y-coordinate of row
r, in terms of
To get you started with your pyramid, check out
pyramid.py. It’ll give you some starter code for getting your canvas set up, with reminders of what things do. There is a ton more information of different commands (which you can and should use in Part 3 of this week’s lab) in the
picture module documentation.
picture.py contains code for filling the canvas with the color
"skyblue". You’ll draw your pyramid over top of it. Your job for now is just to draw the bottom-left square of the pyramid. You’ll draw the rest as part of the main lab.
Put the following code where the comments say
brick-drawing code goes here:
The canvas is 400 x 400 pixels, so this should draw a 40 x 40 brick in the bottom left corner, right? Run the code and see what happens. You should see… nothing. Read the documentation for
picture.draw_filled_square and see if you and your partner can figure out what went wrong.
What is the correct code to draw a 40 x 40 rectangle in the bottom left?
In a later part of this lab, you’ll be implementing a Monte Carlo method—that is, you’ll be using random number generation to estimate quantities that might be difficult to derive using formulas. This warmup gives you a trial run: we’ll look at a couple of programs which estimate what fraction of integers between 1 and 1,000,000,000 are divisible by 5 but not by 3. Note that you could compute this manually with a very long
for loop, but Monte Carlo sampling will be faster. (There’s also an even simpler way of figuring this out, but that’s for you to think about.)
montedivides3.py, you’ll find three programs which all follow the same basic algorithm: (1) repeatedly generate random numbers between 1 and 1,000,000,000; (2) check if each number is divisible by 5 but not 3; and (3) count the number of times this property is satisfied, and divide by the number of random numbers overall. Unfortunately, each of these programs has a bug. This will be your chance to practice something called print debugging.
Let’s first make sure that we understand the principles of our Monte Carlo method. On your own, go through the integers from 1 to 25 (inclusive) and determine how many are divisible by 5 but not 3. Then check your results with your partner. Dividing the count by 25 will give you the fraction of integers between 1 and 25 that are divisible by 5 but not by 3. This fraction is also a ballpark estimate of what you can expect as output from the Monte Carlo programs.
montedivides1.py program several times. Does the program return the same fraction every time it’s executed? Discuss with your partner why the value is changing.
montedivides1.py one more time. It should give you a fraction that’s a bit lower than you expected based on your previous estimate (from Step 1). If you see why already, nice job! But it might not be so obvious, and it definitely won’t be obvious in more complicated programs. One way of helping you understand what’s going on is to temporarily add
print() statements to track the values of the random integer
x and the number of divisible integers
count. You want to make sure
count is incremented correctly based on each value of
Add the line
print(x, count) to the end, but still inside, of the
for loop. This
print() statement will yield a lot of output, but you should be able to predict generally what it will look like. Before running the new program, discuss with your partner what you would expect to be printed if the program was correct.
Now run the program. Is
count being incremented at the right times? Focus first on the numbers that a divisible by 5—these end in either a 0 or a 5. Now double-check whether these numbers are divisible by 3. If they are not divisible by 3,
count should increase. (You can use the Console to check the divisibility of some of the printed numbers). Talk with your partner to identify what is going wrong.
Now do the same thing for
montedivides2.py. First identify the variables of interest. Then add a
print() statements and analyze the output. Then try to pinpoint the bug and fix it.
montedivides3.py. Oh no! This one one doesn’t even run. You should be getting an error message like the following:
File "montedivides3.py", line 11 is_good = (x % 3 != 0) and (x % 5 == 0) ^ SyntaxError: invalid syntax
As you may have heard in class, the Python interpreter’s approach to catching errors is generally as follows: it reads your code under the assumption that it is correct for as long as possible. Only when it has read enough that what you typed earlier could not possibly be correct under any circumstances will it crash, and it will tell you the line where it realized all hope was lost. That means that the real bug is often earlier in the code.
Take a look at the line highlighted by the error message: it’s a little complex, but it’s correct. That means the error is earlier. With your partner, find it and fix it.