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

lua crashed when making a 2d ripple

Started by OrangeC7, 04 February 2018 - 11:51 PM
OrangeC7 #1
Posted 05 February 2018 - 12:51 AM
So, what the program is supposed to do is make a 2d "ripple" around wherever the user clicks. It was supposed to work this way:
- First for loop to track how many layers have been drawn
- Nested for loop to track which column is being drawn to
- Another nested for loop to track which row is being drawn to
- an if statement to check if the current position has been added to the 'ignore' list, which is supposed to track which points have been drawn to in order to not draw to them again. (I want a ripple, not a square that keeps getting bigger)
When I try to run it, though, I click an area on the screen (besides the "close" button in the bottom right), and suddenly lua crashes. (Edit: I tried again and lua didn't crash. But it still doesn't do what I expect ¯\_(ツ)_/¯) =(
So, my questions are
- WHY did it crash not do what I expect?
- How can I prevent it if I can?
- And if I can't (or if there's a simpler way to do this), how else can I do what I'm trying to do?
Here's the code (I put it in a spoiler tag 'cus it's kinda long):
Spoiler

tArgs = {...}
local clr = tArgs[1]
-- If the user didn't specify a color, set it to lightBlue!
if clr == nil then
clr = colors.lightBlue
end
-- Sets up an ignore list so the ripple is a bunch of rings, not squares
local ignore = {...}
term.clear()
local w,h = term.getSize()
term.setCursorPos(w-1,h-1)
term.setBackgroundColor(colors.red)
term.setTextColor(colors.black)
print("x")
-- Pulls a mouse click event.
local _, _, x, y = os.pullEvent("mouse_click")
local done = false
-- If the click was on the "x", done is set to true
if x == w-1 and y == h-1 then
done = true
end
-- if done wasn't true, continue.
if not done then
term.setBackgroundColor(clr)
mrtutils.writeAt(" ",x,y)
table.insert(ignore,{ x,y })
local x1,y1
-- Counts layers
for i=1,w/2 do
-- Counts columns in layers
for j=0,(i*2) do
-- Counts rows in columns
for k=0,(j*2) do
x1,y1 = x+(j-i),x+(k-j)
-- Checks for if the current x,y is in the ignore list
for l=1,#ignore do
if not x1 == ignore[l][1] and not y1 == ignore[l][2] then
mrtutils.writeAt(" ",x1,y1)
sleep(0.01)
-- Adds current x,y to the ignore list so it isn't drawn to again
table.insert(ignore,{ x1,y1 })
end
end
end
end
end
end
term.clear()
term.setBackgroundColor(colors.black)
term.setTextColor(colors.white)
Here's the relevant code for mrtutils (a custom API used in the code above):

-- Writes at a specific location
function writeAt(str, x, y)
term.setCursorPos(x,y)
term.write(str)
end
Note: I'm using CraftOS 1.7
Edited on 04 February 2018 - 11:55 PM
Dog #2
Posted 05 February 2018 - 01:46 AM
I probably don't have the entire fix, but one thing you're doing that might be causing a problem is the way you're initializing your ignore table. You're currently initializing it with whatever command line arguments are provided, but you're already using the first command line argument to specify color. I'm guessing you just want to initialize an empty table - to do that, do this…

local ignore = { }
OrangeC7 #3
Posted 05 February 2018 - 03:38 AM
You're currently initializing it with whatever command line arguments are provided, but you're already using the first command line argument to specify color. I'm guessing you just want to initialize an empty table - to do that, do this…

local ignore = { }
Oh, so that's what that does. Thanks! (I thought it just initialised a blank table) Might explain some other errors I've been getting when using tables recently…
Bomb Bloke #4
Posted 05 February 2018 - 09:56 AM
How do you know that Lua crashed? If you received an error to that effect, then what did that error say?

At a glance, without thinking too much about your program's logic, your most likely looking culprit would be the way you're storing the first command line argument - a string - in clr, which needs to be a number if you want to use it to set your background colour.

You could change this:

local clr = tArgs[1]
-- If the user didn't specify a color, set it to lightBlue!
if clr == nil then
clr = colors.lightBlue
end

… to this:

local clr = colours[ tArgs[1] ] or colours.lightBlue

This way, running your script with an argument of eg "red" will automatically set clr to colours.red - or if the tArgs[1] key isn't found in the colours table (because the user either supplied an incorrect key name or omitted the argument entirely), it'll fall back to light blue.

Note that the argument is still in the form of a string even if the user types numeric characters! If you ever need to convert a string representation of a number to an actual number, use tonumber().

This line also won't do what you expect:

if not x1 == ignore[l][1] and not y1 == ignore[l][2] then

You're "not"ing x1 and y1, as opposed to "x1 == ignore[l][1]" and "y1 == ignore[l][2]". You meant to do:

if x1 ~= ignore[l][1] and y1 ~= ignore[l][2] then

One other point is that sleep() relies on ComputerCraft's timer system, and ComputerCraft only checks timers every server tick - there are twenty of these per second, so your sleep(0.01) call is effectively rounded to sleep(0.05).
OrangeC7 #5
Posted 05 February 2018 - 04:46 PM
How do you know that Lua crashed? If you received an error to that effect, then what did that error say?
It was a blue screen of death, "Lua thread crashed", kinda thing. It didn't say anything else, though.

This way, running your script with an argument of eg "red" will automatically set clr to colours.red - or if the tArgs[1] key isn't found in the colours table (because the user either supplied an incorrect key name or omitted the argument entirely), it'll fall back to light blue.
Thanks! I'll definitely change that.

You're "not"ing x1 and y1, as opposed to "x1 == ignore[l][1]" and "y1 == ignore[l][2]". You meant to do:

if x1 ~= ignore[l][1] and y1 ~= ignore[l][2] then
Okay, I usually do things like this
if not x1 == ignore[l][1] then
for no real reason, because I thought the two syntaxes(?) were interchangeable.

One other point is that sleep() relies on ComputerCraft's timer system, and ComputerCraft only checks timers every server tick - there are twenty of these per second, so your sleep(0.01) call is effectively rounded to sleep(0.05).
I know, I just set it to sleep(0.01) or os.startTimer(0.01) because I know it'll just go as fast as it can. =)
KingofGamesYami #6
Posted 05 February 2018 - 05:10 PM
You're "not"ing x1 and y1, as opposed to "x1 == ignore[l][1]" and "y1 == ignore[l][2]". You meant to do:

if x1 ~= ignore[l][1] and y1 ~= ignore[l][2] then
Okay, I usually do things like this
if not x1 == ignore[l][1] then
for no real reason, because I thought the two syntaxes(?) were interchangeable.

A little bit on this:

You were expecting this behavior:

if not (x1 == ignore[1][1]) then
…but got this behavior:

if (not x1) == ignore[1][1] then
..because order of operations says the latter occurs (kind of like 5 * 4 + 4 = 24, not 40). "not" has a higher precedence than comparison operators.

If you add parentheses as I did in my first example, that is interchangeable with the syntax BB mentioned.