package sudoku; import java.lang.Enum.*; import java.io.File; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Scanner; import java.util.Set; import javax.swing.JTextField; import javafx.application.Application; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.event.ActionEvent; import javafx.geometry.Pos; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.control.ListView; import javafx.scene.control.TextField; import javafx.scene.layout.*; import javafx.scene.text.TextAlignment; import javafx.stage.Stage; public class SudokuMain extends Application { @Override public void start(Stage primaryStage) throws Exception { BorderPane root = new BorderPane(); Scene scene = new Scene(root, 500, 525); primaryStage.setTitle("Sudoku"); primaryStage.setScene(scene); ObservableList obsSud = FXCollections.observableArrayList(new Sudoku()); TilePane tp = new TilePane(); tp.setPrefColumns(9); tp.setMaxWidth(500); tp.setMaxHeight(500); tp.setHgap(5); tp.setVgap(5); HashMap check = new HashMap(); for (int i = 1; i < 10; i++) { String iVal = ((Integer) i).toString(); check.put(iVal, i); } for (int i = 0; i < 81; i++) { TextField tf = new TextField(); tf.setMaxWidth(50); tf.setPrefHeight(50); tf.setAlignment(Pos.CENTER); tp.getChildren().add(tf); int row = (i - i % 9) / 9; int col = i % 9; int regR = (int) Math.floor(row / 3); int regC = (int) Math.floor(col / 3); int sum = regR + regC; if (sum % 2 == 0) { ((TextField) tp.getChildren().get(i)) .setStyle("-fx-background-radius:0; -fx-background-color:#9CD777;"); } else { ((TextField) tp.getChildren().get(i)) .setStyle("-fx-background-radius:0; -fx-background-color:#FFFFFF;"); } } root.setCenter(tp); HBox hbox = new HBox(); TextField txtF = new TextField(); Button solve = new Button("Solve"); Button clear = new Button("Clear"); clear.setOnAction((event) -> { txtF.setText(""); for (int i = 0; i < 81; i++) { ((TextField) tp.getChildren().get(i)).setText(""); int row = (i - i % 9) / 9; int col = i % 9; int regR = (int) Math.floor(row / 3); int regC = (int) Math.floor(col / 3); int sum = regR + regC; if (sum % 2 == 0) { ((TextField) tp.getChildren().get(i)) .setStyle("-fx-background-radius:0; -fx-background-color:#9CD777;"); } else { ((TextField) tp.getChildren().get(i)) .setStyle("-fx-background-radius:0; -fx-background-color:#FFFFFF;"); } } }); solve.setOnAction((event) -> { for (int i = 0; i < 81; i++) { String valTxt = ((TextField) tp.getChildren().get(i)).getText(); if (valTxt.isEmpty() == false) { int row = (i - i % 9) / 9; int col = i % 9; if (check.containsKey(valTxt)) { int val = check.get(valTxt); obsSud.get(0).setValue(row, col, val); } else { ((TextField) tp.getChildren().get(i)) .setStyle("-fx-background-radius:0; -fx-background-color:#DF5151;"); try { int val = Integer.parseInt(valTxt); obsSud.get(0).setValue(row, col, val); } catch (NumberFormatException e) { obsSud.get(0).setValue(row, col, 10); } } } } if (obsSud.get(0).sudokuSolver()) { for (int i = 0; i < 81; i++) { int row = (i - i % 9) / 9; int col = i % 9; String s = ((Integer) obsSud.get(0).getValue(row, col)).toString(); ((TextField) tp.getChildren().get(i)).setText(s); } txtF.setText("Här är lösningen"); } else { txtF.setText("Finns ej lösning"); } obsSud.set(0, new Sudoku()); }); hbox.getChildren().addAll(solve, clear, txtF); root.setBottom(hbox); primaryStage.show(); } public static void main(String[] args) { Application.launch(args); } } package sudoku; import java.util.HashMap; import java.util.HashSet; import java.util.Set; import java.util.ArrayList; public class Sudoku { HashMap> rows; HashMap> cols; HashMap> reg; int[][] matrix; /** * Creates an empty 9x9 matrix to be used as an sudoku. Instantiate the * attribute HashMaps which are corresponding to the Sudokus rows, columns and * regions. */ public Sudoku() { matrix = new int[9][9]; rows = new HashMap>(); cols = new HashMap>(); reg = new HashMap>(); } /** * Sets the value at the square specified by the paramaters, and inserts the * value in the attribute HashMaps. Index: 0 -> 8. * * @param row corresponding to the row where the value is to be set. * @param col corresponding to the column where the value is to be set. * @param val, the value to be inserted. */ public void setValue(int row, int col, int val) { matrix[row][col] = val; int regR = (int) Math.floor(row / 3); int regC = (int) Math.floor(col / 3); String regName = regR + ", " + regC; if (rows.containsKey(row)) { rows.get(row).add(val); } else { rows.put(row, new ArrayList()); rows.get(row).add(val); } if (cols.containsKey(col)) { cols.get(col).add(val); } else { cols.put(col, new ArrayList()); cols.get(col).add(val); } if (reg.containsKey(regName)) { reg.get(regName).add(val); } else { reg.put(regName, new ArrayList()); reg.get(regName).add(val); } } /** * Removes an existing value at the specified square, and removes the value from * the attribute HashMaps. Index: 0 -> 8. * * @param row corresponding to the row where the value is to be removed. * @param col corresponding to the column where the value is to be removed. */ public void removeValue(int row, int col) { Integer val = matrix[row][col]; int regR = (int) Math.floor(row / 3); int regC = (int) Math.floor(col / 3); String regName = regR + ", " + regC; matrix[row][col] = 0; rows.get(row).remove(val); cols.get(col).remove(val); reg.get(regName).remove(val); } /** * Checks if the values in the HashMaps is an integer between 1-9, also checks * if there exists no duplicate values in these HashMaps. * * @return true if all checks pass, otherwise false. */ private boolean check() { Set set = new HashSet(); for (int i = 1; i < 10; i++) { set.add(i); } for (Integer row : rows.keySet()) { Set rowSet = new HashSet(rows.get(row)); if (rowSet.size() != rows.get(row).size() || !set.containsAll(rows.get(row))) { return false; } for (Integer col : cols.keySet()) { Set colSet = new HashSet(cols.get(col)); if (colSet.size() != cols.get(col).size()) { return false; } int regR = (int) Math.floor(row / 3); int regC = (int) Math.floor(col / 3); String regName = regR + ", " + regC; Set regSet = new HashSet(reg.get(regName)); if (regSet.size() != reg.get(regName).size()) { return false; } } } return true; } /** Evaluates which the next position in the Sudoku is, and returns it as an array. * If the parameter indices corresponds to the end of a row, the row increments by 1 and the column is reset to 0. * Otherwise the row value is unchanged while the column value is incremented by 1. * @param row corresponding to the position in the Sudoku, from which the next position is to be evaluated. * @param col corresponding to the position in the Sudoku, from which the next position is to be evaluated. * @return the next position in the Sudoku, if the position is at the end return [-1, -1]. */ private int[] nextPos(int row, int col) { int[] pos = new int[2]; if (col == 8 && row == 8) { pos[0] = -1; pos[1] = -1; } else if (col == 8 && row != 8) { pos[0] = row + 1; pos[1] = 0; } else { pos[0] = row; pos[1] = col + 1; } return pos; } /** * Checks if the value already exists in given row/column/region. * @param row corresponding to the row in the Sudoku. * @param col corresponding to the column in the Sudoku. * @param val, the value to check if it already exists. * @return true if the value exists, false otherwise. */ private boolean contains(int row, int col, int val) { int regR = (int) Math.floor(row / 3); int regC = (int) Math.floor(col / 3); String regName = regR + ", " + regC; if (rows.containsKey(row) && rows.get(row).contains(val)) { return true; } else if (cols.containsKey(col) && cols.get(col).contains(val)) { return true; } else if (reg.containsKey(regName) && reg.get(regR + ", " + regC).contains(val)) { return true; } else { return false; } } /** * Gets the value at the specified row/column. * @param row, row corresponding to the specified square. * @param col, column corresponding to the specified square. * @return The value at the square. */ public int getValue(int row, int col) { return matrix[row][col]; } /** * Checks if the method check returns true, if it does the method solve is called from the starting position. * @return the value of solve if the method check returns true, otherwise false. */ public boolean sudokuSolver() { if (check()) { return solve(0, 0); } return false; } /** * Evaluates if there exists a solution from the specified square and forward using recursion. * @param row, row from which to check if a solution exists. * @param col, column from which to check if a solution exists. * @return true if a solution to the rest Sudoku exists, false otherwise. */ private boolean solve(int row, int col) { int nextRowPos = nextPos(row, col)[0]; int nextColPos = nextPos(row, col)[1]; if (getValue(row, col) != 0) { if (row == 8 && col == 8) { return true; } return solve(nextRowPos, nextColPos); } else { for (int val = 1; val < 10; val++) { if (contains(row, col, val) == false) { setValue(row, col, val); if (row == 8 && col == 8) { return true; } else { if (solve(nextRowPos, nextColPos)) { return true; } else { removeValue(row, col); } } } } } return false; } } package sudoku; import static org.junit.Assert.*; import org.junit.After; import org.junit.Before; import org.junit.Test; //import queue_delegate.FifoQueue; import java.util.NoSuchElementException; import java.util.Queue; import java.util.Iterator; public class SudokuTest { Sudoku sud; @Before public void setUp() throws Exception { sud = new Sudoku(); } @After public void tearDown() throws Exception { sud = null; } /* lösning till tomt sudoku */ @Test public final void testSudokuEmp() { assertTrue(sud.sudokuSolver()); } /* lösning till sudoku i figur 1 */ @Test public final void testSudokuFig1() { sud.setValue(0, 2, 8); sud.setValue(0, 5, 9); sud.setValue(0, 7, 6); sud.setValue(0, 8, 2); sud.setValue(1, 8, 5); sud.setValue(2, 0, 1); sud.setValue(2, 2, 2); sud.setValue(2, 3, 5); sud.setValue(3, 3, 2); sud.setValue(3, 4, 1); sud.setValue(3, 7, 9); sud.setValue(4, 1, 5); sud.setValue(4, 6, 6); sud.setValue(5, 0, 6); sud.setValue(5, 7, 2); sud.setValue(5, 8, 8); sud.setValue(6, 0, 4); sud.setValue(6, 1, 1); sud.setValue(6, 3, 6); sud.setValue(6, 5, 8); sud.setValue(7, 0, 8); sud.setValue(7, 1, 6); sud.setValue(7, 4, 3); sud.setValue(7, 6, 1); sud.setValue(8, 6, 4); assertTrue(sud.sudokuSolver()); } @Test public final void hardestSudokuTest() { sud.setValue(0, 0, 8); sud.setValue(1, 2, 3); sud.setValue(1, 3, 6); sud.setValue(2, 1, 7); sud.setValue(2, 4, 9); sud.setValue(2, 6, 2); sud.setValue(3, 1, 5); sud.setValue(3, 5, 7); sud.setValue(4, 4, 4); sud.setValue(4, 5, 5); sud.setValue(4, 6, 7); sud.setValue(5, 3, 1); sud.setValue(5, 7, 3); sud.setValue(6, 2, 1); sud.setValue(6, 7, 6); sud.setValue(6, 8, 8); sud.setValue(7, 3, 5); sud.setValue(7, 7, 1); sud.setValue(7, 2, 8); sud.setValue(8, 1, 9); sud.setValue(8, 6, 4); assertTrue(sud.sudokuSolver()); } /* "lösning" av olösliga sudoku */ @Test public final void unsolvableTest() { sud.setValue(0, 0, 1); sud.setValue(0, 5, 1); assertFalse(sud.sudokuSolver()); sud = new Sudoku(); sud.setValue(0, 0, 1); sud.setValue(5, 0, 1); assertFalse(sud.sudokuSolver()); sud = new Sudoku(); sud.setValue(0, 0, 1); sud.setValue(2, 2, 1); assertFalse(sud.sudokuSolver()); } }