📄 piece.java
字号:
/* Author: <insert your name here> ugrad.cs.ubc.ca address: <insert your ugrad.cs.ubc.ca e-mail address here> Date: <insert date here> By submitting this file, I acknowledge that the person whose name appears above is the *only* author of the code required to solve this assignment.*/package cs111.tetris.data;import java.awt.*;import java.util.*;//// This is the "starter code" version of the Piece.java file.//// Most of this code is provided for you but there are a few places// left where you need to fill in some implementations.//// We have conveniently left all of the JavaDoc comments intact// so you wouldn't have to switch back and forth between your// JavaDoc browser and Java editor.//// Hint: Open Eclipse's "Tasks" view. All places in the code where// you need to do work are marked with TO DO comments which Eclipse// automatically finds for you and puts in the Tasks view///// To open Eclipse's task view go to menu Window >> Show View and // select "Tasks". Click some of the buttons in the top of the // toolbar and you'll find they let you filter the task to only // see those in the current file.///** * A Piece instance represents a tetris piece in a particular rotation. Each piece * is defined by a number of blocks known as its "body". The body is represented * by the (x, y) coordinates of its blocks, with the origin in the lower-left * corner. <br> * <img src="doc-files/piece.gif" border="0" height="92" width="102"><br> * So the body of this piece is defined by the (x, y) points : (0, 0), (1, * 0), (1, 1), (2, 1). * <p> * The Piece class and the Board class both measure things in this way -- * block by block with the origin in the lower-left corner. As a design * decision, the Piece and Board classes do not know about pixels -- they * measure everything block by block. Or put another way, all the knowledge of * pixels is isolated in the cs111.tetris.gui package. * <p> * Each piece responds to the methods like getWidth(), getHeight(), and * getBody() that allow the client to see the state of the piece. The getSkirt() * method returns an array that shows the lowest y value for each x value * across the piece ({0, 0, 1} for the piece above). The skirt makes it easier * to compute how a piece will come to land in the board. There are no methods * that change a piece -- it is "immutable". To allow the client to see the * various piece rotations that are available, the Piece class builds an array * of the standard tetris pieces internally -- available through the * Piece.getPieces() method. This array contains the first rotation of each of the * standard pieces. Starting with any piece, the nextRotation() method returns * the "next" piece object that represents the next clockwise rotation. * Enough calls to nextRotation() gets the caller back to the original piece. * * @author Nick Parlante (revised by Kris De Volder 2007). */public class Piece { /** * This array contains piece objects, one piece object for each of the * 7 standard tetris pieces in the first rotation. */ static private Piece[] pieces; private Point[] body; private int[] skirt; private int width; private int height; private Piece next; // "next" rotation /** * Defines a new piece given the Points that make up its body. * Does <b>not</b> set up the rotations through the next field. * <p> * This constructor is PRIVATE so clients can not create pieces themselves. * If a client wants a piece object, they must use the {@link #getPieces()} * method */ private Piece(Point[] points) { body = points; width = 0; height = 0; for (int i = 0; i < points.length; i++) { //TODO: update width and height if needed. //Take advantage of this loop to update the width and //height fields. } //TODO: The code that initializes the skirt is missing. //Implementation notes: // You should create an array of the appropriate length to hold the // skirt values. // Then loop through the skirt to initialize it with y values // that are "high enough". // Then loop through the body and for each point in it, // update the appropriate spot in the skirt array // if the point has a smaller y value. } /** * Returns width of the piece measured in blocks. * @return the width of the piece measured in blocks. */ public int getWidth() { return width; } /** * Returns the height of the piece measured in blocks. * @return the height of the piece measured in blocks. */ public int getHeight() { return height; } /** * Returns a pointer to the piece's body. The caller should not modify this * array. */ public Point[] getBody() { return body; } /** * Returns a pointer to the piece's skirt. For each x value across the * piece, the skirt gives the lowest y value in the body. This useful for * computing where the piece will land. The caller should not modify this * array. */ public int[] getSkirt() { return skirt; } /** * Returns a piece that is 90 degrees clockwise rotated from the * receiver. * <p> * Implementation: The Piece class pre-computes all the rotations once. This * method just hops from one pre-computed rotation to the next in constant * time. */ public Piece nextRotation() { return next; } /** * Returns an array containing the first rotation of each of the 7 standard * tetris pieces. The next (clockwise) rotation can be obtained from * each piece with the {@link #nextRotation()} method. In this way, the * client can iterate through all the rotations until eventually getting * back to the first rotation. */ public static Piece[] getPieces() { if (pieces == null) { // We create the array the first time it is asked for. // (If pieces is still null it means we haven't created it yet). pieces = new Piece[] { pieceRow(new Piece(parsePoints("0 0 0 1 0 2 0 3"))), // 0 pieceRow(new Piece(parsePoints("0 0 0 1 0 2 1 0"))), // 1 pieceRow(new Piece(parsePoints("0 0 1 0 1 1 1 2"))), // 2 pieceRow(new Piece(parsePoints("0 0 1 0 1 1 2 1"))), // 3 pieceRow(new Piece(parsePoints("0 1 1 1 1 0 2 0"))), // 4 pieceRow(new Piece(parsePoints("0 0 0 1 1 0 1 1"))), // 5 pieceRow(new Piece(parsePoints("0 0 1 0 1 1 2 0"))), // 6 }; } return pieces; } /** * Returns true if two pieces are the same -- their bodies contain the same * points. Interestingly, this is not the same as having exactly the same * body arrays, since the points may not be in the same order in the bodies. * Used internally to detect if two rotations are effectively the same. * * This methods is only for internal use when we are generating the piece * rotations (we need to know when to stop). * * Client code can use == to compare pieces, since our implementation makes * sure if two pieces are equal then they are actually == . */ private boolean isSameAs(Piece other) { //TODO: implement this correctly return false; // Notes: You need a nested loop // One loop goes over the body of this piece. // For each point of this piece, you should be able to find // an equal point in the body of the other piece. } /** * This is a helper method to setup the next field of a piece. * It creates all rotation of parameter piece and links all these * rotations together by making each of the piece's next field * point to the next rotation. * <p> * The tricky part is that we have to stop creating piece rotations * when we detect that rotating the piece gets us back to a piece * that has the exact same shape as the starting piece. * * @param piece * @return piece (now with its next field properly set up and all rotations * of this piece linked through their next fields into a * cirular list). */ private static Piece pieceRow(Piece piece) { final int MAX_ROTATIONS = 6; // MAX_ROATIONS is a safeguard against buggy student code. // even if isSameAs is broken or if rotatePoints is broken, // the loop should stop after a fixed number of rotations. Piece firstPiece = piece; Piece currentPiece = piece; Piece nextPiece = new Piece(currentPiece.rotatePoints()); int count = 0; while (!nextPiece.isSameAs(firstPiece) && count<MAX_ROTATIONS) { count++; currentPiece.next = nextPiece; currentPiece = nextPiece; nextPiece = new Piece(currentPiece.rotatePoints()); } // The loop ends when nextPiece is a piece object that // represents the same piece as firstPiece. // => nextPiece is discarded (because we shouldn't have two piece objects // that represent the same piece in the same rotation). // We will make currentPiece.next to point to the firstPiece // This creates a kind of linked structure where each piece points // to the next rotation and the last one points back to the beginning. currentPiece.next = firstPiece; return firstPiece; } /** * A helper to rotate a piece. It computes an array of rotated points from * this piece's body. (clockwise!) */ private Point[] rotatePoints() { //TODO: implement this correctly // // This is tricky. Take piece of paper and a pencil. // draw a piece and its clockwise rotation and try to find // a formula that for a given block tells the coordinates // where that block will end up in the rotated piece. // // Maybe it helps to think in terms of "mirroring" transforms // that reflect a piece along one of its central axis. // (From math you may remember that composing two mirroring transforms // results in a rotation transform). // Below is a "dummy" implementation. This is sufficient to avoid the // tester from crashing but is not correct (it doesn't actually do // any rotation). // // Be careful, you should not change the body of this piece! // A correct implementation should make a new array for the // rotated points. return body; } /** * A helper method use to create the initial pieces: * <p> * Given a string of x,y pairs ("0 0 0 1 0 2 1 0"), parses the points into a * Point[] array. (Provided code) */ private static Point[] parsePoints(String string) { ArrayList<Point> points = new ArrayList<Point>(); StringTokenizer tok = new StringTokenizer(string); try { while (tok.hasMoreTokens()) { int x = Integer.parseInt(tok.nextToken()); int y = Integer.parseInt(tok.nextToken()); points.add(new Point(x, y)); } } catch (NumberFormatException e) { throw new RuntimeException("Could not parse x,y string:" + string); } return points.toArray(new Point[points.size()]); } /** * By implementing the toString method you change the way piece objects * get printed out (e.g. with println). This may be convenient if * you are debugging because the Eclipse debugger will show whatever toString * returns when it displays the contents of a variable that has * a Piece object in it. */ public String toString() { try { boolean[][] pieceMap = new boolean[getHeight()][getWidth()]; for (int i = 0; i < body.length; i++) pieceMap[body[i].y][body[i].x] = true; StringBuffer buf = new StringBuffer(getHeight() * (getWidth() + 1)); for (int i = pieceMap.length - 1; i >= 0; i--) { for (int j = 0; j < pieceMap[i].length; j++) { buf.append(pieceMap[i][j] ? "#" : "."); } buf.append("\n"); } return buf.toString(); } catch (Throwable e) { // If anything goes wrong in the code above this catch clause // will get executed. // This really shouldn't happen, but it might, for example, // if your piece rotations are not correctly implemented. // In that case we just fall back on Java's // default toString implementation (inherited from Object). return super.toString(); } } }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -