This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
sjonky's profile picture

Trying to make a turtle autocrafter, Need some advice

Started by sjonky, 25 January 2014 - 07:49 AM
sjonky #1
Posted 25 January 2014 - 08:49 AM
Hello!

I've been trying to make an autocrafting turtle craft some 16k storage cells.
And instead of making it very static, i want to make it a bit more dynamic, so that i can easily change what i want to craft.
Right now im kind of stuck on how to solve a problem i've gotten. I've gotten to the stage where i can check if i got enough items for a thing, and get a table whith the missing stuff.
The problem is searching through multiple levels of recipes, to check if i got everything for the entire craft.

Example:
I start crafting a 16k storage drive, for that i need: 3 redstone, 2 glass, 3 iron and 1 storageBlock. And since storageBlock is a recipe ive got in my code i want to switch over to check that recipe and add to the numbers. So then i need: 4 glowstone, 3 glass, 1 advanced processor, 3 storage segments. Then again storage segments is a recipe in my code, then continue like that until i get to the bottom of everything.

Here is my current code: http://pastebin.com/udkRfK5T

So if anyone would give me some advice on how to efficently solve this i would be grateful. Also if there ways to make my current code more efficient. that would also be great.

Thanks
sjonky #2
Posted 28 January 2014 - 02:57 PM
No advice/suggestions? Still haven't managed to get a Working "enough resources checker" got the crafting itself working like a charm though. But Still i would like it to know that it has all the required resources before it crafts the stuff
surferpup #3
Posted 28 January 2014 - 04:47 PM
It took me a while to wrap my head around the issue. I realize you can do this with other mods (not using a turtle), at the same time I completely respect wanting to figure out how to get a turtle to do it for you. 'nuf said on that.

As a courtesy to others who wish to reply, I am posting your code in the following spoiler:

Original Code

-- Recipes to build
recipes = {
storagecell = {
  redstone = {1,3,9,11},
  quartzcrystal = {2,5,7,10},
  mebasicprocessor = {6}
},

storagesegment = {
		redstone = {1,3,9,11},
		mebasicprocessor = {2},
		glass = {6},
		storagecell = {5,6,10}
},

storageblock = {
		glowstone = {1,3,9,11},
		storagesegment = {5,6,10},
		glass = {6},
		meadvancedprocessor = {2}
},

me16kstorage = {
		glass = {1,3},
		redstone = {2,5,7},
		storageblock = {6},
		iron = {9,10,11}
}
}

appen1 = "appeng.materials." -- Need theese to get the correct name from applied energistics
appen2 = ".name"



chest = peripheral.wrap("left")

function getSlot(slot)
		local info = chest.getStackInSlot(slot)
		if info then
				return info
		else
				return false
		end
end

function split(inputstr, sep) -- splits a string into a table
		if sep == nil then
				sep = "%s"
	end
	t={} ; i=1
		for str in string.gmatch(inputstr, "([^"..sep.."]+)") do
				t[i] = str
				i = i + 1
		end
	return t
end

function getName(slot) -- Returns the name, And if its applied energistics remove the long names
		local convert = {
				appen1.."quartzcrystal"..appen2,
				appen1.."mebasicprocessor"..appen2,
				appen1.."storagecell"..appen2,
				appen1.."storagesegment"..appen2,
				appen1.."storageblock"..appen2,
				appen1.."meadvancedprocessor"..appen2
		}
		local info = getSlot(slot)
		if info then
				info.name = string.lower(info.name)
				for k,v in pairs(convert) do -- Search through table and find appen names
						if info.name == v then
								info.name = split(info.name, ".")
								info.name = info.name[3]
								return info.name
						end
				end
				return info.name
		else
				return false
		end
end

function getQty(slot)
		local info = getSlot(slot)
		if info then
				return info.qty
		else
				return false
		end
end

function getChest() -- Returns the entire inventory of the chest as a table for faster access
		local size = chest.getInventorySize()
		local content = {}
		chest.condenseItems()
		for i = 1, size do
				local name = getName(i)
				local qty = getQty(i)
				if name then
						if content[name] then
								content[name] = content[name] + qty
						else
								content[name] = qty
						end
				else
						break
				end
		end
		return content
end

function getRecipe(name) -- Creates a table which displays needed item and quantity instead of need item and position as recipes does
		local recipe = {}
		for k,v in pairs(name) do
				if recipe[k] then
						recipe[k] = recipe[k] + #v
				else
						recipe[k] = #v
				end
		end
		return recipe
end

function compare(name, qty) -- Checks if name is in the chest, aswell as the correct quantity or more
		local content = getChest()
		local foundName = ""
		local foundQty = 0
		for k,v in pairs(content) do
				if k == name then
						if v >= qty then
								return true
						else
								foundQty = v or 0
						end
				else
						foundName = k
				end
		end
		return false, foundQty
