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

variables and API's. How do I properly reference to them?

Started by Crucidal, 13 August 2014 - 05:50 PM
Crucidal #1
Posted 13 August 2014 - 07:50 PM
Hi,

I am trying to make an API. It is meant to provide movement functions for turtles while keeping track of their relative position.

This is the API:
Spoiler

--[[
This API is created to provide Turtles functions to keep
track of their relative location while moving around.
Basically, after moving or turning I would like to save this information in a file.
Just in case the server shuts down or the chunk unloads.
]]--

--current location--create variables
-- used to be "local x = 0" but that didn't work either
x = 0
y = 0
z = 0

--current direction
d = 0

--[[
support functions:
getLocation FromFile

]]--

--getLocation
function getLocation()
locationFile = fs.open("location", "r")
directionFile = fs.open("direction", "r")
d = tonumber(locationFile.readAll())
gx, gy, gz = locationFile.readAll():match("([^,]+),([^,]+), ([^,]+)")
x, y, z = tonumber(gx), tonumber(gy), tonumber(gz)
locationFile.close()
directionFile.close()
end

--setLocation and direction
function setLocation()
location = "" .. x .. "," .. y .. "," .. z      --this is line 33
direction = "" .. d
locationFile = fs.open("location", "w")
locationFile.write(location)
locationFile.close()

directionFile = fs.open("direction", "w")
directionFile.write(direction)
directionFile.close()
end


--movement functions
function registerMove(amount)
if d == 0 then
y = y+amount
elseif d == 1 then
x = x+amount
elseif d == 2 then
y = y-amount
elseif d == 3 then
x = x-amount
end
end

function move(amount)
for x = 0, amount-1, 1 do
getLocation()
if turtle.forward() then
registerMove()
setLocation()
end
end
end

function moveV(amount)
for x = 0, amount-1, 1 do
getLocation()
if turtle.forward() then
z = z+amount
setLocation()
end
end

end

function turnLaft()
getLocation()
turtle.turnLeft()
if d > 1 then
d = d-1
else
d = 4
end
setLocation()

end

function turnRaight()
getLocation()
turtle.turnRight()
if d < 4 then
d = d+1
else
d = 1
end
setLocation()
end

function init()
x = 0
y = 0
z = 0
d = 0
setLocation()
end

this is the program I use to test moving forward for 5 blocks.

os.loadAPI("movementAPI")
movementAPI.init()
movementAPI.move(5)

I get the following error:
MovementAPI: 33 : attempt to concatenate string and nil
line 33 shows the following code
location = "" .. x .. "," .. y .. "," .. z

I understand that I am probably not using the references to x, y, z and d properly… however I cannot figure out how to do it x)

Could anyone give me some tips?

Cheers!
Chris
Bomb Bloke #2
Posted 13 August 2014 - 08:20 PM
Nah, you're referring to them correctly. Odds are it's to do with the function above - if gx/gy/gz don't represent valid numbers for any reason, then tonumber(gx)/tonumber(gy)/tonumber(gz) will return nil.
Crucidal #3
Posted 13 August 2014 - 09:06 PM
Nah, you're referring to them correctly. Odds are it's to do with the function above - if gx/gy/gz don't represent valid numbers for any reason, then tonumber(gx)/tonumber(gy)/tonumber(gz) will return nil.

Actually, I didn't use that function yet. So I don't know if it will work or not.
I tried to set values for x, y and z in init() and this is not working. Instead of nil, I expected the values to be 0.

Any clue?

maybe you are right…but I have to give it some deeper thought :P/>
Edited on 13 August 2014 - 07:16 PM
hilburn #4
Posted 13 August 2014 - 09:33 PM
why not do:

location=textutils.serialize(locationfile.readall())


much better to use vectors/tables for doing location stuff
Edited on 13 August 2014 - 07:33 PM
Crucidal #5
Posted 13 August 2014 - 10:08 PM
why not do:

location=textutils.serialize(locationfile.readall())


much better to use vectors/tables for doing location stuff

Because that's something that I haven't used before and I don't know anything about the serialize function.

