Download
# Killer Sudoku
#
# Killer Sudoku is a Sudoku puzzle with a twist.
#
# The standard Sudoku puzzle is a 9x9 matrix to be filled out
# in such a way that each row, column and each of the 3x3
# sub-matrices would contain the numbers 1 to 9.
#
# In Killer Sudoku, there is one extra constraint. There are
# so-called "cages" which are much like the 3x3 sub-matrices.
# They may contain at most one of each number (1 to 9) and the
# numbers' sum must be equal to the cage's number (see CSPLib link).
#
# CSPlib Problem 057 - http://www.csplib.org/Problems/prob057/
from Numberjack import *
def get_model(param):
N = param['N']
cages = parsecages(examplecages)
grid = Matrix(N*N, N*N, 1, N*N)
model = Model(
[AllDiff(row) for row in grid.row],
[AllDiff(col) for col in grid.col],
# Contents of sub-matrices must be distinct.
[AllDiff(grid[x:x + N, y:y + N]) for x in range(0, N*N, N)
for y in range(0, N * N, N)],
)
# Contents of cages must be distinct.
for cagetotal, cellids in cages:
cagecells = [grid.flat[x] for x in cellids]
model += AllDiff(cagecells)
model += Sum(cagecells) == cagetotal
return grid, model
def solve(param):
grid, model = get_model(param)
solver = model.load(param['solver'])
solver.setVerbosity(param['verbose'])
solver.solve()
if solver.is_sat():
print str(grid)
elif solver.is_unsat():
print "Unsatisfiable"
else:
print "Unknown"
def parsecages(textblob):
cages = []
for line in textblob.split("\n"):
if not line.strip():
continue
bits = map(int, line.split())
cages.append((bits[0], bits[1:]))
return cages
examplecages = """
3 0 1
15 2 3 4
22 5 13 14 22
4 6 15
16 7 16
15 8 17 26 35
25 9 10 18 19
17 11 12
9 20 21 30
8 23 32 41
20 24 25 33
6 27 36
14 28 29
17 31 40 49
17 34 42 43
13 37 38 46
20 39 48 57
12 44 53
27 45 54 63 72
6 47 55 56
20 50 59 60
6 51 52
10 58 66 67 75
14 61 62 70 71
8 64 73
16 65 74
15 68 69
13 76 77 78
17 79 80
"""
if __name__ == '__main__':
default = {'N': 3, 'solver': 'Mistral', 'verbose': 0}
param = input(default)
solve(param)