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 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)