diff --git a/src/dev/kske/minesweeper/BoardConfig.java b/src/dev/kske/minesweeper/BoardConfig.java index bdb864a..4779f5a 100644 --- a/src/dev/kske/minesweeper/BoardConfig.java +++ b/src/dev/kske/minesweeper/BoardConfig.java @@ -11,7 +11,10 @@ import java.io.Serializable; 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) { @@ -19,9 +22,4 @@ public class BoardConfig implements Serializable { this.height = height; this.mines = mines; } - - @Override - public String toString() { - return String.format("%d %d %d", width, height, mines); - } } diff --git a/src/dev/kske/minesweeper/Minesweeper.java b/src/dev/kske/minesweeper/Minesweeper.java index 35b1f84..e5c59af 100644 --- a/src/dev/kske/minesweeper/Minesweeper.java +++ b/src/dev/kske/minesweeper/Minesweeper.java @@ -1,18 +1,14 @@ 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 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; @@ -34,17 +30,14 @@ import javax.swing.UIManager; */ public class Minesweeper { - private static final String VERSION = "1.1 JE"; + private static final String VERSION = "1.2 JE"; private JFrame mframe; - private Board board; - private Timer timer; - private int gameTime; - private TreeSet 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); + private Board board; + private Timer timer; + private int gameTime; + private ScoreManager scoreManager; /** * Launch the application. @@ -84,7 +77,7 @@ public class Minesweeper { @Override public void windowClosing(WindowEvent e) { - saveScores(); + scoreManager.saveScores(); } }); mframe.setResizable(false); @@ -95,7 +88,7 @@ public class Minesweeper { createMenuBar(); board = new Board(); - board.init(easyConfig); + board.init(EASY); mframe.getContentPane().setLayout(new BorderLayout(0, 0)); mframe.getContentPane().add(board, BorderLayout.CENTER); @@ -119,7 +112,7 @@ public class Minesweeper { timer.setInitialDelay(0); timer.setCoalesce(true); - JLabel lblRemainingMines = new JLabel("Remaining Mines: " + easyConfig.mines); + JLabel lblRemainingMines = new JLabel("Remaining Mines: " + EASY.mines); panel.add(lblRemainingMines, BorderLayout.SOUTH); lblRemainingMines.setHorizontalAlignment(SwingConstants.LEFT); btnRestart.addActionListener((evt) -> { @@ -140,11 +133,7 @@ public class Minesweeper { 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); - } + scoreManager.addScore(evt); } } @@ -155,76 +144,76 @@ public class Minesweeper { } }); - loadScores(); + scoreManager = new ScoreManager(); + scoreManager.loadScores(); timer.start(); } private void createMenuBar() { var menubar = new JMenuBar(); - var gameMenu = new JMenu("Game"); - var highscoreMenuItem = new JMenuItem("Highscores"); - var aboutMenuItem = new JMenuItem("About"); + { + 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"); + 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); + 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); - }); + 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"); - 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) 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); gameTime = 0; diff --git a/src/dev/kske/minesweeper/Score.java b/src/dev/kske/minesweeper/Score.java index 089fe83..00aca0c 100644 --- a/src/dev/kske/minesweeper/Score.java +++ b/src/dev/kske/minesweeper/Score.java @@ -16,13 +16,11 @@ public class Score implements Comparable, Serializable { private final String name; private final int duration; private final Date date; - private final BoardConfig boardConfig; - public Score(String name, int duration, Date date, BoardConfig boardConfig) { + public Score(String name, int duration, Date date) { this.name = name; this.duration = duration; this.date = date; - this.boardConfig = boardConfig; } public String getName() { return name; } @@ -31,8 +29,6 @@ public class Score implements Comparable, Serializable { public Date getDate() { return date; } - public BoardConfig getBoardConfig() { return boardConfig; } - @Override public int compareTo(Score other) { return Integer.compare(duration, other.duration); diff --git a/src/dev/kske/minesweeper/ScoreDialog.java b/src/dev/kske/minesweeper/ScoreDialog.java index 2e79980..ea685ea 100644 --- a/src/dev/kske/minesweeper/ScoreDialog.java +++ b/src/dev/kske/minesweeper/ScoreDialog.java @@ -4,7 +4,7 @@ import java.awt.BorderLayout; import java.awt.Font; import java.text.SimpleDateFormat; import java.util.Iterator; -import java.util.Set; +import java.util.List; import javax.swing.JDialog; import javax.swing.JLabel; @@ -26,7 +26,7 @@ public class ScoreDialog extends JDialog { /** * Create the dialog. */ - public ScoreDialog(Set scores) { + public ScoreDialog(List scores, String boardConfigName) { setModal(true); setBounds(100, 100, 450, 300); setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); @@ -39,7 +39,7 @@ public class ScoreDialog extends JDialog { Score s = iter.next(); data[i][0] = s.getName(); data[i][1] = String.valueOf(s.getDuration()); - data[i][2] = s.getBoardConfig().toString(); + data[i][2] = boardConfigName; data[i][3] = new SimpleDateFormat().format(s.getDate()); } @@ -52,7 +52,7 @@ public class ScoreDialog extends JDialog { panel.add(mtable.getTableHeader(), BorderLayout.CENTER); - JLabel lblHighscores = new JLabel("Highscores"); + JLabel lblHighscores = new JLabel("Highscores: " + boardConfigName); panel.add(lblHighscores, BorderLayout.NORTH); lblHighscores.setFont(new Font("Tahoma", Font.BOLD, 16)); lblHighscores.setHorizontalAlignment(SwingConstants.CENTER); diff --git a/src/dev/kske/minesweeper/ScoreManager.java b/src/dev/kske/minesweeper/ScoreManager.java new file mode 100644 index 0000000..60d2879 --- /dev/null +++ b/src/dev/kske/minesweeper/ScoreManager.java @@ -0,0 +1,92 @@ +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: Minesweeper
+ * File: ScoreManager.java
+ * Created: 15.05.2019
+ * Author: Kai S. K. Engelbart + */ +public class ScoreManager { + + private List 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()); + easy.add(score); + } 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()); + medium.add(score); + } 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()); + hard.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) obj; + obj = in.readObject(); + if (obj instanceof ArrayList) medium = (ArrayList) obj; + obj = in.readObject(); + if (obj instanceof ArrayList) hard = (ArrayList) 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(); + } + } +}