| CIT 594 Assignment
9: Robot, part 3 CIT 594, Spring 2005 |
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) { } } }
Here's
an example of using one Thread to ask another Thread to start, pause, resume,
and stop.
main method in the ThreadExample
class, which creates the GUI and terminates.Clock (which is
a Thread) and tells it to run().
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.
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 --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.
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.
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
or .
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.
Since I've added (slightly) to the assignment, it's now due Tuesday, April 19, before midnight.