added SudokuSolver
This commit is contained in:
		
							
								
								
									
										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