I use this ambulance job that is made in Lua. Unfortunately, being an old script, it's not updated, so it was still set to use "limit" which I changed to "weight" since my gamemode is based on weight as well as the database. But sadly it doesn't work, I get this error.
RegisterServerEvent('esx_ambulancejob:giveItem')
AddEventHandler('esx_ambulancejob:giveItem', function(itemName)
local xPlayer = ESX.GetPlayerFromId(source)
if xPlayer.job.name ~= 'ambulance' then
print(('esx_ambulancejob: %s attempted to spawn in an item!'):format(xPlayer.identifier))
return
elseif (itemName ~= 'medikit' and itemName ~= 'bandage') then
print(('esx_ambulancejob: %s attempted to spawn in an item!'):format(xPlayer.identifier))
return
end
local xItem = xPlayer.getInventoryItem(itemName)
local count = 1
if xItem.weight ~= -1 then (it was xItem.limit before)
count = xItem.weight - xItem.count
end
if xItem.count < xItem.weight then
xPlayer.addInventoryItem(itemName, count)
else
TriggerClientEvent('esx:showNotification', source, _U('max_item'))
end
end)
The error screen when I try to take the item from the pharmacy:
Console Error Image
I don't know which line is 263, but error says object_name.weight is nil.
Watching code, xItem doen't have field weight (xItem.weight = nil).
error is on this line
if xItem.weight ~= -1 then (it was xItem.limit before)
count = xItem.weight - xItem.count
end
for knoing what value is null, you can make, just before a print like this
print("Test: " .. tostring(xItem.weith) .. " - " .. tostring(xItem.count);
But, i think that your operation is false becouse you do
xItem.weight - xItem.count;
maybe you want to make
xItem.weight * xItem.count;
Here is the code I made sure to comment the minimax functions so I can make it easier for you guys to understand. I have tried to give it a go and try to understand what each bit does and I am not confident on what each step is doing or if it is doing what I want correctly.
I should say I tried following this tutorial (https://www.freecodecamp.org/news/minimax-algorithm-guide-how-to-create-an-unbeatable-ai/), I think the website did a good job about explaining how minimax works but the code used in the tutorial is written in a different language I have tried my best to convert it to my lua tic tac toe game, without the AI/minimax algorithm the game is a great 2 player game, but I wanted to challenge my self and give a go at making an ai for tic tac toe, this is how far I have gone.
local board_data = {top_L= " ", top_M = " ", top_R= " ", mid_L= " ", mid_M= " ", mid_R= " ", low_L= " ", low_M= " ",
low_R= " "}
local location = {"top","mid","low"}
local position = {"_L","_M","_R"}
local function find_empty_slots(board_info)
local empty_slots = {}
for key, value in pairs(board_info) do
if value == " " then
table.insert(empty_slots,key)
end
end
return empty_slots
end
local function _draw_board() -- Draws the games table
print(
board_data["top_L"].."┃"..board_data["top_M"].."┃"..board_data["top_R"].."\n"..
"------\n"..
board_data["mid_L"].."┃"..board_data["mid_M"].."┃"..board_data["mid_R"].."\n"..
"------\n"..
board_data["low_L"].."┃"..board_data["low_M"].."┃"..board_data["low_R"].."\n"
)
end
local function _check_if_position_exists_and_is_empty(_input) -- Checks if the user has entered a valid position and that it is empty
return board_data[_input] == " "
end
local function _check_win_condition(current_turn) --Checks if the current player has won
for i = 1, 3 do --Firstly checks each rows
if board_data[location[i]..position[1]] == current_turn and board_data[location[i]..position[2]] == current_turn and board_data[location[i]..position[3]] == current_turn then
return true --Then checks columns
elseif board_data[location[1]..position[i]] == current_turn and board_data[location[2]..position[i]] == current_turn and board_data[location[3]..position[i]] == current_turn then
return true
end -- Then checks diagonals
if board_data[location[1]..position[1]] == current_turn and board_data[location[2]..position[2]] == current_turn and board_data[location[3]..position[3]] == current_turn then
return true
elseif board_data[location[3]..position[1]] == current_turn and board_data[location[2]..position[2]] == current_turn and board_data[location[1]..position[3]] == current_turn then
return true
end
end
end
local function check_tie_condition()
local is_not_empty = 0
for i = 1,3 do
if board_data[location[i]..position[1]] ~= " " and board_data[location[i]..position[2]] ~= " " and board_data[location[i]..position[3]] ~= " " then
is_not_empty = is_not_empty + 3
end
if is_not_empty == 9 then
return true
end
end
end
local function get_array_size(array)
local count = 0
for i in pairs(array) do
count = count + 1
end
return count
end
local function minimax(board,current_player) -- MINIMAX ai
local empty_slots = find_empty_slots(board) -- Finds the empty slots on the tic tac toe board
if _check_win_condition("X")then --Checks if someone has won,lost, or draw
return -1
elseif _check_win_condition("O") then
return 1
elseif empty_slots == 0 then
return 0
end
local all_test_plays = {} --Stores all the plays of the ai
for i = 1, get_array_size(empty_slots) do -- Loops
local current_test_play = {} --a current fake/test play of the game
current_test_play["Index"] = empty_slots[i] --Creates a key called Index and sets the empty slots key to it, the keys are strings, check board data to understand
board_data[empty_slots[i]] = current_player --Sets the empty slot to the current player which would be ai for the moment
if current_player == "O" then -- Checks whos playing and apply the minimax function again to produce a recursion
local result = minimax(board_data,"X") -- Get the result from going again in the board
current_test_play["Score"] = result -- Then saves the result to a new key called score
else
local result = minimax(board_data,"O")
current_test_play["Score"] = result
board_data[empty_slots[i]] = " " -- We set the slots index to a blank/empty when done
table.insert(all_test_plays,current_test_play) -- Insert the current_test_play to all test_plays to compare
end
local best_play = nil -- a place to store the best play for the ai
local best_score = nil -- stores the best score aswell
if current_player == "O" then -- O is the ai in this case
best_score = -1000 -- Not sure why I need to do this, I followed some tutorial written in a different language that used infinity which I assumed it just a big number
for i = 1, get_array_size(all_test_plays) do -- Loop through all the plays avaliable
if all_test_plays[i]["Score"] > best_score then -- compare to see if the score is better then best_score once again i dont have a clue
best_score = all_test_plays[i]["Score"] --Seting the score and play
best_play = all_test_plays[i]["Index"]
end
end
else
best_score = 1000 -- pretty much a copy and past from the top except its not negative
for i = 1, get_array_size(all_test_plays) do
print(all_test_plays[i]["Score"])
if all_test_plays[i]["Score"] < best_score then --THIS IS ERROR LINE 107
best_score = all_test_plays[i]["Score"]
best_play = all_test_plays[i]["Index"]
end
end
end
return best_play -- we return it so this would be the best play the ai would make I think.
end
end
local function _game() --The main loop of the game
local current_turn = "X" -- it holds two states X or O
print("Please select a position from the board, to access the top row, type top_position position being L, M , R e.g top_L \n")
_draw_board()
while true do
print("Player " ..current_turn.. " is selected!")
if current_turn == "O" then -- Checks if its Os turn/AI and make it play the minimax function
local play = minimax(board_data,"O")
print(play)
current_turn = "X"
else -- Else its the players turn
local Input = io.read()
if _check_if_position_exists_and_is_empty(Input) then
board_data[Input] = current_turn
_draw_board()
if _check_win_condition(current_turn) then
print("Player "..current_turn .. " Has won this game!")
break
elseif check_tie_condition() then
print("The game has ended at a tie!")
break
end
if current_turn == "X" then -- Switch to the other player
current_turn = "O"
elseif current_turn == "O" then
current_turn = "X"
end
else
print("\nThat wasnt a command in the table or the slot wasnt empty!, example top_L, mid_R, low_M \n")
end
end
end
end
_game()
Here is the error I received, I also tried including some of the print statements that I put trying to figure out why it didnt do things correctly, or maybe I forgot to do something. I have also commented on which line the error , error-ed out
Player O is selected!
-1
nil
lua: /mnt/1TB SSD/Lua Projects/Tic Tac Toe/main.lua:107: attempt to compare nil with number
stack traceback:
/mnt/1TB SSD/Lua Projects/Tic Tac Toe/main.lua:107: in upvalue 'minimax'
/mnt/1TB SSD/Lua Projects/Tic Tac Toe/main.lua:84: in upvalue 'minimax'
/mnt/1TB SSD/Lua Projects/Tic Tac Toe/main.lua:87: in upvalue 'minimax'
/mnt/1TB SSD/Lua Projects/Tic Tac Toe/main.lua:84: in upvalue 'minimax'
/mnt/1TB SSD/Lua Projects/Tic Tac Toe/main.lua:124: in local '_game'
/mnt/1TB SSD/Lua Projects/Tic Tac Toe/main.lua:153: in main chunk
[C]: in ?
The terminal process "bash '-c', 'lua '/mnt/1TB SSD/Lua Projects/Tic Tac Toe/main.lua''" terminated with exit code: 1.
I won't do all the debugging for you, but at some point table all_test_plays is empty.
This causes minimax's return value best_play to become nil as the loop that could assign a value to it is not run due to the empty table.
This is then assigned to the Score field which later is compared vs a number.
Your logic seems to be broken. Go back through your code and compare it with the tutorial's code. Most importantly make sure you understand what this algorithm is doing. you should be able to do the same with pen and paper. Ensure that your "AI" is doing what you would do. At some point there will be a difference.
I have a motorized roller blind project.
I am following this instructable: https://www.instructables.com/id/Motorized-WiFi-IKEA-Roller-Blind/ .
I am using the code in the instructable, but I suspect tha fault is in one of these codes:
config.lua
-- file : config.lua
local module = {}
module.SSID = {}
module.SSID["ssid"] = "password"
-- example for local MQTT
--module.MQHOST = "ohab.local"
--module.MQPORT = 1883
--module.MQID = node.chipid()
--module.MQUSR = ""
--module.MQPW = ""
-- example for cloud MQTT
module.MQHOST = "192.***.*.*"
module.MQPORT = 1883
module.MQID = node.chipid()
module.MQUSR = "username"
module.MQPW = "password"
module.MQTLS = 1 -- 0 = unsecured, 1 = TLS/SSL
module.ENDPOINT = "/house/masterbedroom/rollerblind/"
module.ID = "0"
--module.SUB = "set"
module.SUB = {[module.ENDPOINT .. module.ID .. "/set"]=0,[module.ENDPOINT .. "all"]=0}
module.POST = module.ENDPOINT .. module.ID .. "/status"
return module
wifi_setup
-- file: setup.lua
local module = {}
local function wifi_wait_ip()
if wifi.sta.getip()== nil then
print("IP unavailable, Waiting...")
else
tmr.stop(1)
gpio.write(pin_led,1) --off
print("\n================== ==================")
print("ESP8266 mode is: " .. wifi.getmode())
print("MAC address is: " .. wifi.ap.getmac())
print("IP is "..wifi.sta.getip())
print("====================================")
mq.start()
end
end
local function wifi_start(list_aps)
if list_aps then
gpio.write(pin_led,0) --on
for key,value in pairs(list_aps) do
if config.SSID and config.SSID[key] then
wifi.setmode(wifi.STATION);
wifi.sta.config(key,config.SSID[key])
wifi.sta.connect()
print("Connecting to " .. key .. " ...")
--config.SSID = nil -- can save memory
tmr.alarm(1, 2500, 1, wifi_wait_ip)
end
end
else
print("Error getting AP list")
end
end
function module.start()
print("Configuring Wifi ...")
wifi.setmode(wifi.STATION);
wifi.sta.getap(wifi_start)
end
return module
Sadly I cant manage to go beyond step 4. After I have tried to download the code to the ESP8266 I only get this error:
PANIC: unprotected error in call to Lua API (wifi_setup.lua:25: bad argument #1 to 'config' (config table not found!))
I have only changed the stuff the instructable told me to change, and I have tried to look for faults myself but I cant find any... It's the first time I am using Lua, so this is totally new for me.
Hope somebody here can offer some help. Been stuck on this for days...
THIS PART OF THE PROBLEM IS SOLVED. CHECK COMMENTS FOR SOLUTION
Sadly when one problem is solved, a new one rises...
The chip is now successfully connecting to the wifi and mqtt service, but when I try to do the dry run by putting in the command 'step_move(1000,FWD,2)' nothing happens. The motor should rotate...
Also when I press the button I get a new panic error as follows:
PANIC: unprotected error in call to Lua API (button.lua:23: attempt to perform arithmetic on upvalue '?' (a nil value))
button.lua
--file button4.lua
do
-- use pin 1 as the input pulse width counter
local pin=5
local debounce = 150 --ms
local longpress = 2000 --ms
local pulse1, pulse2, du, now, trig = 1, 0, 0, tmr.now, gpio.trig
local prev_int_time, int_time, up_time = 0
local cal_steps = 100000
local cal_steps_dn = 0
local cal_steps_up = 0
local cal_state = 0 -- 0 = not calibration, 1 = calibrating down, 2 = calibrating up
state = 0 -- state: 0 = up, 1 = transition, 2 = down
gpio.mode(pin,gpio.INT)
local function pin4cb(level)
int_time = now() / 1000
if ((int_time - prev_int_time) > debounce) then
if (level == 0) then
up_time = int_time
else
if((int_time - up_time) > longpress) then
print("calibrating")
cal_state = 1
--cur_step = 100000
step_move(cal_steps,FWD,2)
else -- short press
print("short", cal_state)
if (cal_state == 2) then -- calibrated up (done)
print("calibration done")
state = 0 -- up
cur_step = 0
tot_steps = cal_steps - step_stepsleft
print("cal_steps: " .. cal_steps)
print("step_stepsleft: " .. step_stepsleft)
print("tot_steps: " .. tot_steps)
step_stop()
pins_disable()
cal_state = 0
if file.open("cfg_tot_steps.lua", "w+") then
file.write("tot_steps=" .. tot_steps .. '\n')
file.close()
end
elseif (cal_state == 1) then -- calibrated dn (switch direction)
print("calibration low point")
print(cal_steps - step_stepsleft)
step_stop()
step_move(cal_steps,REV,2)
cal_state = 2
elseif (cal_state == 0) then
if (state == 0 and step_stepsleft == 0) then -- i am up, go dowm
rollerblind.down()
-- state = 2
elseif (state == 1) then -- i am moving, do nothing
-- do nothing
elseif (state == 2 and step_stepsleft == 0) then -- i am down, go up
rollerblind.up()
-- state = 0
end
end
end
end
--print (level)
prev_int_time = int_time
end
end
gpio.trig(pin, "both", pin4cb)
end
Here is the code for the stepper.lua:
-- stepper.lua
-- code from: http://www.esp8266.com/viewtopic.php?f=19&t=2326
-- simple stepper driver for controlling a stepper motor with a
-- l293d driver
-- nodemcu pins: 0 5 6 7
stepper_pins = {1,3,2,4} -- (A-)blue, (A+)pink, (B-)yellow, (B+)orange
--stepper_pins = {1,2,3,4}
-- half or full stepping
step_states4 = {
{1,0,0,1},
{1,1,0,0},
{0,1,1,0},
{0,0,1,1}
}
step_states8 = {
{1,0,0,0},
{1,1,0,0},
{0,1,0,0},
{0,1,1,0},
{0,0,1,0},
{0,0,1,1},
{0,0,0,1},
{1,0,0,1},
}
step_states = step_states4 -- choose stepping mode
step_numstates = 4 -- change to match number of rows in step_states
step_delay = 10 -- choose speed
step_state = 0 -- updated by step_take-function
step_direction = 1 -- choose step direction -1, 1
step_stepsleft = 0 -- number of steps to move, will de decremented
step_timerid = 4 -- which timer to use for the steps
status_timerid = 2 -- timer id for posing of status messages
-- setup pins
function pins_enable()
for i = 1, 4, 1 do
gpio.mode(stepper_pins[i],gpio.OUTPUT)
end
end
function pins_disable()
-- for i = 1, 4, 1 do -- no power, all pins
for i = 2, 4, 1 do -- no power, all pins except one (to keep it in place)
gpio.mode(stepper_pins[i],gpio.INPUT)
end
end
-- turn off all pins to let motor rest
function step_stopstate()
for i = 1, 4, 1 do
gpio.write(stepper_pins[i], 0)
end
end
-- make stepper take one step
function step_take()
-- jump to the next state in the direction, wrap
step_state = step_state + step_direction
cur_step = cur_step + step_direction * FWD
if step_state > step_numstates then
step_state = 1;
elseif step_state < 1 then
step_state = step_numstates
end
-- write the current state to the pins
pins_enable()
for i = 1, 4, 1 do
gpio.write(stepper_pins[i], step_states[step_state][i])
end
-- might take another step after step_delay
step_stepsleft = step_stepsleft-1
if step_stepsleft > 0 then
-- if cur_step > 0 and cur_step < tot_steps and step_stepsleft > 0 then
tmr.alarm(step_timerid, 10, 0, step_take )
--tmr.alarm(step_timerid, 10, 0, step_take )
else
step_stopstate()
step_stop()
pins_disable()
mq.post_status()
if file.open("cfg_cur_step.lua", "w+") then
file.write("cur_step=" .. cur_step .. '\n')
file.close()
end
end
end
-- public method to start moving number of 'int steps' in 'int direction'
function step_move(steps, direction, delay)
tmr.stop(step_timerid)
step_stepsleft = steps
step_direction = direction
step_delay = delay
step_take()
end
function step_go_to(step, delay)
if step >= cur_step then
steps = step - cur_step
step_move(steps, FWD, delay)
end
if step <= cur_step then
steps = cur_step - step
step_move(steps, REV, delay)
end
end
function percent_go_to(percent, delay)
if(percent >= 0 and percent <= 100) then
step_stop()
tmr.register(status_timerid, 1000, tmr.ALARM_AUTO, function () mq.post_status() end)
tmr.start(status_timerid)
step = percent * tot_steps / 100
step_go_to(step, delay)
end
end
-- public method to cancel moving
function step_stop()
tmr.stop(step_timerid)
tmr.stop(status_timerid)
step_stepsleft = 0
step_stopstate()
end
Let's parse the error message one by one:
unprotected error in call to Lua API (wifi_setup.lua:25: bad argument #1 to 'config' (config table not found!))
Unprotected error means that you did a normal function call, as opposed to a protected call (aka pcall), which is a function call where you expect errors to occur and want to provide means to handle them. Since you did not do a protected call, Lua does not know how to handle the error and terminates right away (which is not a bad thing).
wifi_setup.lua:25 tells you the file and line at which the error occured.
bad argument #1 to 'config' means that the problem is due to the first argument passed to a function named config.
config table not found! is the error message provided by the implementer of that function.
So in summary, there is a problem with the function call wifi.sta.config(key,config.SSID[key]) as it expects a table as first argument, while you provided something different (ie. a string with the BSSID). Indeed checking with the NodeMCU docs shows that you need to pass a table to this function:
wifi.sta.config()
Sets the WiFi station configuration. [...]
Syntax
wifi.sta.config(station_config)
Parameters
station_config table containing configuration data for
station
The expected layout of the table is documented in detail on that page as well.
ComicSansMS provided a great answer the essence of which is that you need to replace
wifi.sta.config(key,config.SSID[key])
with
wifi.sta.config{ssid=key,pwd=config.SSID[key]}
Thus a standalone example could be like this:
--wifi.setmode(wifi.NULLMODE)
config = {}
config.SSID = {}
config.SSID["ssid"] = "password"
function wifi_wait_ip()
if wifi.sta.getip() == nil then
print("IP unavailable, Waiting...")
else
tmr.stop(1)
print("\n====================================")
print("ESP8266 mode is: " .. wifi.getmode())
print("MAC address is: " .. wifi.ap.getmac())
print("IP is " .. wifi.sta.getip())
print("====================================")
end
end
function wifi_start(list_aps)
if list_aps then
for key, value in pairs(list_aps) do
if config.SSID and config.SSID[key] then
wifi.setmode(wifi.STATION);
wifi.sta.config{ssid=key, pwd=config.SSID[key]}
-- wifi.sta.connect() not needed as config() uses auto-connect=true by default
print("Connecting to " .. key .. " ...")
tmr.alarm(1, 2500, 1, wifi_wait_ip)
end
end
else
print("Error getting AP list")
end
end
function start()
print("Configuring Wifi ...")
wifi.setmode(wifi.STATION)
wifi.sta.getap(wifi_start)
end
start()
I'm trying to send a HTTP GET each time I press a button :
wifi.setmode(wifi.STATION)
wifi.sta.config("SSID","PWD")
function loop()
if wifi.sta.status() == 5 then
-- Stop the loop
tmr.stop(0)
else
print("Connecting...")
end
end
tmr.alarm(0, 100, 1, function() loop() end)
print(wifi.sta.getip())
outpin_led = 1
inpin_button = 3
gpio.mode(outpin_led,gpio.OUTPUT)
gpio.mode(inpin_button,gpio.INPUT)
light_on = false
function light()
if not light_on then
-- turn the light on
gpio.write(outpin_led,gpio.HIGH)
light_on = true
http.get("https://google.com", function(clt, data)
print(data)
end)
else
-- turn the light off
gpio.write(outpin_led,gpio.LOW)
light_on = false
end
end
gpio.trig(inpin_button,"down",light)
The line containing http.get is throwing this error message :
> PANIC: unprotected error in call to Lua API (stdin:6: attempt to index global 'http' (a nil value))
I made sure my NodeMCU build contained the http module by compiling it through http://nodemcu-build.com/
Any idea ?
Thanks.
As Marcel Stör pointed out, it was indeed an issue during the flashing of the firmware.
Thanks a lot for your reply and for your work on http://nodemcu-build.com/.
I'm working on a project where I want to update the clock on screen say every 5 seconds unless the user inputs something. This is the code I have so far,
function thread1()
term.clear()
term.setCursorPos(1,1)
write (" SteveCell ")
local time = os.time()
local formatTime = textutils.formatTime(time, false)
write (formatTime)
print ("")
print ("")
for i=1,13 do
write ("-")
end
print("")
print ("1. Clock")
print ("2. Calender")
print ("3. Memo")
print ("4. Shutdown")
for i=1,13 do
write ("-")
end
print ("")
print ("")
write ("Choose an option: ")
local choice = io.read()
local choiceValid = false
if (choice == "1") then
-- do this
elseif (choice == "2") then
-- do that
elseif (choice == "3") then
-- do this
elseif (choice == "4") then
shell.run("shutdown")
else
print ("Choice Invalid")
os.sleep(2)
shell.run("mainMenu")
end
end
function thread2()
localmyTimer = os.startTimer(5)
while true do
local event,timerID = os.pullEvent("timer")
if timerID == myTimer then break end
end
return
end
parallel.waitForAny(thread1, thread2)
shell.run("mainMenu")
Unfortunately it's not working. If someone could help me with this, I would really appreciate it. Thanks :)
You want to do something like this (Im not doing the correct on screen drawing, only the time)
local function thread1_2()
-- both threads in one!
while true do
local ID_MAIN = os.startTimer(5)
local ID = os.startTimer(0.05)
local e = { os.pullEvent() }
if e[1] == "char" then
-- Check all the options with variable e[2] here
print( string.format( "Pressed %s", e[2] ) )
break -- Getting out of the 'thread'
elseif e[1] == "timer" and e[2] == ID then
ID = os.startTimer(0.05) -- shortest interval in cc
redrawTime() -- Redraw and update the time in this function!!!
elseif e[1] == "timer" and e[2] == MAIN_ID then
break
end
end
end
Also, ask this in the proper forum, you have more chance getting an answer there!
Another note, get more into event handling, it really helps.
FYI Lua doesn't have 'multi-threading' as in executing multiple routines simultaneously. What it does have is 'thread parking.' You can switch between routines (yielding) and switch back and it will resume where it left off, but only a single routine will be active at any given time.
This is my go-to Lua reference, which explains in detail:
http://lua-users.org/wiki/CoroutinesTutorial