## Step 1: Define the symbolic representation of the problem
Let's denote the number of soft, smooth, and crunchy cake-pops as $x_1$, $x_2$, and $x_3$ respectively. The objective is to maximize the profit, which can be represented as $4x_1 + 6x_2 + 5x_3$. The constraints are based on the available batter and milk. The batter constraint is $20x_1 + 15x_2 + 12x_3 \leq 1000$, and the milk constraint is $10x_1 + 15x_2 + 18x_3 \leq 800$. Also, $x_1, x_2, x_3 \geq 0$ because the number of cake-pops cannot be negative.

## Step 2: Express the problem in the required symbolic format
The symbolic variables are: 
- $x_1$ for soft cake-pops
- $x_2$ for smooth cake-pops
- $x_3$ for crunchy cake-pops

The objective function is: $4x_1 + 6x_2 + 5x_3$

The constraints are:
- $20x_1 + 15x_2 + 12x_3 \leq 1000$ (batter constraint)
- $10x_1 + 15x_2 + 18x_3 \leq 800$ (milk constraint)
- $x_1 \geq 0$
- $x_2 \geq 0$
- $x_3 \geq 0$

In the required format:
```json
{
'sym_variables': [('x1', 'soft cake-pops'), ('x2', 'smooth cake-pops'), ('x3', 'crunchy cake-pops')],
'objective_function': '4*x1 + 6*x2 + 5*x3',
'constraints': [
    '20*x1 + 15*x2 + 12*x3 <= 1000',
    '10*x1 + 15*x2 + 18*x3 <= 800',
    'x1 >= 0',
    'x2 >= 0',
    'x3 >= 0'
]
}
```

## Step 3: Convert the problem into Gurobi code
Now, let's implement this problem using Gurobi in Python:

```python
import gurobi

def solve_bakery_problem():
    # Create a new model
    model = gurobi.Model()

    # Define the variables
    x1 = model.addVar(name="soft_cake_pops", lb=0, ub=gurobi.GRB.INFINITY)
    x2 = model.addVar(name="smooth_cake_pops", lb=0, ub=gurobi.GRB.INFINITY)
    x3 = model.addVar(name="crunchy_cake_pops", lb=0, ub=gurobi.GRB.INFINITY)

    # Define the objective function
    model.setObjective(4 * x1 + 6 * x2 + 5 * x3, gurobi.GRB.MAXIMIZE)

    # Add constraints
    model.addConstr(20 * x1 + 15 * x2 + 12 * x3 <= 1000, name="batter_constraint")
    model.addConstr(10 * x1 + 15 * x2 + 18 * x3 <= 800, name="milk_constraint")

    # Optimize the model
    model.optimize()

    # Print the results
    if model.status == gurobi.GRB.OPTIMAL:
        print("Optimal solution found.")
        print(f"Number of soft cake-pops: {x1.varValue}")
        print(f"Number of smooth cake-pops: {x2.varValue}")
        print(f"Number of crunchy cake-pops: {x3.varValue}")
        print(f"Max profit: {model.objVal}")
    else:
        print("No optimal solution found.")

solve_bakery_problem()
```