2014-12-05 68 views
0

我正在研究機器人迷宮,其中機器人發現目標而沒有撞到牆上。作爲一種「回溯」方法,我需要機器人以與剛剛遇到交叉路口時相反的方向行進。這是我的代碼:java.lang.reflect.InvocationTargetException錯誤

import uk.ac.warwick.dcs.maze.logic.IRobot; 
import java.util.ArrayList; 
import java.util.*; 
import java.util.Iterator; 

public class Explorer { 

    private int pollRun = 0; // Incremented after each pass. 
    private RobotData robotData; // Data store for junctions. 
    private ArrayList<Integer> nonWallDirections; 
    private ArrayList<Integer> passageDirections; 
    private ArrayList<Integer> beenbeforeDirections; 
    private Random random = new Random(); 
    int [] directions = {IRobot.AHEAD, IRobot.LEFT, IRobot.RIGHT, IRobot.BEHIND}; 

    public void controlRobot (IRobot robot) { 

     // On the first move of the first run of a new maze. 
     if ((robot.getRuns() == 0) && (pollRun ==0)) 
      robotData = new RobotData(); 
     pollRun++; /* Increment poll run so that the data is not reset 
         each time the robot moves. */ 

     int exits = nonwallExits(robot); 
     int direction; 

     nonWallDirections = new ArrayList<Integer>(); 
     passageDirections = new ArrayList<Integer>(); 
     beenbeforeDirections = new ArrayList<Integer>(); 

      // Adding each direction to the appropriate state ArrayList. 
      for(int item : directions) { 
       if(robot.look(item) != IRobot.WALL) { 
        nonWallDirections.add(item); 
       } 
      } 

      for(int item : directions) { 
       if(robot.look(item) == IRobot.PASSAGE) { 
        passageDirections.add(item); 
       } 
      } 

      for(int item : directions) { 
       if(robot.look(item) == IRobot.BEENBEFORE) { 
        beenbeforeDirections.add(item); 
       } 
      } 

     // Calling the appropriate method depending on the number of exits. 
     if (exits < 2) { 
      direction = deadEnd(robot); 
     } else if (exits == 2) { 
      direction = corridor(robot); 
     } else { 
      direction = junction(robot); 
      robotData.addJunction(robot); 
      robotData.printJunction(robot); 
     } 

     robot.face(direction); 
    } 


    /* The specification advised to have to seperate controls: Explorer and Backtrack 
     and a variable explorerMode to switch between them. 
     Instead, whenever needed I shall call this backtrack method. 
     If at a junction, the robot will head back the junction as to when it first approached it. 
     When at a deadend or corridor, it will follow the beenbefore squares until it 
     reaches an unexplored path. */ 
    public int backtrack (IRobot robot) { 

     if (nonwallExits(robot) > 2) { 
      return robotData.reverseHeading(robot); 
     } else { 
       do { 
        return nonWallDirections.get(0); 
       } while (nonwallExits(robot) == 1); 
     } 

    } 


    // Deadend method makes the robot follow the only nonwall exit. 
    public int deadEnd (IRobot robot) { 

     return backtrack(robot); 

    } 


    /* Corridor method will make the robot follow the one and only passage. 
     The exception is at the start. Sometimes, the robot will start with 
     two passages available to it in which case it will choose one randomly. 
     If there is no passage, it will follow the beenbefore squares 
     until it reaches an unexplored path.*/ 
    public int corridor (IRobot robot) { 

     if (passageExits(robot) == 1) { 
      return passageDirections.get(0); 
     } else if (passageExits(robot) == 2) { 
      int randomPassage = random.nextInt(passageDirections.size()); 
      return passageDirections.get(randomPassage); 
     } else { 
       return backtrack(robot); 
     } 
    } 


    /* Junction method states if there is more than one passage, it will randomly select one. 
     This applies to crossroads as well as essentially they are the same. 
     If there is no passage, it will follow the beenbefore squares until it reaches an unexplored 
     path. */ 
    public int junction(IRobot robot) { 

     if (passageExits(robot) == 1) { 
      return passageDirections.get(0); 
     } else if (passageExits(robot) > 1) { 
      int randomPassage = random.nextInt(passageDirections.size()); 
      return passageDirections.get(randomPassage); 
     } else { 
      return backtrack(robot); 
     } 

    } 


