I am currently trying to debug a Minecraft-based turtle farming script with functions and API functions.
When I am executing I get an 'io' exception (which is understandable as I am trying to write to a log for each loop), but it doesn't tell me which script it is in or the line number. Are there any solutions apart from printing throughout program execution?
Exception I have been getting:
io:107: index expected, got nil
main turtle-farming script:
--[[main script for farming procedure]]
--load functions responsible for positioning/repositioning/farming
os.loadAPI("APIS/positioners");
os.loadAPI("APIS/farmers");
--action turtle start farming condition
assert(positioners.check_start_pos(), "Could not initialise the turtle for the farming procedure\nCould not identify it's starting location\n(are there birch planks directly above and beneath it?");
--check Netherrack can be found from start block, declare 'dir' also as local
local init_result, dir = positioners.find_next_neth()
assert(init_result, "Could not find the appropriate direction to travel in from\n the turtle starting position, is the farm set up correctly?");
local loop_vars = {["count"] = 0, ["blockId"] = nil, ["dir"] = nil, ["prevManvr"] = nil, ["action"] = ""};
--**input** assertion tests for required fuel level and seed requirements
assert(turtle.getFuelLevel() > 50, "Fuel level is below required level to start farming, - terminating");
--given 'result' == true can start farming
turtle.forward();
--open log file (overwrite) and save to io class handle
log = io.open("logs/proc_log", "w");
--start main farming process loop
while turtle.getFuelLevel() > 0 do
--reset action variable
loop_vars["action"] = "";
--sort log variables
loop_vars["count"] = loop_vars["count"] + 1;
--identify the block above (returns inv slot(1-4) it matches with or false if it doesn't)
loop_vars["blockId"] = positioners.id();
if blockId == 1 then
loop_vars["action"] = "Netherrack above ";
if loop_vars["dir"] == nil then
print("Netherrack found, planting");
farmers.plant();
--shift turtle forward such that it can evaluate a new block. Ensure that moves forward in case of obstruction
assert(turtle.forward(), "The turtle is impeded from moving forwards in farming route");
--log statement
loop_vars["action"] = loop_vars["action"].."plant and move forward";
else
--initiate change in direction procedure
assert(positioners.change_direction(loop_vars["dir"]), "Failed to complete turtle change direction procedure");
--reset dir
loop_vars["dir"] = nil;
loop_vars["action"] = loop_vars["action"].." actioned 'positioners.change_position(dir)'.";
end;
elseif blockId == 3 then
turtle.forward();
--log statement
loop_vars["action"] = "Proceeding past marble block";
elseif blockId == false then
print("Finding next Netherrack block.");
--retrace back to the most recent 'legitimate' block");
turtle.back();
--call function to reposition turtle for next farming line
result, loop_vars["dir"] = positioners.find_next_neth(loop_vars["prevMan"]);
assert(result, "A new netherrack block could not be found in any direction.");
--assign dir result to prevMan (information for next positioners.change_position() call
loop_vars["prevMan"] = loop_vars["dir"];
--log statement
loop_vars["action"] = "No 'legitimate' block above, 'positioners.find_next_neth() successful,\nassigning dir accordingly.";
else
--log statement
log.write("Unhandled exception in farm_proc main loop");
log.close();
error("positioners.find_next_neth() did not find an appropriate block above it.");
end;
print("farm_proc loop");
--write current turn to log file
log.write("Turn no: "..loop_vars["count"].."\n Id()== "..tostring(loop_vars["blockId"]).."\n Action: "..loop_vars["action"].."\n Previous maneuver: "..loop_vars["prevMan"].."\n\n");
end;
--updating log
log.write("Error - turtle ran out of fuel\n");
log.close();
error("Turtle ran out of fuel whilst planting - terminated");
'positioners' api (movement/detection based tasks):
function find_next_neth(prevMan)
--[[Find direction of the next overhead block of Netherrack from turtle's current pos,
Order;
N
E
W
S
Error (no direction found)]]
--define numbers corresponding to directions
local dirs = {"Back", "Left", "Right", "Forward"};
--create a loop counter to ensure does not exceed full checking loop
local counter = 4;
--loop through movement proc. until netherrack (slot 1) is found above it (if at all)
while true do
print("Looping, current loop counter is: ", counter);
local idAhead = id_ahead();
if counter == 4 then
--placeholder, script should never need to find forwards
elseif counter == 3 then
turtle.turnLeft();
elseif counter == 2 then
turtle.turnRight();
turtle.turnRight();
elseif counter == 1 then
turtle.turnRight();
--decremented counter has reached it's minimum
elseif counter < 1 then
return false, nil;
else
error("For find_next_neth iteration counter out of range");
end;
--decrement counter variable
counter = counter - 1;
--boolean condition to determine whether function is complete, incomplete or broken. 'prevMan' as a logical statement will return true if var has value, and false if not
local testPrev = (not prevMan == dir[(math.abs(counter - 4) + 1))] or not prevMan);
local validBlock = (idAhead == 1 or idAhead == 3)
if testPrev and validBlock then
--all tests are valid - break loop to return
break;
end;
--report success and return result
print("Nether found!\n");
return true, dirs[counter];
end;
end;
function change_direction(dir)
--intialise a base error string
local errMsg = "Could not action positioners.change_direction(dir, prevMan) ";
if dir == "East" then
assert(turtle.forward(), errMsg.."Obstruction to route");
--this should finalise a 180deg turn to the left
turtle.turnLeft()
elseif dir == "West" then
assert(turtle.forward(), errMsg.."Obstruction to route");
--180deg turn left
turtle.turnRight()
elseif dir == "North" then
--no action
elseif dir == "South" then
error("Return to start not yet implemented");
else
error("positioners.change_direction() parameter not as expected");
end;
return true;
end;
function id(slot)
--[[Determines if netherrack is currently above turtle.
Return false means the block
above does not match slots 1-4 of the
turtle's inv. Takes optional int
for specific block slot index
in turtle compare]]
--checking argument
if slot ~= nil then
--select slot given as argument
turtle.select(slot);
if turtle.compareUp() then
--don't return int because specific material requested
return true;
else
return false;
end;
end;
counter = 1
while counter < 5 do
turtle.select(counter);
--identify whether the current selected slot item matches the blovk overhead
if turtle.compareUp() then
--return block above's corresponding slot index
return counter
end;
counter = counter + 1;
end;
--return number indicating unidentfied block
return false;
end;
function id_ahead(slot)
if turtle.forward() then
--save value of id call for later return
local rtn = id(slot)
turtle.back();
return rtn
else
error("turtle could not move forwards in id_ahead(slot)");
end;
end;
function check_start_pos()
--set selected slot to birch planks (no currently selected method implemented so cannot revert this change internally)
turtle.select(2);
--save boolean variables for comparison of blocks above and below position
local up = turtle.compareUp();
local down = turtle.compareDown();
--test if above boolean values are true (turtle in designated start position)
if up and down then
return true;
else
print("Not in appropriate farming start position");
return false;
end;
end;
(The 'farmers' api simply checks current seeds and then plants a crop through farmers.plant())
I believe I have found the solution - the debug library, specifically debug.traceback() allows you to introspect the call stack more closely when catching an error
debug library index
Related
I've written a Lua script for ComputerCraft to control a mob farm. What I'm doing is checking the amount in specific Functional Storage drawers. The API for the mod has, at least to me, a weird quark that if the storage drawer is empty then the call to getItemDetail(num).count fails and returns "Attempt to index a nil value."
I was able to determine that if you just call getItemDetail(num), where num is the index of the drawer, and the drawer is empty then it returns nil. This works some of the time. It fails if that between calling getItemDetail(num) and getItemDetail(num).count the drawer's count goes from >0 to 0 it errors with "Attempt to index a nil value." At least this is my current working theory as to why it fails.
Is there any better way of trapping the index a nill value error so that the program doesn't error and hault?
This is how I'm currently trying to determine if the drawer has a 0 count or not.
data is table of class objects where I keep track of things like the location, index, of the drawers.
-- If you call getItemDetail and it returns nil, then the drawer is empty.
-- If you call getItemDetaiil(num).count it throws a error "Attempt to index a nill value."
if storage.getItemDetail(data[i]:getLocation()) ~= nil then
storageamount = storage.getItemDetail(data[i]:getLocation()).count
else
storageamount = 1
end
if storage.getItemLimit(data[i]:getLocation()) ~= nil then
storagelimit = storage.getItemLimit(data[i]:getLocation())
else
storagelimit = 1024
end
The code loops until the drawer is full and then moves on to the next item.
The class definition for clairity.
Class = {
-- Constructor
new = function(self)
local new = {}
setmetatable(new, {__index = self})
return new
end,
-- Setters
setName = function(self, name) --Tag of item in drawer
self.name = name
end,
setLocation = function(self, location) -- Drawer location from Controller
self.location = location
end,
setRSI = function(self, RSI) -- This is the side of the Computer of the RSI
self.RSI = RSI
end,
setSide = function(self, Side) -- This is the side of RSI that will out RS signal
self.Side = Side
end,
--Getters
getName = function(self, name)
return self.name
end,
getLocation = function(self,location)
return self.location
end,
getRSI = function(self, RSI)
return self.RSI
end,
getSide = function(self, Side)
return self.Side
end
}
The code runs just fine as long as one of the drawers that I'm monitoring doesn't go to zero.
So I really want to be able to trap the error or prevent the program from haulting if encounters the index a nill value error.
Thanks,
You could just store the value of storage.getItemDetail(data[i]:getLocation()) in a variable, check if that variable is not Nil, and get the item's count, something like this:
item = storage.getItemDetail(data[i]:getLocation())
storagelimit = storage.getItemLimit(data[i]:getLocation())
storageamount = 1
if item ~= nil then
storageamount = item.count
end
if storagelimit == nil then
storagelimit = 1024
end
alright this is probably a stupid mistake but I cant find it.
I am trying to iterate over a treesitter node to find the parent node i need.
local function getParent(node)
local type_text = node:type()
print(type_text, "node type")
if type_text == "variable_declaration" then
return 1
end
print("RUNNING AGAIN?")
getParent(node:parent())
end
local outp = getParent(node_at_cursor, 0)
print(outp, "outp")
the output:
identifier node type 0
RUNNING AGAIN?
variable_list node type 1
RUNNING AGAIN?
assignment_statement node type 2
RUNNING AGAIN?
variable_declaration node type 3
nil outp
i don't understand why the return value is always nil. it clearly stops at some point, i see that from the print statements. The function enters the if block and should return 1.
thank you
You are not returning anything from the recursive call.
local function getParent(node)
local type_text = node:type()
print(type_text, "node type")
if type_text == "variable_declaration" then
return 1
end
print("RUNNING AGAIN?")
return getParent(node:parent())
end
I have created a function that (pseudo)randomly creates a table containing numbers. I then loop this function until at least correct result is found. As soon as I've confirmed that at least one such result exists, I stop the function and return the table.
When I create tables containing small values, there are no issues. However, once the random numbers grow to the range of hundreds, the function begins to return nil, even though the table is true the line before I return it.
local sort = table.sort
local random = math.random
local aMin, aMax = 8, 12
local bMin, bMax = 200, 2000
local function compare( a, b )
return a < b
end
local function getNumbers()
local valid = false
local numbers = {}
-- Generate a random length table, containing random number values.
for i = 1, random( aMin, aMax ) do
numbers[i] = random( bMin, bMax )
end
sort( numbers, compare )
-- See if a specific sequence of numbers exist in the table.
for i = 2, #numbers do
if numbers[i-1]+1 == numbers[i] or numbers[i-1] == numbers[i] then
-- Sequence found, so stop.
valid = true
break
end
end
for i = 1, #numbers-1 do
for j = i+1, #numbers do
if numbers[j] % numbers[i] == 0 and numbers[i] ~= 1 then
valid = true
break
end
end
end
if valid then
print( "Within function:", numbers )
return numbers
else
getNumbers()
end
end
local numbers = getNumbers()
print( "Outside function:", numbers )
This function, to my understanding, is supposed to loop infinitely until I find a valid sequence. The only way that the function can even end, according to my code, is if valid is true.
Sometimes, more often than not, with large numbers the function simply outputs a nil value to the outside of the function. What is going on here?
You're just doing getNumbers() to recurse instead of return getNumbers(). This means that if the recursion gets entered, the final returned value will be nil no matter what else happens.
In the else case of the if valid then, you are not returning anything. You only return anything in the valid case. In the else case, a recursive call may return something, but then you ignore that returned value. The print you see is corresponding to the return from the recursive call; it isn't making it out the original call.
You mean to return getNumbers().
I wanted to make a madden mobile sniping bot. So I wanted to use the findImage function to find if a player has sold or not. Here is what I'm thinking.
The first image would be my screen and the second image would be what image I'm trying to find, and if it find it it will tap on it. Here is my script:
local r = findImage ( "/images/auction.bmp" ,1, 1, nil, nil);
if r == nil then alert ( "nil" );
else local n = 0;
for i, v in pairs (r) do n = n + 1;
end if n == 0 then alert ( "false" );
else
alert ( "true" );
end
end
It runs but always returns false. So I was wondering if I'm using the findImage function right and if so why doesn't it return true?
The end statement at line 5 closes the for loop and is directly followed by another if statement before the first one at line 2 has been closed.
Try ending both before issuing another condition.
I'd also recommend lowering the finding tolerance a little. A value of 1 is the most strict which means every single pixel must match precisely in order to be found. A value of 0.8 is just as accurate when finding such a large image yet still allows some fuzziness for improving detection.
Something like this should work.
local r = findImage ( "/images/auction.bmp" ,1, 0.8, nil, nil);
local n = 0;
if r == nil then alert ( "nil" ); else
for i, v in pairs (r) do n = n + 1;
end
end
if n == 0 then alert ( "false" );
else alert ( "true" );
end
I am trying to put together a lua script to be called from Redis (via an EVAL call) in order to return every other nth element of a sorted set (nth being the rank in the set, not the score).
There are very few online examples of Lua scripts that can be used to build upon, would anyone be able to point me in the right direction?
local function copyNOtherElements(table, interval, startpos)
local elemno = 1
local rettab = {}
for k, v in ipairs(table) do
if k >= startpos and (k - startpos) % interval == 0 then
rettab[elemno] = v
elemno = elemno + 1
end
end
return rettab
end
Sorry about formatting, typing on a phone. that's assuming the table is a 1 based array
For future readers, adding Redis into the previous answer, and a bit more efficient code to iterate the Nth elements:
local function zrange_pick(zset_key, step, start, stop)
-- The next four lines can be removed along with the start/stop params if not needed as in OP Q.
if start == nil than
start = 0
if end == nil than
end = -1
local set_by_score = redis.call('ZRANGE', zset_key, start, end)
local result = {}
for n = 1, #set_by_score, step do
table.insert(result, set_by_score[n])
end
return result
end