您现在的位置: J2ME开发网 >> 参考源码 >> J2ME源码 >> 游戏源码 >> 例子正文
Checkers游戏源码
作者:未知    例子来源:本站原创    点击数:    更新时间:2006-5-30
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 dthrows 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(00, 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.TOP|g.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 % != 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 + + mySquareSize/2
          Graphics.VCENTER|Graphics.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 + + mySquareSize/2
          Graphics.VCENTER|Graphics.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 % == 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.TOP|g.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 < + 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[00) {
      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[i1;