From d2689e863f1346e768b24ce00a706643f7414841 Mon Sep 17 00:00:00 2001 From: tmp1u19 <tmp1u19@soton.ac.uk> Date: Sun, 26 Apr 2020 19:08:43 +0300 Subject: [PATCH] Comment everything --- Cage.java | 138 +++++---- Cell.java | 126 +++++--- Element.java | 17 +- GameScene.java | 159 ++++++---- Handler.java | 809 +++++++++++++++++++++++++++++++------------------ UndoRedo.java | 41 ++- Window.java | 245 ++++++++------- 7 files changed, 957 insertions(+), 578 deletions(-) diff --git a/Cage.java b/Cage.java index 0a98e9b..f02ea20 100644 --- a/Cage.java +++ b/Cage.java @@ -1,74 +1,58 @@ - import java.util.ArrayList; +/** + * this class defines a cage of the board + * a cage is made out of cells + * a cage has a result, a symbol and a label on one of the cells + */ public class Cage { - private ArrayList<Cell> cells = new ArrayList<Cell>(); - private int n; - private String result; - private Character symbol = null; - private String label; - - public Cage(int n) { - this.n = n; - } + private ArrayList<Cell> cells = new ArrayList<Cell>(); //will memorise the cells from the cage + private String result; //the result that the cells must have when applying the symbol + private Character symbol = null; //operator to be applied on values to give the result + private int n = GameScene.n; //the size of the board + /** + * this method adds the cell to the cage + * it finds the position of the cell on the board using the cell number provided in the input + * @param cellNumber -> must be converted in coordinates of the cell (i,j) + */ public void addCell(int cellNumber) { + //calculate the coordinates of the cell to be added int i = cellNumber / n; int j = (cellNumber % n) - 1; - Cell cell; + Cell cell; //initialise a cell + + //if j is -1 => the the cell is placed on the n - 1 column if(j == - 1) { - cell = new Cell(n, i - 1, n - 1); + cell = new Cell(i - 1, n - 1); } else { - cell = new Cell(n, i, j); + cell = new Cell(i, j); } - cell.setCellNumber(cellNumber); - - cells.add(cell); - - } - - public ArrayList<Cell> getCells() { - return cells; - } - - public void setResult(String result) { - this.result = result; - } - - public void setSymbol(char symbol) { - this.symbol = symbol; - } - - public void setLabel(String label) { - this.label = label; + cell.setCellNumber(cellNumber); //set the number to the cell + cells.add(cell); //add the cell to the cage } - public char getSymbol() { - return symbol; - } - - public String getResult() { - return result; - } - - public boolean hasSymbol() { - if(symbol == null) { - return false; - } else { - return true; - } - } + /** + * this method sorts the values from the cells in the cage in descending order + * this method is used to calculate division or subtraction in a cage + * @return the array sorted in descending order + */ public int[] getValues() { - int[] values = new int[cells.size()]; + + int[] values = new int[cells.size()]; //create an array of the zice of the number of cells in the cage int size = 0; + //loop through the cells of the cage for(Cell cell : cells) { + //only sort the cage if it is completed if(isCompleted()) { + + //sort the array in descending order int x = Integer.parseInt(cell.getTextField().getText()); if(size != 0) { @@ -91,31 +75,69 @@ public class Cage { } else { values[size] = x; } + size++; + } else { return null; } } - return values; + return values; //return the array which contains the sorted array } + /** + * @return tru if the cage contains cells which all have a value + */ public boolean isCompleted() { for(Cell cell : getCells()) { - if(!isNumeric(cell.getTextField().getText())) { + if(!Handler.isNumeric(cell.getTextField().getText())) { return false; } } return true; } - public boolean isNumeric(String x) { - try { - Integer.parseInt(x); - } catch (NumberFormatException e) { - return false; - } - return true; + /** + * @return the cells from the cage + */ + public ArrayList<Cell> getCells() { + return cells; + } + + /** + * @param result is set to the result of the cage + */ + public void setResult(String result) { + this.result = result; + } + + /** + * @param symbol is set to the symbol of the cage + */ + public void setSymbol(char symbol) { + this.symbol = symbol; + } + + /** + * @return the sumbol of the cage + */ + public char getSymbol() { + return symbol; + } + + /** + * @return the result of the cage + */ + public String getResult() { + return result; + } + + /** + * @return true if the cage has a symbol + */ + public boolean hasSymbol() { + return symbol != null; } } diff --git a/Cell.java b/Cell.java index 301ece1..dca57dd 100644 --- a/Cell.java +++ b/Cell.java @@ -6,54 +6,71 @@ import javafx.scene.text.Font; import javafx.scene.control.*; import javafx.scene.text.FontWeight; +/** + * this class defines a cell which will have the properties of a pane + * the cell will represent the entity which will interact with the user + * the board is made out of cells + */ public class Cell extends Pane { - private TextField input; + private TextField input; //the space in the cell where the player can introduce values + private Label label; //the label which will only be present in some cells + private int cellNumber; //the cell number -> to make the verification of the input easier + + //coordinates of the cell in the grid private int i; private int j; - private int cellNumber; - private Label l; - public Cell(int n, int i, int j) { + /** + * the constructor will create a cell (pane) with the coordinates (i, j) in the grid pane (board) + * will create a text field where the player can introduce values + * @param i -> row coordinate of cell in the grid pane + * @param j -> column coordinate of cell in the grid pane + */ + public Cell(int i, int j) { + //set the coordinates of the cell in the grid pane this.i = i; this.j = j; - input = new TextField(); + //set the initial style for the cell setStyle("- fx-background-color: transparent;"); + //create the text field where the player can interact with the cell; ste properties + input = new TextField(); input.setStyle("-fx-text-box-border: transparent; -fx-background-color: transparent;"); input.setFont(Font.font("Verdana", FontWeight.BOLD, 20)); input.setAlignment(Pos.CENTER); - getChildren().addAll(input); - } - - public void setLabel(String label) { - l = new Label(label); - l.setPadding(new Insets(5)); - l.setAlignment(Pos.TOP_LEFT); - getChildren().add(l); + getChildren().addAll(input); //add the text field to the cell } - public Label getLabel() { - return l; - } - - public TextField getTextField() { - return input; - } + /** + * will set the label to the cell (only some cells will have a label) + * @param l -> the value of the label to be added to the cell + */ + public void setLabel(String l) { - public int getRow() { - return i; - } + //create the bale and add its properties + label = new Label(l); + label.setPadding(new Insets(5)); + label.setAlignment(Pos.TOP_LEFT); - public int getColumn() { - return j; + //add the label to the cell + getChildren().add(label); } + /** + * verify if the cell has a left neighbour in the cage + * @param cage -> the cage to verify in + * @return true if the cell has a left neighbour in the cage + */ public boolean hasLeftNeighbour(Cage cage) { + + //loop through the cells of the cage for(Cell cell : cage.getCells()) { + + //verify if the cell has a left neighbour if(cell.getColumn() + 1 == getColumn() && cell.getRow() == getRow()) { return true; @@ -62,8 +79,17 @@ public class Cell extends Pane { return false; } + /** + * verify if the cell has an up neighbour in the cage + * @param cage -> the cage to verify in + * @return true if the cell has an up neighbour in the cage + */ public boolean hasUpNeighbour(Cage cage) { + + //loop through the cells of the cage for(Cell cell : cage.getCells()) { + + //verify if the cell has an up neighbour if(cell.getRow() + 1 == getRow() && cell.getColumn() == getColumn()) { return true; @@ -72,33 +98,53 @@ public class Cell extends Pane { return false; } - public boolean isAdjacent(Cage cage) { - for(Cell cell : cage.getCells()) { - if((cell.getCellNumber() == getCellNumber() - 1 || - cell.getCellNumber() == getCellNumber() + 1 || - cell.getCellNumber() == getCellNumber() - Window.n || - cell.getCellNumber() == getCellNumber() + Window.n) && - cell.getCellNumber() != getCellNumber()) { - return true; - } - } - return false; + /** + * @return the label of the cell + */ + public Label getLabel() { + return label; + } + + /** + * @return the text field of the cell + */ + public TextField getTextField() { + return input; } + /** + * @return the row position of the cell + */ + public int getRow() { + return i; + } + + /** + * @return the column position of the cell + */ + public int getColumn() { + return j; + } + + /** + * @param cellNumber will be set as the cell number + */ public void setCellNumber(int cellNumber) { this.cellNumber = cellNumber; } + /** + * @return the number of the cell + */ public int getCellNumber() { return cellNumber; } + /** + * @param x -> will be set as value to the cell + */ public void setValue(int x) { getTextField().setText(String.valueOf(x)); } - - public int getValue() { - return Integer.parseInt(getTextField().getText()); - } } diff --git a/Element.java b/Element.java index 91de30d..0d52904 100644 --- a/Element.java +++ b/Element.java @@ -1,12 +1,23 @@ +/** + * this class creates the elements that will be added to the undo and redo stacks + * these elements memorise the position of a cell and the value inside that cell + */ public class Element { - public int i; - public int j; - public int value; + public int i; //row position of cell + public int j; //column position of cell + public int value; //the value of the cell + /** + * the constructor sets the position of the cell and the value of it + * @param i is set in the element + * @param j is set in the element + * @param value is set in the element + */ public Element(int i, int j, int value) { this.i = i; this.j = j; this.value = value; } } + diff --git a/GameScene.java b/GameScene.java index 2148c1e..c6e6b59 100644 --- a/GameScene.java +++ b/GameScene.java @@ -2,8 +2,6 @@ import javafx.application.Application; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.collections.FXCollections; -import javafx.event.ActionEvent; -import javafx.event.EventHandler; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.Scene; @@ -12,7 +10,6 @@ import javafx.scene.layout.*; import javafx.stage.FileChooser; import javafx.stage.Modality; import javafx.stage.Stage; - import java.io.*; import java.util.ArrayList; @@ -31,6 +28,7 @@ import java.util.ArrayList; public class GameScene extends Application { static ArrayList<Cage> cages = new ArrayList<Cage>(); //memorises the cages from the input + static int n; private Window window = new Window(); //helps to display different stages when needed /** @@ -166,7 +164,7 @@ public class GameScene extends Application { @Override public void changed(ObservableValue<? extends String> observableValue, String oldValue, String newValue) { - int n = Character.getNumericValue(cbSize.getSelectionModel().getSelectedItem().toString().charAt(0)); + n = Character.getNumericValue(cbSize.getSelectionModel().getSelectedItem().toString().charAt(0)); if(newValue.equals("file")) { @@ -194,16 +192,13 @@ public class GameScene extends Application { * play button to start the stage with the game * enabled when the size of the board and the way of inputting are selected */ - play.setOnAction(new EventHandler<ActionEvent>() { - @Override - public void handle(ActionEvent actionEvent) { - stage.close(); //close the stage + play.setOnAction(actionEvent -> { + stage.close(); //close the stage - String size = cbSize.getSelectionModel().getSelectedItem().toString(); + String size1 = cbSize.getSelectionModel().getSelectedItem().toString(); - window.mathdokuStage(Character.getNumericValue(size.charAt(0)), - cages).show(); //show the Mathdoku game stage - } + window.mathdoku(Character.getNumericValue(size1.charAt(0)), + cages).show(); //show the Mathdoku game stage }); root.getChildren().addAll(greeting, instruct, buttons, play); //add the labels and buttons to the root @@ -244,10 +239,10 @@ public class GameScene extends Application { while(line != null) { String str[] = line.split(" "); //create an array of the strings splitting the line with regex " " - Cage cage = new Cage(n); //the line represents a new cage => create a new cage + Cage cage = new Cage(); //the line represents a new cage => create a new cage //check if the first string from the array is just a number - if(isNumeric(str[0])) { + if(Handler.isNumeric(str[0])) { cage.setResult(str[0]); //the number represents the result of the cage cage.addCell(Integer.parseInt(str[1])); //the cage of the line consists of only the second element in @@ -269,13 +264,12 @@ public class GameScene extends Application { } cage.getCells().get(0).setLabel(str[0]); //set the label in on the board - cage.setLabel(str[0]); //set the label of the cage cages.add(cage); //add the cage created to an array list of all the cages line = buffer.readLine(); //go to the next line of the file } - checkInput(n, play); + checkInput(n, play); //check if the input can create a board } public boolean checkInput(int n, Button play) throws IOException { @@ -299,53 +293,79 @@ public class GameScene extends Application { return false; } - if(checkAdjacent(n) != cages.size()) { - cages = new ArrayList<Cage>(); - play.setDisable(true); - window.inputError("The cells in the cage are not adjacent").showAndWait(); + //check if the number of paths (cells that are neighbours with each other in a cage) is the same as the number of cages + if(cages.size() != checkAdjacent(n)) { + cages = new ArrayList<Cage>(); //delete the initial cages memorised + play.setDisable(true); //disable the play button + window.inputError("The cells in the cage are not adjacent").showAndWait(); //notify the user about + //the error return false; } - play.setDisable(false); + play.setDisable(false); //if the input passed all the tests, enable the play button return true; //return true if everything is correct } - public boolean checkUniqueCells(int n) throws IOException { + /** + * checks if the cells appear only once in only one cage + * also, can check if cells that don't correspond to the size of the board are present + * @param n -> the size of the board nxn + * @return boolean -> false if the cell appears more than once in the data, true else + */ + public boolean checkUniqueCells(int n) { - int[] verify = new int[n*n + 1]; + int[] verify = new int[n*n + 1]; //create a n x n array that will memorise the number of times a cell appears + // in the input + //add to the address of that cell every time it appears in the input for(Cage cage : cages) { for(Cell cell : cage.getCells()) { verify[cell.getCellNumber()] += 1; } } + //return false if the number of appearances is different than 1 for(int i = 1; i <= n*n; i ++) { if(verify[i] != 1) { return false; } } - return true; + return true; //return true if the any cell appears only once in only one cage } + /** + * checks if the cells within a cage are adjacent + * the idea is similar to the one in the quick union algorithm + * @param n -> the size of the board nxn + * @return the number of paths of cells that are neighbours + */ public int checkAdjacent(int n) { - int[] paths = new int[n*n + 1]; - int count = 0; + int[] paths = new int[n*n + 1]; //array which will have the address and the parent of a cell + int count = 0; //initialise the number of paths with 0 for(int i = 1; i <= n * n; i ++) { - paths[i] = i; + paths[i] = i; //set the parent for each cell with their cell number } + //determine the parents of each cell and the "root" for each path for(Cage cage : cages) { for(Cell cell : cage.getCells()) { + + //if the cell has an up neighbour, set its parent to that up cell if(cell.hasUpNeighbour(cage)) { paths[cell.getCellNumber()] = cell.getCellNumber() - n; } + + //if a cell has a left neighbour => 2 cases if(cell.hasLeftNeighbour(cage)) { + //1st case: the left neighbour is already connected => set the parent of the + // cell to the left neighbour if(paths[cell.getCellNumber() - 1] != cell.getCellNumber() - 1) { paths[cell.getCellNumber()] = cell.getCellNumber() - 1; + //2nd case: the left neighbour is the root => connect the left neighbour to + // the current cell } else { paths[cell.getCellNumber() - 1] = cell.getCellNumber(); } @@ -353,63 +373,77 @@ public class GameScene extends Application { } } + /* + * count the number of "roots" in the path array; + * if the number of paths is the same as the number of cages => the cells in all the cages are adjacent + */ for(int i =1; i <= n * n; i ++) { if(i == paths[i]) { count++; } } - return count; + return count; //return the number of paths } + /** + * this method creates a new window for the user to input directly in a text area + * @param n -> the size of the board nxn + * @param play -> to enable or disable the play button depending on the player's actions + */ public void directInput(int n, Button play) { + Stage stage = new Stage(); //create a new stage + + //create the root of the scene for this stage, set its properties VBox root = new VBox(10); root.setPadding(new Insets(20)); root.setAlignment(Pos.TOP_CENTER); root.setPrefSize(400, 600); + + //create the text area and set its properties TextArea textArea = new TextArea(); textArea.setStyle("-fx-font-size: 15px;"); textArea.setPrefWidth(300); textArea.setPrefHeight(400); Label label = new Label("Provide data for your board below." + - '\n' + "Remember, the size of the board is " + n + "x" + n + " :)"); + '\n' + "Remember, the size of the board is " + n + "x" + n + " :)"); //message for the player label.setStyle("-fx-font-size: 15px; -fx-font-weight:bold;"); - Button ok = new Button("OK"); + Button ok = new Button("OK"); //button which will be pressed to confirm that the player + // finished to introduce the input + + root.getChildren().addAll(label, textArea, ok); //add the elements to the vertical box (root) + play.setDisable(true); //disable the play button + + ok.setOnAction(actionEvent -> { + File file = new File("temp"); //create a temporary file to write the input in + try { + //write to the file created + FileWriter outputFile = new FileWriter(file); + outputFile.write(textArea.getText()); + outputFile.flush(); + outputFile.close(); + } catch (IOException e) { + e.printStackTrace(); + } - root.getChildren().addAll(label, textArea, ok); + try { + openFile(file, n, play); //open the file and extract the data + } catch (IOException e) { + e.printStackTrace(); + } + + file.delete(); //delete the temporary file + stage.close(); //close the stage + }); + //create the scene and set it to the stage Scene scene = new Scene(root); - Stage stage = new Stage(); stage.setScene(scene); - play.setDisable(true); - - ok.setOnAction(new EventHandler<ActionEvent>() { - @Override - public void handle(ActionEvent actionEvent) { - File file = new File("temp"); - try { - FileWriter outputFile = new FileWriter(file); - outputFile.write(textArea.getText()); - outputFile.flush(); - outputFile.close(); - } catch (IOException e) { - e.printStackTrace(); - } - - try { - openFile(file, n, play); - } catch (IOException e) { - e.printStackTrace(); - } - - file.delete(); - stage.close(); - } - }); + //set properties for the stage stage.setTitle("Direct Input"); stage.setMinHeight(530); stage.setMinWidth(400); @@ -418,15 +452,6 @@ public class GameScene extends Application { stage.initModality(Modality.APPLICATION_MODAL); stage.show(); - stage.setResizable(false); - } - - public boolean isNumeric(String x) { - try { - Integer.parseInt(x); - } catch (NumberFormatException e) { - return false; - } - return true; + stage.setResizable(false); //don't permit the player to resize the window } } diff --git a/Handler.java b/Handler.java index 155306f..06b7419 100644 --- a/Handler.java +++ b/Handler.java @@ -1,358 +1,567 @@ import javafx.animation.*; -import javafx.beans.value.ChangeListener; -import javafx.beans.value.ObservableValue; -import javafx.event.ActionEvent; -import javafx.event.EventHandler; import javafx.geometry.Insets; import javafx.scene.Node; import javafx.scene.control.Button; import javafx.scene.control.ButtonType; import javafx.scene.input.MouseButton; -import javafx.scene.input.MouseEvent; -import javafx.scene.layout.Background; -import javafx.scene.layout.BackgroundFill; -import javafx.scene.layout.CornerRadii; -import javafx.scene.layout.GridPane; +import javafx.scene.layout.*; import javafx.scene.paint.Color; import javafx.scene.text.Font; import javafx.scene.text.FontWeight; import javafx.stage.Stage; import javafx.util.Duration; - import java.util.ArrayList; +/** + * this class handles the actions of the user when he interacts with the elements from the mathdoku stage + * it manages the action of the buttons + * it holds the method to draw the board of the game + * it supervises the user interaction with the game, calculating results and compares the numbers introduced + * of the player tot he rules of the game + * when the game is solved correctly, the stage closes, and the application terminates + */ public class Handler { - private Window window = new Window(); - private int n; - static int[][] cellValues; - private UndoRedo undoStack = new UndoRedo(); - private UndoRedo redoStack = new UndoRedo(); - public static ArrayList<Cage> cages; - - public Handler(int n, ArrayList<Cage> cages) { - this.n = n; - this.cages = new ArrayList<Cage>(); - this.cages = cages; + private Window window = new Window(); //manager of the windows that must be opened in different circumstances + private int n = Window.n; //size of board nxn + static int[][] cellValues; //matrix of values of the cells + private UndoRedo undoStack = new UndoRedo(); //stack to memorise the actions of the player + private UndoRedo redoStack = new UndoRedo(); //stack to redo the actions the player had undone + public static ArrayList<Cage> cages; //memorised the cages of the board + + /** + * this is the constructor of the class which will set the cages array as a static variable + * creates the matix of values of size nxn + * @param cages -> are set to the static variable cages + */ + public Handler(ArrayList<Cage> cages) { + Handler.cages = cages; cellValues = new int[n][n]; } - public void helpButton(Button help, GridPane grid) { + /** + * this method will draw the grid and the cages will be emphasized + * @param grid -> the grid to sustain the cells + */ + public void drawGrid(GridPane grid) { - help.setOnAction(new EventHandler<ActionEvent>() { - @Override - public void handle(ActionEvent actionEvent) { + //loop through the cages + for(Cage cage : cages) { + //loop through the cells within a cage + for(Cell cell : cage.getCells()) { - if(help.getText().equals("Enable Help")) { - help.setText("Disable Help"); + //draw the cell depending on the neighbours it has in the cage + if(cell.hasLeftNeighbour(cage) && cell.hasUpNeighbour(cage)) { + cell.setStyle("-fx-border-color: black; -fx-border-width: 0 4 4 0;"); + } else if(cell.hasLeftNeighbour(cage)) { + cell.setStyle("-fx-border-color: black; -fx-border-width: 4 4 4 0;"); + } else if(cell.hasUpNeighbour(cage)) { + cell.setStyle("-fx-border-color: black; -fx-border-width: 0 4 4 4;"); + } else { + cell.setStyle("-fx-border-color: black; -fx-border-width: 4 4 4 4;"); + } - highlight(grid); + doubleClick(cell); //add the property to introduce values via mouse + memoriseValues(cell); //memorise he values as the player introduces them in the cell + grid.add(cell, cell.getColumn(), cell.getRow(), 1, 1); // add the cell to the grid + } + } + } - for(Node cell : grid.getChildren()) { - enableHighlight((Cell) cell, grid); - } + /** + * method that will display a window which will permit the player to introduce or delete values via mouse + * @param cell -> the cell to add the property to + */ + public void doubleClick(Cell cell) { - } else { - help.setText("Enable Help"); - setTransparent(grid); - for(Node cell : grid.getChildren()) { - disableHighlight((Cell) cell, grid); - } + //mouse click event + cell.getTextField().setOnMouseClicked(mouseEvent -> { + if(mouseEvent.getButton().equals(MouseButton.PRIMARY)) { + //count the number of mouse clicks, if they're 2 => show the button window + if(mouseEvent.getClickCount() == 2) { + window.showButtons(cell); //show button window to introduce or delete a value from the cell } } }); } - public void submitButton(Button submit, Stage stage) { - submit.setOnAction(new EventHandler<ActionEvent>() { - @Override - public void handle(ActionEvent actionEvent) { + /** + * this method adds a listener to the text within a cell + * interactively changes the values of the matrix depending on the values from the cells that are changed + * @param cell -> cell to add the listener to + */ + public void memoriseValues(Cell cell) { - boolean completed = true; + cell.getTextField().textProperty().addListener((observableValue, oldValue, newValue) -> { - for(Cage cage : cages) { - if(!cage.isCompleted()) { - window.win("Complete de board!", "Don't stop now...").show(); - completed = false; - break; - } - } + //check if the player entered a numeric value + if (isNumeric(newValue)) { - if(completed) { - if(verify() && verifyCages()) { - win(); - window.win("Congratulations!", "You are the master!").showAndWait(); - stage.close(); - } else { - window.win("Try again...", "...it's not correct.").show(); - } + //check if the player introduced a number between 1 and n + if (Integer.parseInt(newValue) > n || Integer.parseInt(newValue) == 0) { + cell.getTextField().setText(oldValue); //remain to the old value if not + } else { + //the value is correct, set it to the cell and the matrix + cell.setValue(Integer.parseInt(newValue)); + cellValues[cell.getRow()][cell.getColumn()] = Integer.parseInt(newValue); } + } else if (!newValue.matches("\\d*")) { + //the player introduced another character than a number => set the text to null and the matrix element to 0 + cell.getTextField().setText(newValue.replaceAll("[^\\d]", "")); + cellValues[cell.getRow()][cell.getColumn()] = 0; } }); } - public void clearBoard(Button clear, GridPane grid, Button undo, Button redo) { + /** + * method that manages the resizing of the board + * @param root -> root of mathdoku stage + * @param grid -> the board to resize + */ + public void resize(VBox root, GridPane grid) { - clear.setOnAction(new EventHandler<ActionEvent>() { - @Override - public void handle(ActionEvent actionEvent) { + //add a listener to see when the width of the root is resized + root.widthProperty().addListener((obs, oldVal, newVal) -> { - window.getAlertDialog("Are you sure you want to clear " + - "the board?").showAndWait().ifPresent(response -> { - if(response == ButtonType.YES) { - clear(grid); - undo.setDisable(true); - redo.setDisable(true); - } - }); + grid.setMaxWidth((Double) newVal - 100); //set the new width of the grid + + //loop through the cages of the board + for(Cage cage : cages) { + //loop through the cells of a cage + for(Cell cell : cage.getCells()) { + //resize each cell and the text field from inside it accordingly + cell.setPrefWidth((Double) newVal - 100); + cell.getTextField().setPrefSize(cell.getWidth(), cell.getHeight()); + } } }); - } + //add a listener to see when the height of the root is resized + root.heightProperty().addListener((obs, oldVal, newVal) -> { - public void undoAction(Button undo, Button redo, GridPane grid) { - undo.setOnAction(new EventHandler<ActionEvent>() { - @Override - public void handle(ActionEvent actionEvent) { - if(undoStack.isEmpty()) { - undo.setDisable(true); - } else { - redo.setDisable(false); - Element e = undoStack.pop(); - redoStack.push(e); - getCell(grid, e.i, e.j).getTextField().clear(); - cellValues[e.i][e.j] = 0; + grid.setMaxHeight((Double) newVal - 100); //set the new height of the grid + + //loop through the cages of the board + for(Cage cage : cages) { + //loop through the cells of the cage + for(Cell cell : cage.getCells()) { + //resize each cell and the text field from inside it accordingly + cell.setPrefHeight((Double) newVal- 100); + cell.getTextField().setPrefSize(cell.getWidth(), cell.getHeight()); } } }); } - public void redoAction(Button redo, GridPane grid) { - redo.setOnAction(new EventHandler<ActionEvent>() { - @Override - public void handle(ActionEvent actionEvent) { - if(redoStack.isEmpty()) { - redo.setDisable(true); - } else { - Element e = redoStack.pop(); - getCell(grid, e.i, e.j).getTextField().setText(String.valueOf(e.value)); - undoStack.push(e); + /** + * method that manages the help action from the mathdoku stage + * @param help -> the button to add the action to + * @param grid -> the grid changes properties depending on the help option activated (enabled or not) + */ + public void helpButton(Button help, GridPane grid) { + + help.setOnAction(actionEvent -> { + + //the button will change the text depending on the help option is enabled or not + if(help.getText().equals("Enable Help")) { + + help.setText("Disable Help"); //the help button is enabled => set the text to "Disable Help" + highlight(grid);//highlight the grid accordingly (incorrect cages with red, incorrect rows or + // columns with yellow + + //interactively highlight the board depending on the values introduced to the cells + for(Node cell : grid.getChildren()) { + enableHighlight((Cell) cell, grid); + } + } else { + + help.setText("Enable Help"); //the help button is disabled => set the text to "Enable Help" + setTransparent(grid); //make the whole grid transparent + + //disable the highlight option for each cell + for(Node cell : grid.getChildren()) { + disableHighlight((Cell) cell, grid); } } }); } - public void changeFont(Button button, int number) { - button.setOnAction(new EventHandler<ActionEvent>() { - @Override - public void handle(ActionEvent actionEvent) { - for(Cage cage : cages) { - for(Cell cell : cage.getCells()) { - - cell.getTextField().setStyle("-fx-text-box-border: transparent; " + - "-fx-background-color: transparent;"); - cell.getTextField().setFont(Font.font("Verdana", FontWeight.BOLD, number)); - } - cage.getCells().get(0).getLabel().setFont(Font.font(number - 10)); - } - } + /** + * this method will enable the highlight of the board accordingly + * @param cell -> the cell to add the listener, it will be highlighted interactively + * @param grid -> the board to highlight + */ + public void enableHighlight(Cell cell, GridPane grid) { + + cell.getTextField().textProperty().addListener((observableValue, s, t1) -> { + + setTransparent(grid); //set all the board transparent before highlighting + highlight(grid); //highlight the grid }); } - public void addActions(GridPane grid, Button undo) { + /** + * this method disables the highlight option + * @param cell -> the cell to add the listener + * @param grid -> the board to set to be transparent + */ + public void disableHighlight(Cell cell, GridPane grid) { + cell.getTextField().textProperty().addListener((observableValue, s, t1) -> setTransparent(grid)); + } - for(Node cell : grid.getChildren()) { - ((Cell) cell).getTextField().textProperty().addListener(new ChangeListener<String>() { - @Override - public void changed(ObservableValue<? extends String> observableValue, String s, String t1) { - if(isNumeric(((Cell) cell).getTextField().getText())) { - Element e = new Element(((Cell) cell).getRow(), ((Cell) cell).getColumn(), - Integer.parseInt(((Cell) cell).getTextField().getText())); - undoStack.push(e); - undo.setDisable(false); + /** + * this method highlights the grid accordingly + * @param grid to be highlighted + */ + public void highlight(GridPane grid) { + + //loop through the the cells in the grid + for(int i = 0; i < n; i ++) { + for(int j = 0; j < n; j ++) { + + //check for cells that contain a value + if(cellValues[i][j] != 0) { + + //highlight the row if the row doesn't consist of unique numbers + if(!verifyRow(cellValues[i][j], i)) { + highlightRow(i, grid); } - } - }); - } - } - public void doubleClick(Cell cell) { - cell.getTextField().setOnMouseClicked(new EventHandler<MouseEvent>() { - @Override - public void handle(MouseEvent mouseEvent) { - if(mouseEvent.getButton().equals(MouseButton.PRIMARY)) { - if(mouseEvent.getClickCount() == 2) { - window.showButtons(cell); + //highlight the column if the column doesn't consist of unique numbers + if(!verifyColumn(cellValues[i][j], j)) { + highlightColumn(j, grid); } } } - }); + } + + //highlight the cages + for(Cage cage : cages) { + highlightCage(cage); + } } - public void memoriseValues(Cell cell) { - cell.getTextField().textProperty().addListener(new ChangeListener<String>() { - @Override - public void changed(ObservableValue<? extends String> observableValue, String oldValue, - String newValue) { - - if (isNumeric(newValue)) { - if (Integer.parseInt(newValue) > n || Integer.parseInt(newValue) == 0) { - cell.getTextField().setText(oldValue); - } else { - getCellValues()[cell.getRow()][cell.getColumn()] = Integer.parseInt(newValue); - } - } else if (!newValue.matches("\\d*")) { - cell.getTextField().setText(newValue.replaceAll("[^\\d]", "")); - cellValues[cell.getRow()][cell.getColumn()] = 0; - } - } - }); + /** + * this method highlights a specific row from the board if it doesn't contain unique numbers + * @param row to be highlighted + * @param grid which contains the row to be highlighted + */ + public void highlightRow(int row, GridPane grid) { + + //loop through the cells of that row and highlight them with yellow + for(int i = 0; i < n; i ++) { + getCell(grid, row, i).setBackground(new Background(new BackgroundFill(Color.YELLOW, + CornerRadii.EMPTY, Insets.EMPTY))); + } } - public void enableHighlight(Cell cell, GridPane grid) { + /** + * this method highlights a specific column from the board if it doesn't contain unique numbers + * @param column to be highlighted + * @param grid which contains the column to be highlighted + */ + public void highlightColumn(int column, GridPane grid) { - cell.getTextField().textProperty().addListener(new ChangeListener<String>() { - @Override - public void changed(ObservableValue<? extends String> observableValue, String s, String t1) { - setTransparent(grid); - highlight(grid); + //loop through the cells of the column and highlight them with yellow + for(int i = 0; i < n; i ++) { + getCell(grid, i, column).setBackground(new Background(new BackgroundFill(Color.YELLOW, + CornerRadii.EMPTY, Insets.EMPTY))); + } + } + + /** + * this method highlights a cage if it the values within the cells of the cage don't satisfy the result + * @param cage to be highlighted + */ + public void highlightCage(Cage cage) { + + //verify if the values satisfy the result of the cage + if(!verifyCage(cage)) { + //loop through the cells of the cage to highlight them with red + for(Cell cell : cage.getCells()) { + cell.setBackground(new Background(new BackgroundFill(Color.RED, + CornerRadii.EMPTY, Insets.EMPTY))); } - }); + } } - public void disableHighlight(Cell cell, GridPane grid) { - cell.getTextField().textProperty().addListener(new ChangeListener<String>() { - @Override - public void changed(ObservableValue<? extends String> observableValue, String s, String t1) { - setTransparent(grid); + /** + * this method makes the board transparent + * helps with managing the help button + * @param grid to be set transparent + */ + public void setTransparent(GridPane grid) { + + //loop through the cells from the grid + for(Node cell : grid.getChildren()) { + ((Cell) cell).setBackground(new Background(new BackgroundFill(Color.TRANSPARENT, + CornerRadii.EMPTY, Insets.EMPTY))); //make each cell transparent + } + } + + /** + * method that manages the submit button from the mathdoku stage + * @param submit -> the button to add the action to + * @param stage -> necessary for closing the mathdoku stage if the game is correctly completed + */ + public void submitButton(Button submit, Stage stage) { + submit.setOnAction(actionEvent -> { + + boolean isCompleted = true; //will verify if the board is completed + + //loop through the cages and break if there exist a cage that is not completed + for(Cage cage : cages) { + if(!cage.isCompleted()) { + window.win("Complete de board!", "Don't stop now...").show(); //show window with appropriate message + isCompleted = false; //is not completed => set it to false + break; + } + } + + if(isCompleted) { + //check if the board is correctly completed + if(verify() && verifyCages()) { + win(); //animation => each cell randomly changes colors + window.win("Congratulations!", "You are the master!").showAndWait(); //show appropriate message + stage.close(); //close the mathdoku stage when the window with the message is closed + } else { + //the board is not correctly completed + window.win("Try again...", "...it's not correct.").show(); //show message in the win window + } } }); } - public void drawGrid(GridPane grid) { + /** + * this is the animation that will be displayed when the player wins the game + * each cell from the board randomly will be colored differently with a time of changing of 100 milliseconds + * it will look like a disco ball :) + */ + public void win() { - for(Cage cage : cages) { - for(Cell cell : cage.getCells()) { + //create the timeline for managing the animation + Timeline timeline = new Timeline(new KeyFrame(Duration.millis(100), actionEvent -> { - if(cell.hasLeftNeighbour(cage) && cell.hasUpNeighbour(cage)) { - cell.setStyle("-fx-border-color: black; -fx-border-width: 0 4 4 0;"); - } else if(cell.hasLeftNeighbour(cage)) { - cell.setStyle("-fx-border-color: black; -fx-border-width: 4 4 4 0;"); - } else if(cell.hasUpNeighbour(cage)) { - cell.setStyle("-fx-border-color: black; -fx-border-width: 0 4 4 4;"); - } else { - cell.setStyle("-fx-border-color: black; -fx-border-width: 4 4 4 4;"); + //loop through the cages and the cells to change the color randomly + for(Cage cage : cages) { + for(Cell cell : cage.getCells()) { + cell.setBackground(new Background(new BackgroundFill(Color.color(Math.random(), Math.random(), + Math.random()), CornerRadii.EMPTY, Insets.EMPTY))); } + } + })); + + timeline.setCycleCount(Transition.INDEFINITE); //make the changing of color go infinitely + timeline.play(); //start the time line + } + + /** + * method that manages the clear button from the mathdoku stage + * @param clear -> the button to add the action to + * @param grid -> necessary to clear the board of the game + * @param undo -> the undo button must be disabled + * @param redo -> the redo button must be disabled + */ + public void clearBoard(Button clear, GridPane grid, Button undo, Button redo) { - doubleClick(cell); - memoriseValues(cell); - grid.add(cell, cell.getColumn(), cell.getRow(), 1, 1); + //display a confirmation of action window so the player will confirm to clear the board + clear.setOnAction(actionEvent -> window.alertDialog("Are you sure you want to clear " + + "the board?").showAndWait().ifPresent(response -> { + //if the response is "YES" => clear board + if(response == ButtonType.YES) { + clear(grid); + undo.setDisable(true); + redo.setDisable(true); } - } + })); } + /** + * this method clears the board -> sets the values from the cells to null + * @param grid -> the grid to clear + */ public void clear(GridPane grid) { + + //loop through the cells of the grid for(int i =0; i < n; i ++) { for(int j = 0; j < n; j ++) { - cellValues[i][j] = 0; - getCell(grid, i, j).getTextField().clear(); + + cellValues[i][j] = 0; //set the value in the matrix at that position to 0 + getCell(grid, i, j).getTextField().clear(); //clear the text of the cell getCell(grid, i, j).setBackground(new Background(new BackgroundFill(Color.TRANSPARENT, - CornerRadii.EMPTY, Insets.EMPTY))); + CornerRadii.EMPTY, Insets.EMPTY))); //make the cell transparent again } } + + //the undo and redo stacks point now to null undoStack.root = null; redoStack.root = null; } - public void solveBoard(Button solve, GridPane grid, int n) { - solve.setOnAction(new EventHandler<ActionEvent>() { - @Override - public void handle(ActionEvent actionEvent) { - Solver solver = new Solver(n, grid); - solver.backtracking(1, 1); + /** + * this method manages the undo button from the mathdoku stage + * @param undo -> button to add the action to + * @param redo -> needed to enable the redo button + * @param grid -> to get a specific cell from the grid + */ + public void undoAction(Button undo, Button redo, GridPane grid) { + + undo.setOnAction(actionEvent -> { + + //disable the undo button when the undo stack is empty => can't do any other undo action + if(undoStack.isEmpty()) { + undo.setDisable(true); + } else { + redo.setDisable(false); //enable the redo action + Element e = undoStack.pop(); //pop an element from the undo stack + redoStack.push(e);//push that element to the redo stack + getCell(grid, e.i, e.j).getTextField().clear(); //clear the content from that specific cell + cellValues[e.i][e.j] = 0; //set the value of the matrix at that position } }); } - public void setTransparent(GridPane grid) { + /** + * this method manages the redo button from the mathdoku stage + * @param redo -> button to add the action to + * @param grid -> to get the specific cell from the grid + */ + public void redoAction(Button redo, GridPane grid) { + + redo.setOnAction(actionEvent -> { + + //disable the redo button when the redo stack is empty => can't do any redo action + if(redoStack.isEmpty()) { + redo.setDisable(true); + } else { + Element e = redoStack.pop(); //pop the element from the redo stack + getCell(grid, e.i, e.j).getTextField().setText(String.valueOf(e.value)); //set the content of that + // cell to the element popped from the redo stack + undoStack.push(e); //push the element back to the undo stack + } + }); + } + + /** + * method that will be responsible with adding the player's actions to the undo stack + * @param grid -> to loop through the cells from the grid + * @param undo -> to enable the undo stack + */ + public void addActions(GridPane grid, Button undo) { + + //loop through the cells of the board for(Node cell : grid.getChildren()) { - ((Cell) cell).setBackground(new Background(new BackgroundFill(Color.TRANSPARENT, - CornerRadii.EMPTY, Insets.EMPTY))); + + //add a listener to the text of the cells to add the numbers introduced to the undo stack + ((Cell) cell).getTextField().textProperty().addListener((observableValue, s, t1) -> { + + //only add the numeric values + if(isNumeric(((Cell) cell).getTextField().getText())) { + Element e = new Element(((Cell) cell).getRow(), ((Cell) cell).getColumn(), + Integer.parseInt(((Cell) cell).getTextField().getText())); //create the element that must + // be added to the undo stack + undoStack.push(e); //push that element to the undo stack + undo.setDisable(false); //enable the undo button + } + }); } } - public void highlight(GridPane grid) { + /** + * method that manages the font size change + * @param button -> press to change size of the text within a cell and the label of a cage + * @param number -> the number to use to change the size of the text + */ + public void changeFont(Button button, int number) { - for(int i = 0; i < n; i ++) { - for(int j = 0; j < n; j ++) { - if(cellValues[i][j] != 0) { + button.setOnAction(actionEvent -> { - if(!verifyRow(cellValues[i][j], i)) { - highlightRow(i, grid, true); - } + //loop through the cages of the board + for(Cage cage : cages) { + //loop through the cells of the board + for(Cell cell : cage.getCells()) { - if(!verifyColumn(cellValues[i][j], j)) { - highlightColumn(j, grid, true); - } + //set the new properties of the text + cell.getTextField().setStyle("-fx-text-box-border: transparent; " + + "-fx-background-color: transparent;"); + cell.getTextField().setFont(Font.font("Verdana", FontWeight.BOLD, number)); } - } - } - for(Cage cage : cages) { - highlightCage(cage); - } + //change the size of the label of the cage + cage.getCells().get(0).getLabel().setFont(Font.font(number - 10)); + } + }); } + /** + * this method helps with the functionality of the submit button + * determines if the board satisfied the property of having unique individual numbers on the row and column + * @return boolean depending if the row and column both satisfy the property + */ public boolean verify() { + //loop through the values of the matrix of the values of the cells for(int i = 0; i < n; i ++) { for(int j = 0; j < n; j ++) { + //verify for cells that are completed if(cellValues[i][j] !=0 ) { - if(verifyRow(cellValues[i][j], i) == false || verifyColumn(cellValues[i][j], j) == false) { - System.out.println(cellValues[i][j] + " " + i + " " + j); + //if the number is not unique on the row or the column, return false + if(!verifyRow(cellValues[i][j], i) || !verifyColumn(cellValues[i][j], j)) { return false; } } } } - return true; + return true; //return true when the property is satisfied } + /** + * this method verifies if the row contains only one unique value of a number + * @param x -> the number to verify if it appears only once in the row + * @param row -> the row to verify + * @return true if the number x only appears once in the row + */ public boolean verifyRow(int x, int row) { - int count = 0; + int count = 0; //set the number of appearances to 0 + //loop in the matrix of values on the row specified for(int i = 0; i < n; i ++) { if(cellValues[row][i] == x) { - count++; + count++; //count whenever a number in the row is equal to the number to be verified } } - if(count == 1) { - return true; - } - - return false; + return count == 1; //if it only appears once, return true, else return false } + /** + * this method verifies if the column contains only one unique value of a number + * @param x -> the number to verify if it appears only once in the column + * @param column -> the column to verify + * @return true if the number x only appears once in the column + */ public boolean verifyColumn(int x, int column) { - int count = 0; + int count = 0; //set the number of appearances to 0 + //loop in the matrix of values on the column specified for(int i = 0; i < n; i ++) { if(cellValues[i][column] == x) { - count++; + count++; //count whenever a number in the column is equal to the number to be verified } } - if(count == 1) { - return true; - } - return false; + return count == 1; //if it only appears once, return true, else return false } + /** + * this method helps with the functionality of the submit button + * it verifies if all the cages contain cells with values that satisfy the result + * @return true if all the cages satisfy the condition, else false + */ static boolean verifyCages() { for(Cage cage : cages) { @@ -363,10 +572,20 @@ public class Handler { return true; } + /** + * this method verifies is the values from the cells within a cage satisfy the operator of the cage and the result + * @param cage -> the cage to be verified + * @return true if the result is correct + */ public static boolean verifyCage(Cage cage) { + boolean isCorrect = true; + //if the cage is not complete, the method will return true if(cage.isCompleted()) { + + //if the cage doesn't have a symbol => the result from the cell must be the same as the symbol, + // else return false if(!cage.hasSymbol()) { if(Integer.parseInt(cage.getCells().get(0).getTextField().getText()) != Integer.parseInt(cage.getResult()) && Integer.parseInt(cage.getCells().get(0).getTextField().getText()) != 0) { @@ -386,120 +605,110 @@ public class Handler { return isCorrect; } + /** + * this method verifies a cage when the symbol for that cage is '+' + * @param cage -> cage to be verified + * @return true if the sum of the cells in the cage have the correct result of the cage + */ public static boolean verifySum(Cage cage) { - int sum = 0; + + int sum = 0; //initialise the sum with 0 + + //loop through the cells of the cage for(Cell cell : cage.getCells()) { - sum = sum + Integer.parseInt(cell.getTextField().getText()); + sum = sum + Integer.parseInt(cell.getTextField().getText()); //sum the value sof the cells from the cage } - return (sum == Integer.parseInt(cage.getResult())); + + return (sum == Integer.parseInt(cage.getResult())); //return true if the results are the same } + /** + * this method verifies a cage when the symbol for the cage is 'x' or '*' + * @param cage -> cage to be verified + * @return true if the product of the values of the cells from the cage has the correct result + */ public static boolean verifyProduct(Cage cage) { - int product = 1; + + int product = 1; //initialise the product with 1 + + //loop through the cells of the cage for(Cell cell : cage.getCells()) { - product = product * Integer.parseInt(cell.getTextField().getText()); + product = product * Integer.parseInt(cell.getTextField().getText()); //calculate the product of the values } - return ((product == Integer.parseInt(cage.getResult()))); + + return ((product == Integer.parseInt(cage.getResult()))); //return true if the results are the same } + /** + * this method verifies a cage when the symbol for that cage is '-' + * sort the values of the cells from the cage in descending order and subtract in order + * @param cage -> cage to be verified + * @return true if the subtraction of the values of the cells from the cage has the correct result + */ public static boolean verifySubtraction(Cage cage) { - int x = cage.getValues()[0]; - System.out.println(); - System.out.println(x); + + int x = cage.getValues()[0]; //get the first value of the descending sorted array of the + // values of the cells from the cage + + //loop through the values of the descending sorted array of the values of the cells from the cage for(int i = 1; i < cage.getValues().length; i ++) { - x = x - cage.getValues()[i]; - System.out.println(x); + x = x - cage.getValues()[i]; //subtract each value from the result } - return (x == Integer.parseInt(cage.getResult())); + + return (x == Integer.parseInt(cage.getResult())); //return true if the results correspond } + /** + * this method verifies a cage when the symbol for the cage is '/' or 'รท' + * sort the values of the cells of the cage in descending order and divide in order + * @param cage -> cage to be verified + * @return true if the division of the values of the cells from the cage has the correct result + */ public static boolean verifyDivision(Cage cage) { - int x = cage.getValues()[0]; + + int x = cage.getValues()[0]; //get the first value of the descending sorted array of the + // values of the cells form the cage + + //loop through the values of the descending sorted array of the values of the cells from the cage for(int i = 1; i < cage.getValues().length; i ++) { + //if the module operator doesn't result in 0, then return false if(x % cage.getValues()[i] == 0) { - x = x / cage.getValues()[i]; + x = x / cage.getValues()[i]; //else, divide the greatest number (x) to the next value in the array } else { return false; } } - return (x == Integer.parseInt(cage.getResult())); - } - public void highlightRow(int row, GridPane grid, boolean highlight) { - - if(highlight) { - for(int i = 0; i < n; i ++) { - getCell(grid, row, i).setBackground(new Background(new BackgroundFill(Color.YELLOW, - CornerRadii.EMPTY, Insets.EMPTY))); - } - } else { - for(int i = 0; i < n; i ++) { - getCell(grid, row, i).setBackground(new Background(new BackgroundFill(Color.TRANSPARENT, - CornerRadii.EMPTY, Insets.EMPTY))); - } - } - } - - public void highlightColumn(int column, GridPane grid, boolean highlight) { - - if(highlight) { - for(int i = 0; i < n; i ++) { - getCell(grid, i, column).setBackground(new Background(new BackgroundFill(Color.YELLOW, - CornerRadii.EMPTY, Insets.EMPTY))); - } - } else { - for(int i = 0; i < n; i ++) { - getCell(grid, i, column).setBackground(new Background(new BackgroundFill(Color.TRANSPARENT, - CornerRadii.EMPTY, Insets.EMPTY))); - } - } - } - - public void highlightCage(Cage cage) { - - if(!verifyCage(cage)) { - for(Cell cell : cage.getCells()) { - cell.setBackground(new Background(new BackgroundFill(Color.RED, - CornerRadii.EMPTY, Insets.EMPTY))); - } - } - } - - public void win() { - Timeline timeline = new Timeline(new KeyFrame(Duration.millis(100), new EventHandler<ActionEvent>() { - @Override - public void handle(ActionEvent actionEvent) { - for(Cage cage : cages) { - for(Cell cell : cage.getCells()) { - cell.setBackground(new Background(new BackgroundFill(Color.color(Math.random(), Math.random(), - Math.random()), CornerRadii.EMPTY, Insets.EMPTY))); - } - } - } - })); - - timeline.setCycleCount(Transition.INDEFINITE); - timeline.play(); + return (x == Integer.parseInt(cage.getResult())); //return true if the results correspond } + /** + * this method will return the cell from a grid pane, placed at a specific position (i, j) + * @param grid -> the grid which contains the cell + * @param i -> number of the row position + * @param j -> number of the column position + * @return the cell at the corresponding position in the grid + */ static Cell getCell(GridPane grid, int i, int j) { + + //loop through the cells of the grid for(Node cell : grid.getChildren()) { + + //return the cell at the position (i,j) if(((Cell) cell).getRow() == i && ((Cell) cell).getColumn() == j) { return (Cell) cell; } } - return null; - } - public int[][] getCellValues() { - return cellValues; + return null; //return null when the cell doesn't exist in the grid } - public void setN(int n) { - this.n = n; - } - - public boolean isNumeric(String x) { + /** + * this method verifies if the number within a cage is numeric + * @param x -> the value to be verified + * @return true if there exists no exception when trying to convert the value into an integer, false otherwise + */ + public static boolean isNumeric(String x) { try { Integer.parseInt(x); } catch (NumberFormatException e) { @@ -507,4 +716,12 @@ public class Handler { } return true; } + + public void solveBoard(Button solve, GridPane grid, int n) { + solve.setOnAction(actionEvent -> { + Solver solver = new Solver(n, grid); + solver.backtracking(1, 1); + }); + } } + diff --git a/UndoRedo.java b/UndoRedo.java index 0e86438..9d37729 100644 --- a/UndoRedo.java +++ b/UndoRedo.java @@ -1,37 +1,56 @@ - +/** + * this class creates a basic implementation of a stack + * the stack has a root, an address to the next element and the value of the element + * a stack can be empty + * operations specific to a stack: pup() and push() + * this implementation will make the buttons undo and redo functional + */ public class UndoRedo { - StackNode root; + StackNode root; //the root of the stack - private class StackNode { - Element e; - StackNode next; + /** + * the class that defines a node in the stack + */ + private static class StackNode { + Element e; //the value inside the node + StackNode next; //the address to the next element in the stack + /** + * @param e is created to be added to the stack + */ StackNode(Element e) { this.e = e; } } + /** + * @return true if the stack is empty + */ public boolean isEmpty() { - if(root == null) { - return true; - } else { - return false; - } + return root == null; } + /** + * @param e is pushed to the top of the stack + */ public void push(Element e) { + StackNode newNode = new StackNode(e); + //if the root is null => it is the first element in the stack => it becomes the root if(root == null) { root = newNode; } else { - StackNode temp = root; + StackNode temp = root; root = newNode; newNode.next = temp; } } + /** + * @return the element on top of the stack + */ public Element pop() { if(isEmpty()) { diff --git a/Window.java b/Window.java index 1139c3d..45c9e4a 100644 --- a/Window.java +++ b/Window.java @@ -1,5 +1,3 @@ -import javafx.event.ActionEvent; -import javafx.event.EventHandler; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.Scene; @@ -13,168 +11,216 @@ import javafx.stage.Modality; import javafx.stage.Stage; import javafx.stage.StageStyle; import java.util.ArrayList; -import java.util.TimerTask; +/** + * this class creates windows that will appear depending on the situation + * -> window for introduction of values via mouse + * -> window for the winning status of the player + * -> window for confirmation to clear the board + * -> window for showing an error about the input + * -> window for showing the proper game window + */ public class Window { - static int n; + static int n; //the size of the board nxn -> static to be the same everywhere + /** + * this method shows a window which will be displayed when the player double clicks on a cell to introduce + * or delete values via mouse + * @param cell -> the cell which will be modified + */ public void showButtons(Cell cell) { + Stage stage = new Stage(); + + //create the menu (root) of the scene which will contain all the elements; set its properties VBox menu = new VBox(10); menu.setPadding(new Insets(10)); menu.setAlignment(Pos.TOP_LEFT); menu.setStyle("-fx-background-color: MediumSeaGreen; -fx-border-color: white; -fx-border-width: 4 4 4 4;"); + //create a label to ask the player which button will choose; set properties of the label Label message = new Label("Which one will you choose?"); message.setFont(Font.font(20)); message.setStyle("-fx-font-size: 20px; -fx-font-weight:bold;"); - menu.getChildren().add(message); + menu.getChildren().add(message); //add the message to the vertical box - ToggleGroup toggle = new ToggleGroup(); + ToggleGroup toggle = new ToggleGroup(); //create a toggle to group the Radio buttons => the player is allowed + //to only select one option from the buttons + //create the Delete button to delete the value in the cell; set properties of the button RadioButton delete = new RadioButton("Delete"); delete.setFont(Font.font(20)); delete.setStyle("-fx-font-size: 20px; -fx-font-weight:bold;"); + + //add the delete button to the toggle group and to the menu delete.setToggleGroup(toggle); menu.getChildren().add(delete); + //create the radio buttons which are between 1 and n (size of board) for(int i = 0; i < n; i ++) { RadioButton number = new RadioButton(String.valueOf(i + 1)); number.setFont(Font.font(20)); number.setStyle("-fx-font-size: 20px; -fx-font-weight:bold;"); + + //add the button to the toggle group and to the menu (vertical box) number.setToggleGroup(toggle); menu.getChildren().add(number); } - Button choice = new Button("Choose"); - - choice.setFont(Font.font(20)); - choice.setStyle("-fx-font-size: 20px; -fx-font-weight:bold;"); - choice.setCancelButton(true); + //create a button to confirm the option selected in the toggle group; set properties + Button select = new Button("Select"); + select.setFont(Font.font(20)); + select.setStyle("-fx-font-size: 20px; -fx-font-weight:bold;"); + select.setCancelButton(true); - menu.getChildren().add(choice); + //add the select button to the menu + menu.getChildren().add(select); + //create the scene with the root being the menu Scene scene = new Scene(menu); - Stage inputButtons = new Stage(); - choice.setOnAction(new EventHandler<ActionEvent>() { - @Override - public void handle(ActionEvent actionEvent) { - if(toggle.getSelectedToggle() != null) { + //add actions to the button select -> to complete the cell with the value selected or delete the value from cell + select.setOnAction(actionEvent -> { - RadioButton selectedRadioButton = (RadioButton) toggle.getSelectedToggle(); + //one of the buttons has to be selected before selecting + if(toggle.getSelectedToggle() != null) { + RadioButton selectedRadioButton = (RadioButton) toggle.getSelectedToggle(); //value selected from toggle - if(selectedRadioButton.equals("Delete")) { - cell.getTextField().clear(); - } else { - cell.getTextField().setText(selectedRadioButton.getText()); - } - - inputButtons.close(); + if(selectedRadioButton.equals(delete)) { + cell.getTextField().clear(); //delete the value from the cell if "Delete" selected + } else { + cell.getTextField().setText(selectedRadioButton.getText()); //set the new vallue to the cell } + + stage.close(); } }); - inputButtons.setScene(scene); - inputButtons.initStyle(StageStyle.UTILITY); - inputButtons.initModality(Modality.APPLICATION_MODAL); - inputButtons.setTitle("Button Control"); - //inputButtons.setMinWidth(300); - //inputButtons.setMinHeight(100); - - inputButtons.setX(400); - inputButtons.setY(400); - - inputButtons.toFront(); - inputButtons.show(); - inputButtons.setResizable(false); + //set the scene to the stage; add properties to the stage + stage.setScene(scene); + stage.initStyle(StageStyle.UTILITY); + stage.initModality(Modality.APPLICATION_MODAL); + stage.setTitle("Button Control"); + stage.setX(400); + stage.setY(400); + stage.toFront(); //always to front + + stage.show(); + stage.setResizable(false); //don't permit to resize the stage } + /** + * this method will return a window with a message depending on the state of the player in the game + * if the player presses the submit button, different messages will pop up + * @param message1 -> header of the window + * @param message2 -> a comment of state of the player + * @return stage with properties + */ public Stage win(String message1, String message2) { + Stage stage = new Stage(); //create the stage + + //create the menu of the elements (root); set properties VBox menu = new VBox(5); menu.setAlignment(Pos.TOP_CENTER); menu.setStyle("-fx-background: pink;"); + //create messages for the player and set their properties Label congrats = new Label(message1); congrats.setFont(Font.font( "Verdana", FontWeight.BOLD, 35)); - Label message = new Label(message2); - message.setFont(Font.font(25)); + Label comment = new Label(message2); + comment.setFont(Font.font(25)); - menu.getChildren().addAll(congrats, message); + menu.getChildren().addAll(congrats, comment); //add the elements to the menu (root) + //create the scene with the root being the menu and set it to the stage Scene scene = new Scene(menu); + stage.setScene(scene); - Stage winWindow = new Stage(); - winWindow.setScene(scene); - - winWindow.initStyle(StageStyle.UTILITY); - winWindow.initModality(Modality.APPLICATION_MODAL); - winWindow.setTitle("Situation"); - - winWindow.setMinHeight(150); - winWindow.setMinWidth(400); - - winWindow.setX(800); - winWindow.setY(500); + //add properties to the stage + stage.initStyle(StageStyle.UTILITY); + stage.initModality(Modality.APPLICATION_MODAL); + stage.setTitle("Situation"); + stage.setMinHeight(150); + stage.setMinWidth(400); + stage.setX(800); + stage.setY(500); - return winWindow; + return stage; //return the stage created } - public Alert getAlertDialog(String str) { + /** + * window created to get the confirmation of the player that they want to clear the board + * @param str -> set the message for the alert dialog + * @return alert window -> confirmation of action (warning) + */ + public Alert alertDialog(String str) { + //create the alert dialog and set properties Alert alert = new Alert(Alert.AlertType.WARNING, str, - ButtonType.YES); + ButtonType.YES, ButtonType.NO); alert.setX(800); alert.setY(400); alert.setTitle("Confirmation of Action"); - return alert; + return alert; //return the warning alert created } + /** + * window created to notify the player about an error regarding the input to create a game + * @param comment -> message that will be displayed to the error window + * @return alert window -> error + */ public Alert inputError(String comment) { + + //create the alert dialog and set properties Alert alert = new Alert(Alert.AlertType.ERROR); alert.setTitle("Error"); alert.setHeaderText("Input error"); alert.setContentText(comment); - return alert; + return alert; //return the warning alert created } - public Stage mathdokuStage(int n, ArrayList<Cage> cages) { + /** + * window that will create the game board and the buttons specific to it + * all the options for the game are accessed from this window + * @param n -> the size of the board nxn + * @param cages -> the cages that were extracted when a file was opened from the menu window + * @return Stage -> the Mathdoku window where a a user can play the game + */ + public Stage mathdoku(int n, ArrayList<Cage> cages) { - this.n = n; + Window.n = n; //set the size of the board to the n selected by the player in the menu window + Handler handler = new Handler(cages); //create a handler object to handle the functionalities of the game - Handler handler = new Handler(n, cages); - handler.setN(n); - Stage stage = new Stage(); - TimerTask task= new TimerTask() { - @Override - public void run() { - - } - }; + Stage stage = new Stage(); //create the stage for the game + //create the root for the elements that will be added to the scene; set properties of the root VBox root = new VBox(5); root.setStyle("-fx-background-color: SeaShell"); root.setAlignment(Pos.CENTER); root.setPadding(new Insets(20)); + //create the top button menu -> horizontal box HBox buttons = new HBox(); buttons.setAlignment(Pos.CENTER); + //create the grid where the cells of the game will be added; set properties GridPane grid = new GridPane(); grid.setAlignment(Pos.CENTER); grid.setStyle("-fx-border-color: black; -fx-border-width: 4 4 4 4;"); + //create the buttons to be added to the top menu of buttons Button undo = new Button("Undo"); Button redo = new Button("Redo"); Button help = new Button("Enable Help"); Button clear = new Button("Clear"); Button hint = new Button("Hint"); + //set properties for the buttons undo.setStyle("-fx-font-size: 20px; -fx-font-weight:bold; -fx-background-color: MediumSeaGreen;" + "-fx-border-color: black; -fx-border-width: 4 4 4 4;"); redo.setStyle("-fx-font-size: 20px; -fx-font-weight:bold; -fx-background-color: MediumSeaGreen;" + @@ -186,23 +232,31 @@ public class Window { hint.setStyle("-fx-font-size: 20px; -fx-font-weight:bold; -fx-background-color: MediumSeaGreen;" + "-fx-border-color: black; -fx-border-width: 4 4 4 4;"); + //disable the redo and undo buttons (there is nothing to undo or redo yet) undo.setDisable(true); redo.setDisable(true); + //set the functionality for each button from the top menu of buttons handler.helpButton(help, grid); handler.clearBoard(clear, grid, undo, redo); handler.undoAction(undo, redo, grid); handler.redoAction(redo, grid); - buttons.getChildren().addAll(undo, redo, help, clear, hint); + buttons.getChildren().addAll(undo, redo, help, clear, hint); //add the buttons to the top button menu + + handler.drawGrid(grid); //draw the cells in the grid + handler.addActions(grid, undo); //add actions to the undo stack when the undo button is pressed - handler.drawGrid(grid); - handler.addActions(grid, undo); + //create the bottom button menu to change the font size of the numbers in the cells + HBox sizes = new HBox(5); + sizes.setAlignment(Pos.CENTER); + //create the buttons for the bottom menu buttons which will change the font size of the numbers in the cells Button small = new Button("Small"); Button medium = new Button("Medium"); Button large = new Button("Large"); + //set properties for the buttons from the bottom button menu small.setStyle("-fx-font-size: 20px; -fx-font-weight:bold; -fx-background-color: MediumSeaGreen;" + " -fx-border-color: black; -fx-border-width: 4 4 4 4;"); medium.setStyle("-fx-font-size: 20px; -fx-font-weight:bold; -fx-background-color: MediumSeaGreen; " + @@ -210,60 +264,45 @@ public class Window { large.setStyle("-fx-font-size: 20px; -fx-font-weight:bold; -fx-background-color: MediumSeaGreen; " + "-fx-border-color: black; -fx-border-width: 4 4 4 4;"); - HBox sizes = new HBox(5); - sizes.setAlignment(Pos.CENTER); + sizes.getChildren().addAll(small, medium, large); //add elements to the bottom menu of buttons + + //add functionality to the font size changers + handler.changeFont(small, 20); + handler.changeFont(medium, 30); + handler.changeFont(large, 40); - sizes.getChildren().addAll(small, medium, large); + Label note = new Label("Double click the space provided for text in cell"); //message for the player to + //be aware of the functionality - Label note = new Label("Double click the space provided for text in cell"); - Button submit = new Button("Submit"); + Button submit = new Button("Submit"); //submit button to check the answer Button solve = new Button("Solve"); + //set properties for the note and the submit button note.setStyle("-fx-font-size: 15px; -fx-font-weight:bold;"); submit.setStyle("-fx-font-size: 20px; -fx-font-weight:bold; -fx-background-color: pink; " + "-fx-border-color: black; -fx-border-width: 4 4 4 4;"); - handler.changeFont(small, 20); - handler.changeFont(medium, 30); - handler.changeFont(large, 40); + //add functionalities to the submit button handler.submitButton(submit, stage); handler.solveBoard(solve, grid, n); - root.getChildren().addAll(buttons, grid, sizes, note, submit); - - root.widthProperty().addListener((obs, oldVal, newVal) -> { - grid.setMaxWidth((Double) newVal - 100); - for(Cage cage : cages) { - for(Cell cell : cage.getCells()) { - cell.setPrefWidth((Double) newVal - 100); - cell.getTextField().setPrefSize(cell.getWidth(), cell.getHeight()); - } - } - }); + root.getChildren().addAll(buttons, grid, sizes, note, submit); //add the elements to the root - root.heightProperty().addListener((obs, oldVal, newVal) -> { - grid.setMaxHeight((Double) newVal - 100); - for(Cage cage : cages) { - for(Cell cell : cage.getCells()) { - cell.setPrefHeight((Double) newVal- 100); - cell.getTextField().setPrefSize(cell.getWidth(), cell.getHeight()); - } - } - }); + handler.resize(root, grid); //resize the board of the game + //create the scene with the root the vertical box and set it to the stage Scene scene = new Scene(root); stage.setScene(scene); + //set properties for the stage stage.setTitle("Mathdoku"); stage.initStyle(StageStyle.DECORATED); - stage.setX(700); stage.setY(200); - stage.setMinHeight(700); stage.setMinWidth(700); - return stage; + return stage; //return the stage created } } -- GitLab