def solve(puz: puzzle) -> None: """ Main code for solving a single puzzle. :param puz: :return: """ poss1 = [] poss2 = generatePossible(puz) # Makes sure all single and hidden solved while not puz.solved() and poss2 != poss1: poss1 = poss2 solveSingles(puz) solveHidden(puz) poss2 = generatePossible(puz) # If it is not solved move into brute forcing the solution if puz.validPuzzle(): if not puz.solved(): print('Some brute force required.') try: # Opens thread to run brute force on thread = threading.Thread(target=bruteForce, args=[puz], daemon=True) thread.start() count = 0 # Counts how much time has passed to make sure not to long has gone, and that the thread is still alive while not puz.solved() and count < 300 and thread.is_alive(): time.sleep(.5) count += 1 except: print('Threading error.') if not puz.solved(): print('Puzzle was unable to be solved.') else: print('Invalid puzzle')
def eliminateSingles(puz: puzzle) -> None: """ This function iterates through a puzzle and if only one value is possible for a cell it fills it with that value. :param puz: puzzle :return: """ poss = generatePossible(puz) for row in range(9): for col in range(9): if len(poss[row][col]) == 1: puz.setValue(row, col, poss[row][col][0]) poss = generatePossible(puz)
def findNextEmpty(puz: puzzle, i: int, j: int) -> tuple: """ This function goes through a puzzle and returns a tuple of the coordinates of the next empty cell, if none found then -1,-1 is returned. :param puz: puzzle :param i: int :param j: int :return: tuple """ for row in range(i, 9): for col in range(j, 9): if not puz.getValue(row, col): return row, col for row in range(9): for col in range(9): if not puz.getValue(row, col): return row, col return -1, -1
def bruteForce(puz: puzzle, row: int = 0, col: int = 0) -> bool: """ Systematically goes through the puzzle and back tracks on dead end until puzzle is solved. :param puz: puzzle :param col: int :param row: int :return: bool """ poss = generatePossible(puz) row, col = findNextEmpty(puz, row, col) if row == -1: return True for val in poss[row][col]: if puz.setValue(row, col, val): if bruteForce(puz, row, col): return True puz.clearValue(row, col) return False
def solveSingles(puz: puzzle) -> None: """ This function goes through and solves all the singles of a puzzle until none are left. :param puz: puzzle :return: """ poss1 = [] poss2 = generatePossible(puz) while not puz.solved() and poss2 != poss1: poss1 = poss2 eliminateSingles(puz) poss2 = generatePossible(puz)
def solveHidden(puz: puzzle) -> None: """ Loops through the puzzle until all the hidden have been solved. :param puz: puzzle :return: """ poss1 = [] poss2 = generatePossible(puz) while not puz.solved() and poss2 != poss1: poss1 = poss2 eliminateHidden(puz) poss2 = generatePossible(puz)
def generatePossible(puz: puzzle) -> list: """ This function takes in a puzzle and returns a 2D list of all the valid moves for a space. :param puz: puzzle :return: list """ poss = [] for row in range(9): poss.append([]) for col in range(9): poss[row].append([]) for val in range(1, 10): if not puz.checkSpot(row, col, val): poss[row][col].append(val) return poss
def eliminateHidden(puz: puzzle) -> None: """ This function takes in a puzzle and solves all the hidden singles one time. :param puz: puzzle :return: """ # Checking for hidden singles in rows for row in range(9): for val in range(1, 10): count = 0 poss = generatePossible(puz) for col in range(9): if val in poss[row][col]: count += 1 rowPoss = row colPoss = col if count == 1: puz.setValue(rowPoss, colPoss, val) # Checking for hidden singles in columns for col in range(9): for val in range(1, 10): count = 0 poss = generatePossible(puz) for row in range(9): if val in poss[row][col]: count += 1 rowPoss = row colPoss = col if count == 1: puz.setValue(rowPoss, colPoss, val) # Checking for hidden singles in blocks for block in range(9): for val in range(1, 10): count = 0 poss = generatePossible(puz) for row in range(3): for col in range(3): if val in poss[row + block // 3 * 3][col + block % 3 * 3]: count += 1 rowPoss = row colPoss = col if count == 1: puz.setValue(rowPoss, colPoss, val)