This is a read-only snapshot of the ComputerCraft forums,
            taken in April 2020.
        
        
    
     
        Coroutine
Started by KaoS, 16 August 2012 - 08:38 AMPosted 16 August 2012 - 10:38 AM
                Well this is embarrassing :(/>/>  to be honest I have recently taken a look at the lua 5.1 manual and I just cannot understand the coroutines AT ALL, I tried writing one but it did nothing at all, (I think the while true loop in the function may have broken it, not sure) but I would really love to learn it, anyone prepared to explain? I know Pharap knows this stuff
                
            Posted 16 August 2012 - 11:02 AM
                The Coroutine is the Thread API.
The whole System is a Thread, its called the Main Thread, but you can create new Threads that work parallel to all other things.
The parallel API uses the coroutine API to run functions parallel.
BUT: I think coroutine.resume receives other args too, such as events, but Im not sure
PS.: a thread is not a string, not a number, not a table, and not nil, It has its own type: "Thread". And you can't create Threads without the coroutine.create function
                
            coroutine.create(function)coroutine.resume(thread)coroutine.state(thread)coroutine.yield(string)The whole System is a Thread, its called the Main Thread, but you can create new Threads that work parallel to all other things.
The parallel API uses the coroutine API to run functions parallel.
BUT: I think coroutine.resume receives other args too, such as events, but Im not sure
PS.: a thread is not a string, not a number, not a table, and not nil, It has its own type: "Thread". And you can't create Threads without the coroutine.create function
Posted 16 August 2012 - 11:06 AM
                Ok, so  you say
what is the point of that, can you include a loop and have it running while you run other programs?
and I think any other args you put into resume get put into the function somehow
                
            
mycoroutine=coroutine.create(print("test"))
coroutine.resume(mycoroutine)
what is the point of that, can you include a loop and have it running while you run other programs?
and I think any other args you put into resume get put into the function somehow
Posted 16 August 2012 - 11:13 AM
                This is more complex example of coroutines this script is intended for use with a windoes api
                
            Spoiler
--[[
   Windows style OS
   With multi tasking
   By Big Shiny Toys
]]--
local tFunck = {
taskMan = function()
  local mywin = newWin("Task Man",20,12,1,1,"slim") -- "classic" "slim"
  local h = 1
  while true do
   local i,i1,i2,i3,i4,i5 = coroutine.yield()
   wSetCursorPos(mywin,1,1)
   wPrint(mywin,"tasks :"..tostring(#routines).." win :"..tostring(#windata))
   for i = 1, #routines do
	h = h +1
	wSetCursorPos(mywin,1,h)
	wPrint(mywin,i.." :"..coroutine.status(routines[i]))
	h = h +1
	wSetCursorPos(mywin,1,h)
	wPrint(mywin,tostring(coroutine.running(routines[i])))
   end
   h = 1
  end
end,
move = function()
  while true do
   local win = 1
   local i,i1,i2,i3,i4,i5 = coroutine.yield()
   if i == "char" and i1 == "w" then
	break
   end
   local winX,winY = location(win)
   if i == "key" then
	if i1 == 200 then -- up key
	 winY = winY-1
	end
	if i1 == 208 then -- down key
	 winY = winY+1
	end
	if i1 == 203 then -- left key
	 winX = winX-1
	end
	if i1 == 205 then -- right key
	 winX = winX+1
	end
	moveWin(win,winX,winY)
   end
  end
end,
cmd = function()
  local wn = newWin("CMD",20,12,4,4,"slim") -- "classic" "slim"
  wSetCursorPos(wn,1,1)
  wPrint(wn,"test")
  test = wRead(wn)
  wSetCursorPos(wn,1,2)
  wPrint(wn,test)
end
}
test = nil
--  start of OS --
windata = {} -- windata[1] = {} -- windata[1][1] = "Task Manager" windata[1][2] = xpos
local focused = 0
routines = {}
local border = ""
local border2 = ""
function newWin(name,xsiz,ysiz,xpos,ypos,style)
if style == "classic" or style == "slim" then
elseif style == nil then
  style = "classic"
end
if xpos == nil or ypos == nil then
  xpos,ypos = 1,1
end
if xsiz == nil or ysiz == nil then
  xsiz,ysiz = 20,4
end
local maxn = #windata + 1
windata[maxn] = {}
windata[maxn][1] = tostring(name)
windata[maxn][2] = xpos
windata[maxn][3] = ypos
windata[maxn][4] = xsiz
windata[maxn][5] = ysiz
windata[maxn][6] = {}
-- windata[maxn][6][1] = {} -- y lines needs work
for y = 1, windata[maxn][5] do
  windata[maxn][6][y] = {}
  for x = 1, windata[maxn][4] do
   windata[maxn][6][y][x] = " "
  end
end
-- windata[maxn][6][1][1] = {} -- x lines needs work
windata[maxn][7] = style
windata[maxn][8] = 1 -- curX
windata[maxn][9] = 1 -- curY
return(maxn)
end
function wRead(win)
local temp = {}
local sIn = ""
local it,it1,it2,it3,it4,it5
while true do
  it,it1,it2,it3,it4,it5 = coroutine.yield()
  if i == "char" then
   table.insert(temp,il)
  end
  if i == "key" then
   if il == 28 then
	break
   end
   if il == 14 then
	if #temp < 0 then
	else
	 table.remove(temp)
	end
   end
  end
  for i = 1, #temp do
   sIn = sIn..temp[i]
  end
end
return sIn
end
function wSetCursorPos(win,ax,ay)
windata[win][8] = ax
windata[win][9] = ay
end
function wPrint(win,inval)
for i = 1,string.len(inval) do
  windata[win][6][windata[win][9]][windata[win][8]] = string.sub(inval,i,i)
  windata[win][8] = windata[win][8]+1
  if windata[win][8] == windata[win][4]+1 then
   windata[win][8] = 1
   windata[win][9] = windata[win][9]+1
  end
end
end
local function draw(win)
local function safeWrite(this)
  local termX,termY = term.getSize()
  local curX,curY = term.getCursorPos()
  if termX-curX-1 < 0 then
   return
  else
   write (string.sub(this,1,termX-curX-1))
   return
  end
end
local wasx,wasy = term.getCursorPos()
local termX,termY = term.getSize()
if win == nil then
  return
end
border = ""
border2 = ""
for q = 1, windata[win][4]+2 do
  if q == 1 or q == windata[win][4]+2 then
   border = border.."+"
  else
   border = border.."-"
  end
end
for q = 1,windata[win][4]+2 do
  if q == 1 or q == windata[win][4]+2 then
   border2 = border2.."|"
  else
   border2 = border2.." "
  end
end
if windata[win][7] == "classic" then
  for i = 1, windata[win][5]+4 do
   term.setCursorPos(windata[win][2],windata[win][3]+i-1)
   if i == 1 or i == 3 or i == windata[win][5]+4 then
	safeWrite(border) -- here
   else
	safeWrite(border2) -- here
   end
  end
  term.setCursorPos(windata[win][2]+1,windata[win][3]+1)
  safeWrite(string.sub(windata[win][1],1,windata[win][4]-1))
  for y = 1, #windata[win][6] do
   for x = 1, #windata[win][6][y] do
	term.setCursorPos(windata[win][2]+x,windata[win][3]+y+2)
	safeWrite(tostring(windata[win][6][y][x]))
   end
  end
  term.setCursorPos(wasx,wasy)
elseif windata[win][7] == "slim" then
  for i = 1, windata[win][5]+2 do
   term.setCursorPos(windata[win][2],windata[win][3]+i-1)
   if i == 1 or i == windata[win][5]+2 then
	safeWrite(border) -- here
   else
	safeWrite(border2) -- here
   end
  end
  term.setCursorPos(windata[win][2]+2,windata[win][3])
  safeWrite(string.sub(windata[win][1],1,windata[win][4]-1)) -- here
  for y = 1, #windata[win][6] do
   for x = 1, #windata[win][6][y] do
	term.setCursorPos(windata[win][2]+x,windata[win][3]+y)
	safeWrite(tostring(windata[win][6][y][x]))
   end
  end
  term.setCursorPos(wasx,wasy)
end
end
local function run(fFun)
local a = #routines+1
routines[a] = coroutine.create(tFunck[fFun])
end
function location(win)
if win == nil then
  return
else
  return windata[win][2],windata[win][3]
end
end
function moveWin(win,posx,posy)
if win == nil or posx == nil or posy == nil then
  return
else
  windata[win][2],windata[win][3] = posx,posy
end
end
--[[
local mywin = newWin("Hello World",40,7,1,1,"slim")
local mywin2 = newWin("Hello World",10,2,1,1,"slim")
local sel = 3
local tempX,tempY = location(mywin)
draw(mywin)
]]--
-- boot here --
run("taskMan")
run("move")
run("cmd")
-- end boot --
while true do
local e,e1,e2,e3,e4,e5 = os.pullEvent()
term.clear()
term.setCursorPos(1,1)
if e == "char" and e1 == "q" or e1 == "Q" then
  break
end
for i = 1, #routines do
  coroutine.resume(routines[i],e,e1,e2,e3,e4,e5)
end
for p = 1, #windata do
  draw(p)
end
end
-- end os --
--[[
while true do --  test scrip
e,e1,e2,e3,e4,e5 = os.pullEvent()
if e == "char" then
  if e1 == "w" then
   sel = sel+1
   if sel < 1 then sel = 3 end
   if sel > 3 then sel = 1 end
   tempX,tempY = location(sel)
  end
end
if e == "key" then
  if e1 == 200 then -- up
   tempY = tempY-1
  end
  if e1 == 208 then -- down
   tempY = tempY+1
  end
  if e1 == 203 then -- left
   tempX = tempX-1
  end
  if e1 == 205 then -- right
   tempX = tempX+1
  end
end
if sel == 3 then
else
  moveWin(sel,tempX,tempY)
end
term.clear()
draw(mywin2)
draw(mywin)
end
]]--
Posted 16 August 2012 - 11:14 AM
                No, because if you run a function (add the () to a function name to run it)) it will get the return of a function, not the function. if you want to print test in a coroutine then here is the code:Ok, so you saythen when you saymycoroutine=coroutine.create(print("test"))it prints 'test'?coroutine.resume(mycoroutine)
function theprint()
  print("test")
end
local a=coroutine.create(theprint) --will give the function itself as a param, not the return
coroutine.resume(a)  --will start the Thread a
right, you can terminate the shell, and terminate the bios and the Thread will run more.what is the point of that, can you include a loop and have it running while you run other programs?
yes, that can beand I think any other args you put into resume get put into the function somehow
Posted 16 August 2012 - 11:16 AM
                Interesting, I'm guessing that you could then do
mycoroutine=coroutine.create(loadstring("print('test')"))
                
            mycoroutine=coroutine.create(loadstring("print('test')"))
Posted 16 August 2012 - 11:17 AM
                Yes thats right
PS.: Nice picture :(/>/>
                
            PS.: Nice picture :(/>/>
Posted 16 August 2012 - 11:19 AM
                I got that far before but if I made the simple functions:
and called
and
                
            
local function go()
turtle.forward()
turtle.back()
end
local function mover()
while true do
turtle.forward()
turtle.back()
end
end
and called
co=coroutine.create(go)
coroutine.resume(co)
and
co=coroutine.create(mover)
coroutine.resume(co)
Posted 16 August 2012 - 11:21 AM
                Yes thats right
PS.: Nice picture :(/>/>
haha, thanks
Posted 16 August 2012 - 11:28 AM
                Try to clear the co var, like co=nil and then try again… I don't know why it should stop the Thread…I got that far before but if I made the simple functions:local function go() turtle.forward() turtle.back() end local function mover() while true do turtle.forward() turtle.back() end end
and calledit moved forwards and ended successfullyco=coroutine.create(go) coroutine.resume(co)
andjust returned success and did nothingco=coroutine.create(mover) coroutine.resume(co)
Posted 16 August 2012 - 11:30 AM
                I did that, repeatedly restarted the PC. it didn't stop the thread, it claimed it was complete… that's what got me
                
            Posted 16 August 2012 - 11:35 AM
                I got that far before but if I made the simple functions:local function go() turtle.forward() turtle.back() end local function mover() while true do turtle.forward() turtle.back() end end
and calledit moved forwards and ended successfullyco=coroutine.create(go) coroutine.resume(co)
andjust returned success and did nothingco=coroutine.create(mover) coroutine.resume(co)
Perhaps because you're using co instead of mover?
Or maybe because you are trying to do an action then reverse it, it happened so fast it wasn't noticeable. Try making the turtle loop back on itself via turning (eg forward, right, forward, right etc) that way if something goes wrong it won't screw up. Also, make the coroutines do different things, like have one moving, one printing.
Posted 16 August 2012 - 11:37 AM
                yeah but the first one when called just moved forward, that is noticeable, the second one maybe you're right, but it ended the thread, said it ended successfully…. makes 0 sense
                
            Posted 16 August 2012 - 11:40 AM
                You do realise you never actually started mover right?yeah but the first one when called just moved forward, that is noticeable, the second one maybe you're right, but it ended the thread, said it ended successfully…. makes 0 sense
second one resumes co, not mover.co=coroutine.create(go) coroutine.resume(co)co=coroutine.create(mover) coroutine.resume(co)
Posted 16 August 2012 - 11:43 AM
                no, take another look, I restarted the PC and then ran
which redefines co as the coroutine/thread of mover
                
            
co=coroutine.create(mover)
coroutine.resume(co)
which redefines co as the coroutine/thread of mover
Posted 16 August 2012 - 11:46 AM
                What are you using to test it's return value?no, take another look, I restarted the PC and then ranco=coroutine.create(mover) coroutine.resume(co)
which redefines co as the coroutine/thread of mover
Posted 16 August 2012 - 11:49 AM
                you just call it in the lua prompt and it prints out all output or you can run it like this
                
            
for _,w in pairs({coroutine.resume(co)}) do
print(w)
end
Posted 16 August 2012 - 11:53 AM
                In that case I know your problem.you just call it in the lua prompt and it prints out all output or you can run it like thisI haven't tried the second method but it's something I thought aboutfor _,w in pairs({coroutine.resume(co)}) do print(w) end
The mover function goes too long without yielding, thus it encounters an error (which coroutine what tell you about, hence you should test the function before using it in a coroutine.)
Posted 16 August 2012 - 11:57 AM
                yes but there is a long delay in moving forwards and backwards isn't there? long enough to prevent a yield error and that still doesn't explain why the first function only moves forward
                
            Posted 16 August 2012 - 12:00 PM
                Hang on, I have just realised the obvious.yes but there is a long delay in moving forwards and backwards isn't there? long enough to prevent a yield error and that still doesn't explain why the first function only moves forward
THERE IS NO TURTLE.BACK()
Posted 16 August 2012 - 12:05 PM
                um dude…. there is…. I am running CC 1.3 and it works, I just did it right now
                
            Posted 16 August 2012 - 12:08 PM
                In that case your coroutines are just being weird.um dude…. there is…. I am running CC 1.3 and it works, I just did it right now
Posted 16 August 2012 - 12:12 PM
                hahaha, test this for me
I find that strange
                
            
function mover()
while true do
turtle.forward()
turtle.back()
end
end
test=coroutine.create(mover)
true
turtle_response
I find that strange
Posted 16 August 2012 - 12:41 PM
                you are missing () from your turtle.back command
This is a demo of multi tasking using coroutines It is simple demo
the above allows you to type in is also waiting for rednet and moving at the same time it it the power of coroutines to do lots of stuff at ounce.
                
            This is a demo of multi tasking using coroutines It is simple demo
local function mover()
    while true do
	    sleep(0.1) -- this creats a yeild point
	    turtle.forward()
	    turtle.back()
    end
end
local function userInput()
    while true do
	    term.clear()
	    term.setCursorPos(1,1)
	    local input = read()
    end
end
local function rednetCon()
    while true do
	    local event,id,sender = os.pullEvent()
	    if event == "rednet_message" then
		    turtle.turnLeft()
		    local curX,curY = term.getCurosrPos()
		    term.setCursorPos(1,3)
		    print("rednet message")
		    term.setCursorPos(curX,curY)
	    end
    end
end
rednet.open("right")
local threads = {}
table.insert(threads,coroutine.create(mover))
table.insert(threads,coroutine.create(userInput))
table.insert(threads,coroutine.create(rednetCon))
while true do
    local tEvents = {os.pullEvent()}
    for i = 1,#threads do
	    coroutine.resume(threads[i],unpack(tEvents))
    end
end
the above allows you to type in is also waiting for rednet and moving at the same time it it the power of coroutines to do lots of stuff at ounce.
Posted 16 August 2012 - 01:24 PM
                why the while true loop?
I had the brackets on the test, it moved back and forward…
                
            I had the brackets on the test, it moved back and forward…
Posted 16 August 2012 - 01:45 PM
                strange, while taking a look at this it only worked when you add the parameter you did, you can do it like this
                
            
coroutine.resume(co,unpack({os.pullEvent()}))
Posted 16 August 2012 - 02:07 PM
                Ok, this is pretty complex, but I'll try my best to explain it.
First of all, coroutines are not threads, they don't run at the same time. They work like this:
You create a coroutine, then start/resume it and it runs until the function (the one you used to create the coroutine) ends or it yields (using coroutine.yield).
If the function ended (successfully or not) the coroutine is "deleted", and it's status is "dead".
If it yielded, it returns to where you called coroutine.resume, returning all the arguments you passed to the yield call. Now, the coroutine is waiting to continue, wich happens when you call resume again.
Example:
The output should be:
Now, the problem with your function is that the turtle functions yield to wait for a "turtle_response" event, so it returns to where you resumed it, and you never resume it again.
Hope you understand :(/>/>
                
            First of all, coroutines are not threads, they don't run at the same time. They work like this:
You create a coroutine, then start/resume it and it runs until the function (the one you used to create the coroutine) ends or it yields (using coroutine.yield).
If the function ended (successfully or not) the coroutine is "deleted", and it's status is "dead".
If it yielded, it returns to where you called coroutine.resume, returning all the arguments you passed to the yield call. Now, the coroutine is waiting to continue, wich happens when you call resume again.
Example:
local function f1()
  print("Hello")
end
local function f2()
  while true do
	print("Hello!")
	coroutine.yield()
	print("Some other message here...")
  end
end
local co = coroutine.create(f1)
coroutine.resume(co)
co = coroutine.create(f2)
coroutine.resume(co)
print("Hello to you")
sleep(1)
while coroutine.status(co) ~= "dead" do
  coroutine.resume(co)
  sleep(1)
end
The output should be:
Hello
Hello!
Hello to you
Some other message here...
Hello!
Some other message here...
Hello!
Some other message here...
...
Now, the problem with your function is that the turtle functions yield to wait for a "turtle_response" event, so it returns to where you resumed it, and you never resume it again.
Hope you understand :(/>/>
Posted 16 August 2012 - 02:34 PM
                AH!!! finally, I did not know that turtle functions yield :(/>/>  thanks a million
                
            Posted 16 August 2012 - 03:09 PM
                Hang on, I have just realised the obvious.yes but there is a long delay in moving forwards and backwards isn't there? long enough to prevent a yield error and that still doesn't explain why the first function only moves forward
THERE IS NO TURTLE.BACK()
What are you talking about? turtle.back() exists.
Edit: Mobile version is terrible at showing that there is a second page; disregard.
Posted 16 August 2012 - 03:20 PM
                Hmm I wonder if turtle.native.back() yields
                
            Posted 16 August 2012 - 03:22 PM
                I know nothing about the native commands, I saw something about them in the BIOS but didn't investigate
                
            Posted 16 August 2012 - 06:37 PM
                Hmm I wonder if turtle.native.back() yields
Nope!
Posted 17 August 2012 - 04:27 PM
                yeah, it's actually quite interesting, if you make a function that uses native movements to make a turtle move in a loop without any sleep(0.1) and then launch it with the coroutines it works and eventually aborts due to too much time without yielding but the turtle keeps moving, you can shut the turtle down and it keeps moving… very strange right?
                
            Posted 17 August 2012 - 05:48 PM
                haha well IIRC the native commands queue-up so if you call it 10 times it wll move 10 times now since it probably moves slower than once every 0.1 seconds they pile upyeah, it's actually quite interesting, if you make a function that uses native movements to make a turtle move in a loop without any sleep(0.1) and then launch it with the coroutines it works and eventually aborts due to too much time without yielding but the turtle keeps moving, you can shut the turtle down and it keeps moving… very strange right?
interesting that it keeps going even after shutdown though
Posted 17 August 2012 - 05:50 PM
                I might be wrong, but maybe it's because Lua was told to stop, but Java wasn't? I think Java controls the actual position, but Lua interprets the commands?
                
            Posted 17 August 2012 - 05:50 PM
                restart, whatever, it will keep looping no matter what… very strange
                
            Posted 17 August 2012 - 06:41 PM
                It won't loop indefinitely. The queue is 100 elements long - you're not able to put any more tasks in it than there is space. You can save the game and reload, and it will continue where it was.
                
            Posted 01 September 2012 - 10:56 AM
                WAIT A SECOND!!!
Thank you cloudy, you are now my hero lol, with this we can make functions that continue after exit, I will never lose a turtle in a mine again B)/>/> I can't believe I never thought of this
Except stuff that use logic will cause a blocker. And I accidentally deleted your post (who's idea was it to put quote next to delete) - but it's quoted there and I'll restore it when I get to my computer if I can :)/>/>
Posted 01 September 2012 - 11:11 AM
                hahaha, nice one.
you can queue up 100 moves and use an os.pullEvent() to track how far it is (because it returns 'turtle_response' every time it moves) and if you restart then it just stops the GUI but keeps moving, make a loop and your turtle will return, then in the turtle startup you tell it to set a timer for 5 seconds and keep pulling events until the timer expires, if it gets any turtle_response events then it knows it is still moving and can try work out what it going on etc, the awesomeness abounds :)/>/>
                
            you can queue up 100 moves and use an os.pullEvent() to track how far it is (because it returns 'turtle_response' every time it moves) and if you restart then it just stops the GUI but keeps moving, make a loop and your turtle will return, then in the turtle startup you tell it to set a timer for 5 seconds and keep pulling events until the timer expires, if it gets any turtle_response events then it knows it is still moving and can try work out what it going on etc, the awesomeness abounds :)/>/>
 
                 
                 
                 
                 
                 
                 
                