/SwiftConstraint

A constraint programming library written in Swift

Primary LanguageSwiftGNU Lesser General Public License v2.1LGPL-2.1

SwiftConstraint

A constraint programming library written in Swift, based on python-constraint. Note that this library was written as an exercise to learn Swift, see the related projects for other supported options.

You will need to instantiate a Problem, comprised of Variables over a possible Domain, and constrained by Constraints. Several different types of constraints are built in, including:

  • AllDifferentConstraint
  • AllEqualConstraint
  • MaxSumConstraint
  • ExactSumConstraint
  • MinSumConstraint
  • DifferenceConstraint
  • AbsDifferenceConstraint
  • ProductConstraint
  • QuotientConstraint
  • InvertibleQuotientConstraint
  • InSetConstraint
  • NotInSetConstraint
  • SomeInSetConstraint
  • SomeNotInSetConstraint

These constraints are implemented generically for types satisfying the Hashable or Arithmetic protocols.

Examples

Simple

Define two integer variables m, n, over the domain d = {1..10} that must sum to exactly 4

  d = Domain<Int>(values: 1...10)
  m = Variable<Int>(domain: d)
  n = Variable<Int>(domain: d)
  c = ExactSumConstraint<Int>(variables: [m, n], sum: 4)
  p = Problem<Int>()
  p.variables.append(m)
  p.variables.append(n)
  p.constraints.append(c)
  
  # Solve the problem with the BacktrackingSolver.
  let solver = BacktrackingSolver()
  solver.solve(p)
  
  # Print all valid solutions (assignments of x and y).
  for sol in p.solutions {
    print("x:", sol[0], "y:", sol[1], "separator: " ")
  }

Sudoku

Given a Sudoku "board" defined as an N^2 list of integers (0 if not yet filled in), initialize a Problem with the Sudoku puzzle constraints, and use the BacktrackingSolver to solve it:

public class SudokuPuzzle {
  let board: [Int]
  var problem: Problem<Int>
  let size: Int
  
  public init(board: [Int]) {
    self.board = board
    size = Int(sqrt(Double(board.count)))
    problem = Problem<Int>()
    self.init_variables()
    self.init_constraints()
  }
  
  func init_variables() {
    let domain = Domain<Int>(values: 1...size)
    for _ in 1...size {
      for _ in 1...size {
        problem.variables.append(Variable<Int>(domain: domain))
      }
    }
  }
  
  func init_constraints() {
    // Rows must be all different
    for i in 0..<size {
      let row = Array(problem.variables[i*size..<(i+1)*size])
      let c = AllDifferentConstraint(variables: row)
      problem.constraints.append(c)
    }
    
    // Cols must be all different
    for j in 0..<size {
      let col = (0..<size).map { i in self.problem.variables[i*self.size+j] }
      let c = AllDifferentConstraint(variables: col)
      problem.constraints.append(c)
    }
    
    // Boxes must be all diferent
    for i in 0..<3 {
      for j in 0..<4 {
        var box: [Variable<Int>] = []
        for k in 0..<4 {
          for l in 0..<3 {
            let row = 3*j + l
            let col = 4*i + k
            let index = row*size + col
            box.append(problem.variables[index])
          }
        }
        let c = AllDifferentConstraint(variables: box)
        problem.constraints.append(c)
      }
    }
    
    // Pre-specified numbers
    for (i, v) in board.enumerate() {
      if v != 0 { // has a value
        let c = InSetConstraint(variables: [problem.variables[i]], set: [v])
        problem.constraints.append(c)
      }
    }
  }
  
  public func solution() -> [Int]? {
    let solver = BacktrackingSolver(forwardCheck: true)
    solver.solve(problem)
    return problem.solutions.first
  }
}

Related projects