Download
/*******************************************************************************
 * OscaR is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 2.1 of the License, or
 * (at your option) any later version.
 *   
 * OscaR is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License  for more details.
 *   
 * You should have received a copy of the GNU Lesser General Public License along with OscaR.
 * If not, see http://www.gnu.org/licenses/lgpl-3.0.en.html
 ******************************************************************************/
package oscar.examples.cp.hakank

import oscar.cp.modeling._

import oscar.cp.core._
import scala.io.Source._
import scala.math._

/**
 *
 * http://en.wikipedia.org/wiki/Killer_Sudoku
 * """
 * Killer sudoku (also killer su doku, sumdoku, sum doku, addoku, or
 * samunamupure) is a puzzle that combines elements of sudoku and kakuro.
 * Despite the name, the simpler killer sudokus can be easier to solve
 * than regular sudokus, depending on the solver's skill at mental arithmetic;
 * the hardest ones, however, can take hours to crack.
 *
 * ...
 *
 * The objective is to fill the grid with numbers from 1 to 9 in a way that
 * the following conditions are met:
 *
 * - Each row, column, and nonet contains each number exactly once.
 * - The sum of all numbers in a cage must match the small number printed
 *   in its corner.
 * - No number appears more than once in a cage. (This is the standard rule
 *   for killer sudokus, and implies that no cage can include more
 *   than 9 cells.)
 *
 * In 'Killer X', an additional rule is that each of the long diagonals
 * contains each number once.
 * """
 *
 * Here we solve the problem from the Wikipedia page, also shown here
 * http://en.wikipedia.org/wiki/File:Killersudoku_color.svg
 *
 * The output is:
 *   2 1 5 6 4 7 3 9 8
 *   3 6 8 9 5 2 1 7 4
 *   7 9 4 3 8 1 6 5 2
 *   5 8 6 2 7 4 9 3 1
 *   1 4 2 5 9 3 8 6 7
 *   9 7 3 8 1 6 4 2 5
 *   8 2 1 7 3 9 5 4 6
 *   6 5 9 4 2 8 7 1 3
 *   4 3 7 1 6 5 2 8 9
 *
 * 
 *  @author Hakan Kjellerstrand hakank@gmail.com
 * http://www.hakank.org/oscar/
 *
 */

object KillerSudoku {


  /**
   * Ensure that the sum of the segments
   * in cc == res
   *
   */
  def calc(cp: CPSolver,
           cc: Array[Int],
           x: Array[Array[CPIntVar]],
           res: Int) {

    val len = (cc.length / 2).toInt

    // sum the numbers
    cp.add(sum(for{i <- 0 until len} yield x(cc(i*2)-1)(cc(i*2+1)-1)) == res)
  }
  
  def main(args: Array[String]) {

    val cp = CPSolver()

    //
    // data
    //

    // size of matrix
    val cell_size = 3
    val CELLS = 0 until cell_size
    val n = cell_size*cell_size
    val RANGE = 0 until n

    // For a better view of the problem, see
    //  http://en.wikipedia.org/wiki/File:Killersudoku_color.svg

    // hints
    //  sum, the hints
    // Note: this is 1-based
    val problem = Array(Array( 3,  1,1,  1,2),
                        Array(15,  1,3,  1,4, 1,5),
                        Array(22,  1,6,  2,5, 2,6, 3,5),
                        Array(4,   1,7,  2,7),
                        Array(16,  1,8,  2,8),
                        Array(15,  1,9,  2,9, 3,9, 4,9),
                        Array(25,  2,1,  2,2, 3,1, 3,2),
                        Array(17,  2,3,  2,4),
                        Array( 9,  3,3,  3,4, 4,4),
                        Array( 8,  3,6,  4,6, 5,6),
                        Array(20,  3,7,  3,8, 4,7),
                        Array( 6,  4,1,  5,1),
                        Array(14,  4,2,  4,3),
                        Array(17,  4,5,  5,5, 6,5),
                        Array(17,  4,8,  5,7, 5,8),
                        Array(13,  5,2,  5,3, 6,2),
                        Array(20,  5,4,  6,4, 7,4),
                        Array(12,  5,9,  6,9),
                        Array(27,  6,1,  7,1, 8,1, 9,1),
                        Array( 6,  6,3,  7,2, 7,3),
                        Array(20,  6,6,  7,6, 7,7),
                        Array( 6,  6,7,  6,8),
                        Array(10,  7,5,  8,4, 8,5, 9,4),
                        Array(14,  7,8,  7,9, 8,8, 8,9),
                        Array( 8,  8,2,  9,2),
                        Array(16,  8,3,  9,3),
                        Array(15,  8,6,  8,7),
                        Array(13,  9,5,  9,6, 9,7),
                        Array(17,  9,8,  9,9))


    val num_p = problem.length // Number of segments
    println("num_p: " + num_p)

    //
    // Decision variables
    // 
    val x = Array.fill(n,n)(CPIntVar(0 to 9)(cp))
    val x_flat = x.flatten

    //
    // constraints
    //
    var numSols = 0

    cp.solve subjectTo {

      // rows and columns
      for(i <- RANGE) {
        cp.add(allDifferent( Array.tabulate(n)(j=> x(i)(j))), Strong)
        cp.add(allDifferent( Array.tabulate(n)(j=> x(j)(i))), Strong)
      }
      
      // blocks
      for(i <- CELLS; j <- CELLS) {
        cp.add(allDifferent(  (for{ r <- i*cell_size until i*cell_size+cell_size;
                                    c <- j*cell_size until j*cell_size+cell_size
              } yield x(r)(c)).toArray), Strong)
      }
      
      for(i <- 0 until num_p) {
        val segment = problem(i)

        // Remove the sum from the segment
        val s2 = for(i<-1 until segment.length) yield segment(i)                                                  
        // sum this segment
        calc(cp, s2, x, segment(0))

        // all numbers in this segment must be distinct
        val len = segment.length / 2
        cp.add( allDifferent(for(j <- 0 until len) yield x(s2(j * 2) - 1)(s2(j * 2 + 1) - 1)))

      }

    } search {
       
      binaryFirstFail(x_flat)
    } onSolution {
      
      for(i <- RANGE) {
        println(x(i).mkString(""))
      }
      println()

      numSols += 1

   } 
   println(cp.start())

  }

}