konkon / GameManager.java
GameManager.java
Raw
import javafx.application.Platform;
import javafx.scene.control.Alert;
import javafx.scene.control.ButtonType;
import javafx.scene.control.MenuItem;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.RowConstraints;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Optional;
import java.util.Stack;

public class GameManager extends GridPane {

    public ArrayList<Cage> cages;
    private KonKon konkon;
    private Integer n = 0;
    private HashMap<Integer, Cell> cellMap;
    private boolean showMistakes = false;
    private Cell currentlySelected;
    public Stack<HistoricCell> history = new Stack<>(), future = new Stack<>();
    private Alert alert, unsolve;
    private ButtonType loadFromFile, loadFromText, generate, tryAgain, closeProgram;
    MenuItem undo, redo;

    GameManager(KonKon konkon) {
        this.konkon = konkon;
        alert = new Alert(Alert.AlertType.CONFIRMATION,
                "What do you want to do next?");
        alert.setTitle("You did it");

        loadFromFile = new ButtonType("Load new from file");
        loadFromText = new ButtonType("Load new from text");
        generate = new ButtonType("Generate new");
        tryAgain = new ButtonType("Try again");
        closeProgram = new ButtonType("Quit");

        unsolve = new Alert(Alert.AlertType.CONFIRMATION,
                "Select an option");
        unsolve.setTitle("Please try a new puzzle");
        unsolve.getButtonTypes().setAll(loadFromFile, loadFromText, generate, closeProgram);
        alert.getButtonTypes().setAll(loadFromFile, loadFromText, generate, tryAgain, closeProgram);
    }

    void makeGameFromConfig(ArrayList<CageConfig> cages) {
        konkon.evenMoreRoot.getChildren().remove(konkon.winAnim);
        konkon.winSound.stop();
        this.getChildren().clear();
        this.cages = new ArrayList<>();
        for (CageConfig cageConfig : cages) {
            this.cages.add(new Cage(this, cageConfig));
        }
        putCellsOnBoard();
    }

    public void updateFontSize(int mainSize, int overlaySize) {
        for(Cell cell : cellMap.values()) {
            cell.getCurrentValueLabel().setFont(Font.font("Courier New", FontWeight.BOLD, mainSize));
        }
        for(Cage cage : cages) {
            cage.idOverlay.setFont(Font.font("Courier New", FontWeight.BOLD, overlaySize));
        }
    }

    public void checkGameWon() {
        if (checkIfGameOver()) {
            try {
                konkon.evenMoreRoot.getChildren().add(konkon.winAnim);
            } catch(IllegalArgumentException e) {
                konkon.evenMoreRoot.getChildren().remove(konkon.winAnim);
                konkon.evenMoreRoot.getChildren().add(konkon.winAnim);
            }
            konkon.winSound.play();
            Platform.runLater(() -> {
                alert.setHeaderText("Congratulations, you've solved this " +
                        this.n + "x" + this.n + " KonKon puzzle");
                Optional<ButtonType> result = alert.showAndWait();
                if (result.get() == loadFromFile) {
                    konkon.loadFromFile();
                } else if (result.get() == loadFromText) {
                    konkon.loadFromText();
                } else if (result.get() == generate){
                    konkon.generatePuzzlePrompt();
                } else if (result.get() == tryAgain) {
                    for (Cell cell : getCellMap().values()) {
                        cell.clearCell();
                    }
                } else {
                    Platform.exit();
                    System.exit(0);
                }
                konkon.evenMoreRoot.getChildren().remove(konkon.winAnim);
                alert.close();
            });
        }
    }

    public boolean checkIfGameOver() {
        for (Cage cage : cages) {
            if (!cage.isFilledCorrectly(true)) {
                return false;
            }
        }

        for (int i = 0; i < this.n; i++) {
            if (!checkColumnCorrect(i, true) || !checkRowCorrect(i, true)) {
                return false;
            }
        }
        return true;
    }

