# Stanford程式設計方法學公開課作業 1 —-關於Karel 中點尋找問題的討論 （Midpoint finding）

Problem 3

As an exercise in solving algorithmic problems, program Karel to place a single beeper at the center of 1st Street. For example, if Karel starts in the world

it should end with Karel standing on a beeper in the following position:

Note that the final configuration  of the world should have only a single beeper at the midpoint of 1st Street.  Along the way, Karel is allowed to place additional beepers wherever it wants to, but must pick them all up again before it finishes.

In solving this problem, you may count on the following facts about the world:

• Karel starts at 1st Avenue and 1st Street, facing east, with  an infinite number of beepers in its bag.
• The initial state of the world includes no interior walls or beepers.
• The world need not be square, but you may assume that it is at least as tall as it is wide.

Your program, moreover, can assume the following simplifications:

• If the width of the world is odd, Karel must put the beeper in the center square. If the width is even, Karel may drop the beeper on either of the two center squares.
• It does not matter which direction Karel is facing at the end of the run.

There are many different algorithms you can use to solve this problem. The interesting part of this assignment is to come up with a strategy that works.

1. 先將第一行填滿，然後左右各取出一個，並在第二行放下一個，當第一行beeper取完的時候，第二行的beeper數目即為第一行長度的一半

``````/* * File: MidpointFindingKarel.java * ------------------------------- * THe MidpointFindingKarel class will leave a beeper on the corner  * closest to the center of 1st Street * (or either of the two central corners if 1st Street has an even * number of corners).  The world may be of any size, but you are allowed to * assume that it is at least as tall as it is wide. */
import stanford.karel.*;
public class MidpointFindingKarel extends SuperKarel {
public void run() {		fillFirstRow();				//midpoint finding part		while (beepersPresent()) {			collectLeftEnd();			secondRowAddOne();			collectRightEnd();							//step forward one more to make sure there is no more beeper			if (frontIsClear()) { //check whether all beepers have been collected				move(); //if width is odd number Karel will finally arrive (1,1)			} //if width is even number, Karel will finally arrive mid-point		}				//go to the midpoint of the first row, put beeper then collect beepers		//on the 2nd row		goToMidpoint();		putBeeper();		collectSecondRow();			goToOrigin();		moveToBeeper();	}		/*	 * fillFirstRow method is defined to fill the 1st row with beepers	 * Karel will put only one beeper at each corner	 * precondition: facing east, at (1,1) point	 * postcondition: facing east, (1,1) point	 */	private void fillFirstRow() {		goToOrigin(); //go to (1,1) facing east		while (frontIsClear()) {			putBeeper();			move();		}		putBeeper(); //Off-By-One-Bug		//make sure go back to (1,1) point facing east		turnAround();		moveToWall();		turnAround();	}
/*	 * Collect the most left beeper on the 1st row, if there is any	 * Precondition: any case	 * Postcondition: facing east, stop at the position where Karel 	 * just collected one beeper, 1st row	 */	private void collectLeftEnd() {		goToOrigin();		moveToBeeper();		if (beepersPresent()) {			pickBeeper();		}			}		/*Similar as collectLeftEnd, while in this method, Karel will start	  from the most right end, and try to collect the beeper at the 	  most right end	*/	private void collectRightEnd() {		goToOrigin();		moveToWall(); //Reach the right end of the 1st row		turnAround(); //To face west		moveToBeeper();		if (beepersPresent()) {			pickBeeper();		}	}		/*	 * go to 2nd row, add one more beeper	 * precondition: anywhere at 1st row facing east	 * postcondition: on 2nd row, at the most end where no more beeper exist	 */	private void secondRowAddOne() {		goToOrigin();		bigTurnLeft();		turnRight();		while( beepersPresent()) { //arrive at the end of existing beepers			move();		}		putBeeper();	}			/*	 * Make sure Karel will go to the midpoint of the first row	 * Precondition: any case	 * Postcondition: arrive at the mid point of the 1st row, facing south	 */	private void goToMidpoint() {		goToOrigin();		moveToWall(); //reach right end 		bigTurnLeft();		turnLeft();		moveToBeeper();		bigTurnLeft();			}		private void collectSecondRow() {		goToOrigin();		bigTurnLeft(); //facing north		turnRight(); //facing east		while (beepersPresent() || frontIsBlocked()) {			pickBeeper();			move();		}	}		/*Move Karel back to origin of map (1,1), make it facing east	 * Precondition: can be any case	 * Postcondition: standing on (1,1) facing east	 */	private void goToOrigin() { 		while (notFacingSouth()) {			turnLeft();		}		moveToWall();		while (notFacingWest()) {			turnLeft();		}		moveToWall();		while (notFacingEast()) {			turnLeft(); //Karel will face east		}	}			/*	 * move Karel to first beeper on its way	 * precondition: facing east/west	 * postcondition: share same orientation as in precondition, standing on 	 * the corner where first beeper appears on its way	 */	private void moveToBeeper() { 		while ( noBeepersPresent() && frontIsClear() ) {			move();		}	}		/*	 * go to upper/lower row above without changing column number, 	 * make a big turn left	 * precondition: any case	 * postcondition: same column, arrive at upper/lower row 	 * which is one above/under the row in precondition, 	 * direction will turn left	 */	private void bigTurnLeft() {		turnLeft();		move(); 	}		// Similar as in bigTurnLeft, but in this case make a big	//turn right	private void bigTurnRight() {		turnRight();		move(); 	}		private void moveToWall() { //move Karel to wall		while (frontIsClear()) {			move();		}	}	}// end of public class``````
2. 對於方法1中的思路進行進一步優化，其實左側取一個右側再取一個完全可以兼併成為從左側出發一次取兩個，同時放一個到第二行，這將大量節省Karel的運動過程，優化後程式碼如下：
``````/* * File: MidpointFindingKarel.java * ------------------------------- * THe MidpointFindingKarel class will leave a beeper on the corner  * closest to the center of 1st Street * (or either of the two central corners if 1st Street has an even * number of corners).  The world may be of any size, but you are allowed to * assume that it is at least as tall as it is wide. */
import stanford.karel.*;
public class MidpointFindingKarel extends SuperKarel {
public void run() {		fillFirstRow();				//midpoint finding part		while (beepersPresent()) {			secondRowAddOne();			collectLeftEnd();			//step forward one more to make sure there is no more beeper			if (frontIsClear()) { //check whether all beepers have been collected				move(); //if width is odd number Karel will finally arrive (1,1)			} //if width is even number, Karel will finally arrive mid-point					}				//go to the midpoint of the first row, put beeper then collect beepers		//on the 2nd row		goToMidpoint();		putBeeper();		collectSecondRow();			goToOrigin();		moveToBeeper();	}		/*	 * fillFirstRow method is defined to fill the 1st row with beepers	 * Karel will put only one beeper at each corner	 * precondition: facing east, at (1,1) point	 * postcondition: facing east, at the most right end of 1st row	 */	private void fillFirstRow() {		goToOrigin(); //go to (1,1) facing east		while (frontIsClear()) {			putBeeper();			move();		}		putBeeper(); //Off-By-One-Bug	}
/*	 * Collect the most left beeper on the 1st row, if there is any	 * Precondition: any case	 * Postcondition: facing east, stop at the position where Karel 	 * just collected one beeper, 1st row	 */	private void collectLeftEnd() {		goToOrigin();		moveToBeeper();		if ( beepersPresent() ) {			pickBeeper();		}		if ( frontIsClear() ) {			move();		}		if ( beepersPresent() ) {			pickBeeper();		}	}
/*	 * go to 2nd row, add one more beeper	 * precondition: anywhere at 1st row facing east	 * postcondition: on 2nd row, at the most end where no more beeper exist	 */	private void secondRowAddOne() {		goToOrigin();		bigTurnLeft();		turnRight();		while( beepersPresent()) { //arrive at the end of existing beepers			move();		}		putBeeper();	}			/*	 * Make sure Karel will go to the midpoint of the first row	 * Precondition: any case	 * Postcondition: arrive at the mid point of the 1st row, facing south	 */	private void goToMidpoint() {		goToOrigin();		moveToWall(); //reach right end 		bigTurnLeft();		turnLeft();		moveToBeeper();		bigTurnLeft();			}		private void collectSecondRow() {		goToOrigin();		bigTurnLeft(); //facing north		turnRight(); //facing east		while (beepersPresent() || frontIsBlocked()) {			pickBeeper();			move();		}	}		/*Move Karel back to origin of map (1,1), make it facing east	 * Precondition: can be any case	 * Postcondition: standing on (1,1) facing east	 */	private void goToOrigin() { 		while (notFacingSouth()) {			turnLeft();		}		moveToWall();		while (notFacingWest()) {			turnLeft();		}		moveToWall();		while (notFacingEast()) {			turnLeft(); //Karel will face east		}	}			/*	 * move Karel to first beeper on its way	 * precondition: facing east/west	 * postcondition: share same orientation as in precondition, standing on 	 * the corner where first beeper appears on its way	 */	private void moveToBeeper() { 		while ( noBeepersPresent() && frontIsClear() ) {			move();		}	}		/*	 * go to upper/lower row above without changing column number, 	 * make a big turn left	 * precondition: any case	 * postcondition: same column, arrive at upper/lower row 	 * which is one above/under the row in precondition, 	 * direction will turn left	 */	private void bigTurnLeft() {		turnLeft();		move(); 	}			private void moveToWall() { //move Karel to wall		while (frontIsClear()) {			move();		}	}	}// end of public class``````
3. 對於方法2中的思路進行進一步優化，其實karel完全可以將用於中點位置計數的beeper直接放在第一行，這樣Karel的運動過程將會得到進一步的簡化，換言之，Karel將從（1,1）出發，一次取兩個beeper，然後折回（1,1），放下一個beeper，之後再去取兩個beeper，折回在（1,2）放下一個beeper，以此類推，直至開始時放置的beeper全部取完。具體程式碼如下：
``````/* * File: MidpointFindingKarel.java * ------------------------------- * THe MidpointFindingKarel class will leave a beeper on the corner  * closest to the center of 1st Street * (or either of the two central corners if 1st Street has an even * number of corners).  The world may be of any size, but you are allowed to * assume that it is at least as tall as it is wide. */
import stanford.karel.*;
public class MidpointFindingKarel extends SuperKarel {
public void run() {		fillFirstRow();			goToOrigin();		//midpoint finding part		while (beepersPresent()) {			collectLeftEnd();			firstRowAddOne();			move(); //go to blank place without any beeper			moveToBeeper();		}				//go to the midpoint of the first row, put beeper then collect beepers		//on the 2nd row		goToMidpoint();	}		/*	 * fillFirstRow method is defined to fill the 1st row with beepers	 * Karel will put only one beeper at each corner	 * precondition: facing east, at (1,1) point	 * postcondition: facing east, at the most right end of 1st row	 */	private void fillFirstRow() {		goToOrigin(); //go to (1,1) facing east		while (frontIsClear()) {			putBeeper();			move();		}		putBeeper(); //Off-By-One-Bug	}
/*	 * Collect the most left beeper on the 1st row, if there is any	 * Precondition: any case	 * Postcondition: facing east, stop at the position where Karel 	 * just collected one beeper, 1st row	 */	private void collectLeftEnd() {		if ( beepersPresent() ) {			pickBeeper();		}		if ( frontIsClear() ) {			move();		}		if ( beepersPresent() ) {			pickBeeper();		}	}
/*	 * go to 2nd row, add one more beeper	 * precondition: anywhere at 1st row facing east	 * postcondition: on 2nd row, at the most end where no more beeper exist	 */	private void firstRowAddOne() {		goToOrigin();		while( beepersPresent()) { //arrive at the end of existing beepers			move();		}		putBeeper();	}			/*	 * Make sure Karel will go to the midpoint of the first row	 * Precondition: any case	 * Postcondition: arrive at the mid point of the 1st row, facing west	 */	private void goToMidpoint() {		goToOrigin();		while ( beepersPresent() ) {						pickBeeper();			move();		} 		turnAround(); //OBOB, Karel will move one more step 		move(); //at the end of this part		putBeeper();	}		/*Move Karel back to origin of map (1,1), make it facing east	 * Precondition: can be any case	 * Postcondition: standing on (1,1) facing east	 */	private void goToOrigin() { 		while (notFacingSouth()) {			turnLeft();		}		moveToWall();		while (notFacingWest()) {			turnLeft();		}		moveToWall();		while (notFacingEast()) {			turnLeft(); //Karel will face east		}	}			/*	 * move Karel to first beeper on its way	 * precondition: facing east/west	 * postcondition: share same orientation as in precondition, standing on 	 * the corner where first beeper appears on its way	 */	private void moveToBeeper() { 		while ( noBeepersPresent() && frontIsClear() ) {			move();		}	}		/*	 * go to upper/lower row above without changing column number, 	 * make a big turn left	 * precondition: any case	 * postcondition: same column, arrive at upper/lower row 	 * which is one above/under the row in precondition, 	 * direction will turn left	 */	private void bigTurnLeft() {		turnLeft();		move(); 	}			private void moveToWall() { //move Karel to wall		while (frontIsClear()) {			move();		}	}	}// end of public class``````
4. 對於方案1還有另一種優化的思路，那就是擺脫計數器的限制，左右各取出一個，一直這樣取下去，直至取完，那麼Karel此時必然自然位於中點位置從而無需計數，其運動過程將更為簡單快捷。程式設計的要點在於要保證Karel始終執行往復運動直至beeper被全部取盡，程式碼如下：
``````/* * File: MidpointFindingKarel.java * ------------------------------- * THe MidpointFindingKarel class will leave a beeper on the corner  * closest to the center of 1st Street * (or either of the two central corners if 1st Street has an even * number of corners).  The world may be of any size, but you are allowed to * assume that it is at least as tall as it is wide. */import stanford.karel.*;
public class MidpointFindingKarel extends SuperKarel {	public void run() {		fillFirstRow();			goToOrigin();		//midpoint finding part		while (beepersPresent()) {					pickBeeper();									move();			moveToEnd();							}				putBeeper();	}		/*	 * fillFirstRow method is defined to fill the 1st row with beepers	 * Karel will put only one beeper at each corner	 * precondition: facing east, at (1,1) point	 * postcondition: facing east, at the end of 1st row	 */	private void fillFirstRow() {		goToOrigin(); //go to (1,1) facing east		while (frontIsClear()) {			putBeeper();			move();		}		putBeeper(); //Off-By-One-Bug	}		/*	 * Karel will move to the last beeper of the beeper chain	 * Precondition: standing at one end facing that chain	 * Postcondition: standing at another end facing that chain 	 * which means the orientation is changed oppositely 	 */	private void moveToEnd() {				while ( beepersPresent() && frontIsClear() ) {			move();		}		if ( frontIsBlocked() && beepersPresent() ) { //for the case arrive at the wall			turnAround();		} else {			turnAround();			move();		}			}		/*Move Karel back to origin of map (1,1), make it facing east	 * Precondition: can be any case	 * Postcondition: standing on (1,1) facing east	 */	private void goToOrigin() { 		while (notFacingSouth()) {			turnLeft();		}		moveToWall();		while (notFacingWest()) {			turnLeft();		}		moveToWall();		while (notFacingEast()) {			turnLeft(); //Karel will face east		}	}		private void moveToWall() { //move Karel to wall		while (frontIsClear()) {			move();		}	}	}// end of public class``````