www.pudn.com > j2mewireless_examples.zip > Board.java
/*
* @(#)Board.java 1.4 01/04/04
* Copyright (c) 1999-2001 Sun Microsystems, Inc. All Rights Reserved.
*/
package examples.sokoban;
import java.io.IOException;
/**
* Board knows how to move the pusher and how the pusher moves
* the pieces. It also keeps track of the number of moves
* and pushes and the level.
*/
public class Board {
/*
* Directly accessible fields of the game board;
* do not set outside this class.
*/
int width; // Width of the board
int height; // Height of the board
int nmoves; // number of moves executed
int npushes; // number of pushes executed
private byte[] array; // width * height array of game state
private int pusher; // position of the pusher at index into array
private int packets; // total number of packets
private int stored; // number of packets in Stores
private byte[] moves; // recorded moves for undo
// Move directions
public static final int LEFT = 0;
public static final int RIGHT = 3;
public static final int UP = 1;
public static final int DOWN = 2;
public static final int MOVEPACKET = 4;
// Bit definitions for pieces of each board position
public static final byte GROUND = 0; // If nothing there
public static final byte STORE = 1; // If it is a storage place
public static final byte PACKET = 2; // If it has a packet
public static final byte WALL = 4; // If it is a wall
public static final byte PUSHER = 8; // If the pusher is there
/**
* Creates new Board initialized to a simple puzzle.
*/
public Board() {
moves = new byte[200];
screen0();
}
/*
* Create the hard coded simple game board.
*/
public void screen0() {
width = 9;
height = 7;
array = new byte[width*height];
nmoves = 0;
npushes = 0;
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
byte t = (x == 0 || y == 0 ||
x == width-1 || y == height-1) ? WALL : GROUND;
set(x, y, t);
}
}
packets = 2; stored = 0;
set(2, 2, PACKET);
set(4, 4, PACKET);
set(4, 2, STORE);
set(6, 4, STORE);
pusher = index(1, 1);
}
/**
* Move the pusher in the direction indicated.
* If there is a wall, don't move.
* if there is a packet in that direction, try to move it.
* @param move the direction; one of LEFT, RIGHT, UP, DOWN
*/
public void move(int move) {
int p = pusher + indexOffset(move);
// Handle the simple cases
if ((array[p] & WALL) != 0)
return;
int m = movePacket(p, move);
if (m >= 0) {
/*
* Put the pusher in the new index
* and save the move in case of undo.
*/
pusher = p;
saveMove(m);
}
}
/**
* Move the packet in the direction indicated relative to
* the pusher. If it fits into a store position remember.
* @return -1 if can't be moved or the updated move
* including the packet flag if there was a packet to move.
*/
private int movePacket(int index, int move) {
if ((array[index] & PACKET) == 0)
return move; // no packet to move
int dest = index + indexOffset(move);
if (array[dest] > STORE)
return -1; // can't move packet into next spot.
// Remove packet from current location
array[index] &= ~PACKET;
if ((array[index] & STORE) != 0)
stored--;
// Insert packet into new location
array[dest] |= PACKET;
if ((array[dest] & STORE) != 0)
stored++;
npushes++; // count pushes done
return move + MOVEPACKET;
}
/*
* Save a move, extending the array if necessary.
*/
private void saveMove(int move) {
if (nmoves >= moves.length) {
byte[] n = new byte[moves.length+50];
System.arraycopy(moves, 0, n, 0, moves.length);
moves = n;
}
moves[nmoves++] = (byte)move;
}
/*
* Undo the most recent move
*/
public void undoMove() {
if (nmoves <= 0)
return;
int move = moves[--nmoves];
int rev = (move & 3) ^ 3; // reverse the direction
int back = pusher + indexOffset(rev);
if ((move & MOVEPACKET) != 0) {
npushes--; // "unpush"
movePacket(pusher + indexOffset(move), rev);
}
pusher = back;
}
/**
* Determine if the screen has been solved.
*/
public boolean solved() {
return packets == stored;
}
/*
* Return the pieces at the location.
* @param x location in the board.
* @param y location in the board.
* @return flags indicating what pieces are in this location.
* Bit flags; combinations of WALL, PUSHER, STORE, PACKET.
*/
public byte get(int x, int y) {
int offset = index(x, y);
if (offset == pusher)
return (byte)(array[offset] | PUSHER);
else
return array[offset];
}
private void set(int x, int y, byte value) {
array[index(x, y)] = value;
}
private int index(int x, int y) {
if (x < 0 || x >= width ||
y < 0 || y >= height)
return -1;
return y * width + x;
}
/*
* Compute the offset in the array of the cell relative
* to the current pusher location in the direction of the move.
* Note: the walls around the edge always make a +/- guard band.
* Also, the order of evaluation should never try to get to +/- 2.
*/
private int indexOffset(int move) {
switch (move & 3) {
case LEFT:
return -1;
case RIGHT:
return +1;
case UP:
return -width;
case DOWN:
return +width;
}
return 0;
}
/**
* Read a board from a stream.
* Read it into a fixed size array and then shrink to fit.
*/
public void read(java.io.InputStream is, int lev)
throws IOException
{
final int W = 20;
final int H = 20;
byte[] b = new byte[W*H];
int c, w = 0;
int x = 0, y = 0, xn = 0, yn = 0;
packets = 0;
stored = 0;
nmoves = 0;
npushes = 0;
while ((c = is.read()) != -1) {
switch (c) {
case '\n':
if (x > w) {
w = x;
}
y++;
x = 0;
break;
case '$':
b[y*W + x++] = PACKET;
packets++;
break;
case '#':
b[y*W + x++] = WALL;
break;
case ' ':
b[y*W + x++] = GROUND;
break;
case '.':
b[y*W+ x++] = STORE;
break;
case '+': // player and store in same place
b[y*W + x++] = STORE;
case '@':
xn = x;
yn = y;
x++;
break;
}
}
/*
* Copy the board to an array sized to the \
* width and height of the board.
*/
width = w;
height = y;
array = new byte[width * height];
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
array[y * width + x] = b[y * W + x];
}
}
pusher = index(xn, yn);
}
}