Compare commits
No commits in common. "master" and "1.4.0" have entirely different histories.
22
README.md
22
README.md
@ -8,23 +8,11 @@
|
||||
|
||||
### Requirements:
|
||||
- Java 21
|
||||
- Maven
|
||||
- Maven 3.8.8 or lower
|
||||
- GraalVM 21
|
||||
|
||||
### Instructions:
|
||||
|
||||
This will automatically fetch dependencies and run the project:
|
||||
|
||||
```shell
|
||||
mvn javafx:run
|
||||
```
|
||||
|
||||
|
||||
#### Native Image:
|
||||
|
||||
##### Requirements:
|
||||
- GraalVM
|
||||
- Maven 3.8.8
|
||||
|
||||
Create the maven wrapper at version 3.8.8. GluonFX does not work with newer versions as of 2024-07-27.
|
||||
|
||||
```shell
|
||||
@ -38,3 +26,9 @@ Install dependencies, build and run:
|
||||
```
|
||||
|
||||
A native binary should be placed at `target/gluonfx/<your architecture>/libremines`
|
||||
|
||||
Alternatively, to run using the JVM, use:
|
||||
|
||||
```shell
|
||||
./mvnw javafx:run
|
||||
```
|
||||
|
@ -2,7 +2,6 @@ package com.shr4pnel.minesweeper;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
@ -23,11 +22,6 @@ import javafx.scene.input.MouseEvent;
|
||||
import javafx.scene.layout.GridPane;
|
||||
import javafx.stage.Stage;
|
||||
|
||||
/**
|
||||
* The JavaFX controller for minesweeper.fxml.
|
||||
* Handles all operations performed within the GUI.
|
||||
* @author shrapnelnet admin@shr4pnel.com
|
||||
*/
|
||||
public class Controller {
|
||||
/**
|
||||
* The grid within the FXML holding all the tiles.
|
||||
@ -127,8 +121,6 @@ public class Controller {
|
||||
Parent root = FXMLLoader.load(getClass().getResource("about.fxml"));
|
||||
Stage stage = new Stage();
|
||||
stage.setTitle("About");
|
||||
stage.setMinWidth(455);
|
||||
stage.setMinHeight(275);
|
||||
stage.setScene(new Scene(root));
|
||||
stage.show();
|
||||
} catch (IOException e) {
|
||||
@ -149,8 +141,6 @@ public class Controller {
|
||||
Parent root = FXMLLoader.load(getClass().getResource("unimplemented.fxml"));
|
||||
Stage stage = new Stage();
|
||||
stage.setTitle("Unimplemented!");
|
||||
stage.setMinHeight(420);
|
||||
stage.setMinWidth(525);
|
||||
stage.setScene(new Scene(root));
|
||||
stage.show();
|
||||
} catch (IOException e) {
|
||||
@ -237,70 +227,25 @@ public class Controller {
|
||||
if (buttonImage.getUrl().contains("flagged.png")) {
|
||||
return;
|
||||
}
|
||||
if (buttonImage.getUrl().contains("num") && !buttonImage.getUrl().contains("num_0.png")) {
|
||||
chord(clicked, buttonImage.getUrl());
|
||||
return;
|
||||
}
|
||||
// if (buttonImage.getUrl().contains("num") && !buttonImage.getUrl().contains("num_0.png")) {
|
||||
// chord(clicked, buttonImage.getUrl());
|
||||
// return;
|
||||
// }
|
||||
handlePrimaryClick(clicked, column, row);
|
||||
}
|
||||
|
||||
private int adjacentFlagCount(int column, int row) {
|
||||
int flagCount = 0;
|
||||
for (int innerColumn = column - 1; innerColumn <= column + 1; innerColumn++) {
|
||||
for (int innerRow = row - 1; innerRow <= row + 1; innerRow++) {
|
||||
if (innerColumn == column && innerRow == row)
|
||||
continue;
|
||||
|
||||
if (!wrapper.atColumn(innerColumn).atRow(innerRow).isValid()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Button b = (Button) getNodeByColumnRowIndex(innerColumn, innerRow);
|
||||
String bURL = getButtonURL(b);
|
||||
if (bURL.contains("flag")) {
|
||||
flagCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return flagCount;
|
||||
}
|
||||
|
||||
private Button[] chordTilesToOpen(int column, int row) {
|
||||
ArrayList<Button> buttons = new ArrayList<>(8);
|
||||
for (int innerColumn = column - 1; innerColumn <= column + 1; innerColumn++) {
|
||||
for (int innerRow = row - 1; innerRow <= row + 1; innerRow++) {
|
||||
if (innerColumn == column && innerRow == row)
|
||||
continue;
|
||||
|
||||
if (!wrapper.atColumn(innerColumn).atRow(innerRow).isValid()) {
|
||||
continue;
|
||||
}
|
||||
Button b = (Button) getNodeByColumnRowIndex(innerColumn, innerRow);
|
||||
String bURL = getButtonURL(b);
|
||||
// if the tile is unflagged and unopened:
|
||||
if (bURL.contains("blank")) {
|
||||
buttons.add(b);
|
||||
}
|
||||
}
|
||||
}
|
||||
return buttons.toArray(Button[]::new);
|
||||
}
|
||||
|
||||
private void chord(Button clicked, String clickedURL) {
|
||||
int column = GridPane.getColumnIndex(clicked);
|
||||
int row = GridPane.getRowIndex(clicked);
|
||||
int urlLength = clickedURL.length();
|
||||
char requiredAdjacentChar = clickedURL.charAt(urlLength-5);
|
||||
int requiredAdjacent = Integer.parseInt(Character.toString(requiredAdjacentChar));
|
||||
int actualAdjacent = adjacentFlagCount(column, row);
|
||||
if (requiredAdjacent != actualAdjacent) {
|
||||
return;
|
||||
}
|
||||
Button[] buttons = chordTilesToOpen(column, row);
|
||||
for (Button b: buttons) {
|
||||
expandTile(b);
|
||||
}
|
||||
}
|
||||
// private void chord(Button clicked, String clickedURL) {
|
||||
// int column = GridPane.getColumnIndex(clicked);
|
||||
// int row = GridPane.getRowIndex(clicked);
|
||||
// int urlLength = clickedURL.length();
|
||||
// char expectedAdjacentChar = clickedURL.charAt(urlLength-5);
|
||||
// int expectedAdjacent = Integer.parseInt(Character.toString(expectedAdjacentChar));
|
||||
// int actualAdjacent = wrapper.atColumn(column).atRow(row).adjacentBombCount();
|
||||
// if (expectedAdjacent != actualAdjacent) {
|
||||
// return;
|
||||
// }
|
||||
// // chord logic
|
||||
// }
|
||||
|
||||
/**
|
||||
* If the first tile clicked was a bomb, move that bomb to the first available column on row 0 before opening it
|
||||
@ -319,8 +264,8 @@ public class Controller {
|
||||
}
|
||||
}
|
||||
}
|
||||
System.out.println("If you see this, congrats! this should never happen. please email me.");
|
||||
return new int[]{0, 0};
|
||||
// this realistically should never happen... sorry!
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -336,8 +281,7 @@ public class Controller {
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates through the grid to find all tiles that are either blank or flagged.
|
||||
* Used to check if win condition has been met
|
||||
* Iterates through the grid to find all tiles that are either blank or flagged
|
||||
*
|
||||
* @return The number of tiles that were blank or flagged
|
||||
*/
|
||||
@ -346,11 +290,8 @@ public class Controller {
|
||||
int column, row;
|
||||
for (column = 0; column < 30; ++column) {
|
||||
for (row = 0; row < 16; ++row) {
|
||||
Button current = (Button) getNodeByColumnRowIndex(column, row);
|
||||
// this will hopefully never happen..... or will it ......
|
||||
if (current == null) {
|
||||
continue;
|
||||
}
|
||||
Button current = (Button) getNodeByRowColumnIndex(row, column);
|
||||
assert current != null;
|
||||
String currentURL = getButtonURL(current);
|
||||
if (currentURL.contains("blank.png") || currentURL.contains("flagged.png")) {
|
||||
unrevealedTiles++;
|
||||
@ -385,11 +326,13 @@ public class Controller {
|
||||
}
|
||||
if (wrapper.isBomb() && isFirstClick) {
|
||||
int[] chosenColumnAndRow = setBombIfFirstTileIsBomb(column, row);
|
||||
// assertions are evil but i dont care
|
||||
assert chosenColumnAndRow != null;
|
||||
int columnMovedTo = chosenColumnAndRow[0];
|
||||
int rowMovedTo = chosenColumnAndRow[1];
|
||||
wrapper.atColumn(column).atRow(row).switchBomb(columnMovedTo, rowMovedTo);
|
||||
recursiveExpandTiles(column, row);
|
||||
clicked = (Button) getNodeByColumnRowIndex(column, row);
|
||||
clicked = (Button) getNodeByRowColumnIndex(row, column);
|
||||
}
|
||||
isFirstClick = false;
|
||||
int adjacentBombs = wrapper.adjacentBombCount();
|
||||
@ -404,7 +347,7 @@ public class Controller {
|
||||
}
|
||||
|
||||
/**
|
||||
* If a tile is surrounded by tiles with no adjacent bombs, open all the tiles recursively
|
||||
* If a tile is surrounded by tiles with no adjacent bombs, open all of the tiles recursively
|
||||
*
|
||||
* @param column The column that was clicked
|
||||
* @param row The row that was clicked
|
||||
@ -435,7 +378,7 @@ public class Controller {
|
||||
* @param row The row of the button to open
|
||||
*/
|
||||
private void expandTile(int column, int row) {
|
||||
Node tile = getNodeByColumnRowIndex(column, row);
|
||||
Node tile = getNodeByRowColumnIndex(row, column);
|
||||
if (tile != null) {
|
||||
Button button = (Button) tile;
|
||||
if (button.isVisible()) {
|
||||
@ -449,23 +392,6 @@ public class Controller {
|
||||
}
|
||||
}
|
||||
|
||||
private void expandTile(Button button) {
|
||||
Node node = (Node) button;
|
||||
int column = GridPane.getColumnIndex(node);
|
||||
int row = GridPane.getRowIndex(node);
|
||||
if (wrapper.atColumn(column).atRow(row).isBomb()) {
|
||||
gameOver(node);
|
||||
}
|
||||
if (button.isVisible()) {
|
||||
int adjacentBombs = wrapper.atColumn(column).atRow(row).adjacentBombCount();
|
||||
setAdjacentCount(button, adjacentBombs);
|
||||
if (adjacentBombs == 0 && !expandedTiles[column][row]) {
|
||||
expandedTiles[column][row] = true;
|
||||
recursiveExpandTiles(column, row);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates over the grid and returns the node when it's at the correct child
|
||||
*
|
||||
@ -473,7 +399,7 @@ public class Controller {
|
||||
* @param column The desired column to find
|
||||
* @return The node at the specified position
|
||||
*/
|
||||
private Node getNodeByColumnRowIndex(int column, int row) {
|
||||
private Node getNodeByRowColumnIndex(int row, int column) {
|
||||
for (Node node : grid.getChildren()) {
|
||||
if (GridPane.getRowIndex(node) == row && GridPane.getColumnIndex(node) == column) {
|
||||
return node;
|
||||
@ -556,7 +482,7 @@ public class Controller {
|
||||
int column, row;
|
||||
for (column = 0; column < 30; ++column) {
|
||||
for (row = 0; row < 16; ++row) {
|
||||
Button current = (Button) getNodeByColumnRowIndex(column, row);
|
||||
Button current = (Button) getNodeByRowColumnIndex(row, column);
|
||||
assert current != null;
|
||||
String currentURL = getButtonURL(current);
|
||||
boolean tileIsBomb = wrapper.atColumn(column).atRow(row).isBomb();
|
||||
@ -571,7 +497,7 @@ public class Controller {
|
||||
* Create a flag on a tile, representing a tile that the user suspects has a bomb behind it.
|
||||
* This prevents the tile being clicked on.
|
||||
*
|
||||
* @param tileClicked The tile that the user clicks.
|
||||
* @param tileClicked
|
||||
*/
|
||||
private void flag(Node tileClicked) {
|
||||
Button tileAsButton = (Button) tileClicked;
|
||||
|
@ -2,14 +2,7 @@ package com.shr4pnel.minesweeper;
|
||||
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
/**
|
||||
* Used to fill the GridWrapper with bombs. Planned to merge into GridWrapper.
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class Grid {
|
||||
/**
|
||||
* An instance of GridWrapper used to check generation of bombs.
|
||||
*/
|
||||
final GridWrapper grid = new GridWrapper();
|
||||
|
||||
/**
|
||||
|
@ -1,9 +1,5 @@
|
||||
package com.shr4pnel.minesweeper;
|
||||
|
||||
/**
|
||||
* Simplifies operations on the bomb array. Preventing direct access leads to cleaner code.
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class GridWrapper {
|
||||
/**
|
||||
* Number of columns.
|
||||
@ -159,13 +155,4 @@ public class GridWrapper {
|
||||
private boolean isValid(int column, int row) {
|
||||
return column >= 0 && column < COLUMNS && row >= 0 && row < ROWS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given column and row is in bounds using local state
|
||||
*
|
||||
* @return A boolean representing the tile being in bounds.
|
||||
*/
|
||||
public boolean isValid() {
|
||||
return this.currentColumn >= 0 && this.currentColumn < COLUMNS && this.currentRow >= 0 && this.currentRow < ROWS;
|
||||
}
|
||||
}
|
||||
|
@ -9,10 +9,6 @@ import javafx.scene.Scene;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.stage.Stage;
|
||||
|
||||
/**
|
||||
* The application opening point, used to bootstrap JavaFX and open to the GUI.
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class Main extends Application {
|
||||
/**
|
||||
* JavaFX opening method. Creates the stage and bootstraps the application.
|
||||
|
@ -1,8 +1,3 @@
|
||||
/**
|
||||
* The main module for the program. Required to launch to JavaFX as well as bundle into a native binary.
|
||||
* @author shrapnelnet admin@shr4pnel.com
|
||||
* @since 1.0.0
|
||||
*/
|
||||
module libremines {
|
||||
requires javafx.controls;
|
||||
requires javafx.fxml;
|
||||
|
Loading…
x
Reference in New Issue
Block a user