Lua: Why does my table revert to nil after one remove? - lua

The following is part of a script I use to indicate which items (in this case, MOBs in a MUD) are not in my tabled database.
if mqtable[1] == nil then
Note("No mobs missing from database!")
else
if "%1" ~= "" then
mindex = "%1"
mdesc = mqtable[tonumber(mindex)]
end
if "%2" ~= "" then mlvl = "%2" end
if "%3" ~= "" then mkeyw = "%3" end
if not mindex and mqtable[1] then
tprint(mqtable)
elseif mindex and not mlvl then
Note(mdesc)
elseif mindex and mlvl and not mkeyw then
Note("Syntax is: mqmob [index] [level] [keywords]")
else
mobtable[mdesc]={level = mlvl, keywords = mkeyw}
table.save(mobtable,savepath.."/Mobs/"..areazone..".tbl")
Note(mqtable[tonumber(mindex)] .. " saved. Level: ".. mlvl .. " -- Keywords: " .. mkeyw)
table.remove(mqtable, tonumber(mindex))
mobtable = table.load(savepath .. "/Mobs/" .. areazone .. ".tbl")
mlvl, mkeyw, mdesc = nil, nil, nil
end
Assume all undefined functions are working as intended and do not appear to be the root cause. "%1", "%2", and "%3" are passed parameters through a trigger. Essentially, if this were a function, those three parameters would be called as foo(blah, bleh, blargh).
Problem
If I type mqmob (which fires the above script), it displays within the room which MOBs have not been added to my table. I then use mqmob <index> <level> <keyword> to add the mob to that table. Once that has been done, however, the script refuses to work, and regardless of the number of items that were originally on the table, mqmob returns nil. Why is the table being completely wiped after storing one bit of information?
Example
I look in a room and see the following:
A rat dwells here.
A mouse chases after some cheese.
A spider makes its home in the corner.
A flea leaps about playfully.
Say that I have only A spider makes its home in the corner. in the database. The remaining three descriptions are placed into mqtable, and when I call mqmob, it displays:
1="A rat dwells here."
2="A mouse chases after some cheese."
3="A flea leaps about playfully."
Now, I type mqmob 1 20 rat to create mobtable["A rat dwells here."] with {level = 20, keywords = rat}. But once I do that, if I type mqmob again, it returns nil.
Explanation of variables
mobtable is merely a table that contains the mob's description as the key, and its corresponding level and keywords as values. For example:
mobtable = {"A rat nibbles on some cheese." = {keyword = "rat", level = 20}}
mqtable is populated only when there is no corresponding match in mobtable. Essentially, the trigger fires, compares to keys in mobtable, and if not found, populates mqtable. This is to help prevent me from unnecessary work of adding mobs already in mobtable.
The problem I keep running into is that I will add one mob, but when I run the print command a second time, it returns nil. However, I can still add and view the individual indexes in mqtable. It just doesn't seem able to print it up anymore.

Related

How do I get this code to continue to pick a random value from my table? Lua Code

