330 lines
11 KiB
Java

package com.shr4pnel.minesweeper;
import java.net.URL;
import java.util.Timer;
import java.util.TimerTask;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.RadioMenuItem;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.GridPane;
public class Controller {
@FXML
private GridPane grid;
@FXML
private ImageView smiley, time_1, time_2, time_3, bomb_2, bomb_3;
@FXML
private RadioMenuItem color, marks;
private Grid gridHandler;
private GridWrapper wrapper;
private boolean gameOver = false;
private boolean isFirstLoad = true;
static Timer timer = new Timer();
private int time = 0;
private long startTime;
private int bombCount = 99;
private boolean[][] expandedTiles;
private boolean isFirstClick = true;
@FXML
private void initialize() {
setNotYetImplemented(color);
setNotYetImplemented(marks);
setupGrid();
gridHandler = new Grid();
wrapper = gridHandler.grid;
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() {
for (int column = 0; column < 30; ++column) {
for (int row = 0; row < 16; ++row) {
Button blankButton = createBlankButton();
grid.add(blankButton, column, row);
}
}
}
private Button createBlankButton() {
Image blank =
new Image(String.valueOf(getClass().getResource("img/blank.png")), 16, 16, true, true);
ImageView blankImage = new ImageView(blank);
Button blankButton = new Button();
blankButton.setGraphic(blankImage);
blankButton.setMinSize(16, 16);
blankButton.setOnMouseClicked(this::buttonClicked);
blankButton.setOnMousePressed(this::mouseHeld);
blankButton.setOnMouseReleased(this::mouseReleased);
return blankButton;
}
private void mouseHeld(MouseEvent mouseEvent) {
if (gameOver)
return;
setImage(smiley, "img/face_ooh.png");
}
private void mouseReleased(MouseEvent mouseEvent) {
if (gameOver)
return;
setImage(smiley, "img/face_smile.png");
}
private void buttonClicked(MouseEvent e) {
if (gameOver) {
return;
}
Button clicked = (Button) e.getSource();
ImageView buttonImageView = (ImageView) clicked.getGraphic();
Image buttonImage = buttonImageView.getImage();
int column = GridPane.getColumnIndex(clicked);
int row = GridPane.getRowIndex(clicked);
if (isFirstLoad) {
scheduleTimer();
isFirstLoad = false;
}
if (e.getButton() == MouseButton.SECONDARY) {
flag(clicked);
return;
}
if (buttonImage.getUrl().contains("flagged.png")) {
return;
}
handlePrimaryClick(clicked, column, row);
}
private int[] setBombIfFirstTileIsBomb(int column, int row) {
for (int c = 0; c < 30; ++c) {
for (int r = 0; r < 16; ++r) {
if (!wrapper.atColumn(c).atRow(r).isBomb()) {
wrapper.setBomb();
wrapper.updateGrid(column, row, c, r);
return new int[]{c, r};
}
}
}
return null;
}
private void handlePrimaryClick(Button clicked, int column, int row) {
if (wrapper.atColumn(column).atRow(row).isBomb() && !isFirstClick) {
gameOver(clicked);
return;
}
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) getNodeByRowColumnIndex(row, column);
}
isFirstClick = false;
int adjacentBombs = wrapper.adjacentBombCount();
setAdjacentCount(clicked, adjacentBombs);
if (adjacentBombs == 0) {
recursiveExpandTiles(column, row);
}
}
private void recursiveExpandTiles(int column, int row) {
if (column < 0 || column >= 30 || row < 0 || row >= 16 ||
expandedTiles[column][row] && !wrapper.atColumn(column).atRow(row).isBomb()) {
return;
}
expandTile(column, row);
if (wrapper.atColumn(column).atRow(row).adjacentBombCount() == 0) {
recursiveExpandTiles(column, row - 1);
recursiveExpandTiles(column + 1, row - 1);
recursiveExpandTiles(column + 1, row);
recursiveExpandTiles(column + 1, row + 1);
recursiveExpandTiles(column, row + 1);
recursiveExpandTiles(column - 1, row + 1);
recursiveExpandTiles(column - 1, row);
recursiveExpandTiles(column - 1, row - 1);
}
}
private void expandTile(int column, int row) {
Node tile = getNodeByRowColumnIndex(row, column);
if (tile != null) {
Button button = (Button) tile;
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);
}
}
}
}
private Node getNodeByRowColumnIndex(int row, int column) {
for (Node node : grid.getChildren()) {
if (GridPane.getRowIndex(node) == row && GridPane.getColumnIndex(node) == column) {
return node;
}
}
return null;
}
@FXML
private void reinitialize() {
gameOver = false;
bombCount = 99;
updateBombCounter();
resetTimer();
resetGrid();
gridHandler = new Grid();
wrapper = gridHandler.grid;
expandedTiles = new boolean[30][16];
isFirstClick = true;
}
private void resetTimer() {
URL zeroSecondURL = getClass().getResource("img/0_seconds.png");
time_1.setImage(new Image(String.valueOf(zeroSecondURL)));
time_2.setImage(new Image(String.valueOf(zeroSecondURL)));
time_3.setImage(new Image(String.valueOf(zeroSecondURL)));
timer.cancel();
isFirstLoad = true;
timer = new Timer();
}
private void resetGrid() {
URL blank = getClass().getResource("img/blank.png");
for (Node node : grid.getChildren()) {
Button button = (Button) node;
button.setGraphic(new ImageView(new Image(String.valueOf(blank), 16, 16, true, false)));
}
}
private void gameOver(Node tileClicked) {
gameOver = true;
timer.cancel();
setImage(smiley, "img/face_dead.png");
setImage((Button) tileClicked, "img/bomb_death.png");
showAllBombs(GridPane.getColumnIndex(tileClicked), GridPane.getRowIndex(tileClicked));
}
private void flag(Node tileClicked) {
Button tileAsButton = (Button) tileClicked;
ImageView tileGraphic = (ImageView) tileAsButton.getGraphic();
Image tileGraphicImage = tileGraphic.getImage();
if (!tileGraphicImage.getUrl().contains("blank.png") &&
!tileGraphicImage.getUrl().contains("flagged.png")) {
return;
}
boolean flagged = tileGraphicImage.getUrl().contains("flagged.png");
if (flagged) {
bombCount++;
setImage(tileAsButton, "img/blank.png");
updateBombCounter();
return;
}
bombCount--;
updateBombCounter();
setImage((Button) tileClicked, "img/bomb_flagged.png");
}
private void updateBombCounter() {
String bombCountString = String.format("%03d", bombCount);
setBombCounterImage(bomb_2, bombCountString.charAt(1));
setBombCounterImage(bomb_3, bombCountString.charAt(2));
}
private void setBombCounterImage(ImageView imageView, char digit) {
URL imageURL = getClass().getResource("img/" + digit + "_seconds.png");
imageView.setImage(new Image(String.valueOf(imageURL)));
}
private void scheduleTimer() {
startTime = System.currentTimeMillis();
TimerTask task = new TimerTask() {
@Override
public void run() {
if (time >= 999) {
timer.cancel();
} else {
updateTimer();
}
}
};
timer.schedule(task, 0, 1000);
}
private void updateTimer() {
long elapsedTimeMillis = System.currentTimeMillis() - startTime;
time = (int) (elapsedTimeMillis / 1000);
String timeString = String.format("%03d", time);
setTimerImage(time_1, timeString.charAt(0));
setTimerImage(time_2, timeString.charAt(1));
setTimerImage(time_3, timeString.charAt(2));
}
private void setTimerImage(ImageView imageView, char digit) {
URL imageURL = getClass().getResource("img/" + digit + "_seconds.png");
imageView.setImage(new Image(String.valueOf(imageURL)));
}
private void showAllBombs(int clickedColumn, int clickedRow) {
for (Node node : grid.getChildren()) {
Button b = (Button) node;
String buttonURL = ((ImageView) b.getGraphic()).getImage().getUrl();
int column = GridPane.getColumnIndex(node);
int row = GridPane.getRowIndex(node);
if (!(column == clickedColumn && row == clickedRow) &&
wrapper.atColumn(column).atRow(row).isBomb()) {
setImage((Button) node, "img/bomb_revealed.png");
}
if (buttonURL.contains("flagged.png") && !wrapper.atColumn(column).atRow(row).isBomb()) {
setImage(b, "img/bomb_wrong.png");
}
}
}
private void setImage(Button button, String imagePath) {
URL imageURL = getClass().getResource(imagePath);
button.setGraphic(new ImageView(new Image(String.valueOf(imageURL), 16, 16, true, false)));
}
private void setImage(ImageView imageView, String imagePath) {
URL imageURL = getClass().getResource(imagePath);
imageView.setImage(new Image(String.valueOf(imageURL)));
}
@FXML
private void smileyPressed() {
setImage(smiley, "img/face_smile_pressed.png");
}
@FXML
private void smileyReleased() {
setImage(smiley, "img/face_smile.png");
reinitialize();
}
private void setAdjacentCount(Node tileClicked, int adjacentBombs) {
Button button = (Button) tileClicked;
setImage(button, "img/num_" + adjacentBombs + ".png");
}
}