📄 board.java
字号:
package cn.pandaoen.game.minesweeper;
import org.apache.log4j.Logger;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import java.util.Random;
import cn.pandaoen.game.minesweeper.res.Resources;
public class Board extends Canvas implements Listener {
// don't change the constant values
static final int STATE_UNKNOWN = -1;
static final int STATE_UNKNOWN_FLAG = 12;
static final int STATE_UNKNOWN_QUESTION = 13;
static final int STATE_MINE = 9;
static final int STATE_EXPLOSION = 10;
static final int STATE_WRONG = 11;
private static final int INSET = 1;
private final Logger logger = Logger.getLogger(Board.class);
private int flagCount;
private final Point boxSize;
private int rows, columns, mineCount;
private boolean[][] mines;
private int[][] mineCounts;
private int[][] states;
private boolean isExploded;
private boolean mouseBtn1Down;
private boolean mouseBtn2Down;
private int lastRow, lastColumn;
private Color highlight;
private Color light;
private Color normal;
private Color dark;
private Color bg;
private Image[] images = new Image[14];
public Board(Composite parent, int style) {
super(parent, style | SWT.NO_BACKGROUND | SWT.NO_FOCUS);
for (int i = 0; i < 9; i++) {
String name = i + ".gif"; //$NON-NLS-1$
images[i] = Resources.res.getImage(name);
}
images[9] = Resources.res.getImage("mine.gif"); //$NON-NLS-1$
images[10] = Resources.res.getImage("explosion.gif"); //$NON-NLS-1$
images[11] = Resources.res.getImage("wrong.gif"); //$NON-NLS-1$
images[12] = Resources.res.getImage("flag.gif"); //$NON-NLS-1$
images[13] = Resources.res.getImage("question.gif"); //$NON-NLS-1$
Rectangle bounds = images[0].getBounds();
boxSize = new Point(bounds.width + INSET, bounds.height + INSET);
Display display = getDisplay();
highlight = display.getSystemColor(SWT.COLOR_WIDGET_HIGHLIGHT_SHADOW);
light = display.getSystemColor(SWT.COLOR_WIDGET_LIGHT_SHADOW);
normal = display.getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW);
dark = display.getSystemColor(SWT.COLOR_WIDGET_DARK_SHADOW);
bg = display.getSystemColor(SWT.COLOR_WIDGET_BACKGROUND);
addListener(SWT.Paint, this);
addListener(SWT.MouseDown, this);
addListener(SWT.MouseMove, this);
addListener(SWT.MouseUp, this);
addListener(SWT.Dispose, this);
}
public void setMinesAndSize(int mineCount, int rows, int columns) {
logger.trace("Board " + mineCount + " " + rows + " " + columns); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$
checkWidget();
this.mineCount = mineCount;
this.rows = rows;
this.columns = columns;
mines = new boolean[rows][columns];
states = new int[rows][columns];
mineCounts = new int[rows][columns];
reset();
redraw();
}
public void reset() {
checkWidget();
for (int i = 0; i < rows; i++) {
for (int j = 0; j < columns; j++) {
states[i][j] = STATE_UNKNOWN;
mines[i][j] = false;
mineCounts[i][j] = -1;
}
}
setEnabled(true);
distributeMines();
isExploded = false;
flagCount = 0;
lastRow = lastColumn = -1;
redraw();
}
public void _showall() {
GC gc = new GC(this);
for(int i = 0; i < rows; i++) {
for(int j = 0; j < columns; j++) {
int s = states[i][j];
if(mines[i][j])
states[i][j] = STATE_MINE;
else
states[i][j] = getMineCount(i, j);
drawItem(gc, i, j, boxSize.x, boxSize.y, false);
states[i][j] = s;
}
}
gc.dispose();
}
private void distributeMines() {
int size = rows * columns;
int[] indexs = new int[size];
for (int i = 0; i < size; i++)
indexs[i] = i;
int count = mineCount;
Random random = new Random();
while (count-- > 0) {
int rand = random.nextInt(size);
int mineIndex = indexs[rand];
indexs[rand] = indexs[--size];
mines[mineIndex / columns][mineIndex % columns] = true;
}
}
/**
* @return the number of mines
*/
public int getMineCount() {
checkWidget();
return mineCount;
}
public int getFlagCount() {
checkWidget();
return flagCount;
}
private int getMineCount(int r, int c) {
if (mineCounts[r][c] == -1) {
int count = 0;
for (int i = r - 1; i <= r + 1; i++) {
for (int j = c - 1; j <= c + 1; j++) {
if (i != r || j != c) { // not (r, c)
if (i >= 0 && i < rows && j >= 0 && j < columns) {
if (mines[i][j])
count++;
}
}
}
}
mineCounts[r][c] = count;
}
return mineCounts[r][c];
}
private int getStateCount(int r, int c, int state) {
int count = 0;
for (int i = r - 1; i <= r + 1; i++) {
for (int j = c - 1; j <= c + 1; j++) {
if (i != r || j != c) { // not (r, c)
if (i >= 0 && i < rows && j >= 0 && j < columns) {
if (states[i][j] == state)
count++;
}
}
}
}
return count;
}
private boolean isUnknown(int state) {
return state == STATE_UNKNOWN || state >= STATE_UNKNOWN_FLAG;
}
public boolean isExploded() {
checkWidget();
return isExploded;
}
public boolean isSwept() {
checkWidget();
int total = rows * columns;
int numberOfKnown = 0;
for (int i = 0; i < rows; i++) {
for (int j = 0; j < columns; j++) {
if (!isUnknown(states[i][j]))
numberOfKnown++;
}
}
boolean swept = numberOfKnown + mineCount == total;
if (swept) {
for (int i = 0; i < rows; i++) {
for (int j = 0; j < columns; j++) {
if (isUnknown(states[i][j]))
states[i][j] = STATE_UNKNOWN_FLAG;
}
}
redraw();
}
return swept;
}
/**
* @see #drawBox
*/
private void paint(Event event) {
GC gc = event.gc;
int boxWidth = boxSize.x;
int boxHeight = boxSize.y;
for (int i = 0; i < rows; i++) {
for (int j = 0; j < columns; j++) {
drawItem(gc, i, j, boxWidth, boxHeight, false);
}
}
}
private void drawItem(GC gc, int row, int column, int width, int height,
boolean pressed) {
if (row < 0 || row >= rows)
return;
if (column < 0 || column >= columns)
return;
int x = column * width;
int y = row * height;
gc.setBackground(bg);
gc.fillRectangle(x, y, width, height);
int state = states[row][column];
if (state == STATE_UNKNOWN_FLAG)
pressed = false;
if (isUnknown(state)) {
drawBox(gc, x, y, width, height, pressed);
}
if (state >= 0) {
Image image = images[state];
Rectangle rect = image.getBounds();
int offsetX = Math.max(0, (width - rect.width) / 2);
int offsetY = Math.max(0, (height - rect.height) / 2);
gc.drawImage(image, x + offsetX, y + offsetY);
}
}
private void drawBox(GC gc, int x, int y, int width, int height,
boolean pressed) {
Color topLeft = pressed ? dark : highlight;
Color bottomRight = pressed ? highlight : dark;
Color innerTopLeft = pressed ? normal : light;
Color innerBottomRight = pressed ? light : normal;
// draw top left line
gc.setForeground(topLeft);
gc.drawLine(x, y, x + width - 1, y);
gc.drawLine(x, y, x, y + height - 1);
gc.setForeground(innerTopLeft);
gc.drawLine(x + 1, y + 1, x + width - 2, y + 1);
gc.drawLine(x + 1, y + 1, x + 1, y + height - 2);
// draw bottom right line
gc.setForeground(bottomRight);
gc.drawLine(x, y + height - 1, x + width - 1, y + height - 1);
gc.drawLine(x + width - 1, y, x + width - 1, y + height - 1);
gc.setForeground(innerBottomRight);
gc.drawLine(x + 1, y + height - 2, x + width - 2, y + height - 2);
gc.drawLine(x + width - 2, y + 1, x + width - 2, y + height - 2);
}
private void mouseBtn1Down(Event event) {
int width = boxSize.x;
int height = boxSize.y;
int i = event.y / height;
int j = event.x / width;
mouseBtn1Down = true;
GC gc = new GC(this);
drawItem(gc, i, j, width, height, true);
gc.dispose();
lastRow = i;
lastColumn = j;
}
private void mouseBtn2Down(Event event) {
int width = boxSize.x;
int height = boxSize.y;
int i = event.y / height;
int j = event.x / width;
mouseBtn2Down = true;
GC gc = new GC(this);
draw9Boxes(gc, i, j, true);
gc.dispose();
lastRow = i;
lastColumn = j;
}
private void draw9Boxes(GC gc, int r, int c, boolean pressed) {
for (int i = r - 1; i <= r + 1; i++) {
for (int j = c - 1; j <= c + 1; j++) {
if (i >= 0 && i < rows && j >= 0 && j < columns) {
drawItem(gc, i, j, boxSize.x, boxSize.y, pressed);
}
}
}
}
private void mouseBtn3Down(Event event) {
int r = event.y / boxSize.y;
int c = event.x / boxSize.x;
if (r >= 0 && r < rows & c >= 0 && c < columns) {
int newState = states[r][c];
if (states[r][c] == STATE_UNKNOWN)
newState = STATE_UNKNOWN_FLAG;
else if (states[r][c] == STATE_UNKNOWN_FLAG)
newState = STATE_UNKNOWN_QUESTION;
else if (states[r][c] == STATE_UNKNOWN_QUESTION)
newState = STATE_UNKNOWN;
if (newState != states[r][c]) {
if (states[r][c] == STATE_UNKNOWN_FLAG)
flagCount--;
else if (newState == STATE_UNKNOWN_FLAG)
flagCount++;
states[r][c] = newState;
GC gc = new GC(this);
drawItem(gc, r, c, boxSize.x, boxSize.y, false);
gc.dispose();
}
}
}
private void mouseMove(Event event) {
if (!mouseBtn1Down && !mouseBtn2Down)
return;
int width = boxSize.x;
int height = boxSize.y;
int i = event.y / height;
int j = event.x / width;
if (i == lastRow && j == lastColumn)
return;
GC gc = new GC(this);
if (lastRow != -1) {
if (mouseBtn1Down)
drawItem(gc, lastRow, lastColumn, width, height, false);
else
draw9Boxes(gc, lastRow, lastColumn, false);
lastRow = lastColumn = -1;
}
if (i >= 0 && i < rows && j >= 0 && j < columns) {
if (mouseBtn1Down)
drawItem(gc, i, j, width, height, true);
else
draw9Boxes(gc, i, j, true);
lastRow = i;
lastColumn = j;
}
gc.dispose();
}
private void mouseBtn1Up(Event event) {
if (lastRow != -1) {
GC gc = new GC(this);
drawItem(gc, lastRow, lastColumn, boxSize.x, boxSize.y, false);
int state = states[lastRow][lastColumn];
if (state == STATE_UNKNOWN || state == STATE_UNKNOWN_QUESTION) {
sweep(gc, lastRow, lastColumn);
}
lastRow = lastColumn = -1;
gc.dispose();
}
mouseBtn1Down = false;
}
private void mouseBtn2Up(Event event) {
if (lastRow != -1) {
GC gc = new GC(this);
int state = states[lastRow][lastColumn];
draw9Boxes(gc, lastRow, lastColumn, false);
if (!isUnknown(state)) {
int mineCount = getMineCount(lastRow, lastColumn);
int flagCount = getStateCount(lastRow, lastColumn,
STATE_UNKNOWN_FLAG);
if (mineCount == flagCount) {
sweepNeighbors(gc, lastRow, lastColumn);
}
}
lastRow = lastColumn = -1;
gc.dispose();
}
mouseBtn2Down = false;
}
// [(r, c) must be right indexs]
private void sweep(GC gc, int r, int c) {
if (mines[r][c]) {
isExploded = true;
setEnabled(false);
states[r][c] = STATE_EXPLOSION;
revealMines();
redraw();
} else {
// (r, c) is swept
int mineCount = getMineCount(r, c);
if (mineCount > 0) {
states[r][c] = mineCount;
drawItem(gc, r, c, boxSize.x, boxSize.y, false);
} else {
recSweep(gc, r, c);
}
}
}
private void recSweep(GC gc, int r, int c) {
if (mines[r][c]) {
isExploded = true;
setEnabled(false);
states[r][c] = STATE_EXPLOSION;
revealMines();
redraw();
} else {
// (r, c) is swept
int mineCount = getMineCount(r, c);
int flagCount = getStateCount(r, c, STATE_UNKNOWN_FLAG);
states[r][c] = mineCount;
drawItem(gc, r, c, boxSize.x, boxSize.y, false);
if (mineCount == flagCount) {
sweepNeighbors(gc, r, c);
}
}
}
private void sweepNeighbors(GC gc, int r, int c) {
for (int i = r - 1; i <= r + 1; i++) {
for (int j = c - 1; j <= c + 1; j++) {
if (i != r || j != c) { // not (r, c)
if (i >= 0 && i < rows && j >= 0 && j < columns) {
if (states[i][j] == STATE_UNKNOWN
|| states[i][j] == STATE_UNKNOWN_QUESTION) {
recSweep(gc, i, j);
}
}
}
}
}
}
private void revealMines() {
for (int i = 0; i < rows; i++) {
for (int j = 0; j < columns; j++) {
int state = states[i][j];
if (state == STATE_UNKNOWN_FLAG) {
if (!mines[i][j])
states[i][j] = STATE_WRONG;
} else if (isUnknown(state)) {
if (mines[i][j])
states[i][j] = STATE_MINE;
}
}
}
}
/*
* @see org.eclipse.swt.widgets.Composite#computeSize(int, int, boolean)
*/
public Point computeSize(int wHint, int hHint, boolean changed) {
checkWidget();
int width = columns * boxSize.x;
int height = rows * boxSize.y;
Rectangle trim = computeTrim(0, 0, width, height);
return new Point(trim.width, trim.height);
}
/*
* @see org.eclipse.swt.widgets.Listener#handleEvent(org.eclipse.swt.widgets.Event)
*/
public void handleEvent(Event event) {
switch (event.type) {
case SWT.Paint:
paint(event);
break;
case SWT.MouseDown:
logger.trace("Mouse Button " + event.button + " pressed"); //$NON-NLS-1$ //$NON-NLS-2$
if (event.button == 1)
mouseBtn1Down(event);
else if (event.button == 2)
mouseBtn2Down(event);
else if (event.button == 3)
mouseBtn3Down(event);
break;
case SWT.MouseMove:
mouseMove(event);
break;
case SWT.MouseUp:
logger.trace("Mouse Button " + event.button + " released"); //$NON-NLS-1$ //$NON-NLS-2$
if (event.button == 1)
mouseBtn1Up(event);
else if (event.button == 2)
mouseBtn2Up(event);
break;
}
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -