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

Pcutils

Started by robotica34, 18 September 2013 - 12:00 PM
robotica34 #1
Posted 18 September 2013 - 02:00 PM
PCutils by (Mr)robotica34


So, I was trying to test my basic skillz of Lua by writing a computer API, which "helps you do common tasks when making computer programs".

The code itself is pretty simple, but just helps out some simple tasks and avoids typing long and cluttered commands.
The API still lacks some proper functionality and error checking, but I can call this stable. Most of the error-checking is done!

Use of this API:
Spoiler
 os.loadAPI("/pcapi")
local midx, midy = pcapi.middle()
local w, h = term.getSize() --# pcapi is not necesarry here
local title = "Welcome to CraftOS"
local background = "lightblue"
local welcometextln1 = "Welcome to CraftOS! Here you will have a tutorial about ComputerCraft Lua."
local welcometextln2 = "Click space to continue!"
pcapi.backCl(background, true)
pcapi.csPos((midx-#title)/2, 1)
write(title)
pcapi.csPos((midx-#welcometextln1)
-- And so on...
--[[
	I can't give a normal example because of my lack
	of imagination, but you get the point =)
]]--

Pastebin link for the API: http://www.pastebin.com/f1wL29PW

For lazy people out there:
pastebin get f1wL29PW pcapi
Sorry, the code is not commented, but it's simple, so you smart people will understand it =)

I am currently working alone, would appreciate any and all help. I'd also like to learn more in Lua :)/>

All feedback accepted!


Function list:
SpoilertextCl(color)
backCl(color, doclear)
per = wrapPer(optional side)
openRednet(optional side)
drawLine(color, startx, endx, height)
fillBox(color, widths, widthe, heights, heighte)
checkerBoard(fc, sc, widths, widthe, heights, heighte)
button = mkButton(color, text, tlx, brx, tly, bry)
boolean = checkIfClicked(button, clickx, clicky)
redraw(button, color, text)
button = removeButton(button, color)
middle()
csPos(x, y)

FAQ (Frequently Asked Questions):
SpoilerQ: What functions does this API have?
A: Open Function List spoiler.

Q: Why is this even useful?
A: It shortens the amount of code you would need to write to achieve the functionality of my functions.

Q: How long is this?
A: 435 lines.

Q: Where can I use this?
A: You can use this in your coding, but you can't make a post with this and claim that you made it. Also, if someone asks who made this API, refer to me.

Q: How often will you do updates?
A: I'll try to do updates as often as possible, but only when I'm not in school and in mood to code.

Q: asl :P/>
A: Information is in my profile, but only if you really need it. :)/>

Q: Will you do ComputerCraft videoz on the youtubez?
A: I do not have a computer powerful enough to create enjoyable videos.

If you ask a question, it might appear on the FAQ for your convenience :)/>
Changelog:
SpoilerVersion 1.2
Spoiler* Vastly improved error-checking
* Buttons now support text!
+ Added redraw function, so you can change how the button looks at any time, also got rid of turnOnButton and turnOffButton functions, because they were pointless.
* Shrunk code - from 547 lines to 435 lines
+ Added version function, pointless really, but fun :)/>
+ Added writeTimes function - just like printTimes, but uses the write function
* Improved wrapPer and openRednet functions
Version 1.1
Spoiler+ Added local log2 function for color checking
* Improved error-checking
* Changed drawLine(color, height) to drawLine(color, xstart, xend, height)
* Changed fillScreen(color, width, height) to drawBox(color, widths, widthe, heights, heighte)
+ Both textCl and backCl functions now support both strings and numbers (so you can use your favorite colors API if you want ;)/>)
* Improved button functions, so now you can choose the color you want to make the button and the color you want the button to be when it's on, still needs some work there
* Remove button function will also ask for colors!
* Improved clear function, now ENTIRELY clears the screen, including background color and text color resetting
- File expanded because of repetetive code
Version 1.0
SpoilerPut PC Api into release, not documenting changes I made when I didn't make this post - will be too long


Update v1.1!

Finally an update, which has improved the code with the help of the amazing community, and added more useful features to the code.

All changes can be seen in the changelog, version 1.1 .


Update v1.2!

