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

Ideas for better efficiency?

Started by Things, 07 June 2013 - 01:10 AM
Things #1
Posted 07 June 2013 - 03:10 AM
Hi all, new to these forums, thought I'd drop in and check it out :)/>

I've been working on a completely automated fireworks factory using turtles and computers, and it's working great. However now comes the time where I try and optimize the system for speed. One issue I'm having is that one of my crafty turtles keeps timing out without yeilding, however putting a delay in the code slows it down a fair by the looks. Is there a way I can have the turtle yeild only when it's not trying to do something, instead of just a fixed delay in my loop? Or for that matter, optimize how the turtle distributes items between slots?

Basically what it does, is checks dur, which is the duration of the firework, then splits the item in the 2nd slot (gunpowder) between slots 3 and 5. This works fine, however it runs quite slowly, only moving once a second or so. It seems if the loop isn't really doing anything else, it should be able to run a bit faster. It's the bottleneck in my system.

So far I've managed to have it craft 32 fireworks with 6 dyes and 2 effects and a duration of 3 in about 1:30, but if I can speed up the moving items between slots, it'll be a lot faster. I plan on releasing this code once I've worked out the bugs, it took me a lot of time how to figure out how to craft fireworks automatically, allowing you to choose the ingredients :)/>/>

Sorry if the code is a bit messy, I have a bad habit of just throwing stuff together, then fixing it up once it works :)/>/>

modem = peripheral.wrap("right")
modem.open(1)
modem.open(2)
gotAmt = false
craftedAmt = 0
rxamt = false
rxdur = false
splitItems = false
while (true) do
if gotAmt == false then
  local event, modemSide, senderChannel, replyChannel, message, senderDistance = os.pullEvent("modem_message")
  if senderChannel == 1 then
	amt = message
	rxamt = true
	print(amt)
  end
  if senderChannel == 2 then
	dur = message
	rxdur = true
	print(dur)
  end
  if rxamt == true and rxdur == true then
	gotAmt = true
  end
end
if dur == 2 then
  if turtle.getItemCount(2) > amt then
	turtle.select(2)
	turtle.transferTo(3,1)
  end
end
if dur == 3 then
  if turtle.getItemCount(2) > amt then
	turtle.select(2)
	turtle.transferTo(3,1)
  end
if turtle.getItemCount(3) > amt then
	turtle.select(3)
	turtle.transferTo(5,1)
end
end
  if turtle.getItemCount(11) == amt+1 then
   if dur == 1 and turtle.getItemCount(2) == amt or dur == 2 and turtle.getItemCount(3) == amt or dur == 3 and turtle.getItemCount(5) == amt then
	turtle.select(16)
	turtle.craft(amt)
	turtle.drop()
	 gotAmt = false
	 rxdur = false
	 rxamt = false
	 print("Crafted all!")
   end
  end
  sleep(0.01)
end


Look forward to reading through the forums more.
Lyqyd #2
Posted 07 June 2013 - 10:56 AM
Split into new topic.
Bomb Bloke #3
Posted 07 June 2013 - 06:30 PM
Just off the top of my head, I don't think the sleep statement is your bottleneck. In my experience moving items around a turtle's inventory, and crafting with them, is just plain slow.
Engineer #4
Posted 07 June 2013 - 07:47 PM
For a quicker yield, you could use this:

os.queueEvent("newEvent")
coroutine.yield()

That should fix your main "problem" (it isnt a real problem :P/>/>). It is still slowed down, but less then sleep( 0.1 ).

But I want to give you some improvement on your code, please dont take this as offense or anything. I just want to help you.
First of all, you are using multiple if statements, this is not a bad thing, but it can be better so you dont have that end all over the place. An example:

if something == 1 then --# Good habit that you put parentheses, like you did. I prefer not though :P/>/>
   -- Do stuff
elseif something == 2 then
   -- Do other stuff
elseif something == 3 then
   -- Do other other stuff
end
You can put infinite (at least: quite a lot) elseif as you want. But you always must put an end at the end a open with if.

Also, you can set a range if you are using names, with the (and) operator:

local t = 1
if t > 0 and t < 5 then
    print("t is greater than 0 but less than 5")
else
    print("t < 0 or t > 5")
end
--> "t is greater than 0 but less than 5"
This can shorten some code that you have made, I hope I have helped you :)/>