    void putCellsOnBoard() {

        cellMap = new HashMap<>();
        for (Cage cage : cages) {
            cellMap.putAll(cage.getCellMap());
        }

        this.n = (int) Math.sqrt(cellMap.size());

        for (Cage cage : cages) {
            cage.setBorders();

            for (Cell cell : cage.getCells()) {
                cell.fillComboBox();
            }
        }

        getColumnConstraints().clear();
        getRowConstraints().clear();

        for (int i = 0; i < n; i++) {
            ColumnConstraints col = new ColumnConstraints();
            col.setPercentWidth(100.0 / n);
            getColumnConstraints().add(col);

            RowConstraints row = new RowConstraints();
            row.setPercentHeight(100.0 / n);
            getRowConstraints().add(row);
        }

        int count = 1;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                Cell cellToAdd = cellMap.get(count);
                cellToAdd.setXY(j, i);
                add(cellToAdd, j, i);
                count++;
            }
        }

        Solver solver = new Solver(this);
        if(!solver.solve(1)) {
            Platform.runLater(() -> {
                unsolve.setHeaderText("Unsolvable Puzzle Warning");
                Optional<ButtonType> result = unsolve.showAndWait();
                if (result.get() == loadFromFile) {
                    konkon.loadFromFile();
                } else if (result.get() == loadFromText) {
                    konkon.loadFromText();
                } else if (result.get() == generate) {
                    konkon.generatePuzzlePrompt();
                } else {
                    Platform.exit();
                    System.exit(0);
                }
                unsolve.close();
            });
        }
    }

    public Boolean checkColumnCorrect(int col, boolean victoryCheck) {
        ArrayList<Integer> currentValues = new ArrayList<>();
        for (int i = 0; i < this.n; i++) {
            int value = cellMap.get((col + 1) + i * this.n).getCurrentValue();
            if (currentValues.contains(value) || value > this.n) {
                return false;
            } else {
                if (victoryCheck || value != 0) {
                    currentValues.add(value);
                }
            }
        }
        return true;
    }

    public Boolean checkRowCorrect(int row, boolean victoryCheck) {
        ArrayList<Integer> currentValues = new ArrayList<>();
        for (int i = 0; i < this.n; i++) {
            int cellID = ((row * this.n) + 1) + i;
            int value = cellMap.get(cellID).getCurrentValue();
            if (currentValues.contains(value) || value > this.n) {
                return false;
            } else {
                if (victoryCheck || value != 0) {
                    currentValues.add(value);
                }
            }
        }
        return true;
    }

    public void checkAllCellsForMistakes() {
        for (Cell cell : cellMap.values()) {
            if (!checkRowCorrect(cell.getY(), false) || !checkColumnCorrect(cell.getX(), false) || !cell.cage.isFilledCorrectly(false)) {
                cell.setBackground(cell.mistakeBG);
            } else {
                cell.setBackground(cell.normalBG);
            }
        }
    }

    public void logHistory(Cell cell) {
        history.push(new HistoricCell(cell, cell.getCurrentValue()));
        undo.setDisable(false);
        future.clear();
    }

    public void undoLastMove() {
        if (!history.isEmpty()) {
            redo.setDisable(false);
            HistoricCell cellToAdd = history.pop();
            Cell cellToRemove = this.cellMap.get(cellToAdd.cell.getCellID());
            future.push(new HistoricCell(cellToRemove, cellToRemove.getCurrentValue()));
            cellToRemove.setCurrentValue(cellToAdd.newValue);
        }
    }

    public void redoLastMove() {
        if (!future.empty()) {
            HistoricCell cellToAdd = future.pop();
            Cell cellToRemove = this.cellMap.get(cellToAdd.cell.getCellID());
            history.push(new HistoricCell(cellToRemove, cellToRemove.getCurrentValue()));
            cellToRemove.setCurrentValue(cellToAdd.newValue);
        }
    }

    public Integer getN() {
        return n;
    }

    public Cell getCurrentlySelected() {
        return currentlySelected;
    }

    public void setCurrentlySelected(Cell cell) {
        this.currentlySelected = cell;
    }

    public boolean isShowMistakes() {
        return showMistakes;
    }

    public void setShowMistakes(boolean mistake) {
        showMistakes = mistake;
    }

    public HashMap<Integer, Cell> getCellMap() {
        return cellMap;
    }

    public void setUndoRedo(MenuItem undo, MenuItem redo) {
        this.undo = undo;
        this.redo = redo;
    }
}