From 9b223f4c1cee735b37666bb263a1991c2024e527 Mon Sep 17 00:00:00 2001
From: Damien Vergnet
Date: Sun, 19 May 2019 15:13:20 +0200
Subject: [PATCH] feat: can click on discovered cells to click all around
---
build.gradle | 2 +-
.../minesweeper/ConfigTags.java | 2 -
.../minesweeper/MainController.java | 198 ++------------
.../minesweeper/Minesweeper.java | 5 +-
.../minesweeper/ScoresDao.java | 64 +----
.../darmo_creations/minesweeper/Start.java | 3 -
.../minesweeper/events/EventType.java | 1 -
.../minesweeper/gui/MainFrame.java | 16 +-
.../minesweeper/model/CellLabelProvider.java | 7 +
.../minesweeper/model/Grid.java | 255 ++++++++++++++++++
src/main/resources/assets/about.html | 21 +-
src/main/resources/assets/langs/en_US.lang | 2 -
src/main/resources/assets/langs/eo.lang | 12 +-
src/main/resources/assets/langs/fr_FR.lang | 4 +-
14 files changed, 315 insertions(+), 277 deletions(-)
create mode 100644 src/main/java/net/darmo_creations/minesweeper/model/CellLabelProvider.java
create mode 100644 src/main/java/net/darmo_creations/minesweeper/model/Grid.java
diff --git a/build.gradle b/build.gradle
index 03c70c5..2e3a8b3 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,7 +1,7 @@
apply plugin: 'java'
apply plugin: 'eclipse'
-version = '1.1'
+version = '1.2'
sourceCompatibility = 1.8
targetCompatibility = 1.8
diff --git a/src/main/java/net/darmo_creations/minesweeper/ConfigTags.java b/src/main/java/net/darmo_creations/minesweeper/ConfigTags.java
index 8f6b8df..76b8eb4 100644
--- a/src/main/java/net/darmo_creations/minesweeper/ConfigTags.java
+++ b/src/main/java/net/darmo_creations/minesweeper/ConfigTags.java
@@ -18,11 +18,9 @@
*/
package net.darmo_creations.minesweeper;
-import net.darmo_creations.gui_framework.config.tags.BooleanTag;
import net.darmo_creations.gui_framework.config.tags.IntegerTag;
public final class ConfigTags {
- public static final BooleanTag SEND_SCORES = new BooleanTag("send_scores");
public static final IntegerTag BUTTONS_SIZE = new IntegerTag("buttons_size");
private ConfigTags() {}
diff --git a/src/main/java/net/darmo_creations/minesweeper/MainController.java b/src/main/java/net/darmo_creations/minesweeper/MainController.java
index 15bef7c..45a72e3 100644
--- a/src/main/java/net/darmo_creations/minesweeper/MainController.java
+++ b/src/main/java/net/darmo_creations/minesweeper/MainController.java
@@ -18,7 +18,6 @@
*/
package net.darmo_creations.minesweeper;
-import java.awt.Color;
import java.awt.Dimension;
import java.awt.Point;
import java.time.Duration;
@@ -26,7 +25,6 @@
import java.util.List;
import java.util.Map;
import java.util.Optional;
-import java.util.Random;
import java.util.TreeMap;
import javax.swing.JOptionPane;
@@ -40,8 +38,9 @@
import net.darmo_creations.minesweeper.events.TimerEvent;
import net.darmo_creations.minesweeper.gui.MainFrame;
import net.darmo_creations.minesweeper.gui.MainFrame.CellLabel;
-import net.darmo_creations.minesweeper.model.Cell;
+import net.darmo_creations.minesweeper.model.CellLabelProvider;
import net.darmo_creations.minesweeper.model.Difficulty;
+import net.darmo_creations.minesweeper.model.Grid;
import net.darmo_creations.minesweeper.model.Score;
import net.darmo_creations.minesweeper.model.Timer;
import net.darmo_creations.utils.I18n;
@@ -52,13 +51,11 @@
*
* @author Damien Vergnet
*/
-public class MainController extends ApplicationController {
- private Cell[][] grid;
+public class MainController extends ApplicationController implements CellLabelProvider {
private Difficulty difficulty;
+ private Grid grid;
private boolean started;
private boolean finished;
- /** Number of remaining flags */
- private int flags;
private Timer timer;
private int lastTime;
private Map> scores;
@@ -96,9 +93,6 @@ public void onUserEvent(UserEvent e) {
case SHOW_BUTTONS_SIZE:
setButtonsSize();
break;
- case TOGGLE_SEND_SCORES:
- this.config.setValue(ConfigTags.SEND_SCORES, !this.config.getValue(ConfigTags.SEND_SCORES));
- break;
case SHOW_SCORES:
this.frame.showScoresDialog(this.scores);
break;
@@ -147,121 +141,31 @@ private void setGameDifficulty(Difficulty difficulty) {
@SubsribeEvent
public void onCellClicked(CellClickedEvent e) {
- Point p = e.getCell().getCoordinates();
if (!this.started) {
- startGame(p);
+ startGame(e.getCell().getCoordinates());
}
- Cell cell = this.grid[p.y][p.x];
- CellLabel label = e.getCell();
-
- if (e.isMainClick() && !cell.isFlagged()) {
- clickCell(cell, label);
- }
- else if (!e.isMainClick()) {
- if (cell.isFlagged()) {
- cell.setMarked(true);
- label.setIcon(Images.MARK);
- if (this.started && !this.finished)
- this.flags++;
- this.frame.setRemainingMines(this.flags);
- }
- else if (cell.isMarked()) {
- cell.setMarked(false);
- label.setIcon(Images.EMPTY_CELL);
+ if (!this.finished) {
+ if (e.isMainClick()) {
+ clickCell(e);
}
else {
- cell.setFlagged(true);
- label.setIcon(Images.FLAG);
- if (this.started && !this.finished)
- this.flags--;
- this.frame.setRemainingMines(this.flags);
+ this.grid.performSecondaryClick(e);
+ this.frame.setRemainingMines(this.grid.getRemainingFlags());
}
}
}
- private void clickCell(Cell cell, CellLabel label) {
- Point p = label.getCoordinates();
- int nearbyMines = getNearbyMinesNumber(p.y, p.x);
- int res = cell.click(nearbyMines);
-
- switch (res) {
- case Cell.NOTHING:
- label.click();
- label.setBackground(new Color(150, 150, 150));
- label.setIcon(Images.NUMBERS[nearbyMines]);
- if (nearbyMines == 0)
- exploreGrid(p.y, p.x, true);
- if (checkVictory()) {
- endGame(true);
- }
- break;
- case Cell.MINE:
- label.click();
- label.setBackground(Color.RED);
- label.setIcon(Images.MINE);
- endGame(false);
- break;
+ private void clickCell(CellClickedEvent event) {
+ int result = this.grid.performMainClick(event);
+ if (result == Grid.WIN) {
+ endGame(true);
}
- }
-
- /**
- * Explores all the non-clicked cells from the given starting cell that don't have any mines
- * nearby.
- *
- * @param row the starting row
- * @param col the starting column
- */
- private void exploreGrid(int row, int col, boolean ignoreClicked) {
- Cell cell = this.grid[row][col];
-
- if ((!ignoreClicked && cell.isClicked()) || cell.isFlagged())
- return;
-
- if (!ignoreClicked)
- clickCell(cell, this.frame.getCell(new Point(col, row)));
-
- if (getNearbyMinesNumber(row, col) == 0) {
- if (row > 0)
- exploreGrid(row - 1, col, false);
- if (row > 0 && col < this.difficulty.getColumns() - 1)
- exploreGrid(row - 1, col + 1, false);
- if (col < this.difficulty.getColumns() - 1)
- exploreGrid(row, col + 1, false);
- if (row < this.difficulty.getRows() - 1 && col < this.difficulty.getColumns() - 1)
- exploreGrid(row + 1, col + 1, false);
- if (row < this.difficulty.getRows() - 1)
- exploreGrid(row + 1, col, false);
- if (row < this.difficulty.getRows() - 1 && col > 0)
- exploreGrid(row + 1, col - 1, false);
- if (col > 0)
- exploreGrid(row, col - 1, false);
- if (row > 0 && col > 0)
- exploreGrid(row - 1, col - 1, false);
+ else if (result == Grid.LOST) {
+ endGame(false);
}
}
- /**
- * Returns {@code true} if the only remaining cells contain mines; false otherwise.
- */
- private boolean checkVictory() {
- if (!this.started)
- return false;
-
- boolean win = true;
-
- loop: for (int row = 0; row < this.difficulty.getRows(); row++) {
- for (int col = 0; col < this.difficulty.getColumns(); col++) {
- if (!this.grid[row][col].isClicked() && !this.grid[row][col].isMine()) {
- win = false;
- break loop;
- }
- }
- }
-
- return win;
- }
-
@SubsribeEvent
public void onTimerEvent(TimerEvent e) {
this.lastTime = e.getHours() * 3600 + e.getMinutes() * 60 + e.getSeconds();
@@ -275,19 +179,11 @@ private void resetGame() {
this.timer = new Timer();
this.started = this.finished = false;
- this.flags = this.difficulty.getMines();
- this.grid = new Cell[this.difficulty.getRows()][this.difficulty.getColumns()];
-
- // Grid generation.
- for (int row = 0; row < this.difficulty.getRows(); row++) {
- for (int col = 0; col < this.difficulty.getColumns(); col++) {
- this.grid[row][col] = new Cell(row, col);
- }
- }
+ this.grid = new Grid(this.difficulty, this);
this.frame.resetGrid(new Dimension(this.difficulty.getColumns(), this.difficulty.getRows()),
this.config.getValue(ConfigTags.BUTTONS_SIZE));
- this.frame.setRemainingMines(this.flags);
+ this.frame.setRemainingMines(this.grid.getRemainingFlags());
this.frame.setTimer(0, 0, 0);
this.frame.updateMenus(false);
this.frame.pack();
@@ -302,20 +198,7 @@ private void resetGame() {
private void startGame(Point clickedCell) {
this.started = true;
this.frame.updateMenus(true);
-
- // Mines generation
- for (int i = 0; i < this.difficulty.getMines(); i++) {
- int col, row;
- Random rand = new Random();
-
- do {
- col = rand.nextInt(this.difficulty.getColumns());
- row = rand.nextInt(this.difficulty.getRows());
- } while (this.grid[row][col].isMine() || row == clickedCell.y && col == clickedCell.x);
-
- this.grid[row][col].setMine(true);
- }
-
+ this.grid.generateMines(clickedCell.y, clickedCell.x);
this.timer.start();
}
@@ -331,20 +214,7 @@ private void endGame(boolean victory) {
this.finished = true;
this.timer.interrupt();
- for (int row = 0; row < this.difficulty.getRows(); row++) {
- for (int col = 0; col < this.difficulty.getColumns(); col++) {
- Cell cell = this.grid[row][col];
- CellLabel label = this.frame.getCell(new Point(col, row));
-
- label.lock();
- if (cell.isMine()) {
- label.setIcon(Images.MINE);
- }
- else if (!cell.isMine() && cell.isFlagged()) {
- label.setIcon(Images.WRONG_MINE);
- }
- }
- }
+ this.grid.endGame();
this.frame.updateMenus(false);
int choice = 0;
@@ -357,8 +227,6 @@ else if (!cell.isMine() && cell.isFlagged()) {
this.scores.get(this.difficulty).add(score);
sortScores(this.difficulty);
- if (this.config.getValue(ConfigTags.SEND_SCORES))
- ScoresDao.getInstance().sendScore(score, this.difficulty);
}
choice = JOptionPane.showConfirmDialog(this.frame, I18n.getLocalizedString("popup.play_again.text"), title, JOptionPane.YES_NO_OPTION,
JOptionPane.QUESTION_MESSAGE);
@@ -372,26 +240,8 @@ else if (!cell.isMine() && cell.isFlagged()) {
resetGame();
}
- /**
- * Returns the number of mines in the 8 adjacent cells to the one at the specified coordinates.
- *
- * @param row the row
- * @param col the column
- *
- * @return the number of nearby mines
- */
- private int getNearbyMinesNumber(int row, int col) {
- return (row > 0 ? booleanToInt(this.grid[row - 1][col].isMine()) : 0) + //
- (col < this.grid[0].length - 1 && row > 0 ? booleanToInt(this.grid[row - 1][col + 1].isMine()) : 0) + //
- (col < this.grid[0].length - 1 ? booleanToInt(this.grid[row][col + 1].isMine()) : 0) + //
- (col < this.grid[0].length - 1 && row < this.grid.length - 1 ? booleanToInt(this.grid[row + 1][col + 1].isMine()) : 0) + //
- (row < this.grid.length - 1 ? booleanToInt(this.grid[row + 1][col].isMine()) : 0) + //
- (col > 0 && row < this.grid.length - 1 ? booleanToInt(this.grid[row + 1][col - 1].isMine()) : 0) + //
- (col > 0 ? booleanToInt(this.grid[row][col - 1].isMine()) : 0) + //
- (col > 0 && row > 0 ? booleanToInt(this.grid[row - 1][col - 1].isMine()) : 0);
- }
-
- private int booleanToInt(boolean value) {
- return value ? 1 : 0;
- }
+ @Override
+ public CellLabel getLabel(int row, int col) {
+ return this.frame.getCell(new Point(col, row));
+ };
}
diff --git a/src/main/java/net/darmo_creations/minesweeper/Minesweeper.java b/src/main/java/net/darmo_creations/minesweeper/Minesweeper.java
index 6218cd4..c9f94fb 100644
--- a/src/main/java/net/darmo_creations/minesweeper/Minesweeper.java
+++ b/src/main/java/net/darmo_creations/minesweeper/Minesweeper.java
@@ -32,9 +32,7 @@
import net.darmo_creations.utils.version.Version;
public class Minesweeper implements Application {
- public static boolean debug;
-
- public static final Version CURRENT_VERSION = new Version(1, 1, 0, false);
+ public static final Version CURRENT_VERSION = new Version(1, 2, 0, false);
@Override
public void preInit() {
@@ -45,7 +43,6 @@ public void preInit() {
ApplicationRegistry.setLanguages(l);
- WritableConfig.registerTag(ConfigTags.SEND_SCORES, true);
WritableConfig.registerTag(ConfigTags.BUTTONS_SIZE, 15);
}
diff --git a/src/main/java/net/darmo_creations/minesweeper/ScoresDao.java b/src/main/java/net/darmo_creations/minesweeper/ScoresDao.java
index e9ce1e2..032de6c 100644
--- a/src/main/java/net/darmo_creations/minesweeper/ScoresDao.java
+++ b/src/main/java/net/darmo_creations/minesweeper/ScoresDao.java
@@ -18,22 +18,15 @@
*/
package net.darmo_creations.minesweeper;
-import java.io.BufferedReader;
-import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.StringReader;
import java.io.UnsupportedEncodingException;
-import java.net.HttpURLConnection;
-import java.net.URL;
import java.net.URLDecoder;
import java.time.Duration;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.StringJoiner;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
@@ -46,9 +39,7 @@
import org.w3c.dom.Document;
import org.w3c.dom.Element;
-import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
-import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import net.darmo_creations.minesweeper.model.Difficulty;
@@ -109,6 +100,11 @@ public Map> load() {
}
scores.put(difficulty, scoresList);
}
+ for (Difficulty diff : Difficulty.values()) {
+ if (!scores.containsKey(diff)) {
+ scores.put(diff, new ArrayList<>());
+ }
+ }
}
}
catch (NullPointerException | ClassCastException | ParserConfigurationException | SAXException | IOException ex) {}
@@ -152,55 +148,5 @@ public void save(Map> scores) {
catch (ParserConfigurationException | TransformerException | UnsupportedEncodingException ex) {}
}
- public boolean sendScore(Score score, Difficulty difficulty) {
- try {
- String url = String.format("http://darmo-creations.%s/products/minesweeper/post_scores.php", Minesweeper.debug ? "local" : "net");
- URL obj = new URL(url);
- HttpURLConnection con = (HttpURLConnection) obj.openConnection();
-
- con.setRequestMethod("POST");
-
- StringJoiner urlParameters = new StringJoiner("&");
- urlParameters.add("username=" + score.getUsername());
- urlParameters.add("time=" + score.getDuration().getSeconds());
- urlParameters.add("difficulty=" + difficulty.name().toLowerCase());
-
- con.setDoOutput(true);
- try (DataOutputStream wr = new DataOutputStream(con.getOutputStream())) {
- wr.writeBytes(urlParameters.toString());
- wr.flush();
- }
-
- if (con.getResponseCode() == 200) {
- try (BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()))) {
- String inputLine;
- StringBuffer response = new StringBuffer();
-
- while ((inputLine = in.readLine()) != null) {
- response.append(inputLine);
- }
-
- DocumentBuilder dBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
- Document doc = dBuilder.parse(new InputSource(new StringReader(response.toString())));
- Element root = (Element) doc.getElementsByTagName("response").item(0);
- if (root != null) {
- Node node = root.getElementsByTagName("code").item(0);
- if (node != null) {
- int code = Integer.parseInt(node.getTextContent());
-
- if (code == 200)
- return true;
- }
- }
- }
- }
-
- return false;
- }
- catch (IOException | SAXException | ParserConfigurationException | NumberFormatException ex) {
- return false;
- }
- }
-
private ScoresDao() {}
}
diff --git a/src/main/java/net/darmo_creations/minesweeper/Start.java b/src/main/java/net/darmo_creations/minesweeper/Start.java
index dfc4d73..9c044b6 100644
--- a/src/main/java/net/darmo_creations/minesweeper/Start.java
+++ b/src/main/java/net/darmo_creations/minesweeper/Start.java
@@ -22,9 +22,6 @@
public class Start {
public static void main(String[] args) {
- Minesweeper.debug = args.length >= 1 && args[0].equalsIgnoreCase("debug");
- if (Minesweeper.debug)
- System.out.println("Debug mode enabled.");
GuiFramework.run(Minesweeper.class);
}
}
diff --git a/src/main/java/net/darmo_creations/minesweeper/events/EventType.java b/src/main/java/net/darmo_creations/minesweeper/events/EventType.java
index 2cefec5..f3bd743 100644
--- a/src/main/java/net/darmo_creations/minesweeper/events/EventType.java
+++ b/src/main/java/net/darmo_creations/minesweeper/events/EventType.java
@@ -28,6 +28,5 @@
public enum EventType implements UserEvent.Type {
NEW_GAME,
SHOW_BUTTONS_SIZE,
- TOGGLE_SEND_SCORES,
SHOW_SCORES;
}
diff --git a/src/main/java/net/darmo_creations/minesweeper/gui/MainFrame.java b/src/main/java/net/darmo_creations/minesweeper/gui/MainFrame.java
index 484f6d2..7984ac5 100644
--- a/src/main/java/net/darmo_creations/minesweeper/gui/MainFrame.java
+++ b/src/main/java/net/darmo_creations/minesweeper/gui/MainFrame.java
@@ -37,7 +37,6 @@
import javax.swing.ButtonGroup;
import javax.swing.ImageIcon;
-import javax.swing.JCheckBoxMenuItem;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
@@ -55,7 +54,6 @@
import net.darmo_creations.gui_framework.config.WritableConfig;
import net.darmo_creations.gui_framework.events.UserEvent;
import net.darmo_creations.gui_framework.gui.ApplicationFrame;
-import net.darmo_creations.minesweeper.ConfigTags;
import net.darmo_creations.minesweeper.Images;
import net.darmo_creations.minesweeper.MainController;
import net.darmo_creations.minesweeper.events.CellClickedEvent;
@@ -76,7 +74,6 @@ public class MainFrame extends ApplicationFrame {
private JMenu difficultyMenu;
private JMenuItem bigButtonsItem;
- private JCheckBoxMenuItem sendScoresItem;
private JLabel remainingLbl, timeLbl;
private JPanel gridPnl;
@@ -151,10 +148,6 @@ protected JMenuBar initJMenuBar(Map listeners, W
optionsMenu.add(this.bigButtonsItem = new JMenuItem(I18n.getLocalizedString("item.settings.text")), 1);
this.bigButtonsItem.setMnemonic(I18n.getLocalizedMnemonic("item.settings"));
this.bigButtonsItem.addActionListener(e -> ApplicationRegistry.EVENTS_BUS.dispatchEvent(new UserEvent(EventType.SHOW_BUTTONS_SIZE)));
- optionsMenu.add(this.sendScoresItem = new JCheckBoxMenuItem(I18n.getLocalizedString("item.send_scores.text"),
- config.getValue(ConfigTags.SEND_SCORES)), 2);
- this.sendScoresItem.setMnemonic(I18n.getLocalizedMnemonic("item.send_scores"));
- this.sendScoresItem.addActionListener(e -> ApplicationRegistry.EVENTS_BUS.dispatchEvent(new UserEvent(EventType.TOGGLE_SEND_SCORES)));
return menubar;
}
@@ -251,11 +244,11 @@ public final class CellLabel extends JLabel {
public static final int PADDING = 4;
private final Point coordinates;
- private boolean clicked;
+ private boolean locked;
private CellLabel(final Point coordinates, int buttonsSize) {
this.coordinates = coordinates;
- this.clicked = false;
+ this.locked = false;
setIcon(Images.EMPTY_CELL);
setBorder(new BevelBorder(BevelBorder.RAISED));
@@ -267,7 +260,7 @@ private CellLabel(final Point coordinates, int buttonsSize) {
addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
- if (!CellLabel.this.clicked && (SwingUtilities.isLeftMouseButton(e) || SwingUtilities.isRightMouseButton(e))) {
+ if (!CellLabel.this.locked && (SwingUtilities.isLeftMouseButton(e) || SwingUtilities.isRightMouseButton(e))) {
ApplicationRegistry.EVENTS_BUS.dispatchEvent(new CellClickedEvent(CellLabel.this, SwingUtilities.isLeftMouseButton(e)));
}
}
@@ -279,12 +272,11 @@ public Point getCoordinates() {
}
public void lock() {
- this.clicked = true;
+ this.locked = true;
}
public void click() {
CellLabel.this.setBorder(new CompoundBorder(new LineBorder(Color.GRAY.darker(), 1), new EmptyBorder(1, 1, 1, 1)));
- CellLabel.this.clicked = true;
}
public void setIcon(ImageIcon imageIcon) {
diff --git a/src/main/java/net/darmo_creations/minesweeper/model/CellLabelProvider.java b/src/main/java/net/darmo_creations/minesweeper/model/CellLabelProvider.java
new file mode 100644
index 0000000..e6c7a4f
--- /dev/null
+++ b/src/main/java/net/darmo_creations/minesweeper/model/CellLabelProvider.java
@@ -0,0 +1,7 @@
+package net.darmo_creations.minesweeper.model;
+
+import net.darmo_creations.minesweeper.gui.MainFrame.CellLabel;
+
+public interface CellLabelProvider {
+ CellLabel getLabel(int row, int col);
+}
diff --git a/src/main/java/net/darmo_creations/minesweeper/model/Grid.java b/src/main/java/net/darmo_creations/minesweeper/model/Grid.java
new file mode 100644
index 0000000..e5501dd
--- /dev/null
+++ b/src/main/java/net/darmo_creations/minesweeper/model/Grid.java
@@ -0,0 +1,255 @@
+package net.darmo_creations.minesweeper.model;
+
+import java.awt.Color;
+import java.awt.Point;
+import java.util.Random;
+
+import net.darmo_creations.minesweeper.Images;
+import net.darmo_creations.minesweeper.events.CellClickedEvent;
+import net.darmo_creations.minesweeper.gui.MainFrame.CellLabel;
+
+public class Grid {
+ private Cell[][] grid;
+ /** Remaining number of flags. */
+ private int flags;
+ private int minesNb;
+ private CellLabelProvider cellLabelProvider;
+
+ public static final int WIN = 1;
+ public static final int LOST = -1;
+
+ public Grid(Difficulty difficulty, CellLabelProvider cellLabelProvider) {
+ this.grid = new Cell[difficulty.getRows()][difficulty.getColumns()];
+ this.minesNb = difficulty.getMines();
+ this.flags = this.minesNb;
+ for (int row = 0; row < difficulty.getRows(); row++) {
+ for (int col = 0; col < difficulty.getColumns(); col++) {
+ this.grid[row][col] = new Cell(row, col);
+ }
+ }
+ this.cellLabelProvider = cellLabelProvider;
+ }
+
+ public int getRemainingFlags() {
+ return this.flags;
+ }
+
+ public int getRows() {
+ return this.grid.length;
+ }
+
+ public int getColumns() {
+ return this.grid[0].length;
+ }
+
+ public void generateMines(int clickedRow, int clickedCol) {
+ Random rand = new Random();
+ for (int i = 0; i < this.minesNb; i++) {
+ int col, row;
+ do {
+ col = rand.nextInt(getColumns());
+ row = rand.nextInt(getRows());
+ } while (this.grid[row][col].isMine() || row == clickedRow && col == clickedCol);
+
+ this.grid[row][col].setMine(true);
+ }
+ }
+
+ public int performMainClick(CellClickedEvent event) {
+ Point p = event.getCell().getCoordinates();
+ int col = p.x, row = p.y;
+ Cell cell = this.grid[row][col];
+ CellLabel label = event.getCell();
+
+ if (!cell.isFlagged()) {
+ int nearbyMines = getNearbyMinesNumber(row, col);
+
+ if (getNearbyFlagsNumber(row, col) == nearbyMines) {
+ if (row > 0)
+ exploreGrid(row - 1, col, false);
+ if (row > 0 && col < getColumns() - 1)
+ exploreGrid(row - 1, col + 1, false);
+ if (col < getColumns() - 1)
+ exploreGrid(row, col + 1, false);
+ if (row < getRows() - 1 && col < getColumns() - 1)
+ exploreGrid(row + 1, col + 1, false);
+ if (row < getRows() - 1)
+ exploreGrid(row + 1, col, false);
+ if (row < getRows() - 1 && col > 0)
+ exploreGrid(row + 1, col - 1, false);
+ if (col > 0)
+ exploreGrid(row, col - 1, false);
+ if (row > 0 && col > 0)
+ exploreGrid(row - 1, col - 1, false);
+ if (checkVictory()) {
+ return WIN;
+ }
+ }
+ else {
+ int res = cell.click(nearbyMines);
+
+ switch (res) {
+ case Cell.NOTHING:
+ label.click();
+ label.setBackground(new Color(150, 150, 150));
+ label.setIcon(Images.NUMBERS[nearbyMines]);
+ if (nearbyMines == 0)
+ exploreGrid(row, col, true);
+ if (checkVictory()) {
+ return WIN;
+ }
+ break;
+ case Cell.MINE:
+ label.click();
+ label.setBackground(Color.RED);
+ label.setIcon(Images.MINE);
+ return LOST;
+ }
+ }
+ }
+ return 0;
+ }
+
+ public void performSecondaryClick(CellClickedEvent event) {
+ Point p = event.getCell().getCoordinates();
+ int col = p.x, row = p.y;
+ Cell cell = this.grid[row][col];
+ CellLabel label = event.getCell();
+
+ if (cell.isFlagged()) {
+ cell.setMarked(true);
+ label.setIcon(Images.MARK);
+ this.flags++;
+ }
+ else if (cell.isMarked()) {
+ cell.setMarked(false);
+ label.setIcon(Images.EMPTY_CELL);
+ }
+ else {
+ cell.setFlagged(true);
+ label.setIcon(Images.FLAG);
+ this.flags--;
+ }
+ }
+
+ /**
+ * Explores all the non-clicked cells from the given starting cell that don't have any mines
+ * nearby.
+ *
+ * @param row the starting row
+ * @param col the starting column
+ * @param ignoreCenter if true, the center cell (the one that was clicked) will be ignored
+ */
+ private void exploreGrid(int row, int col, boolean ignoreCenter) {
+ Cell cell = this.grid[row][col];
+
+ if ((!ignoreCenter && cell.isClicked()) || cell.isFlagged())
+ return;
+
+ if (!ignoreCenter) {
+ int mines = getNearbyMinesNumber(row, col);
+ cell.click(mines);
+ CellLabel label = this.cellLabelProvider.getLabel(row, col);
+ label.click();
+ label.setBackground(new Color(150, 150, 150));
+ label.setIcon(Images.NUMBERS[mines]);
+ }
+
+ if (getNearbyMinesNumber(row, col) == 0) {
+ if (row > 0)
+ exploreGrid(row - 1, col, false);
+ if (row > 0 && col < getColumns() - 1)
+ exploreGrid(row - 1, col + 1, false);
+ if (col < getColumns() - 1)
+ exploreGrid(row, col + 1, false);
+ if (row < getRows() - 1 && col < getColumns() - 1)
+ exploreGrid(row + 1, col + 1, false);
+ if (row < getRows() - 1)
+ exploreGrid(row + 1, col, false);
+ if (row < getRows() - 1 && col > 0)
+ exploreGrid(row + 1, col - 1, false);
+ if (col > 0)
+ exploreGrid(row, col - 1, false);
+ if (row > 0 && col > 0)
+ exploreGrid(row - 1, col - 1, false);
+ }
+ }
+
+ public void endGame() {
+ for (int row = 0; row < getRows(); row++) {
+ for (int col = 0; col < getColumns(); col++) {
+ Cell cell = this.grid[row][col];
+ CellLabel label = this.cellLabelProvider.getLabel(row, col);
+
+ label.lock();
+ if (cell.isMine()) {
+ label.setIcon(Images.MINE);
+ }
+ else if (!cell.isMine() && cell.isFlagged()) {
+ label.setIcon(Images.WRONG_MINE);
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns {@code true} if the only remaining cells contain mines; false otherwise.
+ */
+ private boolean checkVictory() {
+ boolean win = true;
+
+ loop: for (int row = 0; row < getRows(); row++) {
+ for (int col = 0; col < getColumns(); col++) {
+ if (!this.grid[row][col].isClicked() && !this.grid[row][col].isMine()) {
+ win = false;
+ break loop;
+ }
+ }
+ }
+
+ return win;
+ }
+
+ /**
+ * Returns the number of mines in the 8 adjacent cells to the one at the specified coordinates.
+ *
+ * @param row the row
+ * @param col the column
+ *
+ * @return the number of nearby mines
+ */
+ private int getNearbyMinesNumber(int row, int col) {
+ return (row > 0 ? booleanToInt(this.grid[row - 1][col].isMine()) : 0) + //
+ (col < this.grid[0].length - 1 && row > 0 ? booleanToInt(this.grid[row - 1][col + 1].isMine()) : 0) + //
+ (col < this.grid[0].length - 1 ? booleanToInt(this.grid[row][col + 1].isMine()) : 0) + //
+ (col < this.grid[0].length - 1 && row < this.grid.length - 1 ? booleanToInt(this.grid[row + 1][col + 1].isMine()) : 0) + //
+ (row < this.grid.length - 1 ? booleanToInt(this.grid[row + 1][col].isMine()) : 0) + //
+ (col > 0 && row < this.grid.length - 1 ? booleanToInt(this.grid[row + 1][col - 1].isMine()) : 0) + //
+ (col > 0 ? booleanToInt(this.grid[row][col - 1].isMine()) : 0) + //
+ (col > 0 && row > 0 ? booleanToInt(this.grid[row - 1][col - 1].isMine()) : 0);
+ }
+
+ /**
+ * Returns the number of flagged cells in the 8 adjacent cells to the one at the specified
+ * coordinates.
+ *
+ * @param row the row
+ * @param col the column
+ *
+ * @return the number of nearby flagged cells
+ */
+ private int getNearbyFlagsNumber(int row, int col) {
+ return (row > 0 ? booleanToInt(this.grid[row - 1][col].isFlagged()) : 0) + //
+ (col < this.grid[0].length - 1 && row > 0 ? booleanToInt(this.grid[row - 1][col + 1].isFlagged()) : 0) + //
+ (col < this.grid[0].length - 1 ? booleanToInt(this.grid[row][col + 1].isFlagged()) : 0) + //
+ (col < this.grid[0].length - 1 && row < this.grid.length - 1 ? booleanToInt(this.grid[row + 1][col + 1].isFlagged()) : 0) + //
+ (row < this.grid.length - 1 ? booleanToInt(this.grid[row + 1][col].isFlagged()) : 0) + //
+ (col > 0 && row < this.grid.length - 1 ? booleanToInt(this.grid[row + 1][col - 1].isFlagged()) : 0) + //
+ (col > 0 ? booleanToInt(this.grid[row][col - 1].isFlagged()) : 0) + //
+ (col > 0 && row > 0 ? booleanToInt(this.grid[row - 1][col - 1].isFlagged()) : 0);
+ }
+
+ private int booleanToInt(boolean value) {
+ return value ? 1 : 0;
+ }
+}
diff --git a/src/main/resources/assets/about.html b/src/main/resources/assets/about.html
index 72a54f8..434354e 100644
--- a/src/main/resources/assets/about.html
+++ b/src/main/resources/assets/about.html
@@ -1,16 +1,16 @@
-
+
Minesweeper
- http://darmo-creations.net/product.php?p=minesweeper
+ See on my website
- Version: Release (1.1)
- © Copyright Damien Vergnet 2017.
- Unicode Converter is free software: you can redistribute it and/or modify
+ Version: Release (1.2)
+ © Copyright Damien Vergnet 2017-2019.
+ Minesweeper is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
@@ -22,11 +22,14 @@
Minesweeper
GNU General Public License for more details.
- You should have received a copy of the GNU General Public License along with Minesweeper. If not,
- see http://www.gnu.org/licenses/.
+ You should have received a copy of the GNU General Public License along with
+ Minesweeper. If not, see
+ http://www.gnu.org/licenses/.
- Third party library: GUI-Framework by me.
+ Third party library:
+ GUI-Framework
+ by me.
-
\ No newline at end of file
+