= Karel the Robot in Ruby
Author:: Dave Copeland (mailto:davetron5000 at g mail dot com)
Copyright:: Copyright (c) 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[http://www.cs.mtsu.edu/~untch/karel/], 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://davetron5000@github.com/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 for +command_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)