From 048fa77cd67206acd059a3e5ce09244eaec94ae4 Mon Sep 17 00:00:00 2001 From: D4VID Date: Sun, 13 Oct 2024 22:38:07 +0200 Subject: [PATCH] Finished autocrafting --- craft.lua | 190 ++++++++++++++++++++----------- recipes.lua | 315 +++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 434 insertions(+), 71 deletions(-) diff --git a/craft.lua b/craft.lua index cfb397a..b1e2e0a 100644 --- a/craft.lua +++ b/craft.lua @@ -1,6 +1,7 @@ local component = require("component") local robot = require("robot") local sides = require("sides") +local event = require("event") local recipes = require('recipes') @@ -30,6 +31,17 @@ local function parse_item(str, delimiter) return string.sub(str, 1, pos - 1), tonumber(string.sub(str, pos + 1)) end +---duplicate the table t +---@param t table +---@return table +local function clone(t) + local t2 = {} + for k, v in pairs(t) do + t2[k] = v + end + return t2 +end + ---add count to the selected key, or create it if it doesn't exist ---@param dict table ---@param key string @@ -53,6 +65,21 @@ local function append(a, b) return a end +---Ask the user if they want to proceed +---@return boolean +local function confirm() + while true do + print("Proceed? [y/n]") + local _, _, ascii, _, _ = event.pull("key_down") + local ch = string.char(ascii) + if ch == 'y' then + return true + elseif ch == 'n' then + return false + end + end +end + ---read the facing inventory and return a table of item names and their count or nil if not facing an inventory ---@return table|nil local function read_chest_contents() @@ -87,7 +114,7 @@ local function store_item() -- check if all items have been stored local remaining = inventory.getStackInInternalSlot() if not remaining then - print('Stored successfully') + -- print('Stored successfully') return true end end @@ -106,7 +133,7 @@ local function store_item() print('Cannot store item: ' .. error .. "\n") return false else - print('Stored successfully') + -- print('Stored successfully') return true end else @@ -153,8 +180,11 @@ local function craft_single_recipe(recipe) return false end for i, ingredient in pairs(recipe.shape) do - print('Fetching ' .. ingredient) - fetch_item(ingredient, slot_map[i]) + -- print('Fetching ' .. ingredient) + if not fetch_item(ingredient, slot_map[i]) then + print('Failed to fetch ' .. ingredient) + confirm() + end end -- select slot for the result @@ -172,68 +202,85 @@ end ---comment ---@param recipe table ----@param available_items table ----@return boolean success +---@param available_items table available initially +---@param currently_available table available from previous crafring ---@return table total_required_items ---@return table recipe_crafting_order array of recipes -local function craft_recipe_recursion(recipe, available_items) +local function craft_recipe_recursion(recipe, available_items, currently_available) local total_required_items = {} - local total_crafting_order = { recipe } + local total_crafting_order = {} + -- go through all the required ingredients for item, count in pairs(recipe.requires) do - local available = available_items[item] + -- check if some crafting result provided this item + local available = currently_available[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) + -- print('I have ' .. count .. ' ' .. item .. ' from previous crafting') -- remove it from tracked available items so if other recipe requires it, it needs to craft it - available_items[item] = available_items[item] - count + currently_available[item] = currently_available[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, {}, {} + + -- check if there is enough in the main available storage + available = available_items[item] + if not available then + available = 0 + end + if available >= required_count then + -- print('I have ' .. required_count .. ' ' .. item .. ' available') + add(total_required_items, item, required_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 + -- update count + required_count = required_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 = number_of_crafting_operations * sub_recipe.count - required_count + -- print(' Will make ' .. number_of_crafting_operations .. ' crafting operations') + -- print(' That will produce ' .. excess .. ' items I can use later') + + -- do the crafting + local current_crafting_order = {} + for _ = 1, number_of_crafting_operations do + local required_items, crafting_order = craft_recipe_recursion(sub_recipe, available_items, currently_available) + for k, v in pairs(required_items) do + add(total_required_items, k, v) + end + current_crafting_order = append(current_crafting_order, crafting_order) end - for k, v in pairs(required_items) do - add(total_required_items, k, v) + + total_crafting_order = append(total_crafting_order, current_crafting_order) + if excess > 0 then + add(currently_available, item, excess) end - -- append - current_crafting_order = append(current_crafting_order, crafting_order) + else + -- print('Need ' .. required_count .. ' more ' .. item) + add(total_required_items, item, count) 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 + + -- add the recipe it self to the end + total_crafting_order[#total_crafting_order+1] = recipe + + return total_required_items, total_crafting_order end ---accepts a recipe table ---@param item string ---@return boolean -local function craft_recipe(item) +local function craft_item(item) print('Trying to craft ' .. item) local recipe = recipes[item] if not recipe then @@ -247,14 +294,29 @@ local function craft_recipe(item) return false end - local success, required_items, crafting_order = craft_recipe_recursion(recipe, available_items) - if not success then - print('Failed') + local required_items, crafting_order = craft_recipe_recursion(recipe, clone(available_items), {}) + + print('\nRequires these items:') + local failed = false + for required_item, required_count in pairs(required_items) do + local available = available_items[required_item] + if not available then + available = 0 + end + if available < required_count then + print(available .. "/" .. required_count .. "\t" .. required_item .. "\t- Missing " .. required_count - available) + failed = true + else + print(available .. "/" .. required_count .. "\t" .. required_item) + end + end + if failed then return false end - print('\nRequires these items:') - for k, v in pairs(required_items) do - print(k, v) + + if not confirm() then + print('Operation cancelled') + return false end print('\nCrafting in order:') @@ -262,6 +324,10 @@ local function craft_recipe(item) print(i, rec.result) end + if not confirm() then + print('Operation cancelled') + return false + end -- proceed to crafting the things for i, rec in ipairs(crafting_order) do @@ -275,23 +341,15 @@ local function craft_recipe(item) return true end --- local target = ... +local target = ... --- if not target then --- print("Usage: craft ") --- return --- end - --- craft_recipe('minecraft:iron_pickaxe;0') -craft_recipe('opencomputers:keyboard;0') +if not target then + print("Usage: craft ") + return +end --- store_item() +craft_item(target) --- local contents = read_chest_contents() --- if contents then --- for k,v in pairs(contents) do --- print(k,v) --- end --- else --- print('Not facing a chest') --- end +-- craft_item('minecraft:iron_pickaxe;0') +-- craft_item('opencomputers:keyboard;0') +-- craft_item('opencomputers:material;10') diff --git a/recipes.lua b/recipes.lua index 3b8c0d1..ba6702d 100644 --- a/recipes.lua +++ b/recipes.lua @@ -2,9 +2,13 @@ -- pair of name (string id) and damage separated by ';' -- Iron Pickaxe -- ['minecraft:iron_pickaxe;0'] = { --- result = 'minecraft:iron_pickaxe;0', --- count = 1, --- ingredients = { +-- result = 'minecraft:iron_pickaxe;0', +-- count = 1, +-- requires = { +-- ['minecraft:iron_ingot;0'] = 3, +-- ['minecraft:stick;0'] = 2, +-- }, +-- shape = { -- 'minecraft:iron_ingot;0', 'minecraft:iron_ingot;0', 'minecraft:iron_ingot;0', -- nil, 'minecraft:stick;0', nil, -- nil, 'minecraft:stick;0', nil, @@ -38,13 +42,26 @@ return { nil, nil, nil, } }, + -- Chest + ['minecraft:chest;0'] = { + result = 'minecraft:chest;0', + count = 1, + requires = { + ['minecraft:planks;0'] = 8, + }, + shape = { + 'minecraft:planks;0', 'minecraft:planks;0', 'minecraft:planks;0', + 'minecraft:planks;0', nil, 'minecraft:planks;0', + 'minecraft:planks;0', 'minecraft:planks;0', 'minecraft:planks;0', + } + }, -- Iron Pickaxe ['minecraft:iron_pickaxe;0'] = { result = 'minecraft:iron_pickaxe;0', count = 1, requires = { ['minecraft:iron_ingot;0'] = 3, - ['minecraft:stick;0'] = 5, + ['minecraft:stick;0'] = 2, }, shape = { 'minecraft:iron_ingot;0', 'minecraft:iron_ingot;0', 'minecraft:iron_ingot;0', @@ -65,7 +82,20 @@ return { nil, nil, nil, } }, - -- gold nugget + -- Iron Bars + ['minecraft:iron_bars;0'] = { + result = 'minecraft:iron_bars;0', + count = 16, + requires = { + ['minecraft:iron_ingot;0'] = 6, + }, + shape = { + nil, nil, nil, + 'minecraft:iron_ingot;0', 'minecraft:iron_ingot;0', 'minecraft:iron_ingot;0', + 'minecraft:iron_ingot;0', 'minecraft:iron_ingot;0', 'minecraft:iron_ingot;0', + } + }, + -- Gold nugget ['minecraft:gold_nugget;0'] = { result = 'minecraft:gold_nugget;0', count = 9, @@ -91,6 +121,62 @@ return { nil, nil, nil, } }, + -- Hopper + ['minecraft:hopper;0'] = { + result = 'minecraft:hopper;0', + count = 1, + requires = { + ['minecraft:iron_ingot;0'] = 5, + ['minecraft:chest;0'] = 1, + }, + shape = { + 'minecraft:iron_ingot;0', nil, 'minecraft:iron_ingot;0', + 'minecraft:iron_ingot;0', 'minecraft:chest;0', 'minecraft:iron_ingot;0', + nil, 'minecraft:iron_ingot;0', nil, + } + }, + -- Dropper + ['minecraft:dropper;0'] = { + result = 'minecraft:dropper;0', + count = 1, + requires = { + ['minecraft:cobblestone;0'] = 7, + ['minecraft:redstone;0'] = 1, + }, + shape = { + 'minecraft:cobblestone;0', 'minecraft:cobblestone;0', 'minecraft:cobblestone;0', + 'minecraft:cobblestone;0', nil, 'minecraft:cobblestone;0', + 'minecraft:cobblestone;0', 'minecraft:redstone;0', 'minecraft:cobblestone;0', + } + }, + -- Lever + ['minecraft:lever;0'] = { + result = 'minecraft:lever;0', + count = 1, + requires = { + ['minecraft:stick;0'] = 1, + ['minecraft:cobblestone;0'] = 1, + }, + shape = { + nil, 'minecraft:stick;0', nil, + nil, 'minecraft:cobblestone;0', nil, + nil, nil, nil, + } + }, + -- Redstone Torch + ['minecraft:redstone_torch;0'] = { + result = 'minecraft:redstone_torch;0', + count = 1, + requires = { + ['minecraft:stick;0'] = 1, + ['minecraft:redstone;0'] = 1, + }, + shape = { + nil, 'minecraft:redstone;0', nil, + nil, 'minecraft:stick;0', nil, + nil, nil, nil, + } + }, -- Clock ['minecraft:clock;0'] = { result = 'minecraft:clock;0', @@ -105,6 +191,22 @@ return { nil, 'minecraft:gold_ingot;0', nil, } }, + -- Piston + ['minecraft:piston;0'] = { + result = 'minecraft:piston;0', + count = 1, + requires = { + ['minecraft:iron_ingot;0'] = 1, + ['minecraft:redstone;0'] = 1, + ['minecraft:planks;0'] = 3, + ['minecraft:cobblestone;0'] = 4, + }, + shape = { + 'minecraft:planks;0', 'minecraft:planks;0', 'minecraft:planks;0', + 'minecraft:cobblestone;0', 'minecraft:iron_ingot;0', 'minecraft:cobblestone;0', + 'minecraft:cobblestone;0', 'minecraft:redstone;0', 'minecraft:cobblestone;0', + } + }, -- Button ['minecraft:stone_button;0'] = { result = 'minecraft:stone_button;0', @@ -118,6 +220,24 @@ return { nil, nil, nil, } }, + + ----------------------------------------------------------------------------------------- + + -- Card Base + ['opencomputers:material;5'] = { + result = 'opencomputers:material;5', + count = 1, + requires = { + ['minecraft:iron_nugget;0'] = 3, + ['minecraft:gold_nugget;0'] = 1, + ['opencomputers:material;4'] = 1, -- Printed Circuit Board + }, + shape = { + 'minecraft:iron_nugget;0', nil, nil, + 'minecraft:iron_nugget;0', 'opencomputers:material;4', nil, + 'minecraft:iron_nugget;0', 'minecraft:gold_nugget;0', nil, + } + }, -- Transistor ['opencomputers:material;6'] = { result = 'opencomputers:material;6', @@ -196,6 +316,19 @@ return { 'minecraft:gold_nugget;0', 'opencomputers:material;6', 'minecraft:gold_nugget;0', } }, + -- Disk Platter + ['opencomputers:material;12'] = { + result = 'opencomputers:material;12', + count = 1, + requires = { + ['minecraft:iron_nugget;0'] = 4, + }, + shape = { + nil, 'minecraft:iron_nugget;0', nil, + 'minecraft:iron_nugget;0', nil, 'minecraft:iron_nugget;0', + nil, 'minecraft:iron_nugget;0', nil, + } + }, -- Button Group ['opencomputers:material;14'] = { result = 'opencomputers:material;14', @@ -235,6 +368,9 @@ return { 'minecraft:stone_button;0', 'minecraft:stone_button;0', 'minecraft:stone_button;0', } }, + + ----------------------------------------------------------------------------------------- + -- Keyboard ['opencomputers:keyboard;0'] = { result = 'opencomputers:material;0', @@ -250,4 +386,173 @@ return { 'opencomputers:material;14', 'opencomputers:material;15', 'opencomputers:material;16', } }, + -- Screen (Tier 1) + ['opencomputers:screen1;0'] = { + result = 'opencomputers:screen1;0', + count = 1, + requires = { + ['minecraft:iron_ingot;0'] = 4, + ['minecraft:redstone;0'] = 3, + ['opencomputers:material;7'] = 1, -- Microchip (Tier 1) + ['minecraft:glass;0'] = 1, + }, + shape = { + 'minecraft:iron_ingot;0', 'minecraft:redstone;0', 'minecraft:iron_ingot;0', + 'minecraft:redstone:0', 'opencomputers:material:7;0', 'minecraft:glass:0', + 'minecraft:iron_ingot;0', 'minecraft:redstone:0', 'minecraft:iron_ingot;0', + } + }, + -- Memory (Tier 1) + ['opencomputers:component;6'] = { + result = 'opencomputers:component;6', + count = 1, + requires = { + ['opencomputers:material;7'] = 2, -- Microchip (Tier 1) + ['minecraft:iron_nugget;0'] = 1, + ['opencomputers:material;4'] = 1, -- Printed Circuit Board + }, + shape = { + nil, nil, nil, + 'opencomputers:material;7', 'minecraft:iron_nugget;0', 'opencomputers:material;7', + nil, 'opencomputers:material;4', nil, + } + }, + -- Graphics Card (Tier 1) + ['opencomputers:card;1'] = { + result = 'opencomputers:card;1', + count = 1, + requires = { + ['opencomputers:material;7'] = 1, -- Microchip (Tier 1) + ['opencomputers:material;10'] = 1, -- Arithmetic Logic Unit (ALU) + ['opencomputers:component;6'] = 1, -- Memoty (Tier 1) + ['opencomputers:material;5'] = 1, -- Card Base + }, + shape = { + nil, nil, nil, + 'opencomputers:material;7', 'opencomputers:material;10', 'opencomputers:component;6', + nil, 'opencomputers:material;5', nil, + } + }, + -- EEPROM + ['opencomputers:storage;0'] = { + result = 'opencomputers:storage;0', + count = 1, + requires = { + ['opencomputers:material;6'] = 1, -- Transistor + ['opencomputers:material;7'] = 1, -- Microchip (Tier 1) + ['minecraft:gold_nugget;0'] = 4, + ['minecraft:paper;0'] = 2, + ['minecraft:redstone_torch;0'] = 1, + }, + shape = { + 'minecraft:gold_nugget;0', 'opencomputers:material;6', 'minecraft:gold_nugget;0', + 'minecraft:paper;0', 'opencomputers:material;7', 'minecraft:paper;0', + 'minecraft:gold_nugget;0', 'minecraft:redstone_torch;0', 'minecraft:gold_nugget;0', + } + }, + -- Floppy Disk + ['opencomputers:storage;1'] = { + result = 'opencomputers:storage;1', + count = 1, + requires = { + ['opencomputers:material;12'] = 1, -- Disk Platter + ['minecraft:iron_nugget;0'] = 4, + ['minecraft:paper;0'] = 3, + ['minecraft:lever;0'] = 1, + }, + shape = { + 'minecraft:iron_nugget;0', 'minecraft:lever;0', 'minecraft:iron_nugget;0', + 'minecraft:paper;0', 'opencomputers:material;12', 'minecraft:paper;0', + 'minecraft:iron_nugget;0', 'minecraft:paper;0', 'minecraft:iron_nugget;0', + } + }, + -- Hard Disk Drive (Tier 1) + ['opencomputers:storage;2'] = { + result = 'opencomputers:storage;2', + count = 1, + requires = { + ['opencomputers:material;7'] = 2, -- Microchip (Tier 1) + ['opencomputers:material;4'] = 1, -- Printed Circuit Board + ['opencomputers:material;12'] = 3, -- Disk Platter + ['minecraft:iron_ingot;0'] = 2, + ['minecraft:piston;0'] = 1, + }, + shape = { + 'opencomputers:material;7', 'opencomputers:material;12', 'minecraft:iron_ingot;0', + 'opencomputers:material;4', 'opencomputers:material;12', 'minecraft:piston;0', + 'opencomputers:material;7', 'opencomputers:material;12', 'minecraft:iron_ingot;0', + } + }, + -- Computer Case (Tier 2) + ['opencomputers:case2;0'] = { + result = 'opencomputers:case2;0', + count = 1, + requires = { + ['opencomputers:material;8'] = 1, -- Microchip (Tier 2) + ['opencomputers:material;4'] = 1, -- Printed Circuit Board + ['minecraft:gold_ingot;0'] = 4, + ['minecraft:iron_bars;0'] = 2, + ['minecraft:chest;0'] = 1, + }, + shape = { + 'minecraft:gold_ingot;0', 'opencomputers:material;8', 'minecraft:gold_ingot;0', + 'minecraft:iron_bars;0', 'minecraft:chest;0', 'minecraft:iron_bars;0', + 'minecraft:gold_ingot;0', 'opencomputers:material;4', 'minecraft:gold_ingot;0', + } + }, + -- Analyzer + ['opencomputers:tool;0'] = { + result = 'opencomputers:tool;0', + count = 1, + requires = { + ['opencomputers:material;6'] = 1, -- Transistor + ['opencomputers:material;4'] = 1, -- Printed Circuit Board + ['minecraft:gold_nugget;0'] = 2, + ['minecraft:redstone_torch;0'] = 1, + }, + shape = { + 'minecraft:redstone_torch;0', nil, nil, + 'opencomputers:material;6', 'minecraft:gold_nugget;0', nil, + 'opencomputers:material;4', 'minecraft:gold_nugget;0', nil, + } + }, + + ----------------------------------------------------------------------------------------- + + -- Inventory Upgrade + ['opencomputers:upgrade;17'] = { + result = 'opencomputers:upgrade;17', + count = 1, + requires = { + ['opencomputers:material;7'] = 1, -- Microchip (Tier 1) + ['minecraft:planks;0'] = 4, + ['minecraft:hopper;0'] = 1, + ['minecraft:dropper;0'] = 1, + ['minecraft:chest;0'] = 1, + ['minecraft:piston;0'] = 1, + }, + shape = { + 'minecraft:planks;0', 'minecraft:hopper;0', 'minecraft:planks;0', + 'minecraft:dropper;0', 'minecraft:chest;0', 'minecraft:piston;0', + 'minecraft:planks;0', 'opencomputers:material;7', 'minecraft:planks;0', + } + }, + -- Inventory Controler Upgrade + ['opencomputers:upgrade;18'] = { + result = 'opencomputers:upgrade;18', + count = 1, + requires = { + ['opencomputers:material;8'] = 1, -- Microchip (Tier 2) + ['opencomputers:material;4'] = 1, -- Printed Circuit Board + ['opencomputers:tool;0'] = 1, -- Analyzer + ['minecraft:gold_ingot;0'] = 4, + ['minecraft:dropper;0'] = 1, + ['minecraft:piston;0'] = 1, + }, + shape = { + 'minecraft:gold_ingot;0', 'opencomputers:tool;0', 'minecraft:gold_ingot;0', + 'minecraft:dropper;0', 'opencomputers:material;8', 'minecraft:piston;0', + 'minecraft:gold_ingot;0', 'opencomputers:material;4', 'minecraft:gold_ingot;0', + } + }, }