package jsx;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.WindowConstants;
public class Minesweeper extends JPanel {
List<Tile> field;
Random random;
boolean inGame;
boolean isWin;
public Minesweeper() {
field = new ArrayList<Tile>();
random = new Random();
setPreferredSize(new Dimension(346, 368));
setFocusable(true);
addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
init();
}
}
});
addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (inGame) {
if (e.getButton() == MouseEvent.BUTTON1) {
Tile tile = getTile(e.getX(), e.getY());
if (tile == null)
return;
clickLeft(tile);
} else if (e.getButton() == MouseEvent.BUTTON3) {
Tile tile = getTile(e.getX(), e.getY());
if (tile == null)
return;
clickRight(tile);
}
} else {
init();
}
}
});
init();
}
void init() {
inGame = true;
isWin = false;
for (int x = 0; x < 9; x++) {
for (int y = 0; y < 9; y++) {
field.add(new Tile(x, y));
}
}
for (int i = 0; i < 9; i++) {
Tile t = field.get(random.nextInt(field.size()));
if (t.isMined()) {
i--;
} else {
t.setMined();
for (Tile t0 : getNearby(t)) {
t0.setValue(t0.getValue() + 1);
}
}
}
repaint();
}
List<Tile> getNearby(Tile t) {
List<Tile> out = new ArrayList<Tile>();
for (Tile tile : field) {
if (t.isNear(tile)) {
out.add(tile);
}
}
return out;
}
int getInt(int i) {
if (i < 18) {
return -1;
}
for (int c = 1; c < 10; c++) {
if (i < 18 + 32 * c) {
return c - 1;
}
}
return -1;
}
Tile getTile(int x, int y) {
x = getInt(x);
y = getInt(y);
if (x > -1 && y > -1) {
for (Tile t0 : field) {
if (t0.getX() == x && t0.getY() == y) {
return t0;
}
}
}
return null;
}
void clickRight(Tile tile) {
if (tile.isFlag(Flag.NONE)) {
tile.setFlag(Flag.MARKED);
} else if (tile.isFlag(Flag.MARKED)) {
tile.setFlag(Flag.NONE);
}
repaint();
}
void clickLeft(Tile tile) {
if (!tile.isFlag(Flag.NONE))
return;
// losing
if (tile.isMined()) {
inGame = false;
for (Tile t0 : field) {
if (t0.isMined()) {
if (t0.isFlag(Flag.MARKED)) {
t0.setFlag(Flag.VERIFIED);
} else {
t0.setFlag(Flag.ERROR);
}
} else {
t0.setFlag(Flag.CRUSHED);
}
}
} else {
tile.setFlag(Flag.OPEN);
// TODO check win
}
repaint();
}
enum Flag {
NONE, OPEN, MARKED, CRUSHED, ERROR, VERIFIED;
}
static class Tile {
private boolean mined;
private int value, x, y;
private Flag flag;
public Tile(int x, int y) {
this.mined = false;
this.value = 0;
this.x = x;
this.y = y;
this.flag = Flag.NONE;
}
public boolean isNear(Tile t) {
return Math.abs(x - t.getX()) < 2 && Math.abs(y - t.getY()) < 2;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public boolean isMined() {
return mined;
}
public void setMined() {
this.mined = true;
}
public int getValue() {
return value;
}
public void setValue(int i) {
this.value = i;
}
public boolean isFlag(Flag f) {
return flag == f;
}
public Flag getFlag() {
return flag;
}
public void setFlag(Flag f) {
this.flag = f;
}
}
@Override
public void paint(Graphics g) {
super.paint(g);
g.setColor(new Color(255, 223, 94));
g.fillRect(0, 0, 346, 368);
for (Tile t : field) {
drawTile(g, t);
}
}
Color getTileTextColor(int i) {
switch (i) {
case 1:
return new Color(0, 45, 183);
case 2:
return new Color(22, 122, 0);
case 3:
return new Color(183, 0, 0);
case 4:
return new Color(0, 10, 163);
case 5:
return new Color(183, 0, 0);
case 6:
return new Color(140, 184, 255);
case 7:
return new Color(183, 0, 0);
case 8:
return new Color(147, 0, 160);
}
return null;
}
void drawTile(Graphics gr, Tile tile) {
Graphics2D g = ((Graphics2D) gr);
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_NORMALIZE);
int oX = 18 + tile.getX() * 34;
int oY = 18 + tile.getY() * 34;
// 166, 0, 14
if (tile.isFlag(Flag.OPEN)) {
g.setColor(new Color(221, 234, 255));
g.fillRect(oX, oY, 32, 32);
if (tile.getValue() > 0) {
g.setColor(getTileTextColor(tile.getValue()));
Font font = new Font("Arial", Font.BOLD, 18);
g.setFont(font);
FontMetrics fm = getFontMetrics(font);
int w = fm.stringWidth(String.valueOf(tile.getValue()));
int h = -(int) fm.getLineMetrics(String.valueOf(tile.getValue()), g).getBaselineOffsets()[2];
g.drawString(String.valueOf(tile.getValue()), oX + (32 - w) / 2, oY + 32 - (32 - h) / 2 - 2);
}
} else if (tile.isFlag(Flag.MARKED)) {
g.setColor(new Color(43, 102, 196));
g.fillRect(oX, oY, 32, 32);
g.setColor(new Color(255, 255, 255));
Font font = new Font("Arial", Font.BOLD, 18);
g.setFont(font);
FontMetrics fm = getFontMetrics(font);
int w = fm.stringWidth("?");
int h = -(int) fm.getLineMetrics("?", g).getBaselineOffsets()[2];
g.drawString("?", oX + (32 - w) / 2, oY + 30 - (32 - h) / 2);
} else if (tile.isFlag(Flag.CRUSHED)) {
g.setColor(new Color(186, 37, 0));
g.fillRect(oX, oY, 32, 32);
} else if (tile.isFlag(Flag.VERIFIED)) {
g.setColor(new Color(22, 188, 0));
g.fillRect(oX, oY, 32, 32);
} else if (tile.isFlag(Flag.ERROR)) {
g.setColor(new Color(255, 251, 30));
g.fillRect(oX, oY, 32, 32);
} else if (tile.isFlag(Flag.NONE)) {
g.setColor(new Color(43, 102, 196));
g.fillRect(oX, oY, 32, 32);
}
if (!inGame) {
g.setColor(new Color(255, 255, 255, 30));
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(new Color(44, 44, 44));
g.setFont(new Font("Arial", Font.BOLD, 80));
if (isWin) {
g.drawString("Win!", 110, 201);
} else {
g.drawString("Lose!", 110, 201);
}
}
}
public static void main(String[] args) {
JFrame game = new JFrame();
game.setTitle("Minesweeper");
game.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
game.setSize(346, 368);
game.setResizable(false);
game.add(new Minesweeper());
game.setLocationRelativeTo(null);
game.setVisible(true);
}
}