Karel is to run east along first street, starting at first avenue, until he reaches a corner with a
beeper marking the end of the race. Along the way he may encounter hurdles (walls presenting an obstacle
across first street). He must jump each hurdle in order to continue moving east towards the finish line.
He can not jump several hurdles at once. In other words he must visit each intersection on first
street as he travels east. The hurdles can vary in height, with the only limitation being that they are
finite. You can assume that Karel starts out facing east.
So how do we solve this problem? It seems overwhelmingly complex, doesn't it? You likely wouldn't even know where to begin. This is exactly what top-down design is for. Let's see if we can decompose the problem in order to make it simpler. Remember there are two rules of thumb for doing this: look for repetitive parts, and look for distinct phases. So is there something that Karel will be doing many times in the course of running the race? Certainly. He will have to solve the problem of advancing one block east on first street. If he know how to do that, then would the problem be simpler? [Notice that this question is already dealing with step three of top-down design: combination.] Consider the following program segment:
while not-next-to-a-beeper do beginThis is a solution to the problem! The above fragment of code can be placed between the BEGINNING-OF-EXECUTION and END-OF-EXECUTION lines of the program. The only (sub)problem now is to figure out how to solve advance-one-block. However, this is a simpler problem than the original. We have made some progress.
advance-one-block;
end;
turnoff;
Well, to advance one block, one of two situations will arise. Either there is no hurdle in front of Karel, in which case we can just tell him to move, or there is a hurdle that he must jump. Once again we can defer the complexity of jumping and solve this problem as follows:
define-new-instruction advance-one-block as beginThere are several things to note about this code. First, look at how Karel's vocabulary can be expanded. The DEFINE-NEW-INSTRUCTION facility allows us to define a new word in terms of words we already know (in most programming languages these are called procedures or functions). Also, we have only been able to solve this subproblem by creating a new subsubproblem, jump, which now needs to be solved. Fortunately, jump is simpler than advance-one-block. Finally, observe the use of semicolons and the words BEGIN and END. Why is there no semicolon after the first end? The rule for semicolons is that they are places after each instruction, except not before an ELSE.
if front-is-clear then begin
move;
end
else begin
jump;
end;
end;
So how do we teach Karel to jump? Here is a case where we can identify three distinct phases: first jump up, then cross over, then fall down. So here is a simple implementation of this decomposition:
define-new-instruction jump as beginWhy did we have Karel turnleft both at the beginning and at the end of the jump? This time the decomposition produced three subproblems, but they will each turn out to be simple enough to solve directly. To jump to the top of the wall Karel must repetitively move north and check to see if his right is clear. To fall down he simply moves until he gets to first street (his front will be blocked when he gets there). Here is the rest of the code:
turnleft;
jump-up;
cross-over;
fall-down;
turnleft;
end;
define-new-instruction jump-up as beginFor a copy of the full program click here.
while right-is-blocked do begin
move;
end;
end;define-new-instruction cross-over as begin
turnright;
move;
turnright;
end;define-new-instruction fall-down as begin
while front-is-clear do begin
move;
end;
end;define-new-instruction turnright as begin
turnleft;
turnleft;
turnleft;
end;