def solve(self) -> bool: """Solve the sudoku board recursively using a backtracking algorithm and shows the steps visually.""" coordinates = solver.find_empty(self.puzzle) if not coordinates: return True row, col = coordinates grid_x = col // 3 grid_y = row // 3 square_size = (55, 55) for i in range(1, 10): self.clock.tick(self.speed) self.display.fill(DARK_BLUE) self.draw_empty_board() self.draw_numbers(DARK_BLUE) self.draw_buttons() for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() sys.exit() if event.type == pygame.MOUSEBUTTONDOWN: mouse_pos = pygame.mouse.get_pos() if 688 < mouse_pos[0] < 1019 and 550 < mouse_pos[1] < 605: self.new_speed() if 730 < mouse_pos[0] < 970 and 340 < mouse_pos[1] < 410: self.quick_solve(use_copy=True) return True if solver.is_valid(self.puzzle, row, col, i): self.puzzle[row][col] = i self.draw_empty_board() pygame.draw.rect(self.display, self.GREEN, ((45 + (col * 60) + (grid_x * 5), 120 + (row * 60) + (grid_y * 5)), square_size)) self.draw_numbers(self.GREEN) self.draw_numbers(self.DARK_BLUE, use_copy=True) pygame.display.update() if self.solve(): return True self.puzzle[row][col] = 0 self.draw_empty_board() pygame.draw.rect(self.display, self.RED, ((45 + (col * 60) + (grid_x * 5), 120 + (row * 60) + (grid_y * 5)), square_size)) self.draw_numbers(self.GREEN) self.draw_numbers(self.DARK_BLUE, use_copy=True) pygame.display.update() return False
def place(self, val): row, col = self.selected self.cells[row][col].set_val(val) # Check if placement is valid to keep track for completion if solver.is_valid(self.state, col, row, val): self.cells[row][col].valid = True else: self.cells[row][col].valid = False self.update_state()
def update_board(self, position, number): if number == -1 or position == None: return False row, column = map(lambda x: (x - self.PADDING) // self.CELL_SIZE, position) if row < 0 or row > 8 or column < 0 or column > 8: return False if self.INITIAL_BOARD[row][column] == 0: if is_valid(self.board, (row, column), number) or number == 0: self.board[row][column] = number self.invalid_cell = None else: self.invalid_cell = position
def place(self, val): row, col = self.selected if self.cubes[row][col].value == 0: self.cubes[row][col].set(val) self.update_model() if is_valid(self.model, val, (row, col)) and solve_board(self.model): return True else: self.cubes[row][col].set(0) self.cubes[row][col].set_temp(0) self.update_model() return False
def main(): for team_id, task_id in enumerate(task_ids): print("Generating task for team" , team_id) flag = flags[team_id] task_folder = os.path.join(PACKAGE_PATH, task_id) os.makedirs(task_folder, exist_ok=True) task_file = os.path.join(task_folder, "scanner") os.system("python3 {} {} {}".format(GENERATOR, flag, task_file)) assert is_valid(task_file, flag)
def place_num(self, value): """Check validation of a number to place in the board""" row, col = self.selected if self.cubes[row][col].value == 0: self.cubes[row][col].set_value(value) self.update_model() if is_valid(self.model, value, (row, col)) and solve(self.model): return True elif is_valid_sudoku(self.model): return True else: self.cubes[row][col].set_value(0) self.cubes[row][col].set_temporary(0) self.update_model() return False
def solve_gui(board): """ Solves the given sudoku board using backtracking algorithm. Added graphic capabilities on top of solver.solve method. """ # Find empty space. If none exists, return true. empty = solver.find_zero(board) if not empty: return True row, col = empty # Try all possible values in the coordinate of empty space. # If value is valid, recurse the 'solve' method. # Otherwise, reset value to 0 and try another value to recurse. for i in range(1, 10): if solver.is_valid(row, col, i, board): board[row][col] = i delay_draw(col, row, i, black, 90) if solve_gui(board): return True board[row][col] = 0 delay_draw(col, row, i, blue, 90) return False
def test_valid(self): self.assertTrue(is_valid(self.valid))
def test_same_diag(self): self.assertFalse(is_valid(self.invalid_diag))
def test_same_col(self): self.assertFalse(is_valid(self.invalid_row))
def __init__(self, img_path=None, img=None, max_size=1000, predict_threshold=0.5, overlay=True): """ Initializes the Sudoku grid from an image. Args: img_path (str): path to the image of sudoku img (numpy.ndarray): loaded image instead of img_path max_size (int): maximum allowed size of image predict_threshold (float): minimun threshold to predict digit overlay (bool): generate the output image """ if img_path is None and img is None: raise ValueError("img_path and img both cannot be None.") if img_path is not None and img is not None: raise ValueError("Only one of img_path and img can be used.") if img_path: if not os.path.exists(img_path): raise FileNotFoundError(img_path) img = cv2.imread(img_path, cv2.IMREAD_COLOR) self.status = 'processing' self.original = ip_utils.scale_down(img, max_size) self.solved_image = None self.grayscale = cv2.cvtColor(self.original.copy(), cv2.COLOR_BGR2GRAY) # crop and warp to get the sudoku grid self.cropped, self.crop_matrix, self.crop_corners = ip_utils.crop_grid( self.grayscale) self.cropped_color = None # divide the grid into cells self.grid_cells = ip_utils.divide_into_cells(self.cropped) # extract the digits from the cells self.digits = ip_utils.get_digits(self.cropped, self.grid_cells, size=28) # convert the images of digits to digits using digit recognition model self.board = [[0] * 9 for _ in range(9)] self.solved_board = None pred_indices = [] pred_digits = [] for i, digit in enumerate(self.digits): # if digit is present in cell if cv2.countNonZero(digit) > 0: pred_digits.append(digit) pred_indices.append((i // 9, i % 9)) predicted = predict_digits(np.array(pred_digits), threshold=predict_threshold) for (i, j), d in zip(pred_indices, predicted): self.board[i][j] = d # check if recognized sudoku puzzle is valid if not solver.is_valid(self.board): self.status = 'Unable to detect a valid Sudoku puzzle.' return self.solved_board = copy.deepcopy(self.board) if solver.solve(self.solved_board, validate=False): if overlay: self.solved_image = self.digits_overlay() self.status = 'solved' else: self.status = 'Either the sudoku is invalid or image is blurry.'
def run(board): init_board() set_board(board) orig_board = copy.deepcopy(board) selected = False valued = False prev_val = -1 while True: for event in pygame.event.get(): # Game exit. if event.type == pygame.QUIT: pygame.quit() exit() # Key pressed. elif event.type == pygame.KEYDOWN: # Pressed space bar which solves the sudoku game using backtracking. if event.key == pygame.K_SPACE: if selected: reset_selected_box(valued, prev_pos, prev_val) selected = False valued = False solve_gui(board) # Pressed number character with box selected. elif event.key in [ pygame.K_1, pygame.K_2, pygame.K_3, pygame.K_4, pygame.K_5, pygame.K_6, pygame.K_7, pygame.K_8, pygame.K_9 ]: if selected: reset_selected_box(valued, prev_pos, prev_val) valued = True delay_draw(prev_pos[0] // 55, prev_pos[1] // 55, event.unicode, grey, 0) prev_val = event.unicode # Pressing enter with a box filled in with value. # Value is either valid or invalid. elif event.key == pygame.K_RETURN: # Enter only works if there is a box selected and valued. if selected & valued: value = int(prev_val) col, row = prev_pos[0] // 55, prev_pos[1] // 55 # Input is valid. if solver.is_valid(row, col, value, board): board[row][col] = value delay_draw(col, row, value, green, 200) delay_draw(col, row, value, black, 0) # Input is invalid. else: delay_draw(col, row, value, red, 200) delay_draw(col, row, value, blue, 0) selected = False valued = False prev_val = -1 # Mouse selection. elif event.type == pygame.MOUSEBUTTONDOWN: x, y = pygame.mouse.get_pos() col, row = x // 55, y // 55 # Selected box can be changed. if empty_pos(x, y, orig_board): if board[row][col] != 0: delay_draw(col, row, board[row][col], blue, 0) board[row][col] = 0 if selected: reset_selected_box(valued, prev_pos, prev_val) valued = False delay_draw(col, row, "?", grey, 0) selected = True prev_pos = (x, y)