Today: insights about loops, if statement, ==, make-a-drawing, left_clear(), bit puzzles
"The computer does not have any insight, instead following the simple, mechanical instructions written by the programmer."
These are just words, and rather than try to explain them in different ways, I will hope that after you've written 10 Bit programs, you will understand in your bones what those words are trying to say.
> go-green
We had this example at the end lecture-1. Now we'll look at the details
The loop is a big increase in power, running lines many times. There are a few different types of loop. Here we will look at the while loop.
The while loop syntax is made of four parts: the word while, a test expression, a colon (:), and on the next line indented body lines to run.
while test-expression:
body lines
This diagram shows the sequence between the while loop test and the body:
There is not much to say about the body; it is just a series of lines to run. The test expression is more interesting, controlling the run of the loop.
An expression is a piece of code that runs as usual and also returns a value to that spot in the code. The front_clear() function, acts as an expression here. When called, the front_clear() function checks the square in front of bit, returning True if the square is clear for a move, or False otherwise. The values True and False are the special "boolean" values in Python, which we'll see in more detail later.
bit.front_clear() VisualizationA nice way to visualize the action of an expression is drawing a diagonal arrow through the expression code, showing the returned value coming out of the expression when it runs. Here is a drawing showing the boolean value coming out of the bit.front_clear() function call with bit not blocked and then blocked:
In Bit's world, the way forward is clear if the square in front of Bit is not blocked by the edge of the world or a black square (we'll see black squares shortly).
bit.front_clear() - In The While LoopHere again is the go-green loop:
while bit.front_clear():
bit.move()
bit.paint('green')
The while loop first checks the text expression bit.front_clear(), checking the square ahead and returning True if it is clear. If the test returns True, the loop goes in and runs the body from top to bottom. In this case, that moves bit forward one square. After the body, the loop always goes back to the top to check the test again. In this way, the loop runs forward through all the empty squares, until bit is on a square where the front is not clear, and the loop exits.
When the test is True, bit keeps going. We think of this as Test=Go. Later when we're writing code to advance bit through some other situation, we'll use Test = Go to think about the test.
> go-green
Run the go-green function, and watch it to see how the while loop works. In particular, use the "step" button to advance one line at a time as bit is on the next-to-last square and then the last square, seeing the test return True for all squares but the last.
Position bit a few squares from the end with the steps slider. Step to line 5 and stop. What line comes after line 5?
Step to line 3. What line comes next? It depends on where bit is actually. Run to the end to see both cases.
If you'd like to play around with some code which is similar to go green try writing the code for go-left first. The word "pass" is a placeholder in Python that does nothing. For exercises, replace the word "pass" with your code.
> go-left
The go-right exercise requires two loops - write one loop to go to the right corner. Write a second loop to go down. The first loop and the second loop should be at the same indentation, not one inside the other. Note that all these problems have a Show Solution button, so you can play with them without writing the code.
> go-right
move() then paint() in loop:
paint() then move() in loop:
Next example - use this to play with more features of loops. This code is complete.
> All-Blue
Often we have an English language description of what we want, and then we're thinking about the code to get that effect.
Description: Bit starts in the upper left square facing the right side of the world. Move bit forward until blocked. Paint every square bit occupies blue, including the first. When finished, turn bit to face downwards. (Demo - this code is complete. Shows the basic while-loop with front_clear() to move until blocked, and also taking an action before and after the loop.)
def all_blue(filename):
bit = Bit(filename)
bit.paint('blue') # 1 paint before the loop
while bit.front_clear():
bit.move()
bit.paint('blue')
bit.right() # Turn after the loop
Look at Case-3 and then Case-4. Turn off the Auto-Play option, so when you click Run, the world stays at the start, waiting for you to click Step. See how the while test behaves on Case-3 and then Case-4. For Case-4, the while-test is False the very first time! The result is, the loop body lines run zero times. This is fine actually. There were zero squares to move to, so looping zero times is perfect. This is an example of an "edge" case, the smallest possible input. It's nice that the while loop handles this edge case without any extra code.
Let's do something wrong. Change the loop body to have three moves instead of one, which is buggy code. The problem is, this code may move bit into a wall (a not-clear square).
...
while bit.front_clear():
bit.move()
bit.move()
bit.move()
bit.paint('blue')
bit.right()
With the correct code, the test checks the one square ahead, and the loop body moves one time, that square (e.g. the go-green loop). In this way, each move is preceded by a test and is 100% not going to fail. With three moves - for the second and third moves, we don't know that those squares are clear for a move.
Run the buggy code - bit reaches the right side, but keeps trying to move(). Moving into a wall or black square (i.e. not a clear square) is an error, resulting in an "exception" error message like this and the program halts at that line:
Exception: Bad move, front is not clear, in:all_blue line 6 (Case 1)
If you see an exception like that, your code is doing a move() when the front is not clear. You need to look at your code logic. Normally each move() has a front_clear() check before it, ensuring that the world has space for a move.
See the Debugging chapter in the Python Guide for more information Python Debugging
First rule of debugging - if there's an error - read the error message and see what line number it is talking about. The error messages can be cryptic, and in some cases they are not much help. But very often, the error message and the line number if mentions are excellent clues about the problem in the code.
The system tries to terminate the infinite loop code. Sometimes that does not work, and you have this infinite loop code running in your browser. In that case, you may need to close the experimental server tabs, or possibly restart your browser to kill off the wayward run.
For a loop to work, each run of the body needs to get closer to being done. With these bugs, bit is not making progress and so it never ends.
Here is a buggy All Blue - instead of move() have bit do right() and the left() - a sort of "waggle dance" like bees do in the hive. Notice that bit waggles but never moves forward. Bit can do this for eternity, and never leave that first square! Everyone has days like that.
while bit.front_clear():
bit.right() # waggle dance!
bit.left()
bit.paint('blue')
Run the code - you will see a message about a timeout, suggesting that this code does not seem to reach an end.
move vs. move()Python syntax requires the parenthesis () to make a function call. Unfortunately, if you omit them, easy enough to do, it is syntactically valid, but it does not call the function. So the code below looks right, but actually it's an infinite loop. Bit never moves. Try it yourself and see, then fix it.
while bit.front_clear():
bit.move
bit.paint('blue')
For extra practice, here are some more problems along the lines of All-Blue:
> red2
> go-back
> Bit Loop Section - problems using 1 or more loops
The while-loop is power. If-statement is control, controlling if lines are run or not
We'll run this simple bit of code first, so you can see what it does. Then we'll look at the bits of code that go into it.
Problem statement: Move bit forward until blocked. For each moved-to square, if the square is blue, change it to green. (Demo: this code is complete.).
For reference here is the code:
def change_blue(filename):
bit = Bit(filename)
while bit.front_clear():
bit.move()
if bit.get_color() == 'blue':
bit.paint('green')
The if statement syntax has four parts — the word "if", boolean test-expression, colon, indented body lines
if test-expression:
body lines
Or structurally we can think of it like this:
Here are the key lines of change-blue. We'll look at the individual parts to understand how it works.
....
if bit.get_color() == 'blue':
bit.paint('green')
....
Suppose bit is on a squared painted 'green', as shown below. Here is diagram - visualizing that bit.get_color() is called and it's like there's an arrow coming out of it with the returned 'green' to be used by the calling code.
== Compares Two Values - BooleanPutting this all together, we can read the change-blue code word by word to see what it does.
while bit.front_clear():
bit.move()
if bit.get_color() == 'blue':
bit.paint('green')
Q: What is the translation of the code in the loop into English?
A: For each moved-to square. If the square is blue, paint it green.
!= Not Equal Form== !=
if bit.get_color() == 'red':
# run this if color is 'red'
if bit.get_color() == None:
# run this if square is not painted
if bit.get_color() != 'red':
# run this if square is anything
# other than 'red'
# e.g. 'green' or 'blue' or None
This is a challenging problem for where we are at this point. Just watch (and you can do it in parallel) as I work through the problem. I want to show the thought process and steps to build and debug some complicated code.
Note: it is very common to move bit with an until-blocked loop. This problem is a little unusual, moving bit until hitting a particular color.
> Fix Tree
Problem: The squirrels have stolen the trunk from the tree! Bit starts facing the right side of the world. Move bit forward to find the red stump of the tree. Then Move bit up, painting the blank squares red, stopping on the first green square.
Before:
After:
We have art for these. No expense has been spared!
Don't do it just in your head - too much detail. Need to be able to work gradually and carefully.
Draw a typical "before" state - like what do we have to start. The drawings do not need to be fancy, but it's easier to work out the details on a sketch than in your head. Key lesson for today.
Look at the before state - what code can advance this?
You have the current state. What code could advance things to the next goal?
Q: Look at those bit positions. When do you want bit to move, and when not to move?
A: Want bit to move when the square is not red. What is the code for that?
Aside: if you were talking to a human, you would just say "make it look like this" and point to the goal. But not with a computer! You need to spell out the concrete steps to make the goal for the computer.
In this, case we're thinking while-loop. Draw in the various spots
bit will be in. What is a square where we do not want bit to move?
When bit is on the red square. Otherwise bit should move. What does that look
like in code?
Code looks like...
while bit.get_color() != 'red':
bit.move()
Ok, run it and see. Sometimes it will not do what we thought.
Ok what is the next goal? What would be code to advance to that?
I think a reasonable guess here is similar to the first loop, but painting red, like this
while bit.get_color() != 'green':
bit.move()
bit.paint('red')
OOPS, that code looks reasonable, but does not do what we wanted. The question is: what sequence of actions did the code take to get this output? That's a better question than "why doesn't this work".
Go back to our drawing. Think about the paint/red and the while loop, how they interact.
The problem is that we paint the square red, and then go back to the loop test that is looking for green, which we just obliterated with red. One solution: add an if-statement, only paint blank squares red, otherwise leave the square alone.
This is one solution, which works perfectly..
def fix_tree(filename):
bit = Bit(filename)
while bit.get_color() != 'red':
bit.move()
bit.left()
while bit.get_color() != 'green':
bit.move()
if bit.get_color() == None:
bit.paint('red')
Another solution is to change the loop body to do the paint and then the move - that works perfectly too. I slightly prefer the == None solution as it seems more obvious. Both are fine. Very often there are a few different ways to solve something. If we have a strong preference for one form, we will say so, but in this case both are fine.
Use a drawing to think through the details of what the code is doing. It's tempting to just stare at the code and hit the Run button a lot! That doesn't work! Why is the code doing that?
Or put another way, in office hours, a very common question from the TA would be: can you make a little drawing of the input here, and then look at your line 6, and think about what it's going to do. All the TA does is prompt you to make a drawing and then use that to think about your code.
bit.right_clear()
not
if bit.right_clear():
# run here if right is clear
if not bit.right_clear():
# run here if right is blocked,
# aka not-clear
On the experimental server, the Bit Puzzles section has many problems where bit is in some situation and needs to move around using Python code. We'll look at the Coyote problems, and there are many more problems for practice.
> reverse-coyote (while + right_clear)
Based on the RoadRunner cartoons, which have a real lightness to them. In the cartoons, the coyote is always running off the side of the cliff and hanging in space for a moment. For this problem, the coyote tries to run back onto the cliff before falling.
Before:
After:
Q: What is the while test for this? Remember that Test = Go. What is the test that is True when we want to move, and False when do not want to move?
A: bit.right_clear()
These are all in the "puzzle" section. Reminder: all lecture examples have a Show Solution button if you get stuck.
> climb
> encave (a little harder, like a homework prob)