diff --git a/craft.lua b/craft.lua index 59ba4b2..cfb397a 100644 --- a/craft.lua +++ b/craft.lua @@ -2,24 +2,13 @@ local component = require("component") local robot = require("robot") local sides = require("sides") +local recipes = require('recipes') + local crafting = component.crafting local inventory = component.inventory_controller RESULT_SLOT = 4 -local iron_pickaxe_recipe = { - result = { - item = { 'minecraft:iron_pickaxe', 0 }, - count = 1, - }, - ingredients = { - -- pair of name (string id) and damage separated by ';' - 'minecraft:iron_ingot;0', 'minecraft:iron_ingot;0', 'minecraft:iron_ingot;0', - nil, 'minecraft:stick;0', nil, - nil, 'minecraft:stick;0', nil, - } -} - -- robots inventory is 4 wide and crafting uses the top left portion local slot_map = { 1, 2, 3, @@ -36,9 +25,32 @@ local slot_map = { local function parse_item(str, delimiter) local pos = string.find(str, delimiter) if not pos then - return nil,nil + return nil, nil + end + return string.sub(str, 1, pos - 1), tonumber(string.sub(str, pos + 1)) +end + +---add count to the selected key, or create it if it doesn't exist +---@param dict table +---@param key string +---@param count number +local function add(dict, key, count) + if dict[key] then + dict[key] = dict[key] + count + else + dict[key] = count + end +end + +---append the array b to the end of array a +---@param a table +---@param b table +---@return table +local function append(a, b) + for i = 1, #b do + a[#a + 1] = b[i] end - return string.sub(str, 1, pos-1), tonumber(string.sub(str, pos+1)) + return a end ---read the facing inventory and return a table of item names and their count or nil if not facing an inventory @@ -53,11 +65,7 @@ local function read_chest_contents() local stack = inventory.getStackInSlot(sides.forward, slot) if stack then local name = stack.name .. ";" .. stack.damage - if contents[name] then - contents[name] = contents[name] + stack.size - else - contents[name] = stack.size - end + add(contents, name, stack.size) end end return contents @@ -139,12 +147,12 @@ end ---accepts a recipe table containing result and ingredients ---@param recipe table ---@return boolean -local function craft_recipe(recipe) +local function craft_single_recipe(recipe) if inventory.getStackInInternalSlot(RESULT_SLOT) then print('Cannot craft - output slot occupied') return false end - for i, ingredient in pairs(recipe.ingredients) do + for i, ingredient in pairs(recipe.shape) do print('Fetching ' .. ingredient) fetch_item(ingredient, slot_map[i]) end @@ -162,6 +170,111 @@ local function craft_recipe(recipe) return store_item() end +---comment +---@param recipe table +---@param available_items table +---@return boolean success +---@return table total_required_items +---@return table recipe_crafting_order array of recipes +local function craft_recipe_recursion(recipe, available_items) + local total_required_items = {} + local total_crafting_order = { recipe } + + for item, count in pairs(recipe.requires) do + local available = available_items[item] + if not available then + available = 0 + end + if available >= count then + print('I have ' .. count .. ' ' .. item) + -- don't have to craft anything + add(total_required_items, item, count) + -- remove it from tracked available items so if other recipe requires it, it needs to craft it + available_items[item] = available_items[item] - count + else + local required_count = count - available + print('Missing ' .. required_count .. ' ' .. item) + + -- check if I can craft it + local sub_recipe = recipes[item] + if sub_recipe then + print('Trying to craft ' .. item) + local number_of_crafting_operations = math.ceil(required_count / sub_recipe.count) + local excess = required_count % sub_recipe.count + print(' Will make ' .. number_of_crafting_operations .. ' crafting operations') + print(' That will produce ' .. excess .. ' items I can use later') + + local current_crafting_order = {} + for _ = 1, number_of_crafting_operations do + local success, required_items, crafting_order = craft_recipe_recursion(sub_recipe, available_items) + if not success then + return false, {}, {} + end + for k, v in pairs(required_items) do + add(total_required_items, k, v) + end + -- append + current_crafting_order = append(current_crafting_order, crafting_order) + end + -- prepend + total_crafting_order = append(current_crafting_order, total_crafting_order) + if excess > 0 then + add(available_items, item, excess) + end + else + print('Need ' .. required_count .. ' more ' .. item) + return false, {}, {} + end + end + end + return true, total_required_items, total_crafting_order +end + +---accepts a recipe table +---@param item string +---@return boolean +local function craft_recipe(item) + print('Trying to craft ' .. item) + local recipe = recipes[item] + if not recipe then + print('Cannot craft ' .. item) + return false + end + + local available_items = read_chest_contents() + if not available_items then + print('Not facing a chest') + return false + end + + local success, required_items, crafting_order = craft_recipe_recursion(recipe, available_items) + if not success then + print('Failed') + return false + end + print('\nRequires these items:') + for k, v in pairs(required_items) do + print(k, v) + end + + print('\nCrafting in order:') + for i, rec in ipairs(crafting_order) do + print(i, rec.result) + end + + + -- proceed to crafting the things + for i, rec in ipairs(crafting_order) do + print('Step #' .. i .. ' - ' .. rec.result) + if not craft_single_recipe(rec) then + print('Unexpected error') + return false + end + end + + return true +end + -- local target = ... -- if not target then @@ -169,7 +282,8 @@ end -- return -- end -craft_recipe(iron_pickaxe_recipe) +-- craft_recipe('minecraft:iron_pickaxe;0') +craft_recipe('opencomputers:keyboard;0') -- store_item() diff --git a/recipes.lua b/recipes.lua new file mode 100644 index 0000000..3b8c0d1 --- /dev/null +++ b/recipes.lua @@ -0,0 +1,253 @@ +-- template: +-- pair of name (string id) and damage separated by ';' +-- Iron Pickaxe +-- ['minecraft:iron_pickaxe;0'] = { +-- result = 'minecraft:iron_pickaxe;0', +-- count = 1, +-- ingredients = { +-- 'minecraft:iron_ingot;0', 'minecraft:iron_ingot;0', 'minecraft:iron_ingot;0', +-- nil, 'minecraft:stick;0', nil, +-- nil, 'minecraft:stick;0', nil, +-- } +-- }, + +return { + -- Oak planks + ['minecraft:planks;0'] = { + result = 'minecraft:planks;0', + count = 4, + requires = { + ['minecraft:log;0'] = 1, + }, + shape = { + nil, nil, nil, + nil, 'minecraft:log;0', nil, + nil, nil, nil, + } + }, + -- Sticks + ['minecraft:stick;0'] = { + result = 'minecraft:stick;0', + count = 4, + requires = { + ['minecraft:planks;0'] = 2, + }, + shape = { + nil, 'minecraft:planks;0', nil, + nil, 'minecraft:planks;0', nil, + nil, nil, nil, + } + }, + -- Iron Pickaxe + ['minecraft:iron_pickaxe;0'] = { + result = 'minecraft:iron_pickaxe;0', + count = 1, + requires = { + ['minecraft:iron_ingot;0'] = 3, + ['minecraft:stick;0'] = 5, + }, + shape = { + 'minecraft:iron_ingot;0', 'minecraft:iron_ingot;0', 'minecraft:iron_ingot;0', + nil, 'minecraft:stick;0', nil, + nil, 'minecraft:stick;0', nil, + } + }, + -- Iron nugget + ['minecraft:iron_nugget;0'] = { + result = 'minecraft:iron_nugget;0', + count = 9, + requires = { + ['minecraft:iron_ingot;0'] = 1, + }, + shape = { + 'minecraft:iron_ingot;0', nil, nil, + nil, nil, nil, + nil, nil, nil, + } + }, + -- gold nugget + ['minecraft:gold_nugget;0'] = { + result = 'minecraft:gold_nugget;0', + count = 9, + requires = { + ['minecraft:gold_ingot;0'] = 1, + }, + shape = { + 'minecraft:gold_ingot;0', nil, nil, + nil, nil, nil, + nil, nil, nil, + } + }, + -- Paper + ['minecraft:paper;0'] = { + result = 'minecraft:paper;0', + count = 3, + requires = { + ['minecraft:reeds;0'] = 3, + }, + shape = { + nil, nil, nil, + 'minecraft:reeds;0', 'minecraft:reeds;0', 'minecraft:reeds;0', + nil, nil, nil, + } + }, + -- Clock + ['minecraft:clock;0'] = { + result = 'minecraft:clock;0', + count = 1, + requires = { + ['minecraft:gold_ingot;0'] = 4, + ['minecraft:redstone;0'] = 1, + }, + shape = { + nil, 'minecraft:gold_ingot;0', nil, + 'minecraft:gold_ingot;0', 'minecraft:redstone;0', 'minecraft:gold_ingot;0', + nil, 'minecraft:gold_ingot;0', nil, + } + }, + -- Button + ['minecraft:stone_button;0'] = { + result = 'minecraft:stone_button;0', + count = 1, + requires = { + ['minecraft:stone;0'] = 1, + }, + shape = { + nil, nil, nil, + nil, 'minecraft:stone;0', nil, + nil, nil, nil, + } + }, + -- Transistor + ['opencomputers:material;6'] = { + result = 'opencomputers:material;6', + count = 8, + requires = { + ['minecraft:iron_ingot;0'] = 3, + ['minecraft:gold_nugget;0'] = 2, + ['minecraft:paper;0'] = 1, + ['minecraft:redstone;0'] = 1, + }, + shape = { + 'minecraft:iron_ingot;0', 'minecraft:iron_ingot;0', 'minecraft:iron_ingot;0', + 'minecraft:gold_nugget;0', 'minecraft:paper;0', 'minecraft:gold_nugget;0', + nil, 'minecraft:redstone;0', nil, + } + }, + -- Microchip (Tier 1) + ['opencomputers:material;7'] = { + result = 'opencomputers:material;7', + count = 8, + requires = { + ['minecraft:iron_nugget;0'] = 6, + ['minecraft:redstone;0'] = 2, + ['opencomputers:material;6'] = 1, -- Transistor + }, + shape = { + 'minecraft:iron_nugget;0', 'minecraft:iron_nugget;0', 'minecraft:iron_nugget;0', + 'minecraft:redstone;0', 'opencomputers:material;6', 'minecraft:redstone;0', + 'minecraft:iron_nugget;0', 'minecraft:iron_nugget;0', 'minecraft:iron_nugget;0', + } + }, + -- Microchip (Tier 2) + ['opencomputers:material;8'] = { + result = 'opencomputers:material;8', + count = 4, + requires = { + ['minecraft:gold_nugget;0'] = 6, + ['minecraft:redstone;0'] = 2, + ['opencomputers:material;6'] = 1, -- Transistor + }, + shape = { + 'minecraft:gold_nugget;0', 'minecraft:gold_nugget;0', 'minecraft:gold_nugget;0', + 'minecraft:redstone;0', 'opencomputers:material;6', 'minecraft:redstone;0', + 'minecraft:gold_nugget;0', 'minecraft:gold_nugget;0', 'minecraft:gold_nugget;0', + } + }, + -- Arithmetic Logic Unit (ALU) + ['opencomputers:material;10'] = { + result = 'opencomputers:material;10', + count = 1, + requires = { + ['minecraft:iron_nugget;0'] = 4, + ['opencomputers:material;6'] = 3, -- Transistor + ['minecraft:redstone;0'] = 1, + ['opencomputers:material;7'] = 1, -- Microchip (Tier 1) + }, + shape = { + 'minecraft:iron_nugget;0', 'minecraft:redstone;0', 'minecraft:iron_nugget;0', + 'opencomputers:material;6', 'opencomputers:material;7', 'opencomputers:material;6', + 'minecraft:iron_nugget;0', 'opencomputers:material;6', 'minecraft:iron_nugget;0', + } + }, + -- Control Unit (CU) + ['opencomputers:material;11'] = { + result = 'opencomputers:material;11', + count = 1, + requires = { + ['minecraft:gold_nugget;0'] = 4, + ['opencomputers:material;6'] = 3, -- Transistor + ['minecraft:redstone;0'] = 1, + ['minecraft:clock;0'] = 1, + }, + shape = { + 'minecraft:gold_nugget;0', 'minecraft:redstone;0', 'minecraft:gold_nugget;0', + 'opencomputers:material;6', 'minecraft:clock;0', 'opencomputers:material;6', + 'minecraft:gold_nugget;0', 'opencomputers:material;6', 'minecraft:gold_nugget;0', + } + }, + -- Button Group + ['opencomputers:material;14'] = { + result = 'opencomputers:material;14', + count = 1, + requires = { + ['minecraft:stone_button;0'] = 6, + }, + shape = { + nil, nil, nil, + 'minecraft:stone_button;0', 'minecraft:stone_button;0', 'minecraft:stone_button;0', + 'minecraft:stone_button;0', 'minecraft:stone_button;0', 'minecraft:stone_button;0', + } + }, + -- Arrow Keys + ['opencomputers:material;15'] = { + result = 'opencomputers:material;15', + count = 1, + requires = { + ['minecraft:stone_button;0'] = 4, + }, + shape = { + nil, nil, nil, + nil, 'minecraft:stone_button;0', nil, + 'minecraft:stone_button;0', 'minecraft:stone_button;0', 'minecraft:stone_button;0', + } + }, + -- Numeric Keypad + ['opencomputers:material;16'] = { + result = 'opencomputers:material;16', + count = 1, + requires = { + ['minecraft:stone_button;0'] = 9, + }, + shape = { + 'minecraft:stone_button;0', 'minecraft:stone_button;0', 'minecraft:stone_button;0', + 'minecraft:stone_button;0', 'minecraft:stone_button;0', 'minecraft:stone_button;0', + 'minecraft:stone_button;0', 'minecraft:stone_button;0', 'minecraft:stone_button;0', + } + }, + -- Keyboard + ['opencomputers:keyboard;0'] = { + result = 'opencomputers:material;0', + count = 1, + requires = { + ['opencomputers:material;14'] = 4, + ['opencomputers:material;15'] = 1, + ['opencomputers:material;16'] = 1, + }, + shape = { + nil, nil, nil, + 'opencomputers:material;14', 'opencomputers:material;14', 'opencomputers:material;14', + 'opencomputers:material;14', 'opencomputers:material;15', 'opencomputers:material;16', + } + }, +}