def __repr__(self): return board_string(self.board) def related(self, x, y):
('puzzles/dec-branch-2.txt', STANDARD_OPTIONS), ('puzzles/dec-branch-3.txt', STANDARD_OPTIONS) ] # OPTIONS = set('0123456789ABCDEF') # OPTIONS = set('123456789ABCDEFG') # OPTIONS = set('123456789ABCDEFGHIJKLMNOP') # OPTIONS = set('ABCDEFGHIJKLMNOPQRSTUVWXY') hex = [ ('puzzles/hex-1.txt', set('0123456789ABCDEF')), ('puzzles/hex-2.txt', set('123456789ABCDEFG')) ] cent = [ ('puzzles/cent-1.txt', set('123456789ABCDEFGHIJKLMNOP')), ('puzzles/cent-2.txt', set('ABCDEFGHIJKLMNOPQRSTUVWXY')) ] for path, options in dec: board = sudokuio.get_board(path) print 'Original puzzle' print sudokuio.board_string(board) print board = sudoku.solve(board, options) print 'Single pass attempt' print sudokuio.board_string(board) print board = sudokuio.get_board(path) board = suii.solve(board, options) print sudokuio.board_string(board) raw_input()
def solve(board, options = DEFAULT_OPTIONS): boxlen = int(sqrt(len(options))) board = [ [ Node(x, y, c) if type(c) == str and c not in ('',' ') else Node(x, y, options.copy()) for y, c in enumerate(line) ] for x, line in enumerate(board) ] def row(board, point): row = board[point.x] return row[:point.y] + row[point.y + 1:] def col(board, point): rows = board[:point.x] + board[point.x + 1:] return [row[point.y] for row in rows] def box(board, point): boxx = (point.x / boxlen) * boxlen boxy = (point.y / boxlen) * boxlen return [ board[bx][by] for by in xrange(boxy, boxy + boxlen) if by != point.y for bx in xrange(boxx, boxx + boxlen) if bx != point.x ] def related(board, point): return row(board, point) + col(board, point) + box(board, point) def solved(board): return set not in map(type, chain(*board)) def invalid(board): for point, node in knowns(board): if node in related(board, point): return True def resolve(board, point, options, relations): if len(options) == 1: board[point.x][point.y] = node = options.pop() for i in filter(unknown, relations): i.discard(node) return True def solve_point(board, point, val): board[point.x][point.y] = val for i in filter(unknown, related(board, point)): i.discard(val) def knowns(board): for x, line in enumerate(board): for y, node in enumerate(line): if known(node): yield (Point(x, y), node) def unknowns(board): for x, line in enumerate(board): for y, node in enumerate(line): if unknown(node): yield (Point(x, y), node) def reduce(board, point, node): relrow = row(board, point) relcol = col(board, point) relbox = box(board, point) relations = relrow + relcol + relbox if resolve(board, point, node, relations): return True choices = len(point) # track if we narrow down choices node -= set(filter(known, relations)) if resolve(board, point, node, relations): return True # must be what nothing else can be for group in relrow, relcol, relbox: unique_options = node - set(chain(*filter(unknown, group))) if resolve(board, point, node, relations): return True if choices > len(node): return True def solve_board(board): progress = True while progress: progress = False for point, node in unknowns(board): if reduce(board, point, node): progress = True ''' wrong data structure for this! Shouldn't have to sort every time ''' def get_next_board(queue): for influence in reversed(sorted(queue)): options = queue[influence] while options: point, option, board = options.pop() b2 = deepcopy(board) solve_point(b2, point, option) if invalid(b2): continue solve_board(b2) return b2 def add_options(queue, additions): for influence, options in additions.iteritems(): queue[influence].extend(options) def next_options(board): # get coordinates of all points with # fewest number of guesses to choose from min_guesses = 1000000 # max int for point, node in unknowns(board): opt_count = len(point) if opt_count < min_guesses: min_guesses = opt_count guess_points = [point] elif opt_count == min_guesses: guess_points.append(point) # yield clones of board with each point guessed each way # prioritize by exploring most impactful guesses first options = defaultdict(list) for point in guess_points: for option in board[point.x][point.y]: influence = len([i for i in filter(unknown, related(board, point)) if option in i]) options[influence].append((point, option, board)) return options # Order of traversal based on size of impact to board of next guess visited_boards = [] solve_board(board) board_queue = defaultdict(list) while not solved(board): visited_boards.append(board) print sudokuio.board_string(board) print add_options(board_queue, next_options(board)) while board in visited_boards: board = get_next_board(board_queue) if not board: return False return board
def solve(board, options = DEFAULT_OPTIONS): boxlen = int(sqrt(len(options))) board = [ [c if type(c) == str and c not in ('',' ') else options.copy() for c in line] for line in board ] def known(point): return type(point) != set def unknown(point): return type(point) == set def row(board, x): return list(board[x]) def col(board, y): return [line[y] for line in board] def box(board, x, y): boxx = x / boxlen * boxlen boxy = y / boxlen * boxlen return [ board[bx][by] for by in xrange(boxy, boxy + boxlen) for bx in xrange(boxx, boxx + boxlen) ] def related(board, x, y): boxx = (x / boxlen) * boxlen boxy = (y / boxlen) * boxlen return \ board[x][:y] + board[x][y+1:] + \ [line[y] for line in board[:x] + board[x+1:]] + [ board[bx][by] for by in xrange(boxy, boxy + boxlen) if by != y for bx in xrange(boxx, boxx + boxlen) if bx != x ] def solved(board): return set not in map(type, chain(*board)) def invalid(board): for x, line in enumerate(board): for y, point in enumerate(line): if type(point) != set and point in related(board, x, y): return True def resolve(board, x, y, options, relations): if len(options) == 1: board[x][y] = options.pop() for i in relations: if type(i) == set: i.discard(board[x][y]) return True def solve_point(board, x, y, val): board[x][y] = val for i in related(board, x, y): if type(i) == set: i.discard(val) def reduce(board, x, y): point = board[x][y] def notpoint(i): return i is not point relrow = filter(notpoint, row(board, x)) relcol = filter(notpoint, col(board, y)) relbox = filter(notpoint, box(board, x, y)) relations = relrow + relcol + relbox if resolve(board, x, y, point, relations): return True choices = len(point) # track if we narrow down choices point -= set(filter(known, relations)) if resolve(board, x, y, point, relations): return True # must be what nothing else can be for group in relrow, relcol, relbox: unique_options = point - set(chain(*filter(unknown, group))) if resolve(board, x, y, unique_options, relations): return True if choices > len(point): return True def solve_board(board): progress = True while progress: progress = False for x, line in enumerate(board): for y, point in enumerate(line): if type(point) == set and reduce(board, x, y): progress = True ''' wrong data structure for this! Shouldn't have to sort every time ''' def get_next_board(queue): for influence in reversed(sorted(queue)): options = queue[influence] while options: x, y, option, board = options.pop() b2 = deepcopy(board) solve_point(b2, x, y, option) if valid(b2): continue solve_board(b2) return b2 def add_options(queue, additions): for influence, options in additions.iteritems(): queue[influence].extend(options) def next_boards(board): # get coordinates of all points with # fewest number of guesses to choose from min_guesses = 1000000 # max int for x, line in enumerate(board): for y, point in enumerate(line): if type(point) != set: continue opt_count = len(point) if opt_count < min_guesses: min_guesses = opt_count guess_points = [(x,y)] elif opt_count == min_guesses: guess_points.append((x,y)) # yield clones of board with each point guessed each way # prioritize by exploring most impactful guesses first points = defaultdict(list) for x, y in guess_points: for option in board[x][y]: influence = len([i for i in related(board, x, y) if type(i) == set and option in i]) points[influence].append((x, y, option, board)) return points # Order of traversal based on size of impact to board of next guess visited_boards = [] solve_board(board) board_queue = defaultdict(list) while not solved(board): visited_boards.append(board) print sudokuio.board_string(board) print add_options(board_queue, next_boards(board)) while board in visited_boards: board = get_next_board(board_queue) if not board: return False return board