This method I intended to use is simple to comprehend for someone who only knows basic programming x)

I'm having trouble with the fs readAll() and readLine() function though.
I have a file "location" with "0,0,0" in it. (without quotes) and both the functions I just mentioned return nothing… but there is no error either… when I do a print(locationFile.readAll()) it prints an empty line… =(
Crucidal #6
Posted 13 August 2014 - 10:18 PM
I'm starting to understand my own problem.
I accidently read locationFile twice… and once you read it you cannot read it again unless you save what you read or if you re-open the file.
Crucidal #7
Posted 13 August 2014 - 10:34 PM
gx, gy, gz = locationFile.readAll():match("([^,]+),([^,]+), ([^,]+)")

this code doesn't seem to work but I don't know how else to do this… any ideas?
Bomb Bloke #8
Posted 14 August 2014 - 03:40 AM
Actually, I didn't use that function yet.

It sounds like you've already seen your mistake in saying that, but movementAPI.move() calls getLocation() followed by setLocation() again.

Sometimes, when a function fails within an API, it can be handy to intersperse random print statements throughout your main scripts so you can tell exactly how far the script got before making the final call that crashed. Some other languages - eg Java - make this a lot easier by handing you the entire list of functions sitting in the stack along with the lines they were called from and of course your error message as well.

why not do:

Psst, you mean unserialise!

Anyway, to elaborate on this, it'd work along these lines: First you'd initialise a table with your values in it:

local myVars = {["x"] = 0, ["y"] = 0, ["z"] = 0, ["d"] = 0}

You'd now be able to refer to these variables as myVars.x, myVars.y, and so on. textutils.serialise() can then be used to convert the entire table and its contents to a single string (a series of characters - get it?), which you can write to disk like so:

--setLocation and direction
local function setLocation()
	local locationFile = fs.open("location", "w")
	locationFile.write(textutils.serialise(myVars))
	locationFile.close()
end

And then to read it, textutils.unserialise() comes into play:

--getLocation
function getLocation()
	local locationFile = fs.open("location", "r")
	myVars = textutils.unserialise(locationFile.readAll())
	locationFile.close()
end

gx, gy, gz = locationFile.readAll():match("([^,]+),([^,]+), ([^,]+)")

this code doesn't seem to work but I don't know how else to do this… any ideas?

Something like this:

gx, gy, gz = locationFile.readAll():match("(.*%d*),(.*%d*),(.*%d*)")

The stuff outside the brackets has to be matched verbatim - that includes spaces, so be careful! Within the brackets, ".*" means "get everything until the next match", and "%d*" means "get all digits until the next match".

It may be that you can't apply "match" directly to "locationFile.readAll()". You may need to read the file contents into a variable, then apply "match" to that variable on the following line.
natedogith1 #9
Posted 14 August 2014 - 06:15 AM
gx, gy, gz = locationFile.readAll():match("([^,]+),([^,]+), ([^,]+)")

this code doesn't seem to work but I don't know how else to do this… any ideas?

Something like this:

gx, gy, gz = locationFile.readAll():match("(.*%d*),(.*%d*),(.*%d*)")

The stuff outside the brackets has to be matched verbatim - that includes spaces, so be careful! Within the brackets, ".*" means "get everything until the next match", and "%d*" means "get all digits until the next match".

It may be that you can't apply "match" directly to "locationFile.readAll()". You may need to read the file contents into a variable, then apply "match" to that variable on the following line.

The stuff outside the brackets works in the funny way of regular expressions, it's the stuff inside that's more exact(I think), though it's more of an or(or a nor because of the begining '^').

I'd recomend using something along the lines of '.-(%d+),.-(%d+),.-(%d+)' , as it doesn't return the unnecessary white space characters. The difference between the '*' and the '-' is potentially very important as it's the difference between '12,12,12' returning three twos or three twelves in this case. The '*' is hungry, it eats the string and gives back what's needed, while the '-' isn't hungry, therefore it leaves behind as much of the string as it can.
local str="1234567890"
local dot1,number1=str:match("(.*)(%d*)")
local dot2,number2=str:match("(.-)(%d+)")
print(dot1,"|",number1)
print(dot2,"|",number2)
With the above, dot1 will contain the entire string, while number1 will be an empty string. However, with the second regular expression, dot2 will be an empty string and number2 will be the number, which is much closer to what we want.

Also, checking it out, it seems that your issue was an accidental space, as (I think) Bomb Bloke mentioned.
Bomb Bloke #10
Posted 14 August 2014 - 06:58 AM
I'd recomend using something along the lines of '.-(%d+),.-(%d+),.-(%d+)' , as it doesn't return the unnecessary white space characters.

True, but there shouldn't be any white space. While I'm sure my expression can be improved (I've got a nagging feeling it doesn't need the %d*'s at all), it does have the benefit of being able to handle decimal points and negative values.

Though seriously, I'm no pattern expert. I just bashed stuff into repl.it according to this guide until it worked.
Edited on 14 August 2014 - 05:07 AM
natedogith1 #11
Posted 14 August 2014 - 08:04 AM
I'd recomend using something along the lines of '.-(%d+),.-(%d+),.-(%d+)' , as it doesn't return the unnecessary white space characters.

True, but there shouldn't be any white space. While I'm sure my expression can be improved (I've got a nagging feeling it doesn't need the %d*'s at all), it does have the benefit of being able to handle decimal points and negative values.

Though seriously, I'm no pattern expert. I just bashed stuff into repl.it according to this guide until it worked.
Good point, I didn't even consider it couldn't handle '.' or '-' in the numbers. And yeah, yours would work just fine without the '%d*'(which actually isn't doing anything, because '.*' is eating up all the characters, leaving the '%d*' with nothing). A better regular expression would probably be replace '%d+' with '[%d-.]+', and maybe replace '.' with '%s-' or '%s*'
Crucidal #12
Posted 15 August 2014 - 05:51 PM
Anyway, to elaborate on this, it'd work along these lines: First you'd initialise a table with your values in it:

thanks, this was easy to understand and helped a lot. I used this in my implementation now :-)
next up: implementing a version of the a* algorithm to automatically find a path to it's origin!
Looking forward to it! :-)

gx, gy, gz = locationFile.readAll():match("([^,]+),([^,]+), ([^,]+)")

this code doesn't seem to work but I don't know how else to do this… any ideas?

Something like this:

gx, gy, gz = locationFile.readAll():match("(.*%d*),(.*%d*),(.*%d*)")

The stuff outside the brackets has to be matched verbatim - that includes spaces, so be careful! Within the brackets, ".*" means "get everything until the next match", and "%d*" means "get all digits until the next match".

It may be that you can't apply "match" directly to "locationFile.readAll()". You may need to read the file contents into a variable, then apply "match" to that variable on the following line.

The stuff outside the brackets works in the funny way of regular expressions, it's the stuff inside that's more exact(I think), though it's more of an or(or a nor because of the begining '^').

I'd recomend using something along the lines of '.-(%d+),.-(%d+),.-(%d+)' , as it doesn't return the unnecessary white space characters. The difference between the '*' and the '-' is potentially very important as it's the difference between '12,12,12' returning three twos or three twelves in this case. The '*' is hungry, it eats the string and gives back what's needed, while the '-' isn't hungry, therefore it leaves behind as much of the string as it can.
local str="1234567890"
local dot1,number1=str:match("(.*)(%d*)")
local dot2,number2=str:match("(.-)(%d+)")
print(dot1,"|",number1)
print(dot2,"|",number2)
With the above, dot1 will contain the entire string, while number1 will be an empty string. However, with the second regular expression, dot2 will be an empty string and number2 will be the number, which is much closer to what we want.

Also, checking it out, it seems that your issue was an accidental space, as (I think) Bomb Bloke mentioned.

is there any place where they explain these tokens? It's quite unreadable if you don't know what a * + or % is supposed to do.
If I knew this, I could figure out why your solution doesn't return unnecessary white space characters :-)
KingofGamesYami #13
Posted 15 August 2014 - 06:15 PM
http://www.lua.org/pil/20.2.html