Automating Factorization - Crystallizer

  • The FTB Forum is now read-only, and is here as an archive. To participate in our community discussions, please join our Discord! https://ftb.team/discord

SnowsongWolf

New Member
Jul 29, 2019
15
0
0
So as many of us know, automating the crystallizer from Factorization is a bit of a pain because it doesn't automatically distribute items placed into it, and it can only create 1 type of item at a time. To that end, and with the help of OpenPeripherals and Computercraft, I give you... what I have yet to come up with a name for.

A little preface, this proof of concept was created in the Direwolf20 1.6.4 1.0.16 (1.0.14 and below don't work correctly with peripheral proxies, which are required on the chest) modpack. In my example, the buffer chest is south of the crystallizer (note that this seems backwards in the code and I'm not sure why but that's how it works), and both are hooked up to a computer using wired modems (though in theory wireless should work too) with the peripheral names crystallizer_1 and container_chest_0 respectively. An impulse itemduct sits below the crystallizer to pull out all the goods.

The program assumes the bottle of aqua regia to be in slot #6 of the crystallizer (left of the topmost slot) and it assumes that no one will be throwing other items into the crystallizer manually (no fault tolerance, figuring out what to do with those items was more of a headache than I could afford right now). Otherwise it's completely autonomous and utilizes (container).pullItemIntoSlot to move items around.

Feel free to suggest improvements, I've been sick while working on this (hence the time to do it). And feel free to repost this, just drop my name and it's all good.

A pastebin of the code is at Mq013acZ (http://pastebin.com/Mq013acZ)

Code:
prfCrystal = peripheral.wrap("crystallizer_1")
prfChest = peripheral.wrap("container_chest_0")
dirChest = "north"
dirCrystal = "south"

function getInv(prfSource)
  return prfSource.getAllStacks()
end

function filterInv(prfSource, intId, intDmg)
  intDmg = intDmg or 0
  arrSource = getInv(prfSource)
  arrInv = {}

  for i,v in pairs(arrSource) do
    if (v["id"] == intId and v["dmg"] == intDmg) then
      arrInv[i] = v["qty"]
    end
  end

  return arrInv
end

function countId(prfSource, intId, intDmg) -- return the number of intId:intDmg in inventory prfSource
  intCount = 0
  intDmg = intDmg or 0
  arrInv = getInv(prfSource)

  for i,v in pairs(arrInv) do
    if (v["id"] == intId and v["dmg"] == intDmg) then
      intCount = intCount + v["qty"]
    end
  end

  return intCount
end

function filterInCrystallizer(prfSource, intItem, intDmg)
  intDmg = intDmg or 0
  arrContent = getInv(prfSource)
  arrInv = {0,0,0,0,0}

  for i=1, 5 do
    if arrContent[i] then
      if (arrContent[i]["id"] == intItem and arrContent[i]["dmg"] == intDmg) then
        arrInv[i] = arrContent[i]["qty"]
      end
    end
  end

  return arrInv
end

function inCrystallizer(prfSource)
  arrContent = getInv(prfSource)
  arrInv= {}
  tmp = {}
  for i=1, 5 do
    if arrContent[i] then
      tmp["id"] = arrContent[i]["id"]
      tmp["dmg"] = arrContent[i]["dmg"]
      arrInv[i] = tmp
    end
  end
  return arrInv
end

function equalize(prfSource, prfDest)
  intQty = 0
  intSplit = 0
  intId = 0
  intDmg = 0
  intOdd = 0
  intPullList = {0,0,0,0,0}

  destInv = inCrystallizer(prfDest)
  prfSource.condenseItems()

  if table.getn(destInv) == 0 then
    if table.getn(getInv(prfSource)) == 0 then return end
    tgt = prfSource.getStackInSlot(1)
    intId = tgt["id"]
    intDmg = tgt["dmg"]
 
  else
    for _,v in pairs(destInv) do
      intId = v["id"]
      intDmg = v["dmg"]
    end
  end

  intQty = countId(prfSource, intId, intDmg) + countId(prfDest, intId, intDmg)
  intSplit = math.min(64, math.floor(intQty / 5))
  if intSplit ~= 64 then
    intOdd = intQty % 5
  end

  term.clear()
  print (intId .. ":" .. intDmg .. " " .. intQty .. " " .. intSplit .. "*5 + " .. intOdd)

  for i=1, 5 do
    tmpPull = intSplit - filterInCrystallizer(prfDest, intId, intDmg)[i]
    if i <= intOdd then tmpPull = tmpPull + 1 end
 
    sourceInv = filterInv(prfSource, intId, intDmg)
    for j,k in pairs(sourceInv) do
      tmpPull = tmpPull - prfDest.pullItem(dirCrystal, j, tmpPull, i)
      if tmpPull == 0 then break end
    end
  end





end

-- main loop
while true do
  equalize(prfChest, prfCrystal)
  sleep(0.5)
end
 
Last edited:

casilleroatr

New Member
Jul 29, 2019
1,360
0
0
That's a cool looking program. Is the buffer chest directly adjacent to the crystalliser? In the past I have used a turtle to traverse each crystalliser directly and wrapping the peripherals and unwrapping them as I go. That comes with a few problems in terms of fuelling the turtles and making sure they carry on working properly after being unloaded and reloaded so I really like the idea of being able to control this from a stationary computer.

If its alright by you I would like to make a few suggestions. You could go through the code and localise a lot of the variable names, especially the ones inside the functions. Apparently, local variable recall is faster in Lua - and it removes opportunities for parts of your program from interfering with each other unexpectedly.

A good way of preventing errors due to aqua regia being placed in the wrong slot you can iterate through all six crystalliser slots and if aqua regia is in it pass over, else deposit items.

Finally, 0.5s is a very short time to wait between turns for this program and given how slow the FZ ore processing chain goes, I have found a one minute wait is more than responsive enough (except when you're testing of course).

Thanks for sharing the program
p.s. I hope you are feeling better too (I have heard a flask of aqua regia will cure* what ails ya)

*not really it is really corrosive
 

SnowsongWolf

New Member
Jul 29, 2019
15
0
0
Yeah the chest is directly adjascent. I'd post a picture but shortly after pastebinning the code I hit a bug that destroyed my world save (not related to this). And yeah I know about aqua regia. I was showing a friend how to play Dungeons of Dredmor and he decided to try to drink it thinking it was a health potion. Funny enough, it seems slightly less deadly in Minecraft.

I didn't realize that about the variable names, I'll look into doing that. I'm used to C++ where the scope is defined partly by where you declare the variable. With the way I wrote it I'm pretty sure all variables can be made local. And I was originally going to go through each slot and ignore the aqua regia but it makes filling the slots just a little trickier. Instead of using a for 1 to 5 loop I'd probably store the contents of each slot not containing aqua regia in an array and use another for i,v in pairs loop.

Yeah half a second is a short time, especially when you consider that in my final build the computer's going to control more than just 1 crystallizer. Another alternative would be to use some item detection tubes (if they still exist, haven't used them since 1.4) to trigger computer updates when an item either enters the buffer chest or leaves the crystallizer.

One thing I did find from my testing is that using getAllStacks() is significantly faster than doing several getStackInSlot() commands, even if you're only checking a few slots. So I've got that going for me =D
 

casilleroatr

New Member
Jul 29, 2019
1,360
0
0
I might move over to using the getAllStacks command instead of the inSlot version that I normally use. I think they removed the getSizeInventory method recently making numeric for looping through chests annoying.
 

SnowsongWolf

New Member
Jul 29, 2019
15
0
0
Yeah I'm not sure why but calling 5 getStackInSlot commands on the crystallizer was slower than calling getAllStacks on the chest. getAllStacks really sped up the execution once I started using that and searching through the array for what I wanted.
 

casilleroatr

New Member
Jul 29, 2019
1,360
0
0
Does it return a numerically indexed table with the keys relating to the slots. If so I will feel pretty foolish for all those times when I did this:

Code:
local allSlotData ={}
for i = 1, chestSize do
local data = chest.getStackInSlot(i)
allSlotData[i] = data
end
 

Broomstone

New Member
Jul 29, 2019
38
0
0
Greetings.

SnowsongWolf, have you ever tried automating it with the servos? There is a functioning way to automate the Crystallizer with them.
 
  • Like
Reactions: Meirlean

Broomstone

New Member
Jul 29, 2019
38
0
0
Sure, I will post later how I'm doing it. but if you want something more at hand. here's something that neptunepink posted a while ago on his thread:

"I have prepared for you and the general public a world that is also not documenting, but may hopefully be able to instruct by example. And I found a bunch of bugs that I haven't fixed. Oh well. Requires the version of FZ 0.8.29"

I don't know how to post links, sorry, it is on page 103 of his minecraft forum thread.
 

casilleroatr

New Member
Jul 29, 2019
1,360
0
0
Sure, I will post later how I'm doing it. but if you want something more at hand. here's something that neptunepink posted a while ago on his thread:

"I have prepared for you and the general public a world that is also not documenting, but may hopefully be able to instruct by example. And I found a bunch of bugs that I haven't fixed. Oh well. Requires the version of FZ 0.8.29"

I don't know how to post links, sorry, it is on page 103 of his minecraft forum thread.
Thanks for the link. I will check it out. I look forward to seeing what you post later. I would suggest you put it in a new thread though so as to avoid derailing this one.
 

Lathanael

New Member
Jul 29, 2019
959
0
0
The servo is(just as the router) hands-down one of the best automation features currently available. Not because it is easy to use or the fastest for a given special case. It is because you can do pretty much everything with those little machines. Make a crop farm, automate ore processing, autocrafting and so on. The servo is one of the most versatile blocks/items in current modded MC (only beaten by a CC turtel/OC Robot)