def naiive_backtrack(board_code): """Naiive backtracking algorithm used to find the solution to a board with missing clues. Parameters ---------- board_code : string Board code listed from top left to bottom right. Returns ------- string Board code for solved board. Raises ------ UnsolvableBoardException If the board does not have a solution. InvalidBoardException If the board code is invalid. """ board = util.code_to_board(board_code) if not util.board_is_valid(board): raise util.InvalidBoardException search_board = np.copy(board) if util.board_is_solved(board): return util.board_to_code(board) position = 0 step = 0 while True: step += 1 if position == 81: if not util.board_is_solved(search_board): raise util.InvalidBoardException # if we got here when solving there must have been an issue with the board code print(f'Solved board in {step} steps') return util.board_to_code(search_board) x = util.to_x(position) y = util.to_y(position) if board[x][y] != 0: position += 1 continue while search_board[x][y] <= 9: search_board[x][y] += 1 if util.position_is_valid(search_board, x, y): position += 1 break if search_board[x][y] == 10: search_board[x][y] = 0 position -= 1 if position < 0: raise util.UnsolvableBoardException while board[util.to_x(position)][util.to_y(position)] != 0: position -= 1 if position < 0: raise util.UnsolvableBoardException
def naiive_backtrack_count(board_code): """Naiive backtracking algorithm used to count solutions to a board with missing clues. Parameters ---------- board_code : string Board code listed from top left to bottom right. Returns ------- int The number of unique solutions the board has. """ board = util.code_to_board(board_code) search_board = np.copy(board) if util.board_is_solved(board): return 1 position = 0 step = 0 solutions = 0 while True: step += 1 if position == 81: if not util.board_is_solved(search_board): raise util.InvalidBoardException solutions += 1 position -= 1 while board[util.to_x(position)][util.to_y(position)] != 0: position -= 1 if position < 0: print(f'found {solutions} solutions in {step} steps') return solutions x = util.to_x(position) y = util.to_y(position) if board[x][y] != 0: position += 1 continue while search_board[x][y] <= 9: search_board[x][y] += 1 if util.position_is_valid(search_board, x, y): position += 1 break if search_board[x][y] == 10: search_board[x][y] = 0 position -= 1 if position < 0: print(f'found {solutions} solutions in {step} steps') return solutions while board[util.to_x(position)][util.to_y(position)] != 0: position -= 1 if position < 0: print(f'found {solutions} solutions in {step} steps') return solutions
def test_position_is_valid(): board = util.code_to_board(boards['81'][0]) board[0][0] = -1 assert not util.position_is_valid(board, 0, 0) board = util.code_to_board(boards['81'][0]) board[0][4] = board[0][0] assert not util.position_is_valid(board, 0, 0) assert not util.position_is_valid(board, 0, 4) board = util.code_to_board(boards['81'][0]) board[4][0] = board[0][0] assert not util.position_is_valid(board, 0, 0) assert not util.position_is_valid(board, 4, 0) board = util.code_to_board(boards['81'][0]) board[1][1] = board[0][0] assert not util.position_is_valid(board, 0, 0) assert not util.position_is_valid(board, 1, 1) board = util.code_to_board(boards['81'][0]) for i in range(9 * 9): x = util.to_x(i) y = util.to_y(i) assert util.position_is_valid(board, x, y)
def generate(filled_board): positions = [(util.to_x(i), util.to_y(i)) for i in range(81)] np.random.shuffle(positions) board = np.copy(filled_board) for x, y in tqdm(positions): temp = board[x][y] board[x][y] = 0 if dfs.test_unique(np.copy(board)): continue else: board[x][y] = temp return board
def test_code_to_board(): code = 3 with pytest.raises(util.InvalidBoardException) as err: util.code_to_board(code) assert f'Board code must be a string, got type {type(3)}' in str(err.value) code = '33' with pytest.raises(util.InvalidBoardException) as err: util.code_to_board(code) assert 'Board code must be 81 characters long' in str(err.value) code = 'a' * 81 with pytest.raises(util.InvalidBoardException) as err: util.code_to_board(code) assert 'Board code must only contain numbers 0 - 9' in str(err.value) code = '000304907830000000000600000050080000006250030000000806009000400000000001000023000' board = util.code_to_board(code) for i in range(9 * 9): x = util.to_x(i) y = util.to_y(i) # remember we transpose the board assert board[y][x] == int(code[i])
def test_coordinate_conversions(): for i in range(200): assert util.to_x(i) == i % 9 assert util.to_y(i) == i // 9