📄 checkers.txt
字号:
}
// 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.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 < 8 + offset; i++) {
retVal = (new Integer(retVal << 1)).byteValue();
retVal += (new Integer(eight[i])).byteValue();
}
return(retVal);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -