import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.io.ObjectInputStream
import java.io.ObjectOutputStream
import java.io.Serializable
class Solution {
fun solveSudoku(board: Array<CharArray>): Unit {
cells = Array(9) { Array(9, { Cell() }) }
for (i in 0..8) {
for (j in 0..8) {
when {
board[i][j] != '.' && !set(i, j, board[i][j] - '0') -> return
}
}
}
when {
!findValuesForEmptyCells() -> return
}
for (i in 0..8) {
for (j in 0..8) {
board[i][j] = '0' + cells[i][j].value
}
}
}
class Cell : Serializable {
var value = 0
var numPossibilities = 9
var constraints = BooleanArray(10)
}
private lateinit var cells: Array<Array<Cell>>
private lateinit var emptyCells: MutableList<Pair<Int, Int>>
fun set(i: Int, j: Int, value: Int): Boolean {
val cell = cells[i][j]
when {
cell.value == value -> return true
cell.constraints[value] -> return false
}
cell.constraints = BooleanArray(10, { true })
cell.constraints[value] = false
cell.numPossibilities = 1
cell.value = value
for (it in 0..8) {
when {
i != it && !updateConstraints(it, j, value) -> return false
j != it && !updateConstraints(i, it, value) -> return false
(i / 3) * 3 + it / 3 != i && (j / 3) * 3 + it % 3 != j &&
!updateConstraints((i / 3) * 3 + it / 3, (j / 3) * 3 + it % 3, value) -> return false
}
}
return true
}
private fun updateConstraints(i: Int, j: Int, value: Int): Boolean {
val cell = cells[i][j]
when {
cell.constraints[value] -> return true
cell.value == value -> return false
}
cell.constraints[value] = true
cell.numPossibilities--
when {
cell.numPossibilities > 1 -> return true
}
for (it in 1..9) {
when {
!cell.constraints[it] -> return set(i, j, it)
}
}
return true
}
private fun findValuesForEmptyCells(): Boolean {
emptyCells = mutableListOf()
for (i in 0..8) {
for (j in 0..8) {
when {
cells[i][j].value == 0 -> emptyCells.add(Pair(i, j))
}
}
}
emptyCells.sortBy { cells[it.first][it.second].numPossibilities }
return backtrack(0)
}
private fun backtrack(index: Int): Boolean {
when {
index >= emptyCells.size -> return true
cells[emptyCells[index].first][emptyCells[index].second].value != 0 -> return backtrack(index + 1)
}
val snapshot = copy(cells)
for (value in 1..9) {
when {
!cells[emptyCells[index].first][emptyCells[index].second].constraints[value] -> {
when {
set(emptyCells[index].first, emptyCells[index].second, value) && backtrack(index + 1) -> return true
}
cells = snapshot
}
}
}
return false
}
private fun copy(cells: Array<Array<Cell>>): Array<Array<Cell>> {
val byteArrayOutputStream = ByteArrayOutputStream()
val objectOutputStream = ObjectOutputStream(byteArrayOutputStream)
objectOutputStream.writeObject(cells)
val byteArrayInputStream = ByteArrayInputStream(byteArrayOutputStream.toByteArray())
val objectInputStream = ObjectInputStream(byteArrayInputStream)
@Suppress("UNCHECKED_CAST")
return objectInputStream.readObject() as Array<Array<Cell>>
}
}