分享
 
 
 

Checkers游戏源码

王朝other·作者佚名  2008-05-31
窄屏简体版  字體: |||超大  

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

}

}

(出处:http://www.knowsky.com)

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有