Skip to content

Instantly share code, notes, and snippets.

@MetroWind
Created November 24, 2015 03:37
Show Gist options
  • Save MetroWind/5a3c6ca086d91a202010 to your computer and use it in GitHub Desktop.
Save MetroWind/5a3c6ca086d91a202010 to your computer and use it in GitHub Desktop.
A library to work with Sudokus.
import copy
class Sudoku(object):
"""The Sudoku class. Zero means blank."""
Numbers = set(range(1, 10))
def __init__(self, sudoku=None):
if sudoku:
self.Data = copy.deepcopy(sudoku.Data)
else:
self.Data = []
for i in range(9):
self.Data.append([0,]*9)
def __getitem__(self, idx):
Row, Col = idx
return self.Data[Row][Col]
def __setitem__(self, idx, v):
Row, Col = idx
self.Data[Row][Col] = v
return v
def getRow(self, i):
return self.Data[i]
def getCol(self, i):
return [row[i] for row in self.Data]
def getBlock(self, row, col, flatten=False):
Result = []
for iRow in range(3):
Row = []
for iCol in range(3):
Row.append(self[3 * row + iRow, 3 * col + iCol])
Result.append(Row)
if flatten:
return Result[0] + Result[1] + Result[2]
else:
return Result
def __str__(self):
Lines = []
for Row in range(9):
if Row % 3 == 0:
Lines.append("+---+---+---+")
Line = []
for Col in range(9):
if Col % 3 == 0:
Line.append('|')
n = self[Row, Col]
if n == 0:
Line.append(' ')
else:
Line.append(str(n))
Line.append('|')
Lines.append(''.join(Line))
Lines.append("+---+---+---+")
return '\n'.join(Lines)
@classmethod
def validateSeq(cls, xs, allow_blank=False):
Result = None
if allow_blank:
Xs = [x for x in xs if x > 0]
Set = set(Xs)
Result = (len(Xs) == len(Set) and Set.issubset(cls.Numbers))
else:
Set = set(xs)
Result = (Set == cls.Numbers)
return Result
@classmethod
def validateBlock(cls, blk, allow_blank=False):
return cls.validateSeq(blk[0] + blk[1] + blk[2], allow_blank)
def validate(self, allow_blank=False):
# Validate rows
for i in range(9):
if not self.validateSeq(self.getRow(i), allow_blank):
return False
# Validate columns
for i in range(9):
if not self.validateSeq(self.getCol(i), allow_blank):
return False
# Validate blocks
for row in range(3):
for col in range(3):
if not self.validateBlock(self.getBlock(row, col), allow_blank):
return False
return True
def validateSite(self, row, col, allow_blank=False):
BlockRow = row // 3
BlockCol = col // 3
return self.validateSeq(self.getRow(row), allow_blank) and \
self.validateSeq(self.getCol(col), allow_blank) and \
self.validateSeq(self.getBlock(BlockRow, BlockCol, True))
def sitePossibilities(self, row, col):
"""Return all possible number for the specified site.
"""
CanRow = self.Numbers - set([x for x in self.getRow(row) if x > 0])
CanCol = self.Numbers - set([x for x in self.getCol(col) if x > 0])
CanBlock = self.Numbers - set([x for x in self.getBlock(row // 3, col // 3, True) if x > 0])
return CanRow & CanCol & CanBlock
def findStartingRow(self):
"""Return the index of the row that has least blanks (but has at least
one).
"""
StartingRow = 0
Blanks = 10
for Row, i in zip(self.Data, range(9)):
n = Row.count(0)
if n > 0:
if n < Blanks:
Blanks = n
StartingRow = i
return StartingRow
def solve(self):
# Fill in the 1st number
for Row, iRow in zip(self.Data, range(9)):
for n, iCol in zip(Row, range(9)):
if n == 0:
Possibilities = self.sitePossibilities(iRow, iCol)
if not Possibilities:
return False
for N in Possibilities:
self[iRow, iCol] = N
if self.solve():
return True
else:
self[iRow, iCol] = 0
return False
return True
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment