213 lines
6.3 KiB
Java
213 lines
6.3 KiB
Java
package dev.kske.minesweeper;
|
|
|
|
import java.awt.BorderLayout;
|
|
import java.awt.EventQueue;
|
|
import java.awt.FlowLayout;
|
|
import java.awt.event.KeyEvent;
|
|
import java.awt.event.WindowAdapter;
|
|
import java.awt.event.WindowEvent;
|
|
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.Date;
|
|
import java.util.TreeSet;
|
|
|
|
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.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.1 JE";
|
|
|
|
private JFrame mframe;
|
|
|
|
private Board board;
|
|
private TreeSet<Score> scores;
|
|
private final String scoresFile = "scores.ser";
|
|
private final BoardConfig easyConfig = new BoardConfig(8, 8, 10), mediumConfig = new BoardConfig(16, 16, 40),
|
|
hardConfig = new BoardConfig(30, 16, 99);
|
|
|
|
/**
|
|
* 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.
|
|
*/
|
|
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) {
|
|
saveScores();
|
|
}
|
|
});
|
|
mframe.setResizable(false);
|
|
mframe.setTitle("Minesweeper");
|
|
mframe.setBounds(100, 100, 198, 123);
|
|
mframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
|
|
|
createMenuBar();
|
|
|
|
board = new Board();
|
|
board.init(easyConfig);
|
|
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 FlowLayout(FlowLayout.CENTER, 5, 5));
|
|
|
|
JLabel lblRemainingMines = new JLabel("Remaining Mines: " + easyConfig.mines);
|
|
lblRemainingMines.setHorizontalAlignment(SwingConstants.LEFT);
|
|
headerPanel.add(lblRemainingMines);
|
|
|
|
board.registerGameListener(new GameListener() {
|
|
|
|
@Override
|
|
public void onGameOverEvent(GameOverEvent evt) {
|
|
switch (evt.getGameState()) {
|
|
case LOST:
|
|
JOptionPane.showMessageDialog(mframe, "Game lost!");
|
|
break;
|
|
case WON:
|
|
JOptionPane.showMessageDialog(mframe, "Game won!");
|
|
if (scores.size() < 10 || scores.last().getDuration() > evt.getDuration()) {
|
|
String name = JOptionPane.showInputDialog("Please enter your name");
|
|
Score score = new Score(name, evt.getDuration(), new Date(), evt.getBoardConfig());
|
|
scores.add(score);
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onFlaggedTilesEvent(FlaggedTilesEvent evt) {
|
|
lblRemainingMines.setText("Remaining Mines: " + (evt.getBoard().getMines() - evt.getFlagged()));
|
|
mframe.pack();
|
|
}
|
|
});
|
|
|
|
JButton btnRestart = new JButton("Restart");
|
|
btnRestart.setHorizontalAlignment(SwingConstants.RIGHT);
|
|
headerPanel.add(btnRestart);
|
|
btnRestart.addActionListener((evt) -> board.reset());
|
|
mframe.pack();
|
|
|
|
loadScores();
|
|
}
|
|
|
|
private void createMenuBar() {
|
|
var menubar = new JMenuBar();
|
|
|
|
var gameMenu = new JMenu("Game");
|
|
var highscoreMenuItem = new JMenuItem("Highscores");
|
|
var aboutMenuItem = new JMenuItem("About");
|
|
|
|
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(easyConfig));
|
|
mediumMenuItem.addActionListener((evt) -> initGame(mediumConfig));
|
|
hardMenuItem.addActionListener((evt) -> initGame(hardConfig));
|
|
customMenuItem.addActionListener((evt) -> {
|
|
BoardConfig cfg = new CustomDialog(mframe).showDialog();
|
|
if (cfg != null) initGame(cfg);
|
|
});
|
|
|
|
highscoreMenuItem.addActionListener((evt) -> new ScoreDialog(scores).setVisible(true));
|
|
aboutMenuItem.addActionListener((evt) -> JOptionPane.showMessageDialog(board,
|
|
"Minesweeper version " + VERSION + "\nby Kai S. K. Engelbart"));
|
|
|
|
gameMenu.add(easyMenuItem);
|
|
gameMenu.add(mediumMenuItem);
|
|
gameMenu.add(hardMenuItem);
|
|
gameMenu.addSeparator();
|
|
gameMenu.add(customMenuItem);
|
|
menubar.add(gameMenu);
|
|
menubar.add(highscoreMenuItem);
|
|
menubar.add(aboutMenuItem);
|
|
|
|
mframe.setJMenuBar(menubar);
|
|
}
|
|
|
|
@SuppressWarnings(
|
|
"unchecked"
|
|
)
|
|
private void loadScores() {
|
|
try (var in = new ObjectInputStream(new FileInputStream(scoresFile))) {
|
|
Object obj = in.readObject();
|
|
if (obj instanceof TreeSet<?>) scores = (TreeSet<Score>) obj;
|
|
else throw new IOException("Serialized object has the wrong class.");
|
|
} catch (FileNotFoundException ex) {
|
|
scores = new TreeSet<>();
|
|
} catch (IOException | ClassNotFoundException ex) {
|
|
JOptionPane.showMessageDialog(mframe,
|
|
"The score file seems to be corrupted. It will be replaced when closing the game.", "File error",
|
|
JOptionPane.ERROR_MESSAGE);
|
|
scores = new TreeSet<>();
|
|
}
|
|
}
|
|
|
|
private void saveScores() {
|
|
try (var out = new ObjectOutputStream(new FileOutputStream(scoresFile))) {
|
|
out.writeObject(scores);
|
|
} catch (IOException ex) {
|
|
ex.printStackTrace();
|
|
}
|
|
}
|
|
|
|
private void initGame(BoardConfig config) {
|
|
board.init(config);
|
|
mframe.pack();
|
|
}
|
|
}
|