fuzzy recipe ingredients

This commit is contained in:
kepler155c
2018-02-20 01:15:31 -05:00
parent 2cfa6c8c7f
commit cfc1502f2a
2 changed files with 225 additions and 208 deletions

View File

@@ -10,286 +10,301 @@ local USER_RECIPES = 'usr/config/recipes.db'
local Craft = { } local Craft = { }
local function clearGrid(inventoryAdapter) local function clearGrid(inventoryAdapter)
for i = 1, 16 do for i = 1, 16 do
local count = turtle.getItemCount(i) local count = turtle.getItemCount(i)
if count > 0 then if count > 0 then
inventoryAdapter:insert(i, count) inventoryAdapter:insert(i, count)
if turtle.getItemCount(i) ~= 0 then if turtle.getItemCount(i) ~= 0 then
-- inventory is possibly full -- inventory is possibly full
return false return false
end end
end end
end end
return true return true
end end
local function splitKey(key) local function splitKey(key)
local t = Util.split(key, '(.-):') local t = Util.split(key, '(.-):')
local item = { } local item = { }
if #t[#t] > 8 then if #t[#t] > 8 then
item.nbtHash = table.remove(t) item.nbtHash = table.remove(t)
end end
item.damage = tonumber(table.remove(t)) item.damage = tonumber(table.remove(t))
item.name = table.concat(t, ':') item.name = table.concat(t, ':')
return item return item
end end
function Craft.getItemCount(items, item) function Craft.getItemCount(items, item)
if type(item) == 'string' then if type(item) == 'string' then
item = splitKey(item) item = splitKey(item)
end end
local count = 0 local count = 0
for _,v in pairs(items) do for _,v in pairs(items) do
if v.name == item.name and if v.name == item.name and
(not item.damage or v.damage == item.damage) and (not item.damage or v.damage == item.damage) and
v.nbtHash == item.nbtHash then v.nbtHash == item.nbtHash then
if item.damage then if item.damage then
return v.count return v.count
end end
count = count + v.count count = count + v.count
end end
end end
return count return count
end end
local function turtleCraft(recipe, qty, inventoryAdapter) local function turtleCraft(recipe, qty, inventoryAdapter)
if not clearGrid(inventoryAdapter) then if not clearGrid(inventoryAdapter) then
return false return false
end end
for k,v in pairs(recipe.ingredients) do for k,v in pairs(recipe.ingredients) do
local item = splitKey(v) local item = splitKey(v)
local provideQty = qty local provideQty = qty
--[[ --[[
Turtles can only craft 1 item at a time when using a tool. Turtles can only craft 1 item at a time when using a tool.
if recipe.craftingTools and recipe.craftingTools[k] then if recipe.craftingTools and recipe.craftingTools[k] then
provideQty = 1 provideQty = 1
end end
]]-- ]]--
inventoryAdapter:provide(item, provideQty, k) inventoryAdapter:provide(item, provideQty, k)
if turtle.getItemCount(k) == 0 then -- ~= qty then if turtle.getItemCount(k) == 0 then -- ~= qty then
-- FIX: ingredients cannot be stacked -- FIX: ingredients cannot be stacked
--debug('failed ' .. v .. ' - ' .. provideQty) --debug('failed ' .. v .. ' - ' .. provideQty)
return false return false
end end
end end
return turtle.craft() return turtle.craft()
end end
function Craft.loadRecipes() function Craft.loadRecipes()
Craft.recipes = { } Craft.recipes = { }
Util.merge((Util.readTable(fs.combine(RECIPES_DIR, 'minecraft.db')) or { }).recipes) Util.merge(Craft.recipes, (Util.readTable(fs.combine(RECIPES_DIR, 'minecraft.db')) or { }).recipes)
local config = Util.readTable('usr/config/recipeBooks.db') or { } local config = Util.readTable('usr/config/recipeBooks.db') or { }
for _, book in pairs(config) do for _, book in pairs(config) do
local recipeFile = Util.readTable(book) local recipeFile = Util.readTable(book)
Util.merge(Craft.recipes, recipeFile.recipes) Util.merge(Craft.recipes, recipeFile.recipes)
end end
local recipes = Util.readTable(USER_RECIPES) or { } local recipes = Util.readTable(USER_RECIPES) or { }
Util.merge(Craft.recipes, recipes) Util.merge(Craft.recipes, recipes)
end end
function Craft.sumIngredients(recipe) function Craft.sumIngredients(recipe)
-- produces { ['minecraft:planks:0'] = 8 } -- produces { ['minecraft:planks:0'] = 8 }
local t = { } local t = { }
for _,item in pairs(recipe.ingredients) do for _,item in pairs(recipe.ingredients) do
t[item] = (t[item] or 0) + 1 t[item] = (t[item] or 0) + 1
end end
-- need a check for crafting tool -- need a check for crafting tool
return t return t
end end
function Craft.craftRecipe(recipe, count, inventoryAdapter) function Craft.craftRecipe(recipe, count, inventoryAdapter)
if type(recipe) == 'string' then if type(recipe) == 'string' then
recipe = Craft.recipes[recipe] recipe = Craft.recipes[recipe]
if not recipe then if not recipe then
return 0, 'No recipe' return 0, 'No recipe'
end end
end end
local items = inventoryAdapter:listItems() local items = inventoryAdapter:listItems()
if not items then if not items then
return 0, 'Inventory changed' return 0, 'Inventory changed'
end end
count = math.ceil(count / recipe.count) count = math.ceil(count / recipe.count)
local maxCount = recipe.maxCount or math.floor(64 / recipe.count) local maxCount = recipe.maxCount or math.floor(64 / recipe.count)
for key,icount in pairs(Craft.sumIngredients(recipe)) do for key,icount in pairs(Craft.sumIngredients(recipe)) do
local itemCount = Craft.getItemCount(items, key) local itemCount = Craft.getItemCount(items, key)
if itemCount < icount * count then if itemCount < icount * count then
local irecipe = Craft.recipes[key] local irecipe = Craft.recipes[key]
if irecipe then if irecipe then
local iqty = icount * count - itemCount local iqty = icount * count - itemCount
local crafted = Craft.craftRecipe(irecipe, iqty, inventoryAdapter) local crafted = Craft.craftRecipe(irecipe, iqty, inventoryAdapter)
if crafted ~= iqty then if crafted ~= iqty then
turtle.select(1) turtle.select(1)
return 0 return 0
end end
end end
end end
end end
local crafted = 0 local crafted = 0
repeat repeat
if not turtleCraft(recipe, math.min(count, maxCount), inventoryAdapter) then if not turtleCraft(recipe, math.min(count, maxCount), inventoryAdapter) then
turtle.select(1) turtle.select(1)
break break
end end
crafted = crafted + math.min(count, maxCount) crafted = crafted + math.min(count, maxCount)
count = count - maxCount count = count - maxCount
until count <= 0 until count <= 0
turtle.select(1) turtle.select(1)
return crafted * recipe.count return crafted * recipe.count
end end
local function makeRecipeKey(item) local function makeRecipeKey(item)
if type(item) == 'string' then if type(item) == 'string' then
item = splitKey(item) item = splitKey(item)
end end
return table.concat({ item.name, item.damage or 0, item.nbtHash }, ':') return table.concat({ item.name, item.damage or 0, item.nbtHash }, ':')
end end
function Craft.findRecipe(item) function Craft.findRecipe(key)
return Craft.recipes[makeRecipeKey(item)] if type(key) ~= 'string' then
key = itemDB:makeKey(key)
end
local item = itemDB:splitKey(key)
if item.damage then
return Craft.recipes[makeRecipeKey(item)]
end
-- handle cases where the request is like : IC2:reactorVent:*
for _,recipe in pairs(Craft.recipes) do
if item.name == recipe.name and
(not item.nbtHash or recipe.nbtHash == item.nbtHash) then
return recipe
end
end
end end
-- determine the full list of ingredients needed to craft -- determine the full list of ingredients needed to craft
-- a quantity of a recipe. -- a quantity of a recipe.
function Craft.getResourceList(inRecipe, items, inCount) function Craft.getResourceList(inRecipe, items, inCount)
local summed = { } local summed = { }
local function sumItems(recipe, key, count) local function sumItems(recipe, key, count)
local item = itemDB:splitKey(key) local item = itemDB:splitKey(key)
local summedItem = summed[key] local summedItem = summed[key]
if not summedItem then if not summedItem then
summedItem = Util.shallowCopy(item) summedItem = Util.shallowCopy(item)
summedItem.recipe = Craft.findRecipe(key) summedItem.recipe = Craft.findRecipe(key)
summedItem.count = Craft.getItemCount(items, item) summedItem.count = Craft.getItemCount(items, item)
summedItem.displayName = itemDB:getName(item) summedItem.displayName = itemDB:getName(item)
summedItem.total = 0 summedItem.total = 0
summedItem.need = 0 summedItem.need = 0
summedItem.used = 0 summedItem.used = 0
summed[key] = summedItem summed[key] = summedItem
end end
local total = count local total = count
local used = math.min(summedItem.count, total) local used = math.min(summedItem.count, total)
local need = total - used local need = total - used
if recipe.craftingTools and recipe.craftingTools[key] then if recipe.craftingTools and recipe.craftingTools[key] then
summedItem.total = 1 summedItem.total = 1
if summedItem.count > 0 then if summedItem.count > 0 then
summedItem.used = 1 summedItem.used = 1
summedItem.need = 0 summedItem.need = 0
need = 0 need = 0
elseif not summedItem.recipe then elseif not summedItem.recipe then
summedItem.need = 1 summedItem.need = 1
need = 1 need = 1
end end
else else
summedItem.total = summedItem.total + total summedItem.total = summedItem.total + total
summedItem.count = summedItem.count - used summedItem.count = summedItem.count - used
summedItem.used = summedItem.used + used summedItem.used = summedItem.used + used
if not summedItem.recipe then if not summedItem.recipe then
summedItem.need = summedItem.need + need summedItem.need = summedItem.need + need
end end
end end
if need > 0 and summedItem.recipe then if need > 0 and summedItem.recipe then
need = math.ceil(need / summedItem.recipe.count) need = math.ceil(need / summedItem.recipe.count)
for ikey,iqty in pairs(Craft.sumIngredients(summedItem.recipe)) do for ikey,iqty in pairs(Craft.sumIngredients(summedItem.recipe)) do
sumItems(summedItem.recipe, ikey, math.ceil(need * iqty)) sumItems(summedItem.recipe, ikey, math.ceil(need * iqty))
end end
end end
end end
inCount = math.ceil(inCount / inRecipe.count) inCount = math.ceil(inCount / inRecipe.count)
for ikey,iqty in pairs(Craft.sumIngredients(inRecipe)) do for ikey,iqty in pairs(Craft.sumIngredients(inRecipe)) do
sumItems(inRecipe, ikey, math.ceil(inCount * iqty)) sumItems(inRecipe, ikey, math.ceil(inCount * iqty))
end end
return summed return summed
end end
function Craft.getResourceList4(inRecipe, items, count) function Craft.getResourceList4(inRecipe, items, count)
local summed = Craft.getResourceList(inRecipe, items, count) local summed = Craft.getResourceList(inRecipe, items, count)
-- filter down to just raw materials -- filter down to just raw materials
return Util.filter(summed, function(a) return a.used > 0 or a.need > 0 end) return Util.filter(summed, function(a) return a.used > 0 or a.need > 0 end)
end end
-- given a certain quantity, return how many of those can be crafted -- given a certain quantity, return how many of those can be crafted
function Craft.getCraftableAmount(recipe, count, items, missing) function Craft.getCraftableAmount(recipe, count, items, missing)
local function sumItems(recipe, summedItems, count) local function sumItems(recipe, summedItems, count)
local canCraft = 0 local canCraft = 0
for _ = 1, count do for _ = 1, count do
for _,item in pairs(recipe.ingredients) do for _,item in pairs(recipe.ingredients) do
local summedItem = summedItems[item] or Craft.getItemCount(items, item) local summedItem = summedItems[item] or Craft.getItemCount(items, item)
local irecipe = Craft.recipes[item] local irecipe = Craft.recipes[item]
if irecipe and summedItem <= 0 then if irecipe and summedItem <= 0 then
summedItem = summedItem + sumItems(irecipe, summedItems, 1) summedItem = summedItem + sumItems(irecipe, summedItems, 1)
end end
if summedItem <= 0 then if summedItem <= 0 then
if missing then if missing then
missing.name = item missing.name = item
end end
return canCraft return canCraft
end end
if not recipe.craftingTools or not recipe.craftingTools[item] then if not recipe.craftingTools or not recipe.craftingTools[item] then
summedItems[item] = summedItem - 1 summedItems[item] = summedItem - 1
end end
end end
canCraft = canCraft + recipe.count canCraft = canCraft + recipe.count
end end
return canCraft return canCraft
end end
return sumItems(recipe, { }, math.ceil(count / recipe.count)) return sumItems(recipe, { }, math.ceil(count / recipe.count))
end end
function Craft.canCraft(item, count, items) function Craft.canCraft(item, count, items)
return Craft.getCraftableAmount(Craft.recipes[item], count, items) == count return Craft.getCraftableAmount(Craft.recipes[item], count, items) == count
end end
function Craft.setRecipes(recipes) function Craft.setRecipes(recipes)
Craft.recipes = recipes Craft.recipes = recipes
end end
function Craft.getCraftableAmountTest() function Craft.getCraftableAmountTest()
local results = { } local results = { }
Craft.setRecipes(Util.readTable('usr/etc/recipes.db')) Craft.setRecipes(Util.readTable('usr/etc/recipes.db'))
local items = { local items = {
{ name = 'minecraft:planks', damage = 0, count = 5 }, { name = 'minecraft:planks', damage = 0, count = 5 },
{ name = 'minecraft:log', damage = 0, count = 2 }, { name = 'minecraft:log', damage = 0, count = 2 },
} }
results[1] = { item = 'chest', expected = 1, results[1] = { item = 'chest', expected = 1,
got = Craft.getCraftableAmount(Craft.recipes['minecraft:chest:0'], 2, items) } got = Craft.getCraftableAmount(Craft.recipes['minecraft:chest:0'], 2, items) }
items = { items = {
{ name = 'minecraft:log', damage = 0, count = 1 }, { name = 'minecraft:log', damage = 0, count = 1 },
{ name = 'minecraft:coal', damage = 1, count = 1 }, { name = 'minecraft:coal', damage = 1, count = 1 },
} }
results[2] = { item = 'torch', expected = 4, results[2] = { item = 'torch', expected = 4,
got = Craft.getCraftableAmount(Craft.recipes['minecraft:torch:0'], 4, items) } got = Craft.getCraftableAmount(Craft.recipes['minecraft:torch:0'], 4, items) }
return results return results
end end
function Craft.craftRecipeTest(name, count) function Craft.craftRecipeTest(name, count)
local ChestAdapter = require('chestAdapter18') local ChestAdapter = require('chestAdapter18')
local chestAdapter = ChestAdapter({ wrapSide = 'top', direction = 'down' }) local chestAdapter = ChestAdapter({ wrapSide = 'top', direction = 'down' })
Craft.setRecipes(Util.readTable('usr/etc/recipes.db')) Craft.setRecipes(Util.readTable('usr/etc/recipes.db'))
return { Craft.craftRecipe(Craft.recipes[name], count, chestAdapter) } return { Craft.craftRecipe(Craft.recipes[name], count, chestAdapter) }
end end
Craft.loadRecipes() Craft.loadRecipes()

View File

@@ -63,7 +63,6 @@ local page = UI.Page {
accelerators = { accelerators = {
q = 'quit', q = 'quit',
space = 'grid_select', space = 'grid_select',
t = 'terminate',
}, },
} }
@@ -104,16 +103,19 @@ end
function page:eventHandler(event) function page:eventHandler(event)
if event.type == 'grid_select' then if event.type == 'grid_select' then
local recipes = self.grid:getSelected() local book = self.grid:getSelected()
recipes.enabled = not recipes.enabled book.enabled = not book.enabled
self.info:draw() self.info:draw()
self.grid:draw() self.grid:draw()
self:save() self:save()
elseif event.type == 'grid_focus_row' then elseif event.type == 'grid_focus_row' then
self.info:draw() self.info:draw()
elseif event.type == 'quit' then elseif event.type == 'quit' then
UI:exitPullEvents() UI:exitPullEvents()
end end
UI.Page.eventHandler(self, event) UI.Page.eventHandler(self, event)
end end