|
Checkers游戏源码
发表日期:2007-12-23
|
import Java.util.Vector; import java.io.*; import javax.microedition.io.*; import javax.microedition.rms.*;
import javax.microedition.midlet.*; import javax.microedition.lcdui.*;
/** * This is the main class of the checkers game. * * @author Carol Hamer */ public class Checkers extends MIDlet implements CommandListener {
//----------------------------------------------------- // game object fields
/** * The canvas that the checkerboard is drawn on. */ private CheckersCanvas myCanvas;
/** * The class that makes the http connection. */ private Communicator myCommunicator;
//----------------------------------------------------- // command fields
/** * The button to exit the game. */ private Command myExitCommand = new Command("Exit", Command.EXIT, 99);
//----------------------------------------------------- // initialization and game state changes
/** * Initialize the canvas and the commands. */ public Checkers() { try { //create the canvas and set up the commands: myCanvas = new CheckersCanvas(Display.getDisplay(this)); myCanvas.addCommand(myExitCommand); myCanvas.setCommandListener(this); CheckersGame game = myCanvas.getGame(); myCommunicator = new Communicator(this, myCanvas, game); game.setCommunicator(myCommunicator); } catch(Exception e) { // if there's an error during creation, display it as an alert. errorMsg(e); } }
//---------------------------------------------------------------- // implementation of MIDlet // these methods may be called by the application management // software at any time, so we always check fields for null // before calling methods on them.
/** * Start the application. */ public void startApp() throws MIDletStateChangeException { // tell the canvas to set up the game data and paint the // checkerboard. if(myCanvas != null) { myCanvas.start(); } // tell the communicator to start its thread and make a // connection. if(myCommunicator != null) { myCommunicator.start(); } } /** * Throw out the garbage. */ public void destroyApp(boolean unconditional) throws MIDletStateChangeException { // tell the communicator to send the end game // message to the other player and then disconnect: if(myCommunicator != null) { myCommunicator.endGame(); } // throw the larger game objects in the garbage: myCommunicator = null; myCanvas = null; System.gc(); }
/** * Pause the game. * This method merely ends the game because this * version of the Checkers game does not support * re-entering a game that is in play. A possible * improvement to the game would be to allow * a player to diconeect and leave a game and then * later return to it, using some sort of session * token to find the correct game in progress on * the server side. */ public void pauseApp() { try { destroyApp(false); notifyDestroyed(); } catch (MIDletStateChangeException ex) { } }
//---------------------------------------------------------------- // implementation of CommandListener
/* * Respond to a command issued on the Canvas. */ public void commandAction(Command c, Displayable s) { if(c == myExitCommand) { try { destroyApp(false); notifyDestroyed(); } catch (MIDletStateChangeException ex) { } } } //------------------------------------------------------- // error methods
/** * Converts an exception to a message and displays * the message.. */ void errorMsg(Exception e) { e.printStackTrace(); if(e.getMessage() == null) { errorMsg(e.getClass().getName()); } else { errorMsg(e.getMessage()); } }
/** * Displays an error message alert if something goes wrong. */ void errorMsg(String msg) { Alert errorAlert = new Alert("error", msg, null, AlertType.ERROR); errorAlert.setCommandListener(this); errorAlert.setTimeout(Alert.FOREVER); Display.getDisplay(this).setCurrent(errorAlert); }
}
/** * This class is the display of the game. * * @author Carol Hamer */ class CheckersCanvas extends Canvas {
//--------------------------------------------------------- // static fields
/** * color constant */ public static final int BLACK = 0;
/** * color constant */ public static final int WHITE = 0xffffff;
/** * color constant. * (not quite bright red) */ public static final int RED = 0xf96868;
/** * color constant */ public static final int GREY = 0xc6c6c6;
/** * color constant */ public static final int LT_GREY = 0xe5e3e3;
/** * how many rows and columns the display is divided into. */ public static final int GRID_WIDTH = 8;
//--------------------------------------------------------- // instance fields
/** * The black crown to draw on the red pieces.. */ private Image myBlackCrown;
/** * The red crown to draw on the black pieces.. */ private Image myWhiteCrown;
/** * a handle to the display. */ private Display myDisplay;
/** * a handle to the object that stores the game logic * and game data. */ private CheckersGame myGame;
/** * checkers dimension: the width of the squares of the checkerboard. */ private int mySquareSize;
/** * checkers dimension: the minimum width possible for the * checkerboard squares. */ private int myMinSquareSize = 15;
/** * whether or not we're waiting for another player to join * the game. */ private boolean myIsWaiting;
//----------------------------------------------------- // gets / sets
/** * @return a handle to the class that holds the logic of the * checkers game. */ CheckersGame getGame() { return(myGame); }
/** * Display a screen to inform the player that we're * waiting for another player. */ void setWaitScreen(boolean wait) { myIsWaiting = wait; }
//----------------------------------------------------- // initialization and game state changes
/** * ConstrUCtor performs size calculations. * @throws Exception if the display size is too * small to make a checkers. */ CheckersCanvas(Display d) throws Exception { myDisplay = d; myGame = new CheckersGame(); // a few calculations to make the right checkerboard // for the current display. int width = getWidth(); int height = getHeight(); // get the smaller dimension fo the two possible // screen dimensions in order to determine how // big to make the checkerboard. int screenSquareWidth = height; if(width < height) { screenSquareWidth = width; } mySquareSize = screenSquareWidth / GRID_WIDTH; // if the display is too small to make a reasonable checkerboard, // then we throw an Exception if(mySquareSize < myMinSquareSize) { throw(new Exception("Display too small")); } // initialize the crown images: myBlackCrown = Image.createImage("/blackCrown.png"); myWhiteCrown = Image.createImage("/whiteCrown.png"); }
/** * This is called as soon as the application begins. */ void start() { myDisplay.setCurrent(this); // prepare the game data for the first move: myGame.start(); }
//------------------------------------------------------- // graphics methods
/** * Repaint the checkerboard.. */ protected void paint(Graphics g) { int width = getWidth(); int height = getHeight(); g.setColor(WHITE); // clear the board (including the region around // the board, which can get menu stuff and other // garbage painted onto it...) g.fillRect(0, 0, width, height); // If we need to wait for another player to join the // game before we can start, this displays the appropriate // message: if(myIsWaiting) { // perform some calculations to place the text correctly: Font font = g.getFont(); int fontHeight = font.getHeight(); int fontWidth = font.stringWidth("waiting for another player"); g.setColor(WHITE); g.fillRect((width - fontWidth)/2, (height - fontHeight)/2, fontWidth + 2, fontHeight); // write in black g.setColor(BLACK); g.setFont(font); g.drawString("waiting for another player", (width - fontWidth)/2, (height - fontHeight)/2, g.TOPg.LEFT); return; } // now draw the checkerboard: // first the dark squares: byte offset = 0; for(byte i = 0; i < 4; i++) { for(byte j = 0; j < 8; j++) { // the offset is used to handle the fact that in every // other row the dark squares are shifted one place // to the right. if(j % 2 != 0) { offset = 1; } else { offset = 0; } // now if this is a selected square, we draw it lighter: if(myGame.isSelected(i, j)) { g.setColor(LT_GREY); g.fillRect((2*i + offset)*mySquareSize, j*mySquareSize, mySquareSize, mySquareSize); } else { // if it's not selected, we draw it dark grey: g.setColor(GREY); g.fillRect((2*i + offset)*mySquareSize, j*mySquareSize, mySquareSize, mySquareSize); } // now put the pieces in their places: g.setColor(RED); int piece = myGame.getPiece(i, j); int circleOffset = 2; int circleSize = mySquareSize - 2*circleOffset; if(piece < 0) { // color the piece in black g.setColor(BLACK); g.fillRoundRect((2*i + offset)*mySquareSize + circleOffset, j*mySquareSize + circleOffset, circleSize, circleSize, circleSize, circleSize); // if the player is a king, draw a crown on: if(piece < -1) { g.drawImage(myWhiteCrown, (2*i + offset)*mySquareSize + mySquareSize/2, j*mySquareSize + 1 + mySquareSize/2, Graphics.VCENTERGraphics.HCENTER); } } else if(piece > 0) { // color the piece in red g.fillRoundRect((2*i + offset)*mySquareSize + circleOffset, j*mySquareSize + circleOffset, circleSize, circleSize, circleSize, circleSize); // if the player is a king, draw a crown on: if(piece > 1) { g.drawImage(myBlackCrown, (2*i + offset)*mySquareSize + mySquareSize/2, j*mySquareSize + 1 + mySquareSize/2, Graphics.VCENTERGraphics.HCENTER); } } } } // now the blank squares: // actually, this part is probably not necessary... g.setColor(WHITE); for(int i = 0; i < 4; i++) { for(int j = 0; j < 8; j++) { if(j % 2 == 0) { offset = 1; } else { offset = 0; } g.fillRect((2*i + offset)*mySquareSize, j*mySquareSize, mySquareSize, mySquareSize); } } // if the player has reached the end of the game, // we display the end message. if(myGame.getGameOver()) { // perform some calculations to place the text correctly: Font font = g.getFont(); int fontHeight = font.getHeight(); int fontWidth = font.stringWidth("Game Over"); g.setColor(WHITE); g.fillRect((width - fontWidth)/2, (height - fontHeight)/2, fontWidth + 2, fontHeight); // write in black g.setColor(BLACK); g.setFont(font); g.drawString("Game Over", (width - fontWidth)/2, (height - fontHeight)/2, g.TOPg.LEFT); } }
//------------------------------------------------------- // handle keystrokes
/** * Move the player. */ public void keyPressed(int keyCode) { if(myGame.isMyTurn()) { int action = getGameAction(keyCode); switch (action) { case LEFT: myGame.leFTPressed(); break; case RIGHT: myGame.rightPressed(); break; case UP: myGame.upPressed(); break; case DOWN: myGame.deselect(); break; } repaint(); serviceRepaints(); } }
}
/** * This class contacts a remote server in order to * play a game of checkers against an opponent.. * * @author Carol Hamer */ class Communicator extends Thread {
//-------------------------------------------------------- // static fields
/** * This is the URL to contact. * IMPORTANT: before compiling, the following URL * must be changed to the correct URL of the * machine running the server code. */ public static final String SERVER_URL = "socket://malbec:8007";
/** * The int to signal that the game is to begin. */ public static final byte START_GAME_FLAG = -4;
/** * The byte to signal that the game is to end. */ public static final byte END_GAME_FLAG = -3;
/** * The byte to signal the end of a turn. */ public static final byte END_TURN_FLAG = -2;
//-------------------------------------------------------- // game instance fields
/** * The MIDlet subclass, used to set the Display * in the case where an error message needs to be sent.. */ private Checkers myCheckers;
/** * The Canvas subclass, used to set the Display * in the case where an error message needs to be sent.. */ private CheckersCanvas myCanvas;
/** * The game logic class that we send the opponent's * moves to.. */ private CheckersGame myGame;
/** * Whether or not the MIDlet class has requested the * game to end. */ private boolean myShouldStop;
//-------------------------------------------------------- // data exchange instance fields
/** * The data from the local player that is to * be sent to the opponent. */ private byte[] myMove;
/** * Whether or not the current turn is done and * should be sent. */ private boolean myTurnIsDone = true;
//-------------------------------------------------------- // initialization
/** * Constructor is used only when the program wants * to spawn a data-fetching thread, not for merely * reading local data with static methods. */ Communicator(Checkers checkers, CheckersCanvas canvas, CheckersGame game) { myCheckers = checkers; myCanvas = canvas; myGame = game; }
//-------------------------------------------------------- // methods called by CheckersGame to send move // information to the opponent.
/** * Stop the game entirely. Notify the servlet that * the user is exiting the game. */ synchronized void endGame() { myShouldStop = true; if(myGame != null) { myGame.setGameOver(); } notify(); }
/** * This is called when the player moves a piece. */ synchronized void move(byte sourceX, byte sourceY, byte destinationX, byte destinationY) { myMove = new byte[4]; myMove[0] = sourceX; myMove[1] = sourceY; myMove[2] = destinationX; myMove[3] = destinationY; myTurnIsDone = false; notify(); }
/** * This is called when the local player's turn is over. */ synchronized void endTurn() { myTurnIsDone = true; notify(); }
//-------------------------------------------------------- // main communication method
/** * Makes a connection to the server and sends and receives * information about moves. */ public void run() { DataInputStream dis = null; DataOutputStream dos = null; SocketConnection conn = null; byte[] fourBytes = new byte[4]; try { // tell the user that we're waiting for the other player to join: myCanvas.setWaitScreen(true); myCanvas.repaint(); myCanvas.serviceRepaints(); // now make the connection: conn = (SocketConnection)Connector.open(SERVER_URL); conn.setSocketOption(SocketConnection.KEEPALIVE, 1); dos = conn.openDataOutputStream(); dis = conn.openDataInputStream(); // we read four bytes to make sure the connection works... dis.readFully(fourBytes); if(fourBytes[0] != START_GAME_FLAG) { throw(new Exception("server-side error")); } // On this line it will block waiting for another // player to join the game or make a move: dis.readFully(fourBytes); // if the server sends the start game flag again, // that means that we start with the local player's turn. // Otherwise, we read the other player's first move from the // stream: if(fourBytes[0] != START_GAME_FLAG) { // verify that the other player sent a move // and not just a message ending the game... if(fourBytes[0] == END_GAME_FLAG) { throw(new Exception("other player quit")); } // we move the opponent on the local screen. // then we read from the opponent again, // in case there's a double-jump: while(fourBytes[0] != END_TURN_FLAG) { myGame.moveOpponent(fourBytes); dis.readFully(fourBytes); } } // now signal the local game that the opponent is done // so the board must be updated and the local player // prompted to make a move: myGame.endOpponentTurn(); myCanvas.setWaitScreen(false); myCanvas.repaint(); myCanvas.serviceRepaints(); // begin main game loop: while(! myShouldStop) { // now it's the local player's turn. // wait for the player to move a piece: synchronized(this) { wait(); } // after every wait, we check if the game // ended while we were waiting... if(myShouldStop) { break; } while(! myTurnIsDone) { // send the current move: if(myMove != null) { dos.write(myMove, 0, myMove.length); myMove = null; } // If the player can continue the move with a double // jump, we wait for the player to do it: synchronized(this) { // make sure the turn isn't done before we start waiting // (the end turn notify might accidentally be called // before we start waiting...) if(! myTurnIsDone) { wait(); } } } // after every wait, we check if the game // ended while we were waiting... if(myShouldStop) { break; } // now we tell the other player the this player's // turn is over: fourBytes[0] = END_TURN_FLAG; dos.write(fourBytes, 0, fourBytes.length); // now that we've sent the move, we wait for a response: dis.readFully(fourBytes); while((fourBytes[0] != END_TURN_FLAG) && (fourBytes[0] != END_GAME_FLAG) && (!myShouldStop)) { // we move the opponent on the local screen. // then we read from the opponent again, // in case there's a double-jump: myGame.moveOpponent(fourBytes); dis.readFully(fourBytes); } // if the other player has left the game, we tell the // local user that the game is over. if((fourBytes[0] == END_GAME_FLAG) (myShouldStop)) { endGame(); break; } myGame.endOpponentTurn(); myCanvas.repaint(); myCanvas.serviceRepaints(); } // end while loop } catch(Exception e) { // if there's an error, we display its messsage and // end the game. myCheckers.errorMsg(e.getMessage()); } finally { // now we send the information that we're leaving the game, // then close up and delete everything. try { if(dos != null) { dos.write(END_GAME_FLAG); dos.close(); } if(dis != null) { dis.close(); } if(conn != null) { conn.close(); } dis = null; dos = null; conn = null; } catch(Exception e) { // if this throws, at least we made our best effort // to close everything up.... } } // one last paint job to display the "Game Over" myCanvas.repaint(); myCanvas.serviceRepaints(); } }
/** * This class is a set of simple utility functions that * can be used to convert standard data types to bytes * and back again. It is used especially for data storage, * but also for sending and receiving data. * * @author Carol Hamer */ class DataConverter {
//-------------------------------------------------------- // utilities to encode small, compactly-stored small ints.
/** * Encodes a coordinate pair into a byte. * @param coordPair a pair of integers to be compacted into * a single byte for storage. * WARNING: each of the two values MUST BE * between 0 and 15 (inclusive). This method does not * verify the length of the array (which must be 2!) * nor does it verify that the ints are of the right size. */ public static byte encodeCoords(int[] coordPair) { // get the byte value of the first coordinate: byte retVal = (new Integer(coordPair[0])).byteValue(); // move the first coordinate's value up to the top // half of the storage byte: retVal = (new Integer(retVal << 4)).byteValue(); // store the second coordinate in the lower half // of the byte: retVal += (new Integer(coordPair[1])).byteValue(); return(retVal); }
/** * Encodes eight ints into a byte. * This could be easily modified to encode eight booleans. * @param eight an array of at least eight ints. * WARNING: all values must be 0 or 1! This method does * not verify that the values are in the correct range * nor does it verify that the array is long enough. * @param offset the index in the array eight to start * reading data from. (should usually be 0) */ public static byte encode8(int[] eight, int offset) { // get the byte value of the first int: byte retVal = (new Integer(eight[offset])).byteValue(); // progressively move the data up one bit in the // storage byte and then record the next int in // the lowest spot in the storage byte: for(int i = offset + 1; i < 8 + offset; i++) { retVal = (new Integer(retVal << 1)).byteValue(); retVal += (new Integer(eight[i])).byteValue(); } return(retVal); }
//-------------------------------------------------------- // utilities to decode small, compactly-stored small ints.
/** * Turns a byte into a pair of coordinates. */ public static int[] decodeCoords(byte coordByte) { int[] retArray = new int[2]; // we perform a bitwise and with the value 15 // in order to just get the bits of the lower // half of the byte: retArray[1] = coordByte & 15; // To get the bits of the upper half of the // byte, we perform a shift to move them down: retArray[0] = coordByte >> 4; // bytes in Java are generally assumed to be // signed, but in this coding algorithm we // would like to treat them as unsigned: if(retArray[0] < 0) { retArray[0] += 16; } return(retArray); }
/** * Turns a byte into eight ints. */ public static int[] decode8(byte data) { int[] retArray = new int[8]; // The flag allows us to look at each bit individually // to determine if it is 1 or 0. The number 128 // corresponds to the highest bit of a byte, so we // start with that one. int flag = 128; // We use a loop that checks // the data bit by bit by performing a bitwise // and (&) between the data byte and a flag: for(int i = 0; i < 8; i++) { if((flag & data) != 0) { retArray[i] = 1; } else { retArray[i] = 0; } // move the flag down one bit so that we can // check the next bit of data on the next pass // through the loop: flag = flag >> 1; } return(retArray); }
//-------------------------------------------------------- // standard integer interpretation
/** * Uses an input stream to convert an array of bytes to an int. */ public static int parseInt(byte[] data) throws IOException { DataInputStream stream = new DataInputStream(new ByteArrayInputStream(data)); int retVal = stream.readInt(); stream.close(); return(retVal); }
/** * Uses an output stream to convert an int to four bytes. */ public static byte[] intToFourBytes(int i) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(4); DataOutputStream dos = new DataOutputStream(baos); dos.writeInt(i); baos.close(); dos.close(); byte[] retArray = baos.toByteArray(); return(retArray); }
//-------------------------------------------------------- // integer interpretation illustrated
/** * Java appears to treat a byte as being signed when * returning it as an int--this function converts from * the signed value to the corresponding unsigned value. * This method is used by nostreamParseInt. */ public static int unsign(int signed) { int retVal = signed; if(retVal < 0) { retVal += 256; } return(retVal); }
/** * Takes an array of bytes and returns an int. * This version will return the same value as the * method parseInt above. This version is included * in order to illustrate how Java encodes int values * in terms of bytes. * @param data an array of 1, 2, or 4 bytes. */ public static int nostreamParseInt(byte[] data) { // byte 0 is the high byte which is assumed // to be signed. As we add the lower bytes // one by one, we unsign them because because // a single byte alone is interpreted as signed, // but in an int only the top byte should be signed. // (note that the high byte is the first one in the array) int retVal = data[0]; for(int i = 1; i < data.length; i++) { retVal = retVal << 8; retVal += unsign(data[i]); } return(retVal); }
/** * Takes an arbitrary int and returns * an array of four bytes. * This version will return the same byte array * as the method intToFourBytes above. This version * is included in order to illustrate how Java encodes * int values in terms of bytes. */ public static byte[] nostreamIntToFourBytes(int i) { byte[] fourBytes = new byte[4]; // when you take the byte value of an int, it // only gives you the lowest byte. So we // get all four bytes by taking the lowest // byte four times and moving the whole int // down by one byte between each one. // (note that the high byte is the first one in the array) fourBytes[3] = (new Integer(i)).byteValue(); i = i >> 8; fourBytes[2] = (new Integer(i)).byteValue(); i = i >> 8; fourBytes[1] = (new Integer(i)).byteValue(); i = i >> 8; fourBytes[0] = (new Integer(i)).byteValue(); return(fourBytes); }
/** * Takes an int between -32768 and 32767 and returns * an array of two bytes. This does not verify that * the argument is of the right size. If the absolute * value of i is too high, it will not be encoded * correctly. */ public static byte[] nostreamIntToTwoBytes(int i) { byte[] twoBytes = new byte[2]; // when you take the byte value of an int, it // only gives you the lowest byte. So we // get the lower two bytes by taking the lowest // byte twice and moving the whole int // down by one byte between each one. twoBytes[1] = (new Integer(i)).byteValue(); i = i >> 8; twoBytes[0] = (new Integer(i)).byteValue(); return(twoBytes); }
}
/** * This class takes care of the underlying logic and data of * the checkers game being played. That includes where * all of the pieces are on the board and where it is okay * for them to move to. * * @author Carol Hamer */ class CheckersGame {
//------------------------------------------------------- // static fields
/** * The length of the checkerboard in the x-direction. */ public static final byte X_LENGTH = 4;
/** * The length of the checkerboard in the y-direction. */ public static final byte Y_LENGTH = 8;
//------------------------------------------------------- // instance fields
/** * a handle to the communications class that exchanges * data with the server. */ private Communicator myCommunicator;
/** * This array represents the black squares of the * checkerboard. The two dimensions of the array * represent the two dimensions of the checkerboard. * The value represents what type of piece is on * the square. * 0 = empty * 1 = local player's piece * 2 = local player's king * -1 = remote player's piece * -2 = remote player's king */ private byte[][] myGrid;
/** * If the user has currently selected a piece to move, * this is its X grid coordinate. (-1 if none selected) */ private byte mySelectedX = -1;
/** * If the user has currently selected a piece to move, * this is its Y grid coordinate.(-1 if none selected) */ private byte mySelectedY = -1;
/** * If the user has currently selected a possible * destination square for a move, this is its X coordinate.. * (-1 if none selected) */ private byte myDestinationX = -1;
/** * If the user has currently selected a possible * destination square for a move, this is its Y coordinate.. * (-1 if none selected) */ private byte myDestinationY = -1;
/** * This Vector contains the coordinates of all of the * squares that the player could currently move to. */ private Vector myPossibleMoves = new Vector(4);
/** * Whether or not the currently displayed checkers has * been completed. */ private boolean myGameOver = false;
/** * Whether or not it is currently this player's turn. */ private boolean myTurn = false;
/** * This is true if the player has just jumped and can * jump again. */ private boolean myIsJumping = false;
//------------------------------------------------------- // get/set data /** * get the piece on the given grid square. */ byte getPiece(byte x, byte y) { return(myGrid[x][y]); }
/** * This is callsed by CheckersCanvas to determine if * the square is currently selected (as containing * a piece to move or a destination square). */ boolean isSelected(byte x, byte y) { boolean retVal = false; if((x == mySelectedX) && (y == mySelectedY)) { retVal = true; } else if((x == myDestinationX) && (y == myDestinationY)) { retVal = true; } return(retVal); }
/** * This tells whether or not the keystrokes should currently * be taken into account. */ boolean isMyTurn() { boolean retVal = false; if((!myGameOver) && ((myTurn) (myIsJumping))) { retVal = true; } return(retVal); }
/** * This tells whether or not the game has ended. */ boolean getGameOver() { boolean retVal = false; if(myGameOver) { retVal = true; } return(retVal); }
/** * tell the CheckersGame that the other player has ended the game. */ void setGameOver() { myGameOver = true; }
/** * set the communicator object. */ void setCommunicator(Communicator comm) { myCommunicator = comm; }
//------------------------------------------------------- // initialization
/** * Constructor puts the pieces in their initial positions: */ CheckersGame() { myGrid = new byte[X_LENGTH][]; for(byte i = 0; i < myGrid.length; i++) { myGrid[i] = new byte[Y_LENGTH]; for(byte j = 0; j < myGrid[i].length; j++) { if(j < 3) { // fill the top of the board with remote players myGrid[i][j] = -1; } else if(j > 4) { // fill the bottom of the board with local players myGrid[i][j] = 1; } } } }
/** * This is called just before the player makes the * first move. */ void start() { mySelectedX = 0; mySelectedY = 5; myTurn = true; getMoves(mySelectedX, mySelectedY, myPossibleMoves, false); }
//------------------------------------------------------- // move the opponent // to be called by Communicator
/** * This is called when the opponent wants to move * its piece. * @param moveData an array of four bytes: * moveData[0] = opponent's initial X coordinate * moveData[1] = opponent's initial Y coordinate * moveData[2] = opponent's destination X coordinate * moveData[3] = opponent's destination Y coordinate */ void moveOpponent(byte[] moveData) { // since both players appear on their own screens // as the red side (bottom of the screen), we need // to invert the opponent's move: moveData[0] = (new Integer(X_LENGTH - moveData[0] - 1)).byteValue(); moveData[2] = (new Integer(X_LENGTH - moveData[2] - 1)).byteValue(); moveData[1] = (new Integer(Y_LENGTH - moveData[1] - 1)).byteValue(); moveData[3] = (new Integer(Y_LENGTH - moveData[3] - 1)).byteValue(); myGrid[moveData[2]][moveData[3]] = myGrid[moveData[0]][moveData[1]]; myGrid[moveData[0]][moveData[1]] = 0; // deal with an opponent's jump: if((moveData[1] - moveData[3] > 1) (moveData[3] - moveData[1] > 1)) { int jumpedY = (moveData[1] + moveData[3])/2; int jumpedX = moveData[0]; int parity = moveData[1] % 2; if((parity > 0) && (moveData[2] > moveData[0])) { jumpedX++; } else if((parity == 0) && (moveData[0] > moveData[2])) { jumpedX--; } myGrid[jumpedX][jumpedY] = 0; } // if the opponent reaches the far side, // make him a king: if(moveData[3] == Y_LENGTH - 1) { myGrid[moveData[2]][moveData[3]] = -2; } }
/** * This is called when the opponent's turn is over. * Note that the turn doesn't automatically end after * the opponent moves because the opponent may make * a double or triple jump. */ void endOpponentTurn() { myTurn = true; // Now begin the local player's turn: // First select the first local piece that can be // moved. (rightPressed will select an appropriate // piece or end the game if the local player has // no possible moves to make) mySelectedX = 0; mySelectedY = 0; myDestinationX = -1; myDestinationY = -1; rightPressed(); // the local player's thread has been waiting // for the opponent's turn to end. synchronized(this) { notify(); } }
//------------------------------------------------------- // handle keystrokes // to be called by CheckersCanvas
/** * if the left button is pressed, this method takes * the correct course of action depending on the situation. */ void leftPressed() { // in the first case the user has not yet selected a // piece to move: if(myDestinationX == -1) { // find the next possible piece (to the left) // that can move: selectPrevious(); // if selectPrevious fails to fill myPossibleMoves, that // means that the local player cannot move, so the game // is over: if(myPossibleMoves.size() == 0) { myCommunicator.endGame(); } } else { // if the user has already selected a piece to move, // we give the options of where the piece can move to: for(byte i = 0; i < myPossibleMoves.size(); i++) { byte[] coordinates = (byte[])myPossibleMoves.elementAt(i); if((coordinates[0] == myDestinationX) && (coordinates[1] == myDestinationY)) { i++; i = (new Integer(i % myPossibleMoves.size())).byteValue(); coordinates = (byte[])myPossibleMoves.elementAt(i); myDestinationX = coordinates[0]; myDestinationY = coordinates[1]; break; } } } }
/** * if the left button is pressed, this method takes * the correct course of action depending on the situation. */ void rightPressed() { // in the first case the user has not yet selected a // piece to move: if(myDestinationX == -1) { // find the next possible piece that can // move: selectNext(); // if selectNext fails to fill myPossibleMoves, that // means that the local player cannot move, so the game // is over: if(myPossibleMoves.size() == 0) { myCommunicator.endGame(); } } else { // if the user has already selected a piece to move, // we give the options of where the piece can move to: for(byte i = 0; i < myPossibleMoves.size(); i++) { byte[] coordinates = (byte[])myPossibleMoves.elementAt(i); if((coordinates[0] == myDestinationX) && (coordinates[1] == myDestinationY)) { i++; i = (new Integer(i % myPossibleMoves.size())).byteValue(); coordinates = (byte[])myPossibleMoves.elementAt(i); myDestinationX = coordinates[0]; myDestinationY = coordinates[1]; break; } } } }
/** * If no piece is selected, we select one. If a piece * is selected, we move it. */ void upPressed() { // in the first case the user has not yet selected a // piece to move: if(myDestinationX == -1) { fixSelection(); } else { // if the source square and destination square // have been chosen, we move the piece: move(); } }
/** * If the user decided not to move the selected piece * (and instead wants to select again), this undoes * the selection. This corresponds to pressing the * DOWN key. */ void deselect() { // if the player has just completed a jump and // could possibly jump again but decides not to // (i.e. deselects), then the turn ends: if(myIsJumping) { mySelectedX = -1; mySelectedY = -1; myDestinationX = -1; myDestinationY = -1; myIsJumping = false; myTurn = false; myCommunicator.endTurn(); } else { // setting the destination coordinates to -1 // is the signal that the the choice of which // piece to move can be modified: myDestinationX = -1; myDestinationY = -1; } }
//------------------------------------------------------- // internal square selection methods
/** * When the player has decided that the currently selected * square contains the piece he really wants to move, this * is called. This method switches to the mode where * the player selects the destination square of the move. */ private void fixSelection() { byte[] destination = (byte[])myPossibleMoves.elementAt(0); // setting the destination coordinates to valid // coordinates is the signal that the user is done // selecting the piece to move and now is choosing // the destination square: myDestinationX = destination[0]; myDestinationY = destination[1]; }
/** * This method starts from the currently selected square * and finds the next square that contains a piece that * the player can move. */ private void selectNext() { // Test the squares one by one (starting from the // currently selected square) until we find a square // that contains one of the local player's pieces // that can move: byte testX = mySelectedX; byte testY = mySelectedY; while(true) { testX++; if(testX >= X_LENGTH) { testX = 0; testY++; testY = (new Integer(testY % Y_LENGTH)).byteValue(); } getMoves(testX, testY, myPossibleMoves, false); if((myPossibleMoves.size() != 0) ((testX == mySelectedX) && (testY == mySelectedY))) { mySelectedX = testX; mySelectedY = testY; break; } } }
/** * This method starts from the currently selected square * and finds the next square (to the left) that contains * a piece that the player can move. */ private void selectPrevious() { // Test the squares one by one (starting from the // currently selected square) until we find a square // that contains one of the local player's pieces // that can move: byte testX = mySelectedX; byte testY = mySelectedY; while(true) { testX--; if(testX < 0) { testX += X_LENGTH; testY--; if(testY < 0) { testY += Y_LENGTH; } } getMoves(testX, testY, myPossibleMoves, false); if((myPossibleMoves.size() != 0) ((testX == mySelectedX) && (testY == mySelectedY))) { mySelectedX = testX; mySelectedY = testY; break; } } }
//------------------------------------------------------- // internal utilities
/** * Once the user has selected the move to make, this * updates the data accordingly. */ private void move() { // the piece that was on the source square is // now on the destination square: myGrid[myDestinationX][myDestinationY] = myGrid[mySelectedX][mySelectedY]; // the source square is emptied: myGrid[mySelectedX][mySelectedY] = 0; if(myDestinationY == 0) { myGrid[myDestinationX][myDestinationY] = 2; } // tell the communicator to inform the other player // of this move: myCommunicator.move(mySelectedX, mySelectedY, myDestinationX, myDestinationY); // deal with the special rules for jumps:: if((mySelectedY - myDestinationY > 1) (myDestinationY - mySelectedY > 1)) { int jumpedY = (mySelectedY + myDestinationY)/2; int jumpedX = mySelectedX; int parity = mySelectedY % 2; // the coordinates of the jumped square depend on // what row we're in: if((parity > 0) && (myDestinationX > mySelectedX)) { jumpedX++; } else if((parity == 0) && (mySelectedX > myDestinationX)) { jumpedX--; } // remove the piece that was jumped over: myGrid[jumpedX][jumpedY] = 0; // now get ready to jump again if possible: mySelectedX = myDestinationX; mySelectedY = myDestinationY; myDestinationX = -1; myDestinationY = -1; // see if another jump is possible. // The "true" argument tells the program to return // only jumps because the player can go again ONLY // if there's a jump: getMoves(mySelectedX, mySelectedY, myPossibleMoves, true); // if there's another jump possible with the same piece, // allow the player to continue jumping: if(myPossibleMoves.size() != 0) { myIsJumping = true; byte[] landing = (byte[])myPossibleMoves.elementAt(0); myDestinationX = landing[0]; myDestinationY = landing[1]; } else { myTurn = false; myCommunicator.endTurn(); } } else { // since it's not a jump, we just end the turn // by deselecting everything. mySelectedX = -1; mySelectedY = -1; myDestinationX = -1; myDestinationY = -1; myPossibleMoves.removeAllElements(); myTurn = false; // tell the other player we're done: myCommunicator.endTurn(); } } /** * Given a square on the grid, get the coordinates * of one of the adjoining (diagonal) squares. * 0 = top left * 1 = top right * 2 = bottom left * 3 = bottom right. * @return the coordinates or null if the desired corner * is off the board. */ private byte[] getCornerCoordinates(byte x, byte y, byte corner) { byte[] retArray = null; if(corner < 2) { y--; } else { y++; } // Where the corner is on the grid depends on // whether this is an odd row or an even row: if((corner % 2 == 0) && (y % 2 != 0)) { x--; } else if((corner % 2 != 0) && (y % 2 == 0)) { x++; } try { if(myGrid[x][y] > -15) { // we don't really care about the value, this // if statement is just there to get it to // throw if the coordinates aren't on the board. retArray = new byte[2]; retArray[0] = x; retArray[1] = y; } } catch(ArrayIndexOutOfBoundsException e) { // this throws if the coordinates do not correspond // to a square on the board. It's not a problem, // so we do nothing--we just return null instead // of returning coordinates since no valid // coordinates correspond to the desired corner. } return(retArray); } /** * Determines where the piece in the given * grid location can move. Clears the Vector * and fills it with the locations that * the piece can move to. * @param jumpsOnly if we should return only moves that * are jumps. */ private void getMoves(byte x, byte y, Vector toFill, boolean jumpsOnly) { toFill.removeAllElements(); // if the square does not contain one of the local player's // pieces, then there are no corresponding moves and we just // return an empty vector. if(myGrid[x][y] <= 0) { return; } // check each of the four corners to see if the // piece can move there: for(byte i = 0; i < 4; i++) { byte[] coordinates = getCornerCoordinates(x, y, i); // if the coordinate array is null, then the corresponding // corner is off the board and we don't deal with it. // The later two conditions in the following if statement // ensure that either the move is a forward move or the // current piece is a king: if((coordinates != null) && ((myGrid[x][y] > 1) (i < 2))) { // if the corner is empty (and we're not looking // for just jumps), then this is a possible move // so we add it to the vector of moves: if((myGrid[coordinates[0]][coordinates[1]] == 0) && (! jumpsOnly)) { toFill.addElement(coordinates); // if the space is occupied by an opponent, see if we can jump it: } else if(myGrid[coordinates[0]][coordinates[1]] < 0) { byte[] jumpLanding = getCornerCoordinates(coordinates[0], coordinates[1], i); // if the space on the far side of the opponent's piece // is on the board and is unoccupied, then a jump // is possible, so we add it to the vector of moves: if((jumpLanding != null) && (myGrid[jumpLanding[0]][jumpLanding[1]] == 0)) { toFill.addElement(jumpLanding); } } } } // end for loop } }
(出处:)
|
|
上一篇:查询手机支持的多媒体类型和协议
人气:913
下一篇:GameAction的使用范例
人气:692 |
浏览全部J2EE/J2ME的内容
Dreamweaver插件下载 网页广告代码 祝你圣诞节快乐 2009年新年快乐
|
|