A long waited update is now OUT! Shrunk code, improved it, buttons now support text, error messages are more clear now and more! Check the changelog for details.
theoriginalbit #2
Posted 18 September 2013 - 03:11 PM
1. Take a look into pastebin, its good for hosting large code and there is a program included in ComputerCraft to download from pastebin
2. Take a look into parsing the colours so that people can supply strings or the colours themselves, there was a thread in Ask a Pro a month or two back where I helped someone with colour parsing
3. I looked at this when the contents was only testing spoilers, please use the view button next time instead of the post button
4. The editor does that because of how emoticons work. The /> is applied to the emoticon, as well as any appropriate capitalising, like with B)/>, so that it appears as an emoticon in the normal view. The code tags are designed however to show code, not emoticons or any other formatting, but the damage is already done because the parser that adds the /> is not designed to ignore the match if it is inside of code tags. You also get bad code when you attempt to do any other formatting such as colour or size in code tags, try it!
H4X0RZ #3
Posted 18 September 2013 - 04:34 PM
A little improvement so you don't have to do so much ifs:

local validColors = {["black"] = colors.black, ["white"] = colors.white} --Expand the table to all colors, this kust an example
local function isValidColor(col)
  if validColors[col:lower()] then --check if its a valid number
    return true --return true if its a valid number
  else
    return false --return false if it isnt a valid color
  end
end

