Compare commits
	
		
			2 Commits
		
	
	
		
			37314cf9db
			...
			main
		
	
	| 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] = { |     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 | ||||||
|   | |||||||
							
								
								
									
										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