Treasure Transcript
[00:00:00]
Hey there! I’m going to walk you through how you can use decomposition to
break down complex problems. First we’re going to start with the treasure
problem, treasure.py. Here’s what it is. So Bit has to reach the cave.
Start visual description. Screen reads: Treasure!
treasure.py
To reach the cave, Bit must follow these instructions:
• Turn right on blue squares.
• Turn left on red squares.
Once in the cave, navigate through the passage to the treasure (red square).
Paint the path followed green.
End visual description.
[00:00:18]
First, he has to follow the treasure map, which says turn right on blue squares
and left on red squares. Following those instructions, you should reach the cave.
And then at that point he needs to navigate through the cave to find the
treasure, the red square. And we want to paint everything green as we go.
[00:00:35]
So the first thing that you see here is a natural split in the problem right. There’s
actually two sub problems: get to the cave, then navigate the cave. And we can
start our program with that simple split and that’s the level we want to start on.
We don’t want to get right into while loops, and if statements, and conditions.
[00:01:04]
We want to talk about the pieces of the problem and to find those pieces and
then slowly figure out what the what those pieces mean specifically. So if we’re
going to say get to cave and navigate cave are it’s two big pieces then we just
need to understand just a little bit where those pieces fit.
[00:01:20]
So let’s draw this out. Somewhere, we’ve got a cave. An entrance to a cave right.
And at some point you know Bit starts out here somewhere and he’s gonna
wander around and then he’s going to arrive at the cave.
[00:01:35]
So we need to decide: do we think that Bit should arrive here? Put that as
position one right. Or do we think that Bit should arrive here? I’ll call that
position two. Or maybe further in like you know down here, we could say he
arrives here where he’s blocked in the front, you know. That’s position three.
Various places we could think about.
[00:01:58]
And as we’re trying to decide where the get to cave function ends, we’re thinking
a little bit about what specific situation will Bit be in that tells us we’ve arrived?
And position one sitting out in the open doesn’t have a lot to tell us that we’ve
arrived in the cave.
[00:02:21]
Whereas at position two we have a clear, you know, blocked on right and left
condition that doesn’t happen at all while Bit’s out wandering around. This is the
first time left and right are blocked so that seems promising.
[00:02:35]
You say well what about you know where he comes shooting straight in and then
eventually hits the wall and he’s blocked? That might work. You know, if we come
back and look at our map, here we see though that there are a number of
situations as he comes up or comes over or up that he’ll be blocked in front. And
maybe if we get the order of the turns right then that’ll work fine.
Start visual description. Instructor sketches out the thought process for the problem. End visual description.
[00:03:03]
But seeing as that one’s a little more nuanced in how we do it versus just
checking whether it’s blocked on the left and on the right it is unique, let’s just go
with position two right. That makes a clear obvious end to get to the cave, right.
So Bit starts out here. We’ve got some cave and Bit’s going to end right there.
[00:03:31]
And that’s when you get to the cave stop. So with just this little piece, right, we
can already start to break things down. Let’s come over to PyCharm and do just
that much.
[00:03:40]
Get to cave, bit. And then we’ll say navigate cave, bit. Right. If get to Cave works
and does the right thing and navigate cave works and does the right thing, then
go works. Right? And this is the concept of abstraction. The idea of defining new
words and, assuming those words are correct, putting those words together to
solve a bigger problem. We’ll worry about the details of these individual words or
functions in a minute.
[00:04:19]
But when you’re breaking problems down, this is the way you want to do it. First,
create an abstraction around the bigger pieces and figure out how those pieces
fit together and work your way in.
[00:04:33]
We know a little bit more details around get to cave, right? So I’m going to create
that function and I’m going to add a doc string here. Bit ends just inside the
mouth of the cave, blocked on the left and the right. That was what we decided
is how get to cave ends. We could also mention here Bit turns left on red and
right on blue.
Start visual description. Instructor types the following code:
@Bit.worlds(‘treasure’)
def go(bit):
get_to_cave (bit)
navigate cave(bit)
End visual description.
[00:05:11]
Bit paints every square green. Right, so this little doc string helps to capture the
contract. And it doesn’t mean anything to the computer but it helps me as a
human reading this and understanding oh what does get cave, what is it
supposed to do? It’s supposed to do this. Navigate cave we can now create a little
function and add a little doc string right. Bit starts just inside the mouth of the
cave.
[00:05:42]
Bit ends on the red square. Bit paints every square green. And that helps us know
what navigate cave is supposed to do. So now that we’ve figured this out, let’s
implement just the first part get to cave.
[00:06:02]
And in fact here, let’s just put a little bit.snapshot right here Arrived at cave, with
some enthusiasm, that we can jump to see, are we where we thought we would
be when we get to cave finishes? And if that’s correct then we’ll move on to the
navigating cave part. So up here. Bit ends inside the mouth of the cave, blocked
on left and right, great.
[00:06:29]
Let’s take another look at our picture and look at this process right. We’ve seen
this pattern before in the homework and in the labs where we just move Bit
towards that final goal and we respond to these individual events with a turn.
[00:06:47]
So we can follow a similar pattern. We can say while not in cave, bit. bit dot
move, bit dot paint green, And what’s the other idea that we need? Maybe turn
bit. Right, so if we’re not in the cave, we’re going to move, we’re going to paint,
and maybe we’re going to turn. And again we’re doing the same thing we just did
in the go function.
[00:07:16]
Instead of jumping all the way down into the details, we’re just talking about the
high level ideas. Right? If this while loop keeps going until Bit’s in the cave and Bit
moves, paints, and turns when necessary it’ll work. So let’s state that here and
then go worry about those details later.
[00:07:36]
The other question is: does it matter which order we do the moves, the paints,
and the turns in? So we can come back to our picture here and think through,
right, this idea. If I move and then I paint and maybe turn it’ll be on a green
square. Right, if I move and then paint and now it’s green I’m not going to know
to turn. That’s going to be a problem, right? So just simulating for a moment this
idea on paper of what would happen with our instructions already reveals to us
that the order does matter just a little bit.
[00:08:10]
We need to handle the turns before we paint over the square. So either we need
to paint, then move, and then turn or we need to turn before we paint, right. So
just switching this order here right where we paint a square green, then we move
off of that square, and now we handle a turn. Let’s see if that works right. So if
I’m right here, I paint this green, I move, I don’t have to turn, I paint the square
green, I move, and then I turn, and then I paint it green but I’m already faced in
the right direction and I move.
[00:08:44]
That looks like that’ll work. So let’s try that and if we got it wrong, we’ll see it in
the steps and we’ll go back and fix it. So now we just need to define what does it
mean to be in the cave? So I’m going to just bring up create a new function, in
cave, great. And we’ve already defined this before.
Start visual description. Instructor types the following code:
def get_to_cave (bit):
Bit ends just inside the mouth of the cave, blocked on the left and the right
Bit turns left on red and right on blue. Bit points every square green.
while not in_cave(bit):
bit.paint(‘green’)
bit.move()
maybe turn(bit)
End visual description.
[00:09:03]
It’s got to be blocked on the left and the right, that’s what it means. Now, in cave
here, because it’s being used with not and being used with while it’s supposed to
return a Boolean value. A true or a false. Right, so that’s what we’re going to
return from here. Return bit dot left clear, except we don’t want left clear we
want it blocked, right?
[00:09:28]
So if it’s not left clear and not bit dot right clear then we’re in the cave. That’s
what that means. In the cave means that the left is not clear and the right is not
clear. It’s that simple. Note these parentheses right here, I’ve noticed a few
students will leave this off and just put the parentheses at the end. That won’t
work. It will run but it won’t do what you expect it to do.
[00:09:51]
You want the parentheses after every function that you’re trying to call, right. So
left clear needs its parentheses. Right clear needs its parentheses. So there’s in
cave. How about maybe turn? Let’s create a function maybe turn. And in this
case we’re just trying to capture this simple little concept: turn right on blue
squares, turn left on red squares. So, if bit dot is red bit dot left. Elif bit dot is blue
bit dot right.
[00:10:30]
Now, does it matter if it’s elif in this situation versus if or versus else? Right?
What if we had instead said, you know, else bit dot right? And not that? Well, if it
was red, we’d turn. Otherwise we’d turn the other way. So every clear square
would turn into a turn.
[00:10:55]
We don’t want that. We only want it to check that other turn if it’s not red. And
if, you know, we wanted to do the other turn if it’s blue, actually is blue.
[00:11:07]
Right, so we’re going to use an elif to say okay if it wasn’t red, well then if it’s blue
do this, right. Now in this specific situation, we could have done an if, right. If it’s
red left, if it’s blue right. Because these two conditions of being red or blue are
already mutually exclusive.
[00:11:27]
But because we really only intend for this to get checked if that wasn’t, elif is a
good example. The subtle difference now is only doing it like this, it will call is red
and it will call is blue every time. Putting an elif, it will only call is blue if it wasn’t
already red. So just one less function call most of the time.
Start visual description. Instructor types the following code:
def in cave (bit):
return not bit.left_clear() and not bit.right_clear()
def maybe_turn(bit):
if bit.is_red():
bit.left()
elif bit.is_blue():
bit.right()
End visual description.
[00:11:52]
Alright, that’s maybe turn. So here we’ve defined all the pieces that get cave
needs. Let’s run it and see if we actually made it to the cave. Give it a second to
load. There it is. Alright. Let’s jump to our snapshot. And there! Right where we
thought we would be at the end of arriving at the cave we’ve arrived.
[00:12:24]
Excellent! Half the problem is already solved. So now we just need to navigate
the cave to the end. Same problem as before, ultimately, right? We’re moving
along towards a goal. In this case the goal is that…well let’s not even worry about
the goal, right?
Start visual description. Instructor runs the code and a window appears with the grid to show the output of the current code. End visual description.
[00:12:41]
Let’s just look at this high level piece. We’ve got to get to some finish point, the
treasure, we’ll say we’ll call it found treasure. And we maybe have to turn and we
have to move and paint while we do it. So let’s just do that. While not at
treasure, bit. bit dot move bit dot paint green and maybe turn.
[00:13:07]
Now, if I try to say maybe turn, maybe turn is going to…is already defined and
that looks at reds and greens. This maybe turn has to do with corners, right,
being blocked and making a decision. So instead, let’s maybe call it navigate turn
for example. They’ll have to be different names because they mean slightly
different ideas, but the purpose is the same within this little block, right. If we’re
not at the treasure, we got to move, we got to paint, we’ve got a turn.
[00:13:42]
What does it mean to be at the treasure? Well let’s give it a meaning. Create a
function at treasure bit return bit dot is red. That means we’re at the treasure.
Okay, great. Now I come down here, what does it mean to navigate the turn?
Well, if you come look at our picture here, right. If we’re moving along we have
this idea of if we’re blocked we have to turn.
Start visual description. Instructor types the following code:
def navigate_cave (bit):
Bit starts just inside the mouth of the cave.
Bit ends on the red square.
Bit points every square green.
while not at treasure(bit):
bit.move()
bit.paint(‘green’)
navigate turn(bit)
End visual description.
[00:14:14]
We also notice… so we could we could check and see you know if the front is
blocked we got to look left versus right and figure out which way to go. Okay,
that’s an option. The other idea here is just simply looking if at any point in time
one of these turns is available we just take it.
[00:14:33]
We don’t have to check whether the front is blocked we just have to check if
right is clear versus left is clear. So let’s try that. If bit dot right clear, bit dot right.
Elif bit dot left clear bit dot left. Now, like before, does it matter if it’s an else uh
versus an elif? Right, so if it’s just an else, if the right’s not clear it’s going to end
up turning left that’s no good.
[00:15:05]
We want to check if the left is clear. What if it was just simply an if? Well if the
right clear, if a turn to the right now makes the left open, doing it in sequence, it
will now turn back. That’s no good, right? We only want to check on the left side
if the right side wasn’t clear. So we do an elif.
[00:15:29]
So now they’re mutually exclusive. This will run or that will run. Never both.
That’s what we want. Let’s navigate a turn. Now we’re great. Last thing to check,
does it matter the order that we move, we paint, and we navigate? Well, just like
before, you know, when we were looking here, if we had recognized if I move and
then paint I’m going to have a problem.
Start visual description. Instructor types the following code:
def navigate turn(bit): if bit.right_clear():
bit.right()
elif bit.left_clear():
bit.left()
End visual description.
[00:15:52]
Well in here we’re looking for red, and if I end up moving on to the red and then
painting it and then checking to see if I’m red, I’m never going to find a red, right.
So we want to make sure that my at treasure question finds a red square and if I
paint green first that’s not going to work, right. So let’s just do what we did on
the last. Let’s just take this line cut, paste and paint to green, now move, now
figure out if we need to turn. I’m going to go from there. Let’s see how that’s
working.
[00:16:34]
Treasure. Really close right? What’s missing? The last square didn’t get painted.
And that’s just the move paint problem right. You can either move then paint or
paint then move. And when you do that, at some end you’re going to have to
have that extra paint. So let’s just add an extra paint. Don’t overthink this. Bit dot
paint green.
Start visual description. Instructor adds bit.paint(‘green’) in go function. End visual description.
[00:17:00]
Goal! Okay, we found the treasure. So the key ideas when you approach a
complex problem, train yourself to start with the big ideas, right. Functions for
those big ideas. In some sense, solve the bigger problem just using these big idea
words that you’ve come up with and then go figure out how to…what the actual
meanings of those big ideas are. Talk about how those big ideas fit together, like
we figured out where did get to cave actually end.
Start visual description. Instructor runs code and a window pops up with the output grid showing the correct output for the exercise. End visual description.
[00:17:37]
Work out the interfaces of those ideas and document them, right. Doc strings
that help make it clear where things are supposed to…what they’re supposed to
do. How they fit. Right, and then work your way down from the top. So I figured
out the top piece and you’ll have little red squiggles, right, for all these words you
just made up and haven’t defined. Then you go to define all those little functions.
[00:17:59]
And another function may, in itself, get to cave, you know, we had new ideas. In
cave, maybe turn. Again, taking a simpler problem but still a complex problem
and breaking it down into basic ideas.
[00:18:15]
And then going from there. You want the code that you write to be obvious,
right. Reading through this, while not in cave, paint green, move, and maybe turn
almost sounds obvious but that’s good. You want obvious code. You want the
intent and function of your code to be obvious.
[00:18:38]
You don’t want to spend time trying to interpret your code. Let the computer do
the interpreting. You want to just read it and as quickly as possible understand it
so that you can change it or fix it. Do whatever you need to do.
[00:18:51]
And so writing code that feels obvious is actually a good sign. That means that
you’ve captured the essence of the ideas and figured out how those ideas fit
together. It takes practice but it’s good practice and I invite you to do that
practice now. Maybe you can fit the whole bit problem into one function.
[00:19:09]
Don’t do that. Force yourself to start from the very beginning of the problem
with these bigger ideas and it will pay huge dividends later on as you learn to
program more and more and more complex programs. And thank you for
watching. Have fun with homework!