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

Coroutine help

Started by Thegameboy, 10 April 2014 - 03:01 PM
Thegameboy #1
Posted 10 April 2014 - 05:01 PM
Hi. I'm making a program that needs to somehow detect key presses and start and respond to a timer at the same time, or something very close. I understand the parallel API exists but that can't help me; it either uses waitForAll or waitForAny. Basically I need two functions that don't wait for each other and don't end the other once they're done.

So I guess coroutines may be the only thing left relevant. I've been doing much Google searching but to no avail-can someone help me out?
apemanzilla #2
Posted 10 April 2014 - 05:16 PM
Hi. I'm making a program that needs to somehow detect key presses and start and respond to a timer at the same time, or something very close. I understand the parallel API exists but that can't help me; it either uses waitForAll or waitForAny. Basically I need two functions that don't wait for each other and don't end the other once they're done.

So I guess coroutines may be the only thing left relevant. I've been doing much Google searching but to no avail-can someone help me out?
From what you said, it sounds like parallel.waitForAll would work here. What do you mean by not wanting them to wait for each other? It's difficult to run the main thread along a coroutine, it's far easier to run two or more coroutines in parallel.
Thegameboy #3
Posted 10 April 2014 - 05:24 PM
Hi. I'm making a program that needs to somehow detect key presses and start and respond to a timer at the same time, or something very close. I understand the parallel API exists but that can't help me; it either uses waitForAll or waitForAny. Basically I need two functions that don't wait for each other and don't end the other once they're done.

So I guess coroutines may be the only thing left relevant. I've been doing much Google searching but to no avail-can someone help me out?
From what you said, it sounds like parallel.waitForAll would work here. What do you mean by not wanting them to wait for each other? It's difficult to run the main thread along a coroutine, it's far easier to run two or more coroutines in parallel.


No, if I were to use parallel.waitForAll then I would have to wait until the timer was done before it could read my next key. And that's what I meant by not wanting them to wait for each other.
Lyqyd #4
Posted 10 April 2014 - 05:32 PM
That's really not how the parallel API works. Please post your current code so we can help you fix it to work correctly. You may not need anything more than a simple event handling loop, depending on what you're trying to do.
Thegameboy #5
Posted 10 April 2014 - 06:21 PM
That's really not how the parallel API works. Please post your current code so we can help you fix it to work correctly. You may not need anything more than a simple event handling loop, depending on what you're trying to do.


I'm trying to implement a gravity and movement system. The problem is that you can only move 1 pixel in midair per pixel it falls. Here's the whole code:

x,y = 10,3
--first number is x, next is y
wallpos = {10,15,11,15,12,15,13,15,14,15,15,15,16,15,17,15,18,15,19,15,20,15,12,17}
local minmomentum = .5
local maxmomentum = .1
local momentum = .5
local onGround = false

term.setBackgroundColor(2048)
term.clear()

while true do

onGround = false

parallel.waitForAll(
function ()
  newy,newx = 0,0
  local possibleStopGravity = false

  event,key = os.pullEvent()

  for i=-1,#wallpos,2 do
	if (event == "key" and key == 203) then
	  if (x-1 ~= wallpos[i] or y ~= wallpos[i+1]) then
		paintutils.drawPixel(x,y,2048)
		newx = -1
		if (y+1 == wallpos[i+1] and newx+x == wallpos[i]) then
		  possibleStopGravity = true
		end
		if (onGround == false) then
		  paintutils.drawPixel(x-1,y,16)
		end
	  else
		newx = 0
		possibleStopGravity = false
		break
	  end
	end
	if (event == "key" and key == 205) then
	  if (x+1 ~= wallpos[i] or y ~= wallpos[i+1]) then
		paintutils.drawPixel(x,y,2048)
		newx = 1
		if (y+1 == wallpos[i+1] and newx+x == wallpos[i]) then
		  possibleStopGravity = true
		end
		if (onGround == false) then
		  paintutils.drawPixel(x+1,y,16)
		end
	  else
		newx = 0
		possibleStopGravity = false
		break
	  end
	end
	--if (event == "key" and key == 208) then
	  --if (y+1 ~= wallpos[i+1] or x ~= wallpos[i]) then
		--paintutils.drawPixel(x,y,2048)
		--newy = 1
	  --else
		--newy = 0
		--redstone.setOutput("left", true)
		--break
	  --end
	--end
	if (event == "key" and key == 200) then
	  if (onGround == true) then
		paintutils.drawPixel(x,y,2048)
		newy = -3
	  end
	end
  end

  x = x+newx
  y = y+newy

  if (possibleStopGravity == true) then
	onGround = true
  end

end,
function ()

  for i=0,#wallpos,2 do
	
	if (y+1 == wallpos[i] and x == wallpos[i-1]) then
	  onGround = true
	  momentum = minmomentum
	  
	  break
	end
  end

  if (onGround == false) then
	local timer = os.startTimer(momentum)
	
	while true do
	  local event,timerID = os.pullEvent("timer")
	  
	  if (timerID == timer) then
		if not onGround then
		  paintutils.drawPixel(x,y,2048)
   	   y = y+1
		  
		  if (momentum - .1 >= maxmomentum) then
	   	 momentum = momentum - .1
		  end
		  break
		else
		  momentum = minmomentum
		  break
		end
	  end
	end
  end
end
)

