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

[Lua][Question] Help with security turtle.

Started by Chrisn1995, 02 February 2013 - 02:28 PM
Chrisn1995 #1
Posted 02 February 2013 - 03:28 PM
Title: [Lua][Question]

Hi, I'm trying to make a "turtle security system", however I'm having some difficulty with a program.

On the console I have this:

rednet.open("left")
while true do
rednet.broadcast(read())
end

On the turtle end I have this:

rednet.open("right")
while true do
id, msg == rednet.receive()
if msg == "off" then
turtle.up()
turtle.up()
turtle.up()
end
if msg == "attack" then
turtle.down()
turtle.down()
turtle.down()
while turtle.detectDown ~= true do
turtle.attack()
end
end
end

Basically what it does is, I type "attack" into the console, and the turtle drops down and wails the sword, or I type "off" and the turtle goes back up into it's hiding place. I have only been able to either make it go up, or make it go down and attack, but i haven't been able to make it switch between, given the command. I can only assume that there is some botched code that I'm using, I'm fairly new to this so it wouldn't surprise me. Any suggestions?
Skullblade #2
Posted 02 February 2013 - 03:39 PM
what do you mean switch between?

also u are missing a end in the turtle script to close the while loop
Chrisn1995 #3
Posted 02 February 2013 - 03:42 PM
Sorry, it is there, I had to copy it from hand. In the console, when I type "attack" it goes down and attacks, by "switching", I want to be able to type "off" and make it stop attacking and go up.
Skullblade #4
Posted 02 February 2013 - 03:48 PM
well you could spawn a rednet listener coroutine and once the corouine gets a off the other main part of the code stops…
Chrisn1995 #5
Posted 02 February 2013 - 03:51 PM
Could you give/refer a structure for that, I have only been doing this for a few days now, so my understanding of what you just suggested is limited.
Skullblade #6
Posted 02 February 2013 - 03:53 PM
Honestly i am ok with some parts of CC but one part of CC i am fuzzy with is co routines and i think that my "helping" might do more harm then good :P/>
TheArchitect #7
Posted 02 February 2013 - 03:55 PM
You need to attack AND listen to incoming instructions at the same time. You probably don't even need multithreading.

You can do that by just adding these few lines to your existing code:
rednet.open("right")
while true do
  id, msg == rednet.receive()
  if msg == "off" then
	turtle.up()
	turtle.up()
	turtle.up()
  end
  if msg == "attack" then
	turtle.down()
	turtle.down()
	turtle.down()
	while turtle.detectDown ~= true do
	  turtle.attack()

	  local id, msg = os.pullEvent("rednet_message")	--this will pull a rednet message event
	  if msg=="off" then			--if it's an instruction to turn off, exit the attack loop
		break()
	  end
	
	end
  end
end

(note: this is untested, but if it doesn't work, it's something along these lines)
Skullblade #8
Posted 02 February 2013 - 03:58 PM
You need to attack AND listen to incoming instructions at the same time. You probably don't even need multithreading.

You can do that by just adding these few lines to your existing code:
rednet.open("right")
while true do
  id, msg == rednet.receive()
  if msg == "off" then
	turtle.up()
	turtle.up()
	turtle.up()
  end
  if msg == "attack" then
	turtle.down()
	turtle.down()
	turtle.down()
	while turtle.detectDown ~= true do
	  turtle.attack()

	  local id, msg = os.pullEvent("rednet_message")	--this will pull a rednet message event
	  if msg=="off" then			--if it's an instruction to turn off, exit the attack loop
		break()
	  end
	
	end
  end
end

(note: this is untested, but if it doesn't work, it's something along these lines)
just smacking a rednet event in the middle of the code wont solve anything :blink:/> the program will wait until it gets the rednet message to do anything and unless he tells it to stop it wont ever get a rednet message…
raineth #9
Posted 02 February 2013 - 04:03 PM
If you've never used coroutines before, the easiest thing to do will be to use the Parallel (API) which provides a very simple interface.

Basically, you want to split your code into two functions – one that keeps trying to attack, and one that listens to rednet for commands. If you use parallel.waitForAny, both functions will run in parallel*, so you can have rednet.receive() running (which blocks until a message is received) without holding up your attack loop. There's a short example on the parallel.waitForAny page.

