Aaron

Merge branch 'working_1' into 'master'

Solves easy and medium boards, but not hard yet;



See merge request !1
068 000 930
042 000 600
190 080 040
085 201 007
700 890 000
209 007 503
020 100 050
850 040 760
473 052 009
... ...
000 068 030
190 000 000
803 100 200
400 051 060
700 020 004
000 070 800
010 005 007
004 000 000
050 030 100
... ...
250 003 000
000 000 270
870 006 400
020 008 193
015 040 800
000 100 004
000 734 000
000 600 009
064 009 058
... ...
import math
import random
import logging
class InvalidBoardSize(Exception):
pass
class Sudoku(object):
def __init__(self, size=9):
self.size = size
self.board = self.zero_board(self.size)
self.board = self.zero_board()
self.log = []
def __repr__(self):
out = ''
sr = int(math.sqrt(self.size))
for x in range(self.size):
for y in range(self.size):
out = '%s%s ' % (out, self.board[x][y])
... ... @@ -23,109 +27,242 @@ class Sudoku(object):
return out
def random_board(self, size=None):
if size is None:
size = self.size
@property
def board(self):
return self._board
for y in range(size):
for x in range(size):
try:
self.board[y][x] = random.choice(self.possible_list(y,x))
except:
print(self)
print(self.possible_square(y,x))
print(self.possible_row(y))
print(self.possible_col(x))
print(self.possible_list(y, x))
raise Exception('Fail')
@board.setter
def board(self, value):
side_len = len(value)
return True
root = int(math.sqrt(side_len))
def random_list(self, size=None):
if size is None:
size = self.size
if root**2 != side_len:
raise InvalidBoardSize('Board side length must be square number')
for row in value:
if len(row) != side_len:
raise InvalidBoardSize('Board row length must equal board height')
self._board = value
new_list = list(range(1, size+1))
random.shuffle(new_list)
return new_list
@property
def size(self):
return self._size
@size.setter
def size(self, value):
root = int(math.sqrt(value))
if root**2 != value:
raise InvalidBoardSize('Board size must be a square number')
self._size = value
def zero_board(self, size=None):
self.board = []
if size is None:
size = self.size
return [[0 for x in range(size)] for y in range(size)]
def possible_list(self, y, x):
def load_board_file(self, filename):
p_sq = self.possible_square(y, x)
p_col = self.possible_col(x)
p_row = self.possible_row(y)
self.board = []
rm = []
with open(filename, 'r') as f:
for z in p_sq:
if self.row_has(y, z) or self.col_has(x, z):
rm.append(z)
for line in f.readlines():
for n in rm:
p_sq.remove(n)
clean = line.strip()
row = []
return p_sq
if clean:
def possible_row(self, y):
for char in clean:
try:
c = int(char)
except:
continue
possible = self.random_list(self.size)
row.append(c)
for i in range(self.size):
root = int(math.sqrt(len(row)))
try:
possible.remove(self.board[y][i])
except ValueError:
continue
if root**2 != len(row):
raise Exception('Invalid row length on line %s' % f.line_num)
self.board.append(row)
return True
def save_board_file(self, filename):
return possible
with open(filename, 'w') as f:
f.write(self.__repr__())
def possible_col(self, x):
return True
possible = self.random_list(self.size)
def invert_opts(self, opts, all_opts=None):
if all_opts is None:
all_opts = list(range(1, self.size+1))
for i in range(self.size):
for opt in opts:
try:
possible.remove(self.board[i][x])
except ValueError:
all_opts.remove(opt)
except:
continue
return possible
return all_opts
def possible_square(self, y, x):
def valid_opts(self, y, x):
possible = self.random_list(self.size)
all_valids = []
sr = int(math.sqrt(self.size))
sx = int(x/sr)
sy = int(y/sr)
row_v = self.row_valids(y)
col_v = self.col_valids(x)
sqr_v = self.sqr_valids(y,x)
for i in range(1, self.size+1):
if i in row_v and i in col_v and i in sqr_v:
all_valids.append(i)
for xr in range(sx,sx+sr):
for yr in range(sy,sy+sr):
try:
possible.remove(self.board[yr][xr])
except ValueError:
continue
return all_valids
return possible
def col_valids(self, x):
used = []
for y in range(self.size):
value = self.board[y][x]
if value > 0:
used.append(value)
def row_has(self, y, n):
return self.invert_opts(used)
if n in self.board[y]:
return True
def row_valids(self, y):
used = []
for x in range(self.size):
value = self.board[y][x]
if value > 0:
used.append(value)
return False
return self.invert_opts(used)
def col_has(self, x, n):
def sqr_valids(self, y, x):
for i in range(self.size):
if self.board[i][x] == n:
return True
used = []
return False
root = int(math.sqrt(self.size))
sx = int(x/root)
sy = int(y/root)
for xr in range(sx*root,(sx+1)*root):
for yr in range(sy*root,(sy+1)*root):
value = self.board[yr][xr]
if value > 0:
used.append(value)
return self.invert_opts(used)
def count_zeros(self):
count = 0
for y in range(self.size):
for x in range(self.size):
if self.board[y][x] < 1:
count += 1
return count
def solve_board(self):
zeroes = self.count_zeros()
while zeroes > 0:
beg_zeroes = zeroes
for y in range(self.size):
for x in range(self.size):
value = self.board[y][x]
if value == 0:
opts = self.valid_opts(y,x)
#print('(%s, %s): %s' % (y,x, opts))
if len(opts) == 1:
self.board[y][x] = opts[0]
zeroes -= 1
self.log.append( [(y,x), opts[0]] )
continue
#if len(opts) > 1:
# redu_col = self.reduce_col(x)
# if len(redu_col) == 1:
# self.board[y][x] = redu_col[0]
# self.log.append( [(y,x), redu_col[0]] )
# zeroes -= 1
# continue
# redu_row = self.reduce_row(y)
# if len(redu_row) == 1:
# self.board[y][x] = redu_row[0]
# self.log.append( [(y,x), redu_row[0]] )
# zeroes -= 1
# continue
if len(opts) == 0:
raise Exception('Unsolvable board at (%s,%s)' % (y,x))
if zeroes == beg_zeroes:
raise Exception('Unsolvable board: no valid moves')
return True
def reduce_col(self, x):
sets = []
for y in range(self.size):
value = self.board[y][x]
if value == 0:
sets.append(self.valid_opts(y,x))
#print('RCOL: %s' % sets)
return self.reduce_set(*sets)
def reduce_row(self, y):
sets = []
for x in range(self.size):
value = self.board[y][x]
if value == 0:
sets.append(self.valid_opts(y,x))
#print('RROW: %s' % sets)
return self.reduce_set(*sets)
def reduce_sqr(self, y, x):
pass
def reduce_set(self, *args):
all_opts = []
for arg in args:
if not arg:
return arg
for opt in arg:
if opt not in all_opts:
all_opts.append(opt)
rm = []
for opt in all_opts:
in_all = True
for arg in args:
if opt not in arg:
in_all = False
break
if in_all:
rm.append(opt)
return self.invert_opts(rm, all_opts)
... ...
from sudoku import Sudoku
s = Sudoku(size=9)
s.random_board()
s.load_board_file('hard.txt')
print(s)
try:
s.solve_board()
except Exception as e:
print(s.log)
raise e
print(s)
... ...