diff --git a/src/dev/kske/minesweeper/Board.java b/src/dev/kske/minesweeper/Board.java index 0bf7d8d..92ed51f 100644 --- a/src/dev/kske/minesweeper/Board.java +++ b/src/dev/kske/minesweeper/Board.java @@ -8,7 +8,9 @@ import java.awt.Graphics; import java.awt.Image; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Random; @@ -31,8 +33,9 @@ public class Board extends JPanel { private GameState gameState; private int mines, activeTiles, flaggedTiles; private Tile[][] board; + private BoardConfig initialConfig; - private BoardConfig initialConfig; + private List listeners; static { icons = new HashMap<>(); @@ -45,6 +48,7 @@ public class Board extends JPanel { public Board() { // Not using a layout manager super(null); + listeners = new ArrayList<>(); addMouseListener(new MouseAdapter() { @Override @@ -84,6 +88,7 @@ public class Board extends JPanel { for (int j = 0; j < boardHeight; j++) board[i][j] = new Tile(); initMines(); + notifyFlaggedTilesEvent(new FlaggedTilesEvent(this, flaggedTiles)); repaint(); } @@ -91,6 +96,18 @@ public class Board extends JPanel { init(initialConfig); } + public void registerGameListener(GameListener listener) { + listeners.add(listener); + } + + private void notifyGameStateEvent(GameStateEvent evt) { + listeners.forEach(listener -> listener.onGameStateEvent(evt)); + } + + private void notifyFlaggedTilesEvent(FlaggedTilesEvent evt) { + listeners.forEach(listener -> listener.onFlaggedTilesEvent(evt)); + } + @Override public void paintComponent(Graphics g) { super.paintComponent(g); @@ -172,11 +189,21 @@ public class Board extends JPanel { 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; - else if (mines == activeTiles) gameState = GameState.WON; + if (tile.isMine()) { + gameState = GameState.LOST; + repaint(); + GameStateEvent evt = new GameStateEvent(this, gameState); + notifyGameStateEvent(evt); + } else if (mines == activeTiles) { + gameState = GameState.WON; + repaint(); + GameStateEvent evt = new GameStateEvent(this, gameState); + notifyGameStateEvent(evt); + } // Touch surrounding tiles when there are zero surrounding mines else if (tile.getSurroundingMines() == 0) @@ -198,6 +225,7 @@ public class Board extends JPanel { tile.setFlagged(true); flaggedTiles++; } + notifyFlaggedTilesEvent(new FlaggedTilesEvent(this, flaggedTiles)); repaintTile(n, m); } } diff --git a/src/dev/kske/minesweeper/CustomDialog.java b/src/dev/kske/minesweeper/CustomDialog.java new file mode 100644 index 0000000..e2df3cc --- /dev/null +++ b/src/dev/kske/minesweeper/CustomDialog.java @@ -0,0 +1,63 @@ +package dev.kske.minesweeper; + +import java.awt.BorderLayout; +import java.awt.FlowLayout; + +import javax.swing.JButton; +import javax.swing.JDialog; +import javax.swing.JPanel; +import javax.swing.border.EmptyBorder; + + +/** + * Project: Minesweeper
+ * File: CustomDialog.java
+ * Created: 03.04.2019
+ * Author: Kai S. K. Engelbart + */ +public class CustomDialog extends JDialog { + + private static final long serialVersionUID = -4019516811065781434L; + private final JPanel mcontentPanel = new JPanel(); + + /** + * Launch the application. + */ + public static void main(String[] args) { + try { + CustomDialog dialog = new CustomDialog(); + dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); + dialog.setVisible(true); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * Create the dialog. + */ + public CustomDialog() { + setBounds(100, 100, 450, 300); + getContentPane().setLayout(new BorderLayout()); + mcontentPanel.setLayout(new FlowLayout()); + mcontentPanel.setBorder(new EmptyBorder(5, 5, 5, 5)); + getContentPane().add(mcontentPanel, BorderLayout.CENTER); + { + JPanel buttonPane = new JPanel(); + buttonPane.setLayout(new FlowLayout(FlowLayout.RIGHT)); + getContentPane().add(buttonPane, BorderLayout.SOUTH); + { + JButton okButton = new JButton("OK"); + okButton.setActionCommand("OK"); + buttonPane.add(okButton); + getRootPane().setDefaultButton(okButton); + } + { + JButton cancelButton = new JButton("Cancel"); + cancelButton.setActionCommand("Cancel"); + buttonPane.add(cancelButton); + } + } + } + +} diff --git a/src/dev/kske/minesweeper/FlaggedTilesEvent.java b/src/dev/kske/minesweeper/FlaggedTilesEvent.java new file mode 100644 index 0000000..ecc42fe --- /dev/null +++ b/src/dev/kske/minesweeper/FlaggedTilesEvent.java @@ -0,0 +1,27 @@ +package dev.kske.minesweeper; + +import java.util.EventObject; + +/** + * Project: Minesweeper
+ * File: FlaggedTilesEvent.java
+ * Created: 03.04.2019
+ * Author: Kai S. K. Engelbart + */ +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; } +} diff --git a/src/dev/kske/minesweeper/GameListener.java b/src/dev/kske/minesweeper/GameListener.java new file mode 100644 index 0000000..3aef1dd --- /dev/null +++ b/src/dev/kske/minesweeper/GameListener.java @@ -0,0 +1,14 @@ +package dev.kske.minesweeper; + +/** + * Project: Minesweeper
+ * File: GameStateListener.java
+ * Created: 03.04.2019
+ * Author: Kai S. K. Engelbart + */ +public interface GameListener { + + void onGameStateEvent(GameStateEvent evt); + + void onFlaggedTilesEvent(FlaggedTilesEvent evt); +} diff --git a/src/dev/kske/minesweeper/GameStateEvent.java b/src/dev/kske/minesweeper/GameStateEvent.java new file mode 100644 index 0000000..f61b8ca --- /dev/null +++ b/src/dev/kske/minesweeper/GameStateEvent.java @@ -0,0 +1,27 @@ +package dev.kske.minesweeper; + +import java.util.EventObject; + +/** + * Project: Minesweeper
+ * File: GameStateEvent.java
+ * Created: 03.04.2019
+ * Author: Kai S. K. Engelbart + */ +public class GameStateEvent extends EventObject { + + private static final long serialVersionUID = -966111253980213845L; + + private final Board board; + private final GameState gameState; + + public GameStateEvent(Object source, GameState gameState) { + super(source); + board = (Board) source; + this.gameState = gameState; + } + + public Board getBoard() { return board; } + + public GameState getGameState() { return gameState; } +} diff --git a/src/dev/kske/minesweeper/Minesweeper.java b/src/dev/kske/minesweeper/Minesweeper.java index af35aa7..8f30a5a 100644 --- a/src/dev/kske/minesweeper/Minesweeper.java +++ b/src/dev/kske/minesweeper/Minesweeper.java @@ -2,14 +2,18 @@ package dev.kske.minesweeper; import java.awt.BorderLayout; import java.awt.EventQueue; +import java.awt.FlowLayout; import java.awt.event.KeyEvent; 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; /** * Project: Minesweeper
@@ -68,9 +72,38 @@ public class Minesweeper { 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:"); + lblRemainingMines.setHorizontalAlignment(SwingConstants.LEFT); + headerPanel.add(lblRemainingMines); + + board.registerGameListener(new GameListener() { + + @Override + public void onGameStateEvent(GameStateEvent evt) { + switch (evt.getGameState()) { + case LOST: + JOptionPane.showMessageDialog(mframe, "Game lost!"); + break; + case WON: + JOptionPane.showMessageDialog(mframe, "Game won!"); + } + } + + @Override + public void onFlaggedTilesEvent(FlaggedTilesEvent evt) { + lblRemainingMines.setText("Remaining Mines: " + (evt.getBoard().getMines() - evt.getFlagged())); + mframe.pack(); + } + }); + JButton btnRestart = new JButton("Restart"); + headerPanel.add(btnRestart); btnRestart.addActionListener((evt) -> board.reset()); - mframe.getContentPane().add(btnRestart, BorderLayout.NORTH); mframe.pack(); }