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
import scala.collection.mutable.ArrayBuffer
abstract class MagicSquareSolver(initialGrid: Grid) extends Displayable {
protected var _initial: Grid = initialGrid
protected var grid: Grid = copy(_initial)
protected val _size: Int = _initial.length
protected val SUM: Int = _size * (_size * _size + 1) / 2
protected val DEBUG: Boolean = false
protected val NUMBERS: Array[Int] = Array.range(1, _size * _size + 1)
protected def print(grid: Grid): Unit = {
for (y: Int <- 0 until _size) {
@@ -17,53 +21,67 @@ abstract class MagicSquareSolver(initialGrid: Grid) extends Displayable {
}
def solve(): Unit = {
val sol: Option[Grid] = solveFrom(_initial, 0, 0)
if (sol.isEmpty) {
println("No solution")
} else {
print(sol.get)
val inGrid: ArrayBuffer[Int] = ArrayBuffer.empty
for (y: Int <- 0 until _size) {
for (x: Int <- 0 until _size) {
if (grid(y)(x) != 0) {
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)
val solved: Boolean = _solve(inGrid.toArray)
if (solved) {
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")
return None
return false
}
if (y >= _size) {
val (x: Int, y: Int) = getNextEmpty()
if (x == -1) {
if (DEBUG) println(" Found solution")
return Some(grid)
return true
}
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 numbers: Array[Int] = NUMBERS.diff(inGrid)
val newGrid: Grid = copy(grid)
var x2: Int = x + 1
var y2: Int = y
if (x2 >= _size) {
x2 -= _size
y2 += 1
}
if (DEBUG) println(s" Values to test: " + numbers.mkString("[", ", ", "]"))
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) {
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 sol
return true
}
}
grid(y)(x) = 0
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)
if (values.distinct.length != values.length) return false
@@ -113,7 +131,7 @@ object MagicSquareSolver {
Array(0, 0, 7),
Array(0, 9, 0)
)) with TextDisplay
solver1.solve()
//solver1.solve()
println()
/*
8,1,6
@@ -126,7 +144,7 @@ object MagicSquareSolver {
Array(0, 0, 7),
Array(0, 8, 0)
)) with TextDisplay
solver2.solve()
//solver2.solve()
println()
/*
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()
}
}