/** * This class represents a "turtle" that can be asked to change directions * and move around the screen. It's similar to the concept of the Logo * turtle, though with only four possible headings. * * The version below has been modified for use in a 161 lab: It has a * copy constructor that the original didn't, and there are getter methods * for providing information about the turtle's state. * * @author Brad Richards * @version 2.0 */ public class Turtle { private int xDirection; // Are we heading left (-1) or right (1)? private int yDirection; // Are we heading up (-1) or down (1)? private int distance; // How far do we move when forward() is called? private Circle shell; // The Circle representing our turtle /** * This constructor sets up our "turtle" and leaves it facing north. * It arbitrarily decides that we should move in 20-pixel increments. */ public Turtle() { shell = new Circle(); shell.changeSize(20); shell.changeColor("black"); shell.makeVisible(); faceNorth(); distance = 20; } /** * The second constructor allows the user to specify how far our turtle * should move each time forward() is called: It takes a parameter * containing this extra information, and uses an assignment statement * to store a copy of that value in our distance field. * * Notice the sneaky trick it's using to avoid duplicating the code * above: Java lets you call one constructor from another by using * the name "this" in place of a method name. The first line below * is calling the default (no-input) constructor above to do most of * the setup, and then just changing the distance. * * @param initialDistance The value to use for distance. */ public Turtle(int initialDistance) { this(); // Call constructor above to do most of the work distance = initialDistance; // Set distance to specified value } /** * This copy constructor takes another Turtle object for inspiration, * and sets "us" up to look just like them. We have to take special * care to be sure that we don't share a Circle object with them -- * we want our Circle to be independent from theirs, but to take on * the same *values* as their Circle. * * @param other A reference to another Turtle we want to copy. */ public Turtle(Turtle other) { // Most of the Turtle fields can be copied over via assignment // statements since they're primitive types. xDirection = other.xDirection; yDirection = other.yDirection; distance = other.distance; // The Circle is harder. We could make a new "blank" Circle and // try to copy over the state from other's Circle to ours, but // there aren't enough "getters" and "setters" for us to do that // from our perspective here in the Turtle class. Luckily, there's // a copy constructor in the Circle class! We can create a new // Circle that's a copy of other's circle by passing a reference // to other's Circle to the copy constructor. shell = new Circle(other.shell); shell.makeVisible(); // Need to draw it to make sure its visible } /** * Compares our state with that of another turtle. We consider the two * to be equal if our X and Y directions both match, our distance is the * same as theirs, and our two Circles are at the same location. * * @return True if our state matches that of the other turtle, false otherwise. */ public boolean equals(Turtle other) { // Check that X directions match (int, and Turtle-to-Turtle) // Check that Y directions match (int, and Turtle-to-Turtle) // Check that distances match (int, and Turtle-to-Turtle) // Check that circles are at same location (Obj ref, inside Turtle) // Check that my x position matches their x position // Check that my y position matches their y position // We can compare most fields with ==, but we need to "dig into" the // circles to compare their positions too. There are getter methods // for x and y positions, we just need to get the syntax right to get // to the circle references. return xDirection == other.xDirection && yDirection == other.yDirection && distance == other.distance && shell.getXPosition() == other.shell.getXPosition() && shell.getYPosition() == other.shell.getYPosition(); } /** * A "getter" method for providing the turtle's xDirection. * * @return Our xDirection value. */ public int getXDirection() { return xDirection; } /** * A "getter" method for providing the turtle's yDirection. * * @return Our yDirection value. */ public int getYDirection() { return yDirection; } /** * A "getter" method that returns a reference to our Circle object. * * @return A reference to our Circle. */ public Circle getCircle() { return shell; } /** * Move in the direction we're heading, by the number of pixels stored in the * distance field. The direction is represented by a pair of fields, each of * which is expected to be either 0, 1, or -1. We can figure out how far to * move in each dimension by multiplying the direction by distance. */ public void forward() { shell.slowMoveHorizontal(xDirection * distance); shell.slowMoveVertical(yDirection * distance); } /** * The setDistance method can be used to change how far the turtle moves each * time forward() is called. It takes this information as a parameter, and * stores a copy of the value into our distance field using an assignment * statement. * * @param newDistance The new value to use for distance. */ public void setDistance(int newDistance) { distance = newDistance; } /** * Change the turtle's state such that we'll head "north" (up the screen) the * next time forward() is called. There's no movement along the X axis involved * in moving north, so xDirection is set to 0. The yDirection field is set to * -1 since movement up the screen is in the negative direction. */ public void faceNorth() { xDirection = 0; yDirection = -1; } /** * Change the turtle's state so that we're heading south. */ public void faceSouth() { xDirection = 0; yDirection = 1; } /** * Change the turtle's state so that we're heading west. */ public void faceWest() { xDirection = -1; yDirection = 0; } /** * Change the turtle's state so that we're heading east. */ public void faceEast() { xDirection = 1; yDirection = 0; } }