Compare commits
2 Commits
37314cf9db
...
27918e7a35
| Author | SHA1 | Date | |
|---|---|---|---|
|
27918e7a35
|
|||
|
fe28ec45a5
|
@@ -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
|
||||||
|
|||||||
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user