added SudokuSolver
This commit is contained in:
parent
fe28ec45a5
commit
27918e7a35
61
src/magic_squares/sudoku/GraphicsDisplay.scala
Normal file
61
src/magic_squares/sudoku/GraphicsDisplay.scala
Normal file
@ -0,0 +1,61 @@
|
||||
package magic_squares.sudoku
|
||||
|
||||
import hevs.graphics.FunGraphics
|
||||
import magic_squares.{Displayable, Grid}
|
||||
|
||||
import java.awt.{Color, Font}
|
||||
import javax.swing.SwingConstants
|
||||
|
||||
trait GraphicsDisplay extends Displayable {
|
||||
private var _win: Option[FunGraphics] = None
|
||||
private val WIDTH: Int = 400
|
||||
private val HEIGHT: Int = 400
|
||||
private val MARGIN: Int = 20
|
||||
private val FONT: Font = new Font("Arial", Font.PLAIN, 24)
|
||||
override def display(g: Grid): Unit = {
|
||||
if (_win.isEmpty) {
|
||||
_win = Some(new FunGraphics(WIDTH, HEIGHT))
|
||||
}
|
||||
val win: FunGraphics = _win.get
|
||||
win.clear()
|
||||
val size: Int = SudokuSolver.SIZE
|
||||
val squareSize: Int = SudokuSolver.SQUARE_SIZE
|
||||
|
||||
val w: Int = WIDTH - 2 * MARGIN
|
||||
val h: Int = HEIGHT - 2 * MARGIN
|
||||
val gridSize: Int = math.min(w, h)
|
||||
val cellSize: Int = gridSize / size
|
||||
val ox: Int = (WIDTH - gridSize) / 2
|
||||
val oy: Int = (HEIGHT - gridSize) / 2
|
||||
|
||||
win.setPenWidth(2)
|
||||
win.drawRect(ox, oy, gridSize, gridSize)
|
||||
for (i: Int <- 1 until size / squareSize) {
|
||||
win.drawLine(ox + cellSize * squareSize * i, oy, ox + cellSize * squareSize * i, oy + gridSize)
|
||||
win.drawLine(ox, oy + cellSize * squareSize * i, ox + gridSize, oy + cellSize * squareSize * i)
|
||||
}
|
||||
win.setPenWidth(1)
|
||||
for (i: Int <- 1 until size) {
|
||||
win.drawLine(ox + cellSize * i, oy, ox + cellSize * i, oy + gridSize)
|
||||
win.drawLine(ox, oy + cellSize * i, ox + gridSize, oy + cellSize * i)
|
||||
}
|
||||
|
||||
for (y: Int <- 0 until size) {
|
||||
for (x: Int <- 0 until size) {
|
||||
val v: Int = g(y)(x)
|
||||
if (v != 0) {
|
||||
win.drawString(
|
||||
(ox + cellSize * (x + 0.5)).toInt,
|
||||
(oy + cellSize * (y + 0.5)).toInt,
|
||||
v.toString,
|
||||
FONT,
|
||||
Color.BLACK,
|
||||
SwingConstants.CENTER,
|
||||
SwingConstants.CENTER
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
win.syncGameLogic(60)
|
||||
}
|
||||
}
|
109
src/magic_squares/sudoku/SudokuSolver.scala
Normal file
109
src/magic_squares/sudoku/SudokuSolver.scala
Normal file
@ -0,0 +1,109 @@
|
||||
package magic_squares.sudoku
|
||||
|
||||
import magic_squares.{Displayable, Grid}
|
||||
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
|
||||
abstract class SudokuSolver(initialGrid: Grid) extends Displayable {
|
||||
protected var _initial: Grid = initialGrid
|
||||
protected var grid: Grid = copy(_initial)
|
||||
protected val SIZE: Int = SudokuSolver.SIZE
|
||||
protected val SQUARE_SIZE: Int = SudokuSolver.SQUARE_SIZE
|
||||
protected val DEBUG: Boolean = false
|
||||
protected val NUMBERS: Array[Int] = Array.range(1, SIZE + 1)
|
||||
|
||||
protected def print(grid: Grid): Unit = {
|
||||
for (y: Int <- 0 until SIZE) {
|
||||
println(grid(y).mkString(","))
|
||||
}
|
||||
}
|
||||
|
||||
protected def copy(grid: Grid): Grid = {
|
||||
return grid.map(_.clone())
|
||||
}
|
||||
|
||||
def solve(): Unit = {
|
||||
val solved: Boolean = _solve()
|
||||
if (solved) {
|
||||
display(grid)
|
||||
} else {
|
||||
println("No solution")
|
||||
}
|
||||
}
|
||||
|
||||
private def _solve(): Boolean = {
|
||||
display(grid)
|
||||
|
||||
if (DEBUG) print(grid)
|
||||
|
||||
val (x: Int, y: Int) = getNextEmpty()
|
||||
if (x == -1) {
|
||||
if (DEBUG) println(" Found solution")
|
||||
return true
|
||||
}
|
||||
val numbers: Array[Int] = getPossibleNumbers(x, y)
|
||||
if (numbers.isEmpty) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (DEBUG) println(s" Values to test: " + numbers.mkString("[", ", ", "]"))
|
||||
|
||||
for (n: Int <- numbers) {
|
||||
if (DEBUG) println(s" Testing $n")
|
||||
grid(y)(x) = n
|
||||
if (_solve()) {
|
||||
if (DEBUG) println(" Found solution, collapsing call stack")
|
||||
return true
|
||||
}
|
||||
}
|
||||
grid(y)(x) = 0
|
||||
|
||||
if (DEBUG) println(s" No solution for this configuration")
|
||||
return false
|
||||
}
|
||||
|
||||
private def getNextEmpty(): (Int, Int) = {
|
||||
for (y: Int <- 0 until SIZE) {
|
||||
for (x: Int <- 0 until SIZE) {
|
||||
if (grid(y)(x) == 0) {
|
||||
return (x, y)
|
||||
}
|
||||
}
|
||||
}
|
||||
return (-1, -1)
|
||||
}
|
||||
|
||||
private def getPossibleNumbers(x: Int, y: Int): Array[Int] = {
|
||||
val inGrid: ArrayBuffer[Int] = grid(y).concat(grid.map(_(x))).to(ArrayBuffer)
|
||||
val sx: Int = (x / 3) * 3
|
||||
val sy: Int = (y / 3) * 3
|
||||
for (dy: Int <- 0 until SQUARE_SIZE) {
|
||||
for (dx: Int <- 0 until SQUARE_SIZE) {
|
||||
inGrid.addOne(grid(sy + dy)(sx + dx))
|
||||
}
|
||||
}
|
||||
return NUMBERS.diff(inGrid.distinct)
|
||||
}
|
||||
}
|
||||
|
||||
object SudokuSolver {
|
||||
val SIZE: Int = 9
|
||||
val SQUARE_SIZE: Int = SIZE / 3
|
||||
|
||||
def main(args: Array[String]): Unit = {
|
||||
val solver1: SudokuSolver = new SudokuSolver(Array(
|
||||
Array(5,3,0, 0,7,0, 0,0,0),
|
||||
Array(6,0,0, 1,9,5, 0,0,0),
|
||||
Array(0,9,8, 0,0,0, 0,6,0),
|
||||
|
||||
Array(8,0,0, 0,6,0, 0,0,3),
|
||||
Array(4,0,0, 8,0,3, 0,0,1),
|
||||
Array(7,0,0, 0,2,0, 0,0,6),
|
||||
|
||||
Array(0,6,0, 0,0,0, 2,8,0),
|
||||
Array(0,0,0, 4,1,9, 0,0,5),
|
||||
Array(0,0,0, 0,8,0, 0,7,9),
|
||||
)) with GraphicsDisplay
|
||||
solver1.solve()
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user