minesweeper/src/dev/kske/minesweeper/Board.java

182 lines
5.2 KiB
Java

package dev.kske.minesweeper;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Point;
import java.awt.Rectangle;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import javax.swing.JPanel;
/**
* Project: <strong>Minesweeper</strong><br>
* File: <strong>Board.java</strong><br>
* Created: <strong>22.03.2019</strong><br>
* Author: <strong>Kai S. K. Engelbart</strong>
*/
public class Board extends JPanel {
private static final long serialVersionUID = -279269871397851420L;
private static final int tileSize = 32;
private static Map<String, Image> icons;
private int width, height;
private Rectangle screen;
private GameState gameState;
private int mines, activeTiles, flaggedTiles;
private Tile[][] board;
static {
icons = new HashMap<>();
final String[] names = { "mine2", "mine4", "tile", "tile3", "flag" };
for (String name : names) {
icons.put(name, TextureLoader.loadScaledImage(name, tileSize));
}
}
public Board(int x, int y, int width, int height, int mines) {
// Not using a layout manager
super(null);
this.width = width;
this.height = height;
screen = new Rectangle(x, y, x + width * tileSize, y + height * tileSize);
gameState = GameState.ACTIVE;
this.mines = mines;
activeTiles = width * height;
flaggedTiles = 0;
board = new Tile[width][height];
for (int i = 0; i < width; i++)
for (int j = 0; j < height; j++)
board[i][j] = new Tile();
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
for (int i = 0; i < width; i++)
for (int j = 0; j < height; j++) {
Tile tile = board[i][j];
int x = screen.x + i * tileSize, y = screen.y + j * tileSize;
// Draw background with grid
g.setColor(Color.gray);
g.fillRect(x, y, x + tileSize, y + tileSize);
g.setColor(Color.black);
g.drawRect(x, y, x + tileSize, y + tileSize);
// Draw all mines when the game is won or lost
switch (gameState) {
case LOST:
// Draw tile with normal mine
g.drawImage(icons.get("mine2"), x, y, this);
break;
case WON:
// Draw tile with diffused mine
g.drawImage(icons.get("mine4"), x, y, this);
break;
default:
if (tile.isTouched()) {
// Draw tile with mine
if (tile.isMine()) g.drawImage(icons.get("mine2"), x, y, this);
// Draw flagged tile
else if (tile.isDrawSurroundingMines() && tile.getSurroundingMines() > 0) {
// Draw number of surrounding mines
String numStr = String.valueOf(tile.getSurroundingMines());
FontMetrics fm = g.getFontMetrics();
int w = fm.stringWidth(numStr), h = fm.getHeight();
g.setFont(new Font("Helvetica", Font.PLAIN, 12));
g.drawString(numStr, x + tileSize / 2 - w / 2, y + tileSize / 2 - h / 2);
}
}
// Draw flagged tile
else if (tile.isFlagged()) g.drawImage(icons.get("flag"), x, y, this);
// Draw normal tile
else g.drawImage(icons.get("tile"), x, y, this);
}
}
}
public Point getTilePos(int x, int y) {
return new Point((x - screen.x) / tileSize, (y - screen.y) / tileSize);
}
public void initMines() {
int remaining = mines;
Random random = new Random();
while (remaining > 0) {
// Randomly select a tile
int n = random.nextInt(width);
int m = random.nextInt(height);
// Check if the selected tile already is a mine
if (!board[n][m].isMine()) {
// Decrement the counter
remaining--;
// Place the mine
board[n][m].setMine(true);
// Adjust surrounding mine counters
for (int i = Math.max(0, n - 1); i < Math.min(n + 1, board.length); i++)
for (int j = Math.max(0, m - 1); j < Math.min(m + 1, board[i].length); j++)
if (i != n || j != m) board[i][j].setSurroundingMines(board[i][j].getSurroundingMines() + 1);
}
}
}
public void touchTile(int n, int m) {
Tile tile = board[n][m];
if (!tile.isTouched()) {
tile.setTouched(true);
activeTiles--;
tile.setDrawSurroundingMines(true);
// Adjust the number of flagged tiles if the tile was flagged
if (tile.isFlagged()) {
tile.setFlagged(false);
flaggedTiles--;
}
// Test if the game is won or lost
if (tile.isMine()) gameState = GameState.LOST;
else if (mines == activeTiles) gameState = GameState.WON;
// Touch surrounding tiles when there are zero surrounding mines
else if (tile.getSurroundingMines() == 0)
for (int i = Math.max(0, n - 1); i < Math.min(n + 1, board.length); i++)
for (int j = Math.max(0, m - 1); j < Math.min(m + 1, board[i].length); j++)
touchTile(i, j);
// Redraw the touched tile
repaint(n * tileSize, m * tileSize, (n + 1) * tileSize, (n + 1) * tileSize);
}
}
public void flagTile(int n, int m) {
Tile tile = board[n][m];
if (!tile.isTouched()) {
if (tile.isFlagged()) {
tile.setFlagged(false);
flaggedTiles--;
} else {
tile.setFlagged(true);
flaggedTiles++;
}
repaint(n * tileSize, m * tileSize, (n + 1) * tileSize, (n + 1) * tileSize);
}
}
}