Overview
Karel was invented by Rich Pattis, a graduate student at Stanford university in the 1970s. It was intended to teach the fundamentals of programming without the complexities that are present in many programming languages. To achieve this goal, Rich created an introductory programming language that students can use to teach a robot to solve simple problems. The robot was named Karel, after Karel Čapek, the author of the 1923 play Rossum's Universal Robots, which is where the English language got the word "robot".
The Stanford version of the Karel language shares many similarities with Python, so Karel feels right at home on CircuitPython. When used under CircuitPython, there is a little bit of initialization needed for the display and to set the initial state, and there is a function that will verify whether you've correctly directed Karel to the goal state. All of the other code that controls Karel is the same in CircuitPython as it is in the Stanford version, so you can copy that portion of your programs between the two, and they will work on both.
Parts
This project supports any CircuitPython device with a display of at least 240x240 pixels. Externally connected displays can work but require additional wiring. For the simplest setup, use a device with a built-in display; several options are listed below.
Page last edited December 18, 2024
Text editor powered by tinymce.
Project Setup
Are you new to using CircuitPython? No worries, there is a full getting-started guide here.
Plug the device into your computer with a known good USB cable (not a charge-only cable). The device will appear to your computer in File Explorer or Finder (depending on your operating system) as a flash drive named CIRCUITPY. If the drive does not appear, you can install CircuitPython on your device and then return here.
Download the project files with the Download Project Bundle button below. Unzip the file and copy/paste the code.py and other project files to your CIRCUITPY drive using File Explorer or Finder (depending on your operating system).
Drive Structure
After copying the files, your drive should look like the listing below. It can contain other files as well, but must contain these at a minimum.
# SPDX-FileCopyrightText: 2024 Tim Cocks for Adafruit Industries
#
# SPDX-License-Identifier: MIT
"""
Karel the Robot code.py for devices with built-in display available
at board.DISPLAY.
"""
# pylint: disable=wildcard-import, unused-wildcard-import
from karel.circuitpythonkarel import *
# load a chapter. Edit the chapter filename to change chapters
# see available chapter files in chapters/ directory.
chapter_data = load_state_file("chapters/karel_ch01.json")
def main():
"""
Karel main() function declaration.
Put your code for Karel into this function.
"""
## START OF MAIN FUNCTION, YOUR CODE GOES BELOW HERE ##
## END OF MAIN FUNCTION, YOUR CODE GOES ABOVE HERE ##
print(f"Goal state reached? {world.check_goal_state(chapter_data)}")
# call the main() function
main()
# Run forever so that the ending state of Karel and the world
# remains visible on the display.
while True:
pass
Page last edited December 18, 2024
Text editor powered by tinymce.
Programming Karel
Loading Initial State
Once you've copied the project bundle to your device, you're ready to begin programming Karel. By default, the code.py file will load the world state for Chapter 1 of the Stanford guide. To load a different puzzle, simply update this line of the code to use a different valid puzzle data file name.
chapter_data = load_state_file("chapters/karel_ch01.json")
Look inside of the chapters/ directory within the project files to find the names of all of the puzzle data files. Here are all the ones that come with the project code:
karel_ch01.json karel_ch02.json karel_ch03a.json karel_ch03b.json karel_ch04.json karel_ch05a.json karel_ch05b.json karel_ch05c.json karel_ch06a.json karel_ch06b.json karel_ch07.json karel_ch08.json
Write Code For Karel
Your code goes inside of the main() function body. There are comments that designate the area to put your code.
## START OF MAIN FUNCTION, YOUR CODE GOES BELOW HERE ## ## END OF MAIN FUNCTION, YOUR CODE GOES ABOVE HERE ##
Move your cursor to one of the blank lines between these comments when you start writing code for Karel.
CircuitPython runs your code automatically when you save it, so ave your file (click Save or press ctrl-S or cmd-S) when you're ready, and then watch the display to see how your Karel behaves with the current code.
At the end of the program, it will check the state of Karel and the world to see if they match the goal state for the current puzzle data. Look in the serial console for print messages like Goal state reached? [True or False]. Try to write the programs necessary to successfully reach the goal state and make it print True.
Example Karel Program
Here is an example program that will move a few steps while picking up and then putting back down a beeper.
## START OF MAIN FUNCTION, YOUR CODE GOES BELOW HERE ## # take one step move() # if there is a beeper at this location if beepers_present(): # pick up the beeper pick_beeper() # turn Karel to the left turn_left() # take one step move() # put down a beeper put_beeper() ## END OF MAIN FUNCTION, YOUR CODE GOES ABOVE HERE ##
See the Stanford Python Karel reference page for a complete list of available commands. Remember that you can also write your own functions to achieve more complex behaviors.
Page last edited December 18, 2024
Text editor powered by tinymce.
Stanford Chapters
The Stanford Python Karel guide contains a total of 11 chapters. The first 8 chapters include a puzzle that the student can attempt to solve by programming Karel. There are chapter data files included in the project code for all of the puzzles covered by those 8 chapters. The last few chapters contain extra features, an API reference, and a page that lets you code Karel live in the browser. They do not contain puzzles to be completed, but are still worth taking a look at.
The puzzles on this page are intended for you to do while you read the information presented in each chapter within the Stanford Python Karel guide. Load that guide in a browser tab and read through it as you work on the puzzles.
Chapter 1
Load this puzzle from the file chapters/karel_ch01.json. Your goal is to program Karel to pick up the beeper and walk up and around the obstruction in a U-turn shape, then put the beeper back down.
Chapter 2
Load this puzzle from the file chapters/karel_ch02.json. Your goal is to program Karel to pick up the beeper and walk up onto the hill, place the beeper back down, and take another step forward to the last available corner.
Chapter 3a
Load this puzzle from the file chapters/karel_ch03a.json. Your goal is the same as the puzzle from Chapter 2, but you're intended to declare your own new function turn_right() in order to solve it this time.
Initial state and goal state are the same as depicted above.
Chapter3b
Load this puzzle from the file chapters/karel_ch03b.json. Your goal is to go around the wall and pick up the beeper. Then take the beeper to the opposite corner of the world and place it back down. Karel should remain at the same location as the beeper, and face west. This is a custom puzzle I created; it isn't included in the Stanford Chapter 3 page.
Chapter 4
Load this puzzle from the file chapters/karel_ch04.json. Your goal is to fill the pothole by placing a beeper inside of it, then move Karel to the opposite side of the filled hole. Think about ways to break the challenge down into smaller components and then try to declare functions to carry out the tasks required by them.
Chapter 5a
Load this puzzle from the file chapters/karel_ch05a.json. Your goal is to use a for loop to put down 42 beepers and then move Karel one step past them.
Chapter 5b
Load this puzzle from the file chapters/karel_ch05b.json. Your goal is to place one beeper in each of the 4 corners of the world. Karel should end at the same location, and facing the same direction it was when it began.
Chapter 5c
Load this puzzle from the file chapters/karel_ch05c.json. Your goal is to place 5 beepers in each of the corners of the world. Try to declare a new function or two that make use of for loops inside of them to solve this puzzle.
Chapter 6a
Load this puzzle from the file chapters/karel_ch06a.json. Your goal is to move Karel forward as long as there is an empty space in front. Learn about, and use a while loop to solve this puzzle.
Chapter 6b
Load this puzzle from the file chapters/karel_ch06b.json. Your goal is similar to the previous puzzle, but this time leave a trail of beepers on the ground at each location that Karel moves to. Solving this puzzle requires dealing with the fencepost bug.
Chapter 7
Load this puzzle from the file chapters/karel_ch07.json. Your goal is walk to the end of the row, inverting the beeper presence along the way. Any tile that was empty should have a beeper put down, and any tile that had a beeper already should have the beeper picked up, and be left empty. Learn about if statements and basic conditional to solve this puzzle.
Chapter 8
Load this puzzle from the file chapters/karel_ch08.json. Your goal is to move Karel along the bottom row, stopping to pick up the entire column of beepers that are present and then moving to the next column. Once all beepers are collected, put them down in the bottom right corner of the world. Finally, return Karel to the starting location and direction. This puzzle requires combining together knowledge from all of the previous chapters.
Page last edited December 18, 2024
Text editor powered by tinymce.
Extra Features
Paint Corner
In the Stanford Python Karel guide chapter 9, there is information about a paint_corner() function. At the time of writing this guide, I could not get the paint_corner() function to work for the in-browser Karel code environment on their web pages. However, the CircuitPython implementation of Karel does support paint_corner(). Call it and pass a color name to paint the color at Karel's current location. e.g. paint_corner("blue").
Painted corners cannot be part of the input or goal states, but they sure are fun to play with and pretty to look at. The supported colors are listed below. You can also see them in the serial console by adding this statement inside your main() function: print(COLOR_NAMES)
['white', 'black', 'red', 'orange', 'yellow', 'green', 'blue', 'purple', 'pink', 'light_gray', 'gray', 'brown', 'dark_green', 'turquoise', 'dark_blue', 'dark_red']
Creating Custom Puzzles
The puzzles are encoded into JSON files. If you're not familiar with JSON, this Real Python page can serve as a great guide covering the basics. The file contains an input state and goal state. Each state contains a grid representing the tiles in the world, the location and direction of Karel, and an optional list of locations that contain more than 1 beeper.
The numbers in the grid correspond to indexes within the sprite sheet.
-
7= Empty black void tile -
5= Open corner tile that Karel can move to -
4= Beeper tile -
6= Wall tile that Karel cannot move to.
For Karel's location, the x and y use a zero-based origin of the bottom left corner. "x":0, "y":0 is the bottom left corner of the world.
For beeper_counts, the configuration is a dictionary that has string keys which are comma separated x and y coordinates using the same zero-based origin as Karel. "1, 0": 42 means 42 beepers located at tile x:1, y:0
To create your own custom puzzles, simply copy/paste an existing configuration file and change the grid tile indices as well as Karel's location and direction. If your puzzle requires multiple beepers at the same location, then add a beeper_counts object with appropriate values. See chapters/karel_ch05a.json for an example configuration that uses multiple beepers at a location.
The configuration file for Chapter 1 is embedded below for reference.
{
"input": {
"karel": {
"x": 0,
"y": 0,
"direction": "east"
},
"world": [
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[5, 5, 5, 7, 7, 7, 7, 7, 7, 7],
[6, 6, 5, 7, 7, 7, 7, 7, 7, 7],
[5, 4, 5, 7, 7, 7, 7, 7, 7, 7]
]},
"goal": {
"karel": {
"x": 0,
"y": 2,
"direction": "west"
},
"world": [
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[7, 7, 7, 7, 7, 7, 7, 7, 7, 7],
[4, 5, 5, 7, 7, 7, 7, 7, 7, 7],
[6, 6, 5, 7, 7, 7, 7, 7, 7, 7],
[5, 5, 5, 7, 7, 7, 7, 7, 7, 7]
]}
}
Page last edited December 18, 2024
Text editor powered by tinymce.