Karel J. Robot

Simulator Usage

 

Here are some options for using the KarelJRobot.jar file from the web page.

Setup

If you use a project manager, add the KarelJRobot.jar file to the project. If you use the JDK only, you must add the complete path to KarelJRobot.jar to your CLASSPATH. The four .gif files distributed with the jar file should be in the same directory as the jar file.

You can then write your robot classes as in the book. You can put the classes in the same file with your Main class or in another file. You will need to import kareltherobot.*; (Note that there are no upper case letters here.)

To run the robot progam you must write a program that contains public static void main(String [] args). This can be part of any class, but we assume that it is in class Main here. The file in which you write all of this should have a

import kareltherobot.*;

heading at the beginning. Do this instead of trying to package kareltherobot. (This is a change.)

If you don't care for * import (wisely) then you probably need at least

import kareltherobot.UrRobot;
import kareltherobot.World;
import kareltherobot.Directions;

You may need to import kareltherobot.Robot; as well. See the javadocs for other things you may need: WorldBuilder and such.

The class should implement the Directions interface so that you can use the names North... In your main function you can create, read, and write robot worlds. You need to place beepers and walls with

World.placeBeepers(int street, int avenue, int howmany);
World.placeEWWall( int NorthOfStreet, int atAvenue, int lengthTowardEast);
World.placeNSWall (int atStreet, int EastOfAvenue, int lengthTowardNorth);

When you have a world you can save it with:

World.saveWorld(String filename);

Later you can read it back in rather than recreating it:

World.readWorld(String filename);

The world is no longer visible by default, so you probably want to say

World.setVisible(true);

near the beginning of your main.

Simple Task

In the flavor of the book, you can write a task function with a complete Robot program.

public static void task()
{	UrRobot Karel = new UrRobot(1, 1, North, 1);
	Karel.move();
	Karel.move();
	Karel.putBeeper();
	Karel.turnOff(); 
}

You can call this from main. You can also put the statements directly into main, of course. Note that to get access to the names like North, South, and infinity, your class will need to implement the Directions interface. Note that the interface is Directions (import this), but the class of the direction objects is Direction. Technically, it is Directions.Direction, a class defined within the Directions interface. Yes, you can do that in Java.

Note that with this technique, when you run the program the robots in the task will immediately begin executing. You will be able to adjust the speed of the simulation with the speed slider, but you won't be able to stop and resume it.

You can preset the initial speed in the main function with

World.setDelay(int d)

where a delay of 0 means to run as fast as possible, and 100 means to pause for a full second between robot actions.

You can also set the size of the world (not too big as it scales to a fixed window size) with

World.setSize(int numberOfStreets, int numberOfAvenues)

Note, however, that the robot image is not scaled, so if you have too many streets or avenues the simulaton will look funny.

The system also prints a continuous trace of robot actions to System.out. You can control this with

World.setTrace(boolean t)

Robots have one or two features that are not in the book. One that you might want to use, even with simple programs, is

aRobot.showState(String s)

The robot will print the String on System.out, together with information about its current state.

Here is a complete example.

package kareltherobot;

class RightTurner extends Robot implements Directions
{	public RightTurner(int street, int avenue, Direction direction, int beepers)
	{	super(street, avenue, direction, beepers);
	}
	
	public void turnRight()
	{	turnLeft();
		turnLeft();
		turnLeft();
	}
}

public class Main implements Directions
{	public static void task()
	{	RightTurner Karel = new RightTurner(1, 1, North, infinity);
		Karel.move();
		Karel.move();
		Karel.turnRight();
		Karel.move();
		Karel.putBeeper();
		Karel.turnOff(); 
	}

	public static void main(String [] args)
	{	World.placeEWWall(5, 1, 5);
		World.placeNSWall(1, 5, 5);
		World.setDelay(10);
		World.setVisible(true);
		task();
	}
}

There is also a "speed control" dialog that you can make visible with

World.showSpeedControl(true);

If you have multi threaded robots (below) the speed control can start and stop them and set the thread speed. I'ts stop/resume control doesn't work if you don't use threads. However, it will still show you the mouse position as you mouse over the world. This may be helpful.

If you want to see a world builder dialog when you run just create one. It will be visible.

WorldBuilder builder = new WorldBuilder(true);

The argument to the constructor says we also want the speed control as well as the world builder.

Thread For Simple Task

You can also run your task in its own thread. This is very easy to do. It also allows you to stop and resume the simulation at any time.

To do this create a Java class that implements Runnable. In the required run method of such a class, call your task function. If this is a static function of a class called Main it would be like the following.

class Runner implements Runnable
{	public void run()
	{	Main.task();
	}
}

Then, in your main function, you need to create a new Runner and new Thread to host the Runner object. This is easily done as follows.

World.setupThread(new Runner());

This way the World will create the thread. When you run your program, you will need to start the simulation by clicking the Resume button. Such a task can contain one or more robots. The Resume/Stop button controls the execution of threads, not robots.

You can also treat this run method as if it is the task and write the robot program here. If you do this you may want your Runner class to implement Directions as well as Runnable.

If you create many such classes and start each one up with setupThread, they will all run simultaneously in the same world, perhaps cooperating and perhaps interfering with each other.

 

Robot is Runnable

Actually, the UrRobot class itself implements Runnable, so instead of writing a task, you can just write a run method in your new robot class. If you do this, however, I recommend that you also call setupThread from the constructor of the class, and that the run method not create any additional robots.

class BlockRunner extends UrRobot implements Directions
{	public BlockRunner((int street, int avenue, Direction direction, int beepers)
	{	super(street, avenue, direction, beepers);
		World.setupThread(this);
	}

	public void run()
	{	for (int i = 0; i < 4; ++i)
		{	move();
			move();
			move();
			move();
			turnLeft();
		}
	}
}

Then in main, you only need to create such a robot and it will automatically run, though when the simulation begins, it will be stopped and you can resume it with the Resume button. You don't even need to give the new robot object a name.

public static void main(String [] args)
{	...
	new BlockRunner(3, 3, East, 0);
}

Alternatively, you can leave the setupThread message out of the constructor and call it from main.

NOTE that since UrRobot implements Runnable all subclasses do also, so we don't need to repeat that, though we may.


Many Robots, Many Threads

You can use the above technique and create many robots that can interact in the world. Here we show part of a class that illustrates a famous problem called Dining Philosophers and which sometimes results in "deadlock" where each robot holds something that the others want. It uses another Java class that implements Dice.

public class Philosopher extends AugmentedRobot implements Directions
{	public Philosopher(int street, int avenue,  Direction direction)
	{	super(street, avenue, direction, 0);
		World.setupThread(this);
	}
	
	public void run()
	{	while(true)
		{	think(d.roll());
			getForks(); // beepers for forks. 
			eat(d.roll());
			putForks();
		}
	}
	
	...
	
	Die d = new Die(6);
}

To use this, create a world with four beepers and four Philosophers as follows

public static void main(String [] args)
{	World.placeBeepers(2, 2, 1);
	World.placeBeepers(2, 4, 1);
	World.placeBeepers(4, 2, 1);
	World.placeBeepers(4, 4, 1);
	Philosopher p1 = new Philosopher(2, 3, North);
	Philosopher p2 = new Philosopher(3, 2, East);
	Philosopher p3 = new Philosopher(4, 3, South);
	Philosopher p4 = new Philosopher(3, 4, West);
}

These four robots will share the world, thinking and eating, and perhaps interfering with each other. Here is a peek (running on a Mac).

(Image of four robot philosophers in a world with beeper forks)



Finally, you can also make a robot named Karel sleep for a bit with

Karel.sleep();

The robot will sleep for a time dependent on the delay time that you set in the program or with the slider. The robot's thread will also yield to other threads when this happens.


 

NOTE that the thread manager on a Macintosh (prior to OS X) is not preemptive. Trying to run robot programs at the fastest speed (delay = 0) will not work well. Use World.setDelay(1) for the best performance. Actually, this value is the default. This problem with threads causes other problems for programs, like Karel J. Robot, that do a lot of animation. In particulator, animation programs produce a lot of garbage and the garbage collector has a difficult time keeping up since it runs in its own low priority thread. This has the potential to crash your system, though it should not destroy anything when it does. You may need to physically halt the program with Command-Option-Escape. This should only occur on Macintoshs prior to OS X. It has not been a problem elsewhere, since most thread schedulng is preemptive. This is solved under OS X, of course.

Also note that if you are running on Macintosh OS 9 or earlier, that there is a maximum size for a filename and it is quite short by today's standards. A program that uses inner classes can easily exceed this limit because of the way that the names of the class files of such classes are handled. The best way to avoid this is to use a tool like Codewarrior that will produce a jar file as output from the compiler. These can contain classes with longer names and so you will be able to run such programs. Producing a jar file is one of the setup options in Codewarrior.

Last Updated: February 14, 2005