I am trying to change a string that is the word for a number between one and fifty to the number value, i.e. if
local = "twelve" it will make local_num = 12, if local = "one" then local_num = 1, etc. Do I just use an array and loop a compare?
There is no need for a loop. Just use this table directly:
Word2Number={
["zero"]=0,
["one"]=1,
["two"]=2,
["three"]=3,
["four"]=4,
["five"]=5,
["six"]=6,
["seven"]=7,
["eight"]=8,
["nine"]=9,
["ten"]=10,
["eleven"]=11,
["twelve"]=12,
["thirteen"]=13,
["fourteen"]=14,
["fifteen"]=15,
["sixteen"]=16,
["seventeen"]=17,
["eighteen"]=18,
["nineteen"]=19,
["twenty"]=20,
["twenty-one"]=21,
["twenty-two"]=22,
["twenty-three"]=23,
["twenty-four"]=24,
["twenty-five"]=25,
["twenty-six"]=26,
["twenty-seven"]=27,
["twenty-eight"]=28,
["twenty-nine"]=29,
["thirty"]=30,
["thirty-one"]=31,
["thirty-two" ]=32,
["thirty-three" ]=33,
["thirty-four"]=34,
["thirty-five"]=35,
["thirty-six"]=36,
["thirty-seven"]=37,
["thirty-eight"]=38,
["thirty-nine"]=39,
["forty"]=40,
["forty-one"]=41,
["forty-two"]=42,
["forty-three"]=43,
["forty-four"]=44,
["forty-five"]=45,
["forty-six"]=46,
["forty-seven"]=47,
["forty-eight"]=48,
["forty-nine"]=49,
["fifty"]=50,
}
This is the code to spell an integer
local append, concat, floor, abs = table.insert, table.concat, math.floor, math.abs
local num = {'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten', 'eleven',
'twelve', 'thirteen', 'fourteen', 'fifteen', 'sixteen', 'seventeen', 'eighteen', 'nineteen'}
local tens = {'twenty', 'thirty', 'forty', 'fifty', 'sixty', 'seventy', 'eighty', 'ninety'}
local bases = {{floor(1e18), ' quintillion'}, {floor(1e15), ' quadrillion'}, {floor(1e12), ' trillion'},
{floor(1e9), ' billion'}, {1000000, ' million'}, {1000, ' thousand'}, {100, ' hundred'}}
local insert_word_AND = false -- 101 = "one hundred and one" / "one hundred one"
local function IntegerNumberInWords(n)
-- Returns a string (spelling of integer number n)
-- n should be from -2^53 to 2^53 (-2^63 < n < 2^63 for integer argument in Lua 5.3)
local str = {}
if n < 0 then
append(str, "minus")
end
n = floor(abs(n))
if n == 0 then
return "zero"
end
if n >= 1e21 then
append(str, "infinity")
else
local AND
for _, base in ipairs(bases) do
local value = base[1]
if n >= value then
append(str, IntegerNumberInWords(n / value)..base[2])
n, AND = n % value, insert_word_AND or nil
end
end
if n > 0 then
append(str, AND and "and") -- a nice pun !
append(str, num[n] or tens[floor(n/10)-1]..(n%10 ~= 0 and '-'..num[n%10] or ''))
end
end
return concat(str, ' ')
end
And this is how to use it:
local function digitize(number_as_text)
number_as_text = number_as_text:lower():gsub("%W", "")
for i = 1, 50 do
if IntegerNumberInWords(i):lower():gsub("%W", "") == number_as_text then
return i
end
end
end
print(digitize(io.read()))
Related
I try to fix some old Basewars script for Gmod amd there is something i tried to fix but i dont know what i am missing there. Basicly its about a Bank script. This Bank could original store a amount of 2 Billion $. I would like to expand that storage amount to 1 Quadtrillion.
So Basicly the Basewars Gamemode allows players to have an high amount of money up to 10^18 $
= 1 Quintillion. I looked up into https://wiki.garrysmod.com/page/Category:number
so basicly lua uses double-precision floating-point format. So lua uses 32 bits for storing numbers and that means the number can be from a range of
-2,147,483,647 to 2,147,483,647.
The Basewars Gamemode uses a Function to make out of 10^18
= 1 Quintillion
function BaseWars.NumberFormat(num)
local t = BaseWars.LANG.Numbers
for i = 1, #t do
local Div = t[i][1]
local Str = t[i][2]
if num >= Div or num <= -Div then
return string.Comma(math.Round(num / Div, 1)) .. " " .. Str
end
end
return string.Comma(math.Round(num, 1))
end
The Gamemode is using that faction to convert the amount of money.
and here is the BaseWars.LANG.Numbers:
BaseWars.LANG = {}
BaseWars.LANG.__LANGUAGELOOK = {}
BaseWars.LANG.__LANGUAGELOOK.ENGLISH = {
Numbers = {
[5] = {10^6, "Million"},
[4] = {10^9, "Billion"},
[3] = {10^12, "Trillion"},
[2] = {10^15, "Quadtillion"},
[1] = {10^18, "Quintillion"},
},
CURFORMER = CURRENCY .. "%s",
CURRENCY = CURRENCY,
}
So i know that this function does work but i dont get it, how the variable num can be that high!
Why do i know what it does work ?
Here is what i tried and it manipulated the Capacity up to 1 Quadtrillion but at the point i manipluated a other Problem came up and i do not know what i do wrong!
Here how it was Original:
ENT.Capacity = 2000000000 --Max money in the bank. Can be bugs if it is more than 2000000000 (2 bil)
ENT.Money = 0
ENT.MaxPaper = 0
local Clamp = math.Clamp
function ENT:GSAT(slot, name, min, max)
self:NetworkVar("Int", slot, name)
local getVar = function(minMax)
if self[minMax] and isfunction(self[minMax]) then return self[minMax](self) end
if self[minMax] and isnumber(self[minMax]) then return self[minMax] end
return minMax or 0
end
self["Add" .. name] = function(_, var)
local Val = self["Get" .. name](self) + var
if min and max then
Val = Clamp(tonumber(Val) or 0, getVar(min), getVar(max))
end
self["Set" .. name](self, Val)
end
self["Take" .. name] = function(_, var)
local Val = self["Get" .. name](self) - var
if min and max then
Val = Clamp(tonumber(Val) or 0, getVar(min), getVar(max))
end
self["Set" .. name](self, Val)
end
end
function ENT:StableNetwork()
self:GSAT(2, "Capacity")
self:GSAT(3, "Money", 0, "GetCapacity")
self:GSAT(4, "Paper", 0, "MaxPaper")
self:GSAT(5, "Level", 0, "MaxLevel")
end
function ENT:ThinkFunc() --This Funtion is to auto collect all the Money of Printers
for k, v in pairs( ents.FindByClass( "bw_printer_*" ) ) do
if v:CPPIGetOwner() == self:CPPIGetOwner() then
if self:GetMoney() < self.Capacity then
local allmoney = v:GetMoney()
v:TakeMoney(allmoney)
self:AddMoney(allmoney)
end
end
end
for k, v in pairs( ents.FindByClass( "bw_base_moneyprinter" ) ) do
if v:CPPIGetOwner() == self:CPPIGetOwner() then
if self:GetMoney() < self.Capacity then
local allmoney = v:GetMoney()
v:TakeMoney(allmoney)
self:AddMoney(allmoney)
end
end
end
end
if CLIENT then
local Cp = self:GetCapacity()
local money = tonumber(self:GetMoney()) or 0
local cap = tonumber(Cp) or 0
local moneyPercentage = math.Round( money / cap * 100 ,1)
draw.DrawText( moneyPercentage .."%" , fontName .. ".Huge", w - 4-430, 71+610, self.FontColor, TEXT_ALIGN_CENTER)
local currentMoney = BaseWars.LANG.CURRENCY .. BaseWars.NumberFormat(money)
local maxMoney = BaseWars.LANG.CURRENCY .. BaseWars.NumberFormat(cap)
local font = fontName .. ".Big"
if #currentMoney > 16 then
font = fontName .. ".MedBig"
end
if #currentMoney > 20 then
font = fontName .. ".Med"
end
local fh = draw.GetFontHeight(font)
local StrW,StrH = surface.GetTextSize("")
local moneyW,moneyH = surface.GetTextSize(currentMoney)
draw.DrawText(currentMoney.. " / " .. BaseWars.LANG.CURRENCY .. BaseWars.NumberFormat(cap) , font,
w/2 - StrW/2-225 , (font == fontName .. ".Big" and 106 or 105 + fh / 4)+675, self.FontColor, TEXT_ALIGN_CENTER)
end
Here the changes i did:
removed line:
local cap = tonumber(Cp) or 0
added
local cap = 10^15
if self:GetMoney() < cap then
ENT.Capacity = 2000000000 --Max money in the bank. Can be bugs if it is more than 2000000000 (2 bil)
local cap = 10^15
ENT.Money = 0
ENT.MaxPaper = 0
local Clamp = math.Clamp
function ENT:GSAT(slot, name, min, max)
self:NetworkVar("Int", slot, name)
local getVar = function(minMax)
if self[minMax] and isfunction(self[minMax]) then return self[minMax](self) end
if self[minMax] and isnumber(self[minMax]) then return self[minMax] end
return minMax or 0
end
self["Add" .. name] = function(_, var)
local Val = self["Get" .. name](self) + var
if min and max then
Val = Clamp(tonumber(Val) or 0, getVar(min), getVar(max))
end
self["Set" .. name](self, Val)
end
self["Take" .. name] = function(_, var)
local Val = self["Get" .. name](self) - var
if min and max then
Val = Clamp(tonumber(Val) or 0, getVar(min), getVar(max))
end
self["Set" .. name](self, Val)
end
end
function ENT:StableNetwork()
self:GSAT(2, "Capacity")
self:GSAT(3, "Money", 0, "GetCapacity")
self:GSAT(4, "Paper", 0, "MaxPaper")
self:GSAT(5, "Level", 0, "MaxLevel")
end
function ENT:ThinkFunc() --This Funtion is to auto collect all the Money of Printers
for k, v in pairs( ents.FindByClass( "bw_printer_*" ) ) do
if v:CPPIGetOwner() == self:CPPIGetOwner() then
if self:GetMoney() < cap then
local allmoney = v:GetMoney()
v:TakeMoney(allmoney)
self:AddMoney(allmoney)
end
end
end
for k, v in pairs( ents.FindByClass( "bw_base_moneyprinter" ) ) do
if v:CPPIGetOwner() == self:CPPIGetOwner() then
if self:GetMoney() < cap then
local allmoney = v:GetMoney()
v:TakeMoney(allmoney)
self:AddMoney(allmoney)
end
end
end
end
if CLIENT then
local Cp = self:GetCapacity()
local money = tonumber(self:GetMoney()) or 0
local moneyPercentage = math.Round( money / cap * 100 ,1)
draw.DrawText( moneyPercentage .."%" , fontName .. ".Huge", w - 4-430, 71+610, self.FontColor, TEXT_ALIGN_CENTER)
local currentMoney = BaseWars.LANG.CURRENCY .. BaseWars.NumberFormat(money)
local maxMoney = BaseWars.LANG.CURRENCY .. BaseWars.NumberFormat(cap)
local font = fontName .. ".Big"
if #currentMoney > 16 then
font = fontName .. ".MedBig"
end
if #currentMoney > 20 then
font = fontName .. ".Med"
end
local fh = draw.GetFontHeight(font)
local StrW,StrH = surface.GetTextSize("")
local moneyW,moneyH = surface.GetTextSize(currentMoney)
draw.DrawText(currentMoney.. " / " .. BaseWars.LANG.CURRENCY .. BaseWars.NumberFormat(cap) , font,
w/2 - StrW/2-225 , (font == fontName .. ".Big" and 106 or 105 + fh / 4)+675, self.FontColor, TEXT_ALIGN_CENTER)
end
So now if you i look into the game it looks like this:
and now i have the problem that when it collects money it does it up to 2 billion and if i try to change
local money = tonumber(self:GetMoney()) or 0
to
local money =self:GetMoney()
i get straight -2.1 Bil
or i tried to change the min max values:
function ENT:GSAT(slot, name, min, max)
self:NetworkVar("Int", slot, name)
local getVar = function(minMax)
if self[minMax] and isfunction(self[minMax]) then return self[minMax](self) end
if self[minMax] and isnumber(self[minMax]) then return self[minMax] end
return minMax or 0
end
self["Add" .. name] = function(_, var)
local Val = self["Get" .. name](self) + var
if min and max then
Val = Clamp(tonumber(Val) or 0, getVar(min), 10^15)
end
self["Set" .. name](self, Val)
end
self["Take" .. name] = function(_, var)
local Val = self["Get" .. name](self) - var
if min and max then
Val = Clamp(tonumber(Val) or 0, getVar(min), 10^15)
end
self["Set" .. name](self, Val)
end
end
Basicly these lines:
Val = Clamp(tonumber(Val) or 0, getVar(min), 10^15)
if i run the game the Bank will just collect the money up to 2 Bill and then it goes to -2.1Bil. And at that point i dont know why and when a value does go buggy like it self sets to -2.1 Bil. does math.Round create this bug or do funktion return only return full integer ?
thx for any help
Because the network variable that the bank uses is an integer, it will only be able to store $(2^31-1) through $-(2^31-1). Try changing it to a networked float:
-- ...
function ENT:GSAT(slot, name, min, max)
-- self:NetworkVar("Int", slot, name), changes into:
self:NetworkVar("Float", slot, name)
-- ...
But, beware as the more money players accumulate the less precision will be put into those numbers.
How can I match "words" mixed parenthesis delimited strings, on the basis that they are separated by whitespaces. EG:
split_words_and_parenthesis("1791 (AR6K Async) S 2 ") --> {"1791","AR6K Async","S","2"}
Here's my attempt:
str = "1791 (AR6K Async) S 2 "
for val in str:gmatch("%S+") do
if str:gmatch("(" )then
str:gsub("%b()" , function(s) val=s end)
print(val)
else
print(val)
end
end
output:
(AR6K Async)
(AR6K Async)
(AR6K Async)
(AR6K Async)
(AR6K Async)
Can be solved using string.match if you know the format:
str = "1791 (AR6K Async) S 2 "
s1 = str:match("(%d%d%d%d)%s%(.*%)%s.+%s.+")
s2 = str:match("%d%d%d%d%s(%(.*%))%s.+%s.+")
s3 = str:match("%d%d%d%d%s%(.*%)%s(.+)%s.+")
s4 = str:match("%d%d%d%d%s%(.*%)%s.+%s(.+)")
print(s1)
print(s2)
print(s3)
print(s4)
Another solution that is generic and allows for variable number of entries (try it: simply past in a lua interpreter):
function get_separate_words(str)
local i = 1
local words = {}
function get_parentheses_content(str,is_recursively_called)
local i = 1
local function split(s, sep)
local fields = {}
local sep = sep or ":"
local pattern = string.format("([^%s]+)", sep)
string.gsub(s, pattern, function(c) fields[#fields + 1] = c end)
return fields
end
for j = 1,#str do
local c = string.sub(str,j,j)
local d = string.sub(str,j+1,j+1)
if j <= i then
elseif c == "(" then
i = j + #get_parentheses_content(string.sub(str,j+1,#str),true) + 2
elseif c == ")" and (is_recursively_called or (d == " ") or (not d)) then
print('c')
local parentheses_content = string.sub(str,1,j-1)
return {parentheses_content}
end
end
local parentheses_content = string.match(str,"^(.*)%)%s+[^)]*$")
if parentheses_content then print('a') end
parentheses_content = parentheses_content or string.match(str,"^(.*)%)$")
if parentheses_content then
print("A")
return {parentheses_content}
else
print("B")
return split("("..str," ")
end
end
local function merge(table_a, table_b)
table_a = table_a or {}
table_b = table_b or {}
for k_b, v_b in pairs(table_b) do
if type(v_b) == "table" and type(table_a[k_b] or false) == "table" then
merge(table_a[k_b], table_b[k_b])
else
table_a[k_b] = v_b
end
end
return table_a
end
for j = 1,#str do
local c = string.sub(str,j,j)
if j < i then
elseif c == " " or j == #str then
local word = string.gsub(string.sub(str,i,j)," ","")
if #word > 0 then
table.insert(words, word)
print(word)
end
i = j+1
elseif c == "(" then
local all_characters_after_opening_parentheses = string.sub(str,j+1,#str)
local parentheses_content = get_parentheses_content(all_characters_after_opening_parentheses)[1]
table.insert(words, parentheses_content)
j= j+#parentheses_content+2
i = j
end
end
return words
end
separate_words = get_separate_words("1791 (AR6(K As)ync) S 2 )")
for k,v in ipairs(separate_words) do print(k,v) end
Trying to allow a concatenated table to be referenced as such:
local group = table.concat(arguments, ",", 1)
where arguments = {"1,1,1"}
Currently, doing group[2] gives me the comma. How do I avoid that while still allowing for two-digit numbers?
(snippet of what I'm trying to use it for)
for i = 1, #group do
target:SetGroup(i, tonumber(group[i]))
end
Maybe you want something like
local i = 1
for v in string.gmatch(s, "(%w+),*") do
group[i] = v
i = i + 1
end
Revised version in response to comment, avoiding the table altogether:
local i = 1
for v in string.gmatch(s, "(%w+),*") do
target:SetGroup(i, tonumber(v))
i = i + 1
end
split function (you have to add it to code)
split = function(str, delim)
if not delim then
delim = " "
end
-- Eliminate bad cases...
if string.find(str, delim) == nil then
return { str }
end
local result = {}
local pat = "(.-)" .. delim .. "()"
local nb = 0
local lastPos
for part, pos in string.gfind(str, pat) do
nb = nb + 1
result[nb] = part
lastPos = pos
end
-- Handle the last field
result[nb + 1] = string.sub(str, lastPos)
return result
end
so
local arguments = {"1,1,1"};
local group = split(arguments[1], ",");
for i = 1, #group do
target:SetGroup(i, tonumber(group[i]))
end
also note that
local arguments = {"1,1,1"};
local group = split(arguments[1], ",");
local group_count = #group;
for i = 1, group_count do
target:SetGroup(i, tonumber(group[i]))
end
is faster code ;)
I'm trying to think of an easy way to make all elements in a table shift up one. It is for a game I am playing, attempting to switch between all targets in a table!
For example, let's say I'm surrounded by three mooks who want to kill me, so I target all of them and they're added into an array like so:
{
"mook1",
"mook2",
"mook3",
}
What I want the function to do is change all indexes to go up one (or the amount I specify), and the last to go to the beginning, so the end result would be:
{
"mook3",
"mook1",
"mook2",
}
I attempted it on my own with a simple function like this:
local function nextIndex(tbl, amount)
local t = {}
for k,v in ipairs(tbl) do
if k < #tbl then
t[k+amount] = v
else
t[1] = v
end
end
return t
end
It works as long as the amount is set to 1. I'm sure there is a much smarter and more efficient way of doing this. Could anyone take a whack at it please?!
You can use a function like this:
function wrap( t, l )
for i = 1, l do
table.insert( t, 1, table.remove( t, #t ) )
end
end
You can see a test run on codepad. or, if you're uncomfortable with nesting of function calls;
function wrap( t, l )
for i = 1, l do
table.insert( t, 1, t[#t] )
table.remove( t, #t )
end
end
would work the same way.
I worked a bit more and figured out how to do it. This is the code:
local function nextIndex(tbl, amount)
local t = {}
local i
for k,v in ipairs(tbl) do
i = k + amount
if i <= #tbl then
t[i] = v
else
t[i-#tbl] = v
end
end
return t
end
Is there an easier way to do it though?
So, the task is to rotate the last rot items to the front.
I added parameter n to allow overriding of the sequence end as determined by #t.
-- uses t[#t+1]...t[#t+rot%#t] as scratch space
local function rotate_mod(t, rot, n)
n = n or #t
rot = rot % n
if rot == 0 then return t end
for i = n, 1, -1 do
t[i + rot] = t[i]
end
for i = 1, rot do
t[i], t[i + n] = t[i + n]
end
return t
end
Or if you want a new array (just ignore parameter r):
local function rotate_new(t, rot, n, r)
n, r = n or #t, {}
rot = rot % n
for i = 1, rot do
r[i] = t[n - rot + i]
end
for i = rot + 1, n do
r[i] = t[i - rot]
end
return r
end
Here's a true "in-place" version. It does not need to temporarily enlarge the table:
local function reverse(t, i, j)
while i < j do
t[i], t[j] = t[j], t[i]
i, j = i+1, j-1
end
end
local function rotate_inplace(t, d, n)
n = n or #t
d = (d or 1) % n
reverse(t, 1, n)
reverse(t, 1, d)
reverse(t, d+1, n)
end
Is there a way to use the arg 2 value of table.concat to represent the current table index?
eg:
t = {}
t[1] = "a"
t[2] = "b"
t[3] = "c"
X = table.concat(t,"\n")
desired output of table concat (X):
"1 a\n2 b\n3 c\n"
Simple answer : no.
table.concat is something really basic, and really fast.
So you should do it in a loop anyhow.
If you want to avoid excessive string concatenation you can do:
function concatIndexed(tab,template)
template = template or '%d %s\n'
local tt = {}
for k,v in ipairs(tab) do
tt[#tt+1]=template:format(k,v)
end
return table.concat(tt)
end
X = concatIndexed(t) -- and optionally specify a certain per item format
Y = concatIndexed(t,'custom format %3d %s\n')
I don't think so: how would you tell it that the separator between keys and values is supposed to be a space, for example?
You can write a general mapping function to do what you'd like:
function map2(t, func)
local out = {}
for k, v in pairs(t) do
out[k] = func(k, v)
end
return out
end
function joinbyspace(k, v)
return k .. ' ' .. v
end
X = table.concat(map2(t, joinbyspace), "\n")
No. But there is a work around:
local n = 0
local function next_line_no()
n = n + 1
return n..' '
end
X = table.concat(t,'\0'):gsub('%f[%Z]',next_line_no):gsub('%z','\n')
function Util_Concat(tab, seperator)
if seperator == nil then return table.concat(tab) end
local buffer = {}
for i, v in ipairs(tab) do
buffer[#buffer + 1] = v
if i < #tab then
buffer[#buffer + 1] = seperator
end
end
return table.concat(buffer)
end
usage tab is where the table input is and seperator be both nil or string (if it nil it act like ordinary table.concat)
print(Util_Concat({"Hello", "World"}, "_"))
--Prints
--Hello_world