As with normal sudoku, Killer Sudoku implements another dimension to the sudoku challenge; with the addition of 'cages', a group of cells must add up to the total of the cage, which in a sense limits which values can be placed onto the board. Often, but not always, will you find simply cages instead of hard values as starts to your board game.
Awhile ago, I had made a sudoku solver, which, given a solvable sudoku board, popped out all available solutions. The solver worked both logistically (solving a board assuming no guessing or trial-and-error) and with a brute force attempt (iteration over the sequence of possible values).
To be honest, I hadn't thought of a good way to program a solver for Killer Sudoku logistically, but I wanted to at least try a brute force attempt at it.
Here is what I came up with:
#/usr/bin/python
import math
board = [[0 for col in range(9)] for row in range(9)]
def rowCheck(testx, testy, board):
test=board[testx][testy]
possible=[]
for each in range(0,9):
possible.append(board[testx][each])
if possible.count(test) ==1:
return 1
else:
return 0
def columnCheck(testx, testy, board):
test=board[testx][testy]
possible=[]
for each in range(0,9):
possible.append(board[each][testy])
if possible.count(test) ==1:
return 1
else:
return 0
def nonetCheck(testx,testy,board):
test=board[testx][testy]
rootx=int(math.floor(testx/3))*3
rooty=int(math.floor(testy/3))*3
possible=[]
possible.append(board[rootx][rooty])
possible.append(board[rootx][rooty+1])
possible.append(board[rootx][rooty+2])
possible.append(board[rootx+1][rooty])
possible.append(board[rootx+1][rooty+1])
possible.append(board[rootx+1][rooty+2])
possible.append(board[rootx+2][rooty])
possible.append(board[rootx+2][rooty+1])
possible.append(board[rootx+2][rooty+2])
if possible.count(test) == 1:
return 1
else:
return 0
def cellCheck(testx,testy,board):
test=board[testx][testy]
if test==10:
return 0
else:
return 1
def cageValidate(cages):
for cage in cages:
if len(cage) != 2:
print "Each cage must contain exactly 2 elements!", cage
return -1
if type(cage[0]) is not int:
print "The first element of each cage must be an integer!",cage
return -1
if type(cage[1]) is not list:
print "The second element of each cage must be a list!",cage
return -1
cagevals=cage[1]
if len(cagevals)==0:
print "Every cage must have at least one cell!"
return -1
for set in cagevals:
if len(set) != 2:
print "Every cell coordinate set must have two coordinates!",set
return -1
if type(set[0]) is not int or type(set[1]) is not int:
print "Every cell coordinate must be an integer!",set
return -1
def solve(board,cages):
ioerow=0
ioecol=0
while ioerow != 9:
board[ioerow][ioecol]=board[ioerow][ioecol]+1
if cellCheck(ioerow,ioecol,board) == 0:
board[ioerow][ioecol]=0
ioecol=ioecol-1
if ioecol == -1:
ioecol=8
ioerow=ioerow-1
if ioerow == -1:
print "Solve failed"
return 0
continue
if rowCheck(ioerow,ioecol,board) == 0:
continue
if columnCheck(ioerow,ioecol,board) == 0:
continue
if nonetCheck(ioerow,ioecol,board) == 0:
continue
currcell = [ioerow, ioecol]
cagefail=0
for cage in cages:
if currcell not in cage[1]:
continue
index=cage[1].index(currcell)
cagesum=0
coords=cage[1]
for x in range(0,index+1):
temp=coords[x]
cagesum=cagesum+board[coords[x][0]][coords[x][1]]
if index == len(coords)-1 and cagesum == cage[0]:
continue
if index == len(coords)-1 and cagesum != cage[0]:
cagefail=1
break
if index != len(coords)-1 and cagesum >= cage[0]:
cagefail =1
break
if cagefail == 0:
ioecol = ioecol +1
if ioecol == 9:
ioecol = 0
ioerow = ioerow +1
cages=[[11,[[0,0],[1,0]]],
[8, [[0, 1],[1, 1]]],
[31, [[0,2],[0,3],[0,4],[0,5],[0,6],[1,4]]],
[8, [[0,7],[1,7]]],
[13, [[0,8],[1,8]]],
[14, [[1,2],[1,3],[2,2]]],
[16, [[1,5],[1,6],[2,6]]],
[11, [[2,0],[3,0]]],
[5, [[2,1],[3,1]]],
[29, [[2,3],[2,4],[2,5],[3,4]]],
[8, [[2,7],[3,7]]],
[9, [[2,8],[3,8]]],
[25, [[3,2],[3,3],[4,0],[4,1],[4,2]]],
[20, [[3,5],[3,6],[4,6],[4,7],[4,8]]],
[21, [[4,3],[5,0],[5,1],[5,2],[5,3],[6,0]]],
[22, [[4,4],[5,4],[6,4]]],
[33, [[4,5],[5,5],[5,6],[5,7],[5,8],[6,8]]],
[14, [[6,1],[6,2],[6,3]]],
[17, [[6,5],[6,6],[6,7]]],
[28, [[7,0],[7,1],[7,2],[7,3],[8,0]]],
[5, [[7,4],[8,4]]],
[28, [[7,5],[7,6],[7,7],[7,8],[8,8]]],
[22, [[8,1],[8,2],[8,3]]],
[7, [[8,5],[8,6],[8,7]]]]
solve(board,cages)
cageValidate(cages)
for row in board:
for col in row:
print col,
print ""
Essentially, brute forcing a killer sudoku board is pretty easy; in addition to the simple row, column and nonet checks, Killer Sudoku must have a cage check. This is handled by the above cageValidate function.
I believe this must have been one of my first pieces of code written in python; had I written this today, I would have written some optimization within the cageValidate function (there is no point in validating all cages after every iteration, for example), and I would have encapsulated all of these definitions into a single class.