Learn How to Think with Karel the Robot. Chapter 6. Custom Commands

Chapter 6
Custom Commands

In this chapter you will learn:

  • How to split complex problems into simpler ones which are easier to solve.
  • How to define custom commands using the keyword def.
  • How to use the optional return statement.
  • The Second Maze Algorithm of robotics.
  • That the shortest program may not always be the most efficient one.
  • How to define new commands locally within the body of other commands.

6.1. Why are custom commands useful?

Today, Karel needs to collect 25 computer chips which form five stars:


PIC

Figure 6.1: *

Karel needs to collect 5 stars of chips.


The following program will solve the task, but it would not get you hired as a software engineer. Look at it first, and then we will explain what is wrong with it:

Program 6.1: Collect five stars of chips!
1repeat 4 
2  go 
3left 
4go 
5 
6repeat 4 
7  get 
8  go 
9  right 
10  go 
11  left 
12  left 
13go 
14get 
15go 
16 
17right 
18repeat 6 
19  go 
20left 
21 
22repeat 4 
23  get 
24  go 
25  right 
26  go 
27  left 
28  left 
29go 
30get 
31go 
32 
33left 
34repeat 8 
35  go 
36right 
37 
38repeat 4 
39  get 
40  go 
41  right 
42  go 
43  left 
44  left 
45go 
46get 
47go 
48 
49right 
50repeat 10 
51  go 
52left 
53 
54repeat 4 
55  get 
56  go 
57  right 
58  go 
59  left 
60  left 
61go 
62get 
63go 
64 
65left 
66repeat 6 
67  go 
68right 
69 
70repeat 4 
71  get 
72  go 
73  right 
74  go 
75  left 
76  left 
77go 
78get 
79go 
80 
81right 
82repeat 8 
83  go

The problem with this program is that it contains a 10-line section which was copied and pasted five times:

Program 6.2: Repeated code section
1repeat 4 
2  get 
3  go 
4  right 
5  go 
6  left 
7  left 
8go 
9get 
10go

This is the section of the code which actually collects the pattern of five chips. picture

Copying and pasting code makes the program not only long and cumbersome to work with, but also extremely prone to mistakes. Imagine that the pattern of the chips changes:


PIC

Figure 6.2: *

Another pattern.


Then the above code would have to be redone at five different places (!) Since programmers are only human, they are likely to make mistakes. And changing the same code at five different places makes the likelihood of making a mistake extremely high.

6.2. Defining a custom command star

The correct approach is to define a new command for the repeated functionality. Let’s call it star:

Program 6.3: Custom command star
1# Collect five chips: 
2def star 
3  repeat 4 
4    get 
5    go 
6    right 
7    go 
8    left 
9    left 
10  go 
11  get 
12  go

Notice that custom commands are defined using the keyword def which is followed by the name of the new command. Then, indented, comes the body of the command. The following image shows where Karel should be when the command star is executed, and then the robot’s position after the command finishes:


PIC   PIC

Figure 6.3: *

Karel’s position before and after the command star is executed.


picture

6.3. Assembling the solution of the main task

Recall that the main task is to collect all 25 chips:


PIC

Figure 6.4: *

Main task.


But how can the custom command star be used to solve it, when Karel is far away from the first star of chips?

The answer is - before executing the command star, we will program Karel to get there! In order to stand on the first chip of the first star, the robot needs to make four steps forward, turn left, and make one more step:

repeat 4  
  go  
left  
go

After executing this initial code, Karel is ready to execute the command star for the first time. picture


PIC

Figure 6.5: *

Karel is ready to execute the new command star.



PIC

Figure 6.6: *

Karel has executed the command star.


Next, in order to get ready for collecting the second star of chips, Karel needs to turn right, make 6 steps forward, and turn left:

right  
repeat 6  
  go  
left

After executing this code, he is ready:


PIC

Figure 6.7: *

Karel is ready to execute the command star again.


Now you should able to figure out the rest! Write down the commands Karel needs to get ready to collect the third, fourth, and fifth stars of chips. The final program is as follows:

Program 6.4: The final program
1# Collect five chips: 
2def star 
3  repeat 4 
4    get 
5    go 
6    right 
7    go 
8    left 
9    left 
10  go 
11  get 
12  go 
13 
14# Main program: 
15# Collect the 1st star: 
16repeat 4 
17  go 
18left 
19go 
20star 
21# Collect the 2nd star: 
22right 
23repeat 6 
24  go 
25left 
26star 
27# Collect the 3rd star: 
28left 
29repeat 8 
30  go 
31right 
32star 
33# Collect the 4th star: 
34right 
35repeat 10 
36  go 
37left 
38star 
39# Collect the 5th star: 
40left 
41repeat 6 
42  go 
43right 
44star 
45# Finish at the home square: 
46right 
47repeat 8 
48  go
picture

6.4. Collecting water bottles

Today Karel faces another complex task - to collect 27 water bottles which are stored in three crates:


PIC

Figure 6.8: *

Karel needs to collect 27 water bottles from three crates.


As you already know, it would not be a good idea to go ahead and start writing code right away. Let’s think about it first, and figure out a suitable subtask which is easier to solve than the main task.

Here is a good candidate for such a subtask. Let’s call the new command get9:


PIC   PIC

Figure 6.9: *

Karel’s position before and after executing the command get9.


This time, let’s work like an experienced software engineer, and solve the main task first. We know that the subtask is a routine thing to do, so we can leave it for later. The main program is below. Step through it in your mind to verify that it is correct!

Program 6.5: Main program
1# Main program: 
2# Get to the 1st crate: 
3go 
4# Collect the 1st crate: 
5get9 
6# Collect the 2nd crate: 
7get9 
8# Get to the 3rd crate: 
9left 
10repeat 5 
11  go 
12right 
13# Collect the 3rd crate: 
14get9 
15# Make one last step home: 
16go

This was a piece of cake - which tells us that the subtask was chosen really well! It remains to write the body of the new command get9. But you know what? We can still simplify it by splitting it into three smaller subtasks, where one subtask will be to just collect a row of three water bottles. Let’s call the new command get3:


PIC   PIC

Figure 6.10: *

Karel’s position before and after executing the command get3 (on the left, one water bottle is beneath Karel).


The following code will do it:

Program 6.6: Custom command get3
1# Collect 3 water bottles: 
2def get3 
3  repeat 2 
4    get 
5    go 
6  get

And now, the new command get9 is easy to write:

Program 6.7: Custom command get9
1# Collect 9 water bottles: 
2def get9 
3  get3 
4  right 
5  go 
6  right 
7  get3 
8  left 
9  go 
10  left 
11  get3 
12  go

Finally, let’s put everything together and write the solution program for the main task. The program will consist of two custom commands and a main program:

Program 6.8: Final solution program
1# Collect 3 water bottles: 
2def get3 
3  repeat 2 
4    get 
5    go 
6  get 
7 
8# Collect 9 water bottles: 
9def get9 
10  get3 
11  right 
12  go 
13  right 
14  get3 
15  left 
16  go 
17  left 
18  get3 
19  go 
20 
21# Main program: 
22# Get to the 1st crate: 
23go 
24# Collect the 1st crate: 
25get9 
26# Collect the 2nd crate: 
27get9 
28# Get to the 3rd crate: 
29left 
30repeat 5 
31  go 
32right 
33# Collect the 3rd crate: 
34get9 
35# Make one last step home: 
36go

6.5. The optional return statement

Every custom command can (but does not have to) contain one or more return statements. The return statement terminates and exits the custom command. For example, the command get3 from the previous section could be written as follows:

Program 6.9: Custom command get3 (with a return statement at the end)
1# Collect 3 water bottles: 
2def get3 
3  repeat 2 
4    get 
5    go 
6  get 
7  return
picture

6.6. Arcade game

Karel’s next task is to pass through the entire maze, collecting all gold nuggets and gems:


PIC

Figure 6.11: *

Karel needs to collect all gold nuggets and gems.


This is a complex task, so why don’t we solve a simpler subtask first. It will be much easier to just pass through one floor, left to right, collect all nuggets and gems, and if not at home, then find the opening to the next floor up. Let’s call the new command floor:


PIC

Figure 6.12: *

Before executing the new command floor.



PIC

Figure 6.13: *

After the command floor finishes.


As before, first let’s solve the main task assuming that we already have the command floor. The new command will be easy to define later:

Program 6.10: Main program
1# Main program: 
2repeat 12 
3  # Pass through one floor: 
4  floor 
5  # If not at home, get ready at the left end of the new floor: 
6  if not home 
7    left 
8    while not wall 
9      go 
10    repeat 2 
11      right

And here is the new command floor:

Program 6.11: New command floor
1# Pass through one floor and collect all nuggets and gems: 
2def floor 
3  repeat 14 
4    if gem or nugget 
5      get 
6    go 
7  if gem or nugget 
8    get 
9  # If not home, find the opening up: 
10  if not home 
11    left 
12    while wall 
13      left 
14      go 
15      right 
16    go

By putting the pieces together, we obtain the final solution program:

Program 6.12: Final solution program
1# Pass through one floor and collect all nuggets and gems: 
2def floor 
3  repeat 14 
4    if gem or nugget 
5      get 
6    go 
7  if gem or nugget 
8    get 
9  # If not home, find the opening up: 
10  if not home 
11    left 
12    while wall 
13      left 
14      go 
15      right 
16    go 
17 
18# Main program: 
19repeat 12 
20  # Pass through one floor: 
21  floor 
22  # If not at home, get ready at the left end of the new floor: 
23  if not home 
24    left 
25    while not wall 
26      go 
27    repeat 2 
28      right
picture

6.7. Second Maze Algorithm (SMA)

You already know the First Maze Algorithm of robotics which allows Karel (or any other robot) to pass through unknown mazes. The Second Maze Algorithm (SMA) allows robots to follow an arbitrary winding wall or a line on the ground. Imagine that Karel is in the jungle, and he has to follow a winding line of tree branches to his home square. He stands next to the line which is on his left-hand side:


PIC

Figure 6.14: *

Karel is in the jungle.


The Second Maze Algorithm of robotics will guide Karel safely to his home square:


Algorithm 6.1: *
Second Maze Algorithm
1:  While you are not home, do the following:
2:   Turn left.
3:   While there is a wall in front of you, keep turning right.
4:   Make one step forward.


It can be translated to the following code:

Program 6.13: Second Maze Algorithm translated to Karel code
1while not home 
2  left 
3  while wall 
4    right 
5  go

Let’s have a closer look at how the algorithm works! At the beginning, the line goes straight forward:


PIC


On line 2, Karel will turn left:


PIC


The while loop on line 3 will do one cycle, and so Karel will turn right once:


PIC


Finally, on line 5, he will make one step forward:


PIC


OK, that worked! After two more steps forward, Karel will arrive at a right turn:


PIC


On line 2, he will turn left:


PIC


Then, the while loop on line 3 will do two cycles:


PIC   PIC


And finally, Karel will make one step forward on line 5:


PIC


After this, the robot will make another right turn and two steps forward. Then the situation becomes more interesting because there is no wall on his left. BTW, this case is why the algorithm begins with a left turn, in case you were puzzled about it:


PIC


So, on line 2 Karel will turn left:


PIC


The body of the while loop will be skipped because there is no wall in front of the robot, and he will just make one step forward on line 5:


PIC


Now the situation repeats itself because there is no wall on Karel’s left. Hence, on line 2 he turns left:


PIC


Because there is no wall in front of him, the body of the while loop will be skipped again, and on line 5 he will make one step forward:


PIC


Woo-hoo, almost there! After a few more steps and one more right turn, Karel arrives at a left turn:


PIC


On line 2 he turns left:


PIC


Since there is a wall in front of him, the while loop will make him turn right once:


PIC


And on line 5 he makes one step forward:


PIC


You already know this situation - Karel does not have a wall on his left. On line 2 he turns left:


PIC


Then, because there is no wall in front of the robot, the body of the while loop is skipped, and Karel just makes one step forward:


PIC


6.8. Right-handed version of the SMA

Like the First Maze Algorithm, also the Second Maze Algorithm has a right-handed version. It is useful when Karel needs to follow a wall or line which is on his right:


PIC

Figure 6.15: *

Now the wall is on Karel’s right.


The algorithm is symmetric to the SMA (just switch "left" to "right" and vice versa):


Algorithm 6.2: *
Second Maze Algorithm (right-handed version)
1:  While you are not home, do the following:
2:   Turn right.
3:   While there is a wall in front of you, keep turning left.
4:   Make one step forward.


Here is the corresponding code:

Program 6.14: SMA (right-handed version)
1while not home 
2  right 
3  while wall 
4    left 
5  go

6.9. Rock climbing

Today, Karel will climb some rocks! His task is to collect all the beepers and enter his home square. The shape of the rock is random, and so is the number and positions of the beepers on it:


PIC

Figure 6.16: *

Karel stands in front of a desert rock.