paintutils.drawPixel(x,y,16)
for i=1,#wallpos,2 do
  paintutils.drawPixel(wallpos[i],wallpos[i+1],128)
end
end
Edited on 10 April 2014 - 04:45 PM
Bomb Bloke #6
Posted 10 April 2014 - 11:39 PM
Your problem boils down to a lack of loops. In your timer checking function, you use a while loop to check timer after timer after timer - that's fine.

But in your key checking function, you check one event and that's it. The function's finished, and won't restart until parallel.waitForAll() is called again. I suspect what you want to do is stick a "while true do" loop in that function (so that it can't finish on its own), and switch to parallel.waitForAny().

Or, you could merge your event checks using code along the lines of:

  event,arg1 = os.pullEvent()

  if event == "key" then
    for i=-1,#wallpos,2 do
      if arg1 == 203 then
        ***etc***
      end
    end
  elseif event == "timer" and not onGround then
    if arg1 == timer then
      if not onGround then
        ***etc***
      end
    end
  end
Thegameboy #7
Posted 11 April 2014 - 04:17 PM
Your problem boils down to a lack of loops. In your timer checking function, you use a while loop to check timer after timer after timer - that's fine.

But in your key checking function, you check one event and that's it. The function's finished, and won't restart until parallel.waitForAll() is called again. I suspect what you want to do is stick a "while true do" loop in that function (so that it can't finish on its own), and switch to parallel.waitForAny().

Or, you could merge your event checks using code along the lines of:

  event,arg1 = os.pullEvent()

  if event == "key" then
	for i=-1,#wallpos,2 do
	  if arg1 == 203 then
		***etc***
	  end
	end
  elseif event == "timer" and not onGround then
	if arg1 == timer then
	  if not onGround then
		***etc***
	  end
	end
  end


Wow, I can't believe I didn't catch that. Thanks. But how do you actually multitask (like multiple os.pullEvents at a time) without the parallel API? Because I know that you can't pull an os.pullEvent in a coroutine because it's practically the exact same thing as a coroutine.yield(). I could look in the actual code of the parallel API but it looks a bit daunting and I'll do that as a last resort. I want to know because I don't want to always have to rely on the API. Any suggestions?
CometWolf #8
Posted 11 April 2014 - 05:24 PM
You do what bomb suggested, you pull all the events with the same pullEvent, then use if statements to react accordingly to which event is fired. What parallel does is basically the same thing. It handles all the events with it's own pullEvent, then passes every event onto each parallel function in the order they're defined. Meaning it will pass the event to the first function, wait for it to yield, then do the same to the second. It's obviously going to be more efficent for you to just create your own pullEvent loop.
Bomb Bloke #9
Posted 12 April 2014 - 12:40 AM
Indeed. That code extract I posted is intended to let you ditch the parallel API (and any thought of co-routines) entirely.

Pull an event (any event), if it's one of the ones you want, act on it, if it's not, ignore it. Loop as desired.
Thegameboy #10
Posted 12 April 2014 - 05:49 PM
You do what bomb suggested, you pull all the events with the same pullEvent, then use if statements to react accordingly to which event is fired. What parallel does is basically the same thing. It handles all the events with it's own pullEvent, then passes every event onto each parallel function in the order they're defined. Meaning it will pass the event to the first function, wait for it to yield, then do the same to the second. It's obviously going to be more efficent for you to just create your own pullEvent loop.

Indeed. That code extract I posted is intended to let you ditch the parallel API (and any thought of co-routines) entirely.

Pull an event (any event), if it's one of the ones you want, act on it, if it's not, ignore it. Loop as desired.


Thanks for the help everyone.