# Copyright 2010 Hakan Kjellerstrand hakank@bonetmail.com # # Licensed under the Apache License, Version 2.0 (the 'License'); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an 'AS IS' BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Strimko problem in Google CP Solver. From 360: A New Twist on Latin Squares http://threesixty360.wordpress.com/2009/08/04/a-new-twist-on-latin-squares/ ''' The idea is simple: each row and column of an nxn grid must contain the number 1, 2, ... n exactly once (that is, the grid must form a Latin square), and each "stream" (connected path in the grid) must also contain the numbers 1, 2, ..., n exactly once. ''' For more information, see: * http://www.strimko.com/ * http://www.strimko.com/rules.htm * http://www.strimko.com/about.htm * http://www.puzzlersparadise.com/Strimko.htm I have blogged about this (using MiniZinc model) in 'Strimko - Latin squares puzzle with "streams"' http://www.hakank.org/constraint_programming_blog/2009/08/strimko_latin_squares_puzzle_w_1.html Compare with the following models: * MiniZinc: http://hakank.org/minizinc/strimko2.mzn * ECLiPSe: http://hakank.org/eclipse/strimko2.ecl * SICStus: http://hakank.org/sicstus/strimko2.pl * Gecode: http://hakank.org/gecode/strimko2.cpp This model was created by Hakan Kjellerstrand (hakank@bonetmail.com) See my other Google CP Solver models: http://www.hakank.org/google_or_tools/ """ import sys from constraint_solver import pywrapcp def main(streams="", placed=""): # Create the solver. solver = pywrapcp.Solver('Strimko') # # default problem # if streams == "": streams = [ [1,1,2,2,2,2,2], [1,1,2,3,3,3,2], [1,4,1,3,3,5,5], [4,4,3,1,3,5,5], [4,6,6,6,7,7,5], [6,4,6,4,5,5,7], [6,6,4,7,7,7,7]] # Note: This is 1-based placed = [ [2,1,1], [2,3,7], [2,5,6], [2,7,4], [3,2,7], [3,6,1], [4,1,4], [4,7,5], [5,2,2], [5,6,6]] n = len(streams) num_placed = len(placed) print "n:", n # # variables # x = {} for i in range(n): for j in range(n): x[i,j] = solver.IntVar(1, n, 'x[%i,%i]' % (i,j)) x_flat = [x[i,j] for i in range(n) for j in range(n)] # # constraints # # all rows and columns must be unique, i.e. a Latin Square for i in range(n): row = [x[i,j] for j in range(n)] solver.Add(solver.AllDifferent(row)) col = [x[j,i] for j in range(n)] solver.Add(solver.AllDifferent(col)) # # streams # for s in range(1, n+1): tmp = [x[i,j] for i in range(n) for j in range(n) if streams[i][j] == s] solver.Add(solver.AllDifferent(tmp)) # # placed # for i in range(num_placed): # note: also adjust to 0-based solver.Add(x[placed[i][0]-1, placed[i][1]-1] == placed[i][2]) # # search and solution # db = solver.Phase(x_flat, solver.INT_VAR_DEFAULT, solver.INT_VALUE_DEFAULT) solver.NewSearch(db) num_solutions = 0 while solver.NextSolution(): for i in range(n): for j in range(n): print x[i,j].Value(), print print num_solutions += 1 solver.EndSearch() print print 'num_solutions:', num_solutions print 'failures:', solver.Failures() print 'branches:', solver.Branches() print 'WallTime:', solver.WallTime(), 'ms' if __name__ == '__main__': if len(sys.argv) > 1: problem_file = sys.argv[1] execfile(problem_file) main(streams, placed) else: main()