package sample import javafx.fxml.FXML import javafx.scene.Node import javafx.scene.control.Alert import javafx.scene.control.Alert.AlertType import javafx.scene.control.Label import javafx.scene.control.TextField import javafx.scene.layout.GridPane import javafx.scene.layout.VBox import javafx.scene.paint.Color import javafx.scene.shape.Line import javafx.stage.FileChooser import java.util.* data class Change(var i : Int, var j : Int, var old : String) class Controller { private val LINE_WIDTH = 5.0 private val LINE_SIZE = 20.0 @FXML lateinit var grid: GridPane private var game = Array(9) {IntArray(9)} private var stack = Stack() private var redoStack = Stack() private var fromStack = false private var fromRedoStack = false /** * Loads text file from file picker * Creates SUDOKU grid */ fun load() { val fileChooser = FileChooser() fileChooser.title = "Vyber sudoku" val file = fileChooser.showOpenDialog(grid.scene.window) val fileScanner = Scanner(file) grid.children.clear() var iCounter = 0 var jCounter = 0 var gameIndex = 0 for (i in 0 until 18) { if (i == 0) { for (j in 0 until 18) { val line = Line(0.0, 0.0, LINE_SIZE, 0.0) line.strokeWidth = LINE_WIDTH grid.add(line, j, i) } } if (i % 2 == 0) { val row = fileScanner.nextLine() row.forEachIndexed { index, c -> game[gameIndex][index] = if (c == '.') 0 else Integer.parseInt("$c") } gameIndex++ for (j in 0 until 18) { if (j == 0) { val line = Line(0.0, 0.0, 0.0, LINE_SIZE) line.strokeWidth = LINE_WIDTH grid.add(line, j, i + 1) } if (j % 2 == 0) { if (row[j/2] == '.') { val vBox = VBox() val textField = TextField() textField.maxWidth = 30.0 textField.textProperty().addListener { observable, oldValue, newValue -> if (newValue != "") { try { val value = Integer.parseInt(newValue) if (value < 1 || value > 9) { textField.text = oldValue } } catch (e : Exception) { textField.text = oldValue } } if (newValue.length > 1) { textField.text = oldValue } changed() } textField.style = "-fx-text-inner-color: blue;" grid.add(textField, j + 1, i + 1) } else { val label = Label() label.text = "${row[j/2]}" grid.add(label, j + 1, i + 1) } } else { if (jCounter == 2) { jCounter = 0 val line = Line(0.0, 0.0, 0.0, LINE_SIZE) line.strokeWidth = LINE_WIDTH grid.add(line, j + 1, i + 1) } else { grid.add(Line(0.0, 0.0, 0.0, LINE_SIZE), j + 1, i + 1) jCounter++ } } } } else { if (iCounter == 2) { iCounter = 0 for (j in 0 until 18) { val line = Line(0.0, 0.0, LINE_SIZE, 0.0) line.strokeWidth = LINE_WIDTH grid.add(line, j, i + 1) } } else { for (j in 0 until 18) { grid.add(Line(0.0, 0.0, LINE_SIZE, 0.0), j, i + 1) } iCounter++ } } } } /** * Finds where was a change */ private fun changed() { val children = grid.children for (node in children) { if (node is TextField) { if (node.text != "") { val value = Integer.parseInt(node.text) val i = transformIndex(GridPane.getRowIndex(node)) val j = transformIndex(GridPane.getColumnIndex(node)) if (value != game[i][j]) { if (!fromStack){ stack.push(Change(GridPane.getRowIndex(node), GridPane.getColumnIndex(node), "${game[i][j]}")) } if (!fromRedoStack && !fromStack) { redoStack.clear() } fromStack = false game[i][j] = value (node as TextField).style = "-fx-text-inner-color: blue;"; check(node, i, j) } } } } } /** * From grid index to array index */ private fun transformIndex(i: Int) : Int { return (i - 1)/2 } /** * Checks if number is right */ private fun check(node: Node, i : Int, j : Int) { for (l in getFrom(i)..getTo(i)) { for (m in getFrom(j)..getTo(j)) { if (l != i && m != j) { if (game[l][m] == game[i][j]) { (node as TextField).style = "-fx-text-inner-color: red;" redNode(l, m) } else { blackNode(l, m) } } } } game.forEachIndexed { index, ints -> if (ints[j] == game[i][j] && index != i) { (node as TextField).style = "-fx-text-inner-color: red;" redNode(index, j) } else { blackNode(index, j) } } game[i].forEachIndexed { index, int -> if (int == game[i][j] && index != j) { (node as TextField).style = "-fx-text-inner-color: red;" redNode(i, index) } else { blackNode(i, index) } } } /** * Start index of current 3x3 square */ private fun getFrom(index : Int) : Int { return when { index <= 2 -> 0 index <= 5 -> 3 else -> 6 } } /** * End index of current 3x3 square */ private fun getTo(index : Int) : Int { return when { index <= 2 -> 2 index <= 5 -> 5 else -> 8 } } /** * Makes label node red */ private fun redNode(i : Int, j : Int) { val children = grid.children for (node in children) { if (GridPane.getRowIndex(node) == i*2+1 && GridPane.getColumnIndex(node) == j*2 + 1) { (node as Label).textFill = Color.RED } } } /** * Makes label node black */ private fun blackNode(i : Int, j : Int) { val children = grid.children for (node in children) { if (GridPane.getRowIndex(node) == ((i*2)+1) && GridPane.getColumnIndex(node) == ((j*2)+1) && node is Label) { node.textFill = Color.BLACK } } } /** * Checks and shows result */ fun onCheckClick() { var ok = true var i = 0 var j = 0 while (ok && j <= 8) { if (i > 8) { i = 0 j++ } if (j <= 8) { ok = isOK(i, j) } i++ } if(ok) { val alert = Alert(AlertType.INFORMATION) alert.title = "Výsledek" alert.headerText = null alert.contentText = "Správně" alert.showAndWait() } else { val alert = Alert(AlertType.INFORMATION) alert.title = "Výsledek" alert.headerText = null alert.contentText = "Špatně" alert.showAndWait() } } /** * Checks if specific number is OK */ private fun isOK(i : Int, j : Int) : Boolean { if (game[i][j] == 0) return false for (l in 0 until 9) { if (l != i) { if (game[i][j] == game[l][j]) { return false } } if (l != j) { if (game[i][j] == game[i][l]) { return false } } } for (l in getFrom(i)..getTo(i)) { for (m in getFrom(j)..getTo(j)) { if (l != i && m != j) { if (game[l][m] == game[i][j]) { return false } } } } return true } /** * On undo click */ fun undo() { if (!stack.empty()) { val change = stack.pop() fromStack = true for (node in grid.children) { if (GridPane.getRowIndex(node) == change.i && GridPane.getColumnIndex(node) == change.j) { redoStack.push(Change(change.i, change.j, (node as TextField).text)) (node as TextField).text = change.old check(node, transformIndex(GridPane.getRowIndex(node)), transformIndex(GridPane.getColumnIndex(node))) } } } } /** * On redo click */ fun redo() { if (!redoStack.empty()) { val change = redoStack.pop() fromRedoStack = true for (node in grid.children) { if (GridPane.getRowIndex(node) == change.i && GridPane.getColumnIndex(node) == change.j) { (node as TextField).text = change.old check(node, transformIndex(GridPane.getRowIndex(node)), transformIndex(GridPane.getColumnIndex(node))) } } } } }