def brute_force_solve_sudoku(sudoku: sudoku_board.Sudoku): # 'l' is a list variable that keeps the record of row and col in find_empty_location Function arr = sudoku.board_numbers location = [0, 0] # If there is no unassigned location, we are done if not find_empty_location(arr, location): return True # Assigning list values to row and col that we got from the above Function row = location[0] col = location[1] # consider digits 1 to 9 for num in range(1, 10): # if looks promising if check_location_is_safe(arr, row, col, num): # make tentative assignment sudoku.add_cell(row, col, num) # return, if success, ya! if brute_force_solve_sudoku(sudoku): return True # failure, unmake & try again sudoku.add_cell(row, col, 0) # this triggers backtracking return False
def test_add_cell_with_valid(): sudoku_board = Sudoku(boards.unsolved_easy) row = 3 column = 7 value = 5 sudoku_board.add_cell(row, column, value) assert sudoku_board.board_numbers[row][column] == value
def test_solve_naked_subset_row(): column = 1 row = 0 sudoku_board = Sudoku(boards.naked_pair_test) sudoku_board.board_numbers = sudoku_board.board_numbers.T assert sudoku_board.board_numbers[row, column] == 0 sudoku_solver.solve_naked_subset_row(sudoku_board, row) assert sudoku_board.board_numbers[row, column] == 1
def test_add_cell_with_invalid(): sudoku_board = Sudoku(boards.unsolved_easy) row = 3 column = 7 value = 's' with pytest.raises(Exception): # noinspection PyTypeChecker sudoku_board.add_cell(row, column, value)
def test_brute_force_solve_sudoku(): board = Sudoku(boards.unsolved_easy) sudoku_solver.brute_force_solve_sudoku(board) assert (board.board_numbers == boards.solved_easy).all() board = Sudoku(boards.unsolved_hard) sudoku_solver.brute_force_solve_sudoku(board) assert (board.board_numbers == boards.solved_hard).all()
def solve_all_single_value_cells(sudoku: sudoku_board.Sudoku) -> None: solved_value = True while solved_value: solved_value = False for row in range(9): for column in range(9): values = get_possible_cell_values(sudoku, row, column) if len(values) == 1 and sudoku.board_numbers[row][column] == 0: sudoku.add_cell(row, column, values[0]) solved_value = True
def test_find_empty_location(): board = Sudoku(boards.unsolved_easy) location = [0, 0] empty = sudoku_solver.find_empty_location(board.board_numbers, location) assert empty board = Sudoku(boards.solved_easy) location = [0, 0] empty = sudoku_solver.find_empty_location(board.board_numbers, location) assert not empty
def test_crosshatch_box(): sudoku_board = Sudoku(boards.unique_candidate_test) assert sudoku_board.board_numbers[7, 0] == 0 sudoku_solver.crosshatch_box(sudoku_board, 6) assert sudoku_board.board_numbers[7, 0] == 4 sudoku_board = Sudoku(boards.unsolved_hard) assert sudoku_board.board_numbers[5, 6] == 0 sudoku_solver.crosshatch_box(sudoku_board, 5) assert sudoku_board.board_numbers[5, 6] == 8
def test_solve_board(): board = Sudoku(boards.unsolved_easy) sudoku_solver.solve_board(board) assert sudoku_solver.verify_board(board) assert (board.board_numbers == boards.solved_easy).all() board = Sudoku(boards.unsolved_very_hard) sudoku_solver.solve_board(board) assert sudoku_solver.verify_board(board) assert (board.board_numbers == boards.solved_very_hard).all() board = Sudoku(boards.empty) sudoku_solver.solve_board(board) assert sudoku_solver.verify_board(board)
def test_used_in_box(): board = Sudoku(boards.unsolved_easy) used = sudoku_solver.used_in_box(board.board_numbers, 0, 0, 5) assert used used = sudoku_solver.used_in_box(board.board_numbers, 0, 0, 1) assert not used
def test_check_location_is_safe(): board = Sudoku(boards.unsolved_easy) safe = sudoku_solver.check_location_is_safe(board.board_numbers, 0, 0, 1) assert safe safe = sudoku_solver.check_location_is_safe(board.board_numbers, 0, 0, 5) assert not safe
def test_solve_naked_subset_column(): column = 0 row = 1 sudoku_board = Sudoku(boards.naked_pair_test) assert sudoku_board.board_numbers[row, column] == 0 sudoku_solver.solve_naked_subset_column(sudoku_board, column) assert sudoku_board.board_numbers[row, column] == 1
def reset_clicked(self): board = boards.empty for row in range(9): for column in range(9): self.text_boxes[row][column].delete(0, tk.END) self.sudoku = Sudoku(board, self)
def crosshatch_box(sudoku: sudoku_board.Sudoku, box_number: int) -> bool: all_values = [1, 2, 3, 4, 5, 6, 7, 8, 9] unused_values = [] box = sudoku.get_box_from_index(box_number) box_values = box.flatten() for value in all_values: if value not in box_values: unused_values.append(value) solved_value = False # Loop through each value that is not in the box. for value in unused_values: possible_rows = [] possible_columns = [] # Check each row that goes through the box. for row in get_rows_from_box_index(box_number): # If the value is not in that row then it could be in that row of the box. if not check_row_for_value(sudoku, row, value): possible_rows.append(row) # Check each column that goes through the box. for column in get_columns_from_box_index(box_number): # If the value is not in that column then it could be in that column of the box. if not check_column_for_value(sudoku, column, value): possible_columns.append(column) # Remove duplicates from possible rows and columns. possible_rows = list(set(possible_rows)) possible_columns = list(set(possible_columns)) # A cell position is only possible if the value is 0. Save the index if it is. possible_index = [] for row in possible_rows: for column in possible_columns: if sudoku.board_numbers[row, column] == 0: possible_index.append([row, column]) # If there is only one possible value for this cell then add it to the board. if len(possible_index) == 1: sudoku.add_cell(possible_index[0][0], possible_index[0][1], value) solved_value = True return solved_value
def test_get_possible_cell_values_with_unsolved_easy(): sudoku_board = Sudoku(boards.unsolved_easy) possible_values = sudoku_solver.get_possible_cell_values( sudoku_board, 0, 2) assert possible_values == [1, 2, 4] possible_values = sudoku_solver.get_possible_cell_values( sudoku_board, 0, 1) assert possible_values == [3] possible_values = sudoku_solver.get_possible_cell_values( sudoku_board, 8, 8) assert possible_values == [9]
def solve_naked_subset_row(sudoku: sudoku_board.Sudoku, row: int) -> None: cell_possible_values = [] for column in range(9): cell_possible_values.append( get_possible_cell_values(sudoku, row, column)) subset_indices_two = [] for cell in range(9): if len(cell_possible_values[cell]) == 2: subset_indices_two.append(cell) values_two = [] if len(subset_indices_two) == 2: values_two = cell_possible_values[subset_indices_two[0]] for index in range(9): if index not in subset_indices_two: cell_possible_values[index] = ( list(set(cell_possible_values[index]) - set(values_two))) for column in range(9): if len(cell_possible_values[column]) == 1: sudoku.add_cell(row, column, cell_possible_values[column][0])
def solve_board(sudoku: sudoku_board.Sudoku): iterations = 0 while not verify_board(sudoku): iterations += 1 solve_all_single_value_cells(sudoku) solve_all_crosshatch_boxes(sudoku) solve_all_naked_subsets(sudoku) if iterations == 5: if sudoku.gui is None: print("Could not find a solution after {0} iterations.".format( iterations)) print("Have solved the board to the following point...") sudoku.print_board() print("Will now try to brute force solve the board...") brute_force_solve_sudoku(sudoku) if verify_board(sudoku): if sudoku.gui is None: print( "Have completed the board with the following solution, after {0} iterations..." .format(iterations)) sudoku.print_board() return
def solve_clicked(self): board = boards.empty self.solve_button.config(state='disabled') self.reset_button.config(state='disabled') for row in range(9): for column in range(9): current_box = self.text_boxes[row][column] if str.isdigit(current_box.get()) and len(current_box.get()) == 1: current_box.config(state='disabled') board[row][column] = int(current_box.get()) self.sudoku = Sudoku(board, self) try: sudoku_solver.solve_board(self.sudoku) self.reset_button.config(state='enabled') self.close_button.config(state='enabled') except tk.TclError: pass
def get_possible_cell_values(sudoku: sudoku_board.Sudoku, row: int, column: int) -> List[int]: all_values = [1, 2, 3, 4, 5, 6, 7, 8, 9] possible_values = [] if sudoku.board_numbers[row][column] != 0: possible_values.append(sudoku.board_numbers[row][column]) else: row_values = sudoku.board_numbers[row, :].flatten() column_values = sudoku.board_numbers[:, column].flatten() box_values = sudoku.get_box_from_cell(row, column).flatten() used_values = unique( list(filter(non_zero, chain(row_values, column_values, box_values)))) for value in all_values: if value not in used_values: possible_values.append(value) return possible_values
def test_get_box_from_cell_with_valid(): sudoku_board = Sudoku(boards.unsolved_easy) box1 = sudoku_board.get_box_from_cell(0, 0) expected_box1 = array([[5, 3, 0], [6, 0, 0], [0, 9, 8]]) assert (box1 == expected_box1).all() box2 = sudoku_board.get_box_from_cell(1, 3) expected_box2 = array([[0, 7, 0], [1, 9, 5], [0, 0, 0]]) assert (box2 == expected_box2).all() box6 = sudoku_board.get_box_from_cell(3, 8) expected_box6 = array([[0, 0, 3], [0, 0, 1], [0, 0, 6]]) assert (box6 == expected_box6).all() box7 = sudoku_board.get_box_from_cell(8, 2) expected_box7 = array([[0, 6, 0], [0, 0, 0], [0, 0, 0]]) assert (box7 == expected_box7).all() box9 = sudoku_board.get_box_from_cell(6, 6) expected_box9 = array([[2, 8, 0], [0, 0, 5], [0, 7, 9]]) assert (box9 == expected_box9).all()
def test_solve_all_single_value_cells_with_unsolved_easy(): sudoku_board = Sudoku(boards.unsolved_easy) sudoku_solver.solve_all_single_value_cells(sudoku_board) solved_board = sudoku_board.board_numbers expected_board = boards.solved_easy assert (array(expected_board) == solved_board).all()
def test_verify_row_with_valid_rows(): sudoku_board = Sudoku(boards.solved) for i in range(9): assert sudoku_solver.verify_row(sudoku_board, i)
def test_verify_board_with_invalid(): sudoku_board = Sudoku(boards.invalid) assert not sudoku_solver.verify_board(sudoku_board)
def test_verify_board_with_valid(): sudoku_board = Sudoku(boards.solved) assert sudoku_solver.verify_board(sudoku_board)
def test_solve_all_naked_subsets(): sudoku_board = Sudoku(boards.naked_pair_test) sudoku_solver.solve_all_naked_subsets(sudoku_board) assert sudoku_board.board_numbers[0, 0] == 0
def test_check_column_for_value(): sudoku = Sudoku(boards.unique_candidate_test) assert sudoku_solver.check_column_for_value(sudoku, 2, 4) assert not sudoku_solver.check_column_for_value(sudoku, 2, 5)
parser.add_argument( "-f", "--fast", dest="solve_fast", help="Solve quickly without displaying values being added in.", default=False, action="store_true") args = parser.parse_args() if args.use_gui: gui = sudoku_gui.Gui() gui.solve_fast = args.solve_fast gui.window.mainloop() else: print("Welcome to Soduku solver...") correct = "no" sudoku = None while correct.strip() != "yes": print( "Please enter the Soduku board in rows of 9, separating numbers with a space." ) print("Enter 0 for any number that is unknown.") board = read_board_in() sudoku = Sudoku(board) print("You entered the following board...") sudoku.print_board() print("Is that correct? (yes/no)") correct = input() sudoku_solver.solve_board(sudoku)
def test_verify_column_with_invalid_columns(): sudoku_board = Sudoku(boards.invalid) for i in range(9): assert not sudoku_solver.verify_column(sudoku_board, i)
def test_verify_box_with_invalid_boxes(): sudoku_board = Sudoku(boards.invalid) for i in range(9): assert not sudoku_solver.verify_box(sudoku_board, i)
def test_solve_all_crosshatch_boxes(): sudoku_board = Sudoku(boards.unique_candidate_test) assert sudoku_board.board_numbers[7, 0] == 0 sudoku_solver.solve_all_crosshatch_boxes(sudoku_board) assert sudoku_board.board_numbers[7, 0] == 4