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)
is the function to create a new Threadcoroutine.resume(thread)
resumes/startes a thread,coroutine.state(thread)
returns the state of the coroutine (see the manual)coroutine.yield(string)
stops the actual Thread and waites for a eventThe 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"))
then when you say
coroutine.resume(mycoroutine)
it prints 'test'?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)
it moved forwards and ended successfullyand
co=coroutine.create(mover)
coroutine.resume(co)
just returned success and did nothingPosted 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
I haven't tried the second method but it's something I thought aboutPosted 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
then call mover() and the turtle will go back and forth until you terminate it, then try
test=coroutine.create(mover)
and resume test, all you will get back as output will be
true
turtle_response
and it will not move at allI 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()}))
does anyone know why?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
Now, what will happen here is that the first function runs successfully and ends, so it returns. Then, the second function will yield, going back to the resume call, so you'll see the "Hello to you" message, and then it is run in a loop that keeps resuming it until it's dead (wich will never happen). When it's resumed, it will continue where it yielded and print the second message.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 :)/>/>