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: Minesweeper
* File: Board.java
* Created: 22.03.2019
* Author: Kai S. K. Engelbart */ public class Board extends JPanel { private static final long serialVersionUID = -279269871397851420L; private static final int tileSize = 32; private static Map 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); } } }