CIT 594 Assignment 9: Robot, part 3
CIT 594, Spring 2005

File Input/Output

In my SwingExamples.zip program I gave examples of using JFileChooser to select a file for input or output, but I didn't give examples of actually reading from or writing to files. Here is some example code:

    private void load() {
        JFileChooser chooser = new JFileChooser();
        BufferedReader reader;
        String program = "";
        
        chooser.setDialogTitle("Load which file?");
        // Get the file chosen in a JChooser open file dialog
        int result = chooser.showOpenDialog(this);
        if (result == JFileChooser.APPROVE_OPTION) {
            File file = chooser.getSelectedFile();
            //...and prepare to use the file
            try {
                if (file == null) return;
                String fileName = file.getCanonicalPath();
                reader = new BufferedReader(new FileReader(fileName));
                // Read in the text file, appending newlines as needed
                String line = reader.readLine();
                while (line != null) {
                    program += line + "\n";
                    line = reader.readLine();
                }
                // Close the file when you're done with it
                reader.close();
            }
            catch (IOException e) { }
        }
        programArea.setText(program);
    }
	
    private void save() {
        String fileName;
        PrintWriter writer;
        JFileChooser chooser = new JFileChooser();
        
        chooser.setDialogTitle("Save file as?");
        // Get the file chosen in a JChooser save file dialog
        int result = chooser.showSaveDialog(this);
        if (result == JFileChooser.APPROVE_OPTION) {
            File file = chooser.getSelectedFile();
            //...and prepare to use the file
            try {
                if (file == null) return;
                fileName = file.getCanonicalPath();
                writer =
                    new PrintWriter(new FileOutputStream(fileName), true);  
                // Could use print and println, but this is easier:
                writer.write(programArea.getText());
                // Close the file when you're done with it
                writer.close();
            }
            catch (IOException e) { }       
        }
    }

Handling Threads

Here's an example of using one Thread to ask another Thread to start, pause, resume, and stop.


Here are the classes ThreadExample.java and Clock.java. To keep the classes short and simple, I made the boolean variables paused and stopped public in Clock, so they could be directly accessed from ThreadExample.

Specifics

When your program starts, a single nondaemon Thread (#1) executes your main method. Thread #1 executes code to create a GUI, and when it does this, Java automatically starts another nondaemon Thread (#2) to handle the GUI. Your Thread #1 reaches the end of the main method and, having nothing more to do, dies.

Thread #2 (the GUI Thread) monitors your GUI, and occasionally does something in response to your actions. One of those actions might be clicking the Run button. At this point, if you simply have a call to interpret, Thread #2 goes off and interprets the robot's program, and doesn't pay any more attention to the GUI until after it returns from interpret--which could be quite a long time. Until it returns, the GUI doesn't change. At that point, all of the changes you made to the GUI happen at once, and you only see the final result. This is bad.

To see the changes in the GUI as the robot does things, you need to leave the GUI Thread (#2) free to do its work, and that means you need to run the interpreter in a separate Thread (#3). Thus, while Thread #3 is doing the robot's work, Thread #2 is able to display it in the GUI.

So here's what you need to do:

public class RobotInterpreter extends Thread

Now, when your GUI (Thread #2) creates a RobotInterpreter, it will be creating a Thread (#3), because your RobotInterpreter is now a special kind of Thread (the kind that knows how to do robot things).

Your code for the Run button will look something like this:

interpreter = new RobotInterpreter(robby); // Create the new Thread
interpreter.setProgram(program); // Tell the interpreter what program to use
interpreter.start(); // Start the new Thread, then keep going

There is a new method in here, setProgram. That's because you can no longer call interpret(Tree program) directly--when a Thread starts, it starts in method public void run()--which, notice, has no parameters. So before you start the new Thread, you have to have some way to tell it what program it is going to use. That's the purpose of setProgram: to put the program Tree into a local variable of your RobotInterpreter object.

The call to start() returns right away, so the GUI (#2) can keep doing its thing, and the Java scheduler will soon create a new Thread (#3) and send it to the run() method contained in the interpreter object.

Since RobotInterpreter extends Thread, it has to replace the do-nothing version of run() that it inherits with something meaningful. Like this:

public void run() {
    interpret(program);
}

There is a little bit more to do, in order to handle the Pause and Stop buttons. Your interpret method should start out something like this:

private void interpret(Tree node) {
    if (stopped) return;
    while (paused) {
        try {
            Thread.sleep(100);
        }
        catch (InterruptedException e) { }
    }
    ...
}

And, of course, you have to give the variables stopped and paused the correct values.

Using the BoardGame API

Your RobotInterpreter tells your Robot to do things. Your Robot program is in charge of doing things; my BoardGame API is in charge of showing you what was done. Don't get these confused.

If, as I suggested, your Robot keeps track of everything in an array, it need a way to tell my BoardGame API what it is doing, in order to display stuff on the screen.

To put your robot on the board initially, create a Board (say, board) and a Robot (say, robby), then do board.place(robby, 0, 0). Once you have done this, the robot will know which board it is on, and the board will know that it has a robot on it. You can do something similar for all the other Pieces you need.

Each time your Robot does something that should be displayed, it should call some BoardGame method--either a method that it inherits directly (from Piece), or a method of the Board it is on. For example, if your robot moves to location [3][5] in your array, it should call moveTo(3, 5). Since your robot extends Piece, it inherits this method, and can use it directly. If it picks up the object at [3][6] on the board, one way to show this is to call board.remove(3, 6). If the robot turns to face in some other direction, this doesn't affect the BoardGame API in any way, but you still want to redraw the robot--so the robot should call redraw(), which is another inherited method.

 

Extra work

I had forgotten to ask you to write an actual program in the robot language. I'll do that now.

Create a Board containing just a few Obstacles, some Beepers, Apples, and Oranges. Your Robot and RobotInterpreter classes should be essentially independent of these other kinds of pieces, except that they have to distinguish moveable pieces from immovable ones. (Apples and oranges are moveable). By "essentially independent", I mean that (just for convenience) one of these classes might create these other objects and place them on the Board; but you shouldn't otherwise use these class names in Robot or RobotInterpreter.

I recommend that you create an interface Moveable that some of your pieces implement, and some don't.

The program you write in the robot's language will refer to these objects. For example, it might say take apple or if seeing obstacle.

The robot's job is to:

If you try to get every single beeper and piece of fruit, this could be a very hard problem. Don't make it that hard. The object is simply to write a reasonable program in this new language. Move some things around and make sure your robot works properly.

Due date

Since I've added (slightly) to the assignment, it's now due Tuesday, April 19, before midnight.