Boardpublic class Board extends Object The class Board knows how the pieces move, handles undo, and
handles reading of screens. |
Fields Summary |
---|
private int | level | private byte[] | array | private byte[] | pathmap | private int | width | private int | height | private int | pusher | private int | packets | private int | stored | private byte[] | moves | private int | nmoves | private int | npushes | public static final int | LEFT | public static final int | RIGHT | public static final int | UP | public static final int | DOWN | public static final int | MOVEPACKET | public static final byte | GROUND | public static final byte | STORE | public static final byte | PACKET | public static final byte | WALL | public static final byte | PUSHER |
Constructors Summary |
---|
public Board()Creates new Board initialized to a simple puzzle. // If the pusher is there
moves = new byte[200];
screen0();
|
Methods Summary |
---|
private int | dx(int dir)Convert a left/right direction into an offset.
if (dir == LEFT)
return -1;
if (dir == RIGHT)
return +1;
return 0;
| private int | dy(int dir)Convert a up/down direction into an offset.
if (dir == UP)
return -1;
if (dir == DOWN)
return +1;
return 0;
| private void | findTarget(int t, byte pathlen)Find the shortest path to the pusher via a fill search algorithm
if (array[t] > STORE)
return; // Either a wall or looped back to player
// Already tried here and this way is longer
if (pathmap[t] <= pathlen)
return;
pathmap[t] = pathlen++; // set path length to this location
if (t == pusher)
return;
findTarget(t - 1, pathlen); // to previous cell
findTarget(t + 1, pathlen); // to next cell
findTarget(t - width, pathlen); // to previous row
findTarget(t + width, pathlen); // to next row
| public byte | get(int x, int y)Return the pieces at the location.
int offset = index(x, y);
if (offset == pusher)
return (byte)(array[offset] | PUSHER);
else
return array[offset];
| public int | getHeight()Get the height of the board.
return height;
| public int | getMoves()Get the number of moves to get this far.
return nmoves;
| public int | getPusherLocation()Get the location of the pusher.
It is returned as an int with the lower 16 bits being
the x index and the upper 16 bits being the y index.
int x = pusher % width;
int y = pusher / width;
return (y << 16) + x;
| public int | getPushes()Get the number of packets pushed around.
return npushes;
| public int | getStored()Get the number of packets stored.
return stored;
| public int | getWidth()Get the width of the game board.
return width;
| private int | index(int x, int y)Compute the index in the array of the x, y location.
if (x < 0 || x >= width ||
y < 0 || y >= height)
return -1;
return y * width + x;
| private int | indexOffset(int move)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.
switch (move & 3) {
case LEFT:
return -1;
case RIGHT:
return +1;
case UP:
return -width;
case DOWN:
return +width;
}
return 0;
| public int | move(int move)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.
int obj = pusher + indexOffset(move);
// Handle the simple cases
if ((array[obj] & WALL) != 0)
return -1;
int m = movePacket(obj, move);
if (m < 0)
return -1; // If can't move packet, done!
pusher = obj; // move the pusher to the new spot
saveMove(m);
return m;
| private int | movePacket(int index, int move)Move the packet in the direction indicated relative to
the pusher. If it fits into a store position remember.
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;
| public void | read(java.io.InputStream is, int l)Read a board from a stream.
Read it into a fixed size array and then shrink to fit.
final int W = 20;
final int H = 20;
byte[] b = new byte[W * H];
// Add resize code later.
int c, w = 0;
int x = 0, y = 0, xn = 0, yn = 0, npackets = 0;
try {
while ((c = is.read()) != -1) {
switch (c) {
case '\n":
if (x > w) {
w = x;
}
y++;
x = 0;
break;
case '$":
b[y * W + x++] = PACKET;
npackets++;
break;
case '#":
b[y * W + x++] = WALL;
break;
case ' ":
b[y * W + x++] = GROUND;
break;
case '.":
b[y * W + x++] = STORE;
break;
case '*":
b[y * W + x] = PACKET;
b[y * W + x++] |= STORE;
npackets++;
stored++;
break;
case '+": // player and store in same place
b[y * W + x++] = STORE;
case '@":
xn = x;
yn = y;
x++;
break;
}
}
} catch (java.io.IOException ex) {
ex.printStackTrace();
}
if (y > 0) {
array = new byte[w * y];
if (y > w) { // Switch x for y while copying
width = y;
height = w;
for (y = 0; y < width; y++) {
for (x = 0; x < w; x++) {
array[index(y, x)] = b[y * W + x];
}
}
pusher = index(yn, xn);
} else {
width = w;
height = y;
array = new byte[width * height];
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
array[index(x, y)] = b[y * W + x];
}
}
pusher = index(xn, yn);
}
stored = 0;
packets = npackets;
level = l;
nmoves = 0;
npushes = 0;
}
| public int | runTo(int x, int y, int max)Move the player to the position (x,y), if possible. Return
the direction it moved if successful, otherwise -1.
The position (x,y) must be empty.
int target = index(x, y);
if (target < 0 || target >= array.length)
return -1;
if (target == pusher)
return -1; // already there
/* Fill the path map */
if (pathmap == null || pathmap.length != array.length) {
pathmap = new byte[array.length];
}
// Fill with unset value.
for (int i = 0; i < pathmap.length; i++)
pathmap[i] = 127;
// flood fill search to find a shortest path to the push point.
findTarget(target, (byte)0);
/*
* if we didn't make it back to the players position,
* there is no valid path to that place.
*/
if (pathmap[pusher] == 127) {
return -1;
} else {
// We made it back, so let's walk the path we just built up
// Save the final move to return
int pathlen = pathmap[pusher];
int pathmin = pathlen - max;
int dir = -1;
for (pathlen--; pathlen >= pathmin; pathlen--) {
if (pathmap[pusher - 1] == pathlen) {
dir = LEFT;
saveMove(dir);
pusher--;
} else if (pathmap[pusher + 1] == pathlen) {
dir = RIGHT;
saveMove(dir);
pusher++;
} else if (pathmap[pusher - width] == pathlen) {
dir = UP;
saveMove(dir);
pusher -= width;
} else if (pathmap[pusher + width] == pathlen) {
dir = DOWN;
saveMove(dir);
pusher += width;
} else {
/*
* if we get here, something is SERIOUSLY wrong,
* so we should abort
*/
throw new RuntimeException("runTo abort");
}
}
return dir;
}
| 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;
| public void | screen0()Create the hard coded simple game board.
width = 9;
height = 7;
array = new byte[width * height];
level = 0;
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);
| private void | set(int x, int y, byte value)Set the value of the location.
array[index(x, y)] = value;
| public boolean | solved()Determine if the screen has been solved.
return packets == stored;
| public int | undoMove()Undo the most recent move
if (nmoves <= 0)
return -1;
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); // move packet
}
pusher = back;
return move;
|
|