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] = {
if (DEBUG) println(s"Solving from $x, $y")
if (DEBUG) print(grid)
display(grid)
if (!isValid(grid)) {
if (DEBUG) println(" Grid is invalid")
return None
}
if (y >= _size) {
if (DEBUG) println(" Found solution")
return Some(grid)
}
var values: Array[Int] = Array(_initial(y)(x))
if (values(0) == 0) values = (1 to _size * _size).toArray
if (DEBUG) println(s" Values to test: " + values.mkString("[", ", ", "]"))
val newGrid: Grid = copy(grid)
var x2: Int = x + 1
var y2: Int = y
if (x2 >= _size) {
x2 -= _size
y2 += 1
}
for (i: Int <- values) {
if (DEBUG) println(s" Testing $i")
newGrid(y)(x) = i
val sol: Option[Grid] = solveFrom(newGrid, x2, y2)
if (sol.isDefined) {
if (DEBUG) println(" Found solution, collapsing call stack")
return sol
} }
} }
if (DEBUG) println(s" No solution for this configuration") val solved: Boolean = _solve(inGrid.toArray)
return None if (solved) {
display(grid)
} else {
println("No solution")
}
} }
private def isValid(grid: Grid): Boolean = { private def _solve(inGrid: Array[Int]): Boolean = {
display(grid)
if (DEBUG) print(grid)
if (!isValid()) {
if (DEBUG) println(" Grid is invalid")
return false
}
val (x: Int, y: Int) = getNextEmpty()
if (x == -1) {
if (DEBUG) println(" Found solution")
return true
}
val numbers: Array[Int] = NUMBERS.diff(inGrid)
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(inGrid.appended(n))) {
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 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()
}
}