Skip to content

Blake Rayvid

Menu
  • About
  • Portfolio
Menu

Skyrim Alchemy Optimizer

Posted on June 10, 2024June 11, 2025 by Blake

This Jupyter notebook can be used to maximize alchemy profitability using the ingredients you have on hand in The Elder Scrolls V: Skyrim.

Open in Colab

It uses integer linear programming from scipy.optimize.milp to determine which potions to make, and in what quantities, to maximize total value. It needs a csv file of the ingredients you have with their quantities, and a csv file of all possible potions you could make with your ingredients. I used this helpful spreadsheet to create my csvs, which are available here as examples.

Skyrim Alchemy Optimizer

Python Setup

import numpy as np
import pandas as pd
from scipy.optimize import milp, Bounds, LinearConstraint

Read in Ingredients

ingredients = pd.read_csv('ingredients_have.csv')
IngredientQuantity
Blisterwort4
Blue Butterfly Wing4
Blue Dartwing1
Blue Mountain Flower24
Bone Meal5
… more ingredients

Read in Recipes

recipes = pd.read_csv('recipes_can_make.csv')
recipes = recipes[recipes['Magnitude'] > 0]
MagnitudeIngredient 1Ingredient 2Ingredient 3MyPotionID
159Blue DartwingBlue Mountain FlowerGlow Dust3028
156Blue DartwingBlue Mountain FlowerNightshade3037
156Blue DartwingBlue Mountain FlowerSpider Egg3045
156Blue DartwingBlue Mountain FlowerSpriggan Sap3046
113BlisterwortBlue Mountain FlowerBlue Butterfly Wing2130
… more recipes

Create recipe matrix A in Ax <= b

One row for each ingredient, one column for each potion. “1” indicates the ingredient is used in the potion.

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
012…238223832384
0000…000
1000…000
2111…000
3111…000
4000…000
5000…000
6000…000
7000…000
8000…000
9000…000
10000…000
… more rows

Optimization Setup

f = np.array(-1 * recipes['Magnitude'], dtype=int)
b_max = np.array(ingredients['Quantity'], dtype=int)
x_lb = np.zeros(shape=len(recipes))
bounds = Bounds(lb=x_lb)
constraint = LinearConstraint(A, ub=b_max)
integrality = np.ones(shape=len(recipes), dtype=int)

Perform Optimization

res = milp(c=f, integrality=integrality, bounds=bounds, constraints=constraint)

Display Recommended Potions

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['QtyToMake'] = res.x[indices_to_make].astype(int)
to_make_df[['Magnitude','Type','Ingredient 1','Ingredient 2','Ingredient 3','MyPotionID','QtyToMake']].head(50)
MagnitudeTypeIngredient 1Ingredient 2Ingredient 3MyPotionIDQtyToMake
6112MixedFrost MirriamHistcarpPurple Mountain Flower103712
7110MixedBlue Butterfly WingBlue Mountain FlowerButterfly Wing26664
11109MixedBlisterwortBlue Mountain FlowerSpriggan Sap22103
19108MixedBlisterwortBlue Mountain FlowerSpider Egg22091
31108MixedBlue Mountain FlowerBone MealSpider Egg34164
33108MixedBlue Mountain FlowerGlow DustHagraven Feathers36281
34108MixedBlue Mountain FlowerGlow DustSwamp Fungal Pod36431
35108MixedBlue Mountain FlowerRock Warbler EggSpider Egg37801
45107MixedCreep ClusterEctoplasmSkeever Tail63181
57107MixedFrost MirriamPurple Mountain FlowerSkeever Tail105191
… more recommendations
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.

View on GitHub


Tags:
NumPy, Pandas, Python, SciPy
Categories: Data Science, Exploration, Optimization

Post navigation

← RouteCat
Sentiment Classifier →

Categories

  • Data Science
  • Exploration
  • Finance
  • Health
  • Interactive
  • Optimization
  • Utilities

Tags

D3.js Desmos Flask Gemini API JavaScript JQuery MATLAB Matplotlib Netlify NetworkX NLTK NumPy P5.js Pandas PostgreSQL Python QuantConnect Railway Scikit-Learn SciPy SpaCy TensorFlow Vader YFinance

© 2025 Blake Rayvid. All rights reserved.