function setBC(col)
  if isValidColor(col) then --if col is a valid color
    term.setBackgroundColor(validColors[col:lower()]
  else
    error("not a valid color!",0)
  end
end
I hope you understand it
theoriginalbit #4
Posted 18 September 2013 - 04:44 PM
A little improvement so you don't have to do so much ifs:

local validColors = {["black"] = colors.black, ["white"] = colors.white} --Expand the table to all colors, this kust an example
local function isValidColor(col)
  if validColors[col:lower()] then --check if its a valid number
	return true --return true if its a valid number
  else
	return false --return false if it isnt a valid color
  end
end

function setBC(col)
  if isValidColor(col) then --if col is a valid color
	term.setBackgroundColor(validColors[col:lower()]
  else
	error("not a valid color!",0)
  end
end
I hope you understand it
Why not just return the colour?

local validColors = {["black"] = colors.black, ["white"] = colors.white}

local funciton parseColor(col)
  return validColors[col:lower()]
end

function setBC(col)
  col = parseColor(col)
  if not col then
	error("not a valid color!", 2)
  end
  term.setBackgroundColor(col)
end
Also notice the throwback level of 2 on the error call there, this is better as it tells them where they went wrong, not just that they went wrong.
robotica34 #5
Posted 19 September 2013 - 02:54 AM
I'm happy to see feedback in just 12 hours :D/>
1. Take a look into pastebin, its good for hosting large code and there is a program included in ComputerCraft to download from pastebin
I was looking at what could've I forgotten, that was pastebin, thanks for that :D/> Will put that link now!
A little improvement so you don't have to do so much ifs:

local validColors = {["black"] = colors.black, ["white"] = colors.white} --Expand the table to all colors, this kust an example
local function isValidColor(col)
  if validColors[col:lower()] then --check if its a valid number
	return true --return true if its a valid number
  else
	return false --return false if it isnt a valid color
  end
end

function setBC(col)
  if isValidColor(col) then --if col is a valid color
	term.setBackgroundColor(validColors[col:lower()]
  else
	error("not a valid color!",0)
  end
end
I hope you understand it
After looking at the "col:lower()" part I finally figured out that you can manipulate variables. I didn't know that, and it'll help me out very much, thanks! Also, the idea is pretty good.
2. Take a look into parsing the colours so that people can supply strings or the colours themselves, there was a thread in Ask a Pro a month or two back where I helped someone with colour parsing
3. I looked at this when the contents was only testing spoilers, please use the view button next time instead of the post button
2. I don't really know why parsing colors would be useful, still monitors and computer-related things support only 16 colors. Maybe I could do that, idk.
3. Thanks for the tip, I didn't notice the button :D/>

Just 1 question: what do the error levels mean? Like "error("not a valid color!", 2)?
theoriginalbit #6
Posted 19 September 2013 - 12:56 PM
2. I don't really know why parsing colors would be useful, still monitors and computer-related things support only 16 colors. Maybe I could do that, idk.
What Freack100 posted, and then I fixed would be classed as colour parsing. Not quite complete, because it still only supports strings, not numbers and strings, but its a start.

Just 1 question: what do the error levels mean? Like "error("not a valid color!", 2)?
Its all about who the error is going to blame.
Level 0 — Blames no one, there is no filename or line number, just the red message on the screen
Level 1 (or none provided) — Blames your function, the file name is the name of your file, and the line number is where the error was called
Level 2 (great for APIs) — Blames the person who called your function, the file name is theirs, the line number is where they called your function
….. this continues until eventually the levels get all the way back to the bios.

Examples
SpoilerExample 1:

error("Just this message, no ones fault", 0)

Example 2:

error("I'm the problem") --# or error("I'm the problem", 1)

Example 3:

error("Lets blame the shell", 2)

Example 4:

error("Maybe the bios", 3)

Example 5 (assume file name `test`)

local function one( var )
  if type(var) ~= "string" then
	error("Invalid argument #1: Expected string, got "..type(var), 2)
  end
  print("Valid argument: ", var)
end

local function two()
  one( "Hello World" ) --# works fine, outputs "Valid argument: Hello World"
  one( true ) --# errors with "test:10: Invalid argument #1: Expected string, got boolean"
end

two()
robotica34 #7
Posted 20 September 2013 - 01:46 AM
Thanks for the post, I analyzed it and learnt even more :D/>
One thing about parsing colors - how can I detect that the number value of the color is "pure" (let's say, 4 for magenta, but 3 is white and orange)? Is there a formula?
theoriginalbit #8
Posted 20 September 2013 - 05:22 AM
One thing about parsing colors - how can I detect that the number value of the color is "pure" (let's say, 4 for magenta, but 3 is white and orange)? Is there a formula?
Yes there actually is, without going into too much detail as to why this very nice design choice was made, the colours are actually on a binary scale. This means that
white = 2^0
orange = 2^1

red = 2^14
black = 2^15
Knowing this, and knowing math, we can actually reverse a 2n number using log2(n). Lua doesn't have an implementation however so we must make one like so

local function log2(num)
  return math.log(num) / math.log(2)
end
Now what this will do is it will return us a number which we can the validate. Something like this would do the trick

local function validateColor(col)
  col = log2(col)
  if col < 0 or col > 15 then
	return false --# it is out of bounds, white is 2^0 and black is 2^15
  end
  if col % 1 ~= 0 then
	return false --# it is not a whole number, meaning its a combination of colours
  end
  return true
  --# all the above can be compacted down to this
  return col >= 0 and col <= 15 and (col % 1) == 0
end
Now the second if conditional may look confusing to you with the % operator, if it does open the spoiler and read, if not, just skip the spoiler
SpoilerThe modulo/modulus operator (%) is used to return the remainder of a divide. For example:
1 % 3 is 1 because 1 goes into 3 0 times with a remainder of 1
2 % 3 is 2 because 2 goes into 3 0 times with a remainder of 2
3 % 3 is 0 because 3 goes into 3 1 time with a remainder of 0
4 % 3 is 1 because 4 goes into 3 1 time with a remainder of 1
etc, etc, etc. I hope you can see how this can be useful

How we use it to check for a whole number is, for example
if someone has 3, which is white and orange combined
log2(3) equals 1.58
1.58 % 1 equals 0.58
0.58 is not equal to 0 therefore it is not a whole number

Now if you wanted to do something a little more true of how ComputerCraft deals with `impure` colors you can do the following

local function parseColor(col)
  return 2 ^ math.floor(math.log(col) / math.log(2))
end
What we do there is break it down to the number from 0—15, we then round it down, so 5 is still 5, 5.1 is 5m but 5.9 is also 5 and then we return the binary of that number. So for example 3 would become 2. log2(3) equals 1.58 rounded down equals 1, 2 ^ 1 equals 2.
The reason we do this math.floor is because of the way they do it in the ComputerCraft code, without going into too much detail and explanation why Java works in this way, in the CC code they cast a double (iirc) into an integer, and the way that Java deals with double/float to int casting is to round the numbers down making the double 1.1 equal 1 when its an integer, or the double 1.9 also equal 1 when its an integer.
Now if you actually check the above method with how ComputerCraft actually does this you will notice that its the same, try the following

local function parseColor(col)
  return 2 ^ math.floor(math.log(col)/math.log(2))
end

local c = colors.combine(colors.white, colors.orange)
term.setTextColor(c)
print("ComputerCraft's colour parsing")
term.setTextColor( parseColor(c) )
print("Our colour parsing, its the same color")

Hope this all makes sense and helps

— BIT
robotica34 #9
Posted 20 September 2013 - 06:59 AM
Hello,
I'm currently working on an update, that's why I'm not replying.
I somehow get this error in this line:

if type(color) ~= "string" or type(color) ~= "number" then error("string or number expected, got "..type(color), 2) end
It says:
"pcall: string or number expected, got string" (pcall is because i'm on a lua interpreter).
What is the deal here?
FYI, inputs are drawLine(color, startx, endx, height).

EDIT:
I finally got it. The problem was, it always checked the second conditional, causing it to break, because one variable can't have two types. Instead, I did

if type(color) ~= "string" then
  if type(color) ~= "number" then
	error("string or number expected, got "..type(color), 2)
  end
end
SECOND EDIT:
It wasn't the problem because of ifs, it's because I used OR instead of AND. Derp.
theoriginalbit #10
Posted 20 September 2013 - 07:22 AM
What is the deal here?
It is because of how the OR works. Basically what you're saying here is that `color` needs to be both a string and a number.
OR
true or true = true
true or false = true
false or true = true
false or false = false
So lets say what you supply is a boolean, type(color) ~= "string" will resolve to true because a boolean is not a string, as seen above nothing else in the statement can change the result of true when using ORs, so it ignores the rest of the conditional and performs the contents of the if statement. What you need to do is use AND
AND
true and true = true
true and false = false
false and true = false
false and false = false
That way if it is not a string and not a number, then it needs to error.

Alternatively
SpoilerYou can also use NOT in conjunction with ors to do the same thing

if not (type(color) == "string" or type(color) == "number") then
notice how we surround the conditional (which says it needs to be a number, or a string) in brackets and then apply NOT to it. This inverts the result, so if it is a string or number the if statement doesn't run
not true = false
not false = true
robotica34 #11
Posted 20 September 2013 - 09:16 AM
You maybe saw my edit for solution, but I didn't figure out that I should use AND instead of OR. Thanks :D/>
BTW, I'm rewriting a big portion of my code, so it'll take quite a while, but should be done in under 20 minutes.
robotica34 #12
Posted 20 September 2013 - 10:16 AM
Sorry if the update is running late, been playing around with other things, that's why I'm late. Also putting in a lot of error checking.
robotica34 #13
Posted 20 September 2013 - 12:51 PM
The update is out! Making this post for those who only check notifications. Check the OP for details.
robotica34 #14
Posted 21 September 2013 - 09:30 AM
I would like to receive some feedback :)/> Been feeling alone, no feedback on the update :(/> Also, I'm working on v1.2 - shrinking code, reimplementing drawBox, mkButton, checkIfClicked, turnOnButton, turnOffButton, also error-checking is being redone - I'll now give more exact information on where they went wrong, not that they just went wrong somewhere when calling my function. So, stay tuned!
Vilsol #15
Posted 21 September 2013 - 01:10 PM
Quite a lot of useless code.
For example:

for k, v in pairs(availableColors) do
	  if input == v then
		 correctColor = true
		 break
	  else
		 correctColor = false
	  end
	end
Could be just

for k, v in pairs(availableColors) do
	  if input == v then
		 correctColor = true
		 break
	  end
	end

The function wrapPer is just a huge mess, which I don't understand. You can just use peripheral.getNames() and check against the side table.
The function fillBox actually is drawBox. And also, why don't you just use your own background color function?
robotica34 #16
Posted 21 September 2013 - 01:23 PM
Thanks for the peripheral tip, but actually I can't do
 for k,v in pairs(availableColors) do
 if input == v then
  correctColor = true
  break
 end
end 
because if color is incorrect I will have nil and I don't want to do that. I could also check if correctColor == nil, but it's easier to do if correctColor.
drawBox is now update (working on 1.2) and removed like 150 lines of code already by calling a main function for all error-checking.
I'm a beginner and it's a pain in the ass to figure out how to do things easily. I barely use "help api" for something.

P.S. When I was playing "illegal" Tekkit about two years ago, I used a switcher made by Vilsol, it was in Latvian. Is that the same you? :D/> Btw, I live in Lithuania, so I live near you :P/>
theoriginalbit #17
Posted 21 September 2013 - 01:27 PM
Thanks for the peripheral tip, but actually I can't do
-code snip-
because if color is incorrect I will have nil and I don't want to do that. I could also check if correctColor == nil, but it's easier to do if correctColor.
set it to false first, before the loop. Also nil will resolve to false in an if statement when you do the following

if correctColor then
  print("It was a value! Any value, except false or nil")
else
  print("It was false or nil")
end
Vilsol #18
Posted 21 September 2013 - 01:40 PM
Thanks for the peripheral tip, but actually I can't do
 for k,v in pairs(availableColors) do
 if input == v then
  correctColor = true
  break
 end
end 
because if color is incorrect I will have nil and I don't want to do that. I could also check if correctColor == nil, but it's easier to do if correctColor.
drawBox is now update (working on 1.2) and removed like 150 lines of code already by calling a main function for all error-checking.
I'm a beginner and it's a pain in the ass to figure out how to do things easily. I barely use "help api" for something.

P.S. When I was playing "illegal" Tekkit about two years ago, I used a switcher made by Vilsol, it was in Latvian. Is that the same you? :D/>/> Btw, I live in Lithuania, so I live near you :P/>/>

Yes that was my switcher ^_^/> (And yes it was in Latvian).
What you can do is after the for loop make a check if the correctColor is still false, just error out.
robotica34 #19
Posted 22 September 2013 - 01:40 PM
Help me!
 if type(side) ~= "string" then
error("insert error here "..type(side), 2)
end 
If I enter a side with type string, it outputs me this:
 pcapi:105: attempt to call string 
WTF?!!
BTW, line 105 is
 if type(side) ~= "string" then 
What should I do?
AgentE382 #20
Posted 22 September 2013 - 07:59 PM
Help me!
 if type(side) ~= "string" then
error("insert error here "..type(side), 2)
end 
If I enter a side with type string, it outputs me this:
 pcapi:105: attempt to call string 
WTF?!!
BTW, line 105 is
 if type(side) ~= "string" then 
What should I do?
Would you post the full function, please?
theoriginalbit #21
Posted 22 September 2013 - 09:07 PM
-snip-
Look around the function that like 105 is in. You have a variable named `type`, and its a string, that is overriding the `type` function, so when you do type(side) its trying to "call [a] string"
This could have been answered by reading this topic in Ask a Pro.
robotica34 #22
Posted 23 September 2013 - 01:57 AM
You are a genius! I would've not figured it out myself :D/> Thank you!
robotica34 #23
Posted 23 September 2013 - 12:46 PM
I am rewriting my buttons, and I have a hard time figuring out how to split a string into equal x long parts. How can I do it easily?
bowstones #24
Posted 23 September 2013 - 11:45 PM
have you tried to include another script file using the "require" command? CC doesn't seem to like it.
AgentE382 #25
Posted 25 September 2013 - 09:31 AM
I am rewriting my buttons, and I have a hard time figuring out how to split a string into equal x long parts. How can I do it easily?

function splitequally(str, sublen)
  local len = #str
  local substrs = {}
  for i = 1, math.ceil(len/sublen) do
    substrs[i] = string.sub(str, (i - 1) * sublen + 1, i * sublen)
  end
  return substrs
end
Alternatively, you can do `return unpack(substrs)`, if you want multiple returns.
AgentE382 #26
Posted 25 September 2013 - 09:32 AM
have you tried to include another script file using the "require" command? CC doesn't seem to like it.

Start your own topic in the "Ask a pro" section.
robotica34 #27
Posted 26 September 2013 - 03:04 AM
I am rewriting my buttons, and I have a hard time figuring out how to split a string into equal x long parts. How can I do it easily?

function splitequally(str, sublen)
  local len = #str
  local substrs = {}
  for i = 1, math.ceil(len/sublen) do
	substrs[i] = string.sub(str, (i - 1) * sublen + 1, i * sublen)
  end
  return substrs
end
Alternatively, you can do `return unpack(substrs)`, if you want multiple returns.
I'm super sorry, but I've already figured it out. Now to print that text in the middle of a button!
robotica34 #28
Posted 27 September 2013 - 09:59 AM
The update is running late, but now we have UPDATE v1.2! Check OP for details.
robotica34 #29
Posted 07 October 2013 - 12:24 PM
Anyone?..
robotica34 #30
Posted 26 October 2013 - 10:55 AM
Hello people,

You might wonder why I'm not releasing an update for several weeks now. This is because of two reasons - first, I'm working on Minesweeper, and second, I got a bit 'tired' of coding - what I mean by that, I'm struggling to get new information on how to solve different problems. My lack of knowledge really brings me to the end of programming. I might do something for the Utility API (which I'll call PCutils later), but not soon.

Sorry for the bad news.
Sincerely,
robotica34
robotica34 #31
Posted 20 August 2019 - 09:28 AM
Jesus Christ, talk about digging up some cringe…
LDDestroier #32
Posted 17 December 2019 - 11:30 PM
Jesus Christ, talk about digging up some cringe…
It happens to all of us. I know I'm not particularly proud of some of MY early posts…