Compare commits

..

2 Commits

Author SHA1 Message Date
27918e7a35 added SudokuSolver 2024-04-30 14:28:34 +02:00
fe28ec45a5 improved MagicSquareSolver 2024-04-30 13:51:57 +02:00
3 changed files with 232 additions and 44 deletions

View File

@@ -1,10 +1,14 @@
package magic_squares package magic_squares
import scala.collection.mutable.ArrayBuffer
abstract class MagicSquareSolver(initialGrid: Grid) extends Displayable { abstract class MagicSquareSolver(initialGrid: Grid) extends Displayable {
protected var _initial: Grid = initialGrid protected var _initial: Grid = initialGrid
protected var grid: Grid = copy(_initial)
protected val _size: Int = _initial.length protected val _size: Int = _initial.length
protected val SUM: Int = _size * (_size * _size + 1) / 2 protected val SUM: Int = _size * (_size * _size + 1) / 2
protected val DEBUG: Boolean = false protected val DEBUG: Boolean = false
protected val NUMBERS: Array[Int] = Array.range(1, _size * _size + 1)
protected def print(grid: Grid): Unit = { protected def print(grid: Grid): Unit = {
for (y: Int <- 0 until _size) { for (y: Int <- 0 until _size) {
@@ -17,53 +21,67 @@ abstract class MagicSquareSolver(initialGrid: Grid) extends Displayable {
} }
def solve(): Unit = { def solve(): Unit = {
val sol: Option[Grid] = solveFrom(_initial, 0, 0) val inGrid: ArrayBuffer[Int] = ArrayBuffer.empty
if (sol.isEmpty) { for (y: Int <- 0 until _size) {
println("No solution") for (x: Int <- 0 until _size) {
} else { if (grid(y)(x) != 0) {
print(sol.get) inGrid.addOne(grid(y)(x))
}
} }
} }
private def solveFrom(grid: Grid, x: Int, y: Int): Option[Grid] = { val solved: Boolean = _solve(inGrid.toArray)
if (DEBUG) println(s"Solving from $x, $y") if (solved) {
if (DEBUG) print(grid)
display(grid) display(grid)
if (!isValid(grid)) { } else {
println("No solution")
}
}
private def _solve(inGrid: Array[Int]): Boolean = {
display(grid)
if (DEBUG) print(grid)
if (!isValid()) {
if (DEBUG) println(" Grid is invalid") if (DEBUG) println(" Grid is invalid")
return None return false
} }
if (y >= _size) {
val (x: Int, y: Int) = getNextEmpty()
if (x == -1) {
if (DEBUG) println(" Found solution") if (DEBUG) println(" Found solution")
return Some(grid) return true
} }
var values: Array[Int] = Array(_initial(y)(x)) val numbers: Array[Int] = NUMBERS.diff(inGrid)
if (values(0) == 0) values = (1 to _size * _size).toArray
if (DEBUG) println(s" Values to test: " + values.mkString("[", ", ", "]"))
val newGrid: Grid = copy(grid) if (DEBUG) println(s" Values to test: " + numbers.mkString("[", ", ", "]"))
var x2: Int = x + 1
var y2: Int = y
if (x2 >= _size) {
x2 -= _size
y2 += 1
}
for (i: Int <- values) { for (n: Int <- numbers) {
if (DEBUG) println(s" Testing $i") if (DEBUG) println(s" Testing $n")
newGrid(y)(x) = i grid(y)(x) = n
val sol: Option[Grid] = solveFrom(newGrid, x2, y2) if (_solve(inGrid.appended(n))) {
if (sol.isDefined) {
if (DEBUG) println(" Found solution, collapsing call stack") if (DEBUG) println(" Found solution, collapsing call stack")
return sol return true
} }
} }
grid(y)(x) = 0
if (DEBUG) println(s" No solution for this configuration") if (DEBUG) println(s" No solution for this configuration")
return None return false
} }
private def isValid(grid: Grid): Boolean = { 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 isValid(): Boolean = {
val values: Array[Int] = grid.reduce((a, b) => a.concat(b)).filter(_ != 0) val values: Array[Int] = grid.reduce((a, b) => a.concat(b)).filter(_ != 0)
if (values.distinct.length != values.length) return false if (values.distinct.length != values.length) return false
@@ -113,7 +131,7 @@ object MagicSquareSolver {
Array(0, 0, 7), Array(0, 0, 7),
Array(0, 9, 0) Array(0, 9, 0)
)) with TextDisplay )) with TextDisplay
solver1.solve() //solver1.solve()
println() println()
/* /*
8,1,6 8,1,6
@@ -126,7 +144,7 @@ object MagicSquareSolver {
Array(0, 0, 7), Array(0, 0, 7),
Array(0, 8, 0) Array(0, 8, 0)
)) with TextDisplay )) with TextDisplay
solver2.solve() //solver2.solve()
println() println()
/* /*
no solution no solution

View 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)
}
}

View 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()
}
}