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 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 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 choices = new ArrayList<>(); for(int i=2; i<9; i++) { choices.add(i); } ChoiceDialog dialog = new ChoiceDialog<>(4, choices); dialog.setTitle("Generate a puzzle"); dialog.setContentText("Choose a size"); Optional result = dialog.showAndWait(); if (result.isPresent()){ int size = result.get(); Config configObject = new Config(Generator.generate(size)); ArrayList 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 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 cageConfigs = configObject.getCages(); if (cageConfigs != null) { gameGrid.makeGameFromConfig(cageConfigs); } } }