def solve(board: Board) -> bool: """Main solver. Initially this just invokes constraint propagation. In phase 2 of the project, you will add recursive back-tracking search (guess-and-check with recursion). """ log.debug("Called solve on board:\n{}".format(board)) propogate(board) if board.is_solved: return True if not board.is_consistent: return False fewest_candidates = 10 best_tile = None for tile in board.tiles: for i in tile: if (i.value == sdk_tile.UNKNOWN) & (len(i.candidates) < fewest_candidates): best_tile = i fewest_candidates = len(i.candidates) board_saved = board.as_list() for choice in best_tile.candidates: # Save Gamestate # Apply the step to partial solution best_tile.set_value(choice) if solve(board): return True else: board.set_tiles(board_saved) return False
def read(f: Union[IOBase, str], board: sdk_board.Board = None) -> sdk_board.Board: """Read a Sudoku board from a file. Pass in a path or an already opened file. Optionally pass in a board to be filled. """ if isinstance(f, str): log.debug("Reading from string") f = open(f, "r") else: log.debug(f"Reading from file {f}") if board is None: board = sdk_board.Board() values = [] for row in f: row = row.strip() log.debug(f"Reading row |{row}|") values.append(row) if len(row) != NROWS: raise InputError("Puzzle row wrong length: {}" .format(row)) log.debug(f"Read values: {values}") if len(values) != NROWS: raise InputError("Wrong number of rows in {}" .format(values)) board.set_tiles(values) f.close() return board
def solve(board: Board) -> bool: """Main solver. Initially this just invokes constraint propagation. In phase 2 of the project, you will add recursive back-tracking search (guess-and-check with recursion). """ log.debug("Called solve on board:\n{}".format(board)) propagate(board) if board.is_solved(): return True if not board.is_consistent(): return False min_cands = 10 best_tile = None save = board.as_list() for row in board.tiles: for tile in row: if tile.value == sdk_tile.UNKNOWN and len(tile.candidates) < min_cands: best_tile = tile min_cands = len(tile.candidates) for cands in best_tile.candidates: best_tile.set_value(cands) if solve(board): return True else: board.set_tiles(save) return False
def propagate(board: Board): """Propagate constraints until we either solve the puzzle, show the puzzle as given is unsolvable, or can make no more progress by constraint propagation. """ logging.info("Propagating constraints") changed = True while changed: logging.info("Invoking naked single") changed = naked_single(board) if board.is_solved() or not board.is_consistent(): return changed = hidden_single(board) or changed if board.is_solved() or not board.is_consistent(): return return
def solve(board: Board) -> bool: """Main solver. Initially this just invokes constraint propagation. In part 2 of the project, you will add recursive back-tracking search (guess-and-check with recursion). A complete solution for solve for part 2 will - find the the best tile to guess values for - guess each possible value for that tile - if a guess is wrong, reset the board - return True if the board is solved, false otherwise """ log.debug("Called solve on board:\n{}".format(board)) propagate(board) if board.is_solved(): return True if not board.is_consistent(): return False log.info("Invoking back-track search") # There must be at least one tile with value UNKNOWN # and multiple candidate values. Choose one with # fewest candidates. min_candidates = len(sdk_tile.CHOICES) + 1 best_tile = None for row in board.tiles: for tile in row: if (tile.value == sdk_tile.UNKNOWN) and (len(tile.candidates) < min_candidates): min_candidates = len(tile.candidates) best_tile = tile assert not ( best_tile is None ) # best_tile should never be None. If it is, we've made a mistake log.info("Guess-and-check on tile[{}][{}]".format(best_tile.row, best_tile.col)) saved = board.as_list() for guess in best_tile.candidates: best_tile.set_value(guess) log.info("Guessing {}".format(guess)) if solve(board): return True # That guess didn't work. Restore old board and try again board.set_tiles(saved) return False