Writing Concurrent Programs with this Simulator

As with the Chapter 3 usage, there are three parts. We won't show the simple change to KarelProgram as it is completely standard. Here is the code for the Philosopher class that solves the Dining Robots program discussed in Chapter 8. It is a bit simpler than described in the book, as nothing special needs to be done to make a Robot "runnable" here. All robots in this simulator are runnable, hence the run method we have already seen in Chapter 2 and Chapter 3. The existing code from the book is almost unchanged except for simpler constructors:

package alice.kareluser;

import org.alice.apis.moveandturn.Color;

import alice.kareltherobot.Direction;
import alice.kareltherobot.Robot;

import com.jbergin.util.Die;

/** A class of philosopher robots designed to simulate the classic
 * Dining Philosophers simulation (with 4 philosophers). 
 * @author Joseph Bergin
 *
 */
public class Philosopher extends Robot implements Runnable
{

	public Philosopher(int street, int avenue, Direction direction)
	{
		super(street, avenue, direction);
	}

	public Philosopher(int street, int avenue, Direction direction, int beepers)
	{
		super(street, avenue, direction, beepers);
	}

	public Philosopher(int street, int avenue, Direction direction, int beepers,
			Color color)
	{
		super(street, avenue, direction, beepers, color);
	}

	/**
	 * Turn right by making three left turns
	 */
	public void turnRight()
	{
		turnLeft();
		turnLeft();
		turnLeft();
	}

	/**
	 * Turn around by making two left turns
	 */
	public void turnAround()
	{
		turnLeft();
		turnLeft();
	}

	/**
	 * Back up one block and face the original direction. Will fail if the rear is blocked
	 * by a wall.
	 */
	public void backUp()
	{
		turnAround();
		move();
		turnAround();
	}

	/**
	 * This is the method used by the thread system (doTogether). It will be invoked
	 * automatically. This is the task for one Philosopher robot. A robot thinks for a 
	 * random period, then tries to eat (for a random period), but must first pick up two
	 * "forks" (beepers) to its left and right. But there are only four forks. 
	 */
	public void run()
	{
		while (true)
		{
			think(d.roll());
			getForks();
			eat(d.roll());
			say("burp");
			putForks();
		}
	}

	/** Philosopher backs away from the table to think, and 
	 * occasionally makes an exclamation.*/
	private void think(int time)
	{
		for (int i = 0; i < time; ++i)
		{
			backUp();
			int r = d.roll();
			if (r < 3)
			{
				think("???  ?");
			} else if (r == 6)
			{
				say("Eureka!");
			}

			move();
		}
	}

	/** Philosopher moves in to the table to eat and occasionally
	 * comments on the food. */
	private void eat(int time)
	{
		for (int i = 0; i < time; ++i)
		{
			move();
			int r = d.roll();
			if (r < 4)
			{
				say("yum");
			}
			backUp();
		}
	}

	/**
	 * Philosopher picks up a fork from its left and one from its right, but waits until
	 * they are available.
	 */
	private void getForks()
	{
		turnLeft();
		move();
		while (!anyBeepersInBeeperBag())
		{
			while (!nextToABeeper())
			{
				turnAround();
				turnAround();
			} // nothing, but with panache
			pickBeeper();
			think("Found 1 fork.");
		}
		turnAround();
		move();
		putBeeper();
		move();
		while (!anyBeepersInBeeperBag())
		{
			while (!nextToABeeper())
			{
				turnAround();
				turnAround();
			} // nothing
			pickBeeper();
			think("Found the other fork.");
		}
		turnAround();
		move();
		putBeeper();
		turnRight();
		// showState("Eat ");
	}

	/**
	 * Philosopher puts down both of its forks, one to the left and one to the right.
	 */
	private void putForks()
	{
		pickBeeper();
		pickBeeper();
		turnLeft();
		move();
		putBeeper();
		turnAround();
		move();
		move();
		putBeeper();
		turnAround();
		move();
		turnRight();
		// showState("Think ");
	}

	private Die d = new Die(6);

}

Note that the task of each of the four robots to be created (from this one class) have their task it the run method. This is essential. We have also embellished it a bit, since the robots are allowed to "think" and their thoughts will be displayed in the simulation.

The concurrency setup is all in the world class. We use the doTogether method of Alice's virtual machine. We just invoke this method giving it any number of "runnable" objects as arguments. Here it is the four philosophers:
package alice.kareluser;

import org.alice.apis.moveandturn.AbstractCamera;
import org.alice.apis.moveandturn.Color;
import org.alice.virtualmachine.DoTogether;

import org.alice.apis.moveandturn.gallery.environments.grounds.SandyGround;

import alice.kareltherobot.Direction;
import alice.kareltherobot.KarelWorld;

/**
 * A simple world in which four philosophers will think and eat together.
 * 
 * @author Joseph Bergin
 * 
 */
public class PhilosopherWorld extends KarelWorld
{

	public PhilosopherWorld()
	{
		super(new GrassyGround());
	}

	private Philosopher plato; /// declare the philosophers
	private Philosopher aristotle;
	private Philosopher kant;
	private Philosopher descartes;

	/**
	 * Execute the Think and Eat task of the four philosophers Run four philosophers
	 * together - competing for the forks.
	 */
	@Override
	public void run()
	{
		DoTogether.invokeAndWait(plato, aristotle, kant, descartes); /// this is where the concurrency occurs. 
	}

	/**
	 * Set up the philosopher situation Add four philosophers to the world and the "forks"
	 * they will use.
	 */
	@Override
	public void setTheStage()
	{
		cameraStayFocusedOn(3,3);
		readWorld("worlds", "philosopher.kwld"); // beeper forks
		plato = new Philosopher(2, 3, Direction.NORTH, 0, Color.RED);
		addRobot(plato);
		aristotle = new Philosopher(3, 2, Direction.EAST, 0, Color.GREEN);
		addRobot(aristotle);
		kant = new Philosopher(4, 3, Direction.SOUTH, 0, Color.BLUE);
		addRobot(kant);
		descartes = new Philosopher(3, 4, Direction.WEST, 0, Color.YELLOW);
		addRobot(descartes);
	}

}

The robots are all created and placed in the setTheStage method as usual. Then run the modified KarelProgram as the main.

 

Last Updated: March 16, 2010