    // Calculates number of exits. 
    private int nonwallExits (IRobot robot) { 

     int nonwallExits = 0; 

     for(int item : directions) { 
      if(robot.look(item) != IRobot.WALL) { 
       nonwallExits++; 
      } 
     } 

     return nonwallExits; 
    } 


    // Calculates number of passages. 
    private int passageExits (IRobot robot) { 

     int passageExits = 0; 

     for(int item : directions) { 
      if(robot.look(item) == IRobot.PASSAGE) { 
       passageExits++; 
      } 
     } 

     return passageExits; 
    } 


    // Calculates number of beenbefores. 
    private int beenbeforeExits (IRobot robot) { 

     int beenbeforeExits = 0; 

     for(int item : directions) { 
      if(robot.look(item) == IRobot.PASSAGE) { 
       beenbeforeExits++; 
      } 
     } 

     return beenbeforeExits; 
    } 

    // Resets Junction Counter in RobotData class. 
    public int reset() { 

     return robotData.resetJunctionCounter(); 

    } 
} 

class RobotData { 

    /* It was advised in the specification to include the variable: 
     private static int maxJunctions = 10000; 
     However, as I am not using arrays, but ArrayLists, I do not 
     need this. */ 
    private static int junctionCounter = 0; 
    private ArrayList<Junction> junctionList = new ArrayList<Junction>(); 
    private Iterator<Junction> junctionIterator = junctionList.iterator(); 

    // Resets the Junction counter. 
    public int resetJunctionCounter() { 

     return junctionCounter = 0; 

    } 


    // Adds the current junction to the list of arrays. 
    public void addJunction(IRobot robot) { 

     Junction newJunction = new Junction(robot.getLocation().x, robot.getLocation().y, robot.getHeading()); 
     junctionList.add(newJunction); 
     junctionCounter++; 

    } 


    // Gets the junction counter for Junction info method in Junction class. 
    public int getJunctionCounter (IRobot robot) { 

     return junctionCounter; 
    } 


    // Prints Junction info. 
    public void printJunction(IRobot robot) { 

     String course = ""; 
     switch (robot.getHeading()) { 
      case IRobot.NORTH: 
       course = "NORTH"; 
       break; 
      case IRobot.EAST: 
       course = "EAST"; 
       break; 
      case IRobot.SOUTH: 
       course = "SOUTH"; 
       break; 
      case IRobot.WEST: 
       course = "WEST"; 
       break; 
     } 

     System.out.println("Junction " + junctionCounter + " (x=" + robot.getLocation().x + ", y=" + robot.getLocation().y +") heading " + course); 

    } 

    /* Iterates through the junction arrayList to find the 
     heading of the robot when it first approached the junction. */ 
    public int searchJunction(IRobot robot) { 

     Junction currentJunction = junctionIterator.next(); 
     while (junctionIterator.hasNext()) { 
      if ((((currentJunction.x)==(robot.getLocation().x))) && ((currentJunction.y)==(robot.getLocation().y))) 
       break; 
     } 

     return currentJunction.arrived; 
    } 


    // Returns the reverse of the heading the robot had when first approaching the junction. 
    public int reverseHeading(IRobot robot) { 

     int firstHeading = searchJunction(robot); 
     int reverseHeading = 1; // Random integer to Iniitalise variable. 

     switch (firstHeading) { 
        case IRobot.NORTH: 
         if (robot.getHeading() == IRobot.NORTH) 
          reverseHeading = IRobot.BEHIND; 
         else if (robot.getHeading() == IRobot.EAST) 
          reverseHeading = IRobot.RIGHT; 
         else if (robot.getHeading() == IRobot.SOUTH) 
          reverseHeading = IRobot.AHEAD; 
         else 
          reverseHeading = IRobot.LEFT; 
        break; 

        case IRobot.EAST: 
         if (robot.getHeading() == IRobot.NORTH) 
          reverseHeading = IRobot.LEFT; 
         else if (robot.getHeading() == IRobot.EAST) 
          reverseHeading = IRobot.BEHIND; 
         else if (robot.getHeading() == IRobot.SOUTH) 
          reverseHeading = IRobot.RIGHT; 
         else 
          reverseHeading = IRobot.AHEAD; 
        break; 

        case IRobot.SOUTH: 
         if (robot.getHeading() == IRobot.NORTH) 
          reverseHeading = IRobot.AHEAD; 
         else if (robot.getHeading() == IRobot.EAST) 
          reverseHeading = IRobot.LEFT; 
         else if (robot.getHeading() == IRobot.SOUTH) 
          reverseHeading = IRobot.BEHIND; 
         else 
          reverseHeading = IRobot.RIGHT; 
        break; 

        case IRobot.WEST: 
         if (robot.getHeading() == IRobot.NORTH) 
          reverseHeading = IRobot.RIGHT; 
         else if (robot.getHeading() == IRobot.EAST) 
          reverseHeading = IRobot.AHEAD; 
         else if (robot.getHeading() == IRobot.SOUTH) 
          reverseHeading = IRobot.LEFT; 
         else 
          reverseHeading = IRobot.BEHIND; 
        break; 
       } 

     return reverseHeading; 

    } 

} 


