Home > rkarel

rkarel

Rkarel is a project mainly written in RUBY and VIM SCRIPT, based on the Apache-2.0 license.

Ruby implementation of Karel The Robot

= 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

gem install karel
karel init example.karel
karel run example.karel
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)

Previous:rallot