⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 game.java

📁 一个java的俄罗斯方块实现
💻 JAVA
字号:
package cs111.tetris.data;import java.util.Random;/** * A Game instance represents the complete state of a Tetris * game and implements the game logic.  * That is, it keeps track of a Board and a current * piece being played on the board and provides a method for * moving the piece correctly in accordance with the rules of the  * tetris game. * <p> * The most important method in this class is {@link #doMove(int)}. * Calling <code>doMove</code> is equivalent to issuing a command to * move the current piece being played (left, right, down or drop). * <p>  * This code knows nothing of pixels or how the * board and game info is shown on the screen. This code also knows  * nothing of where the <code>doMove</code> commands * come from. Therefore, the way the game is controlled or displayed * on the screen could be implemented in different ways without * a need to change the code in this class (and other classes in this * package). *  * @author Kris De Volder */public class Game {	// These constants are meant to be used as the argument of 	// the doMove method. (This is much more readable than using 	// these "magic" numbers directly)	public static final int VERB_LEFT = 1;	public static final int VERB_RIGHT = 2;	public static final int VERB_ROTATE = 3;	public static final int VERB_DROP = 4;	public static final int VERB_DOWN = 5;	// size of the board in blocks	private int WIDTH;	private static final int DEFAULT_WIDTH = 10;	private int HEIGHT;	private static final int DEFAULT_HEIGHT = 20;	// Extra blocks at the top for pieces to start.	// If a piece is sticking up into this area	// when it has landed -- game over!	private final int TOP_SPACE;	private static final int DEFAULT_TOP_SPACE = 4;	// When this is true, plays a fixed sequence of 100 pieces	private boolean testMode = false;	private final int TEST_LIMIT = 100;	// Board data structures	private Board board;	private Piece[] pieces;	// The current piece in play or null	private Piece currentPiece;	private int currentX;	private int currentY;	private boolean moved; // did the player move the piece	// The piece we're thinking about playing	// -- set by computeNewPosition	private Piece newPiece;	private int newX;	private int newY;	// State of the game	private int count; // how many pieces played so far	private Random random; // the random generator for new pieces	private boolean gameOn; // indicates whether the game is running	private GameListener[] listener = new GameListener[2];	   // listeners stored in this array will be notified (by calling	   // one of their methods) whenever the game state is changed.	   // Currently we provide a rather dumb implementation that	   // only has space for 2 listeners. This is sufficient for the	   // application we are using it in now (but rather limiting	   // with respect to possible future evolution of this program).	   // => A better implementation may be needed.	/**	 * Create tetris game with a board of given height, width and topSpace	 * (measured in tetris blocks).	 * 	 * @param width	 *            The width of the game board	 * @param height	 *            The height of the game board not including the area at the top	 *            reserved for entering new pieces.	 * @param topSpace	 *            The height of the new pieces area at the top.	 */	public Game(int width, int height, int topSpace) {		super();		WIDTH = width;		HEIGHT = height;		TOP_SPACE = topSpace;		pieces = Piece.getPieces();		board = new Board(WIDTH, HEIGHT + TOP_SPACE);	}	/**	 * Create a Tetris game with a board of default dimensions.	 */	public Game() {		this(DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_TOP_SPACE);	}	/**	 * Start (or restart) the game. This clears the board	 * and enters the first piece in play at the top of the board.	 */	public void startGame() {		// cheap way to reset the board state		board = new Board(WIDTH, HEIGHT + TOP_SPACE);		count = 0;		notifyCountChanged();		if (testMode)			random = new Random(0); // same seq every time		else			random = new Random(); // diff seq each game		setGameOn(true);		addNewPiece();	}	/**	 * Stop the game. This puts the game in a stopped state.	 * A game in stopped state will not accept any further 	 * moves until the game is restarted.	 */	public void stopGame() {		setGameOn(false);	}	/**	 * Sets the gameOn status and notifies listeners if needed.	 * @param b	 */	private void setGameOn(boolean b) {		if (gameOn != b) {			gameOn = b;			notifyGameOnChanged();		}	}	/**	 * Given a piece, tries to install that piece	 * into the board and set it to be the current piece.     * <p>     * If the placement is not possible, then the placement	 * is undone, and the board is not changed. The board	 * should be in the committed state when this is called.	 * Returns the same error code as Board.place().	 */	private int setCurrent(Piece piece, int x, int y) {		int result = board.place(piece, x, y);		if (result <= Board.PLACE_ROW_FILLED) { // SUCCESS			currentPiece = piece;			currentX = x;			currentY = y;			notifyBoardChanged();		} else {			board.undo();		}		return (result);	}	/**	 * Selects the next piece to use using the random generator set in	 * startGame(). A subclass may override this method to put in a custom 	 * piece picking algorithm.	 * 	 * @return The next Piece to play	 */	protected Piece pickNextPiece() {		int pieceNum = (int) (pieces.length * random.nextDouble());		return pieces[pieceNum];	}	/**	 * Tries to add a new piece at the top of the board.	 * Ends the game if it's not possible.	 */	private void addNewPiece() {				if (testMode && count >= TEST_LIMIT) {			stopGame();			return;		}		count++;		Piece piece = pickNextPiece();		// Center it up at the top		int px = (board.getWidth() - piece.getWidth()) / 2;		int py = board.getHeight() - piece.getHeight();		// commit things the way they are		board.commit();		currentPiece = null;		// add the new piece to be in play		int result = setCurrent(piece, px, py);		// This probably never happens, since		// the blocks at the top allow space		// for new pieces to at least be added.		if (result > Board.PLACE_ROW_FILLED) {			stopGame();		}		notifyBoardChanged();		board.undo(); // This has to happen before notifyCountChanged		              // because we don't want the AI to see the falling piece.		notifyCountChanged();	}	/**	 * Figures out the new position for the current piece	 * based on the given verb (LEFT, RIGHT, ...).	 * The board should be in the committed state --	 * i.e. the piece should not be in the board at the moment.	 * This is necessary so dropHeight() may be called without	 * the piece "hitting itself" on the way down.	 * <p>	 * Sets the variables newX, newY, and newPiece to hold	 * what it thinks the new piece position should be.	 *	 * @param verb What direction is the piece moving in?	 */	private void computeNewPosition(int verb) {		// As a starting point, the new position is the same as the old		newPiece = currentPiece;		newX = currentX;		newY = currentY;		// Make changes based on the verb		switch (verb) {		case VERB_LEFT:			newX--;			break;		case VERB_RIGHT:			newX++;			break;		case VERB_ROTATE:			newPiece = newPiece.nextRotation();			// tricky: make the piece appear to rotate about its center			// can't just leave it at the same lower-left origin as the			// previous piece.			newX = newX + (currentPiece.getWidth() - newPiece.getWidth()) / 2;			newY = newY + (currentPiece.getHeight() - newPiece.getHeight()) / 2;			break;		case VERB_DOWN:			newY--;			break;		case VERB_DROP:			// note: if the piece were in the board, it would interfere here			newY = board.dropHeight(newPiece, newX);			if (newY>currentY) { // We should never drop "upwards"				newY = currentY-1;			}			break;		default:			throw new RuntimeException("Bad verb");		}	}	/**	 * Issue a command to the game to move the current piece.	 * <p>	 * In response to the command the game state will be updated apropriately.	 * This method implements the game logic so that clients don't have to 	 * worry about it. Several things might happen as a result of a call 	 * to this method.	 * <ul>	 *   <li> The piece is moved.	 *   <li> The piece comes to rest and a new piece is added at the top	 *   <li> If a piece comes to rest some rows may be full and will get cleared.	 *   <li> If piece comes to rest with some portion of it in the topSpace => the game 	 *        is over (gameOn changes to false).	 *   <li> The move is illegal => nothing happens.	 * <p>	 * As the game state is updated any installed {@link GameListener}s will be	 * notified. (So the GUI can show this on the screen, or the AI can notice when 	 * it is time to compute the next move).	 * 	 * @param verb	 *            One of the VERB constants indicating whether to move the	 *            current piece DOWN, LEFT, RIGHT or to DROP it.	 */	public void doMove(int verb) {		if (!gameOn)			return;		if (currentPiece != null) {			board.undo(); // remove the piece from its old position		}		// Sets the newXXX ivars		computeNewPosition(verb);		// try out the new position (rolls back if it doesn't work)		int result = setCurrent(newPiece, newX, newY);		// if row clearing is going to happen, draw the		// whole board so the green row shows up		if (result == Board.PLACE_ROW_FILLED)			notifyBoardChanged();		boolean failed = (result >= Board.PLACE_OUT_BOUNDS);		// if it didn't work, put it back the way it was		if (failed) {			if (currentPiece != null)				board.place(currentPiece, currentX, currentY);		}		/*		 How to detect when a piece has landed:		 if this move hits something on its DOWN verb,		 and the previous verb was also DOWN (i.e. the player was not		 still moving it),  then the previous position must be the correct		 "landed" position, so we're done with the falling of this piece.		 */		if (failed && verb == VERB_DOWN && !moved) { // it's landed			if (board.clearRows()) {				notifyBoardChanged();			}			// if the board is too tall, we've lost			if (board.getMaxHeight() > board.getHeight() - TOP_SPACE) {				stopGame();			}			// Otherwise add a new piece and keep playing			else {				addNewPiece();			}		}		// Note if the player made a successful non-DOWN move --		// used to detect if the piece has landed on the next tick()		moved = (!failed && verb != VERB_DOWN);	}	/**	 * Returns number of pieces played so far, including	 * the current piece being played.	 * 	 * @return Number of pieces played	 */	public int getCount() {		return count;	}	/**	 * Returns the X coordinate of the top-left	 * corner of the current piece being played.	 * 	 * @return x coordinate of current piece	 */	public int getCurrentX() {		return currentX;	}	/**	 * Returns the Y coordinate of the top-left	 * corner of the current piece being played.	 * 	 * @return Y coordinate of current piece	 */	public int getCurrentY() {		return currentY;	}	/**	 * Returns the current piece being played or	 * null if no piece is being played (e.g. when the game is	 * not on).	 * 	 * @return the current piece	 */	public Piece getCurrentPiece() {		return currentPiece;	}	/**	 * Returns a reference to the Board. Clients should	 * take care not to modify the board state. Or, if	 * they modify the board state they should return it	 * back to its original state by calling undo()	 * before returning!	 * 	 * @return A reference to the Board	 */	public Board getBoard() {		return board;	}	/**	 * Returns the height of the board not including the topSpace.	 * 	 * @return Height in block of the tetris board, not	 * including the "top space" where new pieces enter.	 */	public int getHeight() {		return HEIGHT;	}	/**	 * Returns the width of the board in blocks.	 * 	 * @return Width in blocks of the tetris board.	 */	public int getWidth() {		return WIDTH;	}	/**	 * Returns the number number of rows at the top where new	 * pieces are entered.	 * 	 * @return The number of rows where new	 * pieces get played.	 */	public int getTopSpace() {		return TOP_SPACE;	}	/**	 * Returns whether the game is in the "on" state. If a game is	 * not in the "on" state it will not accept any moves.	 * 	 * @return Whether the game is in "on" state. 	 */	public boolean isGameOn() {		return gameOn;	}	/**	 * Turns on the "test" feature. This will cause the Game	 * to play the same sequence of pieces every time and limit 	 * gameplay to TEST_LIMIT # pieces.	 */	public void enableTestMode() {		testMode = true;	}	/**	 * Is the game's running in test mode?	 * 	 * @return true if the game is in test mode false otherwise.	 */	public boolean isTestMode() {		return testMode;	}		///////// For linking the GameState to a GUI ///////	/**	 * Installs a listener. All installed listeners will be notified (by calling one	 * of its method) whenever the state of the game is updated. 	 * 	 * @param l The listener to be added.	 */	public void addListener(GameListener l) {		for (int i = 0; i < listener.length; i++) {			if (listener[i] == null) {				listener[i] = l;				return;			}		}		throw new Error("Can't add more listeners (no more space).");	}		////// Some helper methods that get called when game state changes /////	private void notifyBoardChanged() {		for (int i = 0; i < listener.length; i++)			if (listener[i] != null)				listener[i].boardChanged();	}	private void notifyCountChanged() {		for (int i = 0; i < listener.length; i++)			if (listener[i] != null)				listener[i].countChanged();	}	private void notifyGameOnChanged() {		for (int i = 0; i < listener.length; i++)			if (listener[i] != null)				listener[i].gameOnChanged();	}}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -