Example #1
0
    def __repr__(self): return board_string(self.board)

    def related(self, x, y):
Example #2
0
    ('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()
Example #3
0
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
Example #4
0
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