/*
* @(#)Board.java 1.22 02/07/25 @(#)
*
* Copyright (c) 2000-2001 Sun Microsystems, Inc. All rights reserved.
* PROPRIETARY/CONFIDENTIAL
* Use is subject to license terms.
*/
package example.tilepuzzle;
import example.About;
import java.util.Random;
import javax.microedition.lcdui.*;
import javax.microedition.midlet.MIDlet;
public class Board extends Canvas implements CommandListener {
MIDlet midlet;
Display dpy;
Options options;
// this string must be exactly 15 characters long
String letters = "RATEYOURMINDPAL";
Font font;
Piece blankp;
Piece[] all;
Piece[][] grid;
Random rand;
// grid origin in pixels
int gridx;
int gridy;
// grid width and height, in cells
int gridw;
int gridh;
// cell geometry in pixels
int cellw;
int cellh;
int cellyoff;
int cellxoff;
// commands
static final int CMD_ABOUT = 0;
static final int CMD_EXIT = 1;
static final int CMD_OPTIONS = 2;
static final int CMD_RESET = 3;
static final int CMD_START = 4;
static final int CMD_UNLOCK = 5;
static final int CMD_ZLAST = 6; // must be ze last, of course
Command cmd[];
// state variables
static final int INITIALIZED = 0;
static final int PLAYING = 1;
static final int WON = 2;
int gameState;
boolean cheated;
void D(String s) { System.out.println(s); }
class Piece {
String label;
boolean inv;
int serial; // serial number for ordering
int ix, iy; // initial location in grid coordinates
int x, y; // current location in grid coordinates
Piece(String str, int ser, int nx, int ny, boolean v) {
label = str;
serial = ser;
x = ix = nx;
y = iy = ny;
inv = v;
}
void setLocation(int nx, int ny) {
x = nx;
y = ny;
}
boolean isHome() {
return (x == ix) && (y == iy);
}
void goHome() {
setGrid(this, ix, iy);
}
// assumes background is white
void paint(Graphics g) {
int px = x * cellw;
int py = y * cellh;
if (label != null) {
if (inv) {
// black outlined, white square with black writing
g.setColor(0);
g.setFont(font);
g.drawRect(px, py, cellw-2, cellh-2);
g.drawString(label,
px+cellxoff, py+cellyoff,
Graphics.TOP|Graphics.LEFT);
} else {
// black square with white writing
g.setColor(0);
g.fillRect(px, py, cellw-1, cellh-1);
g.setColor(0xFFFFFF);
g.setFont(font);
g.drawString(label,
px+cellxoff, py+cellyoff,
Graphics.TOP|Graphics.LEFT);
}
}
}
}
class BoardCommand extends Command {
int tag;
BoardCommand(String label, int type, int pri, int tag_) {
super(label, type, pri);
tag = tag_;
}
}
void setGrid(Piece p, int x, int y) {
grid[x][y] = p;
p.setLocation(x, y);
}
// swap the piece at sx, sy with the blank piece
// assumes that this is a legal move
void moveBlank(int swapx, int swapy) {
setGrid(grid[swapx][swapy], blankp.x, blankp.y);
setGrid(blankp, swapx, swapy);
}
// swaps the pieces at (x1, y1) and (x2, y2)
// no parity checking is done!
void swap(int x1, int y1, int x2, int y2)
{
Piece t = grid[x1][y1];
setGrid(grid[x2][y2], x1, y1);
setGrid(t, x2, y2);
}
boolean isSolved() {
for (int i = 0; i < gridh; i++) {
for (int j = 0; j < gridw; j++) {
if (! grid[j][i].isHome()) {
return false;
}
}
}
return true;
}
// return a random integer in the range [0..n)
int randRange(int n) {
int r = rand.nextInt() % n;
if (r < 0)
r += n;
return r;
}
// randomize by making random moves
void randomize_by_moving() {
int dx, dy, v;
for (int i = 0; i < 100; i++) {
dx = dy = 0;
v = (rand.nextInt() & 2) - 1; // 1 or -1
if ((rand.nextInt() & 1) == 0)
dx = v;
else
dy = v;
if (blankp.x + dx < 0)
dx = 1;
if (blankp.x + dx == gridw)
dx = -1;
if (blankp.y + dy < 0)
dy = 1;
if (blankp.y + dy == gridh)
dy = -1;
moveBlank(blankp.x + dx, blankp.y + dy);
}
// now move the blank tile to the lower right corner
while (blankp.x != gridw-1)
moveBlank(blankp.x + 1, blankp.y);
while (blankp.y != gridh-1)
moveBlank(blankp.x, blankp.y + 1);
}
// shuffle the tiles randomly and place the blank at the bottom right
void shuffle() {
int limit = gridw*gridh-1;
Piece ta[] = new Piece[limit];
Piece temp;
System.arraycopy(all, 0, ta, 0, limit);
for (int i = 0; i < limit; i++) {
int j = randRange(limit);
temp = ta[j];
ta[j] = ta[i];
ta[i] = temp;
}
for (int i = 0; i < limit; i++) {
setGrid(ta[i], i/gridw, i%gridw);
}
setGrid(blankp, gridw-1, gridh-1);
}
void randomize(boolean hard) {
shuffle();
int ra, rb;
int x, y;
if (hard) {
ra = 7;
rb = 0;
} else {
ra = 0;
rb = 7;
}
x = rand.nextInt() & 1;
y = rand.nextInt() & 1;
if (x == 1 && y == 1) {
x = 2;
y = 0;
}
swap(x, y, all[ra].x, all[ra].y);
swap((rand.nextInt()&1)+1, 3, all[rb].x, all[rb].y);
if ((displacement() & 1) == 1)
swap(1, 3, 2, 3);
}
// Compute and return the displacement, that is, the number of
// pairs of tiles that are out of order. The blank tile *must*
// be in the lower right corner.
int displacement() {
boolean temp[] = new boolean[gridw*gridh-1]; // all false
int n = 0;
for (int i = 0; i < gridh; i++) {
for (int j = 0; j < gridw; j++) {
Piece p = grid[j][i];
if (p == blankp)
continue;
temp[p.serial] = true;
for (int k = 0; k < p.serial; k++) {
if (!temp[k])
n++;
}
}
}
return n;
}
void resetGrid() {
Piece temp[] = new Piece[gridw*gridh];
int k = 0;
for (int i = 0; i < gridw; i++) {
for (int j = 0; j < gridh; j++) {
temp[k++] = grid[i][j];
}
}
for (k = 0; k < temp.length; k++) {
temp[k].goHome();
}
}
void rearrangeFunnily(boolean hard) {
resetGrid();
if (hard) {
// RATE YOUR MIDP LAN
swap(0, 0, 3, 1);
swap(2, 2, 3, 2);
swap(3, 2, 0, 3);
swap(0, 3, 2, 3);
} else {
// RATE YOUR MIDP NAL
swap(2, 2, 3, 2);
swap(3, 2, 0, 3);
}
}
void setState(int ns) {
gameState = ns;
switch (gameState) {
case INITIALIZED:
addCommand(cmd[CMD_ABOUT]);
removeCommand(cmd[CMD_RESET]);
addCommand(cmd[CMD_START]);
addCommand(cmd[CMD_UNLOCK]);
addCommand(cmd[CMD_EXIT]);
addCommand(cmd[CMD_OPTIONS]);
break;
case PLAYING:
addCommand(cmd[CMD_ABOUT]);
addCommand(cmd[CMD_RESET]);
removeCommand(cmd[CMD_START]);
removeCommand(cmd[CMD_UNLOCK]);
addCommand(cmd[CMD_EXIT]);
addCommand(cmd[CMD_OPTIONS]);
break;
case WON:
addCommand(cmd[CMD_ABOUT]);
removeCommand(cmd[CMD_RESET]);
addCommand(cmd[CMD_START]);
addCommand(cmd[CMD_UNLOCK]);
addCommand(cmd[CMD_EXIT]);
addCommand(cmd[CMD_OPTIONS]);
break;
}
}
public Board(MIDlet midlet_) {
int i;
// "global" variables
midlet = midlet_;
dpy = Display.getDisplay(midlet);
gridw = 4;
gridh = 4;
font = Font.getFont(Font.FACE_SYSTEM,
Font.STYLE_PLAIN, Font.SIZE_MEDIUM);
// REMIND update when font metrics info gets implemented
cellw = font.charWidth('M') + 7;
cellh = font.getHeight() + 1;
cellxoff = 3;
cellyoff = 0;
gridx = (getWidth() - (gridw*cellw) + 1) / 2;
gridy = 10;
cheated = false;
rand = new Random();
// create the grid arrays
grid = new Piece[gridw][];
for (i = 0; i < gridw; i++) {
grid[i] = new Piece[gridh];
}
all = new Piece[gridw*gridh];
for (i = 0; i < (gridw*gridh)-1; i++) {
int x = i % gridw;
int y = i / gridw;
String s = letters.substring(i, i+1);
grid[x][y] =
all[i] =
new Piece(s, i, x, y, i < (gridw*gridh/2));
}
// make the special blank piece
blankp = new Piece(null, gridw*gridh-1, gridw-1, gridh-1, false);
grid[gridw-1][gridh-1] = blankp;
all[gridw*gridh-1] = blankp;
// set up commands
cmd = new Command[CMD_ZLAST];
cmd[CMD_ABOUT] =
new BoardCommand("About", Command.HELP, 5, CMD_ABOUT);
cmd[CMD_EXIT] =
new BoardCommand("Exit", Command.EXIT, 6, CMD_EXIT);
cmd[CMD_OPTIONS] =
new BoardCommand("Options", Command.SCREEN, 3, CMD_OPTIONS);
cmd[CMD_RESET] =
new BoardCommand("Reset", Command.SCREEN, 1, CMD_RESET);
cmd[CMD_START] =
new BoardCommand("Start", Command.SCREEN, 1, CMD_START);
cmd[CMD_UNLOCK] =
new BoardCommand("Unlock", Command.SCREEN, 4, CMD_UNLOCK);
// set up the listener
setCommandListener(this);
// set up options screen
options = new Options(dpy, this);
// set up initial state
setState(INITIALIZED);
}
public void commandAction(Command c, Displayable d) {
switch (((BoardCommand) c).tag) {
case CMD_ABOUT:
About.showAbout(Display.getDisplay(midlet));
break;
case CMD_EXIT:
midlet.notifyDestroyed();
break;
case CMD_OPTIONS:
dpy.setCurrent(options);
break;
case CMD_RESET:
cheated = false;
resetGrid();
setState(INITIALIZED);
repaint();
break;
case CMD_START:
cheated = false;
if (options.funny) {
rearrangeFunnily(options.hard);
} else {
randomize(options.hard);
}
setState(PLAYING);
repaint();
break;
case CMD_UNLOCK:
cheated = true;
setState(PLAYING);
repaint();
break;
}
}
public void showNotify() {
// System.out.println("Board: showNotify");
}
public void hideNotify() {
// System.out.println("Board: hideNotify");
}
public void paint(Graphics g) {
g.setColor(0xFFFFFF);
g.fillRect(0, 0, getWidth(), getHeight());
g.translate(gridx, gridy);
g.setColor(0);
g.drawRect(-2, -2, gridw*cellw + 2, gridh*cellh + 2);
for (int j = 0; j < gridw; j++) {
for (int k = 0; k < gridh; k++) {
grid[j][k].paint(g);
}
}
if (gameState == WON) {
g.translate(-g.getTranslateX(), -g.getTranslateY());
g.setColor(0);
g.setFont(Font.getDefaultFont());
g.drawString(
(cheated ? "CHEATER!" : "YOU WIN!"),
getWidth() / 2,
getHeight() - 1,
Graphics.BOTTOM|Graphics.HCENTER);
}
}
public void keyPressed(int code) {
if (gameState != PLAYING)
return;
int game = getGameAction(code);
int swapx = blankp.x;
int swapy = blankp.y;
int direction = (options.reversed ? -1 : 1);
switch (game) {
case Canvas.UP:
swapy += direction;
break;
case Canvas.DOWN:
swapy -= direction;
break;
case Canvas.LEFT:
swapx += direction;
break;
case Canvas.RIGHT:
swapx -= direction;
break;
default:
return;
}
if (swapx < 0 || swapx >= gridw ||
swapy < 0 || swapy >= gridh) {
return;
}
moveBlank(swapx, swapy);
repaint();
if (isSolved()) {
setState(WON);
}
}
}
|