minesweeper/src/dev/kske/minesweeper/Minesweeper.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();
}
}