This task is perfect for the Second Maze Algorithm (right-handed version). Just executing Program 6.14 will make Karel follow the contour of the rock ("climb on the rock") and get to his home square. So, the only change we need to make, is to check for a beeper after each step the robot makes:

Program 6.15: Collect all beepers!
1while not home 
2  right 
3  while wall 
4    left 
5  go 
6  if beeper 
7    get

6.10. Program length vs. efficiency

You already know that a well thought-out program usually is shorter and more elegant than a program which was written hastily. However, program length is not the only thing that matters. Even more important is efficiency. picture

In this section we will show you that the shortest program may not always be the most efficient one.

Today, Karel is collecting pearls in the ocean:


PIC

Figure 6.17: *

Karel is diving and collecting pearls.


This task is perfect for the Second Maze Algorithm (right-handed version) - Karel just needs to follow the wall on his right-hand side, and it will lead him to his home square. The solution program is practically the same as Program 6.15, the only difference being that the robot is looking for pearls instead of beepers:

1while not home 
2  right 
3  while wall 
4    left 
5  go 
6  if pearl 
7    get

This program needs 503 operations to collect all pearls and get Karel home. Save this number for a future comparison!

Next, let’s solve the task differently. We will write a new command column for the robot to collect all pearls in the column above him, and get ready below the next column. Then, the main program will just loop over all columns:

1# Collect one column of pearls: 
2def column 
3  while not wall 
4    go 
5    if pearl 
6      get 
7  left 
8  left 
9  while not wall 
10    go 
11  right 
12  go 
13  right 
14 
15# Main program: 
16repeat 14 
17  column 
18repeat 7 
19  go

As you can see, this program is longer than the previous one - it has 16 lines of code as opposed to 7. However, it provides a solution which is much more efficient. It collectc all pearls and gets the robot home with only 242 operations! picture

6.11. Writing monolithic code vs. using custom commands

Sometimes it is not obvious whether one will gain anything by introducing a custom command. For illustration, let’s return to the five stars of chips from Section 6.1 (page 490), but now the stars will form a regular repeating pattern. The task is the same - collect all the chips!


PIC

Figure 6.18: *

Five stars of chips forming a repeating pattern.


The code can be written as one monolithic piece, without defining any custom commands:

Program 6.16: Collect all stars! (monolithic code)
1go 
2repeat 5 # loop over stars 
3  repeat 4 # collect one star 
4    get 
5    go 
6    right 
7    go 
8    left 
9    left 
10  go 
11  get 
12  go 
13  right # move to the next star or home square 
14  repeat 3 
15    if not home 
16      go 
17  left

This code is very dense, but it does not contradict any good programming practices - there is no part which would be copied and pasted at multiple different places in the program (as it was in the original task in Section 6.1). The comments make it readable, so it is a good code.

Now let’s solve the same task using the custom command star from Section 6.1. And moreover, we will define another new command next for the robot to move to the next star or to the home square:

Program 6.17: Collect all stars! (with custom commands)
1# Collect one star of chips: 
2def star 
3  repeat 4 
4    get 
5    go 
6    right 
7    go 
8    left 
9    left 
10  go 
11  get 
12  go 
13 
14# Move to the next star or home square: 
15def next 
16  right 
17  repeat 3 
18    if not home 
19      go 
20  left 
21 
22# Main program: 
23go 
24repeat 5 
25  star 
26  next

The latter program had 21 lines (not counting empty lines and comments) while the former only had 17. This is not a huge difference. On the other hand, the latter program is so well readable that the main program does not need any comments - it is completely self-explanatory. Readability matters. If we had to pick up our favorite, then the slightly longer but much more readable program would definitely be our choice. picture

6.12. Defining commands inside other commands

It is possible in Karel to define commands within the body of other commands. This feature was implemented for conceptual compatibility with Python. Its usefulness becomes clear in large, complex programs where it helps keep code better organized.

For illustration, let’s return to Section 6.4 (page 516) where Karel was collecting water bottles from three crates. The solution Program 6.8 defined new commands get3 and get9. Notably, the command get3 was only used within the body of the command get9 and nowhere else. Therefore, it can be defined locally in the body of the command get9:

