Untitled

 avatar
unknown
plain_text
2 years ago
4.6 kB
2
Indexable
#!/usr/bin/env python
# coding:utf-8

"""
Each sudoku board is represented as a dictionary with string keys and
int values.
e.g. my_board['A1'] = 8
"""
import sys
import time

ROW = "ABCDEFGHI"
COL = "123456789"


def print_board(board):
    """Helper function to print board in a square."""
    print("-----------------")
    for r in ROW:
        row = ''
        for c in COL:
            row += (str(board[r + c]) + " ")
        print(row)


def board_to_string(board):
    """Helper function to convert board dictionary to string for writing."""
    ordered_vals = []
    for r in ROW:
        for c in COL:
            ordered_vals.append(str(board[r + c]))
    return ''.join(ordered_vals)


def backtracking(board):
    """Takes a board and returns solved board."""
    r, c = get_mrv(board)
    if r is None:
        return board

    for val in range(1, 10):
        if valid(board, val, r, c):
            if not forward_checking(board, val, r, c):
                continue
            board[ROW[r] + COL[c]] = val
            solved_board = backtracking(board)
            if solved_board:
                return solved_board
            board[ROW[r] + COL[c]] = 0

    return None


def get_mrv(board):
    mrv = 9
    r, c = None, None

    for i in range(9):
        for j in range(9):
            if board[ROW[i] + COL[j]] == 0:
                rv_count = get_rv_count(board, i, j)
                if rv_count < mrv:
                    mrv = rv_count
                    r, c = i, j

    return r, c


def get_rv_count(board, r, c):
    rv_count = 0

    for val in range(1, 10):
        if valid(board, val, r, c):
            rv_count += 1

    return rv_count


def valid(board, val, r, c):
    # check column constraints
    for i in range(9):
        if board[ROW[i] + COL[c]] == val:
            return False

    # check row constraints
    for j in range(9):
        if board[ROW[r] + COL[j]] == val:
            return False

    # check box constraints
    box_row = r // 3
    box_col = c // 3
    for i in range(3):
        for j in range(3):
            if board[ROW[box_row * 3 + i] + COL[box_col * 3 + j]] == val:
                return False

    return True


def forward_checking(board, val, r, c):
    board[ROW[r] + COL[c]] = val
    for i in range(9):
        for j in range(9):
            if board[ROW[i] + COL[j]] == 0 and get_rv_count(board, i, j) == 0:
                board[ROW[r] + COL[c]] = 0
                return False
    board[ROW[r] + COL[c]] = 0

    return True


if __name__ == '__main__':
    start_time = time.time()
    if len(sys.argv) > 1:

        # Running sudoku solver with one board $python3 sudoku.py <input_string>.
        print(sys.argv[1])
        # Parse boards to dict representation, scanning board L to R, Up to Down
        board = {ROW[r] + COL[c]: int(sys.argv[1][9 * r + c])
                 for r in range(9) for c in range(9)}

        solved_board = backtracking(board)

        # Write board to file
        out_filename = 'output.txt'
        outfile = open(out_filename, "w")
        outfile.write(board_to_string(solved_board))
        outfile.write('\n')

    else:
        # Running sudoku solver for boards in sudokus_start.txt $python3 sudoku.py

        #  Read boards from source.
        src_filename = 'sudokus_start.txt'
        try:
            srcfile = open(src_filename, "r")
            sudoku_list = srcfile.read()
        except:
            print("Error reading the sudoku file %s" % src_filename)
            exit()

        # Setup output file
        out_filename = 'output.txt'
        outfile = open(out_filename, "w")

        # Solve each board using backtracking
        for line in sudoku_list.split("\n"):

            if len(line) < 9:
                continue

            # Parse boards to dict representation, scanning board L to R, Up to Down
            board = {ROW[r] + COL[c]: int(line[9 * r + c])
                     for r in range(9) for c in range(9)}

            # Print starting board. TODO: Comment this out when timing runs.
            print_board(board)

            # Solve with backtracking
            solved_board = backtracking(board)

            # Print solved board. TODO: Comment this out when timing runs.
            print_board(solved_board)

            # Write board to file
            outfile.write(board_to_string(solved_board))
            outfile.write('\n')

        print("Finishing all boards in file.")
    end_time = time.time()
    print("Program completed in %.3f second(s)" % (end_time - start_time))