Minecraft Enchantment Calculator

Optimal anvil order calculator (Java 1.20+). Two optimization modes, full vanilla rules, sub-20ms plans.

Tech: Python, JavaScript, HTML, TailwindCSS

GitHubLive

I built this to answer a deceptively tricky question Minecraft players hit all the time: “What’s the cheapest way to put *this exact set* of enchantments on *this* item?” The app models Mojang’s anvil math (merge costs, level caps, incompatibilities, prior-work penalty, 39-level hard cap) and searches the space of possible book+item merge orders to find an optimal plan. No front-end frameworks—just one HTML file with vanilla JS and a tiny Flask backend. Typical plans compute in ≲20 ms thanks to heavy memoization.

There are two optimization modes: minimize **total levels** (XP you actually pay across the whole sequence) or minimize final **prior-work penalty** (useful when you care about keeping the item “cheap” for the next upgrade). In both modes, ties fall back to the other metric, giving stable, sensible plans.

# Quickstart
git clone https://github.com/BrianKYildirim/minecraft-enchantment-order.git
cd minecraft-enchantment-order
python -m venv .venv
# Windows
.venv\Scripts\activate.bat
# macOS/Linux
# source .venv/bin/activate
pip install -r requirements.txt
python run.py
# Open http://127.0.0.1:5000

All 1.20 items and enchantments are encoded with weights, level caps, and incompatibilities. The UI live-validates: incompatible picks are greyed out, impossible states can’t be selected, and you can enter an existing prior-work penalty (0–39) which is included in the optimization.

Under the hood, the core search is a recursive, memoized bipartition over the set (base + books). For each split, sub-solutions are merged, enforcing anvil rules in a single place (the model). Results are memoized by the multiset of inputs (as an immutable tuple), dramatically pruning identical subproblems.

# calculator.py (excerpt)
@lru_cache(maxsize=None)
def _cheapest_single(work_tuple: Tuple[EnchantedItem, ...]):
    if len(work_tuple) == 1:
        item = work_tuple[0]
        return {item.anvil_uses: ([], 0, 0, item)}  # steps, total_levels, total_xp, final

    best = {}
    for i in range(1, len(work_tuple)):
        for left_idx in itertools.combinations(range(len(work_tuple)), i):
            right_idx = tuple(sorted(set(range(len(work_tuple))) - set(left_idx)))
            lefts  = tuple(work_tuple[j] for j in left_idx)
            rights = tuple(work_tuple[j] for j in right_idx)

            for lw, (lsteps, llv, lxp, litem) in _cheapest_single(lefts).items():
                for rw, (rsteps, rlv, rxp, ritem) in _cheapest_single(rights).items():
                    try:
                        merged, cost_lv, cost_xp = litem.merge(ritem)  # anvil rules & 39 cap
                    except (InvalidTarget, MergeTooExpensive):
                        continue
                    w = merged.anvil_uses
                    cand = (lsteps + rsteps + [Step(litem, ritem, cost_lv, cost_xp, merged.prior_penalty())],
                            llv + rlv + cost_lv, lxp + rxp + cost_xp, merged)
                    if w not in best or best[w][1] > cand[1]:  # minimize total_levels
                        best[w] = cand
    return best

Models are immutable. `EnchantedItem.merge(...)` returns a new item plus both cost views (levels & XP), applies level stacking, enforces incompatibilities, and clamps at the 39-level limit. A thin planner (`plan_enchants`) builds missing books from desired targets, runs the search, and picks the best solution per the chosen mode.

# Example usage
from enchant.models import EnchantedItem
from enchant.calculator import plan_enchants

base = EnchantedItem("netherite_sword", enchants={"mending":1}, anvil_uses=2)
desired = {"sharpness":5, "looting":3, "sweeping_edge":3}

plan = plan_enchants(base, desired, mode="levels")
print(plan.total_levels, plan.total_xp, plan.final_prior_work)
for step in plan.steps:
    print(f"{step.left} + {step.right} -> +{step.cost_levels}L (PW={step.result_prior})")

SEO & distribution: I tuned metadata and internal links so the app could be discovered by queries like “minecraft anvil order calculator.” Over the first three months it reached ~110k impressions at ~5.5 average position, then stabilized. The app is intentionally fast (single HTML, no client deps) which helps both UX and Core Web Vitals.

Why it’s interesting technically: (1) the search explores an exponential space but remains snappy due to memoization and canonicalization; (2) encoding all anvil rules in one pure function keeps correctness tractable; (3) the UI prevents invalid states rather than surfacing errors late; (4) the two optimization modes align with real player goals without complicating the UI.