* They don't technically run in parallel – only one coroutine is actually executing at any given time, but from a distance it looks like they're both running at the same time.
Chrisn1995 #10
Posted 02 February 2013 - 04:09 PM
Do you think a "Repeat/Until" loop would be able to work?

repeat
turtle.attack()
until msg == "off"

Or something?

Edit: Will try Raineth
raineth #11
Posted 02 February 2013 - 04:15 PM
You need to attack AND listen to incoming instructions at the same time. You probably don't even need multithreading.

You can do that by just adding these few lines to your existing code:
rednet.open("right")
while true do
  id, msg == rednet.receive()
  if msg == "off" then
	turtle.up()
	turtle.up()
	turtle.up()
  end
  if msg == "attack" then
	turtle.down()
	turtle.down()
	turtle.down()
	while turtle.detectDown ~= true do
	  turtle.attack()

	  local id, msg = os.pullEvent("rednet_message")	--this will pull a rednet message event
	  if msg=="off" then			--if it's an instruction to turn off, exit the attack loop
		break()
	  end
	
	end
  end
end

(note: this is untested, but if it doesn't work, it's something along these lines)

You're right, you could also architect your code this way – but it's a little more work than this because os.pullEvent("rednet_message") will block code execution until a message is received. What you would need to do instead is listen for any event (because you can't listen for more than 1 specific type), filter out the ones you don't care about, and use timers to ensure that there are a steady stream of events (otherwise, your turtle will just sit around waiting when nothing is going on.)

For example:

local timer_id = os.startTimer(0.1) -- wake up in at most 100ms

[... inside your main loop ...]
  local type, p1, p2 = os.pullEvent()
  if type == "rednet_message" then
    [handle your message here]
  elseif type == "timer" and p1 == timer_id then
    timer_id = os.startTimer(0.1) -- start a new timer
  end
  -- attack any time we receieve an event, whether or not it's one we cared about
  turtle.attack()

edit: I forgot that the sender ID is the first value for rednet_message, fixed example.
Chrisn1995 #12
Posted 02 February 2013 - 04:59 PM
Looking over the parallel api, I wasn't able to discern where in the code I should place it, I currently have this:

if msg == "attack" then
while turtle.detectDown ~= true do
paralell.waitForAny(rednet.receive,turtle.attack)
end

if msg == "off" then
turtle.up()
end

Which still manages to punch the crap out of me as I test, but I can't seem to get it to stop with a command.
raineth #13
Posted 02 February 2013 - 05:18 PM
Looking over the parallel api, I wasn't able to discern where in the code I should place it, I currently have this:

if msg == "attack" then
while turtle.detectDown ~= true do
paralell.waitForAny(rednet.receive,turtle.attack)
end

if msg == "off" then
turtle.up()
end

Which still manages to punch the crap out of me as I test, but I can't seem to get it to stop with a command.

In your code, you're calling rednet.receive() but throwing the result away. You're also never doing anything that would break you out of the loop when you receive a message, so the msg == "off" line will never be run. Finally, in your while loop you forgot the parenthesis after turtle.detectDown (you meant turtle.detectDown()), so you're comparing the function itself (rather than its return value) to true – which it never will be.

I hacked up your original code a bit to give you a better example. I haven't tested it, but this should be pretty close to what you're after:
if not rednet.open("right") then
  error("couldn't open rednet!")
end

local attacking = false

function handleMessages()
  while true do
    local id, msg == rednet.receive()
    if msg == "off" then
      turtle.up()
      turtle.up()
      turtle.up()
      attacking = false
    elseif msg == "attack" then
      turtle.down()
      turtle.down()
      turtle.down()
      attacking = true
    elseif msg == "terminate" then
      return -- parallel.waitForAny() will exit when we return
    end
  end
end

function attackLoop()
  while true do
    if attacking then
      turtle.attack()
    else
      os.sleep(1)
    end
  end
end

-- run until either handleMessages() or attackLoop() returns
parallel.waitForAny(handleMessages, attackLoop)
Chrisn1995 #14
Posted 03 February 2013 - 06:17 AM
Works great, thanks a bunch.