konkon / KonKon.java
KonKon.java
Raw
import javafx.animation.PauseTransition;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.*;
import javafx.scene.layout.*;
import javafx.scene.media.AudioClip;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import javafx.scene.media.MediaView;
import javafx.scene.paint.Color;
import javafx.stage.FileChooser;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.util.Duration;

import java.io.File;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Random;

public class KonKon extends Application {

    VBox root;
    GameManager gameGrid;
    StackPane evenMoreRoot;
    ImageView winAnim;
    AudioClip winSound;
    Stage stage;
    MenuItem undo, redo;

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage stage) throws Exception {

        this.stage = stage;
        evenMoreRoot = new StackPane();
        MenuBar menuBar = makeMenuBar();
        root = new VBox(menuBar);

        winAnim = new ImageView();
		// gif obtained from https://giphy.com/gifs/memecandy-Qs75BqLW44RrP0x6qL
        winAnim.setImage(new Image(this.getClass().getResourceAsStream("/res/giphy.gif")));
        winAnim.fitWidthProperty().bind(stage.widthProperty());
        winAnim.fitHeightProperty().bind(stage.heightProperty());

		// sound obtained from https://www.youtube.com/watch?v=_Z3ra0CxCE0
        winSound = new AudioClip(this.getClass().getResource("/res/cheer.mp3").toURI().toString());

        evenMoreRoot.getChildren().addAll(root);

        makeDefaultGUI();
        Config configObject = new Config(Generator.generate(4));
        ArrayList<CageConfig> cageConfigs = configObject.getCages();
        if (cageConfigs != null) {
            gameGrid.makeGameFromConfig(cageConfigs);
        }

        Scene scene = new Scene(evenMoreRoot, 800, 800, Color.rgb(202, 181, 214));
        scene.addEventHandler(KeyEvent.KEY_PRESSED, new KeyPressHandler(gameGrid));
        stage.setTitle("KonKon - Default Puzzle");

        stage.minHeightProperty().bind(stage.widthProperty());
        stage.maxHeightProperty().bind(stage.widthProperty());
        stage.setScene(scene);
        stage.show();

    }

    private void makeDefaultGUI() {
        root.setSpacing(20);
        setUpEmptyGameGrid();
        VBox.setVgrow(gameGrid, Priority.ALWAYS);
        root.getChildren().add(gameGrid);

    }

    private void setUpEmptyGameGrid() {
        gameGrid = new GameManager(this);
        gameGrid.setUndoRedo(undo, redo);
        undo.setDisable(true);
        redo.setDisable(true);
        gameGrid.setPadding(new Insets(5));
        gameGrid.setAlignment(Pos.CENTER);
    }

    private MenuBar makeMenuBar() {

        Menu file = new Menu("Load Game");

        MenuItem loadFromFile = new MenuItem("From File");
        loadFromFile.setOnAction(actionEvent -> loadFromFile());

        MenuItem loadFromText = new MenuItem("From Text");
        loadFromText.setOnAction(actionEvent -> loadFromText());

        MenuItem generate = new MenuItem("Generate a puzzle");
        generate.setOnAction(actionEvent -> generatePuzzlePrompt());
        file.getItems().addAll(loadFromFile, loadFromText, generate);

        Menu fontSize = new Menu("Font Size");
        MenuItem small = new MenuItem("Small");
        small.setOnAction(actionEvent -> gameGrid.updateFontSize(20, 10));
        MenuItem medium = new MenuItem("Medium");
        medium.setOnAction(actionEvent -> gameGrid.updateFontSize(35, 15));
        MenuItem large = new MenuItem("Large");
        large.setOnAction(actionEvent -> gameGrid.updateFontSize(50, 25));
        fontSize.getItems().addAll(small, medium, large);

        Menu edit = new Menu("Edit");

        undo = new MenuItem("Undo");
        undo.setAccelerator(new KeyCodeCombination(KeyCode.Z, KeyCombination.CONTROL_DOWN));
        undo.setOnAction(actionEvent -> {
            if (!undo.isDisable()) {
                gameGrid.undoLastMove();
            }
            if (gameGrid.history.isEmpty()) {
                undo.setDisable(true);
            }
        });

        redo = new MenuItem("Redo");
        redo.setAccelerator(new KeyCodeCombination(KeyCode.Y, KeyCombination.CONTROL_DOWN));
        redo.setOnAction(actionEvent -> {
            if (!redo.isDisable()) {
                gameGrid.redoLastMove();
            }
            if (gameGrid.future.isEmpty()) {
                redo.setDisable(true);
            }
            if (!gameGrid.history.isEmpty()) {
                undo.setDisable(false);
            }
        });

        MenuItem clear = new MenuItem("Clear Board");
        clear.setOnAction(actionEvent -> {
            Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
            alert.setTitle("Clear Board Confirmation");
            alert.setHeaderText("This will clear the value of every cell on the board.");
            alert.setContentText("Press OK to continue");

            Optional<ButtonType> result = alert.showAndWait();
            if (result.get() == ButtonType.OK && gameGrid.getCellMap() != null) {
                for (Cell cell : gameGrid.getCellMap().values()) {
                    cell.clearCell();
                }
            }
        });

        edit.getItems().addAll(undo, redo, clear);

        Menu mistake = new Menu();
        Label mistakeLabel = new Label("Show Mistakes");
        mistakeLabel.setOnMouseClicked(mouseEvent -> {
            if (gameGrid.getCellMap() != null) {
                if (gameGrid.isShowMistakes()) {
                    gameGrid.setShowMistakes(false);
                    mistakeLabel.setText("Show Mistakes");
                    for (Cell cell : gameGrid.getCellMap().values()) {
                        cell.setBackground(cell.normalBG);
                    }
                } else {
                    gameGrid.setShowMistakes(true);
                    mistakeLabel.setText("Hide Mistakes");
                    gameGrid.checkAllCellsForMistakes();
                }
            }
        });
        mistake.setGraphic(mistakeLabel);

        Menu solver = new Menu("Solver");
        MenuItem hint = new MenuItem("Show Hint");
        hint.setOnAction(actionEvent -> {
            int cellToCheck = 0;
            Cell cell = null;
            while(cellToCheck == 0) {
                Random r = new Random();
                Cell cellTemp = gameGrid.getCellMap().get(r.nextInt(gameGrid.getCellMap().size()));
                if(cellTemp.getCurrentValue() == 0) {
                    cellToCheck = cellTemp.getCellID();
                    cell = gameGrid.getCellMap().get(cellToCheck);
                }
            }
            cell.getCurrentValueLabel().setText(String.valueOf(cell.correctAnswer));
            PauseTransition pauseTransition = new PauseTransition(Duration.seconds(1));
            Cell finalCell = cell;
            pauseTransition.setOnFinished(e -> finalCell.setCurrentValue(0));
            pauseTransition.play();
        });
        MenuItem solve = new MenuItem("Solve");
        solve.setOnAction(actionEvent -> {
            for(Cell cell : gameGrid.getCellMap().values()) {
                cell.setCurrentValue(cell.correctAnswer);
            }
        });

        solver.getItems().addAll(hint, solve);

        MenuBar menuBar = new MenuBar();
        menuBar.getMenus().addAll(file, edit, fontSize, solver, mistake);

        return menuBar;
    }

    public void generatePuzzlePrompt() {
        List<Integer> choices = new ArrayList<>();
        for(int i=2; i<9; i++) {
            choices.add(i);
        }
        ChoiceDialog<Integer> dialog = new ChoiceDialog<>(4, choices);
        dialog.setTitle("Generate a puzzle");
        dialog.setContentText("Choose a size");
        Optional<Integer> result = dialog.showAndWait();
        if (result.isPresent()){
            int size = result.get();
            Config configObject = new Config(Generator.generate(size));
            ArrayList<CageConfig> cageConfigs = configObject.getCages();
            if (cageConfigs != null) {
                gameGrid.makeGameFromConfig(cageConfigs);
            }
        }
    }

    public void loadFromText() {

        Stage textInput = new Stage();
        textInput.setTitle("Load Puzzle From Text");
        VBox window = new VBox();
        window.setSpacing(20);
        window.setPadding(new Insets(20));
        window.setAlignment(Pos.CENTER);

        TextArea inputBox = new TextArea();
        VBox.setVgrow(inputBox, Priority.ALWAYS);
        Clipboard clipboard = Clipboard.getSystemClipboard();
        if (clipboard.hasString()) {
            inputBox.setText(clipboard.getString());
        }

        Button confirm = new Button("Generate");
        confirm.setAlignment(Pos.CENTER);
        confirm.setOnMouseClicked(event -> {
            Config configObject = new Config(inputBox.getText());
            ArrayList<CageConfig> cageConfigs = configObject.getCages();
            if (cageConfigs != null) {
                gameGrid.makeGameFromConfig(cageConfigs);
            }
            textInput.close();
        });

        window.getChildren().addAll(inputBox, confirm);
        Scene textScene = new Scene(window, 500, 300);

        textInput.setScene(textScene);
        textInput.initModality(Modality.APPLICATION_MODAL);
        textInput.show();
    }

    public void loadFromFile() {
        FileChooser fileChooser = new FileChooser();
        fileChooser.getExtensionFilters().setAll(
                new FileChooser.ExtensionFilter("KonKon Files", "*.txt", "*.kon")
        );
        String currentPath = Paths.get(".").toAbsolutePath().normalize().toString();
        fileChooser.setInitialDirectory(new File(currentPath));
        File selectedFile = fileChooser.showOpenDialog(stage);
        Config configObject = new Config(selectedFile);
        ArrayList<CageConfig> cageConfigs = configObject.getCages();
        if (cageConfigs != null) {
            gameGrid.makeGameFromConfig(cageConfigs);
        }
    }

}