Karel the Robot in Ruby
- Author
-
Dave Copeland (davetron5000 at g mail dot com)
- Copyright
-
Copyright © 2010 by Dave Copeland
- License
-
Distributes under the Apache License, see LICENSE.txt in the source distro
What is this?
This is a ruby implementation of Karel The Robot, a programming language designed for extreme beginners. It concerns controlling a robot, named Karel, in a grid-based world comprised of walls and beepers. Karel can pick up and put down beepers, move forward, and turn left. Karel can also detect things about his environment.
Install
For the time being, there’s no gem so just clone the repo.
git clone https://[email protected]/davetron5000/rkarel.git
cd rkarel
bin/karel init example.karel
bin/karel run example.karel
bin/karel run -g example.karel
Usage
While the karel language doesn’t have any concept of the world, you must still define one in which your program will run. This is done at the top of the program via:
WORLD <<END
W B
W
WWW
B
K
END
This defines Karel’s world to be 5x9 with walls represented by W’s, a beeper represented by B’s, and Karel’s initial location represented as a “K”. Karel is assumed to be facing north with no beepers at the start of all programs.
You then can define subroutines as such:
DEFINE('TURNRIGHT') {
TURNLEFT()
TURNLEFT()
TURNLEFT()
}
You can then call this subroutine as you would any other
MOVE()
TURNRIGHT()
MOVE()
MOVE()
Once you’ve defined your subroutines, you can begin writing your program:
MOVE()
TURNLEFT()
ITERATE(3.TIMES) {
MOVE
}
IF(front_clear) {
MOVE()
}
ELSE {
PUTBEEPER()
}
TURNLEFT()
WHILE(not_on_beeper) {
MOVE
}
PICKBEEPER()
Commands
MOVE
-
Move Karel forward one square. If Karel can’t, he explodes and the program aborts
TURNLEFT
-
Rotate Karel, in place, to the left
PICKBEEPER
-
Pick up the beeper at Karel’s position. If there is no beeper, Karel explodes and the problem aborts
PUTBEEPER
-
Put down a beeper at Karel’s position. If there is a beeper or if Karel has no beepers, Karel explodes and the problem aborts
Control Flow
IF
-
takes a condition and a curly-braced block to perform if the condition holds
ELSE
-
when after an IF, executes the curly-braced block if the condition didn’t hold
ITERATE(N.TIMES)
-
perform something a constant number of times
WHILE
-
takes a condition and a curly-braced block and repeatedly performs the block’s statements until the condition holds
Conditions
on_beeper
-
true if Karel is on the same square as a beeper
not_on_beeper
-
true if Karel is not on the same square as a beeper
front_clear
-
true if the square in front of Karel is clear
front_not_clear
-
true if the square in front of Karel is not clear
left_clear
-
true if the square to the left of Karel is clear
left_not_clear
-
true if the square to the left of Karel is not clear
right_clear
-
true if the square to the left of Karel is clear
right_not_clear
-
true if the square to the left of Karel is not clear
facing_north
-
true if Karel is facing north
not_facing_north
-
true if Karel is not facing north
facing_south
-
true if Karel is facing south
not_facing_south
-
true if Karel is not facing south
facing_east
-
true if Karel is facing east
not_facing_east
-
true if Karel is not facing east
facing_west
-
true if Karel is facing west
not_facing_west
-
true if Karel is not facing west
Subroutines
You can define your own subroutines via the DEFINE
directive:
DEFINE('RUN_TO_WALL') {
WHILE(front_clear) {
MOVE()
}
}
RUN_TO_WALL()
Whitespace and comments
Whitespace is not significant and is ignored. Comments are lines starting with optional whitespace then followed by a #. The remainder of the line is ignored.
Example
Here’s an example program that has Karel grabbing both beepers and stopping on the last one (the one in the upper-left corner):
WORLD <<END
B WW B
W
W
WWW K
END
WHILE(front_clear) {
MOVE()
}
TURNLEFT()
MOVE()
PICKBEEPER()
TURNLEFT()
# Karel is now facing south
WHILE(front_clear) {
MOVE()
}
TURNLEFT()
TURNLEFT()
MOVE()
TURNLEFT()
WHILE(front_clear) {
MOVE()
}
TURNLEFT()
TURNLEFT()
TURNLEFT()
WHILE(not_on_beeper) {
MOVE()
}
PICKBEEPER()
We can reduce the line count and make it more readable with some subroutines:
DEFINE('TURNRIGHT') {
ITERATE(3.TIMES) {
TURNLEFT()
}
}
DEFINE('TURNAROUND') {
TURNLEFT()
TURNLEFT()
}
DEFINE('RUN') {
WHILE(front_clear) {
MOVE()
}
}
DEFINE('BACKUP') {
TURNAROUND()
MOVE()
}
RUN()
TURNLEFT()
MOVE()
PICKBEEPER()
TURNLEFT()
RUN()
BACKUP()
TURNLEFT()
RUN()
TURNRIGHT()
WHILE(not_on_beeper) {
MOVE()
}
PICKBEEPER()
Non-DSL Mode
If you wish to use this code as an engine to embed inside another application, the DSL mode is quite inconvienient. In this case, you can create an object and call the DSL methods on it.
engine = Engine.new
engine.WORLD <<END
B WW B
WW W
W WW
K W
END
Need to think this out more; what is possible and what isn’t?
Vim Syntax File
In contrib
is a vim syntax file for karel source as described here.
Command Line Interface
karel command_name [command-specific options] [--] arguments...
-
Use the command
help
to get a summary of commands -
Use the command
help command_name
to get a help forcommand_name
-
Use
--
to stop command line argument processing; useful if your arguments have dashes in them
Commands
check
-
Performs a basic syntax check
help
-
Shows list of commands or help for one command
init
-
Creates a new example Karel program
run
-
Executes a Karel program
check source_file
Performs a basic syntax check
help [command]
Shows list of commands or help for one command
init source_file
Creates a new example Karel program
Options
These options are specified after the command.
-f, --force
-
Overwite files
run source_file
Executes a Karel program
Options
These options are specified after the command.
-g, --debug
-
Show the world after each command is executed
-s
-
Run silently; do not show the world before or after
-x arg
-
Number of “ticks” to allow before assuming we are in an infinite loop ( default:
1000
)