9 Commits

Author SHA1 Message Date
2827d79556 Win condition, flagging underflow fix 2024-07-23 18:54:23 +01:00
shr4pnel
367ad5921a Update README.md 2024-06-16 11:20:28 +01:00
shr4pnel
fd82088a08 Update README.md 2024-06-16 11:19:48 +01:00
8e836b1183 fix button glow 2024-06-16 10:36:27 +01:00
44125ce13a Add not yet implemented messages 2024-06-16 10:31:10 +01:00
76b44d22cf clarify mvn reqs in readme 2024-06-16 10:21:58 +01:00
d19f9899cf Fix initial bomb moving adjacency logic 2024-06-16 10:20:40 +01:00
shr4pnel
7e536cf644 Update README.md 2024-06-11 23:44:27 +01:00
shr4pnel
f5c63f9401 Update README.md 2024-06-10 22:51:41 +01:00
4 changed files with 84 additions and 13 deletions

View File

@@ -2,6 +2,9 @@
"named this way because i didn't realise there was another libremines!" "named this way because i didn't realise there was another libremines!"
![Libremines screenshot](https://github.com/shrapnelnet/libremines/assets/133451255/f19e0006-a587-4f39-8626-67606db7cd58)
## Building ## Building
### Requirements: ### Requirements:
@@ -11,14 +14,13 @@
### Instructions: ### Instructions:
Optional, create maven wrapper: Create the maven wrapper. gluonfx does not like versions of maven that are not 3.8.8:
```shell ```shell
mvn wrapper:wrapper mvn wrapper:wrapper -Dmaven=3.8.8
``` ```
This is helpful for version management, in case you have an incompatible version of maven installed, but most people will not need this. This is helpful for version management, in case you have an incompatible version of maven installed.
Install dependencies: Install dependencies:
```shell ```shell
@@ -31,4 +33,4 @@ Build and run:
GRAALVM_HOME=/path/to/graalvm ./mvnw gluonfx:build GRAALVM_HOME=/path/to/graalvm ./mvnw gluonfx:build
``` ```
A native binary should be placed at `target/gluonfx/<your architecture>/libremines` A native binary should be placed at `target/gluonfx/<your architecture>/libremines`

View File

@@ -4,9 +4,11 @@ import java.net.URL;
import java.util.Timer; import java.util.Timer;
import java.util.TimerTask; import java.util.TimerTask;
import javafx.event.ActionEvent;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.scene.Node; import javafx.scene.Node;
import javafx.scene.control.Button; import javafx.scene.control.Button;
import javafx.scene.control.RadioMenuItem;
import javafx.scene.image.Image; import javafx.scene.image.Image;
import javafx.scene.image.ImageView; import javafx.scene.image.ImageView;
import javafx.scene.input.MouseButton; import javafx.scene.input.MouseButton;
@@ -20,6 +22,9 @@ public class Controller {
@FXML @FXML
private ImageView smiley, time_1, time_2, time_3, bomb_2, bomb_3; private ImageView smiley, time_1, time_2, time_3, bomb_2, bomb_3;
@FXML
private RadioMenuItem color, marks;
private Grid gridHandler; private Grid gridHandler;
private GridWrapper wrapper; private GridWrapper wrapper;
private boolean gameOver = false; private boolean gameOver = false;
@@ -33,12 +38,18 @@ public class Controller {
@FXML @FXML
private void initialize() { private void initialize() {
setNotYetImplemented(color);
setNotYetImplemented(marks);
setupGrid(); setupGrid();
gridHandler = new Grid(); gridHandler = new Grid();
wrapper = gridHandler.grid; wrapper = gridHandler.grid;
expandedTiles = new boolean[30][16]; expandedTiles = new boolean[30][16];
} }
private void setNotYetImplemented(RadioMenuItem node) {
node.setOnAction((ActionEvent e) -> System.out.println("https://http.cat/images/501.jpg"));
}
private void setupGrid() { private void setupGrid() {
for (int column = 0; column < 30; ++column) { for (int column = 0; column < 30; ++column) {
for (int row = 0; row < 16; ++row) { for (int row = 0; row < 16; ++row) {
@@ -109,6 +120,33 @@ public class Controller {
return null; return null;
} }
private String getButtonURL(Button button) {
ImageView graphic = (ImageView) button.getGraphic();
Image image = graphic.getImage();
return image.getUrl();
}
private int numberOfUnrevealedTiles() {
int unrevealedTiles = 0;
int column, row;
for (column = 0; column < 30; ++column) {
for (row = 0; row < 16; ++row) {
Button current = (Button) getNodeByRowColumnIndex(row, column);
assert current != null;
String currentURL = getButtonURL(current);
if (currentURL.contains("blank.png") || currentURL.contains("flag.png")) {
unrevealedTiles++;
}
}
}
return unrevealedTiles;
}
private boolean checkWinCondition() {
int unrevealedTiles = numberOfUnrevealedTiles();
return unrevealedTiles == 99;
}
private void handlePrimaryClick(Button clicked, int column, int row) { private void handlePrimaryClick(Button clicked, int column, int row) {
if (wrapper.atColumn(column).atRow(row).isBomb() && !isFirstClick) { if (wrapper.atColumn(column).atRow(row).isBomb() && !isFirstClick) {
gameOver(clicked); gameOver(clicked);
@@ -122,14 +160,18 @@ public class Controller {
int rowMovedTo = chosenColumnAndRow[1]; int rowMovedTo = chosenColumnAndRow[1];
wrapper.atColumn(column).atRow(row).switchBomb(columnMovedTo, rowMovedTo); wrapper.atColumn(column).atRow(row).switchBomb(columnMovedTo, rowMovedTo);
recursiveExpandTiles(column, row); recursiveExpandTiles(column, row);
isFirstClick = false; clicked = (Button) getNodeByRowColumnIndex(row, column);
} }
isFirstClick = false;
int adjacentBombs = wrapper.adjacentBombCount(); int adjacentBombs = wrapper.adjacentBombCount();
setAdjacentCount(clicked, adjacentBombs); setAdjacentCount(clicked, adjacentBombs);
if (adjacentBombs == 0) { if (adjacentBombs == 0) {
recursiveExpandTiles(column, row); recursiveExpandTiles(column, row);
} }
boolean win = checkWinCondition();
if (win) {
win();
}
} }
private void recursiveExpandTiles(int column, int row) { private void recursiveExpandTiles(int column, int row) {
@@ -213,6 +255,28 @@ public class Controller {
showAllBombs(GridPane.getColumnIndex(tileClicked), GridPane.getRowIndex(tileClicked)); showAllBombs(GridPane.getColumnIndex(tileClicked), GridPane.getRowIndex(tileClicked));
} }
private void win() {
gameOver = true;
timer.cancel();
setImage(smiley, "img/face_win.png");
flagAllRemaining();
}
private void flagAllRemaining() {
int column, row;
for (column = 0; column < 30; ++column) {
for (row = 0; row < 16; ++row) {
Button current = (Button) getNodeByRowColumnIndex(row, column);
assert current != null;
String currentURL = getButtonURL(current);
boolean tileIsBomb = wrapper.atColumn(column).atRow(row).isBomb();
if (currentURL.contains("blank.png") && tileIsBomb) {
setImage(current, "img/bomb_flagged.png");
}
}
}
}
private void flag(Node tileClicked) { private void flag(Node tileClicked) {
Button tileAsButton = (Button) tileClicked; Button tileAsButton = (Button) tileClicked;
ImageView tileGraphic = (ImageView) tileAsButton.getGraphic(); ImageView tileGraphic = (ImageView) tileAsButton.getGraphic();
@@ -229,7 +293,8 @@ public class Controller {
return; return;
} }
bombCount--; bombCount--;
updateBombCounter(); if (bombCount > 0)
updateBombCounter();
setImage((Button) tileClicked, "img/bomb_flagged.png"); setImage((Button) tileClicked, "img/bomb_flagged.png");
} }
@@ -280,8 +345,8 @@ public class Controller {
String buttonURL = ((ImageView) b.getGraphic()).getImage().getUrl(); String buttonURL = ((ImageView) b.getGraphic()).getImage().getUrl();
int column = GridPane.getColumnIndex(node); int column = GridPane.getColumnIndex(node);
int row = GridPane.getRowIndex(node); int row = GridPane.getRowIndex(node);
if (!(column == clickedColumn && row == clickedRow) && // if the tile isn't the one that was clicked AND the tile is a bomb
wrapper.atColumn(column).atRow(row).isBomb()) { if (!(column == clickedColumn && row == clickedRow) && wrapper.atColumn(column).atRow(row).isBomb()) {
setImage((Button) node, "img/bomb_revealed.png"); setImage((Button) node, "img/bomb_revealed.png");
} }
if (buttonURL.contains("flagged.png") && !wrapper.atColumn(column).atRow(row).isBomb()) { if (buttonURL.contains("flagged.png") && !wrapper.atColumn(column).atRow(row).isBomb()) {

View File

@@ -0,0 +1,4 @@
* {
-fx-focus-color: transparent;
-fx-faint-focus-color: transparent;
}

View File

@@ -6,7 +6,7 @@
<?import javafx.scene.layout.*?> <?import javafx.scene.layout.*?>
<?import javafx.scene.shape.*?> <?import javafx.scene.shape.*?>
<VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="350.0" prefWidth="500.0" style="-fx-border-color: black; -fx-border-width: 1px;" xmlns="http://javafx.com/javafx/11.0.14-internal" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.shr4pnel.minesweeper.Controller"> <VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="350.0" prefWidth="500.0" style="-fx-border-color: black; -fx-border-width: 1px;" stylesheets="@fix-glow.css" xmlns="http://javafx.com/javafx/17.0.2-ea" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.shr4pnel.minesweeper.Controller">
<children> <children>
<MenuBar style="-fx-background-color: white;"> <MenuBar style="-fx-background-color: white;">
<menus> <menus>
@@ -26,8 +26,8 @@
<RadioMenuItem mnemonicParsing="false" text="Internmediate" toggleGroup="$difficulty" /> <RadioMenuItem mnemonicParsing="false" text="Internmediate" toggleGroup="$difficulty" />
<RadioMenuItem mnemonicParsing="false" selected="true" text="Expert" toggleGroup="$difficulty" /> <RadioMenuItem mnemonicParsing="false" selected="true" text="Expert" toggleGroup="$difficulty" />
<SeparatorMenuItem mnemonicParsing="false" /> <SeparatorMenuItem mnemonicParsing="false" />
<RadioMenuItem mnemonicParsing="false" selected="true" text="Marks (?)" /> <RadioMenuItem fx:id="marks" mnemonicParsing="false" selected="true" text="Marks (?)" />
<RadioMenuItem mnemonicParsing="false" selected="true" text="Color" /> <RadioMenuItem fx:id="color" mnemonicParsing="false" selected="true" text="Color" />
<SeparatorMenuItem mnemonicParsing="false" /> <SeparatorMenuItem mnemonicParsing="false" />
<MenuItem mnemonicParsing="false" style="-fx-padding-right: 15; -fx-border-insets: 10px; -fx-background-insets: 10px;" text="Best Times..." /> <MenuItem mnemonicParsing="false" style="-fx-padding-right: 15; -fx-border-insets: 10px; -fx-background-insets: 10px;" text="Best Times..." />
<SeparatorMenuItem mnemonicParsing="false" /> <SeparatorMenuItem mnemonicParsing="false" />