Edit: Bomb bloke is right, you dont need the sleep because turtle.x yields. It is just slow :P/>
GopherAtl #5
Posted 07 June 2013 - 09:54 PM
turtle actions all yield, so you can ONLY be getting "too long without yielding" errors in parts of your code that AREN'T running any turtle actions.

select, getItemCount, getItemSpace, and getFuelLevel are the only turtle functions I can think of off the top of my head that don't count as actions, so loops that call only these commands are the only ones that need to sleep, and sleeps in those places should not significantly affect the system's speed, since they're presumably waiting for enough items to do some action anyway.

:edit: not select, it yields too. Checked, and the other 3 are the only 3 that don't.
Things #6
Posted 07 June 2013 - 10:45 PM
Thanks for all the suggestions, I did a bit more work on it last night after my post and managed to get it a fair bit faster moving items between slots. I think now it's running pretty quickly, though still a bit inefficient as it'll move items out of slot 2 into 3, then move 3 into 5, and repeat, instead of moving the right amount out of 2, then once that's done, moving onto 3. However since it does this as the items are being fed to it (pneumatic tubes and filters), he does generally keep up. I've managed to get the process down to 1:40 to craft an entire stack with 6 colours, 2 effects and a duration of 3, which is pretty good I think. Most of that time is wasted in items moving through pipes!

This is what I did last night:


modem = peripheral.wrap("right")
modem.open(1)
modem.open(2)
gotAmt = false
rxamt = false
rxdur = false
clearedslot = false
while (true) do
if gotAmt == false then
  local event, modemSide, senderChannel, replyChannel, message, senderDistance = os.pullEvent("modem_message")
  if senderChannel == 1 then
  amt = message
  rxamt = true
  print(amt)
  end
  if senderChannel == 2 then
  dur = message
  rxdur = true
  print(dur)
  end
  if rxamt == true and rxdur == true then
	gotAmt = true
	qty = amt * dur + amt + amt
end
end
if dur == 2 then
  if clearedslot == false and turtle.getItemCount(3) > 0 then
   turtle.select(3)
   turtle.transferTo(11,1)
   clearedslot = true
  end
if turtle.getItemCount(2) > amt then
turtle.select(2)
turtle.transferTo(3,turtle.getItemCount(2) - amt)
end
end
if dur == 3 then
	if clearedslot == false and turtle.getItemCount(2) > 0 then
	 turtle.select(2)
	 turtle.transferTo(11,1)
	 clearedslot = true
   end
if turtle.getItemCount(2) > amt then
turtle.select(2)
turtle.transferTo(3,turtle.getItemCount(2) - amt)
end
if turtle.getItemCount(3) > amt then
turtle.select(3)
turtle.transferTo(5,turtle.getItemCount(3) - amt)
end
if turtle.getItemCount(4) > 0 then
turtle.select(4)
turtle.transferTo(5)
end
end
gotQty = turtle.getItemCount(1) + turtle.getItemCount(2) + turtle.getItemCount(3) + turtle.getItemCount(5) + turtle.getItemCount(11)
  if gotQty == qty and gotAmt == true then
  if dur == 1 and turtle.getItemCount(2) == amt or dur == 2 and turtle.getItemCount(3) == amt or dur == 3 and turtle.getItemCount(5) == amt then
	turtle.select(16)
	turtle.craft(amt)
	turtle.drop()
	 gotAmt = false
	 rxdur = false
	 rxamt = false
	 clearedslot = false
	 print("Crafted all!")
	 modem.transmit(4,4,"Done!")
	end
	end
	sleep(0.01)
   end

The sleep is still in there, but it doesn't matter too much as it's moving the correct amount of items at once, instead of 1 item each iteration.

The main annoyance here is I don't think there's any way to compare against item ID's, originally I kept 1 of an item in the turtle so it could check whether the correct items were in the right slots, but that only allowed me to craft 63 items at a time. I managed to get past that by having the main computer count how many different items the turtles should get, so they count their inv until they reach that. But it also means that the timing of items going into the turtles has to be specific, so items end up in the right slots.

For example, this code is for the turtle that crafts the stars into the final rockets. He expects that the stars will get there first and end up in slot 1, so that gunpowder will end up in slot 2. However, the gunpowder and paper are slightly different distances from him when you use a duration of 2 vs 3, so in a duration of 2, paper gets there first, so he has to move that out of the way and allow gunpowder into slot 2 so it can be split.

Confusing stuff :)/>