end

function printT(t)
		for k,v in pairs(t) do
				print(k..": "..v)
		end
end

function gotRecipe(recipe, amount) -- Checks a recipe if everything is in the chest, Returns missing materials
		amount = amount or 1
		local missing = {}
		local content = getChest()
		local recipe = getRecipe(recipe)
		for itemName,itemQty in pairs(recipe) do
				found, qty = compare(itemName, itemQty * amount)
				if not found then
						if missing[itemName] then
								missing[itemName] = missing[itemName] + ((itemQty*amount)-qty)
						else
								missing[itemName] = itemQty*amount-qty
						end
				end
		end
		return missing
end

function isRecipe(name) -- Checks if name is a recipe in this code or not
		if recipes[name] then
				return true
		else
				return false
		end
end

function gotEnough(recipe, amount)
		amount = amount or 1
		local missing = gotRecipe(recipe, amount)
		if missing then
				for k,v in pairs(missing) do
						if isRecipe(k) then

						end
				end
		end
end

What you are trying to do is create some sort of database which, when queried properly, can tell you what recipes you could make based on your current inventory levels. In addition, there are hierarchical needs as well, e.g.: I want to make x which takes 1 crafted y and 3 headstone. Hmm. Do I have what I need to make the crafted y?

In your current code, you are limited to hard coded tables. I would recommend you come up with a structure you are happy with for an individual recipe and then store a table of recipes to a file, reading the file whenever that is necessary. Then you could write routines to add/edit/delete recipes from/to the file.

You may need to create several tables. One being a table of all ingredients you currently have. Another being a table with recipes. That way you can check the ingredients as you select a recipe. For each ingredient, you should also have a flag which you set to decide if the ingredient is elemental (cannot be crafted, must be supplied) or can be crafted with recipe id[x]. That way if it finds the ingredient, it is happy, but if it does not, it further checks to see if it has to request it or if the turtle can craft it itself.

Here is my concept of the algorithm:
I want to make Recipe ID "Thingy".

local availableInventoryTable {} – – structure is { {itemID,quantity}, {itemID,quantity} , etc }
local missingInventoryTable {} – structure is { {itemID,quantity}, {itemID,quantity} , etc }
function checkIngredients(recipeID) – returns table of need or false
  • Load table of requirements
    • requirement = {item, recipeID} where recipeID= nil if elemental
  • for each requirement, check inventory
    • found
      • deduct from available inventory and move to next requirement
    • not found
      • can I craft it? get recipeID
        • need = checkIngredients(recipeID) (this is a recursive call)
        • if need = false that means I can make it and I have updated availableInventoryTable
        • if need is a table, then update missingInventoryTable
      • I can't craft it, add requirement to need – structure is { {itemID,quantity}, {itemID,quantity} , etc }
  • return false if we have ingredients or we can craft ingredients
  • return need if we are missing ingredients.
Useage:
currentInvetoryTable structure is { {itemD,quantity}, {itemD,quantity} , etc }
availableInventoryTable {}= currentInventoryTable
local need = checkIngredients(recipeID)
if need ==false then
[indent=1]I have everything[/indent]
else
[indent=1]for k,v in pairs(need) do[/indent]
[indent=2]missingInventoryTable.k = missingInventoryTable.k + v[/indent]
[indent=1]end[/indent]
[indent=1]do something to notify user of missing Inventory[/indent]
end

This is all highly conceptual, and I can see that you have begun implementing portions of these ideas. However, consider what I am saying and see if that helps you.

As for your code itself, you follow decent indentation style, and your choice of naming functions and variables goes a long way towards making self-documenting code.

Also, just some advice: people on this site are willing to help – but they all volunteer. Try not to get snippy, especially when you drop code which takes time to sort through.
Edited on 28 January 2014 - 03:58 PM
sjonky #4
Posted 29 January 2014 - 04:15 PM
Thanks for a good answer :)/>

Yeah i know i can do this with other mods :P/> But well working turtle autocrafting is alot of fun to code. and it's very cheap compared to other ways of autocrafting.

If i understand everything in your answer i think i have most of it covered already in the code.

Although you do have some different approches which i would like to try out. which maybe can help with the problem. I do feel im really close to solving it though. It's probably a pretty good idea writing it down in "none code" as you have done. i usually dont do that.

Im thinking i should break down the gotRecipe into smaller parts, so that i can use some parts of it again.
Edited on 29 January 2014 - 03:16 PM
surferpup #5
Posted 29 January 2014 - 10:34 PM
Think about that recursive call to checkIngredients – I believe you will find it is a key element of your solution. – Oh. and if I helped you, vote my answer up :)/>