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

Help! Easy for anyone

Started by weareborg2913, 02 February 2014 - 09:33 PM
weareborg2913 #1
Posted 02 February 2014 - 10:33 PM
i just need a program that toggles output on the right and left side + bottom

It should be controlled by buttons on the screen above… (7 long * 3 high)

The sides are for nuke control, the bottom is for emergancy door close
awsmazinggenius #2
Posted 02 February 2014 - 10:58 PM
So, control redstone? If you'd like, you can PM me with some more details and I can code it, but recently a few topics have been moved to General for requests. If you want me to code it, do you want it ASAP with minimal testing, or a nice UI and some testing?
surferpup #3
Posted 02 February 2014 - 11:27 PM
Please see the Redstone Basics tutorial. It will teach you exactly how to do toggle redstone. As for buttons and touch screen, please see responses to this question which will give you the tutorials you need to figure this out. What you are asking is not all that difficult.

This is Ask a Pro, where we answer questions from people who are writing programs, not for where we take program requests. There are dozens of examples of what you are trying to do. You will be much better off learning to code it yourself.
Edited on 02 February 2014 - 10:29 PM
awsmazinggenius #4
Posted 03 February 2014 - 12:12 AM
surferpup, you do make a good point. When I make the program, I planned to add lots of comments for when he does decide to learn. But sometimes it's just easier to get someone else to do it for you.
surferpup #5
Posted 03 February 2014 - 01:28 AM
Here is an outline for you:
  • Establish connections to your monitors using monitor=peripheral.wrap("whateverSide")
  • Draw your buttons using monitor.setCursorPosition(x,y) and monitor.write (check out the term API)
  • turn your redstone.setOutput(side) to on/off for each side you are using according to your needs (if you need bundled cables, there are tutorials on this as well)
  • start your while loop to check for monitor touch events (os.pullEvent("monitor_touch)
  • If the button was pressed, do what you need to do.
  • continue your while loop.
I know it may be easier for you to start with an already created routine, but it is so much more fulfilling to do it yourself.

It is very kind of [member='awsmazinggenius'] to offer to write code to start you out, but you really can do this yourself.
Edited on 03 February 2014 - 12:29 AM
surferpup #6
Posted 03 February 2014 - 03:47 AM
Alright [member='awsmazinggenius']– I'll start. Anyone can add in:

Nuclear Fire Control Program

 
--[[

    Launch Control Program

    Prints four buttons on a monitor.  When pressed, the redstone output 
    assigned to the button will go on for an interval then turn off.

    The user can press the q key in the computer to quit.

    Under these settings, will operate on a single monitor

]]


--[[

    Set local variables

]]

local monitorSide = "top"-- change this to reflect your monitor side
local monitor = peripheral.wrap(monitorSide)

local buttonColorReady = colors.lime
local buttonColorFire = colors.red
local textColorReady = colors.white
local textColorFire = colors.yellow
local buttonWidth = 7
local buttonHeight = 5

local launchInterval = 3 -- how long to keep signal on in seconds

local buttons = {
    [1]={
          label = "Silo1";
          startX = 1;
          startY = 1;
          side = "left"; -- redstone output side
        };
    [2]={
          label = "Silo2";
          startX = 2+buttonWidth;
          startY = 1;
          side = "right"
        };
    [3]={
          label = "Silo3";
          startX = 1;
          startY = 2+buttonHeight;
          side = "back";
        };
    [4]={
          label = "Silo4";
          startX = 2+buttonWidth;
          startY = 2+buttonHeight;
          side = "bottom"
        };
}


--[[

    These are all of the functions

]]

local function drawButton(startX,startY,label,buttonColor,textColor)

    -- draw a single button
    monitor.setBackgroundColor(buttonColor)
    monitor.setTextColor(textColor)

    for i = 1,buttonHeight do
        monitor.setCursorPos(startX,startY+i-1)
        monitor.write(string.rep(" ",buttonWidth))
    end

    -- create button label to be centered on button.
    if #label > buttonWidth-2 then -- restrict label to 13 wide
        label = string.sub(label,1,buttonWidth-2)
    end
    label = string.rep(" ",math.floor((buttonWidth-#label)/2)) .. label
    monitor.setCursorPos(startX,startY + math.floor(buttonHeight/2))
    monitor.write(label)
    monitor.setBackgroundColor (colors.black)
end    


local function allRedstoneOff()
    -- Turn all redstone output off
    for k,v in pairs(rs.getSides()) do
     rs.setOutput(v,false)
    end
end

local function drawAllButtons()
    -- draws all buttons
    monitor.clear()
    monitor.setTextScale(0.5)

    -- cycle through buttons table
    for i = 1,#buttons do
        drawButton(buttons[i].startX,buttons[i].startY,buttons[i].label,buttonColorReady,textColorReady)
    end
end

local function launchMissile(missile)
    -- this function updates button, and turns redstone on/off for the interval
    drawButton(buttons[missile].startX,buttons[missile].startY,"Fire!",buttonColorFire,textColorFire)
    rs.setOutput(buttons[missile].side,true)
    sleep(launchInterval)
    rs.setOutput(buttons[missile].side,false)
    drawButton(buttons[missile].startX,buttons[missile].startY,buttons[missile].label,buttonColorReady,textColorReady)
end


--[[

    This is the main program
]]

monitor.clear()
allRedstoneOff()
drawAllButtons()

-- event loop.  Will listen for react to only
-- monitor_touch and keu events

while true do
    event = {os.pullEvent()}
    if event[1]=="monitor_touch" then  -- monitor was pressed
        for i,v in ipairs(event) do
            print (v)
        end

        -- For each button, test if it was pressed based on x,y of click
        for i = 1, #buttons do
            local startX,startY = buttons[i].startX,buttons[i].startY

            -- this is how to test if button in question was pressed
            if (event[3] >= startX and event[3] <= startX+buttonWidth) and (event[4] >= startY and event[4] <= startY+buttonHeight) then
                launchMissile(i)
                break;
            end
        end

    elseif event[1]=="key" and event[2] == keys.q then -- user wants to quit
        break
    end
end

-- quit program
monitor.clear()
allRedstoneOff()
print ("Done.")


You all can on take explaining it and improving upon it. I realized after I wrote it that it wasn't for launch control, but for door open/close control. No matter. Just modify it as necessary.
Edited on 03 February 2014 - 03:19 AM
surferpup #7
Posted 03 February 2014 - 05:11 AM
Here is the same program, modified as a door control program, precisely as originally specified.




Door Control Program


--[[

	--[[

	Door Control Program

	Prints three buttons on a monitor.  When pressed, the redstone output
	assigned to the button will go on for an interval then turn off,
	or it will toggle depending on settings.

	Redstone settings at startup can be controlled with options
	to leave alone, turn off or turn on.

	The user can press the q key in the computer to quit.

	Under these settings, will operate on a single monitor

]]


--[[

	Set local variables

]]

local monitorSide = "top"-- change this to reflect your monitor side
local monitor = peripheral.wrap(monitorSide)

local buttonColorOn = colors.lime
local buttonColorOff = colors.red
local textColorOn = colors.white
local textColorOff = colors.yellow
local buttonWidth = 13
local buttonHeight = 3
local xMargin = 1


local buttons = {
	[1]={
		  label = "Left";
		  startX = 1 + xMargin;
		  startY = 1;
		  side = "left"; -- redstone output side
		  pulse = true; -- toggle (false) or pulse (true)
		  pulseInterval = 2; -- how long to pulse
		  startState = 0; --redstone start state (-1 leave alone, 0 off, 1 on)
		};
	[2]={
		  label = "Right";
		  startX = 1 + xMargin;
		  startY = 2+buttonHeight;
		  side = "right";
		  pulse = false;
		  pulseInterval = 0;
		  startState = 1;
		};
	[3]={
		  label = "Bottom";
		  startX = 1 + xMargin;
		  startY = 2*(buttonHeight)+3;
		  side = "bottom";
		  pulse = false;
		  pulseInterval = 0;
		  startState = 0;
		};

}

--[[

	These are all of the functions

]]

local function drawButton(startX,startY,label,buttonColor,textColor)

	-- draw a single button
	monitor.setBackgroundColor(buttonColor)
	monitor.setTextColor(textColor)

	for row = 1,buttonHeight do
		monitor.setCursorPos(startX,startY+row-1)
		monitor.write(string.rep(" ",buttonWidth))
	end

	-- create button label to be centered on button.
	if #label > buttonWidth-2 then -- restrict label to 2 less than buttonWidth
		label = string.sub(label,1,buttonWidth-2)
	end
	-- this will center label on button
	monitor.setCursorPos(startX+math.floor((buttonWidth-#label)/2),startY + math.floor(buttonHeight/2))
	monitor.write(label)
	monitor.setBackgroundColor (colors.black)
end	


local function allRedstoneOff()
	-- Turn all redstone output off
	for k,v in pairs(rs.getSides()) do
	 rs.setOutput(v,false)
	end
end

local function updateButton(button)
	if rs.getOutput(buttons[button].side) then
		drawButton(buttons[button].startX,buttons[button].startY,buttons[button].label,buttonColorOn,textColorOn)
	else
		drawButton(buttons[button].startX,buttons[button].startY,buttons[button].label,buttonColorOff,textColorOff)
	end
end


local function drawAllButtons()
	-- draws all buttons
	monitor.clear()
	monitor.setTextScale(0.5)

	-- cycle through buttons table
	for button = 1,#buttons do
		updateButton(button)
	end
end

local function toggle(button)
	-- this function updates button, and turns redstone on/off for the interval
	local side = buttons[button].side;
	local pulse = buttons[button].pulse
	rs.setOutput(side,not rs.getOutput(side))
	updateButton(button)
	if pulse then
		sleep(buttons[button].pulseInterval)
		rs.setOutput(side,(not rs.getOutput(side)))
		updateButton(button)
	end
end

local function checkStates()
	for button = 1, #buttons do
		updateButton(button)
	end
end

local function setStartStates()
	for button = 1, #buttons do
		if buttons[button].startState == 0 then
			rs.setOutput(buttons[button].side,false)
		elseif buttons[button].startState == 1 then
			rs.setOutput(buttons[button].side,true)
		end
	end
end


--[[

	This is the main program
]]


monitor.clear()
setStartStates()
drawAllButtons()

-- event loop.  Will listen for and react to
-- only monitor_touch and key events

while true do
	local event = {os.pullEvent()}
	if event[1]=="monitor_touch" then  -- monitor was pressed

		-- For each button, test if it was pressed based on x,y of click
		for button = 1, #buttons do
			local startX,startY = buttons[button].startX,buttons[button].startY

			-- this is how to test if button in question was pressed
			if (event[3] >= startX and event[3] <= startX+buttonWidth) and (event[4] >= startY and event[4] <= startY+buttonHeight) then
				toggle(button)
				break;
			end
		end

	elseif event[1]=="key" and event[2] == keys.q then -- user wants to quit
		break
	end
end

-- quit program
monitor.clear()
print ("Done.")



Edit:
  • Updated some variable names.
  • Changed event variable to a local variable due to [member='ingie']'s whining (he was correct).
  • Posted Screenshot of my test setup.
  • Corrected typos
  • Used math.floor function to effectively center label on button
Edited on 03 February 2014 - 11:49 AM
awsmazinggenius #8
Posted 03 February 2014 - 09:28 AM
Thanks for writing that surferpup, let's see if it fits his needs.

EDIT: Damn ninja :ph34r:/>
Edited on 03 February 2014 - 06:47 PM
ingie #9
Posted 03 February 2014 - 11:23 AM
Anyone can add in:

challenge accepted ;)/>

1. well done for taking up the banner/ putting your head above the parapet

which leads me onto:

2. ewwwww, you used global variables within in a function….. :o/> … i feel sullied and unusual :)/>

[ but you're excused, due to 1. :)/> ]
surferpup #10
Posted 03 February 2014 - 12:22 PM
2. ewwwww, you used global variables within in a function….. :o/> … i feel sullied and unusual :)/>

Other than the event variable (my bad), there are no globals that I saw. But I might have missed one. Go take a shower, you will feel much better.

Please feel free to expand/explain/improve. After all, this is supposed to be a learning lesson for our friend [member='weareborg2913'] (although for all I know he hasn't seen any of these posts).
Edited on 03 February 2014 - 11:28 AM
ingie #11
Posted 03 February 2014 - 12:29 PM
don't worry, i was only been jocular, not dismissing your code - sorry if you took my tone wrongly

there's several others tho… monitor/buttonheight/buttonwidth - but i'm purely being purist for "comedy" effect … i did hint that my pedantry was irrelevant in the circumstances :)/>
surferpup #12
Posted 03 February 2014 - 12:32 PM
Thanks for writing that surferpup, let's see if it fits his needs.

You are welcome. Care to use it as the basis for a tutorial? This question seems to come up a lot. I tried to incorporate a few good practices in making it (use of tables for buttons, self-documenting code, event loop with both monitor touch and key events tested, local variables – thanks ingie – use of functions, organized code, checking redstone states, allow for both toggle and pulse). All elements of a good door control with touch button tutorial are here.
surferpup #13
Posted 03 February 2014 - 12:37 PM
don't worry, i was only been jocular, not dismissing your code - sorry if you took my tone wrongly

there's several others tho… monitor/buttonheight/buttonwidth - but i'm purely being purist for "comedy" effect … i did hint that my pedantry was irrelevant in the circumstances :)/>

I took you at your tone – that's why I said you were whining in my correction :P/>

For the record, the table buttons is local as are all the keys defined thereunder. Also, the variables monitor, buttonHeight and buttonWidth are declared local. You must have misread the code (shocking, right?). You are absolutely correct in pointing out that it is best practice to use local variables unless a global is absolutely essential. The only global I found was event, and I changed it. Good catch.
ingie #14
Posted 03 February 2014 - 12:45 PM
You are welcome. Care to use it as the basis for a tutorial? This question seems to come up a lot. I tried to incorporate a few good practices in making it (use of tables for buttons, self-documenting code, event loop with both monitor touch and key events tested, local variables – thanks ingie – use of functions, organized code, checking redstone states, allow for both toggle and pulse). All elements of a good door control with touch button tutorial are here.

i'd agree - it's basically what is called a "design pattern" isn't it… or several nice design patterns in one bit of code…
i was looking at some of the tutorials last night and thought how although there's a lot of excellent stuff on how to solve problems and how to use libraries and devices, there's nothing specific on patterns…

the reason i think about this is that i see CC as being as much an educational tool for the "Why Johnny Can't Code" argument, as anything else. So outlining principles of general coding style and pattern is as important as "how to do x" type tutorials. This way when today's teenage CC scripters go out to work, if they choose computing, then they have good discipline in how to recognise the same thing when it crops up again, by its pattern rather than its specific need.

if you get me :)/>

I took you at your tone – that's why I said you were whining in my correction :P/>

good good, i just wanted to make sure :)/>

For the record, the table buttons is local as are all the keys defined thereunder. Also, the variables monitor, buttonHeight and buttonWidth are declared local. You must have misread the code (shocking, right?). You are absolutely correct in pointing out that it is best practice to use local variables unless a global is absolutely essential. The only global I found was event, and I changed it. Good catch.

my bad in my definition, by global, i mean variables scoped outside the function… i.e. not local to the function - which is bad-ish practice for later abstraction. I always pass such variables in as params even if they are local to the main program, as that way it's simpler to extract the functions to a separate API later if needed, without any changes to the method.
Edited on 03 February 2014 - 11:46 AM
surferpup #15
Posted 03 February 2014 - 12:45 PM
if you get me :)/>

I get you. I actually wrote it because I have seen so many of these "Can you help me write a quick touch screen button program?" questions. So, instead of just cranking out a hack, I wanted to show that it is a relatively straightforward program that can be extended if you use good coding practices. Again – basis for a good tutorial…
ingie #16
Posted 03 February 2014 - 01:05 PM
I get you. I actually wrote it because I have seen so many of these "Can you help me write a quick touch screen button program?" questions. So, instead of just cranking out a hack, I wanted to show that it is a relatively straightforward program that can be extended if you use good coding practices. Again – basis for a good tutorial…

aye, totally - i saw your button table and thought: nicely done. i have a similar table for my door proximity opening system - where additionally one of the values of a "door" item is the function by which the trigger is activated - it's things like this that make me love lua for its first-class functional style ability

p.s. sorry, i'd edited my previous post whilst you were typing that to explain what i meant by global variables - i don't like editing after a subsequent post as it can change context… but just so you know :)/>
surferpup #17
Posted 03 February 2014 - 01:16 PM
my bad in my definition, by global, i mean variables scoped outside the function… i.e. not local to the function - which is bad-ish practice for later abstraction. I always pass such variables in as params even if they are local to the main program, as that way it's simpler to extract the functions to a separate API later if needed, without any changes to the method.

There is a fine line between Object-Oriented (OOP) principles and functional programming. Both are possible in Lua, and yes, generally speaking abstraction is good. This code is primarily functional. In the code, I have essentially only two true "abstract" functions – drawButton and allRedstoneOff (which doesn't get called in this program). Other than that, the rest rely at the very least on the buttons table, which I do not pass in. I felt going further on abstraction would make the code less readable for the purposes of a tutorial-like code. As such, they are more like driver-functions.

I have a similar table for my door proximity opening system - where additionally one of the values of a "door" item is the function by which the trigger is activated - it's things like this that make me love lua for its first-class functional style ability

Nice link to the wikipedia article on first-class functions. Did you miss a link to your code demonstrating this?

As for the rest - we're on the same page. No worries :)/>
ingie #18
Posted 03 February 2014 - 01:21 PM
aye, again - totally understood within the realms of the thread :)/> i was just explaining where my pedantry arose from, not criticising your answer - i totally agree with the "do it simply rather than cleverly" in tutorials [ see my similar response to originalbit re: my turtle/AE extracting util in the programs section ]

Did you miss a link to your code demonstrating this?

no, but see my PM ;)/> as i say there, i didn't want to change the thrust of this thread into a general theory debate [ any more than i have already ;)/> ] when it should be about the original Q and your answer/usage of it.
awsmazinggenius #19
Posted 03 February 2014 - 07:49 PM
I think I'll just say I've seen 3 similar topics including this, and surferpup send me and ingie a template for a tutorial. (Just so others know it is happening.)