Skip to content

Instantly share code, notes, and snippets.

@warmuuh
Last active April 18, 2019 08:13
Show Gist options
  • Save warmuuh/39f3aeba3e371b9f0c3a to your computer and use it in GitHub Desktop.
Save warmuuh/39f3aeba3e371b9f0c3a to your computer and use it in GitHub Desktop.
package scala.dsl
sealed class Level(val emptyCount: Int)
case object Full extends Level(-1);
case object Easy extends Level(30);
case object Medium extends Level(50);
case object Hard extends Level(70);
object Sudoku {
def generate(level: Level) = {
val container = generateSolution().get;
removeRandomFields(level.emptyCount, container);
container
}
def generateSolution(y: Int = 0, x: Int = 0, c: SudokuContainer = new SudokuContainer()): Option[SudokuContainer] = {
if (y == 9) return Some(c);
for (value <- util.Random.shuffle(1 to 9)) {
val nc = c.modify(x, y, CellValue(value))
if (nc.validate()) {
val (ny, nx) = (y + ((x + 1) / 9), (x + 1) % 9)
generateSolution(ny, nx, nc) match {
case None =>
case x @ _ => return x
}
}
}
return None;
}
def removeRandomFields(count: Int, c: SudokuContainer) = {
for (i <- 0 to count) {
val (y, x) = (util.Random.nextInt(9), util.Random.nextInt(9))
c.matrix(y)(x) = EmptyCell
}
}
//DSL stuff:
def apply(e: SudokuEntry*) = {
val cells = e.filter { _.curRow.length == 9 }
.map(_.curRow)
new SudokuContainer(Array.tabulate(9, 9)((x, y) => cells(x)(y)))
}
implicit def intToSudoku(i: Int): SudokuEntry = new SudokuEntry(CellValue(i))
val ● = new SudokuEntry(EmptyCell)
val ----------|-----------|---------- = new SudokuEntry(EmptyCell)
}
sealed trait Cell;
case class CellValue(val i: Int) extends Cell;
case object EmptyCell extends Cell;
class SudokuContainer(val matrix: Array[Array[Cell]] = Array.fill[Cell](9,9)(EmptyCell)) {
def validate(): Boolean = {
def containsDuplicatesInRows = (m: Array[Array[Cell]]) => {
m.map { row => row.filterNot(_ == EmptyCell).groupBy(identity).collect { case (x, ys) if ys.size > 1 => true } }.filter(_.size > 0).size > 0
}
lazy val duplicateRows = containsDuplicatesInRows(matrix)
lazy val duplicateCols = containsDuplicatesInRows(matrix.transpose)
lazy val cells = matrix.flatMap(_.grouped(3)).grouped(3).toArray.transpose.map(_.grouped(3).toArray).flatten.map(_.flatten)
lazy val duplicateCells = containsDuplicatesInRows(cells)
!duplicateRows && !duplicateCols && !duplicateCells;
}
def modify(x: Int, y: Int, nv: Cell) = {
val newMatrix = matrix map { _.clone }
newMatrix(y)(x) = nv
new SudokuContainer(newMatrix)
}
def complete(): Boolean = {
val elementCount: Int = matrix.map({ row => row filterNot { _ == EmptyCell } length }).foldLeft(0)(_ + _);
elementCount == 81 && validate()
}
override def toString() = {
matrix.map({ r =>
r.map( _ match {
case EmptyCell => "●"
case CellValue(v) => v.toString()
}
).grouped(3).map({ block: Array[String] => block.mkString(" ∙ ") }).mkString(" | ")
}).grouped(3).map({ block: Array[String] => block.mkString(",\n") })
.mkString(",\n----------|-----------|----------,\n")
}
}
class SudokuEntry(val v: Cell) {
var curRow: List[Cell] = List(v)
def ∙(o: SudokuEntry) = {
curRow = curRow ++ o.curRow
this;
}
def |(o: SudokuEntry) = {
curRow = curRow ++ o.curRow
this;
}
}
package scala.dsl
object Test extends App {
import Sudoku._;
val sudoku = Sudoku(
1 ∙ 2 ∙ 3 | 4 ∙ ● ∙ ● | 7 ∙ 8 ∙ 9,
4 ∙ 5 ∙ 6 | 7 ∙ 8 ∙ 9 | 1 ∙ 2 ∙ 3,
7 ∙ 8 ∙ 9 | 1 ∙ 2 ∙ 3 | 4 ∙ 5 ∙ 6,
----------|-----------|----------,
2 ∙ 3 ∙ 4 | 5 ∙ 6 ∙ 7 | 8 ∙ 9 ∙ 1,
5 ∙ 6 ∙ 7 | 8 ∙ 9 ∙ 1 | 2 ∙ 3 ∙ 4,
8 ∙ 9 ∙ 1 | 2 ∙ 3 ∙ 4 | 5 ∙ 6 ∙ 7,
----------|-----------|----------,
3 ∙ 4 ∙ 5 | 6 ∙ 7 ∙ 8 | 9 ∙ 1 ∙ 2,
6 ∙ 7 ∙ 8 | 9 ∙ 1 ∙ 2 | 3 ∙ 4 ∙ 5,
9 ∙ 1 ∙ 2 | 3 ∙ 4 ∙ 5 | 6 ∙ 7 ∙ 8)
println("manual sudoku:")
println(sudoku)
println(s"is valid: ${sudoku.validate()}")
println(s"is complete: ${sudoku.complete()}")
val gsudoku = Sudoku.generate(Full)
println("generated sudoku:")
println(gsudoku)
println(s"is valid: ${gsudoku.validate()}")
println(s"is complete: ${gsudoku.complete()}")
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment