11
.classpath
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<classpath>
|
||||||
|
<classpathentry kind="src" path="src"/>
|
||||||
|
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER">
|
||||||
|
<attributes>
|
||||||
|
<attribute name="module" value="true"/>
|
||||||
|
</attributes>
|
||||||
|
</classpathentry>
|
||||||
|
<classpathentry kind="lib" path="res"/>
|
||||||
|
<classpathentry kind="output" path="bin"/>
|
||||||
|
</classpath>
|
57
.gitignore
vendored
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
.metadata
|
||||||
|
bin/
|
||||||
|
tmp/
|
||||||
|
*.tmp
|
||||||
|
*.bak
|
||||||
|
*.swp
|
||||||
|
*~.nib
|
||||||
|
local.properties
|
||||||
|
.settings/
|
||||||
|
.loadpath
|
||||||
|
.recommenders
|
||||||
|
|
||||||
|
# External tool builders
|
||||||
|
.externalToolBuilders/
|
||||||
|
|
||||||
|
# Locally stored "Eclipse launch configurations"
|
||||||
|
*.launch
|
||||||
|
|
||||||
|
# PyDev specific (Python IDE for Eclipse)
|
||||||
|
*.pydevproject
|
||||||
|
|
||||||
|
# CDT-specific (C/C++ Development Tooling)
|
||||||
|
.cproject
|
||||||
|
|
||||||
|
# CDT- autotools
|
||||||
|
.autotools
|
||||||
|
|
||||||
|
# Java annotation processor (APT)
|
||||||
|
.factorypath
|
||||||
|
|
||||||
|
# PDT-specific (PHP Development Tools)
|
||||||
|
.buildpath
|
||||||
|
|
||||||
|
# sbteclipse plugin
|
||||||
|
.target
|
||||||
|
|
||||||
|
# Tern plugin
|
||||||
|
.tern-project
|
||||||
|
|
||||||
|
# TeXlipse plugin
|
||||||
|
.texlipse
|
||||||
|
|
||||||
|
# STS (Spring Tool Suite)
|
||||||
|
.springBeans
|
||||||
|
|
||||||
|
# Code Recommenders
|
||||||
|
.recommenders/
|
||||||
|
|
||||||
|
# Annotation Processing
|
||||||
|
.apt_generated/
|
||||||
|
|
||||||
|
# Scala IDE specific (Scala & Java development for Eclipse)
|
||||||
|
.cache-main
|
||||||
|
.scala_dependencies
|
||||||
|
.worksheet
|
||||||
|
/scores.ser
|
||||||
|
/scores old.ser
|
17
.project
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<projectDescription>
|
||||||
|
<name>Minesweeper</name>
|
||||||
|
<comment></comment>
|
||||||
|
<projects>
|
||||||
|
</projects>
|
||||||
|
<buildSpec>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
</buildSpec>
|
||||||
|
<natures>
|
||||||
|
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||||
|
</natures>
|
||||||
|
</projectDescription>
|
BIN
res/flag.png
Normal file
After Width: | Height: | Size: 6.1 KiB |
BIN
res/mine.png
Normal file
After Width: | Height: | Size: 9.9 KiB |
BIN
res/mine2.png
Normal file
After Width: | Height: | Size: 8.9 KiB |
BIN
res/mine3.png
Normal file
After Width: | Height: | Size: 7.0 KiB |
BIN
res/mine4.png
Normal file
After Width: | Height: | Size: 7.6 KiB |
BIN
res/smiley.png
Normal file
After Width: | Height: | Size: 8.1 KiB |
BIN
res/smiley1.png
Normal file
After Width: | Height: | Size: 6.5 KiB |
BIN
res/smiley2.png
Normal file
After Width: | Height: | Size: 7.2 KiB |
BIN
res/smiley3.png
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
res/tile.png
Normal file
After Width: | Height: | Size: 6.2 KiB |
BIN
res/tile2.png
Normal file
After Width: | Height: | Size: 4.8 KiB |
BIN
res/tile3.png
Normal file
After Width: | Height: | Size: 7.2 KiB |
BIN
res/tile4.png
Normal file
After Width: | Height: | Size: 4.9 KiB |
258
src/dev/kske/minesweeper/Board.java
Normal file
@ -0,0 +1,258 @@
|
|||||||
|
package dev.kske.minesweeper;
|
||||||
|
|
||||||
|
import java.awt.BasicStroke;
|
||||||
|
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.Image;
|
||||||
|
import java.awt.event.MouseAdapter;
|
||||||
|
import java.awt.event.MouseEvent;
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
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 boardWidth, boardHeight;
|
||||||
|
private GameState gameState;
|
||||||
|
private int mines, activeTiles, flaggedTiles;
|
||||||
|
private Tile[][] board;
|
||||||
|
private BoardConfig boardConfig;
|
||||||
|
|
||||||
|
private Instant start, finish;
|
||||||
|
|
||||||
|
private List<GameListener> listeners;
|
||||||
|
|
||||||
|
static {
|
||||||
|
icons = new HashMap<>();
|
||||||
|
final String[] names = { "mine2", "mine4", "tile", "tile3" };
|
||||||
|
for (String name : names) {
|
||||||
|
icons.put(name, TextureLoader.loadScaledImage(name, tileSize));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Board() {
|
||||||
|
// Not using a layout manager
|
||||||
|
super(null);
|
||||||
|
listeners = new ArrayList<>();
|
||||||
|
addMouseListener(new MouseAdapter() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mousePressed(MouseEvent evt) {
|
||||||
|
int n = evt.getX() / tileSize, m = evt.getY() / tileSize;
|
||||||
|
Tile tile = board[n][m];
|
||||||
|
|
||||||
|
if (tile.isTouched() || gameState != GameState.ACTIVE) return;
|
||||||
|
switch (evt.getButton()) {
|
||||||
|
case MouseEvent.BUTTON1:
|
||||||
|
touchTile(n, m);
|
||||||
|
break;
|
||||||
|
case MouseEvent.BUTTON3:
|
||||||
|
flagTile(n, m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void init(BoardConfig config) {
|
||||||
|
boardConfig = config;
|
||||||
|
|
||||||
|
boardWidth = config.width;
|
||||||
|
boardHeight = config.height;
|
||||||
|
|
||||||
|
setPreferredSize(new Dimension(config.width * tileSize, config.height * tileSize));
|
||||||
|
|
||||||
|
gameState = GameState.ACTIVE;
|
||||||
|
mines = config.mines;
|
||||||
|
activeTiles = boardWidth * boardHeight;
|
||||||
|
flaggedTiles = 0;
|
||||||
|
|
||||||
|
notifyFlaggedTilesEvent(new FlaggedTilesEvent(this, flaggedTiles));
|
||||||
|
|
||||||
|
// Initialize board
|
||||||
|
board = new Tile[boardWidth][boardHeight];
|
||||||
|
for (int i = 0; i < boardWidth; i++)
|
||||||
|
for (int j = 0; j < boardHeight; j++)
|
||||||
|
board[i][j] = new Tile();
|
||||||
|
initMines();
|
||||||
|
repaint();
|
||||||
|
revalidate();
|
||||||
|
|
||||||
|
start = Instant.now();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reset() {
|
||||||
|
init(boardConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void paintComponent(Graphics g) {
|
||||||
|
super.paintComponent(g);
|
||||||
|
for (int i = 0; i < boardWidth; i++)
|
||||||
|
for (int j = 0; j < boardHeight; j++) {
|
||||||
|
Tile tile = board[i][j];
|
||||||
|
int x = i * tileSize, y = j * tileSize;
|
||||||
|
|
||||||
|
// Draw background
|
||||||
|
g.setColor(Color.gray);
|
||||||
|
g.fillRect(x, y, x + tileSize, y + tileSize);
|
||||||
|
|
||||||
|
// Draw tile with normal mine
|
||||||
|
if (gameState == GameState.LOST && tile.isMine()) g.drawImage(icons.get("mine2"), x, y, this);
|
||||||
|
// Draw tile with diffused mine
|
||||||
|
else if (gameState == GameState.WON && tile.isMine()) g.drawImage(icons.get("mine4"), x, y, this);
|
||||||
|
else 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());
|
||||||
|
g.setFont(new Font("Arial", Font.BOLD, 18));
|
||||||
|
g.setColor(Color.red);
|
||||||
|
FontMetrics fm = g.getFontMetrics();
|
||||||
|
int w = fm.stringWidth(numStr), h = fm.getHeight();
|
||||||
|
g.drawString(numStr, x + (tileSize - w) / 2, y + (tileSize - h) / 2 + fm.getAscent());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw flagged tile
|
||||||
|
else if (tile.isFlagged()) g.drawImage(icons.get("tile3"), x, y, this);
|
||||||
|
|
||||||
|
// Draw normal tile
|
||||||
|
else g.drawImage(icons.get("tile"), x, y, this);
|
||||||
|
|
||||||
|
// Draw grid
|
||||||
|
((Graphics2D) g).setStroke(new BasicStroke(2.0f));
|
||||||
|
g.setColor(Color.black);
|
||||||
|
g.drawRect(x, y, x + tileSize, y + tileSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void registerGameListener(GameListener listener) {
|
||||||
|
listeners.add(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void notifyGameStateEvent(GameOverEvent evt) {
|
||||||
|
listeners.forEach(listener -> listener.onGameOverEvent(evt));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void notifyFlaggedTilesEvent(FlaggedTilesEvent evt) {
|
||||||
|
listeners.forEach(listener -> listener.onFlaggedTilesEvent(evt));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void repaintTile(int n, int m) {
|
||||||
|
repaint(n * tileSize, m * tileSize, (n + 1) * tileSize, (n + 1) * tileSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initMines() {
|
||||||
|
int remaining = mines;
|
||||||
|
Random random = new Random();
|
||||||
|
while (remaining > 0) {
|
||||||
|
// Randomly select a tile
|
||||||
|
int n = random.nextInt(boardWidth);
|
||||||
|
int m = random.nextInt(boardHeight);
|
||||||
|
|
||||||
|
// Check if the selected tile already is a mine and is not touched
|
||||||
|
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 + 2, board.length); i++)
|
||||||
|
for (int j = Math.max(0, m - 1); j < Math.min(m + 2, board[i].length); j++)
|
||||||
|
board[i][j].setSurroundingMines(board[i][j].getSurroundingMines() + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private 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--;
|
||||||
|
notifyFlaggedTilesEvent(new FlaggedTilesEvent(this, flaggedTiles));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test if the game is won or lost
|
||||||
|
if (tile.isMine()) {
|
||||||
|
gameState = GameState.LOST;
|
||||||
|
onGameOver();
|
||||||
|
} else if (mines == activeTiles) {
|
||||||
|
gameState = GameState.WON;
|
||||||
|
onGameOver();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 + 2, board.length); i++)
|
||||||
|
for (int j = Math.max(0, m - 1); j < Math.min(m + 2, board[i].length); j++)
|
||||||
|
if (i != n || j != m) touchTile(i, j);
|
||||||
|
|
||||||
|
repaintTile(n, m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private 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++;
|
||||||
|
}
|
||||||
|
notifyFlaggedTilesEvent(new FlaggedTilesEvent(this, flaggedTiles));
|
||||||
|
repaintTile(n, m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onGameOver() {
|
||||||
|
finish = Instant.now();
|
||||||
|
int duration = (int) Duration.between(start, finish).toMillis();
|
||||||
|
|
||||||
|
repaint();
|
||||||
|
GameOverEvent evt = new GameOverEvent(this, gameState, boardConfig, duration);
|
||||||
|
notifyGameStateEvent(evt);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMines() { return mines; }
|
||||||
|
|
||||||
|
public int getActiveTiles() { return activeTiles; }
|
||||||
|
|
||||||
|
public int getFlaggedTiles() { return flaggedTiles; }
|
||||||
|
|
||||||
|
public BoardConfig getBoardConfig() { return boardConfig; }
|
||||||
|
}
|
25
src/dev/kske/minesweeper/BoardConfig.java
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package dev.kske.minesweeper;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Project: <strong>Minesweeper</strong><br>
|
||||||
|
* File: <strong>BoardConfig.java</strong><br>
|
||||||
|
* Created: <strong>01.04.2019</strong><br>
|
||||||
|
* Author: <strong>Kai S. K. Engelbart</strong>
|
||||||
|
*/
|
||||||
|
public class BoardConfig implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = -6083006887427383946L;
|
||||||
|
|
||||||
|
public static final BoardConfig EASY = new BoardConfig(8, 8, 10), MEDIUM = new BoardConfig(16, 16, 40),
|
||||||
|
HARD = new BoardConfig(30, 16, 99);
|
||||||
|
|
||||||
|
public final int width, height, mines;
|
||||||
|
|
||||||
|
public BoardConfig(int width, int height, int mines) {
|
||||||
|
this.width = width;
|
||||||
|
this.height = height;
|
||||||
|
this.mines = mines;
|
||||||
|
}
|
||||||
|
}
|
112
src/dev/kske/minesweeper/CustomDialog.java
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
package dev.kske.minesweeper;
|
||||||
|
|
||||||
|
import java.awt.BorderLayout;
|
||||||
|
import java.awt.FlowLayout;
|
||||||
|
import java.awt.Font;
|
||||||
|
import java.awt.Frame;
|
||||||
|
import java.awt.GridLayout;
|
||||||
|
|
||||||
|
import javax.swing.JButton;
|
||||||
|
import javax.swing.JDialog;
|
||||||
|
import javax.swing.JLabel;
|
||||||
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.JSlider;
|
||||||
|
import javax.swing.border.EmptyBorder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Project: <strong>Minesweeper</strong><br>
|
||||||
|
* File: <strong>CustomDialog.java</strong><br>
|
||||||
|
* Created: <strong>03.04.2019</strong><br>
|
||||||
|
* Author: <strong>Kai S. K. Engelbart</strong>
|
||||||
|
*/
|
||||||
|
public class CustomDialog extends JDialog {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = -4019516811065781434L;
|
||||||
|
private final JPanel mcontentPanel = new JPanel();
|
||||||
|
|
||||||
|
private BoardConfig result;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create the dialog.
|
||||||
|
*/
|
||||||
|
public CustomDialog(Frame owner) {
|
||||||
|
super(owner, ModalityType.APPLICATION_MODAL);
|
||||||
|
setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
|
||||||
|
|
||||||
|
setBounds(100, 100, 450, 300);
|
||||||
|
getContentPane().setLayout(new BorderLayout());
|
||||||
|
mcontentPanel.setBorder(new EmptyBorder(5, 5, 5, 5));
|
||||||
|
getContentPane().add(mcontentPanel, BorderLayout.CENTER);
|
||||||
|
mcontentPanel.setLayout(new GridLayout(0, 3, 0, 0));
|
||||||
|
{
|
||||||
|
JLabel lblBoardWidthText = new JLabel("Board Width:");
|
||||||
|
lblBoardWidthText.setFont(new Font("Tahoma", Font.PLAIN, 14));
|
||||||
|
mcontentPanel.add(lblBoardWidthText);
|
||||||
|
}
|
||||||
|
JLabel lblBoardWidth = new JLabel("");
|
||||||
|
lblBoardWidth.setFont(new Font("Tahoma", Font.PLAIN, 14));
|
||||||
|
mcontentPanel.add(lblBoardWidth);
|
||||||
|
JSlider sliderBoardWidth = new JSlider();
|
||||||
|
sliderBoardWidth.addChangeListener((evt) -> lblBoardWidth.setText(String.valueOf(sliderBoardWidth.getValue())));
|
||||||
|
sliderBoardWidth.setValue(16);
|
||||||
|
sliderBoardWidth.setMinimum(2);
|
||||||
|
sliderBoardWidth.setMaximum(30);
|
||||||
|
mcontentPanel.add(sliderBoardWidth);
|
||||||
|
{
|
||||||
|
JLabel lblBoardHeightText = new JLabel("Board Height:");
|
||||||
|
lblBoardHeightText.setFont(new Font("Tahoma", Font.PLAIN, 14));
|
||||||
|
mcontentPanel.add(lblBoardHeightText);
|
||||||
|
}
|
||||||
|
JLabel lblBoardHeight = new JLabel("");
|
||||||
|
lblBoardHeight.setFont(new Font("Tahoma", Font.PLAIN, 14));
|
||||||
|
mcontentPanel.add(lblBoardHeight);
|
||||||
|
JSlider sliderBoardHeight = new JSlider();
|
||||||
|
sliderBoardHeight
|
||||||
|
.addChangeListener((evt) -> lblBoardHeight.setText(String.valueOf(sliderBoardHeight.getValue())));
|
||||||
|
sliderBoardHeight.setValue(16);
|
||||||
|
sliderBoardHeight.setMaximum(30);
|
||||||
|
sliderBoardHeight.setMinimum(2);
|
||||||
|
mcontentPanel.add(sliderBoardHeight);
|
||||||
|
{
|
||||||
|
JLabel lblNumberOfMinesText = new JLabel("Number of Mines:");
|
||||||
|
lblNumberOfMinesText.setFont(new Font("Tahoma", Font.PLAIN, 14));
|
||||||
|
mcontentPanel.add(lblNumberOfMinesText);
|
||||||
|
}
|
||||||
|
JLabel lblNumMines = new JLabel("");
|
||||||
|
lblNumMines.setFont(new Font("Tahoma", Font.PLAIN, 14));
|
||||||
|
mcontentPanel.add(lblNumMines);
|
||||||
|
JSlider sliderNumMines = new JSlider();
|
||||||
|
sliderNumMines.addChangeListener((evt) -> lblNumMines.setText(String.valueOf(sliderNumMines.getValue())));
|
||||||
|
sliderNumMines.setValue(16);
|
||||||
|
sliderNumMines.setMinimum(2);
|
||||||
|
sliderNumMines.setMaximum(200);
|
||||||
|
mcontentPanel.add(sliderNumMines);
|
||||||
|
{
|
||||||
|
JPanel buttonPane = new JPanel();
|
||||||
|
buttonPane.setLayout(new FlowLayout(FlowLayout.RIGHT));
|
||||||
|
getContentPane().add(buttonPane, BorderLayout.SOUTH);
|
||||||
|
{
|
||||||
|
JButton okButton = new JButton("Start Game");
|
||||||
|
okButton.setActionCommand("OK");
|
||||||
|
okButton.addActionListener((evt) -> {
|
||||||
|
result = new BoardConfig(sliderBoardWidth.getValue(), sliderBoardHeight.getValue(),
|
||||||
|
sliderNumMines.getValue());
|
||||||
|
dispose();
|
||||||
|
});
|
||||||
|
buttonPane.add(okButton);
|
||||||
|
getRootPane().setDefaultButton(okButton);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
JButton cancelButton = new JButton("Cancel");
|
||||||
|
cancelButton.setActionCommand("Cancel");
|
||||||
|
cancelButton.addActionListener((evt) -> dispose());
|
||||||
|
buttonPane.add(cancelButton);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public BoardConfig showDialog() {
|
||||||
|
setVisible(true);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
27
src/dev/kske/minesweeper/FlaggedTilesEvent.java
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package dev.kske.minesweeper;
|
||||||
|
|
||||||
|
import java.util.EventObject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Project: <strong>Minesweeper</strong><br>
|
||||||
|
* File: <strong>FlaggedTilesEvent.java</strong><br>
|
||||||
|
* Created: <strong>03.04.2019</strong><br>
|
||||||
|
* Author: <strong>Kai S. K. Engelbart</strong>
|
||||||
|
*/
|
||||||
|
public class FlaggedTilesEvent extends EventObject {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = -5809857531886339312L;
|
||||||
|
|
||||||
|
private final Board board;
|
||||||
|
private final int flagged;
|
||||||
|
|
||||||
|
public FlaggedTilesEvent(Object source, int flagged) {
|
||||||
|
super(source);
|
||||||
|
board = (Board) source;
|
||||||
|
this.flagged = flagged;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Board getBoard() { return board; }
|
||||||
|
|
||||||
|
public int getFlagged() { return flagged; }
|
||||||
|
}
|
14
src/dev/kske/minesweeper/GameListener.java
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
package dev.kske.minesweeper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Project: <strong>Minesweeper</strong><br>
|
||||||
|
* File: <strong>GameStateListener.java</strong><br>
|
||||||
|
* Created: <strong>03.04.2019</strong><br>
|
||||||
|
* Author: <strong>Kai S. K. Engelbart</strong>
|
||||||
|
*/
|
||||||
|
public interface GameListener {
|
||||||
|
|
||||||
|
void onGameOverEvent(GameOverEvent evt);
|
||||||
|
|
||||||
|
void onFlaggedTilesEvent(FlaggedTilesEvent evt);
|
||||||
|
}
|
35
src/dev/kske/minesweeper/GameOverEvent.java
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
package dev.kske.minesweeper;
|
||||||
|
|
||||||
|
import java.util.EventObject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Project: <strong>Minesweeper</strong><br>
|
||||||
|
* File: <strong>GameOverEvent.java</strong><br>
|
||||||
|
* Created: <strong>03.04.2019</strong><br>
|
||||||
|
* Author: <strong>Kai S. K. Engelbart</strong>
|
||||||
|
*/
|
||||||
|
public class GameOverEvent extends EventObject {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = -966111253980213845L;
|
||||||
|
|
||||||
|
private final Board board;
|
||||||
|
private final GameState gameState;
|
||||||
|
private final BoardConfig boardConfig;
|
||||||
|
private final int duration;
|
||||||
|
|
||||||
|
public GameOverEvent(Object source, GameState gameState, BoardConfig boardConfig, int duration) {
|
||||||
|
super(source);
|
||||||
|
board = (Board) source;
|
||||||
|
this.gameState = gameState;
|
||||||
|
this.boardConfig = boardConfig;
|
||||||
|
this.duration = duration;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Board getBoard() { return board; }
|
||||||
|
|
||||||
|
public GameState getGameState() { return gameState; }
|
||||||
|
|
||||||
|
public BoardConfig getBoardConfig() { return boardConfig; }
|
||||||
|
|
||||||
|
public int getDuration() { return duration; }
|
||||||
|
}
|
12
src/dev/kske/minesweeper/GameState.java
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package dev.kske.minesweeper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Project: <strong>Minesweeper</strong><br>
|
||||||
|
* File: <strong>GameState.java</strong><br>
|
||||||
|
* Created: <strong>22.03.2019</strong><br>
|
||||||
|
* Author: <strong>Kai S. K. Engelbart</strong>
|
||||||
|
*/
|
||||||
|
public enum GameState {
|
||||||
|
|
||||||
|
ACTIVE, WON, LOST;
|
||||||
|
}
|
221
src/dev/kske/minesweeper/Minesweeper.java
Normal file
@ -0,0 +1,221 @@
|
|||||||
|
package dev.kske.minesweeper;
|
||||||
|
|
||||||
|
import static dev.kske.minesweeper.BoardConfig.EASY;
|
||||||
|
import static dev.kske.minesweeper.BoardConfig.HARD;
|
||||||
|
import static dev.kske.minesweeper.BoardConfig.MEDIUM;
|
||||||
|
|
||||||
|
import java.awt.BorderLayout;
|
||||||
|
import java.awt.EventQueue;
|
||||||
|
import java.awt.event.KeyEvent;
|
||||||
|
import java.awt.event.WindowAdapter;
|
||||||
|
import java.awt.event.WindowEvent;
|
||||||
|
|
||||||
|
import javax.swing.JButton;
|
||||||
|
import javax.swing.JFrame;
|
||||||
|
import javax.swing.JLabel;
|
||||||
|
import javax.swing.JMenu;
|
||||||
|
import javax.swing.JMenuBar;
|
||||||
|
import javax.swing.JMenuItem;
|
||||||
|
import javax.swing.JOptionPane;
|
||||||
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.SwingConstants;
|
||||||
|
import javax.swing.Timer;
|
||||||
|
import javax.swing.UIManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Project: <strong>Minesweeper</strong><br>
|
||||||
|
* File: <strong>Minesweeper.java</strong><br>
|
||||||
|
* Created: <strong>21.03.2019</strong><br>
|
||||||
|
* Author: <strong>Kai S. K. Engelbart</strong>
|
||||||
|
*/
|
||||||
|
public class Minesweeper {
|
||||||
|
|
||||||
|
private static final String VERSION = "1.0";
|
||||||
|
|
||||||
|
private JFrame mframe;
|
||||||
|
|
||||||
|
private Board board;
|
||||||
|
private Timer timer;
|
||||||
|
private int gameTime;
|
||||||
|
private ScoreManager scoreManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Launch the application.
|
||||||
|
*/
|
||||||
|
public static void main(String[] args) {
|
||||||
|
EventQueue.invokeLater(() -> {
|
||||||
|
try {
|
||||||
|
Minesweeper window = new Minesweeper();
|
||||||
|
window.mframe.setVisible(true);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create the application.
|
||||||
|
*
|
||||||
|
* @wbp.parser.entryPoint
|
||||||
|
*/
|
||||||
|
public Minesweeper() {
|
||||||
|
initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the contents of the frame.
|
||||||
|
*/
|
||||||
|
private void initialize() {
|
||||||
|
try {
|
||||||
|
UIManager.setLookAndFeel(UIManager.createLookAndFeel("Nimbus"));
|
||||||
|
} catch (Exception ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
mframe = new JFrame();
|
||||||
|
mframe.addWindowListener(new WindowAdapter() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void windowClosing(WindowEvent e) {
|
||||||
|
scoreManager.saveScores();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
mframe.setResizable(false);
|
||||||
|
mframe.setTitle("Minesweeper");
|
||||||
|
mframe.setBounds(100, 100, 359, 86);
|
||||||
|
mframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||||
|
|
||||||
|
createMenuBar();
|
||||||
|
|
||||||
|
board = new Board();
|
||||||
|
board.init(EASY);
|
||||||
|
mframe.getContentPane().setLayout(new BorderLayout(0, 0));
|
||||||
|
mframe.getContentPane().add(board, BorderLayout.CENTER);
|
||||||
|
|
||||||
|
JPanel headerPanel = new JPanel();
|
||||||
|
mframe.getContentPane().add(headerPanel, BorderLayout.NORTH);
|
||||||
|
headerPanel.setLayout(new BorderLayout(0, 0));
|
||||||
|
|
||||||
|
JButton btnRestart = new JButton("Restart");
|
||||||
|
btnRestart.setHorizontalAlignment(SwingConstants.RIGHT);
|
||||||
|
headerPanel.add(btnRestart, BorderLayout.EAST);
|
||||||
|
|
||||||
|
JPanel panel = new JPanel();
|
||||||
|
headerPanel.add(panel, BorderLayout.WEST);
|
||||||
|
panel.setLayout(new BorderLayout(0, 0));
|
||||||
|
|
||||||
|
JLabel lblTime = new JLabel("Time:");
|
||||||
|
panel.add(lblTime, BorderLayout.NORTH);
|
||||||
|
|
||||||
|
timer = new Timer(1000, e -> lblTime.setText("Time: " + gameTime++ + "s"));
|
||||||
|
timer.setRepeats(true);
|
||||||
|
timer.setInitialDelay(0);
|
||||||
|
timer.setCoalesce(true);
|
||||||
|
|
||||||
|
JLabel lblRemainingMines = new JLabel("Remaining Mines: " + EASY.mines);
|
||||||
|
panel.add(lblRemainingMines, BorderLayout.SOUTH);
|
||||||
|
lblRemainingMines.setHorizontalAlignment(SwingConstants.LEFT);
|
||||||
|
btnRestart.addActionListener((evt) -> { board.reset(); gameTime = 0; timer.restart(); });
|
||||||
|
mframe.pack();
|
||||||
|
|
||||||
|
board.registerGameListener(new GameListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onGameOverEvent(GameOverEvent evt) {
|
||||||
|
timer.stop();
|
||||||
|
switch (evt.getGameState()) {
|
||||||
|
case LOST:
|
||||||
|
JOptionPane.showMessageDialog(mframe, "Game lost!");
|
||||||
|
break;
|
||||||
|
case WON:
|
||||||
|
JOptionPane.showMessageDialog(mframe, "Game won!");
|
||||||
|
scoreManager.addScore(evt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFlaggedTilesEvent(FlaggedTilesEvent evt) {
|
||||||
|
lblRemainingMines.setText("Remaining Mines: " + (evt.getBoard().getMines() - evt.getFlagged()));
|
||||||
|
mframe.pack();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
scoreManager = new ScoreManager();
|
||||||
|
scoreManager.loadScores();
|
||||||
|
timer.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createMenuBar() {
|
||||||
|
var menubar = new JMenuBar();
|
||||||
|
|
||||||
|
{
|
||||||
|
var gameMenu = new JMenu("Game");
|
||||||
|
|
||||||
|
var easyMenuItem = new JMenuItem("Easy");
|
||||||
|
var mediumMenuItem = new JMenuItem("Medium");
|
||||||
|
var hardMenuItem = new JMenuItem("Hard");
|
||||||
|
var customMenuItem = new JMenuItem("Custom");
|
||||||
|
|
||||||
|
gameMenu.setMnemonic(KeyEvent.VK_G);
|
||||||
|
easyMenuItem.setMnemonic(KeyEvent.VK_E);
|
||||||
|
mediumMenuItem.setMnemonic(KeyEvent.VK_M);
|
||||||
|
hardMenuItem.setMnemonic(KeyEvent.VK_H);
|
||||||
|
customMenuItem.setMnemonic(KeyEvent.VK_C);
|
||||||
|
|
||||||
|
easyMenuItem.addActionListener((evt) -> initGame(EASY));
|
||||||
|
mediumMenuItem.addActionListener((evt) -> initGame(MEDIUM));
|
||||||
|
hardMenuItem.addActionListener((evt) -> initGame(HARD));
|
||||||
|
customMenuItem.addActionListener((evt) -> {
|
||||||
|
BoardConfig cfg = new CustomDialog(mframe).showDialog();
|
||||||
|
if (cfg != null) initGame(cfg);
|
||||||
|
});
|
||||||
|
|
||||||
|
gameMenu.add(easyMenuItem);
|
||||||
|
gameMenu.add(mediumMenuItem);
|
||||||
|
gameMenu.add(hardMenuItem);
|
||||||
|
gameMenu.addSeparator();
|
||||||
|
gameMenu.add(customMenuItem);
|
||||||
|
menubar.add(gameMenu);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var highscoreMenu = new JMenu("Highscores");
|
||||||
|
|
||||||
|
var easyMenuItem = new JMenuItem("Easy");
|
||||||
|
var mediumMenuItem = new JMenuItem("Medium");
|
||||||
|
var hardMenuItem = new JMenuItem("Hard");
|
||||||
|
|
||||||
|
highscoreMenu.setMnemonic(KeyEvent.VK_H);
|
||||||
|
easyMenuItem.setMnemonic(KeyEvent.VK_E);
|
||||||
|
mediumMenuItem.setMnemonic(KeyEvent.VK_M);
|
||||||
|
hardMenuItem.setMnemonic(KeyEvent.VK_H);
|
||||||
|
|
||||||
|
easyMenuItem.addActionListener((evt) -> scoreManager.displayEasy());
|
||||||
|
mediumMenuItem.addActionListener((evt) -> scoreManager.displayMedium());
|
||||||
|
hardMenuItem.addActionListener((evt) -> scoreManager.displayHard());
|
||||||
|
|
||||||
|
highscoreMenu.add(easyMenuItem);
|
||||||
|
highscoreMenu.add(mediumMenuItem);
|
||||||
|
highscoreMenu.add(hardMenuItem);
|
||||||
|
menubar.add(highscoreMenu);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var aboutMenuItem = new JMenuItem("About");
|
||||||
|
|
||||||
|
aboutMenuItem.addActionListener((evt) -> JOptionPane.showMessageDialog(board,
|
||||||
|
"Minesweeper version " + VERSION + "\nby Kai S. K. Engelbart"));
|
||||||
|
|
||||||
|
menubar.add(aboutMenuItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
mframe.setJMenuBar(menubar);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initGame(BoardConfig config) {
|
||||||
|
board.init(config);
|
||||||
|
gameTime = 0;
|
||||||
|
timer.restart();
|
||||||
|
mframe.pack();
|
||||||
|
}
|
||||||
|
}
|
36
src/dev/kske/minesweeper/Score.java
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
package dev.kske.minesweeper;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Project: <strong>Minesweeper</strong><br>
|
||||||
|
* File: <strong>Score.java</strong><br>
|
||||||
|
* Created: <strong>16.04.2019</strong><br>
|
||||||
|
* Author: <strong>Kai S. K. Engelbart</strong>
|
||||||
|
*/
|
||||||
|
public class Score implements Comparable<Score>, Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 3384023296639779740L;
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
private final int duration;
|
||||||
|
private final Date date;
|
||||||
|
|
||||||
|
public Score(String name, int duration, Date date) {
|
||||||
|
this.name = name;
|
||||||
|
this.duration = duration;
|
||||||
|
this.date = date;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() { return name; }
|
||||||
|
|
||||||
|
public int getDuration() { return duration; }
|
||||||
|
|
||||||
|
public Date getDate() { return date; }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(Score other) {
|
||||||
|
return Integer.compare(duration, other.duration);
|
||||||
|
}
|
||||||
|
}
|
60
src/dev/kske/minesweeper/ScoreDialog.java
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
package dev.kske.minesweeper;
|
||||||
|
|
||||||
|
import java.awt.BorderLayout;
|
||||||
|
import java.awt.Font;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.swing.JDialog;
|
||||||
|
import javax.swing.JLabel;
|
||||||
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.JTable;
|
||||||
|
import javax.swing.SwingConstants;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Project: <strong>Minesweeper</strong><br>
|
||||||
|
* File: <strong>ScoreDialog.java</strong><br>
|
||||||
|
* Created: <strong>16.04.2019</strong><br>
|
||||||
|
* Author: <strong>Kai S. K. Engelbart</strong>
|
||||||
|
*/
|
||||||
|
public class ScoreDialog extends JDialog {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 3637727047056147815L;
|
||||||
|
private JTable mtable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create the dialog.
|
||||||
|
*/
|
||||||
|
public ScoreDialog(List<Score> scores, String boardConfigName) {
|
||||||
|
setModal(true);
|
||||||
|
setBounds(100, 100, 450, 300);
|
||||||
|
setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
|
||||||
|
getContentPane().setLayout(new BorderLayout(0, 0));
|
||||||
|
|
||||||
|
String[] columnNames = { "Place", "Name", "Duration", "Date" };
|
||||||
|
String[][] data = new String[scores.size()][4];
|
||||||
|
Iterator<Score> iter = scores.iterator();
|
||||||
|
for(int i = 0; i < data.length; i++) {
|
||||||
|
Score s = iter.next();
|
||||||
|
data[i][0] = String.valueOf(i + 1);
|
||||||
|
data[i][1] = s.getName();
|
||||||
|
data[i][2] = String.valueOf(s.getDuration());
|
||||||
|
data[i][3] = new SimpleDateFormat().format(s.getDate());
|
||||||
|
}
|
||||||
|
|
||||||
|
mtable = new JTable(data, columnNames);
|
||||||
|
getContentPane().add(mtable);
|
||||||
|
|
||||||
|
JPanel panel = new JPanel();
|
||||||
|
getContentPane().add(panel, BorderLayout.NORTH);
|
||||||
|
panel.setLayout(new BorderLayout(0, 0));
|
||||||
|
|
||||||
|
panel.add(mtable.getTableHeader(), BorderLayout.CENTER);
|
||||||
|
|
||||||
|
JLabel lblHighscores = new JLabel("Highscores: " + boardConfigName);
|
||||||
|
panel.add(lblHighscores, BorderLayout.NORTH);
|
||||||
|
lblHighscores.setFont(new Font("Tahoma", Font.BOLD, 16));
|
||||||
|
lblHighscores.setHorizontalAlignment(SwingConstants.CENTER);
|
||||||
|
}
|
||||||
|
}
|
101
src/dev/kske/minesweeper/ScoreManager.java
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
package dev.kske.minesweeper;
|
||||||
|
|
||||||
|
import static dev.kske.minesweeper.BoardConfig.EASY;
|
||||||
|
import static dev.kske.minesweeper.BoardConfig.HARD;
|
||||||
|
import static dev.kske.minesweeper.BoardConfig.MEDIUM;
|
||||||
|
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.ObjectInputStream;
|
||||||
|
import java.io.ObjectOutputStream;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.swing.JOptionPane;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Project: <strong>Minesweeper</strong><br>
|
||||||
|
* File: <strong>ScoreManager.java</strong><br>
|
||||||
|
* Created: <strong>15.05.2019</strong><br>
|
||||||
|
* Author: <strong>Kai S. K. Engelbart</strong>
|
||||||
|
*/
|
||||||
|
public class ScoreManager {
|
||||||
|
|
||||||
|
private List<Score> easy, medium, hard;
|
||||||
|
private final String scoresFile = "scores.ser";
|
||||||
|
|
||||||
|
public ScoreManager() {
|
||||||
|
easy = new ArrayList<>();
|
||||||
|
medium = new ArrayList<>();
|
||||||
|
hard = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addScore(GameOverEvent evt) {
|
||||||
|
// Determine board config
|
||||||
|
BoardConfig config = evt.getBoardConfig();
|
||||||
|
if (config == EASY && (easy.size() < 10 || easy.get(9).getDuration() > evt.getDuration())) {
|
||||||
|
String name = JOptionPane.showInputDialog("Please enter your name");
|
||||||
|
Score score = new Score(name, evt.getDuration(), new Date());
|
||||||
|
sortInsert(score, easy);
|
||||||
|
} else if (config == MEDIUM && (medium.size() < 10 || medium.get(9).getDuration() > evt.getDuration())) {
|
||||||
|
String name = JOptionPane.showInputDialog("Please enter your name");
|
||||||
|
Score score = new Score(name, evt.getDuration(), new Date());
|
||||||
|
sortInsert(score, medium);
|
||||||
|
} else if (config == HARD && (hard.size() < 10 || hard.get(9).getDuration() > evt.getDuration())) {
|
||||||
|
String name = JOptionPane.showInputDialog("Please enter your name");
|
||||||
|
Score score = new Score(name, evt.getDuration(), new Date());
|
||||||
|
sortInsert(score, hard);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sortInsert(Score score, List<Score> list) {
|
||||||
|
for (int i = 0; i < list.size(); i++)
|
||||||
|
if (list.get(i).getDuration() > score.getDuration()) {
|
||||||
|
list.add(i, score);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
list.add(score);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void displayEasy() {
|
||||||
|
new ScoreDialog(easy, "Easy").setVisible(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void displayMedium() {
|
||||||
|
new ScoreDialog(medium, "Medium").setVisible(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void displayHard() {
|
||||||
|
new ScoreDialog(hard, "Hard").setVisible(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public void loadScores() {
|
||||||
|
try (var in = new ObjectInputStream(new FileInputStream(scoresFile))) {
|
||||||
|
Object obj = in.readObject();
|
||||||
|
if (obj instanceof ArrayList<?>) easy = (ArrayList<Score>) obj;
|
||||||
|
obj = in.readObject();
|
||||||
|
if (obj instanceof ArrayList<?>) medium = (ArrayList<Score>) obj;
|
||||||
|
obj = in.readObject();
|
||||||
|
if (obj instanceof ArrayList<?>) hard = (ArrayList<Score>) obj;
|
||||||
|
else throw new IOException("Serialized object has the wrong class.");
|
||||||
|
} catch (FileNotFoundException ex) {} catch (IOException | ClassNotFoundException ex) {
|
||||||
|
JOptionPane.showMessageDialog(null,
|
||||||
|
"The score file seems to be corrupted. It will be replaced when closing the game.", "File error",
|
||||||
|
JOptionPane.ERROR_MESSAGE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void saveScores() {
|
||||||
|
try (var out = new ObjectOutputStream(new FileOutputStream(scoresFile))) {
|
||||||
|
out.writeObject(easy);
|
||||||
|
out.writeObject(medium);
|
||||||
|
out.writeObject(hard);
|
||||||
|
} catch (IOException ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
38
src/dev/kske/minesweeper/TextureLoader.java
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
package dev.kske.minesweeper;
|
||||||
|
|
||||||
|
import java.awt.Image;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Project: <strong>Minesweeper</strong><br>
|
||||||
|
* File: <strong>TextureLoader.java</strong><br>
|
||||||
|
* Created: <strong>25.03.2019</strong><br>
|
||||||
|
* Author: <strong>Kai S. K. Engelbart</strong>
|
||||||
|
*/
|
||||||
|
public class TextureLoader {
|
||||||
|
|
||||||
|
private TextureLoader() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads an image from the resource folder and scales it to a square.
|
||||||
|
*
|
||||||
|
* @param name The name of the file without the PNG extension in the resource
|
||||||
|
* folder
|
||||||
|
* @param scale The side length of the square to which the image will be scaled
|
||||||
|
* @return The scaled image
|
||||||
|
*/
|
||||||
|
public static Image loadScaledImage(String name, int scale) {
|
||||||
|
BufferedImage in = null;
|
||||||
|
try {
|
||||||
|
in = ImageIO.read(new File("res" + File.separator + name + ".png"));
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
Image scaled = in.getScaledInstance(scale, scale, Image.SCALE_SMOOTH);
|
||||||
|
return scaled;
|
||||||
|
}
|
||||||
|
}
|
43
src/dev/kske/minesweeper/Tile.java
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
package dev.kske.minesweeper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Project: <strong>Minesweeper</strong><br>
|
||||||
|
* File: <strong>Tile.java</strong><br>
|
||||||
|
* Created: <strong>22.03.2019</strong><br>
|
||||||
|
* Author: <strong>Kai S. K. Engelbart</strong>
|
||||||
|
*/
|
||||||
|
public class Tile {
|
||||||
|
|
||||||
|
private boolean mine, flagged, touched;
|
||||||
|
|
||||||
|
private boolean drawSurroundingMines;
|
||||||
|
private int surroundingMines;
|
||||||
|
|
||||||
|
public Tile() {
|
||||||
|
mine = flagged = touched = drawSurroundingMines = false;
|
||||||
|
|
||||||
|
surroundingMines = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isMine() { return mine; }
|
||||||
|
|
||||||
|
public void setMine(boolean mine) { this.mine = mine; }
|
||||||
|
|
||||||
|
public boolean isFlagged() { return flagged; }
|
||||||
|
|
||||||
|
public void setFlagged(boolean flagged) { this.flagged = flagged; }
|
||||||
|
|
||||||
|
public boolean isTouched() { return touched; }
|
||||||
|
|
||||||
|
public void setTouched(boolean touched) { this.touched = touched; }
|
||||||
|
|
||||||
|
public boolean isDrawSurroundingMines() { return drawSurroundingMines; }
|
||||||
|
|
||||||
|
public void setDrawSurroundingMines(boolean drawSurroundingMines) {
|
||||||
|
this.drawSurroundingMines = drawSurroundingMines;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSurroundingMines() { return surroundingMines; }
|
||||||
|
|
||||||
|
public void setSurroundingMines(int surroundingMines) { this.surroundingMines = surroundingMines; }
|
||||||
|
}
|
3
src/module-info.java
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
module Minesweeper {
|
||||||
|
requires java.desktop;
|
||||||
|
}
|