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

Button press to toggle a boolean variable?

Started by shournk, 20 May 2014 - 11:34 PM
shournk #1
Posted 21 May 2014 - 01:34 AM
I am trying to set up a computer so that when it receives a *specific* momentary redstone signal, it toggles a variable as true/false. I have a table of variables



local states = {
room1=false,
room2=false,
room3=false
}

and each variable should be toggled upon receiving a redstone signal from respective buttons (using rednet colors).



Everything I found online pointed me in the direction of using os.pullEvent("redstone"), but that is triggered by any redstone event, not just the one for the button. How do I do this?
mistamadd001 #2
Posted 21 May 2014 - 03:02 AM
use the os.pullEvent("redstone") to trigger an if statement for each toggle eg.


if os.pullEvent("redstone") then
  bundledColor = rs.getBundledInput("side")
  if colors.test(bundledColor, colors.white) == true then -- make the button for room1 attach to a white cable
	if room1 == true then room1 = false
	elseif room1 == false then room1 = true
	end
  elseif colors.test(bundledColor, colors.orange) == true then -- make the button for room2 attach to an orange cable
	if room2 == true then room2 = false
	elseif room2 == false then room2 = true
	end
  elseif colors.test(bundledColor, colors.magenta) == true then -- make the button for room3 attach to a magenta cable
	if room3 == true then room3 = false
	elseif room3 == false then room3 = true
	end
  else
	print("Unrecognised input detected") -- if the system registers any other form of redstone input (either normal or bundled) it will print this
	sleep(3)
  end
end

I think that's right, I've not worked with bundled cable on CC before
Edited on 21 May 2014 - 01:06 AM
gezepi #3
Posted 21 May 2014 - 05:14 AM
Another way to toggle boolean values is with not.

local room1 = true
--room1 is true
room1 = not room1
--room1 is false
room1 = not room1
--room1 is  true again
mistamadd001 #4
Posted 21 May 2014 - 05:21 AM
Another way to toggle boolean values is with not.

local room1 = true
--room1 is true
room1 = not room1
--room1 is false
room1 = not room1
--room1 is  true again

That makes my if then end look a lot nicer lol, I literally read the wiki on the redstone API and wrote the code from that, people need to remember that the Wiki is there, I've solved so many of my own questions using it. I use it almost daily, while I'm still learning, otherwise I look at other peoples code for the info I need, I just taught myself how to write a pastebin style downloader by reading the pastebin program haha
theoriginalbit #5
Posted 21 May 2014 - 09:10 AM
Spoileruse the os.pullEvent("redstone") to trigger an if statement for each toggle eg.

if os.pullEvent("redstone") then
  bundledColor = rs.getBundledInput("side")
  if colors.test(bundledColor, colors.white) == true then -- make the button for room1 attach to a white cable
	if room1 == true then room1 = false
	elseif room1 == false then room1 = true
	end
  elseif colors.test(bundledColor, colors.orange) == true then -- make the button for room2 attach to an orange cable
	if room2 == true then room2 = false
	elseif room2 == false then room2 = true
	end
  elseif colors.test(bundledColor, colors.magenta) == true then -- make the button for room3 attach to a magenta cable
	if room3 == true then room3 = false
	elseif room3 == false then room3 = true
	end
  else
	print("Unrecognised input detected") -- if the system registers any other form of redstone input (either normal or bundled) it will print this
	sleep(3)
  end
