// class Robot.java //:Assignment 4: //:Student Name: Duo Zhou //:Studnet Number: 722452 /**FileName: Robot.java * Date: July, 27, 2000 * Purpose: Learn how to use JFrame, JPanel, Synchronized method, creating multithread, * the basic idea of creating GUI * Name of Files: This assignment include Building.java, ConnectedWall.java, Elevator.java, * ElevatorFrame.java, ElevatorPanel.java, Floor.java, MailDeliveryApp.java, * MailPiece.java, Position.java, Robot.java * Brief Discription: Robot.java is used to simulate the movement of a robot, who are supposed * to deliver mails along walls; also it is going to create a panel, which * are used to display the layout of the current floor, and modelling the * movement of the robot; ask the robot to deliver mails currently with the * display of panel and the movement of the elevator. */ import java.awt.*; import java.awt.event.*; import java.awt.image.*; import java.awt.geom.*; import java.util.*; import javax.swing.*; import javax.swing.border.*; import java.lang.*; class Robot extends JPanel { private Building building; // Building inside which Robot is located public static Floor currentFloor; // floor object where Robot is located public static Elevator availableElevator; protected int currentFloorNum; // floor number where Robot is located protected Position currentPosition; // Robot position and surrounding Tiles position private Position frontTilePos = new Position(0,0); public Position rightTilePos = new Position(0,0); private static final int DOCKING_ROW = 25; // in the BASEMENT floor private static final int DOCKING_COL = 25; private static final Position DOCKING_POS = new Position(DOCKING_ROW,DOCKING_COL); private int currentDirection; // direction Robot is facing public static final int NORTH = 0; public static final int EAST = 1; public static final int SOUTH = 2; public static final int WEST = 3; private int newDirection; // to set up new direction value before computing neighbors private char robotShape; // changes for each direction of movement private double currentSpeed; private static final double LOADED_SPEED = 0.100; // when carrying mail private static final double EMPTY_SPEED = 0.050; // after deliverying mail private int mailBox; // This array holds the bag of mailPieces private MailPiece mail[][] = new MailPiece[Building.MAX_FLOORS][Floor.MAX_ROOMS]; //************************************************************************************************* public static final int N_IMG = 4; //no of pictures to show the animation of our robot; public static final int X_CELL = 20; //we use squares of blocks to refer to a simple char public static final int Y_CELL = 20; // in the floor layout; public static final int N_STEP = 1; //no of steps use to produce smoothness, //from one cell to another; public static final int STEP = 1; //length of each step, in term of no of cells, //can be changed to float; public static int m_creatureX; // current Position of the robot: row public static int m_creatureY; // ... : col; protected int m_posX = 0; //the position in the x axis of our robot in each cell; protected int m_posY = 0; //the position in the y axis of our robot in each cell; protected int m_creatureDir = 0; // Current direction of the robot; protected int[][] m_flags; //two dimensional array to hold the corresponding //integer for each tile; public int floorno; protected int m_creatureInd = 0; //the index of pictures for robot to create animation; protected Image m_wallImage; //wall image; protected Image m_ballImage; //use ball image to represent '*' sign in the original array; protected Image m_humanImage; //use human image to represend of human in the layout; protected Image m_mailImage; //use mail image to represned 'M'; protected Image[][] m_creature; //two dimensional array of robot; protected Thread m_runner; //**** we want the current processing of the display of frames //**** and the running of our robot; protected JFrame m_parent; //the parent JFrame private JLabel robotLabel; //create a JLabel, to show the current state of the robot; protected int previousFloorNum; //previousFloorNum; public Robot(JFrame parent, Building building){ this.building = building; //hold a reference of the current Building; currentFloorNum = Building.BASEMENT; //started from the BASEMENT currentFloor = building.getFloor(Building.BASEMENT);//get the BASEMENT floor; currentDirection = EAST; //towards the rising Sun currentPosition = DOCKING_POS; //started from the DOCKING_POS; currentSpeed = LOADED_SPEED; //speed of the robot with mails loaded; setBackground(Color.black); //this is also a JPanel; m_parent=parent; //get a reference of parent JFrame; robotLabel=new JLabel(" ", SwingConstants.LEFT); //create a lable; robotLabel.setBackground(Color.white); //set the label background to white; robotLabel.setSize(Floor.MAX_COLS*20, 40); //set the size of the lable; robotLabel.setVerticalTextPosition(SwingConstants.TOP); //set the vertical text position in the label; robotLabel.setOpaque(true); //set the label to be visible; add(robotLabel, BorderLayout.NORTH); //add the label to the top in the BorderLayout; AffineTransform[] at = new AffineTransform[3]; //Create a matrix to hold the animation of the at[0] = new AffineTransform(0, 1, -1, 0, Y_CELL, 0); //robot: three AffineTransforms are used for at[1] = new AffineTransform(-1, 0, 0, 1, X_CELL, 0); //flipping our Pac-man images. at[2] = new AffineTransform(0, -1, -1, 0, Y_CELL, X_CELL); ImageIcon icon = new ImageIcon("wall.gif"); m_wallImage = icon.getImage(); //load the wall image; icon = new ImageIcon("ball.gif"); m_ballImage = icon.getImage(); //load the ball image to indicate the door of a mailbox; icon = new ImageIcon("human.gif"); m_humanImage = icon.getImage(); //load the human image. icon = new ImageIcon("envelop.gif"); m_mailImage = icon.getImage(); //load the envelop image to represent 'M'; m_creature = new Image[N_IMG][4]; //create an two-dimensional array reference of the robot image; //******************** Flipping the images to create the result of animation ************************ for (int k=0; k=0 && m_creatureX = 0 && m_creatureY < Floor.MAX_ROWS ) { if (currentFloor.floorLayout[m_creatureY][m_creatureX]=='^') m_creatureDir = 3; //representing moving north; else if (currentFloor.floorLayout[m_creatureY][m_creatureX]=='<') m_creatureDir=2; //representing moving west; else if (currentFloor.floorLayout[m_creatureY][m_creatureX]=='v') m_creatureDir=1; //representing moving south; else if (currentFloor.floorLayout[m_creatureY][m_creatureX]=='>') m_creatureDir=0; //representing moving east; m_creatureInd++; //change the animation image of the robot; m_creatureInd = m_creatureInd % N_IMG; final int x = (int)(m_creatureX*X_CELL); //get the pixel position of the final int y = (int)(m_creatureY*Y_CELL); //robot; //The paintImmediately() method can be used to force very quick repaints but //should only be called from within the AWT event dispatching thread. Additionally, //because I do not want any other painting or movement to occur while this paint //takes place, I wrap the call in a Runnable and send it to the event queue with //invokeAndWait(); Runnable painter = new Runnable() { public void run() { Robot.this.paintImmediately(x-X_CELL-1, y+40-Y_CELL-1, 3*X_CELL, 1*Y_CELL+40); if (previousFloorNum!=currentFloorNum) { Robot.this.paintImmediately(0, 40, Floor.MAX_COLS*X_CELL, Floor.MAX_ROWS*Y_CELL+40); previousFloorNum=currentFloorNum; robotLabel.setText("I coming up to floor no "+currentFloorNum+ "! "); } else if(frontTilePos.equal( mail[currentFloorNum][mailBox].getPosition())) { robotLabel.setText("Delivering mail on floor no "+ currentFloorNum +", to mailbox no " +mailBox +"! "); } } }; try { SwingUtilities.invokeAndWait(painter); } catch (Exception e) {} try { sleep(1); } catch (InterruptedException ex) { break; } if (m_flags[m_creatureY][m_creatureX] == 0) //if the robot image hasn't been displayed in the m_flags[m_creatureY][m_creatureY] = -1; //current position, then set it to display; } else try { sleep(150); } catch (InterruptedException ex) { break; } } } }; m_runner.start(); //start the thread; } //******************************* Get the size of the panel *********************************** public Dimension getPreferredSize() { return new Dimension(Floor.MAX_COLS*X_CELL, Floor.MAX_ROWS*Y_CELL+65); } public Dimension getMaximumSize() { return getPreferredSize(); } public Dimension getMinimumSize() { return getPreferredSize(); } //****************************** paintComponent ************************************************ //since the parent JFrame has already been passed over, so we don't need to ask it to display on //the parent JFrame specifically; public void paintComponent(Graphics g){ Graphics2D g2 = (Graphics2D) g; //cast to Graphics 2D; //the following statement is used to change user command from the keyboard to rendering algorithms g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, //Sets the value of a single preference RenderingHints.VALUE_ANTIALIAS_ON); //for the rendering algorithms g2.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); //Replaces the values of all preferences //for the rendering algorithms with the specified hints. //the above two statement is not useful in this project, but it is left for further development; g2.setColor(getBackground()); g2.fill(g.getClip()); //fill the current clipping area; for (int i=0; i0 && floorNum < Building.MAX_FLOORS){ currentFloorNum = floorNum; currentFloor = building.getFloor(currentFloorNum); return true; } else return false; } //////////////////////////////////////////////////////////////////// // This could become the Runnable interface run() method // When the robot runs inside a separate thread public void deliverMail(){ //m_parent=parent; int wallNum =0; boolean done; // signal when done following a wall // This loop delivers mail over all floors in the building while(true) { if(currentFloorNum == Building.BASEMENT){ System.out.println("Robot starting from Docking Position in first floor\n\n"); }else{ System.out.println("Robot starting from Inside Elevator\n\n"); } System.out.println("\nStarting Mail Delivery for floor: " + currentFloorNum); //pause(2.0); currentFloor.printLayout(); //pause(2.0); // Follow each connected wall on this floor while( currentFloor.connectedWalls[wallNum] != null) { // printPosition(currentPosition); // pause(1.0); System.out.println("Moving to connected wall starting position"); //pause(2.0); moveTo(currentFloor.connectedWalls[wallNum].startPos); pointTo(currentFloor.connectedWalls[wallNum].startDir); // starting direction currentFloor.printLayout(); System.out.println("Robot in position to start deliverying mail along wall"); //pause(2.0); // printPosition(currentPosition); // Robot will follow each "connected walls" until it finds a stopping position // Then it will look for a starting position within a second or third set // of connected walls until there are no more connected wall islands. // Then it will move towards the elevator, by either making a direct dash // or by following connected walls until a direct dash could be made. // Selection of starting and stopping positions for the various connected // walls should be such that the starting position within a connected wall // is accessible in an unobstructed way from the stopping position on the // previous wall. // This loop follows a connected wall circuit till it finds stopping position. done = false; while(! done ){ // This loop makes Robot follow a straight line wall w/o obstacles while( currentFloor.isWall(rightTilePos) && !currentFloor.isWall(frontTilePos)){ if((currentFloor.connectedWalls[wallNum].stopPos).equal(currentPosition)){ break; } pause(LOADED_SPEED); if(currentFloor.isMailBox(rightTilePos)) dropMailPiece(); step(currentDirection); // step after deliverying mail } // end of straight line while() // Found a corner, hit a wall or both, so turn around corner // change direction so as to keep wall in your right, remain in loop as // long as position is not a stop position for this wall. if( ! currentFloor.isWall(rightTilePos) ){ newDirection = (currentDirection + 1)%4; // turn right pointTo(newDirection); step(currentDirection); // move into new straight wall section if(currentFloor.isMailBox(rightTilePos)) dropMailPiece(); } else if( currentFloor.isWall(frontTilePos) ){ newDirection = ((currentDirection-1)+4)%4; // just turn left pointTo(newDirection); // do not move yet, if(currentFloor.isMailBox(rightTilePos))// might need to turn dropMailPiece(); // again if in cul-de-sac } // check we are not at stopping position after last step if((currentFloor.connectedWalls[wallNum].stopPos).equal(currentPosition)){ done = true; // signal break out of outer loop also. break; // we should be able to do this with labeled break... } } // end of connected wall circuit loop currentFloor.printLayout(); //Print after surrounding wall completely wallNum++; if(wallNum >= Floor.MAX_WALLS){ break; } } // end of floor delivery while() loop // take elevator to next floor if there are more floors // first move in front of elevator moveTo(currentFloor.INFRONT_ELEVATOR_POS); currentFloor.printLayout(); System.out.println("Robot moved in front of elevator"); //pause(2.0); availableElevator=building.getElevator(); //cause the method is synchronized, so //let's wait for the elevator to be available; // now call elevator to our floor availableElevator.callFrom(currentFloorNum); currentFloor.printLayout(); System.out.println("Robot called Elevator to current floor"); //pause(2.0); // now move inside elevator moveTo(currentFloor.INSIDE_ELEVATOR_POS); currentFloor.printLayout(); pause(5.0); System.out.println("Robot moved inside Elevator"); if(currentFloorNum+1 == Building.MAX_FLOORS){ availableElevator.moveRobTo(Building.BASEMENT); currentFloor.printLayout(); System.out.println("Robot moved to Basement floor"); //pause(2.0); moveTo(DOCKING_POS); currentFloor.printLayout(); System.out.println("Robot moved to Docking position on Basement"); System.out.println("Mail Delivery Task Completed."); //pause(5.0); System.exit(0); // and stop all activity }else { availableElevator.moveRobTo(currentFloorNum+1); // currentFloor within Robot is only changed by elevator currentFloor.printLayout(); System.out.println("Robot moved to Floor #"+currentFloorNum); wallNum = 0; } }// end of building delivery while() loop }// end of mailDelivery() method /* This method determines if righ-hand-side tile is a wall or not */ boolean rightHandWall(){ boolean answer=false; int row=currentPosition.row, col=currentPosition.col; switch(currentDirection){ case NORTH: answer = currentFloor.isWall(new Position(row,col+1)); break; case EAST: answer = currentFloor.isWall(new Position(row+1,col)); break; case SOUTH: answer = currentFloor.isWall(new Position(row,col-1)); break; case WEST: answer = currentFloor.isWall(new Position(row-1,col)); break; default: System.out.println("Error:rightHandWall: Invalid currentDirection"); } return answer; }// end of rightHandWall() ////////////////////////////////////////////////////////////////////// // This is the method that causes the robot to move. void step(int direction){ // moves one step in the indicated direction currentFloor.setTile(currentPosition,' '); // erase Robot trail switch(direction){ case NORTH: (currentPosition.row)--; pointTo(NORTH); break; case EAST: (currentPosition.col)++; pointTo(EAST); break; case SOUTH: (currentPosition.row)++; pointTo(SOUTH); break; case WEST: (currentPosition.col)--; pointTo(WEST); break; default: System.out.println("Error:step(): Invalid direction"); } // end of switch // Print floor layout after each step, when we have a better GUI currentFloor.setTile(currentPosition,robotShape); // set Robot shape into tile // currentFloor.printLayout(); // Too flickery when at the console Date date = new Date(); // Print position and time /* System.out.println("Position after move= (" + currentPosition.row + "," + currentPosition.col + ") at Time=" + (System.currentTimeMillis()%100)); // pause(0.1); */ } // end of step() void pointTo(int direction){ switch(direction){ case NORTH: currentDirection = NORTH; rightTilePos.row = currentPosition.row; rightTilePos.col = currentPosition.col+1; frontTilePos.row = currentPosition.row-1; frontTilePos.col = currentPosition.col; robotShape= '^'; break; case EAST: currentDirection = EAST; rightTilePos.row = currentPosition.row+1; rightTilePos.col = currentPosition.col; frontTilePos.row = currentPosition.row; frontTilePos.col = currentPosition.col+1; robotShape= '>'; break; case SOUTH: currentDirection = SOUTH; rightTilePos.row = currentPosition.row; rightTilePos.col = currentPosition.col-1; frontTilePos.row = currentPosition.row+1; frontTilePos.col = currentPosition.col; robotShape= 'v'; break; case WEST: currentDirection = WEST; rightTilePos.row = currentPosition.row-1; rightTilePos.col = currentPosition.col; frontTilePos.row = currentPosition.row; frontTilePos.col = currentPosition.col-1; robotShape= '<'; break; default: System.out.println("Error:pointTo: Invalid direction"); } // end of switch } // end of pointTo() // This methods delivers a mail piece by facing the mail box on its right hand side // removing from the mailBag the mail piece whose code corresponds to the mail // box coordinates, and then waiting for 2.0 seconds. After the wait it turns left // to its original direction. The assumption that the mailBox should be on its // right hand side Tile is verified by testing the tile contents for an asterisk // void dropMailPiece(){ mailBox = 0; // always search mail bag from the start newDirection = (currentDirection + 1)%4 ; // rotate clockwise to face the wall pointTo(newDirection); // This is a good spot to show robot's new shape // scan the mailBag for a mailPiece addressed to this coordinate while(mailBox < currentFloor.directory.length){ if( mail[currentFloorNum][mailBox] != null ){ if(frontTilePos.equal( mail[currentFloorNum][mailBox].getPosition())){ System.out.println("Delivering "+ mail[currentFloorNum][mailBox].message); // print mail pause(0.5); mail[currentFloorNum][mailBox] = null; // remove mail piece // leave at mailBox as an 'M' currentFloor.floorLayout[frontTilePos.row][frontTilePos.col] = 'M'; break; } } mailBox++; } // end of while() loop pause(0.5); newDirection = ( (currentDirection - 1) + 4 )%4; // return to original direction pointTo(newDirection); } // end of dropMailPiece() // The moveTo() method performs an adaptive movement process where it tries to // gradually reduce the difference in row/col positions between the current position // and a target position, while avoiding wall obstacles. This method assumes the // target position is on line-of-sight with current position. // boolean moveTo(Position target){ int deltaRow, deltaCol; // Search while target not reached. or until break give-up while(currentPosition.row != target.row || currentPosition.col != target.col){ deltaRow = Math.abs(target.row - currentPosition.row); deltaCol = Math.abs(target.col - currentPosition.col); // go around human standing in front of Robot if(currentFloor.isHuman(frontTilePos)){ dodgeHuman(); continue; } if(currentPosition.row < target.row && currentPosition.col < target.col){ if(deltaRow >= deltaCol){ // try to reduce largest delta first unless obstacle if(!currentFloor.isWall(new Position(currentPosition.row+1,currentPosition.col))){ step(SOUTH); // move on vertical axis when deltas are equal }else{ step(EAST); } }else if(deltaRow < deltaCol){ if(!currentFloor.isWall(new Position(currentPosition.row,currentPosition.col+1))){ step(EAST); }else{ step(SOUTH); } } }else if(currentPosition.row < target.row && currentPosition.col > target.col){ if(deltaRow >= deltaCol){ // try to reduce largest delta first unless obstacle if(!currentFloor.isWall(new Position(currentPosition.row+1,currentPosition.col))){ step(SOUTH);// move on vertical axis when deltas are equal }else{ step(WEST); } }else if(deltaRow < deltaCol){ if(!currentFloor.isWall(new Position(currentPosition.row,currentPosition.col-1))){ step(WEST); }else{ step(SOUTH); } } }else if(currentPosition.row > target.row && currentPosition.col < target.col){ if(deltaRow >= deltaCol){ // try to reduce largest delta first unless obstacle if(!currentFloor.isWall(new Position(currentPosition.row-1,currentPosition.col))){ step(NORTH);// move on vertical axis when deltas are equal }else{ step(EAST); } }else if(deltaRow < deltaCol){ if(!currentFloor.isWall(new Position(currentPosition.row,currentPosition.col+1))){ step(EAST); }else{ step(NORTH); } } }else if(currentPosition.row > target.row && currentPosition.col > target.col){ if(deltaRow >= deltaCol){ // try to reduce largest delta first unless obstacle if(!currentFloor.isWall(new Position(currentPosition.row-1,currentPosition.col))){ step(NORTH);// move on vertical axis when deltas are equal }else{ step(WEST); } }else if(deltaRow < deltaCol){ if(!currentFloor.isWall(new Position(currentPosition.row,currentPosition.col-1))){ step(WEST); }else{ step(NORTH); } } }else if(currentPosition.row == target.row && currentPosition.col > target.col){ if(!currentFloor.isWall(new Position(currentPosition.row,currentPosition.col-1))){ step(WEST); } }else if(currentPosition.row == target.row && currentPosition.col < target.col){ if(!currentFloor.isWall(new Position(currentPosition.row,currentPosition.col+1))){ step(EAST); } }else if(currentPosition.row > target.row && currentPosition.col == target.col){ if(!currentFloor.isWall(new Position(currentPosition.row-1,currentPosition.col))){ step(NORTH); } }else if(currentPosition.row < target.row && currentPosition.col == target.col){ if(!currentFloor.isWall(new Position(currentPosition.row+1,currentPosition.col))){ step(SOUTH); } }else { // no movement was possible due to obstacles, try another move strategy return false; // escape from loop } }// end of while() return true; } // end of moveTo() //This method "dodges" around a Human that gets in the middle of a Robot move // It assumes that humans do not stand next to walls nor next to other humans // Robot goes around clockwise. void dodgeHuman(){ switch(currentDirection){ case NORTH: step(WEST); step(NORTH); step(NORTH); step(EAST); pointTo(NORTH); break; case EAST: step(NORTH); step(EAST); step(EAST); step(SOUTH); pointTo(EAST); break; case SOUTH: step(EAST); step(SOUTH); step(SOUTH); step(WEST); pointTo(SOUTH); break; case WEST: step(SOUTH); step(WEST); step(WEST); step(NORTH); pointTo(SOUTH); break; } // end of case() }// end of dodgeHuman() // This method introduces a pause for the specified seconds void pause(double seconds){ try{ Thread.sleep((long)(1000.0*seconds)); }catch(InterruptedException e){ } } // end of pause() public void printPosition(Position pos){ System.out.println("Position: ("+ pos.row + "," + pos.col + ")" ); } }// end of Robot class