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 + diff --git a/src/main/resources/assets/langs/en_US.lang b/src/main/resources/assets/langs/en_US.lang index 82fdae8..64cad69 100644 --- a/src/main/resources/assets/langs/en_US.lang +++ b/src/main/resources/assets/langs/en_US.lang @@ -31,8 +31,6 @@ item.check_updates.text=Check for updates on startup item.check_updates.mnemonic=u item.settings.text=Settings… item.settings.mnemonic=s -item.send_scores.text=Send scores to server -item.send_scores.mnemonic=e menu.lang.text=Language menu.lang.mnemonic=l menu.help.text=Help diff --git a/src/main/resources/assets/langs/eo.lang b/src/main/resources/assets/langs/eo.lang index a3c31e7..af423f1 100644 --- a/src/main/resources/assets/langs/eo.lang +++ b/src/main/resources/assets/langs/eo.lang @@ -1,6 +1,6 @@ dialog.scores.title=Plej bonaj poentaroj dialog.settings.title=Agordoj -dialog.about.title=Pri +dialog.about.title=Pri la ludo dialog.update.title=Ĝisdatigo havebla popup.laf_error.title=Eraro @@ -8,7 +8,7 @@ popup.laf_error.text=Ne povis ŝarĝi sistemon GUI. Uzante defaŭlta. popup.confirm.title=Konfirmi popup.victory.title=Venko! popup.victory.text=Vi venkis! -popup.game_over.title=La ludo finiĝis! +popup.game_over.title=La ludo estas finita! popup.game_over.text=Vi perdis! popup.enter_name.text=Tajpu nomon: popup.play_again.text=Ĉu vi volas reludi? @@ -31,13 +31,11 @@ item.check_updates.text=Kontroli ĝisdatigoj al la programoinicio item.check_updates.mnemonic=k item.settings.text=Agordoj… item.settings.mnemonic=b -item.send_scores.text=Sendi poentaroj al la servilo -item.send_scores.mnemonic=s menu.lang.text=Lingvo menu.lang.mnemonic=l menu.help.text=Helpo menu.help.mnemonic=h -item.about.text=Pri +item.about.text=Pri la ludo item.about.mnemonic=p label.mines.text=Mino @@ -45,13 +43,13 @@ label.username.text=Pseŭdonomo label.time.text=Tempo label.buttons_size.text=Dimensioj de la butonoj -label.updates_check_blocked.text=Kontrolo de ĝisdatigoj malaktivitita +label.updates_check_blocked.text=Kontrolo de ĝisdatigoj malaktivitata label.checking_updates.text=Kontrolanta ĝisdatigoj label.update_check_failed.text=Kontrolo de ĝisdatigoj malsukcesis label.update_available.text=Ĝisdatigo havebla label.no_update.text=Neniom ĝisdatigo havebla label.download_link.text=Elŝutu tien: -label.changelog.text=Changelog: +label.changelog.text=Ŝanĝoj: button.close.text=Fermi button.validate.text=Validigi diff --git a/src/main/resources/assets/langs/fr_FR.lang b/src/main/resources/assets/langs/fr_FR.lang index 7c016eb..426db4e 100644 --- a/src/main/resources/assets/langs/fr_FR.lang +++ b/src/main/resources/assets/langs/fr_FR.lang @@ -31,8 +31,6 @@ item.check_updates.text=Vérifier les mises à jour au démarrage item.check_updates.mnemonic=v item.settings.text=Paramètres… item.settings.mnemonic=p -item.send_scores.text=Envoyer les scores au serveur -item.send_scores.mnemonic=e menu.lang.text=Langue menu.lang.mnemonic=l menu.help.text=Aide @@ -51,7 +49,7 @@ label.update_check_failed.text=La vérification des mises à jour a échoué label.update_available.text=Mise à jour disponible label.no_update.text=Aucune mise à jour disponible label.download_link.text=Lien de téléchargement : -label.changelog.text=Changelog : +label.changelog.text=Changements : button.close.text=Fermer button.validate.text=Valider