用Scala解数独

用Scala解数独,scala,sudoku,Scala,Sudoku,我正在学习Scala(而且是函数式编程新手)。 我已经创建了一个数独棋盘(这将是一个游戏,你可以在一个数独中设置数字,也许是一个自动数独解算器) 检查行和列是否正确可以很好地以面向功能的方式工作 不过,为了检查3x3正方形是否正确,我编写了一个非常难看的方法(请参见下面的代码) 关于如何更容易、更具规模地解决这个问题,有什么建议吗 我已经找到了.slice(from,until),.splitAt(until),.slide(size,step)方法,并再次尝试使用“转置”方法。虽然我不能想出一

我正在学习Scala(而且是函数式编程新手)。 我已经创建了一个数独棋盘(这将是一个游戏,你可以在一个数独中设置数字,也许是一个自动数独解算器)

检查行和列是否正确可以很好地以面向功能的方式工作

不过,为了检查3x3正方形是否正确,我编写了一个非常难看的方法(请参见下面的代码)

关于如何更容易、更具规模地解决这个问题,有什么建议吗

我已经找到了.slice(from,until),.splitAt(until),.slide(size,step)方法,并再次尝试使用“转置”方法。虽然我不能想出一个更有效的方法来做这件事

class Sudoku(){
  val sudoku =
    Array(
      Array(0, 5, 0, 3, 0, 9, 0, 2, 6),
      Array(3, 8, 9, 4, 2, 0, 1, 5, 7),
      Array(4, 0, 6, 1, 0, 0, 0, 8, 9),
      Array(0, 1, 3, 7, 9, 8, 0, 0, 4),
      Array(0, 0, 8, 0, 0, 0, 5, 0, 0),
      Array(0, 6, 0, 0, 0, 3, 0, 0, 0),
      Array(0, 0, 1, 9, 3, 0, 0, 4, 0),
      Array(9, 3, 5, 6, 4, 0, 8, 0, 1),
      Array(0, 0, 2, 8, 7, 0, 0, 0, 5)
  )

  def checkSudoku(): Unit ={
    println(check(sudoku))
  }

  private def check(sudoku: Array[Array[Int]]): Boolean = checkRows(sudoku) && checkCols(sudoku) && checkSquars(sudoku)
  private def checkCols(sudoku: Array[Array[Int]]) = checkRows(sudoku.transpose)
  private def checkRows(sudoku: Array[Array[Int]]): Boolean = sudoku.forall(row => checkRow(row))
  private def checkRow(row: Array[Int]): Boolean = row.distinct.length == row.length


  private def checkSquars(sudoku: Array[Array[Int]]): Boolean ={
    val squared : Array[Array[Int]] = Array.ofDim[Int](1,3)

    for(i <- 0 to (sudoku.length-1)/3) {
      for(o <- 0 to (sudoku(i).length-1)/3) {
        squared((i*3)+o) = sudoku(0+(i*3)).slice(0+(o*3), 3+(o*3)) ++ sudoku(1+(i*3)).slice(0+(o*3), 3+(o*3)) ++ sudoku(2+(i*3)).slice(0+(o*3), 3+(o*3))
      }
    }
    squared.forall(row => checkRow(row))
  }
}

