Skip to content
Snippets Groups Projects
Commit 3d12e9df authored by tmp1u19's avatar tmp1u19 :octopus:
Browse files

Add Solver to the board

parent 276b3df9
No related branches found
No related tags found
No related merge requests found
......@@ -76,8 +76,8 @@ public class Cell extends Pane {
for(Cell cell : cage.getCells()) {
if((cell.getCellNumber() == getCellNumber() - 1 ||
cell.getCellNumber() == getCellNumber() + 1 ||
cell.getCellNumber() == getCellNumber() - 8 ||
cell.getCellNumber() == getCellNumber() + 8) &&
cell.getCellNumber() == getCellNumber() - Window.n ||
cell.getCellNumber() == getCellNumber() + Window.n) &&
cell.getCellNumber() != getCellNumber()) {
return true;
}
......@@ -92,5 +92,13 @@ public class Cell extends Pane {
public int getCellNumber() {
return cellNumber;
}
public void setValue(int x) {
getTextField().setText(String.valueOf(x));
}
public int getValue() {
return Integer.parseInt(getTextField().getText());
}
}
......@@ -16,27 +16,52 @@ import javafx.stage.Stage;
import java.io.*;
import java.util.ArrayList;
/**
* this class holds the main method and runs the application
* it creates the menu page where the user can select the size of
* the board and the way the user wants to input the board
* the class opens and reads a file and memorises the cages and their
* label (result and symbol) and the cells of the cells
* the class also provides methods for verifying if the input of the user
* can create a valid board for the game
* -> if a cell appears exactly once in just one cage
* -> if the cells are adjacent in a cage
*/
public class GameScene extends Application {
private ArrayList<Cage> cages = new ArrayList<Cage>();
private Window window = new Window();
static ArrayList<Cage> cages = new ArrayList<Cage>(); //memorises the cages from the input
private Window window = new Window(); //helps to display different stages when needed
/**
* main function which launches the application
*/
public static void main(String[] args) {
launch(args);
}
/**
* overrides method for the application to start an application
* @param stage -> is the main stage where the menu is displayed
*/
@Override
public void start(Stage stage) {
FileChooser fileChooser = new FileChooser();
VBox root = new VBox(20);
FileChooser fileChooser = new FileChooser(); //used to choose a file from the system
VBox root = new VBox(20); //the root of the application where all elements will be added
//set the style for the root
root.setStyle("-fx-background-color: SeaShell");
root.setPrefSize(800, 700);
root.setAlignment(Pos.TOP_CENTER);
root.setPadding(new Insets(30));
//set a title of the content of the application
Label greeting = new Label("Mathdoku");
greeting.setStyle("-fx-font-size: 70px; -fx-font-weight:bold;");
//add the instructions of the game
String instructions = "A player needs to fill the cells in an NxN square grid with the numbers 1 to N " +
"(one number per cell), while adhering to the following constraints:" + '\n' +
"- Each number must appear exactly once in each row." + '\n' +
......@@ -49,115 +74,145 @@ public class GameScene extends Application {
" operator to the numbers in that cage. For - and ÷, this can be done in any order." + '\n' +
"Note: If a cage consists of a single cell, then no arithmetic operator is shown. " +
"The label simply shows the number that must be in that cell.";
Label instruct = new Label("Instructions:" + '\n' + instructions);
//set properties and style for the instructions
instruct.setWrapText(true);
instruct.setStyle("-fx-font-size: 15px;");
instruct.setPadding(new Insets(40));
/* create the grid pane which contains the choice box with functionalities:
-> select size for the board of the game
-> select way in which you want to read input
*/
GridPane buttons = new GridPane();
buttons.setPadding(new Insets(20));
buttons.setHgap(10);
buttons.setAlignment(Pos.TOP_CENTER);
//button which will change the stage to the one of Mathdoku game
Button play = new Button("Play");
//set style and properties of the button
play.setStyle("-fx-font-size: 20px; -fx-font-weight:bold; -fx-background-color: MediumSeaGreen;" +
"-fx-border-color: black; -fx-border-width: 4 4 4 4;");
play.setDisable(true);
play.setDisable(true); //disable the play button until the all the details will be inputted
//label to let the user know where to input the size
Label size = new Label("Choose size: ");
size.setStyle("-fx-font-size: 20px; -fx-font-weight:bold;");
//the size of the board will be selected; the size is between 2x2 and 8x8
ChoiceBox cbSize = new ChoiceBox();
cbSize.setItems(FXCollections.observableArrayList(
"2x2", "3x3", "4x4", "5x5",
"6x6", "7x7", "8x8")
);
//set style and properties for the choice box
cbSize.setStyle("-fx-font-size: 15px;");
cbSize.setPrefWidth(110);
//label to let the user know where to input the file or where to access the text area
Label input = new Label("Input from: ");
input.setStyle("-fx-font-size: 20px; -fx-font-weight:bold;");
//the way of inputting will be selected: file or direct input in a special text area
ChoiceBox cbInput = new ChoiceBox();
cbInput.setItems(FXCollections.observableArrayList(
"file", "text area")
);
//set style and properties for the choice box
cbInput.setStyle("-fx-font-size: 15px;");
cbInput.setPrefWidth(110);
//add all the buttons to the grid pane with the coordinates where to be placed
buttons.add(size, 0, 0, 1, 1);
buttons.add(cbSize, 1, 0, 1, 1);
buttons.add(input, 2, 0, 1, 1);
buttons.add(cbInput, 3, 0, 1, 1);
//disable the input button -> first the user has to select the size of the board
input.setDisable(true);
cbInput.setDisable(true);
/*
if the size of the board is selected, permit the user to input -> enable the input button
*/
cbSize.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<String>() {
@Override
public void changed(ObservableValue<? extends String> observableValue, String oldValue,
String newValue) {
size.setDisable(true);
size.setDisable(true); //disable the input label for design
input.setDisable(false);
cbInput.setDisable(false);
play.setDisable(true);
}
});
/*
* get the size of the board selected
* identify the way the input is wanted to be inputted:
-> file:
* use the file chooser to choose a file from the system
* if the file is null, don't permit the user to play
* if the file is not null, open it and use it as input
-> text area:
* input from the text area
*/
cbInput.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<String>() {
@Override
public void changed(ObservableValue<? extends String> observableValue, String oldValue, String newValue) {
int n = Character.getNumericValue(cbSize.getSelectionModel().getSelectedItem().toString().charAt(0));
//play.setDisable(false);
if(newValue.equals("file")) {
if(cbSize.getSelectionModel().getSelectedItem() != null) {
File file = fileChooser.showOpenDialog(stage); //memorise the choice of the user
File file = fileChooser.showOpenDialog(stage);
if(file != null) {
try {
openFile(file, n, play);
} catch (IOException e) {
e.printStackTrace();
}
} else {
play.setDisable(true);
//verify if the file is selected
if(file != null) {
try {
openFile(file, n, play); //open the file and use it as input for the game
} catch (IOException e) {
e.printStackTrace();
}
} else {
play.setDisable(true); //if the file is not selected, disable the play button
}
} else if(newValue.equals("text area")) {
directInput(n, play);
directInput(n, play); //input from text area if the text area is selected
}
input.setDisable(true);
input.setDisable(true); //disable the input label for design
}
});
/*
* 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();
stage.close(); //close the stage
String size = cbSize.getSelectionModel().getSelectedItem().toString();
Stage newStage = window.mathdokuStage(Character.getNumericValue(size.charAt(0)), cages);
newStage.show();
window.mathdokuStage(Character.getNumericValue(size.charAt(0)),
cages).show(); //show the Mathdoku game stage
}
});
root.getChildren().addAll(greeting, instruct, buttons, play);
root.getChildren().addAll(greeting, instruct, buttons, play); //add the labels and buttons to the root
//create the scene with parent being the root
Scene scene = new Scene(root);
stage.setScene(scene);
//set properties for the stage
stage.setTitle("Welcome to Mathdoku!");
stage.setX(650);
stage.setY(250);
......@@ -166,60 +221,93 @@ public class GameScene extends Application {
stage.setMinHeight(700);
stage.show();
stage.setResizable(false);
stage.setResizable(false); //don't resize the menu window
}
public boolean openFile(File file, int n, Button play) throws IOException {
/**
* this method opens an input file and creates the cages with the details provided
* after creating the cages, check if the input is correct and can create a logical game based
* on the the numbers provided
* @param file -> reads from the file with the data for the board
* @param n -> represents the size of the board game
* @param play -> the button which has to be enabled or disabled in different situations
* @return boolean -> depending on the input and if it can create a valid board for the game
* @throws IOException
*/
public void openFile(File file, int n, Button play) throws IOException {
BufferedReader buffer = new BufferedReader(new FileReader(file.getPath()));
BufferedReader buffer = new BufferedReader(new FileReader(file.getPath())); //extract the data from the file
String line = buffer.readLine();
String line = buffer.readLine(); //read first line
//loop through all the lines using a buffer until it reaches the end of the file
while(line != null) {
String str[] = line.split(" ");
Cage cage = new Cage(n);
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
//check if the first string from the array is just a number
if(isNumeric(str[0])) {
cage.setResult(str[0]);
cage.addCell(Integer.parseInt(str[1]));
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
// the array
//else, there are more cells in the cage
} else {
String[] cells = str[1].split(",");
String[] cells = str[1].split(","); //create an array of strings that memorises all the cells
//add the cells to the cage
for(String cell : cells) {
cage.addCell(Integer.parseInt(cell));
}
//set the symbol and result of the cage
cage.setSymbol(str[0].charAt(str[0].length() - 1));
cage.setResult(str[0].substring(0, str[0].length() - 1));
}
cage.getCells().get(0).setLabel(str[0]);
cage.setLabel(str[0]);
cages.add(cage);
line = buffer.readLine();
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);
}
public boolean checkInput(int n, Button play) throws IOException {
try {
//check if the cells appear exactly in one cage
if(!checkUniqueCells(n)) {
cages = new ArrayList<Cage>();
play.setDisable(true);
//display an error window with message
window.inputError("The cells are not part of exactly " +
"one cage.").showAndWait();
return false;
}
//if ArrayIndexOutOfBoundsException appears =>
// => the input file provides data for a much larger board
} catch (ArrayIndexOutOfBoundsException e) {
cages = new ArrayList<Cage>();
play.setDisable(true);
//display an error window with message
window.inputError("Some cells don't belong to the bord").showAndWait();
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();
return false;
}
play.setDisable(false);
return true;
return true; //return true if everything is correct
}
public boolean checkUniqueCells(int n) throws IOException {
......@@ -241,16 +329,37 @@ public class GameScene extends Application {
return true;
}
public boolean checkAdjacent() {
public int checkAdjacent(int n) {
int[] paths = new int[n*n + 1];
int count = 0;
for(int i = 1; i <= n * n; i ++) {
paths[i] = i;
}
for(Cage cage : cages) {
for(Cell cell : cage.getCells()) {
if(!cell.isAdjacent(cage)) {
return false;
if(cell.hasUpNeighbour(cage)) {
paths[cell.getCellNumber()] = cell.getCellNumber() - n;
}
if(cell.hasLeftNeighbour(cage)) {
if(paths[cell.getCellNumber() - 1] != cell.getCellNumber() - 1) {
paths[cell.getCellNumber()] = cell.getCellNumber() - 1;
} else {
paths[cell.getCellNumber() - 1] = cell.getCellNumber();
}
}
}
}
return true;
for(int i =1; i <= n * n; i ++) {
if(i == paths[i]) {
count++;
}
}
return count;
}
public void directInput(int n, Button play) {
......
......@@ -16,6 +16,7 @@ import javafx.scene.layout.GridPane;
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;
......@@ -24,10 +25,10 @@ public class Handler {
private Window window = new Window();
private int n;
private int[][] cellValues;
static int[][] cellValues;
private UndoRedo undoStack = new UndoRedo();
private UndoRedo redoStack = new UndoRedo();
private ArrayList<Cage> cages;
public static ArrayList<Cage> cages;
public Handler(int n, ArrayList<Cage> cages) {
this.n = n;
......@@ -62,29 +63,31 @@ public class Handler {
});
}
public void submitButton(Button submit) {
public void submitButton(Button submit, Stage stage) {
submit.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent actionEvent) {
for(int i = 0; i < n; i ++) {
for(int j = 0; j < n; j ++) {
System.out.print(getCellValues()[i][j]);
}
System.out.println();
}
System.out.println();
boolean completed = true;
for(Cage cage : cages) {
System.out.print(cage.getResult() + " ");
if(!cage.isCompleted()) {
window.win("Complete de board!", "Don't stop now...").show();
completed = false;
break;
}
}
if(verify() && verifyCages()) {
win();
} else {
window.win("Too bad...", "You lost the game");
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();
}
}
}
});
}
......@@ -262,6 +265,16 @@ public class Handler {
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);
}
});
}
public void setTransparent(GridPane grid) {
for(Node cell : grid.getChildren()) {
((Cell) cell).setBackground(new Background(new BackgroundFill(Color.TRANSPARENT,
......@@ -340,7 +353,7 @@ public class Handler {
return false;
}
public boolean verifyCages() {
static boolean verifyCages() {
for(Cage cage : cages) {
if(!verifyCage(cage)) {
......@@ -350,7 +363,7 @@ public class Handler {
return true;
}
public boolean verifyCage(Cage cage) {
public static boolean verifyCage(Cage cage) {
boolean isCorrect = true;
if(cage.isCompleted()) {
......@@ -373,7 +386,7 @@ public class Handler {
return isCorrect;
}
public boolean verifySum(Cage cage) {
public static boolean verifySum(Cage cage) {
int sum = 0;
for(Cell cell : cage.getCells()) {
sum = sum + Integer.parseInt(cell.getTextField().getText());
......@@ -381,7 +394,7 @@ public class Handler {
return (sum == Integer.parseInt(cage.getResult()));
}
public boolean verifyProduct(Cage cage) {
public static boolean verifyProduct(Cage cage) {
int product = 1;
for(Cell cell : cage.getCells()) {
product = product * Integer.parseInt(cell.getTextField().getText());
......@@ -389,7 +402,7 @@ public class Handler {
return ((product == Integer.parseInt(cage.getResult())));
}
public boolean verifySubtraction(Cage cage) {
public static boolean verifySubtraction(Cage cage) {
int x = cage.getValues()[0];
System.out.println();
System.out.println(x);
......@@ -400,7 +413,7 @@ public class Handler {
return (x == Integer.parseInt(cage.getResult()));
}
public boolean verifyDivision(Cage cage) {
public static boolean verifyDivision(Cage cage) {
int x = cage.getValues()[0];
for(int i = 1; i < cage.getValues().length; i ++) {
if(x % cage.getValues()[i] == 0) {
......@@ -469,7 +482,7 @@ public class Handler {
timeline.play();
}
public Cell getCell(GridPane grid, int i, int j) {
static Cell getCell(GridPane grid, int i, int j) {
for(Node cell : grid.getChildren()) {
if(((Cell) cell).getRow() == i && ((Cell) cell).getColumn() == j) {
return (Cell) cell;
......@@ -495,4 +508,3 @@ public class Handler {
return true;
}
}
import javafx.scene.layout.GridPane;
import javax.print.attribute.HashDocAttributeSet;
public class Solver {
private int n;
private int[][] sol;
private GridPane grid;
public Solver(int n, GridPane grid) {
this.n = n;
sol = new int[n+1][n+1];
for(int i = 1; i <= n; i ++) {
for(int j = 1; j <= n; j ++) {
sol[i][j] = 0;
}
}
this.grid = grid;
}
void first(int row, int col) {
sol[row][col] = 0;
}
boolean next(int row, int col) {
if(sol[row][col] < n) {
sol[row][col]++;
return true;
}
return false;
}
boolean isValid(int row, int col) {
for(int i = 1; i < col; i ++) {
if(sol[row][col] == sol[row][i]) {
return false;
}
}
for(int i = 1; i < row; i ++) {
if(sol[row][col] == sol[i][col]) {
return false;
}
}
for(int i = 1; i < col; i ++) {
Handler.getCell(grid, row - 1, i - 1).setValue(sol[row][i]);
}
for(int i = 1; i < row; i ++) {
Handler.getCell(grid, i - 1, col - 1).setValue(sol[i][col]);
}
for(Cage cage : Handler.cages) {
if(!Handler.verifyCage(cage)) {
return false;
}
}
return true;
}
boolean isSolution(int col, int row) {
if(col == n && row == n) {
Handler.getCell(grid, row - 1, col - 1).setValue(sol[row][col]);
return true;
}
return false;
}
void out() {
for(int i = 1; i <= n; i ++) {
for(int j = 1; j <=n; j ++) {
System.out.print(sol[i][j] + " ");
}
System.out.println();
}
}
void backtracking(int row, int col) {
first(row, col);
while(next(row, col)) {
if(isValid(row, col)) {
if(isSolution(col, row) && Handler.verifyCages()) {
out();
System.out.println();
} else {
if(col == n) {
backtracking(row + 1, 1);
} else {
backtracking(row, col + 1);
}
}
}
}
}
}
......@@ -8,6 +8,7 @@ import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
......@@ -23,30 +24,34 @@ public class Window {
VBox menu = new VBox(10);
menu.setPadding(new Insets(10));
menu.setAlignment(Pos.TOP_LEFT);
menu.setStyle("-fx-background: rgb(201, 76, 76);");
menu.setStyle("-fx-background-color: MediumSeaGreen; -fx-border-color: white; -fx-border-width: 4 4 4 4;");
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);
ToggleGroup toggle = new ToggleGroup();
RadioButton delete = new RadioButton("Delete");
delete.setFont(Font.font(20));
delete.setStyle("-fx-font-size: 20px; -fx-font-weight:bold;");
delete.setToggleGroup(toggle);
menu.getChildren().add(delete);
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;");
number.setToggleGroup(toggle);
menu.getChildren().add(number);
}
RadioButton delete = new RadioButton("Delete");
delete.setFont(Font.font(20));
delete.setToggleGroup(toggle);
menu.getChildren().add(delete);
Button choice = new Button("Choose");
choice.setFont(Font.font(20));
choice.setStyle("-fx-font-size: 20px; -fx-font-weight:bold;");
choice.setCancelButton(true);
menu.getChildren().add(choice);
......@@ -76,24 +81,25 @@ public class Window {
inputButtons.initStyle(StageStyle.UTILITY);
inputButtons.initModality(Modality.APPLICATION_MODAL);
inputButtons.setTitle("Button Control");
inputButtons.setMinWidth(300);
inputButtons.setMinHeight(370);
//inputButtons.setMinWidth(300);
//inputButtons.setMinHeight(100);
inputButtons.setX(400);
inputButtons.setY(400);
inputButtons.toFront();
inputButtons.show();
inputButtons.setResizable(false);
}
public void win(String message1, String message2) {
public Stage win(String message1, String message2) {
VBox menu = new VBox(5);
menu.setAlignment(Pos.TOP_CENTER);
menu.setStyle("-fx-background: pink;");
Label congrats = new Label(message1);
congrats.setFont(Font.font(35));
congrats.setFont(Font.font( "Verdana", FontWeight.BOLD, 35));
Label message = new Label(message2);
message.setFont(Font.font(25));
......@@ -106,7 +112,7 @@ public class Window {
winWindow.initStyle(StageStyle.UTILITY);
winWindow.initModality(Modality.APPLICATION_MODAL);
winWindow.setTitle("Game Won!");
winWindow.setTitle("Situation");
winWindow.setMinHeight(150);
winWindow.setMinWidth(400);
......@@ -114,7 +120,7 @@ public class Window {
winWindow.setX(800);
winWindow.setY(500);
winWindow.show();
return winWindow;
}
public Alert getAlertDialog(String str) {
......@@ -152,6 +158,7 @@ public class Window {
};
VBox root = new VBox(5);
root.setStyle("-fx-background-color: SeaShell");
root.setAlignment(Pos.CENTER);
root.setPadding(new Insets(20));
......@@ -210,15 +217,17 @@ public class Window {
Label note = new Label("Double click the space provided for text in cell");
Button submit = new Button("Submit");
Button solve = new Button("Solve");
note.setStyle("-fx-font-size: 15px; -fx-font-weight:bold;");
submit.setStyle("-fx-font-size: 20px; -fx-font-weight:bold; -fx-background-color: MediumSeaGreen; " +
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);
handler.submitButton(submit);
handler.submitButton(submit, stage);
handler.solveBoard(solve, grid, n);
root.getChildren().addAll(buttons, grid, sizes, note, submit);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment