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

TitanOS

Started by Czarified, 16 March 2013 - 09:59 AM
Czarified #1
Posted 16 March 2013 - 10:59 AM
First of all, this isn't an actual OS. My friends and I are building a replica of Titan Tower (from the teen titans show) in our world and I wanted a touchscreen computer system on the first floor to welcome guests and explain all the amenities offered and how to use them. Right now I've got what I'll call a working prototype. I have a logo that displays (almost) properly, a menu that will launch a customized screen for each option (which will most definitely be modified later on), and a back and home button.

My question is simply optimization. I want to know if this programs runs as efficiently as possible. One thing I've noticed is that after pressing the home button, I have to terminate the program multiple times in the terminal to end it. I don't know what's causing this, but I assume it's my home() function.


Anyway here's the pastebin link:

http://pastebin.com/gf9ft4pu

And the code:
Spoiler
--Monitor Setup

local mon = peripheral.wrap("right")
mon.setTextScale(1)
mon.setTextColor(colors.white)
local button={}
mon.setBackgroundColor(colors.black)

--Variable Definition

buttonPush = false
option = 0


--Functions

function home()
shell.run("TitanOS")
end

function setTable(name, func, xmin, xmax, ymin, ymax)
   button[name] = {}
   button[name]["func"] = func
   button[name]["active"] = true
   button[name]["xmin"] = xmin
   button[name]["ymin"] = ymin
   button[name]["xmax"] = xmax
   button[name]["ymax"] = ymax
end


function fill(text, color, bData)
   mon.setBackgroundColor(color)
   local yspot = math.floor((bData["ymin"] + bData["ymax"]) /2)
   local xspot = math.floor((bData["xmax"] - bData["xmin"] - string.len(text)) /2) +1
   for j = bData["ymin"], bData["ymax"] do
	  mon.setCursorPos(bData["xmin"], j)
	  if j == yspot then
		 for k = 0, bData["xmax"] - bData["xmin"] - string.len(text) +1 do
			if k == xspot then
			   mon.write(text)
			else
			   mon.write(" ")
			end
		 end
	  else
		 for i = bData["xmin"], bData["xmax"] do
			mon.write(" ")
		 end
	  end
   end
   mon.setBackgroundColor(colors.black)
end


function screen()
   local currColor
   for name,data in pairs(button) do
	  local on = data["active"]
	  if on == true then currColor = colors.lime else currColor = colors.red end
	  fill(name, currColor, data)
   end
end


function checkxy(x, y)
   for name, data in pairs(button) do
	  if y>=data["ymin"] and  y <= data["ymax"] then
		 if x>=data["xmin"] and x<= data["xmax"] then
			data["func"]()
			data["active"] = not data["active"]
			print(name)
		 end
	  end
   end
end


function heading(text,a)
   w, h = mon.getSize()
   mon.setCursorPos((w-string.len(text))/2+1,a)
   mon.write(text)
end


function dummy()
buttonPush = not buttonPush
end

function menu1()
if buttonPush == true then
dummy()
end
button = {}
mon.clear()
sleep(.25)
sleep(.25)
heading("Please follow the On Screen Navigation Below",1)
while buttonPush == false do
setTable("Option1", one, 5, 15, 3, 5)
setTable("Option2", two, 5, 15, 7, 9)
setTable("Option3", three, 5, 15, 11, 13)
screen()
local e, side, x,y = os.pullEvent("monitor_touch")
print(x..":"..y)
checkxy(x, y)
sleep(.1)
end
end

function menu2()
if buttonPush == true do
dummy()
end
button = {}
mon.clear()
sleep(.25)
while buttonPush == false then
heading("Button ",1)
mon.write(option.." was pressed.")
setTable("Back", menu1, 50, 55, 17, 19)
setTable("Home", home, 2, 6, 16, 18)
screen()
local e, side, x, y = os.pullEvent("monitor_touch")
print(x..":"..y)
checkxy(x, y)
sleep(.1)
end
end

function one()
dummy()
option = 1
menu2()
end


function two()
dummy()
option = 2
menu2()
end

function three()
dummy()
option = 3
menu2()
end

--ASCII Variables

local Logo = {
  [[		   _  __									  _			 ___		  ]],
  [[   o O O  | |/ /	___	_ __	  _ _   __ _	__| |	___	/ __|	___  ]],
  [[  o	   | ' <	/ _ \  | '  \	| '_| / _' |  / _' |   / -_)  ( |__	/ _ \ ]],
  [[ TS__[O]  |_|\_\   \___/  |_|_|_|  _|_|_  \__,_|  \__,_|   \___|   \___|   \___/ ]],
  [[{======|__|"""""|_|"""""|_|"""""|_|"""""|_|"""""|_|"""""|_|"""""| |"""""|_|"""""|]],
  [[/o--000'""'-0-0-'"'-0-0-'"'-0-0-'"'-0-0-'"'-0-0-'"'-0-0-'"'-0-0-'.'-0-0-'"'-0-0-']],
}

--Program
--------------

--Opening Screen
while buttonPush == false do
mon.clear()
mon.setTextScale(1)
term.redirect(mon)
term.clear()
term.setCursorPos(1,1)
for i = 1, #Logo do
print(Logo[i])
end
heading("Welcome to Titan Tower!",12)
heading("A Komrade Co. Enterprise.",13)
term.restore()
   setTable("Press to Begin", dummy, 30, 50, 15, 17)
   screen()
   local e,side,x,y = os.pullEvent("monitor_touch")
   print(x..":"..y)
   checkxy(x,y)
   sleep(.1)
end

menu1()

menu2()


With minor changes and more comments added. Home function modified to reboot computer (program set as startup).
Spoiler

--Monitor Setup
local mon = peripheral.wrap("right")  --Monitor Wrap
mon.setTextScale(1)
mon.setTextColor(colors.white)
mon.setBackgroundColor(colors.black)
  
--Variable Definition
buttonPush = false
option = 0
local button={}

--Functions
function home()
os.reboot()					   --Program is set as startup
end
 
function setTable(name, func, xmin, xmax, ymin, ymax)
   button[name] = {}
   button[name]["func"] = func	    --Function to be called when button is pressed.
   button[name]["active"] = true	  --Defualt active state.
   button[name]["xmin"] = xmin
   button[name]["ymin"] = ymin
   button[name]["xmax"] = xmax
   button[name]["ymax"] = ymax
end
  
function fill(text, color, bData)	 --Centers button text, colors button according to state
   mon.setBackgroundColor(color)
   local yspot = math.floor((bData["ymin"] + bData["ymax"]) /2)
   local xspot = math.floor((bData["xmax"] - bData["xmin"] - #text) /2) +1
   for j = bData["ymin"], bData["ymax"] do
	  mon.setCursorPos(bData["xmin"], j)
	  if j == yspot then
		 for k = 0, bData["xmax"] - bData["xmin"] - #text +1 do
		    if k == xspot then
			   mon.write(text)
		    else
			   mon.write(" ")
		    end
		 end
	  else
		 for i = bData["xmin"], bData["xmax"] do
		    mon.write(" ")
		 end
	  end
   end
   mon.setBackgroundColor(colors.black)
end
	
function screen()					 --Sets color according to state
   local currColor
   for name,data in pairs(button) do
	  local on = data["active"]
	  if on == true then currColor = colors.lime else currColor = colors.red end
	  fill(name, currColor, data)
   end
end
	
function checkxy(x, y)			    --Prints x and y location of touch, calls function from the button.
   for name, data in pairs(button) do
	  if y>=data["ymin"] and  y <= data["ymax"] then
		 if x>=data["xmin"] and x<= data["xmax"] then
		    data["func"]()
		    data["active"] = not data["active"]
		    print(name)
		 end
	  end
   end
end
	
function heading(text,a)			  --Centers given text on given line
   w, h = mon.getSize()
   mon.setCursorPos((w-string.len(text))/2+1,a)
   mon.write(text)
end

function dummy()					  --Toggles buttonPush and allows exit of the while loop
buttonPush = not buttonPush	   --without needing a break command.
end
function menu1()					  --Main Menu containing 3 button options
if buttonPush == true then	    --Makes sure that every time you call menu1(),
  dummy()					   --the buttons will be drawn.
end
button = {}					   --Clears the button table so only new buttons will,
mon.clear()					   --be displayed.
sleep(.25)
heading("Please follow the On Screen Navigation Below",1)
while buttonPush == false do
  setTable("Option1", one, 5, 15, 3, 5)
  setTable("Option2", two, 5, 15, 7, 9)
  setTable("Option3", three, 5, 15, 11, 13)
  screen()
  local e, side, x,y = os.pullEvent("monitor_touch")
  print(x..":"..y)
  checkxy(x, y)				 --Calls function from selected button
  sleep(.1)
end
end
function menu2()					  --New menu telling you which button was pressed.
if buttonPush == true do		  --Makes sure buttons will always be drawn
  dummy()
end
button = {}					   --Resetting button table again.
mon.clear()
sleep(.25)
while buttonPush == false then
  heading("Button ",1)				   --Tells you which button was
  mon.write(option.." was pressed.")	 --pressed.
  setTable("Back", menu1, 50, 55, 17, 19)
  setTable("Home", home, 2, 6, 16, 18)
  screen()
  local e, side, x, y = os.pullEvent("monitor_touch")
  print(x..":"..y)
  checkxy(x, y)
  sleep(.1)
end
end
function one()					    --Toggles buttonPush and sets option.
dummy()
option = 1
menu2()
end

function two()					    --Same as one
dummy()
option = 2
menu2()
end
function three()					  --Same as one and two
dummy()
option = 3
menu2()
end
--ASCII Variables
local Logo = {
  [[		   _  __									  _			 ___		  ]],
  [[   o O O  | |/ /    ___    _ __	  _ _   __ _    __| |    ___    / __|    ___  ]],
  [[  o	   | ' <    / _ \  | '  \    | '_| / _' |  / _' |   / -_)  | (__    / _ \ ]],
  [[ TS__[O]  |_|\_\   \___/  |_|_|_|  _|_|_  \__,_|  \__,_|   \___|   \___|   \___/ ]],
  [[{======|__|"""""|_|"""""|_|"""""|_|"""""|_|"""""|_|"""""|_|"""""| |"""""|_|"""""|]],
  [[/o--000'""'-0-0-'"'-0-0-'"'-0-0-'"'-0-0-'"'-0-0-'"'-0-0-'"'-0-0-'.'-0-0-'"'-0-0-']],
}
--Program

--Home Screen
while buttonPush == false do
mon.clear()
mon.setTextScale(1)
term.redirect(mon)
term.clear()
term.setCursorPos(1,1)
for i = 1, #Logo do			   --Printing ASCII Logo
  print(Logo[i])
end
heading("Welcome to Titan Tower!",12)
heading("A Komrade Co. Enterprise.",13)
term.restore()
   setTable("Press to Begin", dummy, 30, 50, 15, 17)
   screen()
   local e,side,x,y = os.pullEvent("monitor_touch")
   print(x..":"..y)
   checkxy(x,y)
   sleep(.1)
end
menu1()
menu2()


Note: Original touchsreen button code is from Direwolf20. I've modified it to fit my needs, but the original code is not mine.
Edited on 16 March 2013 - 03:18 PM
Engineer #2
Posted 16 March 2013 - 12:21 PM


function setTable(name, func, xmin, xmax, ymin, ymax)
   button[name] = {}
   button[name]["func"] = func
   button[name]["active"] = true
   button[name]["xmin"] = xmin
   button[name]["ymin"] = ymin
   button[name]["xmax"] = xmax
   button[name]["ymax"] = ymax
end

Why the direwolf way? Maybe you use the func argument for some reason, but I cant figure out why.
In my opinion the direwolf way is kinda bad, why just dont use regular tables?

Maybe you should check my tutorial out! (Yes, this is a little bit promotion)
Czarified #3
Posted 16 March 2013 - 02:26 PM
Well I'm using the dw20 method simply because he was the first to introduce me to the topic and I'm very familiar with the way his method works. I'll check out your tutorial. Also, the "func" argument is basically the core of my program. Every time I click a button, it launches the function defined in that cell (found in the checkxy() function)
Engineer #4
Posted 16 March 2013 - 02:49 PM
Well I actually dont like the fact that you have a function for that. But I have to admit that it is easier to create another button, but linewise to use a table it is shorter.
After all, your preference is over my preference, and it works and that is good thing.
Pharap #5
Posted 16 March 2013 - 03:34 PM
I'm going to be blunt, your home function is terrible.
All you're doing is chaining function calls, which will eventually overflow the call stack (yep - a stack overflow). I'm assuming lua doesn't have a built in system for handling that kind of situation.

A better way would to be to have a way to set everything back to default, clear the screen and redraw it, or simply set this file as startup and restart the computer (I'm assuming this is going to be the only program running on the computer, or it's replacing the default shell to a degree).

If you want to cut down a bit:

string.len(text) -- slow to write
#text --same result, much less typing
data["ymin"]  --one way
data.ymin --other way

Beyond that things are a bit all over the place and hard to read so I can't really decipher much about the program this late.
I would recommend leaving comments explaining things if you ask for feedback in future, makes it a lot easier for people to just look at things, know what's going on and evaluate them straight away as opposed to putting people off trying to decipher someone else's code. (One of the ugliest things in the world to look at is code you didn't write yourself lol)

On a side note, feel free to use the 'direwolf' way. Everyone has their own way of writing code, and as much as people complain about other's habits (I am a notorious hater of underscores, camelCase (as opposed to PascalCase) and Hungarian notation (prefixing a variable with the name of its intended type, as can be found in some of the computercraft core apis/programs) you should feel free to do things whichever way suits you. As long as you leave comments when you ask people to look at it lol
Czarified #6
Posted 16 March 2013 - 03:48 PM
Thank you Pharap! That was helpful. The reason the home function is so horrible is because that was the first and only way i could think to accomplish it. The startup solution is a much better idea and I will implement that. I'll also add in some comments tonight or tomorrow and update the OP with the result.
Pharap #7
Posted 16 March 2013 - 04:00 PM
You're welcome.
This is why it helps to know how some of the back-end stuff works.
When running programs, what actually happens is the fs api is used to read in all the text from the file, and then a loadstring is performed to turn it into a function. Hence you are just calling function after function in a long chain.
Ordinarily I'd say set it up so that home changes states to default and redraws the home screen, but as this is really just a shell/intro system, you'll be fine using the reboot-startup method.
I'm assuming you're in an environment where you can trust people not to ctrl+T your program or attach a disk drive to override the startup, so I won't warn you about remembering to put measures in place to stop that.

I hope your Titans Tower goes well, I do miss that programme sometimes lol
Czarified #8
Posted 16 March 2013 - 04:21 PM
You're welcome.
This is why it helps to know how some of the back-end stuff works.
When running programs, what actually happens is the fs api is used to read in all the text from the file, and then a loadstring is performed to turn it into a function. Hence you are just calling function after function in a long chain.
Ordinarily I'd say set it up so that home changes states to default and redraws the home screen, but as this is really just a shell/intro system, you'll be fine using the reboot-startup method.
I'm assuming you're in an environment where you can trust people not to ctrl+T your program or attach a disk drive to override the startup, so I won't warn you about remembering to put measures in place to stop that.

I hope your Titans Tower goes well, I do miss that programme sometimes lol

Thanks again Pharap! and yes, it's just a few of my friends on the server. I've updated the OP and added more comments. I'll see if I can make it more clear tomorrow, it's getting kind of late here though, so I'm done coding for the night. :D/>
Pharap #9
Posted 16 March 2013 - 04:27 PM
Thanks again Pharap! and yes, it's just a few of my friends on the server. I've updated the OP and added more comments. I'll see if I can make it more clear tomorrow, it's getting kind of late here though, so I'm done coding for the night. :D/>

Late, don't talk to me about late (03:30 am here XD)
I hope to see more of your progress in the future. Have fun programming (and sleeping, sleep is good too sometimes lol)