val sudoku = new Sudoku()
sudoku.checkSudoku();
class数独(){
瓦尔数独=
排列(
数组(0,5,0,3,0,9,0,2,6),
数组(3,8,9,4,2,0,1,5,7),
数组(4,0,6,1,0,0,0,8,9),
数组(0,1,3,7,9,8,0,0,4),
数组(0,0,8,0,0,0,5,0,0),
数组(0,6,0,0,0,0,3,0,0,0,0),
数组(0,0,1,9,3,0,0,4,0),
数组(9,3,5,6,4,0,8,0,1),
数组(0,0,2,8,7,0,0,0,5)
)
def checkSudoku():单位={
println(支票(数独))
}
私有def检查(数独:数组[Array[Int]]):布尔=校验行(数独)和校验列(数独)和校验列(数独)
私有def checkCols(数独:数组[Array[Int]])=checkRows(数独.转置)
private def checkRows(数独:数组[Array[Int]]):Boolean=sudoku.forall(row=>checkRow(row))
private def checkRow(row:Array[Int]):Boolean=row.distinct.length==row.length
私有def校验码(数独:数组[Array[Int]]):布尔值={
valsquared:Array[Array[Int]=Array.ofDim[Int](1,3)
对于(i,这里有一个(稍微有点愚蠢的)方法来执行checkSquares方法:

val rowBlocks = sudoku.grouped(3).toArray
def splitRow(row: Array[Int]) = row.grouped(3).toArray

val squares = rowBlocks.map( block => block.map(splitRow).transpose)
你应该说服自己,
squares
是数独游戏中一个3x3的正方形数组。现在你只需要检查以下条件:

squares.forall(_.forall(sq => sq.flatten.distinct.length == sq.flatten.length))
下面是一个(有点愚蠢的)方法来执行checkSquares方法:

val rowBlocks = sudoku.grouped(3).toArray
def splitRow(row: Array[Int]) = row.grouped(3).toArray

val squares = rowBlocks.map( block => block.map(splitRow).transpose)
你应该说服自己,
squares
是数独游戏中一个3x3的正方形数组。现在你只需要检查以下条件:

squares.forall(_.forall(sq => sq.flatten.distinct.length == sq.flatten.length))

这里有一个替代方法,可以将所有的正方形都作为直线。我试图让它更具可读性:

  val squareSize = 3
  val boardSize = sudoku.length

  val squareLines = for {
    rowStart <- List.range(0, boardSize, squareSize)
    colStart <- List.range(0, boardSize, squareSize)
  } yield {
    List.range(0, squareSize).flatMap {
      i =>
        sudoku(rowStart + i).slice(colStart, colStart + squareSize)
    }
  }
val squareSize=3
val boardSize=sudoku.length
val平方线=用于{

rowStart这里有一个替代方法,可以将所有的正方形都作为直线。我试图让它更具可读性:

  val squareSize = 3
  val boardSize = sudoku.length

  val squareLines = for {
    rowStart <- List.range(0, boardSize, squareSize)
    colStart <- List.range(0, boardSize, squareSize)
  } yield {
    List.range(0, squareSize).flatMap {
      i =>
        sudoku(rowStart + i).slice(colStart, colStart + squareSize)
    }
  }
val squareSize=3
val boardSize=sudoku.length
val平方线=用于{

rowStart我开始使用list/array来管理拼图,但很久以后,我发现使用map更好

接下来,使用Array[Set[Int]]而不是Array[Array[Int]]来删除组中的重复项

我加入了函数
conflict(…)
来检查冲突。您可以将其用作参考。随着我继续进行更改(即,我不再使用此函数,因为我有其他方法来实现相同的目的,但它达到了目的)我希望你能明白我的意思,如果你需要帮助的话,给我一声喊

val digits = ('1' to '9').mkString
val alphas = ('A' to 'I').mkString

def cross(rows: String, cols: String) = for {
  row <- rows
  col <- cols
} yield {"" + row + col}

val verticals = digits.map(d => cross(alphas, d.toString))

val horizontals = alphas.map(a => cross(a.toString, digits))

val blocks = for {
  rowBlk <- alphas.grouped(3)
  colBlk <- digits.grouped(3)
} yield (cross(rowBlk, colBlk))

val all = horizontals ++ verticals ++ blocks

def everyone = cross(alphas, digits).foldLeft(Map.empty[String, Set[Set[Int]]])((m,a) => m + (a -> (all.filter(_.contains(a)).map(_.toSet).toSet)))

def conflict(cell: String, solution: Map[String, Set[Int]]) = 
  if (everyone(cell)
    .map(cells => cells.toList.foldLeft(List.empty[Int])((chars, c) => 
      if (solution(c).size > 1) chars else chars ++ solution(c).toList))
        .forall(xs => xs.size == xs.distinct.size)) solution else Map.empty
val数字=('1'到'9')。mkString
val alphas=('A'到'I')。mkString
def交叉(行:字符串,列:字符串)=用于{
行交叉(a.toString,数字))
val块=用于{
rowBlk(all.filter(u.contains(a)).map(u.toSet.toSet)))
def冲突(单元格:字符串,解决方案:映射[String,Set[Int]])=
如果(每个人(单元)
.map(cells=>cells.toList.foldLeft(List.empty[Int])((chars,c)=>
if(解决方案(c.size>1)chars else chars++解决方案(c.toList))
.forall(xs=>xs.size==xs.distinct.size))解决方案else Map.empty

我开始使用列表/数组来管理拼图,但很久以后,我发现使用地图更好

接下来,使用Array[Set[Int]]而不是Array[Array[Int]]来删除组中的重复项

我加入了函数
conflict(…)
来检查冲突。您可以将其用作参考。随着我继续进行更改(即,我不再使用此函数,因为我有其他方法来实现相同的目的,但它达到了目的)我希望你能明白我的意思,如果你需要帮助的话,给我一声喊

val digits = ('1' to '9').mkString
val alphas = ('A' to 'I').mkString

def cross(rows: String, cols: String) = for {
  row <- rows
  col <- cols
} yield {"" + row + col}

val verticals = digits.map(d => cross(alphas, d.toString))

val horizontals = alphas.map(a => cross(a.toString, digits))

val blocks = for {
  rowBlk <- alphas.grouped(3)
  colBlk <- digits.grouped(3)
} yield (cross(rowBlk, colBlk))

val all = horizontals ++ verticals ++ blocks

def everyone = cross(alphas, digits).foldLeft(Map.empty[String, Set[Set[Int]]])((m,a) => m + (a -> (all.filter(_.contains(a)).map(_.toSet).toSet)))

def conflict(cell: String, solution: Map[String, Set[Int]]) = 
  if (everyone(cell)
    .map(cells => cells.toList.foldLeft(List.empty[Int])((chars, c) => 
      if (solution(c).size > 1) chars else chars ++ solution(c).toList))
        .forall(xs => xs.size == xs.distinct.size)) solution else Map.empty
val数字=('1'到'9')。mkString
val alphas=('A'到'I')。mkString
def交叉(行:字符串,列:字符串)=用于{
行交叉(a.toString,数字))
val块=用于{
rowBlk(all.filter(u.contains(a)).map(u.toSet.toSet)))
def冲突(单元格:字符串,解决方案:映射[String,Set[Int]])=
如果(每个人(单元)
.map(cells=>cells.toList.foldLeft(List.empty[Int])((chars,c)=>
if(解决方案(c.size>1)chars else chars++解决方案(c.toList))
.forall(xs=>xs.size==xs.distinct.size))解决方案else Map.empty

checkRow方法在哪里?在checkRows()方法私有def checkRow(row:Array[Int]):Boolean=row.distinct.length==row.length下面是checkRow方法?在checkRows()方法私有def checkRow(row:Array[Int]):Boolean=row.distinct.length==row.length下面是checkRow方法