Optimize smelting process by pre-building furnace compatibility sets and using sorted candidate lists for improved efficiency

This commit is contained in:
MayaTheShy
2026-03-16 19:21:44 -04:00
parent 594d8c954c
commit 9e1852a219

View File

@@ -195,6 +195,35 @@ local SMELTABLE = {
["farmersdelight:minced_beef"] = { result = "farmersdelight:beef_patty", furnaces = {"minecraft:furnace", "minecraft:smoker"} },
}
-- Pre-build furnace compatibility sets for O(1) lookup
for _, recipe in pairs(SMELTABLE) do
recipe.furnaceSet = {}
for _, ft in ipairs(recipe.furnaces) do
recipe.furnaceSet[ft] = true
end
end
-- Pre-built smelt candidate lists per furnace type (sorted: food first, then alpha)
-- Avoids rebuilding & re-sorting in autoSmelt on every cycle.
local smeltCandidatesByType = {}
do
for _, ftype in ipairs(FURNACE_TYPES) do
smeltCandidatesByType[ftype] = {}
end
for itemName, recipe in pairs(SMELTABLE) do
local isFood = recipe.furnaceSet["minecraft:smoker"] or false
for ft, _ in pairs(recipe.furnaceSet) do
table.insert(smeltCandidatesByType[ft], { name = itemName, recipe = recipe, food = isFood })
end
end
for _, list in pairs(smeltCandidatesByType) do
table.sort(list, function(a, b)
if a.food ~= b.food then return a.food end
return a.name < b.name
end)
end
end
-- Fuel items, ordered by preference (best first)
-- burn_time = how many items one fuel smelts
local FUEL_LIST = {
@@ -782,7 +811,28 @@ end
local function refreshCache(onProgress)
activity.scanning = true
local chests = getChests()
-- Single pass over peripherals: classify into chests and furnaces
local chests = {}
local furnaces = {}
local furnaceTypeSet = {}
for _, ft in ipairs(FURNACE_TYPES) do furnaceTypeSet[ft] = true end
for _, name in ipairs(peripheral.getNames()) do
local ptype = peripheral.getType(name)
if ptype == "minecraft:chest" then
table.insert(chests, name)
elseif furnaceTypeSet[ptype] then
table.insert(furnaces, name)
end
end
-- Update peripheral caches so getChests()/getFurnaces() stay fresh
local now = os.clock()
cachedChests = chests
cachedChestsTime = now
cachedFurnaces = furnaces
cachedFurnacesTime = now
local catalogue = {}
local totalSlots = 0
local usedSlots = 0
@@ -837,16 +887,8 @@ local function refreshCache(onProgress)
cache.dropperOk = peripheral.wrap(DROPPER_NAME) ~= nil
cache.barrelOk = peripheral.wrap(BARREL_NAME) ~= nil
-- Count furnaces
local furnaceCount = 0
for _, ftype in ipairs(FURNACE_TYPES) do
for _, name in ipairs(peripheral.getNames()) do
if peripheral.getType(name) == ftype then
furnaceCount = furnaceCount + 1
end
end
end
cache.furnaceCount = furnaceCount
-- Furnace count already computed from single-pass above
cache.furnaceCount = #furnaces
-- Scan furnace contents for smelter dashboard
refreshFurnaceStatus()
@@ -1722,6 +1764,9 @@ local function drawSmelterDashboard()
bx1, by1, bx2, by2 = drawButton(bx2 + 2, 4, "Missing", colors.white, tabMissingBg)
addSmelterZone(bx1, by1, bx2, by2, "tab", "missing")
-- Hoisted counter: reused by bottom accent bar to avoid re-scanning recipes
local craftAvailCount = nil
if smelterView == "status" then
-- ===== Furnace Status View =====
-- Column headers
@@ -1839,12 +1884,9 @@ local function drawSmelterDashboard()
local resultShort = recipe.result:gsub("^minecraft:", ""):gsub("_", " ")
resultShort = resultShort:sub(1,1):upper() .. resultShort:sub(2)
local types = ""
for _, ft in ipairs(recipe.furnaces) do
if ft == "minecraft:furnace" then types = types .. "F"
elseif ft == "minecraft:smoker" then types = types .. "S"
elseif ft == "minecraft:blast_furnace" then types = types .. "B"
end
end
if recipe.furnaceSet["minecraft:furnace"] then types = types .. "F" end
if recipe.furnaceSet["minecraft:smoker"] then types = types .. "S" end
if recipe.furnaceSet["minecraft:blast_furnace"] then types = types .. "B" end
local enabled = not disabledRecipes[inputName]
local inStorage = 0
if cache.catalogue[inputName] then
@@ -1965,6 +2007,7 @@ local function drawSmelterDashboard()
})
end
end
craftAvailCount = #availList
-- Column headers
monFill(5, colors.gray)
@@ -2050,6 +2093,7 @@ local function drawSmelterDashboard()
})
end
end
craftAvailCount = #CRAFTABLE - #missList
-- Column headers
monFill(5, colors.gray)
@@ -2136,12 +2180,8 @@ local function drawSmelterDashboard()
bottomMsg = " Tap MAKE to craft "
if activity.crafting then bottomMsg = " CRAFTING... " end
elseif smelterView == "missing" then
local totalC = #CRAFTABLE
local availC = 0
for _, r in ipairs(CRAFTABLE) do
if canCraftRecipe(r) then availC = availC + 1 end
end
bottomMsg = string.format(" Available: %d/%d recipes ", availC, totalC)
local availC = craftAvailCount or 0
bottomMsg = string.format(" Available: %d/%d recipes ", availC, #CRAFTABLE)
end
monCenter(h, bottomMsg, colors.pink, colors.purple)
@@ -2172,9 +2212,11 @@ local function sortBarrel(barrelOverride)
for slot, item in pairs(contents) do
local moved = 0
local triedChests = {} -- dedup: skip chests already tried via catalogue
if catalogue[item.name] then
for _, entry in ipairs(catalogue[item.name]) do
triedChests[entry.chest] = true
local n = barrel.pushItems(entry.chest, slot)
if n and n > 0 then
moved = moved + n
@@ -2189,15 +2231,17 @@ local function sortBarrel(barrelOverride)
if moved < item.count then
for _, chest in ipairs(chests) do
local n = barrel.pushItems(chest, slot)
if n and n > 0 then
moved = moved + n
adjustCache(item.name, chest, n)
needsRedraw = true
smelterNeedsRedraw = true
print(string.format("[SORT] %s x%d -> %s", item.name, n, chest))
if not triedChests[chest] then
local n = barrel.pushItems(chest, slot)
if n and n > 0 then
moved = moved + n
adjustCache(item.name, chest, n)
needsRedraw = true
smelterNeedsRedraw = true
print(string.format("[SORT] %s x%d -> %s", item.name, n, chest))
end
if moved >= item.count then break end
end
if moved >= item.count then break end
end
end
@@ -2275,15 +2319,7 @@ local function autoSmelt()
local inputItem = contents[SLOT_INPUT]
if inputItem then
local recipe = SMELTABLE[inputItem.name]
local validHere = false
if recipe then
for _, ft in ipairs(recipe.furnaces) do
if ft == furnaceType then
validHere = true
break
end
end
end
local validHere = recipe and recipe.furnaceSet[furnaceType] or false
if not validHere then
-- This item doesn't belong in this furnace type — pull it out
for _, chest in ipairs(chests) do
@@ -2336,37 +2372,13 @@ local function autoSmelt()
-- 4) Load smeltable items into empty input slot
inputItem = contents[SLOT_INPUT]
if not inputItem then
-- Build sorted candidate list: food first, then everything else
local candidates = {}
for itemName, recipe in pairs(SMELTABLE) do
-- Check if this furnace type is compatible
local compatible = false
for _, ft in ipairs(recipe.furnaces) do
if ft == furnaceType then
compatible = true
break
end
end
if compatible and not disabledRecipes[itemName] and catalogue[itemName] then
local isFood = false
for _, ft in ipairs(recipe.furnaces) do
if ft == "minecraft:smoker" then
isFood = true
break
end
end
table.insert(candidates, { name = itemName, recipe = recipe, food = isFood })
end
end
-- Sort: food first
table.sort(candidates, function(a, b)
if a.food ~= b.food then return a.food end
return a.name < b.name
end)
-- Use pre-built candidate list (already sorted: food first, then alpha)
local candidates = smeltCandidatesByType[furnaceType] or {}
-- Try each candidate
for _, cand in ipairs(candidates) do
local itemName = cand.name
if not disabledRecipes[itemName] and catalogue[itemName] then
-- Count total of this item across all chests
local totalInStorage = 0
for _, src in ipairs(catalogue[itemName]) do
@@ -2403,6 +2415,7 @@ local function autoSmelt()
end
if loaded or remaining < math.min(available, 64) then break end
end
end
end
end
end