end
some improvements to help you with your coding: you don't need to put the os.pullEvent in the if statement, it will wait there until a redstone event happens. you also don't need all the == true the test function will return a boolean, there is no need to test if true is true (always true) or false is true (always false) just use the original return value. then there's the improvement gezepi suggested. lastly when using os.pullEvent, os.pullEventRaw, or coroutine.yield you don't need to use a sleep, actually its suggested you don't otherwise you might miss events like another redstone pulse or the such! there's a few other improvements you could use such as a ternary operator (well Lua's version of it), but that's starting to get to the point of more than needed for such a simple use case.
Edited on 21 May 2014 - 07:10 AM
shournk #6
Posted 21 May 2014 - 01:42 PM
Instead of doing if-elseif-elseif-elseif for every single input possibility, I'm trying to use a loop to grab from a table. Do I have the right idea here? And I keep getting a "colors:36: not enough arguments" error. What am I doing wrong?

http://pastebin.com/iBUEPj1B
CometWolf #7
Posted 21 May 2014 - 02:33 PM
I suppose you could if you really wanted to

local tRooms = {
  {
    color = "white",
    state = false
  },
  {
    color = "orange",
    state = false
  },
  {
    color = "magenta",
    state = false
  }
}
while true do
  os.pullEvent"redstone"
  local bundleColor = rs.getBundledInput"side"
  for _i,room in ipairs(tRooms) do
    if colors.test(bundleColor,colors[room.color]) then
	  room.state = not room.state
	  break
    end
  end
end

Your error is the result of you using pairs the wrong way

    for mechanism,mSignal in pairs(mechanismSignal) do
	    for _,mState in pairs(states) do
		    cycle(mechanismSignal[mechanism], mechanismSignal[mSignal], states[mState])

Lemme break down the syntax of a pairs loop for you

for tableKey,tableValue in pairs(table) do
  --the local tableKey here contains the table key holding the table value, which is stored in tableValue

So, what you're effectively doing here

    for mechanism,mSignal in pairs(mechanismSignal) do
	    for _,mState in pairs(states) do
		    cycle(mechanismSignal[mechanism], mechanismSignal[mSignal], states[mState])  --the first argument here is correct, though redundant. The second one is whatever's stored under the key of what mechanismSignal has as it's value under the key mechanism. Same goes for the thrid

It should instead be

    for mechanism,mSignal in pairs(mechanismSignal) do
	    for _,mState in pairs(states) do
		    cycle(mSignal, mSignal, mState)

Also, you don't appear to even be using the first argument (mechanism) in the cycle function.
theoriginalbit #8
Posted 21 May 2014 - 02:36 PM
Also, you don't appear to even be using the first argument (mechanism) in the cycle function.
yes they are, even you quoted them using it… lol.
So, what you're effectively doing here

for mechanism,mSignal in pairs(mechanismSignal) do
  for _,mState in pairs(states) do
    cycle(mechanismSignal[mechanism], mechanismSignal[mSignal], states[mState])
CometWolf #9
Posted 21 May 2014 - 05:53 PM
I believe you misunderstood, im refering to the actual function. Yes he's passing it to the function, but it's not being used within it.

local function cycle(mechanism, mSignal, mState)
	    print(rs.getBundledInput(rSide))
	    currentInput = rs.getBundledInput(rSide)
print(input)
print(mSignal)
	    if checkColor(currentInput, mSignal) then
		    if states[mState] then
			    states[mState] = false
print(states[mState].."now false")
		    elseif not states[mState] then
			    states[mState] = true
print(states[mState].."now true")
		    end
	    end
    end
shournk #10
Posted 21 May 2014 - 07:43 PM
I suppose you could if you really wanted to

local tRooms = {
  {
	color = "white",
	state = false
  },
  {
	color = "orange",
	state = false
  },
  {
	color = "magenta",
	state = false
  }
}
while true do
  os.pullEvent"redstone"
  local bundleColor = rs.getBundledInput"side"
  for _i,room in ipairs(tRooms) do
	if colors.test(bundleColor,colors[room.color]) then
	  room.state = not room.state
	  break
	end
  end
end

Your error is the result of you using pairs the wrong way

	for mechanism,mSignal in pairs(mechanismSignal) do
		for _,mState in pairs(states) do
			cycle(mechanismSignal[mechanism], mechanismSignal[mSignal], states[mState])

Lemme break down the syntax of a pairs loop for you

for tableKey,tableValue in pairs(table) do
  --the local tableKey here contains the table key holding the table value, which is stored in tableValue

So, what you're effectively doing here

	for mechanism,mSignal in pairs(mechanismSignal) do
		for _,mState in pairs(states) do
			cycle(mechanismSignal[mechanism], mechanismSignal[mSignal], states[mState])  --the first argument here is correct, though redundant. The second one is whatever's stored under the key of what mechanismSignal has as it's value under the key mechanism. Same goes for the thrid

It should instead be

	for mechanism,mSignal in pairs(mechanismSignal) do
		for _,mState in pairs(states) do
			cycle(mSignal, mSignal, mState)

Also, you don't appear to even be using the first argument (mechanism) in the cycle function.

Thank you! I'm using your loop instead, and I'm able to have the signal output properly. I've added a second for loop within the while loop that outputs a signal if the boolean is true.
The problem is that the first for loop sets the state to truefalsetruefalsetruefalse for every iteration (which I know from adding a print at the end) and it ends on false, causing the output to turn off when the first for loop is done. What is causing this, and how can I make it only change once?

http://pastebin.com/vNDXX1ur

EDIT: I now know that it's a problem with the os.pullEvent"redstone" detecting when the redstone turns on, then again when it turns off. I know because I added a sleep(0.9) right after the pullEvent, so that it waits till the second event (turn off) passes before continuing. Is there a cleaner way to do this?
Edited on 21 May 2014 - 06:19 PM
CometWolf #11
Posted 21 May 2014 - 08:48 PM
Note the difference between yours and mine

	    if door.state then
		    if not signalCheck(rSide,door.mechanism) then
			    enableWire(rSide,door.mechanism)
		    end
	    elseif not door.state then
		    if signalCheck(rSide,door.mechanism) then
			    disableWire(rSide,door.mechanism)
		    end
	    end


	    if colors.test(bundleColor,colors[room.color]) then
		  room.state = not room.state
		  break
	    end

Yours act based on the current state aswell as the actual signal, whilst mine simply acts on a positive redstone signal. Although mine would mess up if you pressed another button before the first button lets up, so the sleep honestly isn't a bad idea. However, a slight variation enabling quick button presses might be more desireable.

local sleep = function(time)
  local timer = os.startTimer(time)
  while true do
    local tEvent = os.pullEvent()
    if tEvent[1] == "redstone" then
	  queue = true
    elseif tEvent[1] == "timer" and tEvent[2] == timer then
	  break
    end
  end
  if queue then
    os.queueEvent"redstone"
  end
end
This would make it retrigger any redstone events that occured during the sleep, so provided the first button is now off, and the second is still on, it should work as desired.
MR_nesquick #12
Posted 21 May 2014 - 10:56 PM

local states = {

room1= {false,colors.white},
room2=  {false,colors.orange},
room3=  {false,colors.magenta},
}

function toggle(col)
for k,v in pairs(states) do
  if col == k[2] and k[1] == true then
	k[1] = false
   elseif col == k[2] and k[1] == false then
	k[1] = true
  end
end
end


toggle(rs.getBundledInput"side")
Edited on 21 May 2014 - 08:58 PM
shournk #13
Posted 21 May 2014 - 11:27 PM
SpoilerNote the difference between yours and mine

		if door.state then
			if not signalCheck(rSide,door.mechanism) then
				enableWire(rSide,door.mechanism)
			end
		elseif not door.state then
			if signalCheck(rSide,door.mechanism) then
				disableWire(rSide,door.mechanism)
			end
		end


		if colors.test(bundleColor,colors[room.color]) then
		  room.state = not room.state
		  break
		end

Yours act based on the current state aswell as the actual signal, whilst mine simply acts on a positive redstone signal. Although mine would mess up if you pressed another button before the first button lets up, so the sleep honestly isn't a bad idea. However, a slight variation enabling quick button presses might be more desireable.

local sleep = function(time)
  local timer = os.startTimer(time)
  while true do
	local tEvent = os.pullEvent()
	if tEvent[1] == "redstone" then
	  queue = true
	elseif tEvent[1] == "timer" and tEvent[2] == timer then
	  break
	end
  end
  if queue then
	os.queueEvent"redstone"
  end
end
This would make it retrigger any redstone events that occured during the sleep, so provided the first button is now off, and the second is still on, it should work as desired.

Thank you so much! Final question: How can I change the value of one of these nested tables? I tried naming each of them (as seen here) but now the code doesn't work. For example, I want to be able to do


tDoors[nameOfSubTable][othervalue] = true



Final question: How can I change the value of one of these nested tables? I tried naming each of them (as seen here) but now the code doesn't work. For example, I want to be able to do


tDoors[nameOfSubTable][othervalue] = true
Edited on 21 May 2014 - 09:28 PM
CometWolf #14
Posted 21 May 2014 - 11:38 PM
It's because i used iPairs instead of just pairs. iPairs requires a numerically indexed table while pairs will work on any table. Since your table is indexed with strings, you need to use pairs.
Edited on 21 May 2014 - 09:38 PM
shournk #15
Posted 22 May 2014 - 12:01 AM
It's because i used iPairs instead of just pairs. iPairs requires a numerically indexed table while pairs will work on any table. Since your table is indexed with strings, you need to use pairs.

I actually had that thought, but I forgot I was using two for loops with ipairs. Thank you!