Program 6.18: Defining command get3 locally in the command get9
1# Collect 9 water bottles: 
2def get9 
3 
4  # Collect three water bottles 
5  def get3 
6    repeat 2 
7      get 
8      go 
9    get 
10 
11  # Main body of the command: 
12  get3 
13  right 
14  go 
15  right 
16  get3 
17  left 
18  go 
19  left 
20  get3 
21  go 
22 
23# Main program: 
24# Get to the 1st crate: 
25go 
26# Collect the 1st crate: 
27get9 
28# Collect the 2nd crate: 
29get9 
30# Get to the 3rd crate: 
31left 
32repeat 5 
33  go 
34right 
35# Collect the 3rd crate: 
36get9 
37# Make one last step home: 
38go
picture

If we tried to call the command get3 outside the body of the command get9, the program would crash with an error message. In larger programs, limiting the validity of commands helps improve code structure.

6.13. Create your own Karel language!

Being able to define your own commands is powerful. If you like, you can now create your own language for Karel, adding all the commands which you were missing! For example, the Karel language does not have a command to turn around 180 degrees, so let’s add it:

Program 6.19: New command to turn around 180 degrees
1# Turn around 180 degrees: 
2def turn180 
3  repeat 2 
4    left

With this new command in hand, many algorithms become simpler to implement, such as the First Maze Algorithm:

Program 6.20: First Maze Algorithm with the turn180 command
1# First Maze Algorithm: 
2while not home 
3  if wall 
4    left 
5    if wall 
6      turn180 
7  go

Speaking of the First Maze Algorithm, why don’t we just define a new command move to move one step forward in the maze:

Program 6.21: New command move
1# Move one step forward in the maze: 
2def move 
3  if wall 
4    left 
5    if wall 
6      turn180 
7  go

Then, passing through an unknown maze becomes extremely simple:

Program 6.22: Compact version of the FMA using the new command move
1# Pass through an unknown maze using the FMA: 
2while not home 
3  move

Now let’s change the subject. If your native language is different from English, you can make Karel speak your language! For example, "get" is "ukuthola" in Zulu:

Program 6.23: Translating command get to Zulu
1# Command get in Zulu: 
2def ukuthola 
3  get

6.14. Review questions

Friendly reminder - for every question either none, one, or several answers may be correct.

QUESTION 6.1. Is copying and pasting code a bad thing?

A
No because the length of the program grows more quickly.
B
Yes because the program becomes prone to mistakes.
C
No because the program will run faster.
D
No because this makes programming easier.

QUESTION 6.2. Is it a good idea to split complex tasks into simpler ones?

A
No because it is a loss of time.
B
No because it’s more work.
C
Yes because the simpler tasks are easier to solve.
D
No because an experienced programmer just solves the whole task at once.

QUESTION 6.3. When does one need to identify repeating patterns?

A
When working with repeat loops.
B
When working with while loops.
C
When working with custom commands.
D
When debugging programs.

QUESTION 6.4. What keyword does Karel use to define new commands?

A
new
B
define
C
var
D
def

QUESTION 6.5. What keyword does Karel use to terminate and exit custom commands?

A
end
B
terminate
C
exit
D
return

QUESTION 6.6. Can custom commands define other custom commands in their body?

A
Yes.
B
No.
C
Only if the locally-defined command is a one-liner.
D
Only if the locally-defined command does not contain a return statement.

QUESTION 6.7. Can custom commands define other custom commands in their body?

A
Yes.
B
No.
C
Only if the locally-defined command is a one-liner.
D
Only if the locally-defined command does not contain a return statement.

QUESTION 6.8. What is the purpose of the Second Maze Algorithm (SMA)?

A
To pass through a maze without loops and dead ends.
B
To pass through a maze without forsk, loops and dead ends.
C
To follow an arbitrary winding wall or line on the ground.
D
To find a way out of an arbitrary maze.

QUESTION 6.9. The left-handed version of the SMA assumes that

A
Karel stands next to a wall, and the wall is on his left.
B
Karel stands next to a wall, and the wall is on his right.
C
Karel faces the wall.
D
Karel stands with his back turned to the wall.

QUESTION 6.10. What of the following actions represent one operation in the Karel language?

A
Karel makes one step forward.
B
Karel turns around 180 degrees.
C
Karel places an object on the ground.
D
Karel walks from one side of the maze to the opposite one.

QUESTION 6.11. When is a program efficient?

A
When it has as few lines as possible.
B
When it is carefully thought-out.
C
When it uses nested loops and conditions.
D
When it solves the given task using as few operations as possible.


Table of Contents

Created on September 27, 2018 in Karel.
Add Comment
0 Comment(s)

Your Comment

By posting your comment, you agree to the privacy policy and terms of service.