Blake Rayvid - https://github.com/brayvid
Make the most of the ingredients you have in The Elder Scrolls V: Skyrim. Maximize total magnitude (essentially in-game value) with integer linear programming in scipy.
import numpy as np
import pandas as pd
from scipy.optimize import milp, Bounds, LinearConstraintUses local files "ingredients_have.csv" and "recipes_can_make.csv"
I made my CSVs using this helpful spreadsheet: https://docs.google.com/spreadsheets/d/1010C6ltqv7apuBoNYuFIFSBZER4YI03Y54kIsoKs5RI/edit?usp=sharing
# Ingredients we have with quantity on hand
ingredients = pd.read_csv('ingredients_have.csv');ingredients| Ingredient | Quantity | |
|---|---|---|
| 0 | Blisterwort | 4 |
| 1 | Blue Butterfly Wing | 4 |
| 2 | Blue Dartwing | 1 |
| 3 | Blue Mountain Flower | 24 |
| 4 | Bone Meal | 5 |
| 5 | Butterfly Wing | 6 |
| 6 | Canis Root | 2 |
| 7 | Creep Cluster | 1 |
| 8 | Deathbell | 6 |
| 9 | Dragons Tongue | 5 |
| 10 | Ectoplasm | 5 |
| 11 | Elves Ear | 10 |
| 12 | Fire Salts | 1 |
| 13 | Fly Amanita | 1 |
| 14 | Frost Mirriam | 3 |
| 15 | Garlic | 7 |
| 16 | Giant Lichen | 2 |
| 17 | Glow Dust | 2 |
| 18 | Hagraven Feathers | 2 |
| 19 | Histcarp | 2 |
| 20 | Honeycomb | 4 |
| 21 | Ice Wraith Teeth | 2 |
| 22 | Imp Stool | 2 |
| 23 | Lavender | 17 |
| 24 | Luna Moth Wing | 4 |
| 25 | Mora Tapinella | 2 |
| 26 | Mudcrab Chitin | 2 |
| 27 | Nightshade | 7 |
| 28 | Nirnroot | 3 |
| 29 | Nordic Barnacle | 2 |
| 30 | Orange Dartwing | 2 |
| 31 | Purple Mountain Flower | 15 |
| 32 | Red Mountain Flower | 1 |
| 33 | River Betty | 2 |
| 34 | Rock Warbler Egg | 3 |
| 35 | Salt Pile | 14 |
| 36 | Scaly Pholiota | 1 |
| 37 | Skeever Tail | 2 |
| 38 | Slaughterfish Scales | 3 |
| 39 | Snowberries | 6 |
| 40 | Spider Egg | 8 |
| 41 | Spriggan Sap | 3 |
| 42 | Swamp Fungal Pod | 2 |
| 43 | Taproot | 2 |
| 44 | Thistle Branch | 2 |
| 45 | Torchbug Thorax | 3 |
| 46 | Troll Fat | 3 |
| 47 | Tundra Cotton | 7 |
| 48 | Vampire Dust | 1 |
| 49 | Void Salts | 1 |
| 50 | White Cap | 6 |
# Potions list with magnitude and ingredient names (1,2 + optional 3rd)
recipes = pd.read_csv('recipes_can_make.csv')
recipes = recipes[recipes['Magnitude'] > 0]
recipes[['Magnitude','Ingredient 1','Ingredient 2','Ingredient 3','MyPotionID']].head(50)| Magnitude | Ingredient 1 | Ingredient 2 | Ingredient 3 | MyPotionID | |
|---|---|---|---|---|---|
| 0 | 159 | Blue Dartwing | Blue Mountain Flower | Glow Dust | 3028 |
| 1 | 156 | Blue Dartwing | Blue Mountain Flower | Nightshade | 3037 |
| 2 | 156 | Blue Dartwing | Blue Mountain Flower | Spider Egg | 3045 |
| 3 | 156 | Blue Dartwing | Blue Mountain Flower | Spriggan Sap | 3046 |
| 4 | 113 | Blisterwort | Blue Butterfly Wing | Blue Mountain Flower | 2130 |
| 5 | 113 | Blue Butterfly Wing | Blue Mountain Flower | Rock Warbler Egg | 2680 |
| 6 | 112 | Frost Mirriam | Histcarp | Purple Mountain Flower | 10371 |
| 7 | 110 | Blue Butterfly Wing | Blue Mountain Flower | Butterfly Wing | 2666 |
| 8 | 110 | Blue Butterfly Wing | Blue Mountain Flower | Imp Stool | 2677 |
| 9 | 110 | Blue Butterfly Wing | Blue Mountain Flower | Swamp Fungal Pod | 2684 |
| 10 | 110 | Glow Dust | Nightshade | River Betty | 11628 |
| 11 | 109 | Blisterwort | Blue Mountain Flower | Spriggan Sap | 2210 |
| 12 | 109 | Blue Butterfly Wing | Bone Meal | Spriggan Sap | 2703 |
| 13 | 109 | Butterfly Wing | Glow Dust | Nightshade | 4738 |
| 14 | 109 | Creep Cluster | Ectoplasm | Histcarp | 6302 |
| 15 | 109 | Creep Cluster | Histcarp | Red Mountain Flower | 6550 |
| 16 | 109 | Creep Cluster | River Betty | Skeever Tail | 6725 |
| 17 | 109 | Nightshade | River Betty | Spriggan Sap | 14461 |
| 18 | 108 | Blisterwort | Blue Butterfly Wing | Spriggan Sap | 2154 |
| 19 | 108 | Blisterwort | Blue Mountain Flower | Spider Egg | 2209 |
| 20 | 108 | Blue Butterfly Wing | Blue Mountain Flower | Bone Meal | 2665 |
| 21 | 108 | Blue Butterfly Wing | Blue Mountain Flower | Canis Root | 2667 |
| 22 | 108 | Blue Butterfly Wing | Blue Mountain Flower | Nirnroot | 2679 |
| 23 | 108 | Blue Butterfly Wing | Blue Mountain Flower | Spider Egg | 2682 |
| 24 | 108 | Blue Butterfly Wing | Bone Meal | Glow Dust | 2693 |
| 25 | 108 | Blue Butterfly Wing | Bone Meal | Nightshade | 2700 |
| 26 | 108 | Blue Butterfly Wing | Bone Meal | Spider Egg | 2702 |
| 27 | 108 | Blue Butterfly Wing | Glow Dust | Hagraven Feathers | 2860 |
| 28 | 108 | Blue Butterfly Wing | Hagraven Feathers | Spider Egg | 2908 |
| 29 | 108 | Blue Butterfly Wing | Lavender | Spider Egg | 2969 |
| 30 | 108 | Blue Dartwing | Glow Dust | Nightshade | 3203 |
| 31 | 108 | Blue Mountain Flower | Bone Meal | Spider Egg | 3416 |
| 32 | 108 | Blue Mountain Flower | Butterfly Wing | Glow Dust | 3429 |
| 33 | 108 | Blue Mountain Flower | Glow Dust | Hagraven Feathers | 3628 |
| 34 | 108 | Blue Mountain Flower | Glow Dust | Swamp Fungal Pod | 3643 |
| 35 | 108 | Blue Mountain Flower | Rock Warbler Egg | Spider Egg | 3780 |
| 36 | 108 | Glow Dust | Hagraven Feathers | Nightshade | 11513 |
| 37 | 108 | Glow Dust | Luna Moth Wing | Nightshade | 11598 |
| 38 | 108 | Glow Dust | Nightshade | Nordic Barnacle | 11624 |
| 39 | 108 | Glow Dust | Nightshade | Snowberries | 11631 |
| 40 | 108 | Glow Dust | Nightshade | Swamp Fungal Pod | 11632 |
| 41 | 107 | Blisterwort | Spider Egg | Spriggan Sap | 2648 |
| 42 | 107 | Blue Butterfly Wing | Canis Root | Spider Egg | 2724 |
| 43 | 107 | Blue Mountain Flower | Imp Stool | Nightshade | 3716 |
| 44 | 107 | Canis Root | Spider Egg | Spriggan Sap | 5163 |
| 45 | 107 | Creep Cluster | Ectoplasm | Skeever Tail | 6318 |
| 46 | 107 | Creep Cluster | Frost Mirriam | Purple Mountain Flower | 6392 |
| 47 | 107 | Creep Cluster | Frost Mirriam | Red Mountain Flower | 6393 |
| 48 | 107 | Creep Cluster | Frost Mirriam | Taproot | 6402 |
| 49 | 107 | Creep Cluster | Frost Mirriam | White Cap | 6406 |
One row for each ingredient, one column for each potion. "1" indicates the ingredient is used in the potion.
# Boolean matrix A says what ingredients are in what recipes
A = pd.DataFrame(0, index=range(len(ingredients)),columns=range(len(recipes)))
for i in range(len(recipes)):
if ingredients.iloc[ingredients["Ingredient"].str.find(recipes.loc[i, "Ingredient 1"]).idxmax()]["Quantity"] > 0:
A.iloc[ingredients["Ingredient"].str.find(recipes.loc[i, "Ingredient 1"]).idxmax(), i] = 1
if ingredients.iloc[ingredients["Ingredient"].str.find(recipes.loc[i, "Ingredient 2"]).idxmax()]["Quantity"] > 0:
A.iloc[ingredients["Ingredient"].str.find(recipes.loc[i, "Ingredient 2"]).idxmax(), i] = 1
if not pd.isnull(recipes.loc[i, "Ingredient 3"]):
if ingredients.iloc[ingredients["Ingredient"].str.find(recipes.loc[i, "Ingredient 3"]).idxmax()]["Quantity"] > 0:
A.iloc[ingredients["Ingredient"].str.find(recipes.loc[i, "Ingredient 3"]).idxmax(), i] = 1
A| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | ... | 2375 | 2376 | 2377 | 2378 | 2379 | 2380 | 2381 | 2382 | 2383 | 2384 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 1 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | 1 | 1 | 1 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 2 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 3 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 4 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 5 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 6 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 7 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 8 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 9 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 10 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 11 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 12 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 13 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 14 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 15 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 16 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 17 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 18 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 19 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 20 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 21 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 |
| 22 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 23 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 1 | 1 | 1 | 0 | 0 | 0 | 1 | 0 | 0 |
| 24 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 |
| 25 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 26 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 27 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 28 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 1 | 0 | 0 | 1 | 0 | 1 | 1 | 1 | 1 |
| 29 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 30 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 31 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 32 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 33 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 34 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 35 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 36 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 37 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 38 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 39 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 |
| 40 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 41 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 |
| 42 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 43 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 44 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 45 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 46 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 47 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 1 |
| 48 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 |
| 49 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
| 50 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
51 rows × 2385 columns
find x to minimize f.x with Ax <= b, x >= lb
f = -1 * magnitude, b = qty of each ingredient on hand
# Objective function f.x to minimize
f = np.array(-1 * recipes['Magnitude'],dtype=int); # f = -1*value so that minimizing f.x maximizes total value# Bounds
b_max = np.array(ingredients['Quantity'],dtype=int) # Cannot use more than we have on hand
x_lb = np.zeros(shape=len(recipes)) # Cannot use less than 0# milp parameters
bounds = Bounds(lb=x_lb)
constraint = LinearConstraint(A, ub=b_max)
integrality = np.ones(shape=len(recipes),dtype=int) # All x should be integers# Perform optimization
res = milp(c=f, integrality=integrality, bounds=bounds, constraints=constraint)# Display the potions we should make to maximize magnitude where the last column is quantity to make
total_magnitude = int(-res.fun)
num_potions = int(sum(res.x))
indices_to_make = np.nonzero(res.x > 0)
to_make_df = recipes.iloc[indices_to_make].copy()
to_make_df.loc[:,'QtyToMake'] = res.x[indices_to_make].astype(int)
to_make_df[['Magnitude','Type','Ingredient 1','Ingredient 2','Ingredient 3','MyPotionID','QtyToMake']].head(50)| Magnitude | Type | Ingredient 1 | Ingredient 2 | Ingredient 3 | MyPotionID | QtyToMake | |
|---|---|---|---|---|---|---|---|
| 6 | 112 | Mixed | Frost Mirriam | Histcarp | Purple Mountain Flower | 10371 | 2 |
| 7 | 110 | Mixed | Blue Butterfly Wing | Blue Mountain Flower | Butterfly Wing | 2666 | 4 |
| 11 | 109 | Mixed | Blisterwort | Blue Mountain Flower | Spriggan Sap | 2210 | 3 |
| 19 | 108 | Mixed | Blisterwort | Blue Mountain Flower | Spider Egg | 2209 | 1 |
| 31 | 108 | Mixed | Blue Mountain Flower | Bone Meal | Spider Egg | 3416 | 4 |
| 33 | 108 | Mixed | Blue Mountain Flower | Glow Dust | Hagraven Feathers | 3628 | 1 |
| 34 | 108 | Mixed | Blue Mountain Flower | Glow Dust | Swamp Fungal Pod | 3643 | 1 |
| 35 | 108 | Mixed | Blue Mountain Flower | Rock Warbler Egg | Spider Egg | 3780 | 1 |
| 45 | 107 | Mixed | Creep Cluster | Ectoplasm | Skeever Tail | 6318 | 1 |
| 57 | 107 | Mixed | Frost Mirriam | Purple Mountain Flower | Skeever Tail | 10519 | 1 |
| 103 | 105 | Mixed | Blue Mountain Flower | Lavender | Nightshade | 3749 | 7 |
| 104 | 105 | Mixed | Blue Mountain Flower | Lavender | Spider Egg | 3755 | 2 |
| 303 | 59 | Potion | Blue Dartwing | Swamp Fungal Pod | NaN | 3388 | 1 |
| 334 | 57 | Mixed | Deathbell | Salt Pile | Taproot | 8001 | 1 |
| 361 | 55 | Poison | River Betty | Salt Pile | Troll Fat | 15006 | 2 |
| 367 | 53 | Poison | Deathbell | Nirnroot | Salt Pile | 7937 | 2 |
| 370 | 53 | Poison | Deathbell | Salt Pile | Troll Fat | 8004 | 1 |
| 379 | 50 | Poison | Deathbell | Salt Pile | NaN | 8006 | 2 |
| 384 | 17 | Mixed | Elves Ear | Fire Salts | Salt Pile | 8930 | 1 |
| 391 | 16 | Potion | Dragons Tongue | Fly Amanita | Scaly Pholiota | 8094 | 1 |
| 397 | 15 | Potion | Garlic | Taproot | Vampire Dust | 11060 | 1 |
| 400 | 14 | Mixed | Ectoplasm | Giant Lichen | Void Salts | 8576 | 1 |
| 443 | 12 | Mixed | Canis Root | Imp Stool | Rock Warbler Egg | 5088 | 2 |
| 461 | 12 | Mixed | Luna Moth Wing | Nordic Barnacle | Orange Dartwing | 14094 | 1 |
| 465 | 12 | Potion | Dragons Tongue | Elves Ear | Mora Tapinella | 8058 | 2 |
| 468 | 12 | Potion | Honeycomb | Purple Mountain Flower | Slaughterfish Scales | 12905 | 1 |
| 469 | 12 | Potion | Mudcrab Chitin | Purple Mountain Flower | Thistle Branch | 14343 | 2 |
| 470 | 11 | Mixed | Ectoplasm | Red Mountain Flower | NaN | 8873 | 1 |
| 493 | 11 | Mixed | Dragons Tongue | Elves Ear | White Cap | 8067 | 2 |
| 516 | 11 | Mixed | Elves Ear | Snowberries | White Cap | 9130 | 2 |
| 582 | 10 | Mixed | Elves Ear | Ice Wraith Teeth | White Cap | 9037 | 2 |
| 658 | 9 | Mixed | Bone Meal | Lavender | Nirnroot | 4095 | 1 |
| 704 | 9 | Mixed | Purple Mountain Flower | Snowberries | Torchbug Thorax | 14933 | 3 |
| 760 | 9 | Potion | Ectoplasm | Elves Ear | Tundra Cotton | 8459 | 1 |
| 765 | 9 | Potion | Ectoplasm | Giant Lichen | Tundra Cotton | 8575 | 1 |
| 804 | 9 | Potion | Garlic | Lavender | Luna Moth Wing | 10953 | 1 |
| 806 | 9 | Potion | Garlic | Lavender | Salt Pile | 10961 | 5 |
| 855 | 9 | Potion | Honeycomb | Purple Mountain Flower | Tundra Cotton | 12911 | 3 |
| 879 | 8 | Mixed | Luna Moth Wing | Nordic Barnacle | NaN | 14098 | 1 |
| 1017 | 8 | Mixed | Hagraven Feathers | Lavender | Luna Moth Wing | 12101 | 1 |
| 1144 | 8 | Potion | Orange Dartwing | Purple Mountain Flower | Snowberries | 14612 | 1 |
| 1443 | 7 | Potion | Purple Mountain Flower | Slaughterfish Scales | Tundra Cotton | 14922 | 2 |
print(f"To maximize magnitude and therefore value, create {num_potions} potions of the {len(to_make_df)} unique types listed above for a total magnitude of {total_magnitude}.")To maximize magnitude and therefore value, create 76 potions of the 42 unique types listed above for a total magnitude of 3905.