/* * This program version will actually attempt navigation through a maze. * * * A modestly clueful user is required to first press the gray button three * times to set the sensor values for the background of the maze (white). Then, * press the black button three times to set the track lightness values. * Finally, the RUN button starts the main program. * * For now I will ignore whether it can get into an infinite loop if there is a * loop in the maze. I will leave that array in place and an empty method for * that, anyway. * * ******************* * Julie Powers-Boyle * 20060223 Thurs, Programming WS * $ID * ******************* */ import josx.platform.rcx.*; public class MazeNav implements SensorConstants, SensorListener, TimerListener{ // different values since there are differences among sensors static int backgroundValues[] = {0,0,0}; static int trackValues[] = {0,0,0}; static int finishValues[] = {0,0,0}; // if this value is found, it finishes static double coordinates[] = {0,0}; // [x,y] as estimated by motor duration static int direction; // right, left, up, down // static double linearizedSpatialMemoryLog[]; // for haveWeBeenHere static int choiceLog[][]; // can I add to the size of this? static float timeLog[]; // and to this? static int stepNumber; // this serves as an index number for arrays static float currentTime; Timer timer = new Timer(10, new MazeNav()); // too often? too sparse? public static void main (String args[]) throws InterruptedException{ activateRCX(); setLightValues(); //Don't run away until someone says to do so TextLCD.print("RUN?"); Button.RUN.waitForPressAndRelease(); // Now start explore(); // When that's done finish(); Button.RUN.waitForPressAndRelease(); } // closing main // massively decomposed methods: // Setup: activateRCX and setLightValues static void activateRCX(){ for (int i = 0; i <= 2; i++){ Sensor.SENSORS[i].setTypeAndMode(SENSOR_TYPE_LIGHT, SENSOR_MODE_PCT); Sensor.SENSORS[i].activate(); } for (int i = 0; i <= 2; i++){ Sensor.SENSORS[i].addSensorListener(MazeNav); } Motor.A.setPower(3); // just to start Motor.C.setPower(3); } static void setLightValues() throws InterruptedException{ // I'm making this much more complicated than I probably need to later, // because I want to see the values of the different-voltaged sensors. // This idea adapted from Jim's LineWalker.java String displayThis; int i; TextLCD.print("P 0"); // P for paper for (i = 0; i <= 2; i++){ Button.PRGM.waitForPressAndRelease(); backgroundValues[i] = Sensor.SENSORS[i].readValue(); displayThis = "P" + Integer.toString(i+1) + " " + Integer.toString(backgroundValues[i]); TextLCD.print(displayThis); //let's watch bugs } Thread.sleep(1000); // yes, I could probably nest these loops TextLCD.print("T 0"); for (i = 0; i <= 2; i++){ Button.VIEW.waitForPressAndRelease(); trackValues[i] = Sensor.SENSORS[i].readValue(); displayThis = "T" + Integer.toString(i+1) + " " + Integer.toString(trackValues[i]); TextLCD.print(displayThis); //let's watch bugs } Thread.sleep(1000); TextLCD.print("F 0"); for (i = 0; i <= 2; i++){ Button.PRGM.waitForPressAndRelease(); finishValues[i] = Sensor.SENSORS[i].readValue(); displayThis = "F" + Integer.toString(i+1) + " " + Integer.toString(finishValues[i]); TextLCD.print(displayThis); //let's watch bugs } Thread.sleep(1000); } // Umbrella activity: explore static void explore() throws InterruptedException{ // Thread moving forward with waiting for an event where the sensors // change, in which case there is a decision to be made about direction. while (true){ moveForward(); // implements SensorListener, waiting, then completeStop(false); // if it's the finishing color, return } } // Movement: moveForward, moveBackward, turnLeft, turnRight, completeStop void moveForward(){ activateTimer(); Motor.A.forward(); Motor.C.forward(); } void moveBackward(){ activateTimer(); Motor.A.backward(); Motor.C.backward(); } void turnLeft() throws InterruptedException{ Motor.A.backward(); Motor.C.forward(); Thread.sleep(1000); // this needs calibration TextLCD.print("heh"); completeStop(true); updateDirection(0); } void turnRight() throws InterruptedException{ Motor.A.forward(); Motor.C.backward(); Thread.sleep(1000); // this needs to be calibrated TextLCD.print("SPLAT"); completeStop(true); updateDirection(2); } void completeStop(boolean wentDistance) throws InterruptedException{ // and stops the timer if robot had not run in a cirle Motor.A.stop(); Motor.C.stop(); if (wentDistance == true) { stopTimer(); updateCoordinates(); updateChoiceLog(); } Thread.sleep(500); } // Timer: activateTimer, timedOut, stopTimer void activateTimer(){ currentTime = 0; // reset stopwatch timer.start(); } void timedOut(){ // With a precision of 10 milliseconds, this will increment currentTime, // which will serve as a stopwatch. currentTime += .01; } void stopTimer(){ // This might eventually have other functionality, so it remains. timer.stop(); } // Sensor: void stateChanged(Sensor aSource, int aOldValue, int aNewValue){ // do something with this event } // Decisions: whatColorIsit, areWeOffTrack, chooseDirection, // backOutFromDeadEnd static void whatColorIsIt(int whichSensor){ // This determines what the sensor is seeing } static void areWeOffTrack(){ // If the time values in the last few rounds of the array are too small, // it will assume it has veered off course and will wait for human // intervention. } static void chooseDirection(){ // If presented with more than one directional option, this will pick // one at random (sort of). Random random = new Random(); // this is absurd, but Math.random won't // work here int someNum = random.nextInt(3); // an int under 3 while (true){ if(choiceLog[stepNumber][someNum] == 1){ choiceLog[stepNumber][3] = someNum; return; } else if (someNum != 2){ someNum++; } else { someNum = 0; } } } static void backOutFromDeadEnd(){ // If the robot finds itself in a dead end, its navigation plan is a // little bit different from its usual one. } // Write: updateDirection, updateCoordinates, updateTimeLog static void updateDirection(int turn){ // This updates the direction variable, keeping track of its actual // orientation. The following integers denote the following directions: // 0 left; 1 up; 2 right; 3 down // These keep the values of left and right consistent. if (turn == 0){ // process left turn if (direction != 0){ direction--; } else { direction = 3; } } else{ // process right turn if (direction != 3){ direction++; } else{ direction = 0; } } } static void updateCoordinates(){ // This is practicing knowing where it is in case I can implement it // better. Takes a value of time the motor was on, and uses that with // directional information to estimate location relative to its start. // Positive values are for right and up. if (direction == 0){ // left coordinates[0] -= currentTime; } else if (direction == 1){ // up coordinates[1] += currentTime; } else if (direction == 2){ // right coordinates[0] += currentTime; } else { // down coordinates[1] -= currentTime; } } static void updateTimeLog(){ // Whenever it stops, it will put for how many hundredths of seconds the // motor had been on then. timeLog[stepNumber] = currentTime; } /* More complicated versions static void updateCoordinates(double time){ // This takes the timer value, looks at the direction and previous // coordinates, and creates a new array within the big array to // represent. } static void haveWeBeenHereBefore(){ // This will allow the thing to remember better. } */ // Ending: finish, passivateRCX void finish() throws InterruptedException{ TextLCD.print("Done!"); Motor.A.setPower(6); Motor.B.setPower(6); Motor.A.forward(); Motor.C.backward(); Thread.sleep(3000); Motor.A.backward(); Motor.C.forward(); Thread.sleep(3000); completeStop(true); passivateRCX(); } static void passivateRCX(){ for (int i = 0; i <= 2; i++){ Sensor.SENSORS[i].passivate(); } } }