Karel The Robot
Karel The Robot is a simple teaching environment for imperative programming basics. The original idea was developed in the 1970s by Richard Pattis at Stanford University:
In the 1970s, a Stanford graduate student named Rich Pattis decided that it would be easier to teach the fundamentals of programming if students could somehow learn the basic ideas in a simple environment free from the complexities that characterize most programming languages.
In sophisticated languages like Java, there are so many details that learning these details often becomes the focus of the course. When that happens, the much more critical issues of problem solving tend to get lost in the shuffle. By starting with Karel, you can concentrate on solving problems from the very beginning. And because Karel encourages imagination and creativity, you can have quite a lot of fun along the way.
This project started in 2012 due to dissatisfaction with the available Karel environments. Since then, thousands of German university students have been introduced to the basics of imperative programming via this project.
Problem solving means translating human-understandable problem descriptions into machine-executable programs. Ideally, machine-executable programs should also be human-understandable; we attain that ideal with abstractions.
Abstractions aid tremendously in developing solutions to problems in a top-down (decomposing a complex problem into simpler subproblems) or bottom-up (composing simple subsolutions into a complex solution) fashion.
Humans like to organize processes (for example, doing the laundry) in hierarchical levels of abstraction:
do laundry: - wash laundry 🧼 - wait 1 hour ⏳ - hang laundry 🧺
wash laundry: 🧼 - put clothes into washing drum - apply laundry detergent - close washing drum - put plug into socket - choose temperature - press start button
hang laundry: 🧺 - open washing drum - put clothes into laundry basket - remove plug from socket - put clothes onto clothes line
If we keep delving deeper into lower levels of abstraction until we reach individual muscle movements, even the most simple-minded being can do the laundry by following the given instructions carefully. And how do we call such beings? Robots!
Karel The Robot requires Java version 8 or later: https://adoptopenjdk.net
The pre-selected version and JVM are usually fine, no need to change them.
Download karel.jar (~250 kb)
Open Windows explorer, navigate to the
Downloadsfolder and double-click on
karel.jar.
If double-clicking does not start Karel, the most probable causes are:
karel.jarwas silently renamed to
karel.jar.ZIP
Apparently, some Windows browsers silently rename
.jarfiles to
.jar.ZIPduring download. Since Windows explorer hides file extensions by default, you probably won't even notice the wrong extension; the file name will show up as
karel.jar, and the file type as
ZIP archive. Double-clicking on that file will not start Karel, but open the ZIP archive instead. You will see
a,
b,
c,
d,
e,
f,
g,
META-INF,
tiles,
a.class,
font.pngand
MainKt.class.
Unfortunately, changing file extensions in Windows explorer is not trivial. I recommend downloading
karel.jarfrom the Windows command line instead. Press the Windows key (the key on the bottom left with the Windows logo on it), write
cmdand confirm with Enter. Then enter the following lines:
cd Downloads curl -o karel.jar https://raw.githubusercontent.com/fredoverflow/karel/release/karel.jar java -jar karel.jar
The last line verifies the download by starting Karel. If that fails, enter
java -version. If that also fails, Java is not installed correctly.
Open a terminal in the download folder and write:
java -jar karel.jar
Java animations tend to stutter on Linux. Replacing
xrenderwith
openglmay help:
java -jar -Dsun.java2d.opengl=True karel.jar
Your code is automatically saved to a new file each time you click the start button. The save folder is named
karel, and it is located in your home directory. The full path is displayed in the title bar.
| Shortcut | Command | Meaning | | -------- | ----------------- | ------- | | F1 |
moveForward();| Karel moves one square forward in the direction he currently faces.
turnLeft();| Karel turns 90° to the left. | | F3 |
turnAround();| Karel turns 180° around. | | F4 |
turnRight();| Karel turns 90° to the right. | | F5 |
pickBeeper();| Karel picks a beeper from the square he currently stands on.
dropBeeper();| Karel drops a beeper onto the square he currently stands on.
Sometimes the same sequence of commands appears multiple times: ``` void roundTrip() { moveForward(); moveForward(); moveForward(); moveForward(); moveForward(); moveForward(); moveForward(); moveForward(); moveForward();
turnAround();moveForward(); moveForward(); moveForward(); moveForward(); moveForward(); moveForward(); moveForward(); moveForward(); moveForward();
}
You can extract such a sequence of commands into a new, custom command:void moveAcrossWorld() { moveForward(); moveForward(); moveForward(); moveForward(); moveForward(); moveForward(); moveForward(); moveForward(); moveForward(); }
and use it just like a primitive command:void roundTrip() { moveAcrossWorld(); turnAround(); moveAcrossWorld(); } ``` Deciding when a sequence of commands is worth extracting and choosing a good name for the custom command are essential development skills you will acquire over time.
Instead of writing the same sequence of commands multiple times:
void dance() { moveForward(); turnLeft(); moveForward(); turnLeft(); moveForward(); turnLeft(); moveForward(); turnLeft(); }you can use
repeatand only write it once:
void dance() { repeat (4) { moveForward(); turnLeft(); } }
Sometimes you only want to do something if some condition holds:
if (onBeeper()) { pickBeeper(); }Optionally, you can also specify what to do in case the condition does not hold:
if (onBeeper()) { pickBeeper(); } else { dropBeeper(); }Note that conditions are only checked when control flow actually reaches them (when the corresponding line is highlighted in the code editor). Conditions are not periodically checked in the background! In large programs with lots of potentially contradicting conditionals, such periodic background checks would quickly lead to incomprehensible program behavior.
| Shortcut | Condition | Meaning | | -------- | ---------------- | ------- | | F7 |
onBeeper()| Karel checks whether a beeper is on the square he currently stands on. | | F8 |
beeperAhead()| Karel checks whether a beeper is on the square immediately in front of him. | | F9 |
leftIsClear()| Karel checks whether no wall is between him and the square to his left. | | F10 |
frontIsClear()| Karel checks whether no wall is between him and the square in front of him. | | F11 |
rightIsClear()| Karel checks whether no wall is between him and the square to his right. |
An
elsewith nothing but another
ifinside:
if (leftIsClear()) { turnLeft(); } else { if (rightIsClear()) { turnRight(); } }can be simplified by leaving out the block between the
elseand
if:
if (leftIsClear()) { turnLeft(); } else if (rightIsClear()) { turnRight(); }Note that without the
else, Karel might turn left and then immedately turn right again, given
frontIsClear()originally held. The
elseprevents the second
iffrom executing in case the first condition was already
true.
!
An
if/elsewith an empty first block:
if (onBeeper()) { } else { dropBeeper(); }can be simplified by negating the condition with a leading
!:
if (!onBeeper()) { dropBeeper(); }
&&
An
ifwith nothing but another
ifinside:
if (frontIsClear()) { if (beeperAhead()) { moveForward(); pickBeeper(); } }can be simplified by combining both conditions with
&&:
if (frontIsClear() && beeperAhead()) { moveForward(); pickBeeper(); }
||
An
if/else ifwith identical blocks:
if (!frontIsClear()) { turnRight(); } else if (beeperAhead()) { turnRight(); }can be simplified by combining both conditions with
||:
if (!frontIsClear() || beeperAhead()) { turnRight(); }
| Condition | Meaning |
| --------- | ------- |
| !a
| holds if a does not hold (and vice versa) |
| a && b
| holds if both a and b hold |
| a || b
| holds if a or b (or both) hold |
| a || !b && c
| a || ((!b) && c)
|
ifchecks the condition and then executes the block at most once:
void moveForwardSafely() { if (frontIsClear()) { moveForward(); // This line is executed 0 or 1 times } }
whilere-checks the condition after the block is executed:
void moveToWall() { while (frontIsClear()) { moveForward(); // This line is executed 0 to 9 times } }
| Windows | Effect | Macintosh | | -----------: | :-------------------------: | ---------------- | | F1 |
moveForward();| F1 | | F2 |
turnLeft();| F2 | | F3 |
turnAround();| F3 | | F4 |
turnRight();| F4 | | F5 |
pickBeeper();| F5 | | F6 |
dropBeeper();| F6 | | F7 |
onBeeper()| F7 | | F8 |
beeperAhead()| F8 | | F9 |
leftIsClear()| F9 | | F10 |
frontIsClear()| F10 | | F11 |
rightIsClear()| F11 | | F12 | start