if gdt.LedButton0.ButtonState
then gdt.Lcd0.Text = Random
else gdt.Lcd0.Text = Text
end
Random = ( myTable[ math.random( #myTable ) ] )
I want the code to continue to pick a random value from the table when a button is pressed. Currently, it picks a value the first time it is pressed and sticks with that value until the program is reset
I have tried looping the code, but the problem persists
You forgot to seed() it.
local function SomeFuncThatGetsExecutedOnceAtStartOfProgram()
math.randomseed(os.time())
end
(where math is a local reference to the Math library and os is a local reference to the Operating System library)
Then in your function:
Random = ( myTable[ math.random( #myTable ) ] )
(although, keep in mind # doesn't really return the number of items in a table)
Choose Random before or in your if ... then ... else ... end
Like...
-- Maybe you need to set math.randomseed() first?
-- Then...
-- math.randomseed(math.random(os.time()))
if gdt.LedButton0.ButtonState then
Random = myTable[math.random(#myTable)]
gdt.Lcd0.Text = Random
else
gdt.Lcd0.Text = Text
end

Change result, using :split() [LUA]

i just stuck in editing script.
The lines that make me trouble
local name = Item(item):getName():split('+')
and
doItemSetAttribute(itemEx.uid, ITEM_ATTRIBUTE_NAME, it:getName()..((nLevel>0) and " +"..nLevel or ""))
Ok, this create exitem with new name in game, the result is for example:
Armor +1
My target is to change it and get this:
Armor (1%)
I edited this second line to this:
doItemSetAttribute(itemEx.uid, ITEM_ATTRIBUTE_NAME, it:getName()..((nLevel>0) and " ("..nLevel.."%)" or ""))
And when "upgrade" item in game, the script change item name to:
Armor (1%)
But now by this line local name = Item(item):getName():split('+') The script dont see new item name properly to make next upgrade.
I try local name = Item(item):getName():split('(','%)) etc. But i cant get it.
Should read item as Armor (x%).
I try get help here:
http://lua-users.org/wiki/SplitJoin
But i really cant get it :|
Anyone can throw little light?
This should work, or something very similar. I don't know your expected args for doItemSetAttribute() so I'm guessing there, but this looks right, with the information given.
function string .split( text, delimiter )
local head, tail = text, '' -- default returns what was given, with no added bonus
local location = text :find( delimiter )
if location then -- if expected symbol exists, then do the split
head = text :sub( 1, location -1 ) -- before symbol
tail = text :sub( location +1, -1 ) -- after symbol
end
return head, tail
end
local name, bonus = Item(item) :getName() :split('+')
if #bonus > 0 then bonus = '(' ..bonus ..'%)' end -- add parenthesis if bonus exists
doItemSetAttribute( itemEx.uid, ITEM_ATTRIBUTE_NAME, name ..bonus )

"table expected, got string" when nesting tables and attempting table.remove

I'm currently attempting to create a table of tables, and removing parts from the previous nested table to make the next nested table, thereby decreasing the length of each nested table by 1.
However, upon running the code below, it triggers a bad argument #1 to 'remove' (got string, expected table) error. I can't understand why this is.
possiblePorts = {}
possiblePorts[1] = {"VGA","USB","Ethernet","9mm","HDMI"}
for i=2,5 do
possiblePorts[i] = table.remove(possiblePorts[i-1],math.random(1,5))
end
I'm expecting it to create a table of:
possiblePorts = {
{"VGA","USB","Ethernet","9mm","HDMI"},
{"VGA","Ethernet","9mm","HDMI"},
{"VGA","9mm","HDMI"},
{"9mm","HDMI"},
{"9mm"}
} --formatted for simple viewing
or something similar - why is it not doing so, and what can I do to fix it?
table.remove will return the removed element not the remaining elements of the table.
Lua 5.3 Reference Manual #table.remove
What happens in your code is the first loop works with no issues.
During the second loop, possiblePorts[i-1] is now 2 so we attempt to use table.remove on the value at index 2. The value we put at index 2, in the first loop, was a string so we generate the error trying to pass it as the first arg of table.remove.
You also cannot use math.random(1,5) on each table as that gives you a risk of hitting outside the end of the array, and this will result in an error from table.remove. You want to change 5 out for the length of the array.
This code does what you were trying to accomplish
local possiblePorts = {}
possiblePorts[1] = {"VGA","USB","Ethernet","9mm","HDMI"}
for i=2,5 do
possiblePorts[i] = {}
local skip = math.random(1,#possiblePorts[i-1]) -- Get value we will skip at random
local index = 0 -- Index for new array
for j=1,#possiblePorts[i-1] do -- Loop over all the elements of that last array.
if j ~= skip then -- If the value is not the one we are skipping add it.
index = index + 1
possiblePorts[i][index] = possiblePorts[i-1][j]
end
end
end
for k,v in ipairs(possiblePorts) do
print(k, "{" .. table.concat(v," ") .. "}")
end
Output:
1 {VGA USB Ethernet 9mm HDMI}
2 {USB Ethernet 9mm HDMI}
3 {USB Ethernet HDMI}
4 {Ethernet HDMI}
5 {Ethernet}

Multiple Messages & Issues Searching Lua Tables

I am attempting to make a Lua script for an online community I am a part of, I am having a problem when I attempt to search through a table array I believe. It doesn't detect the results I want.
The way it is supposed to work is that when someone types /gps [streetname] it will search the table at the top, detect the matching streetname & the coordinates and then set a waypoint to that relevant position.
At the moment it works when there is just one entry in the table, but when I put more, it will provide the error message for any non-matching streets & then the waypoint set message for the matching streets. I've Googled and don't appear to be able to find anything to help.
Any help would be appreciated.
waypoint = {
{404.08, -920.23, 'sinnerstreet', 'Sinner Street'},
{360.85, -956.46, 'atleestreet', 'Atlee Street'},
{500.48, -956.80, 'littlebighornavenue', 'Little Bighorn Avenue'},
}
RegisterCommand('gps', function(source, args, rawCommand)
for k,v in pairs(waypoint) do
x, y, streetname, displayname = table.unpack(v)
results = ""
if args[1] == nil then
if IsWaypointActive() then
SetWaypointOff()
TriggerEvent('chatMessage', '^1^*GPS Navigation: ^r^7Your GPS system has been reset.')
return end
elseif args[2] == nil and args[3] == nil then
results = args[1]
elseif args[2] ~= nil and args[3] == nil then
results = args[1] .. args[2]
else
results = args[1] .. args[2] .. args[3]
end
results = string.lower(results) -- This convertes the args into lower case
end
-- This locates the streetname and sets a waypoint to it for the player
if string.find(streetname, results) then
SetNewWaypoint(x, y)
TriggerEvent('chatMessage', '^1^*GPS Navigation: ^r^7Your waypoint to ^1' .. displayname .. '^r^7 has been set.')
else
TriggerEvent('chatMessage', '^1^*GPS Navigation: ^r^7There has been an error with your street name, please try again.')
end
end)
TriggerEvent('chat:addSuggestion', '/gps', 'This creates a waypoint to your designated street. ^*USE: /gps [streetname]')
To be honest, your code makes little to no sense, and it's probably because you're not using all the nice stuff Lua has to offer.
{404.08, -920.23, 'sinnerstreet', 'Sinner Street'},
You're storing redundant data there. The third value is really just the fourth one with spaces removed and all lowercase.
'sinnerstreet' == ('Sinner Street'):gsub("[^%l]", ""):lower()
In english: take "Sinner Street", globally (meaning in the entire string) substitute everything that is not a lowercase (%l) letter with nothing (""), then make the result of that lowercase. What you get is "sinnerstreet".
x, y, streetname, displayname = table.unpack(v)
Using globals there, that's not good. Globals are the devil. Don't use them.
Then, a few lines further down:
SetNewWaypoint(x, y)
Think about it for a moment. You set x and y in each iteration of your for loop. After the loop is done, they always contain the coordinates of the last waypoint you iterated over. I doubt that's what you want. Use local; it forces you to think what you want the scope of your variables to be, which will help you spot this kind of problem.
elseif args[2] ~= nil and args[3] == nil then
results = args[1] .. args[2]
Unless you specifically want to limit it to 3 arguments, which I doubt, you can also use table.concat to concatenate all the values in a sequence (read: array)
results = string.lower( table.concat(args) )
The thing that puzzles me is why you do this in a loop. For every waypoint, you set result to the same value, which is all the arguments concatenated and converted to lower case.
now what though? You check if result (what the user searched for) contains streetname, which, as we have previously found out, contains the name of the last waypoint in the list.
Using tables for searching
Lua has tables, one of if not the most powerful general-purpose data structure in programming.
local map = {}
for _,waypoint in ipairs(waypoints) do
map[waypoint[3]:lower()] = waypoint
end
This will get you something that looks about like this:
local map = {
sinnerstreet = {404.08, -920.23, 'sinnerstreet', 'Sinner Street'},
atleestreet = {360.85, -956.46, 'atleestreet', 'Atlee Street'},
littlebighornavenue ={500.48, -956.80, 'littlebighornavenue', 'Little Bighorn Avenue'},
}
and if you want to know if a street exists, you can just do this:
if map['atleestreet'] then
print(map.atleestreet[4])
end
if treats everything that isn't false or nil as truthy, so you can just write `map['atleestreet'] in the condition
my_table['text'] can be written as my_table.text
Looking up string indices in a table is pretty fast because of how it's implemented.
Conclusion
Try thinking your code through. If necessary, go through it line by line, writing down what values the variables hold in each moment. If you've been at it for a while, get some rest first or do something else for a while.
Then set your variables to local wherever possible (read: everywhere), figure out what needs to be inside and outside the loop and try again.
Remarks
Instead of if something == nil you can just write if not something, and if something ~= nil just if something
Apologies
Sorry for the long wall of text and using spaces inside brackets, but I wanted things to be specially easy to understand.

Ruby - Writing a program that guesses letters in a word

Background: Using a class, I'm building a hangman-like game where player 1 enters a word to be guessed, and player 2 attempts to guess it. The number of guesses allotted to player 2 is relative to the word in question, but repeated guesses do not count against the player.
Each guess should provide continual feedback to player 2 by showing their progress towards guessing the word, which should be printed at the end of each guessing phase. ex) The word 'code' would be displayed as "_ _ _ _ ", if the letter 'o' were to be guessed, the feedback would look like " _ o _ _", etc. Once the word is guessed or a player has 0 attempts left, a winner is announced.
Issue 1: I can't get the program to close when the game_won? method evaluates to true. It continues to run until attempts == game_word.length + 2. Any ideas on how to end the program with the winning statement?
Issue 2: I tried adding a game_lost method, but couldn't get it to work once player 2 runs out of attempts (tried creating an instance variable to be tied to attempts in the interface, but whenever it was called outside of the class, an error popped up stating that it was a nil class, rather than an integer. How can I make a functional method that states when the game is lost?
Issue 3: Whenever an incorrect letter is entered, the "Nope, try again..." response is printed out as many times as the length of the word is. It seems to be printing out whatever is evaluated last, x amount of times, in the guess_the_letter method.
Issue 4: If the word entered contains more than one of the same letter, the word progress update will appear as many times as that letter exists within the game word. (Seems to be a similar issue to issue 3) Any ideas as to what I'm doing wrong here.
class GuessingGame
def initialize(word)
#word = word.downcase
#display_word = "_" * word.length
end
# Take a guess in the form of a letter and check to see if it is in the
# target word, update the word pattern to include the missing letter
def guess_the_letter(g_letter)
g_letter.downcase
#word.split("").each_with_index do |w_letter, index|
if g_letter == w_letter
#display_word[index] = g_letter
puts "Here is your progress towards guessing the word:"
p #display_word
end
if !#word.include? (g_letter)
puts "Nope, try again..."
end
end
end
# Determine winning conditions
def game_won?
if #word == #display_word
puts "Congratulations Player 2, you won!"
true
else
false
end
end
def game_lost?
#method body
end
end
puts "Welcome to the Word Guessing Game!"
puts "This game is for 2 players."
puts "Player 1, please enter a word for player 2 to guess..."
game_word = gets.chomp
game = GuessingGame.new(game_word)
attempts = 0
guessed_letters = []
#Create an interface for the users to play the game
until attempts == game_word.length + 2
puts "Player 2, please guess a letter..."
letter_guess = gets.chomp
if guessed_letters.include? letter_guess
puts "You already tried that letter, enter a new one."
letter_guess = gets.chomp
end
guessed_letters << letter_guess
game.guess_the_letter(letter_guess)
game.game_won?
attempts += 1
end
When asking this kind of question, you should indicate, where line 46 is.
In your case, I guess it's the expression attempts == #word.length. You are not in class context, so #word does not refer to the instance variable in GuessingGame.
You can not directly access an instance variable of another object (i.e. an object different to self), so you need to provide an accessor method. Ruby makes this easy using attr_reader:
attr_reader :word
creates a read-accessor method to #word which is called word.
UPDATE: I just see that you have modified your original code. With the new code, you will have the same problem with #bad_guesses.
BTW, if you edit your posting, please always explain what you changed.

Resources