class Junction { 

    int x; 
    int y; 
    int arrived; 

    public Junction(int xcoord, int ycoord, int course) { 

     x = xcoord; 
     y = ycoord; 
     arrived = course; 

    } 

} 

每當它是回溯並達到它已經訪問過一個路口,凍結這來了。

`java.lang.reflect.InvocationTargetException 
    at sun.reflect.GeneratedMethodAccessor41.invoke(Unknown Source) 
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 
    at java.lang.reflect.Method.invoke(Method.java:483) 
    at uk.ac.warwick.dcs.maze.controllers.PolledControllerWrapper.start(PolledControllerWrapper.java:70) 
    at uk.ac.warwick.dcs.maze.logic.ControllerThread.run(ControllerThread.java:46) 
Caused by: java.util.ConcurrentModificationException 
    at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901) 
    at java.util.ArrayList$Itr.next(ArrayList.java:851) 
    at RobotData.searchJunction(Explorer.java:242) 
    at RobotData.reverseHeading(Explorer.java:255) 
    at Explorer.backtrack(Explorer.java:74) 
    at Explorer.junction(Explorer.java:122) 
    at Explorer.controlRobot(Explorer.java:56) 
    ... 5 more` 
+1

請不要刪除你的代碼,錯誤消息或其他相關數據毀損你的問題,因爲這會讓問題對未來的遊客完全沒有價值。我已經將你的問題回滾到之前的狀態,並且如果你將來試圖破壞它,它會再次執行。 – 2014-12-05 12:12:29

回答

3

我認爲您的searchJunction()是正確的或安全的,但由於錯誤的迭代器通過junctionList可能會引發ConcurrentModificationException。問題應該更多地是關於迭代器而不是反射。

您可以試試:

  1. private Iterator<Junction> junctionIterator = junctionList.iterator();當初始化RobotData對象並沒有太大的意義,因爲該列表是空的。嘗試將其移動到searchJunction()
  2. 檢查hasNext()第一,然後調用next()

    public int searchJunction(IRobot robot) { 
        Iterator<Junction> junctionIterator = junctionList.iterator(); 
        while (junctionIterator.hasNext()) { 
         Junction currentJunction = junctionIterator.next(); 
         if ((((currentJunction.x)==(robot.getLocation().x))) && ((currentJunction.y)==(robot.getLocation().y))) 
          break; 
        } 
    
        return currentJunction.arrived; 
    } 
    
+1

非常感謝!你是個天才!我還有一個問題。是否有可能讓機器人跑迷宮一次,記住正確的路線,然後在第二次運行時採取正確的路線?如果是這樣,我該怎麼做? – codeav3 2014-12-05 03:21:51

+1

如何使用LinkedList類型的實例變量保存正確的路徑:IRobot.AHEAD/IRobot.LEFT/.....?如果迷宮可能發生變化,則需要設計一些映射機制來在返回保存的解決方案時評估迷宮的身份。 – 2014-12-05 03:27:17

+0

嗯我從來沒有聽說過LinkedList。思想進入更多細節? – codeav3 2014-12-05 03:30:15

1

它看起來像這個問題是在下面的代碼 -

`public int searchJunction(IRobot robot) { 

    Junction currentJunction = junctionIterator.next(); 
    while (junctionIterator.hasNext()) { 
     if ((((currentJunction.x)==(robot.getLocation().x))) && ((currentJunction.y)==(robot.getLocation().y))) 
      break; 
    } 

    return currentJunction.arrived; 
} 

要調用junctionIterator.next()調用junctionIterator.hasNext前()。迭代器規範說,只有在調用hasNext()後,才應該調用next()

相關問題