input: Write a function to solve a Sudoku puzzle given a 9x9 grid filled with numbers from 0 to 9 where 0 represents an empty cell. The function should solve the puzzle by filling all empty cells such that the final grid adheres to the rules of Sudoku. Return the solved board if a solution exists, otherwise print "No solution".
  
  def solve_sudoku(board): 
    
      # Utility function to find vacant cells 
      # Returns a boolean     
      def find_vacant_cell(board, l): 
          for row in range(9): 
              for col in range(9): 
                  if board[row][col] == 0: 
                      l[0] = row 
                      l[1] = col 
                      return True
          return False
    
      # Utility function to check if a number  
      # is not currently placed in a particular  
      # row, column and block 
      def is_safe(board, row, col, num): 
    
          # Check if 'num' is not already placed  
          # in current row, current column  
          # and current 3x3 box 
          for i in range(9): 
    
              # Check the rows
              if(board[row][i] == num): 
                  return False
    
              # Check the columns 
              if(board[i][col] == num): 
                  return False
    
              # Check the 3x3 boxes 
              if(board[(row//3)*3 + i//3][(col//3)*3 + i%3] == num): 
                  return False
    
          return True
    
      # Solve the sudoku 
      def solve(board): 
          # 'l' is a list variable that keeps 
          # the record of row and col in 
          # find_vacant_cell function 
          l = [0, 0] 
    
          # If there is no unassigned location, 
          # we are done 
          if (not find_vacant_cell(board, l)): 
              return True
    
          # Assigning the list values
          row = l[0] 
          col = l[1] 
    
          # consider digits 1 to 9 
          for num in range(1, 10): 
    
              # if it is a safe position
              if (is_safe(board, row, col, num)): 
    
                  # make tentative assignment 
                  board[row][col] = num 
    
                  # return if succcess
                  if (solve(board)): 
                      return True
    
                  # failure, unmake and try again 
                  board[row][col] = 0
    
          # trigger backtracking 
          return False
    
      #Driver Program 
      if (solve(board)): 
          for row in board: 
              print(row) 
      else: 
          print("No solution")
  
  assert solve_sudoku([
      [7,8,0,4,0,0,1,2,0],
      [6,0,0,0,7,5,0,0,9],
      [0,0,0,6,0,1,0,7,8],
      [0,0,7,0,4,0,2,6,0],
      [0,0,1,0,5,0,9,3,0],
      [9,0,4,0,6,0,0,0,5],
      [0,7,0,3,0,0,0,1,2],
      [1,2,0,0,0,7,4,0,0],
      [0,4,9,2,0,6,0,0,7]
  ]) is not None
  
  assert solve_sudoku([
      [5,3,0,0,7,0,0,0,0],
      [6,0,0,1,9,5,0,0,0],
      [0,9,8,0,0,0,0,6,0],
      [8,0,0,0,6,0,0,0,3],
      [4,0,0,8,0,3,0,0,1],
      [7,0,0,0,2,0,0,0,6],
      [0,6,0,0,0,0,2,8,0],
      [0,0,0,4,1,9,0,0,5],
      [0,0,0,0,8,0,0,7,9]
  ]) is not None
  
  assert solve_sudoku([
      [0,0,0,0,0,0,0,0,0],
      [0,0,0,0,0,0,0,0,0],
      [0,0,0,0,0,0,0,0,0],
      [0,0,0,0,0,0,0,0,0],
      [0,0,0,0,0,0,0,0,0],
      [0,0,0,0,0,0,0,0,0],
      [0,0,0,0,0,0,0,0,0],
      [0,0,0,0,0,0,0,0,0],
      [0,0,0,0,0,0,0,0,0]
  ]) is not None
  
  assert solve_sudoku([
      [1,2,3,0,0,0,0,0,0],
      [4,5,6,0,0,0,0,0,0],
      [7,8,9,0,0,0,0,0,0],
      [0,0,0,1,2,3,0,0,0],
      [0,0,0,4,5,6,0,0,0],
      [0,0,0,7,8,9,0,0,0],
      [0,0,0,0,0,0,1,2,3],
      [0,0,0,0,0,0,4,5,6],
      [0,0,0,0,0,0,7,8,9]
  ]) is not None
  
  assert solve_sudoku([
      [1,2,3,4,5,6,7,8,9],
      [4,5,6,7,8,9,1,2,3],
      [7,8,9,1,2,3,4,5,6],
      [2,3,4,5,6,7,8,9,1],
      [5,6,7,8,9,1,2,3,4],
      [8,9,1,2,3,4,5,6,7],
      [3,4,5,6,7,8,9,1,2],
      [6,7,8,9,1,2,3,4,5],
      [9,1,2,3,4,5,6,7,8]
  ]) is None
output: 
  def solve_sudoku(board): 
    
      def find_vacant_cell(board, l): 
          for row in range(9): 
              for col in range(9): 
                  if board[row][col] == 0: 
                      l[0] = row 
                      l[1] = col 
                      return True
          return False
    
      def is_safe(board, row, col, num): 
          for i in range(9): 
              if(board[row][i] == num): 
                  return False
              if(board[i][col] == num): 
                  return False
              if(board[(row//3)*3 + i//3][(col//3)*3 + i%3] == num): 
                  return False
          return True
    
      def solve(board): 
          l = [0, 0] 
          if (not find_vacant_cell(board, l)): 
              return True
          row = l[0] 
          col = l[1] 
          for num in range(1, 10): 
              if (is_safe(board, row, col, num)): 
                  board[row][col] = num 
                  if (solve(board)): 
                      return True
                  board[row][col] = 0
          return False
    
      if (solve(board)): 
          return board
      else: 
          return None
  