// // BuggleWorld.java // BuggleDemoS.java // // Modified by Bob Martin about 4 years ago and again in April 2003. // [9/6/04] Modified by Lyn to merge in Wellesley changes. /* To Do: * Fix output so that doesn't generate spurious updates. * Make step do the right thing when no more steps to be taken. * Allow other instructions to be executed via menu between steps. (they are currently allowed but not handled correctly). */ /* *** Modified for Java 1.1 the last time I (bob martin) taught CX121 I tried to put 3 stars *** on the lines/section I modified; Modified again for the Swing classes (april, 2003); this time I tried to put 4 stars **** on the relevant lines */ // *** addItem( to add( import java.awt.*; import java.awt.event.*; // *** import java.applet.*; // applet.* ?? import java.util.*; import javax.swing.*; // **** public class BuggleWorld extends JApplet implements ActionListener { public int rows = 9; // Number of rows. public int cols = 9; // Number of columns. public Vector buggles; // The buggles in the world. public boolean bagels [] []; // Bagels (at most one per cell). public String strings [] []; //[9/6/04] Strings (at most one per cell). public Color marks [] []; // The trail marks in the world; public boolean horizontalWalls [] []; // leftmost points of horizontal wall segments. public boolean verticalWalls [] []; // bottommost points of vertical wall segments. private boolean boundariesAreWalls = true; private final static Color backgroundColor = Color.green; private final static Color buttonBackgroundColor = Color.white; public static BuggleWorld standardWorld; private BuggleGrid grid; private JPanel instructionPanel; // **** was Panel private JPanel controlPanel; private JPanel setPositionPanel; private JPanel setHeadingPanel; private JPanel setColorPanel; private JPanel setDelayPanel; // [9/6/04] //private JLabel output; public BuggleExecuter exec; private Buggle selectedBuggle; // Currently selected buggle. // Menu items apply to this buggle. // By default, it's the most recently created buggle. // Color selected in colorChoices menu private int selectedX = 1; private int selectedY = 1; private Direction selectedHeading = Direction.EAST; private Color selectedColor = Color.red; //private Choice colorChoices; private JComboBox colorChoices; // **** //private Choice headingChoices; private JComboBox headingChoices; // **** //private Choice xChoices; //private Choice yChoices; private JComboBox xChoices; // **** private JComboBox yChoices; // **** private JComboBox delayChoices; // [9/6/04] private boolean debugOn = false; private int buggleDelay = 0; // [9/6/04] Amount of time buggle waits between moves. // [9/6/04] Can be used to slow down buggle. public void debugPrintln(String s) { if (debugOn) System.out.println("Debug: " + s); } public void init() { debugPrintln("Start init();"); standardWorld = this; initializeWalls(); debugPrintln("Making executer"); exec = new BuggleExecuter(this); debugPrintln("Executer = " + exec); initState(); grid = new BuggleGrid(this); // Make grid in which buggles are displayed this.makeInstructionPanel(); // Make panel for instructions to the selected buggle this.makeControlPanel(); // Make panel for controlling execution of the buggle program this.makeOutput(); // Make the area for displaying textual feedback // System.out.println("Setting world layout"); /* this.getContentPane().setLayout( new BorderLayout() ); setBackground(backgroundColor); this.add("Center", grid); this.add("West", instructionPanel); this.add("South", controlPanel); */ Container c = getContentPane(); //**** debugPrintln("Setting world layout"); c.setLayout( new BorderLayout() ); setBackground( backgroundColor ); c.add( "Center", grid ); c.add( "West", instructionPanel ); c.add( "South", controlPanel ); //this.add("North", output); debugPrintln("setup();"); setup(); // Note: can't call this until *after* grid is made. // System.out.println("Stop init"); } public void makeInstructionPanel() { // System.out.println("Making instruction panel"); instructionPanel = new JPanel(); //Container cp = instructionPanel.getContentPane(); //**** //cp.setLayout(new GridLayout(11,1)); instructionPanel.setLayout(new GridLayout(12,1)); instructionPanel.setBackground(Color.green); newInstructionPanelItem("new Buggle()"); newInstructionPanelItemPair("forward()", "backward()"); newInstructionPanelItemPair("left()","right()"); newInstructionPanelItemPair("getPosition()", "getHeading()"); newInstructionPanelItemPair("getColor()", "isBrushDown()"); newInstructionPanelItemPair("isFacingWall()", "isOverBagel()"); //xChoices = new Choice(); // **** String xNames[] = {"1","2","3","4","5","6","7","8"}; xChoices = new JComboBox( xNames ); xChoices.setMaximumRowCount( 8 ); xChoices.addActionListener(this); //for (int x = 1; x <= 8; x++) { // xChoices.add(Integer.toString(x)); //} //yChoices = new Choice(); // **** String yNames[] = {"1","2","3","4","5","6","7","8"}; yChoices = new JComboBox( yNames ); yChoices.setMaximumRowCount( 8 ); yChoices.addActionListener(this); //for (int y = 1; y <= 8; y++) { // yChoices.add(Integer.toString(y)); //} setPositionPanel = new JPanel(); //Container c2 = setPositionPanel.getContentPane(); //**** //c2.setLayout(new FlowLayout()); setPositionPanel.setLayout(new FlowLayout()); JButton setPositionButton = new JButton("setPosition"); setPositionButton.setBackground(buttonBackgroundColor); setPositionPanel.add(setPositionButton); setPositionButton.addActionListener(this); // *** setPositionPanel.add(new JLabel("(new Point(")); setPositionPanel.add(xChoices); setPositionPanel.add(new JLabel(",")); setPositionPanel.add(yChoices); setPositionPanel.add(new JLabel("))")); newInstructionPanelItem(setPositionPanel); //headingChoices = new Choice(); // **** String hNames[] = {"Direction.EAST","Direction.NORTH","Direction.WEST","Direction.SOUTH"}; headingChoices = new JComboBox( hNames ); headingChoices.setMaximumRowCount( 4 ); headingChoices.addActionListener(this); //headingChoices.add("Direction.EAST"); //headingChoices.add("Direction.NORTH"); //headingChoices.add("Direction.WEST"); //headingChoices.add("Direction.SOUTH"); setHeadingPanel = new JPanel(); //Container c3 = setHeadingPanel.getContentPane(); //**** //c3.setLayout(new FlowLayout()); setHeadingPanel.setLayout(new FlowLayout()); JButton setHeadingButton = new JButton("setHeading"); setHeadingButton.setBackground(buttonBackgroundColor); setHeadingPanel.add(setHeadingButton); setHeadingButton.addActionListener(this); // *** setHeadingPanel.add(new JLabel("(")); setHeadingPanel.add(headingChoices); setHeadingPanel.add(new JLabel(")")); newInstructionPanelItem(setHeadingPanel); //colorChoices = new Choice(); // **** String cNames[] = {"Color.red","Color.green","Color.blue","Color.yellow","Color.cyan","Color.magenta"}; colorChoices = new JComboBox( cNames ); colorChoices.setMaximumRowCount( 6 ); colorChoices.addActionListener(this); //colorChoices.add("Color.red"); //colorChoices.add("Color.green"); // also line 363: cast getSelectedItem() //colorChoices.add("Color.blue"); //colorChoices.add("Color.yellow"); //colorChoices.add("Color.cyan"); //colorChoices.add("Color.magenta"); //??colorChoices.setBackground(selectedColor); setColorPanel = new JPanel(); //Container c4 = setColorPanel.getContentPane(); //**** //c4.setLayout(new FlowLayout()); setColorPanel.setLayout(new FlowLayout()); JButton setColorButton = new JButton("setColor"); setColorButton.setBackground(buttonBackgroundColor); setColorPanel.add(setColorButton); setColorButton.addActionListener(this); // *** setColorPanel.add(new JLabel("(")); setColorPanel.add(colorChoices); setColorPanel.add(new JLabel(")")); newInstructionPanelItem(setColorPanel); // [lyn, 9/6/04] All delay stuff is new String dNames[] = {"0","50","100","200","400", "800"}; delayChoices = new JComboBox( dNames ); delayChoices.setMaximumRowCount( 6 ); delayChoices.addActionListener(this); setDelayPanel = new JPanel(); setDelayPanel.setLayout(new FlowLayout()); JButton setDelayButton = new JButton("setDelay"); setDelayButton.setBackground(buttonBackgroundColor); setDelayPanel.add(setDelayButton); setDelayButton.addActionListener(this); // *** setDelayPanel.add(new JLabel("(")); setDelayPanel.add(delayChoices); setDelayPanel.add(new JLabel(")")); newInstructionPanelItem(setDelayPanel); newInstructionPanelItemPair("brushDown()","brushUp()"); // newInstructionPanelItem("brushDown()"); // newInstructionPanelItem("brushUp()"); newInstructionPanelItemPair("dropBagel()", "pickUpBagel()"); } public void newInstructionPanelItem (String s) { JButton b = new JButton(s); b.setBackground(buttonBackgroundColor); instructionPanel.add(b); b.addActionListener(this); // *** } public void newInstructionPanelItemPair (String s1, String s2) { JPanel p = new JPanel(); //Container c = p.getContentPane(); //**** //c.setLayout(new FlowLayout()); p.setLayout(new GridLayout(1,2)); p.setBackground(buttonBackgroundColor); JButton b1 = new JButton(s1); b1.setBackground(buttonBackgroundColor); JButton b2 = new JButton(s2); b2.setBackground(buttonBackgroundColor); p.add(b1); p.add(b2); instructionPanel.add(p); b1.addActionListener(this); // *** b2.addActionListener(this); } public void newInstructionPanelItem (Component c) { c.setBackground(buttonBackgroundColor); instructionPanel.add(c); } public void makeControlPanel () { // System.out.println("Making control panel"); controlPanel = new JPanel(); //Container c = controlPanel.getContentPane(); //**** //c.setLayout(new FlowLayout()); controlPanel.setLayout(new GridLayout(1,3)); controlPanel.setBackground(Color.blue); newControlPanelItem("Step"); newControlPanelItem("Run"); newControlPanelItem("Pause"); newControlPanelItem("Reset"); } public void newControlPanelItem (String s) { JButton b = new JButton(s); b.setBackground(buttonBackgroundColor); controlPanel.add(b); b.addActionListener(this); // *** } public void makeOutput() { // The following may not be true -- it may be an artifact of my misunderstanding, // since Labels give exacly the same problem. // TextFields are broken in Cafe! // When use TextFields here, spurious paints are generated and // strange things happen to the graphics contexts of other components. //output = new TextField("", 30); //output.setBackground(Color.white); //output.setForeground(Color.blue); //output.setEditable(false); // output = new JLabel("Output"); // output.setAlignment(JLabel.CENTER); } public void initState () { debugPrintln("Start initState();"); marks = new Color [cols+1] [rows+1]; // Entries will be null unless set otherwise. initializeBagels(); initializeStrings(); //[9/6/04] buggles = new Vector(); //bagels = new Vector(); selectedBuggle = null; debugPrintln("Finish initState();"); } public void setup () { // Do nothing. Intended to be overridden by subclasses. } /* *** old version *** public boolean handleEvent(Event evt) { if (evt.id == Event.WINDOW_DESTROY) { System.exit(0); } return super.handleEvent(evt); } */ // *** old *** public boolean action(Event event, Object arg) { // try { public void actionPerformed( ActionEvent event ) { Object src = event.getSource(); //[9/6/04] String arg = event.getActionCommand(); // *** debugPrintln("actionPerformed: command = " + arg + "; src = " + src); if (arg.equals("Run")) { debugPrintln("Calling paint from run"); // System.out.println("Run"); // initState(); grid.paint(); // changed **** exec.run(); } else if (arg.equals("Step")) { exec.step(); } else if (arg.equals("Pause")) { exec.pause(); } else if (arg.equals("Reset")) { resetHookBefore(); reset(); resetHookAfter(); // Modified on [9/6/04] // initState(); // debugPrintln("Calling paint from reset."); // grid.paint(); // exec.reset(); } else if (arg.equals("new Buggle()")) { clearOutput(); Buggle b = new Buggle(); // Note: new will automatically make b the selected buggle. } else if (arg.equals("forward()")) { clearOutput(); selectedBuggle().forward(); } else if (arg.equals("backward()")) { clearOutput(); selectedBuggle().backward(); } else if (arg.equals("left()")) { clearOutput(); selectedBuggle().left(); } else if (arg.equals("right()")) { clearOutput(); selectedBuggle().right(); } else if (arg.equals("getPosition()")) { clearOutput(); Point p = selectedBuggle().getPosition(); this.printValue(this.pointString(p)); } else if (arg.equals("getHeading()")) { clearOutput(); Direction d = selectedBuggle().getHeading(); this.printValue(this.directionString(d)); } else if (arg.equals("getColor()")) { clearOutput(); Color c = selectedBuggle().getColor(); this.printValue(this.colorString(c)); } else if (arg.equals("isBrushDown()")) { clearOutput(); boolean b = selectedBuggle().isBrushDown(); this.printValue(this.booleanString(b)); } else if (arg.equals("setPosition")) { clearOutput(); String arg4 = (String)xChoices.getSelectedItem(); // *** & **** selectedX = Integer.parseInt((String) arg4, 10); arg4 = (String)yChoices.getSelectedItem(); // *** selectedY = Integer.parseInt((String) arg4, 10); selectedBuggle().setPosition( new Point(selectedX, selectedY) ); } else if (arg.equals("setHeading")) { clearOutput(); String arg3 = (String)headingChoices.getSelectedItem(); // *** //selectedBuggle().setHeading(selectedHeading); if (arg3.equals("Direction.NORTH")) { //selectedHeading = Direction.NORTH; selectedBuggle().setHeading( Direction.NORTH ); } else if (arg3.equals("Direction.EAST")) { //selectedHeading = Direction.EAST; selectedBuggle().setHeading( Direction.EAST ); } else if (arg3.equals("Direction.SOUTH")) { selectedBuggle().setHeading( Direction.SOUTH ); //selectedHeading = Direction.SOUTH; } else if (arg3.equals("Direction.WEST")) { selectedBuggle().setHeading( Direction.WEST ); //selectedHeading = Direction.WEST; } } else if (arg.equals("setDelay") || (src == delayChoices)) { // [lyn, 9/6/04] clearOutput(); int delay = Integer.parseInt((String) delayChoices.getSelectedItem()); setBuggleDelay(delay); } else if (arg.equals("setColor")) { clearOutput(); String arg2 = (String)colorChoices.getSelectedItem(); // *** //selectedBuggle().setColor( Color.blue ); // *** old selectedBuggle().setColor(selectedColor); if (arg2.equals("Color.red")) { //selectedColor = Color.red; selectedBuggle().setColor( Color.red ); } else if (arg2.equals("Color.green")) { //selectedColor = Color.green; selectedBuggle().setColor( Color.green ); } else if (arg2.equals("Color.blue")) { //selectedColor = Color.blue; selectedBuggle().setColor( Color.blue ); } else if (arg2.equals("Color.yellow")) { //selectedColor = Color.yellow; selectedBuggle().setColor( Color.yellow ); } else if (arg2.equals("Color.cyan")) { //selectedColor = Color.cyan; selectedBuggle().setColor( Color.cyan ); } else if (arg2.equals("Color.magenta")) { //selectedColor = Color.magenta; selectedBuggle().setColor( Color.magenta ); } // *** old *** } else if (event.target == xChoices) { } else if (src == xChoices) { //String arg4 = xChoices.getSelectedItem(); // *** //selectedX = Integer.parseInt((String) arg4, 10); //xAction(arg4); } else if (src == yChoices) { //String arg5 = yChoices.getSelectedItem(); // *** //yAction(arg5); } else if (src == headingChoices) { //headingAction(arg); } else if (src == colorChoices) { colorAction(arg); } else if (arg.equals("brushDown()")) { clearOutput(); selectedBuggle().brushDown(); } else if (arg.equals("brushUp()")) { clearOutput(); selectedBuggle().brushUp(); } else if (arg.equals("isFacingWall()")) { clearOutput(); boolean b = selectedBuggle().isFacingWall(); this.printValue(this.booleanString(b)); } else if (arg.equals("isOverBagel()")) { clearOutput(); boolean b = selectedBuggle().isOverBagel(); this.printValue(this.booleanString(b)); } else if (arg.equals("pickUpBagel()")) { clearOutput(); selectedBuggle().pickUpBagel(); } else if (arg.equals("dropBagel()")) { clearOutput(); selectedBuggle().dropBagel(); } else { System.out.println("Unhandled arg: " + arg); // *** return super.action(event, arg); } //repaint(); //**** //this.paint(); // *** // *** old *** return true; //} catch (BuggleException e) { // printError(e.getMessage()); // return true; //} } public void xAction (Object arg) { selectedX = Integer.parseInt((String) arg, 10); } public void yAction (Object arg) { selectedY = Integer.parseInt((String) arg, 10); } public void headingAction (Object arg) { //if (arg.equals("Direction.NORTH")) { // selectedHeading = Direction.NORTH; //} else if (arg.equals("Direction.EAST")) { // selectedHeading = Direction.EAST; //} else if (arg.equals("Direction.SOUTH")) { // selectedHeading = Direction.SOUTH; //} else if (arg.equals("Direction.WEST")) { // selectedHeading = Direction.WEST; //} } public void colorAction (Object arg) { /* arg = colorChoices.getSelectedItem(); // *** //selectedBuggle().setColor( Color.blue ); if (arg.equals("Color.red")) { selectedColor = Color.red; } else if (arg.equals("Color.green")) { selectedColor = Color.green; } else if (arg.equals("Color.blue")) { selectedColor = Color.blue; } else if (arg.equals("Color.yellow")) { selectedColor = Color.yellow; } else if (arg.equals("Color.cyan")) { selectedColor = Color.cyan; } else if (arg.equals("Color.magenta")) { selectedColor = Color.magenta; } */ //colorChoices.setBackground( selectedColor ); } public String pointString (Point p) { return "Point[x=" + p.x + ", y=" + p.y + "]"; } public String directionString (Direction d) { return "Direction[dir=" + d + "]"; } public String colorString (Color c) { return "Color[red=" + c.getRed() + ", blue=" + c.getBlue() + ", green=" + c.getGreen() + "]"; } public String booleanString (boolean b) { if (b) { return "true"; } else { return "false"; } } public void printError (String s) { // Print error message in output area. // System.out.println("printError: " + s); String message = "Buggle Error: " + s; System.out.println(message); // Output is broken for now. // output.setForeground(Color.red); // output.setText(message); } public void printValue (String s) { // Print value in output area. // System.out.println("printValue: " + s); String message = "Value: " + s; System.out.println(message); // Output is broken for now. // output.setForeground(Color.blue); // output.setText(message); } public void printInstruction (String s) { // Print instruction in output area. // System.out.println("printInstruction: " + s); System.out.println(s); // Output is broken for now. // output.setForeground(Color.black); // output.setText(s); } public void clearOutput() { //System.out.println("clearOutput: "); //output.setText(""); } public Buggle selectedBuggle () { // Return the currentBuggle, or yell if it's null. if (selectedBuggle == null) { throw new BuggleException ("No buggle is selected!"); } else { return selectedBuggle; } } //*** added the next three public void reset() { debugPrintln("BuggleWorld.reset()"); initState(); debugPrintln("Calling paint from reset."); grid.paint(); exec.reset(); debugPrintln("Finish BuggleWorld.reset()"); } public void resetHookBefore() { // Hook to do extra things when press resetbutton } public void resetHookAfter() { // Hook to do extra things when press resetbutton } public void setDimensions (int cols, int rows) { debugPrintln("Start setDimensions(" + cols + ", " + rows + ")"); this.cols = cols; this.rows = rows; initializeWalls(); grid.makeGrid(); reset(); debugPrintln("Finish setDimensions(" + cols + ", " + rows + ")"); } //*** to here public void selectBuggle (Buggle b) { Buggle previousSelected = selectedBuggle; selectedBuggle = b; this.buggleChanged(selectedBuggle); if (previousSelected != null) { this.buggleChanged(previousSelected); } } private void initializeBagels() { bagels = new boolean [cols] [rows]; for (int x = 0; x < cols; x ++) { for (int y = 0; y < rows; y++) { bagels[x][y] = false; } } } private void initializeStrings() {//[9/6/04] debugPrintln("intializeStrings(); cols = " + cols + "; rows = " + rows); strings = new String [cols] [rows]; } protected void initializeWalls () { debugPrintln("Start initializeWalls"); horizontalWalls = new boolean [cols + 1] [rows + 1]; verticalWalls = new boolean [cols + 1] [rows + 1]; for (int x = 0; x <= cols; x ++) { for (int y = 0; y <= rows; y++) { horizontalWalls[x][y] = false; verticalWalls[x][y] = false; } } // Add boundaries at walls if specified. for (int x = 0; x <= cols; x++) { horizontalWalls[x][0] = boundariesAreWalls; horizontalWalls[x][rows] = boundariesAreWalls; } for (int y = 0; y <= rows; y++) { verticalWalls[0][y] = boundariesAreWalls; verticalWalls[cols][y] = boundariesAreWalls; } debugPrintln("Finish initializeWalls"); } public void addHorizontalWall (int x, int y) { if ((0 <= x) && (x <= cols) && (0 <= y) && (y <= rows)) { horizontalWalls[x][y] = true; } else { throw new BuggleException( "addHorizontalWall: point out of range -- (" + x + ", " + y + ")"); } } public void addVerticalWall (int x, int y) { if ((0 <= x) && (x <= cols) && (0 <= y) && (y <= rows)) { verticalWalls[x][y] = true; } else { throw new BuggleException( "addVerticalWall: point out of range -- (" + x + ", " + y + ")"); } } // public void start () { // // System.out.println("start()"); // //debugPrintln("Calling BuggleGraid.paint() from start()"); // //**** grid.paint(); // **** this caused problems - too soon? // } public void start () { // System.out.println("start()"); debugPrintln("Calling BuggleGraid.paint() from start()"); grid.paint(); } public void paint( Graphics g ) { //debugPrintln("BuggleWorld asked by system to paint(g)."); super.paint( g ); } // public void paintComponent( Graphics g ) { // need to extend JPanel for this? // //debugPrintln("BuggleWorld asked by system to paint(g)."); // super.paintComponent( g ); // } public void run () { // This is a hook that subclasses can override without having to worry // about other operations that might be performed by start(). // By default, does nothing. this.printError("Default run() behavior of BuggleWorld is to do nothing."); } public Point addCoordinates(Point p1, Point p2) { int newx = ((p1.x + p2.x - 1) % cols) + 1; if (newx <= 0) newx = newx + cols; int newy = ((p1.y + p2.y - 1) % rows) + 1; if (newy <= 0) newy = newy + rows; Point p = new Point(newx, newy); /* System.out.println("Add coords: p1 = " + p1 + "; p2 = " + p2 + "; result = " + p); */ return p; } public boolean isBagelAt(Point p) { return bagels[p.x - 1][p.y - 1]; } public void addBagel(Point p) { bagels[p.x - 1][p.y - 1] = true; cellChanged(p); } public void addBagel(int x, int y) { // This version is called from interface and does *not* update visual rep. bagels[x - 1][y - 1] = true; } public void removeBagel(Point p) { bagels[p.x - 1][p.y - 1] = false; cellChanged(p); } public void removeBagel(int x, int y) { // This version is called from interface and does *not* updated visual rep. bagels[x - 1][y - 1] = false; } public void addString(Point p, String s) { //[9/6/04] strings[p.x - 1][p.y - 1] = s; cellChanged(p); } public boolean isStringAt(Point p) { // [9/6/04] return (strings[p.x - 1][p.y - 1]) != null; } public String getStringAt(Point p) { // [9/6/04] String s = strings[p.x - 1][p.y - 1]; String result; if (s == null) { result = ""; } else { result= s; } // System.out.println("getStringAt(" + p + ") = " + result); return result; } public void add (Buggle b) { // System.out.println("Buggles=" + buggles ); buggles.addElement(b); debugPrintln("Calling BuggleGrid.draw() from BuggleWorld.add(Buggle)"); grid.draw(b); } public boolean wallInDirection(Point p, Direction d) { // Indicate whether there is a wall in the d direction of the grid cell at point p. if (d == Direction.NORTH) { return horizontalWalls[p.x-1][p.y]; } else if (d == Direction.EAST) { return verticalWalls[p.x][p.y-1]; } else if (d == Direction.SOUTH) { return horizontalWalls[p.x-1][p.y-1]; } else if (d == Direction.WEST) { return verticalWalls[p.x-1][p.y-1]; } else { throw new BuggleException("Shouldn't happen: wallInDirection"); } } void cellChanged(Point p) { debugPrintln("Calling drawCell from cellChanged " + p); grid.drawCell(p); } void buggleChanged(Buggle b) { debugPrintln("Calling drawCell from buggleChanged "); grid.drawCell(b.position()); } public void buggleMoved(Buggle b, Point oldpos, Point newpos) { // We have been informed that B has moved from oldpos to newpos. // For multiple buggles, should move buggle to top! debugPrintln("Calling drawCell (1) from buggleMoved"); grid.drawCell(oldpos); debugPrintln("Calling drawCell (2) from buggleMoved"); grid.drawCell(newpos); } /*public void addBagel(Point p) { // Adds a bagel at coordinate p. Assumes at most one bagel at any position. // So adding a bagel to a cell with a bagel already in it has no effect. bagels.addElement(p); // represent a bagel as a point. } public void removeBagel(Point p) { for (int i = bagels.size() - 1; i >= 0; i--) { if (((Point) bagels.elementAt(i)).equals(p)) { bagels.removeElementAt(i); } } } public boolean bagelAt(Point p) { for (int i = bagels.size() - 1; i >= 0; i--) { if (((Point) bagels.elementAt(i)).equals(p)) { return true; } } return false; }*/ public void draw (Buggle b) { debugPrintln("Draw buggle " + b); debugPrintln("Position = " + nullify(b.position())); grid.draw(b); //repaint(); //**** } public String nullify (Object obj) { if (obj == null) return "null"; else return obj.toString(); } public void markTrail(Point p, Color c) { marks[p.x][p.y] = c; } public Color markColorAt(Point p) { return marks[p.x][p.y]; } public boolean withCompanion (Buggle b) { Point pos = b.position(); for (int i = 0; i < buggles.size(); i++) { Buggle x = (Buggle) buggles.elementAt(i); if (pos.equals(x.position()) && (x != b)) return true; } return false; } public int getBuggleDelay () { //[9/6/04] return buggleDelay; } public void setBuggleDelay (int i) { //[9/6/04] buggleDelay = i; } } class BuggleGrid extends Canvas //{ implements MouseListener, MouseMotionListener { // *** /* A rectangular area of the BuggleWorld applet that displays the state of the world */ public BuggleWorld world; // private Graphics gfx; // Graphics context of this grid private int cellWidth; private int cellHeight; private Rectangle gridRect; private final static Color floorColor = Color.white; private final static Color gridLineColor = Color.green; private final static Color bagelColor = new Color (200,100,50); private final static Color wallColor = Color.black; private Point lastHorizontalWall; private Point lastVerticalWall; private Graphics gfx; public void init () { // *** this.addMouseListener(this); // *** this.addMouseMotionListener(this); // *** } public BuggleGrid(BuggleWorld bw) { world = bw; makeGrid(); } // *** size() has been depreciated *** public void makeGrid() { // System.out.println("makeGrid() Size: width = " + d.width + "; height = " + d.height); Dimension d = getSize(); // ?? size(); cellWidth = d.width / world.cols; cellHeight = d.height / world.rows; gridRect = new Rectangle(0, 0, world.cols * cellWidth, world.rows * cellHeight); } public Point cellOrigin (Point p) { // Returns the graphics coordinate of the upper left corner of the cell at coord p. // Cell coordinates range from (1,1) to (cols, rows), from lower left to upper right. return new Point( (p.x - 1) * cellWidth, (world.rows - p.y) * cellHeight); } public Rectangle cellRectangle (Point p) { // Cell coordinates range from (1,1) to (cols, rows), from lower left to upper right. Point origin = cellOrigin(p); // Account for width of grid line in rectangle dimensions. // (Don't include grid lines in rectangle.) return new Rectangle(origin.x + 1, origin.y + 1, cellWidth - 2, cellHeight - 2); } public void paintGrid() { world.debugPrintln("BuggleGrid.paintGrid()"); gfx = this.getGraphics(); makeGrid(); gfx.setColor(floorColor); gfx.fillRect(gridRect.x, gridRect.y, gridRect.width, gridRect.height); int left = gridRect.x; int right = left + gridRect.width; int top = gridRect.y; int bottom = top + gridRect.height; gfx.setColor(gridLineColor); // Paint horizontal grid lines for (int j = 0; j <= world.rows; j++) { gfx.drawLine(left, j * cellHeight, right, j * cellHeight); } // Paint vertical grid lines for (int i = 0; i <= world.cols; i++) { gfx.drawLine(i * cellWidth, top, i * cellWidth, bottom); } // Could say the following, but it is better (from visual perspective) // to break up into more primitive operations. /*for (int i=1; i<=world.rows; i++) { for (int j=1; j<=world.cols; j++) { world.debugPrintln("Calling drawCell from paintGrid"); drawCell(new Point(i,j)); }} */ // Paint trails & bagels & strings world.debugPrintln("in paintGrid: painting trails and bagels"); for (int i=1; i<=world.rows; i++) { for (int j=1; j<=world.cols; j++) { Point p = new Point(i,j); world.debugPrintln("in paintGrid: testing mark at (" + i + ", " + j + ")"); // Fill the background color of the cell. // It's either the floor color or the trail color. Color c = world.markColorAt(p); if (c != null) drawMark(c, p); else c = floorColor; // Draw any bagels if (world.isBagelAt(p)) drawBagel(p, c); // Draw any strings if (world.isStringAt(p)) { drawString(p, c); } } } // Paint walls world.debugPrintln("in paintGrid: painting walls"); for (int x = 0; x <= world.cols; x ++) { for (int y = 0; y <= world.rows; y++) { world.debugPrintln("in paintGrid: testing horizontal wall at (" + x + ", " + y + ")"); if(world.horizontalWalls[x][y]) { drawHorizontalWall(x,y); } world.debugPrintln("in paintGrid: testing vertical wall at (" + x + ", " + y + ")"); if(world.verticalWalls[x][y]) { drawVerticalWall(x,y); } } } // Paint buggles Enumeration bugs = world.buggles.elements(); while ( bugs.hasMoreElements() ) { Buggle next = (Buggle) bugs.nextElement(); this.draw(next); } // [9/6/04] Causes a painting loop! // world.debugPrintln("calling repaint() from BuggleGrid.paintGrid()"); // repaint(); //***** needed this **** } public void drawHorizontalWall(int x, int y) { // Graphics gfx = this.getGraphics(); Point wp = wallOrigin(new Point(x,y)); if (world.horizontalWalls[x][y]) { gfx.setColor(wallColor); gfx.drawLine(wp.x, wp.y-1, wp.x + cellWidth, wp.y-1); gfx.drawLine(wp.x, wp.y, wp.x + cellWidth, wp.y); gfx.drawLine(wp.x, wp.y+1, wp.x + cellWidth, wp.y+1); } else { gfx.setColor(gridLineColor); gfx.drawLine(wp.x, wp.y, wp.x + cellWidth, wp.y); } } public void drawVerticalWall(int x, int y) { // Graphics gfx = this.getGraphics(); Point wp = wallOrigin(new Point(x,y)); if (world.verticalWalls[x][y]) { gfx.setColor(wallColor); gfx.drawLine(wp.x-1, wp.y - cellHeight, wp.x-1 , wp.y); gfx.drawLine(wp.x, wp.y - cellHeight, wp.x , wp.y); gfx.drawLine(wp.x+1, wp.y - cellHeight, wp.x+1 , wp.y); } else { gfx.setColor(gridLineColor); gfx.drawLine(wp.x, wp.y - cellHeight, wp.x , wp.y); } } public Point wallOrigin (Point p) { // Returns the graphics coordinate of the wall point denoted by coordinate p. // Wall coordinates range from (0,0) to (cols, rows), from lower left to upper right. return new Point(p.x * cellWidth, (world.rows - p.y) * cellHeight); } public void paint() { world.debugPrintln("BuggleGrid paint();"); this.paint( gfx ); //this.getGraphics() } public void paint( Graphics g ) { // super.paint(g); //****?? paintComponent?? world.debugPrintln("BuggleGrid paint(Graphics g);"); paintGrid(); // System.out.println("Start Paint"); // resize(); // Paint floor: //System.out.println("Begin printing all buggles."); Enumeration bugs = world.buggles.elements(); //System.out.println("Begin printing all buggles1."); while (bugs.hasMoreElements()) { //System.out.println("Begin printing all buggles2."); Buggle b = (Buggle) bugs.nextElement(); //System.out.println("Begin printing all buggles3."); world.debugPrintln("Calling drawBuggle from BuggleGrid.paint(Graphics g)"); this.draw(b); } //System.out.println("End printing all buggles."); // System.out.println("Stop Paint"); } public void draw( Buggle b ) { //System.out.println("Draw buggle " + b); //System.out.println("Position = " + nullify(b.position())); world.debugPrintln("Calling drawCell from draw(Buggle)"); drawCell(b.position()); } public String nullify (Object obj) { if (obj == null) return "null"; else return obj.toString(); } public void drawCell (Point p) { world.debugPrintln("Draw cell " + p); Color c = world.markColorAt(p); //System.out.println("Mark color = " + nullify(c)); if (c != null) { drawMark(c, p); } else { c = floorColor; } // Fill the background color of the cell. // It's either the floor color or the trail color. Rectangle r = this.cellRectangle(p); if (gfx == null) { Graphics gfx = this.getGraphics(); } gfx.setColor(c); gfx.setPaintMode(); gfx.fillRect(r.x, r.y, r.width, r.height); // Draw any bagels if (world.isBagelAt(p)) { drawBagel(p, c); } // Draw any strings [9/6/04] if (world.isStringAt(p)) { drawString(p, c); } // Draw any buggles in this cell drawBugglesAt(p); // Redraw any walls adjoining this cell if (world.horizontalWalls[p.x-1][p.y-1]) { drawHorizontalWall(p.x-1, p.y-1); } if (world.verticalWalls[p.x-1][p.y-1]) { drawVerticalWall(p.x-1, p.y-1); } if (world.horizontalWalls[p.x-1][p.y]) { drawHorizontalWall(p.x-1, p.y); } if (world.verticalWalls[p.x][p.y-1]) { drawVerticalWall(p.x, p.y-1); } // Experiment to see if yielding fixes drawing problem in step. It doesn't. //System.out.println("drawCell yielding."); //**** Thread.yield(); //*** //System.out.println("drawCell after yield."); } public void drawMark (Color c, Point p) { // Really want stipple pattern here -- how to get it? // Graphics gfx = this.getGraphics(); world.debugPrintln("drawMark(" + c + ", " + p + ")"); Rectangle r = cellRectangle(p); gfx.setColor(c); gfx.setPaintMode(); gfx.fillRect(r.x, r.y, r.width, r.height); } public void drawBagel(Point p, Color background) { world.debugPrintln("drawBagel(" + p + ", " + background + ")"); int inset = 3; double holeFactor = 0.35; Point origin = cellOrigin(p); int bagelX = origin.x + inset; int bagelY = origin.y + inset; int bagelWidth = cellWidth - (2*inset); int bagelHeight = cellHeight - (2*inset); int holeWidth = (int) (holeFactor*bagelWidth); int holeHeight = (int) (holeFactor*bagelHeight); int holeX = bagelX + ((bagelWidth - holeWidth)/2); int holeY = bagelY + ((bagelHeight - holeHeight)/2); // Graphics gfx = this.getGraphics(); gfx.setColor(bagelColor); gfx.fillOval(bagelX, bagelY, bagelWidth, bagelHeight); gfx.setColor(background); gfx.fillOval(holeX , holeY, holeWidth, holeHeight); gfx.setColor(Color.black); gfx.drawOval(bagelX, bagelY, bagelWidth, bagelHeight); gfx.drawOval(holeX , holeY, holeWidth, holeHeight); } public void drawString(Point p, Color background) { world.debugPrintln("drawString(" + p + ", " + background + ")"); int inset = 3; String s = world.getStringAt(p); Point origin = cellOrigin(p); int stringX = origin.x + inset; int stringY = origin.y + cellHeight - inset; gfx.setColor(Color.black); gfx.drawString(s, stringX, stringY); } public void drawBugglesAt (Point p) { // System.out.println("drawBugglesAt " + p); // System.out.println("buggles = " + nullify(world.buggles)); /*for (int i = world.buggles.size() - 1; i >= 0; i--) { Buggle b = (Buggle) world.buggles.elementAt(i); if (b.getPosition().equals(p)) b.drawInRect(this.getGraphics(), cellRectangle(p)); break; }*/ // Draw all buggles at current position. for (int i = 0; i < world.buggles.size(); i++) { Buggle b = (Buggle) world.buggles.elementAt(i); if (b.position().equals(p)) b.drawInRect( //this.getGraphics(), gfx, cellRectangle(p) ); } } // This method is called when the user clicks the mouse to start a scribble. // *** 1.0 public boolean mouseDown(Event e, int x, int y) { public void mousePressed(MouseEvent e) { int x = e.getX(), y = e.getY(); // *** if ( mouseInHorizontalWall(x,y) ) { Point p = horizontalWallAt(x,y); world.horizontalWalls[p.x][p.y]=!world.horizontalWalls[p.x][p.y]; if (p == lastHorizontalWall) { gfx.setXORMode(Color.white); drawHorizontalWall(p.x,p.y); } if (world.horizontalWalls[p.x][p.y]) { gfx.setPaintMode(); drawHorizontalWall(p.x,p.y); } gfx.setXORMode(Color.white); drawHorizontalWall(p.x,p.y); } else if (mouseInVerticalWall(x,y)) { Point p = verticalWallAt(x,y); world.verticalWalls[p.x][p.y]=!world.verticalWalls[p.x][p.y]; drawVerticalWall(p.x,p.y); if (p == lastVerticalWall) { gfx.setXORMode(Color.white); drawVerticalWall(p.x,p.y); } if (world.verticalWalls[p.x][p.y]) { gfx.setPaintMode(); drawVerticalWall(p.x,p.y); } gfx.setXORMode(Color.white); drawVerticalWall(p.x,p.y); } // ***return true; } // *** 1.0 public boolean mouseMove(Event e, int x, int y) { public void mouseDragged(MouseEvent e) { // This is hard to comprehend an inefficient. // Certainly there must be a better way of handling this! int x = e.getX(), y = e.getY(); // *** if (mouseInHorizontalWall(x,y)) { if (lastVerticalWall != null) { mouseLeavesVerticalWall(lastVerticalWall); lastVerticalWall = null; } Point h = horizontalWallAt(x,y); if (!(h.equals(lastHorizontalWall))) { if (lastHorizontalWall != null) { mouseLeavesHorizontalWall(lastHorizontalWall); } lastHorizontalWall = h; mouseEntersHorizontalWall(lastHorizontalWall); } } else if (mouseInVerticalWall(x,y)) { if (lastHorizontalWall != null) { mouseLeavesHorizontalWall(lastHorizontalWall); lastHorizontalWall = null; } Point v = verticalWallAt(x,y); if (!(v.equals(lastVerticalWall))) { if (lastVerticalWall != null) { mouseLeavesVerticalWall(lastVerticalWall); } lastVerticalWall = v; mouseEntersVerticalWall(v); } } else if (lastVerticalWall != null) { mouseLeavesVerticalWall(lastVerticalWall); lastVerticalWall = null; } else if (lastHorizontalWall != null) { mouseLeavesHorizontalWall(lastHorizontalWall); lastHorizontalWall = null; } // *** return true; } public void mouseEntersHorizontalWall(Point p) { // Graphics gfx = this.getGraphics(); gfx.setXORMode(Color.white); drawHorizontalWall(p.x, p.y); } // ***The other, unused methods of the MouseListener interface. public void mouseReleased(MouseEvent e) {;} public void mouseClicked(MouseEvent e) {;} public void mouseEntered(MouseEvent e) {;} public void mouseExited(MouseEvent e) {;} // ***The other method of the MouseMotionListener interface. public void mouseMoved(MouseEvent e) {;} public void mouseLeavesHorizontalWall(Point p) { // Graphics gfx = this.getGraphics(); gfx.setXORMode(Color.white); drawHorizontalWall(p.x, p.y); } public void mouseEntersVerticalWall(Point p) { // Graphics gfx = this.getGraphics(); gfx.setXORMode(Color.white); drawVerticalWall(p.x, p.y); } public void mouseLeavesVerticalWall(Point p) { // Graphics gfx = this.getGraphics(); gfx.setXORMode(Color.white); drawVerticalWall(p.x, p.y); } public boolean mouseInHorizontalWall(int x, int y) { int probe = (y + 1) % cellHeight; return ((0 <= probe) && (probe <= 2)); } public boolean mouseInVerticalWall(int x, int y) { int probe = (x + 1) % cellWidth; return ((0 <= probe) && (probe <= 2)); } public Point horizontalWallAt(int x, int y) { return new Point (x/cellWidth, world.cols - ((y+1)/cellHeight)); } public Point verticalWallAt(int x, int y) { return new Point ((x+1)/cellWidth, world.cols - (y/cellHeight) - 1); } /* // This method is called when the user drags the mouse. public boolean mouseDrag(Event e, int x, int y) { // System.out.println("Mouse Drag Event" + "; x = " + x + "; y = " + y); return true; }*/ } class BuggleExecuter { // Control the executiong of buggles. Allow buggles to be stepped, // be run until explicitly paused, or be reset. private BuggleRunner runner; // Encapsulates running into object suitable for thread. // Make only one of these. private Thread thread; // Make new thread every time reset. //private javax.swing.Timer timer; // **** Make new thread every time reset. private BuggleWorld world; private boolean stepMode = false; private boolean isFirstSteppedInstruction = true; private String currentInstruction; private static int state; private static final int UNSTARTED = 0; private static final int RUNNING = 1; // Running or scheduled to be run. private static final int YIELDED = 2; private static final int SUSPENDED = 3; private static final int DELAY = 30; // **** private static boolean execDebug = false; public BuggleExecuter(BuggleWorld w) { world = w; runner = new BuggleRunner(w); init(); } // [9/6/04] public void execDebugPrintln(String s) { if (execDebug) { System.out.println("Exec debug: " + s); } } public void init () { thread = new Thread(runner); //timer = new javax.swing.Timer(DELAY, null); // **** //****?? getContentPane().add( new ReboundPanel(timer) ); state = UNSTARTED; execDebugPrintln("init: state set to UNSTARTED"); stepMode = false; execDebugPrintln("init: stepMode is false"); currentInstruction = null; isFirstSteppedInstruction = true; } public void run() { world.debugPrintln("run()"); // Run buggles until done or sent an explicit stop message. stepMode = false; execDebugPrintln("run: set stepMode to false"); go(); } public void step() { execDebugPrintln("step()"); stepMode = true; execDebugPrintln("step: set stepMode to true."); if ((! isFirstSteppedInstruction) && (! (currentInstruction.equals("")))) { world.printInstruction(currentInstruction); currentInstruction = ""; // Needed so don't print anything when no more instructions. } go(); } private void go() { execDebugPrintln("go: STATE = " + stateString()); if (state == UNSTARTED) { execDebugPrintln("go: starting thread"); state = RUNNING; // [9/6/04] execDebugPrintln("go: state set to RUNNING"); thread.start(); execDebugPrintln("go: after starting thread"); } else if (state == SUSPENDED) { execDebugPrintln("go: resuming thread"); state = RUNNING; execDebugPrintln("go: state set to RUNNING"); thread.resume(); // [9/6/04] Schedules buggle thread to run again (but doesn't run yet). execDebugPrintln("go: after resuming thread"); } else { // Already running or yielded -- ignore. // System.out.println("Buggle Execution Error -- unexpected state in go(): " + stateString()); } } public void pause() { execDebugPrintln("pause()"); if ((state == UNSTARTED) || (state == SUSPENDED)) { // Do nothing } else { if ( thread.isAlive() ) { //if clause added by alice (01/22/03) in order to avoid SecurityException by suspending a dead thread execDebugPrintln("pause: suspending thread " + stateString()); thread.suspend(); // [9/6/04] state = SUSPENDED; // [9/6/04] execDebugPrintln("pause: state set to SUSPENDED"); } isFirstSteppedInstruction = true; execDebugPrintln("pause: after suspending thread " + stateString()); } } public void reset() { execDebugPrintln("reset()"); if ((! (state==UNSTARTED)) && thread.isAlive() ) { //[9/6/04] //additional clause added by alice (01/22/03) in order to avoid SecurityException by suspending a dead thread execDebugPrintln("reset: stopping thread " + stateString()); thread.stop(); } execDebugPrintln("reset: resetting runner."); runner.reset(); init(); } public void waitIfNecessary(String instruction) { // Buggles call this method when about to perform the next primitive. // Message is a string documenting what the next primitive is. // This is the only method that the thread being controlled by the // executer actually calls. So must be careful to update state *before* // suspending thread! // If not in step mode, return immediately. // If in step mode, wait until next step is indicated. // First instruction needs to be treated specially. execDebugPrintln("waitIfNecessary: " + instruction); int delay = world.getBuggleDelay(); if (delay > 0) { try { Thread.sleep(delay); } catch (InterruptedException e) { } } if (state != RUNNING) { // [9/6/04] // Don't do anything special -- buggle routines were called during initialization. execDebugPrintln("waitIfNecessary: called when not running"); } else { if (stepMode) { if (isFirstSteppedInstruction) { // No instruction is pending, so execute this one by returning. execDebugPrintln("waitIfNecessary: printing first instruction."); world.printInstruction(instruction); isFirstSteppedInstruction = false; } else { // [9/6/04] // Careful: perform all thread updates *before* suspending thread! currentInstruction = instruction; execDebugPrintln("waitIfNecessary: suspending thread " + stateString()); state = SUSPENDED; execDebugPrintln("waitIfNecessary: state set to SUSPENDED"); stepMode = false; execDebugPrintln("waitIfNecessary: stepMode set to false"); thread.suspend(); // Note: this unschedules the current thread! execDebugPrintln("waitIfNecessary: after suspending thread " + stateString()); } } else { // [9/6/04] execDebugPrintln("waitIfNecessary: yielding"); state = YIELDED; execDebugPrintln("waitIfNecessary: state set to YIELDED"); Thread.yield(); // Let interface run no matter what state = RUNNING; execDebugPrintln("waitIfNecessary: state set to RUNNING"); execDebugPrintln("waitIfNecessary: after yielding"); } } } public String stateString () { if (state == UNSTARTED) { return "UNSTARTED"; } else if (state == RUNNING) { return "RUNNNING"; } else if (state == YIELDED) { return "YIELDED"; } else if (state == SUSPENDED) { return "SUSPENDED"; } else { return "UNKNOWN"; } } } class BuggleRunner implements Runnable { /* A way to encapsulate the behavior of the buggles into a thread-like object */ private BuggleWorld world; private boolean done = false; public BuggleRunner(BuggleWorld w) { // System.out.println("new Buggle(runner)"); world = w; } public void run () { // System.out.println("BuggelRunner run();"); world.run(); done = true; } public boolean isDone() { return done; } public void reset () { // System.out.println("BuggelRunner reset();"); done = false; } } class Buggle { private BuggleWorld world; // The world to which the Buggle belongs. private Point position; // Location of the Buggle private static final int _defaultX = 1; private static final int _defaultY = 1; private Direction heading; private Color color; // Color of the Buggle. private boolean trailmode = true; private static final boolean _defaultTrailmode = true; private static final Color _defaultColor = Color.red; private static final Color selectedBuggleOutlineColor = Color.black; private static final Color unselectedBuggleOutlineColor = Color.white; private static final int inset = 3; // Number of pixels by which drawn buggle is inset from cell edge private BuggleExecuter exec; public Buggle() { this(_defaultColor, _defaultX, _defaultY); } public Buggle(int x, int y) { this(_defaultColor, x, y); } public Buggle(Color c) { this(c, _defaultX, _defaultY); } public Buggle(Color c, int x, int y) { // System.out.println ("New Buggle."); color = c; position = new Point(x, y); heading = Direction.EAST; trailmode = _defaultTrailmode; //System.out.println ("Getting world."); world = BuggleWorld.standardWorld; // Should also be a way to make this non-standard. if (world == null) throw new BuggleException("BuggleWorld of newly created buggle is null!"); else { //System.out.println ("Adding robot to world" + world); } exec = world.exec; // Cache executer locally. exec.waitIfNecessary("new Buggle()"); // Careful! Must select buggle before adding it // (which will try to draw it, and may complain if not selected). world.selectBuggle(this); //System.out.println("This buggle = " + this ); world.add(this); } public String toString () { return "[position = (" + position.x + ", " + position.y + ")" + "; heading = " + heading + "; color = " + color + "; brushDown? " + trailmode +"]"; } public void forward(int n){ for (int i = 0; i < n; i++) { forward(); } } public void forward () throws BuggleException { exec.waitIfNecessary("forward()"); if (world.wallInDirection(position, heading)) throw new BuggleException("FORWARD: Can't move through wall!"); if (trailmode) world.markTrail(position, color); Point oldPosition = position; position = world.addCoordinates(position, heading.toPoint()); world.buggleMoved(this, oldPosition, position); } public void paintCell(Color c) { // Paint the current grid cell with the given color world.markTrail(position, c); world.buggleChanged(this); } public Color getCellColor() { // Paint the current grid cell with the given color return world.markColorAt(position); } public void backward(int n){ for (int i = 0; i < n; i++) { backward(); } } public void backward () throws BuggleException { // System.out.println("forward();"); exec.waitIfNecessary("backward()"); heading = heading.opposite(); if (world.wallInDirection(position, heading)) throw new BuggleException("BACKWARD: Can't move through wall!"); if (trailmode) world.markTrail(position, color); Point oldPosition = position; position = world.addCoordinates(position, heading.toPoint()); heading = heading.opposite(); world.buggleMoved(this, oldPosition, position); } public Point getPosition () { //System.out.println("position();"); exec.waitIfNecessary("getPosition()"); return position; } public Point position () { //System.out.println("position();"); // Non-waiting version of primitive return position; } public void setPosition (Point p) { //System.out.println("position();"); exec.waitIfNecessary("setPosition(" + world.pointString(p) + ")"); Point oldPosition = position; position = p; world.buggleMoved(this,oldPosition,position); } public Direction getHeading () { exec.waitIfNecessary("getHeading()"); // System.out.println("heading();"); return heading; } public void setHeading (Direction d) { //System.out.println("position();"); exec.waitIfNecessary("setHeading()"); heading = d; world.buggleChanged(this); } public void left() { exec.waitIfNecessary("left()"); heading = heading.left(); world.buggleChanged(this); } public void right() { //System.out.println("right();"); exec.waitIfNecessary("right()"); heading = heading.right(); world.buggleChanged(this); } public boolean isFacingWall () { //System.out.println("facingWall();"); exec.waitIfNecessary("isFacingWall()"); return world.wallInDirection(position, heading); } public boolean isOverBagel () { //System.out.println("overBagel();"); exec.waitIfNecessary("isOverBagel()"); return world.isBagelAt(position); } public void pickUpBagel () { exec.waitIfNecessary("pickUpBagel()"); if (! world.isBagelAt(position)) throw new BuggleException("pickUpBagel: no bagel to pick up!"); world.removeBagel(position); // Handled by removeBagel. // cellChanged(position); } public void dropBagel () { exec.waitIfNecessary("dropBagel()"); if (world.isBagelAt(position)) throw new BuggleException("dropBagel: already a bagel in this cell!"); world.addBagel(position); // Handled by removeBagel. // cellChanged(position); } public String dropString (String s) { exec.waitIfNecessary("dropString()"); world.addString(position, s); return s; } public int dropInt (int n) { exec.waitIfNecessary("dropInt()"); world.addString(position, Integer.toString(n)); return n; } public boolean withCompanion () { exec.waitIfNecessary("withCompanion()"); return world.withCompanion(this); } public Color getColor () { return color; } public void setColor (Color c) { exec.waitIfNecessary("setColor(" + world.colorString(c) + ")"); color = c; world.buggleChanged(this); } public boolean isBrushDown() { return trailmode; } public void brushDown() { trailmode = true; } public void brushUp() { trailmode = false; } public void drawInRect(Graphics g, Rectangle r) { //System.out.println("Draw in rect: g = " + g + "; rgt = " + r); // Draw myself in given rectangle of graphics. // Assume simple triangle shape for now; do something cuter in future. // Compute triangle based on direction; there must be a cleverer way to do this! Point p1 = new Point(0,0); Point p2 = new Point(0,0); Point p3 = new Point(0,0); if (heading == Direction.SOUTH) { p1.x = r.x + r.width - inset; p1.y = r.y + inset; p2.x = r.x + inset; p2.y = r.y + inset; p3.x = r.x + (r.width / 2); p3.y = r.y + r.height - inset; } else if (heading == Direction.EAST) { p1.x = r.x + inset; p1.y = r.y + inset; p2.x = r.x + inset; p2.y = r.y + r.height - inset; p3.x = r.x + r.width - inset; p3.y = r.y + (r.height/2); } else if (heading == Direction.NORTH) { p1.x = r.x + inset; p1.y = r.y + r.height - inset; p2.x = r.x + r.width - inset; p2.y = r.y + r.height - inset; p3.x = r.x + (r.width/2); p3.y = r.y + inset; } else if (heading == Direction.WEST) { p1.x = r.x + r.width - inset; p1.y = r.y + r.height - inset; p2.x = r.x + r.width - inset; p2.y = r.y + inset; p3.x = r.x + inset; p3.y = r.y + (r.height/2); } // Handle off-by-one bug in Symantec cafe polygons. // (Symantec cafe treats polygon coords as 1-based, not 0-based). p1.x = p1.x - 1; p1.y = p1.y - 1; p2.x = p2.x - 1; p2.y = p2.y - 1; p3.x = p3.x - 1; p3.y = p3.y - 1; int [] xs = {p1.x, p2.x, p3.x,p1.x}; int [] ys = {p1.y, p2.y, p3.y,p1.y}; g.setPaintMode(); g.setColor(color); //System.out.println("p1 = " + p1 + "; p2 = " + p2 + "; p3 = " + p3); g.fillPolygon(xs, ys, 4); if (this == world.selectedBuggle()) { g.setColor(selectedBuggleOutlineColor); } else { g.setColor(unselectedBuggleOutlineColor); } g.drawPolygon(xs, ys, 4); } } class Direction { private int dir; public static final Direction NORTH = new Direction(0); public static final Direction EAST = new Direction(1); public static final Direction SOUTH = new Direction(2); public static final Direction WEST = new Direction(3); private static final Point northPoint = new Point(0,1); private static final Point eastPoint = new Point(1, 0); private static final Point southPoint = new Point(0, -1); private static final Point westPoint = new Point(-1, 0); private static final Direction rights [] = {EAST, SOUTH, WEST, NORTH}; private static final Direction lefts [] = {WEST, NORTH, EAST, SOUTH}; private static final Direction opposites [] = {SOUTH, WEST, NORTH, EAST}; private static final Point points [] = {northPoint, eastPoint, southPoint, westPoint}; private Direction(int d) { dir = d; } public boolean equals (Direction d) { return dir == d.dir; } // Carefully define the following so that == works as well as .equals public Direction right() { return rights[dir]; /* if this == NORTH return EAST; else if this == EAST return SOUTH; else if this == SOUTH return WEST; else if this == WEST return NORTH; */ } public Direction left() { return lefts[dir]; /* if this == NORTH return WEST; else if this == EAST return NORTH; else if this == SOUTH return EAST; else if this == WEST return SOUTH; */ } public Direction opposite() { return opposites[dir]; } public Point toPoint() { return points[dir]; } public String toString () { if (dir == 0) return "NORTH"; else if (dir == 1) return "EAST"; else if (dir == 2) return "SOUTH"; else if (dir == 3) return "WEST"; else return "Unknown direction"; } } class BuggleException extends RuntimeException { public BuggleException (String msg) { super(msg); } }