Bracket finding algorithm lua? - algorithm

I'm making a JSON parser and I am looking for an algorithm that can find all of the matching brackets ([]) and braces ({}) and put them into a table with the positions of the pair.
Examples of returned values:
table[x][firstPos][secondPos] = type
table[x] = {firstPos, secondPos, bracketType}
EDIT: Let parse() be the function that returns the bracket pairs. Let table be the value returned by the parse() function. Let codeString be the string containing the brackets that I want to detect. Let firstPos be the position of the first bracket in the Nth pair of brackets. Let secondPos be the position of the second bracket in the Nth pair of brackets. Let bracketType be the type of the bracket pair ("bracket" or "brace").
Example:
If you called:
table = parse(codeString)
table[N][firstPos][secondPos] would be equal to type.

Well, In plain Lua, you could do something like this, also taking into account nested brackets:
function bm(s)
local res ={}
if not s:match('%[') then
return s
end
for k in s:gmatch('%b[]') do
res[#res+1] = bm(k:sub(2,-2))
end
return res
end
Of course you can generalize this easy enough to braces, parentheses, whatever (do keep in mind the necessary escaping of [] in patterns , except behind the %b pattern).
If you're not restricted to plain Lua, you could use LPeg for more flexibility
If you are not looking for the contents of the brackets, but the locations, the recursive approach is harder to implement, since you should keep track of where you are. Easier is just walking through the string and match them while going:
function bm(s,i)
local res={}
res.par=res -- Root
local lev = 0
for loc=1,#s do
if s:sub(loc,loc) == '[' then
lev = lev+1
local t={par=res,start=loc,lev=lev} -- keep track of the parent
res[#res+1] = t -- Add to the parent
res = t -- make this the current working table
print('[',lev,loc)
elseif s:sub(loc,loc) == ']' then
lev = lev-1
if lev<0 then error('too many ]') end -- more closing than opening.
print(']',lev,loc)
res.stop=loc -- save bracket closing position
res = res.par -- revert to the parent.
end
end
return res
end
Now that you have all matched brackets, you can loop through the table, extracting all locations.

I figured out my own algorithm.
function string:findAll(query)
local firstSub = 1
local lastSub = #query
local result = {}
while lastSub <= #self do
if self:sub(firstSub, lastSub) == query then
result[#result + 1] = firstSub
end
firstSub = firstSub + 1
lastSub = lastSub + 1
end
return result
end
function string:findPair(openPos, openChar, closeChar)
local counter = 1
local closePos = openPos
while closePos <= #self do
closePos = closePos + 1
if self:sub(closePos, closePos) == openChar then
counter = counter + 1
elseif self:sub(closePos, closePos) == closeChar then
counter = counter - 1
end
if counter == 0 then
return closePos
end
end
return -1
end
function string:findBrackets(bracketType)
local openBracket = ""
local closeBracket = ""
local openBrackets = {}
local result = {}
if bracketType == "[]" then
openBracket = "["
closeBracket = "]"
elseif bracketType == "{}" then
openBracket = "{"
closeBracket = "}"
elseif bracketType == "()" then
openBracket = "("
closeBracket = ")"
elseif bracketType == "<>" then
openBracket = "<"
closeBracket = ">"
else
error("IllegalArgumentException: Invalid or unrecognized bracket type "..bracketType.."\nFunction: findBrackets()")
end
local openBrackets = self:findAll(openBracket)
if not openBrackets[1] then
return {}
end
for i, j in pairs(openBrackets) do
result[#result + 1] = {j, self:findPair(j, openBracket, closeBracket)}
end
return result
end
Will output:
5 14
6 13
7 12
8 11
9 10

Related

Why Does A For Loop Return "attempt to get length of a Vector3 value"

I have no clue what is wrong with this code, it is literally saying that a for loop that is perfectly fine, is attempting to get a vector3 value.
local locations = {}
local Players = game:GetService("Players")
local function SplitString(String)
local CurrentString = ""
local x = 1
for i = 1, #String do
local FullString = string.sub(String,1,i)
local Character = string.sub(String,i,i)
local CurrentString = string.sub(String, x, i)
print(locations[1])
if Character == "," then
CurrentString = string.sub(String,x,i-1)
table.insert(locations,CurrentString)
x = i + 1
CurrentString = string.sub(String, x , i)
print(locations)
end
--Add the character to the string.
if Character == " " then
x = i + 1
CurrentString = string.sub(String, x, i + 1)
end
if #FullString + 1 == #String then
table.insert(locations, CurrentString)
end
end
end
Players.PlayerAdded:Connect(function(player)
player.CharacterAdded:Connect(function(char)
local humanoidrootpart = char:WaitForChild("HumanoidRootPart")
wait(5)
while humanoidrootpart do
local location = humanoidrootpart.position
print(location)
SplitString(location)
print(locations[1])
local RoundLocatX = math.floor(locations[1]*50)/50
local RoundLocatZ = math.floor(locations[3]*50)/50
local RoundLocatXLength = RoundLocatX.length
local RoundLocatZLength = RoundLocatZ.length
if RoundLocatX > 50 then
print('hi')
end
wait(1)
end
end)
end)
This is the error:
ServerScriptService.Terrain Generation:9: attempt to get length of a Vector3 value
I would post this on the roblox forum but they are stupid and require some dumb amount of reading time to be able to post on it.
Your error is pointing at this line :
for i = 1, #String do
and saying that you are trying to get the length of a Vector3 object. That means that #String is not working as expected. So looking at the object you passed into the SplitString function...
local location = humanoidrootpart.position
print(location)
SplitString(location)
Your location variable is a Vector3, not a string, and it doesn't support the length operator, #.
But you don't have to parse it as a string to get the values out of it. Since it is a Vector3, you can simply index the different values in it.
local location = humanoidrootpart.Position
local RoundLocatX = math.floor(location.X * 50) / 50
local RoundLocatZ = math.floor(location.Z * 50) / 50

VBScript division decimal by integer returns extra remainder

I'm trying to validate my VBScript function that divides an input value by 1440. When I pass 43.56 as input parameter, it returns me the following result:
0.030250000000000002
while the correct answer is
0.03025
What should I do to make it work correctly both with evenly divided integers, like 1440 / 1440 = 1, and when one value is a decimal.
Currently my function looks like so:
Function convertMinutesToDays(value)
If IsNumeric(value) And TypeName(value) <> "Boolean" Then
convertMinutesToDays = value / 1440
Else
convertMinutesToDays = 0
End If
End Function
Actually, if you simply put Response.Write convertMinutesToDays(43.56), it will show 0.03025, but we are using it within an assert javascript method, like so:
Call AssertAreEqual(convertMinutesToDays(43.56), 0.03025, "convertMinutesToDays: pass 43.56 should return 0.03025")
The javascript code:
<script language="JavaScript" runat="server">
function AssertAreEqual(val1, val2, message)
{
var retVal = false;
var divAttributes = "";
var equality = "";
if (val1 === val2)
{
divAttributes = "class=\"unittest assertareequal pass\"";
equality = "=";
retVal = true;
}
else
{
divAttributes = "class=\"unittest assertareequal fail\"";
equality = "!=";
retVal = false;
}
Response.Write("<div " + divAttributes + ">Actual:" + val1 + " " + equality + " " + val2 + ":Expected | " + message + "</div>");
return retVal;
}
</script>
The output:
Actual:0.030250000000000002 != 0.03025:Expected | convertMinutesToDays: pass 43.56 should return 0.03025
Any ideas?
This is due to the way floating point math works in VBScript(well most any language really). For your code, the Round function should suffice because you are always dividing by a single number so rounding errors won't add up on you the way they can in other types of functions. I chose 5 decimal places because it fixes your example. If you find others that still are off then you may need to up that but for representing partial days 5 is probably enough.
Function convertMinutesToDays(value)
If IsNumeric(value) And TypeName(value) <> "Boolean" Then
convertMinutesToDays = Round(value / 1440, 5)
Else
convertMinutesToDays = 0
End If
End Function

Redis interval query

I have data in the form of a tuple (S, T), where S is string and T is integer. Neither S nor T is unique, while their combination is unique. I need to get all tuples where S1 == S2 and |T1 - T2| <= C. Is is possible to do efficiently with Redis?
One way would be to store the data in a list and do the retrieval with a Lua script. First, for tuples of the form (Sn,Tn), insert it like this:
LPUSH myKey S1:T1
LPUSH myKey S2:T2
... and so on
Then, use the Lua script below:
local function split(div,str)
if (div=='') then return false end
local pos,arr = 0,{}
for st,sp in function() return string.find(str,div,pos,true) end do
table.insert(arr,string.sub(str,pos,st-1))
pos = sp + 1
end
table.insert(arr,string.sub(str,pos))
return arr
end
local key = KEYS[1]
local sVal = ARGV[1]
local tVal = tonumber(ARGV[2])
local cVal = tonumber(ARGV[3])
local length = redis.call("LLEN", key)
if (tonumber(length) == 0) then
return nil
end
local data = redis.call("LRANGE", key, 0, tonumber(length))
local retval = {}
for index,val in pairs(data) do
local parts = split(":", val)
if (parts[1] == sVal and math.abs(tonumber(parts[2]) - tVal) <= cVal) then
table.insert(retval, val)
end
end
return retval
Save it as script.lua and execute it with:
$ redis-cli "$(cat script.lua)" 1 myKey sValue tValue cValue
This will return all tuples (in Sn:Tn form) that matches S1 == S2 and |T1 - T2| <= C.

parallel iteration in lua

I would like to for-loop through multiple tables in parallel in Lua. I could just do:
for i in range(#table1)
pprint(table1[i])
pprint(table2[i])
end
But I'd rather something like python's zip:
for elem1, elem2 in zip(table1, table2):
pprint(elem1)
pprint(elem2)
end
Is there such a thing in standard Lua (or at least in whatever comes packaged with torch?).
If you want something in Lua that's similar to some Python function, you should look at Penlight first. For this specific case there is the seq.zip function. It seems that Penlight is installed together with Torch, but you can also get it via LuaRocks (which again is bundled with at least one Torch distribution).
Anyway, the seq.zip function in Penlight only supports zipping two sequences. Here is a version that should behave more like Python's zip, i.e. allowing more (or less) than two sequences:
local zip
do
local unpack = table.unpack or unpack
local function zip_select( i, var1, ... )
if var1 then
return var1, select( i, var1, ... )
end
end
function zip( ... )
local iterators = { n=select( '#', ... ), ... }
for i = 1, iterators.n do
assert( type( iterators[i] ) == "table",
"you have to wrap the iterators in a table" )
if type( iterators[i][1] ) ~= "number" then
table.insert( iterators[i], 1, -1 )
end
end
return function()
local results = {}
for i = 1, iterators.n do
local it = iterators[i]
it[4], results[i] = zip_select( it[1], it[2]( it[3], it[4] ) )
if it[4] == nil then return nil end
end
return unpack( results, 1, iterators.n )
end
end
end
-- example code (assumes that this file is called "zip.lua"):
local t1 = { 2, 4, 6, 8, 10, 12, 14 }
local t2 = { "a", "b", "c", "d", "e", "f" }
for a, b, c in zip( {ipairs( t1 )}, {ipairs( t2 )}, {io.lines"zip.lua"} ) do
print( a, b, c )
end
--------------------------------------------------------------------------------
-- Python-like zip() iterator
--------------------------------------------------------------------------------
function zip(...)
local arrays, ans = {...}, {}
local index = 0
return
function()
index = index + 1
for i,t in ipairs(arrays) do
if type(t) == 'function' then ans[i] = t() else ans[i] = t[index] end
if ans[i] == nil then return end
end
return unpack(ans)
end
end
--------------------------------------------------------------------------------
-- Example use:
--------------------------------------------------------------------------------
a = {'a','b','c','d'}
b = {3,2,1}
c = {7,8,9,10,11}
for a,b,c,line in zip(a,b,c,io.lines(arg[0])) do
print(a,b,c,line)
end
print '\n--- Done! ---'
Made a version with variable number of lists based on the other responses and added coroutines, it's not the fastest but I think it's very readable. I'm gonna use it but maybe someone else also finds it usefull.
Also this was made for use with neovim's LuaJit.
local zipgen = function(args)
-- find smallest iterator's size
local min = #args[1]
for i = 2, #args, 1 do
min = #args[i] < min and #args[i] or min
end
-- create list with 'i'th element from all iterators
for i=1, min do
local ans = {}
for j=1, #args do
-- get 'j'th iterator's 'i'th element
ans[j] = args[j][i]
end
-- return list of 'i'th elements
coroutine.yield(unpack(ans))
end
end
-- python like zip iterator
zip = function(...)
local args = {...}
-- return a function that resumes the coroutine when called
return coroutine.wrap(function() zipgen(args) end)
end

generating TTT game tree in lua

I am attempting to write a tic-tac-toe game in lua, and plan on using the minimax algorithm to decide non-human moves. The first step in this involves generating a tree of all possible board states from a single input state. I am trying to recursively do this, but cannot seem to figure out how. (I think) I understand conceptually how this should be done, but am having trouble implementing it in lua.
I am trying to structure my tree in the following manner. Each node is a list with two fields.
{ config = {}, children = {} }
Config is a list of integers (0,1,2) that represent empty, X, and O and defines a TTT board state. Children is a list nodes which are all possible board states one move away from the current node.
Here is my function that I currently have to build the game tree
function tree_builder(board, player)
supertemp = {}
for i in ipairs(board.config) do
--iterate through the current board state.
--for each empty location create a new node
--representing a possible board state
if board.config[i] == 0 then
temp = {config = {}, children = {}}
for j in ipairs(board.config) do
temp.config[j] = board.config[j]
end
temp.config[i] = player
temp.children = tree_builder(temp, opposite(player))
supertemp[i] = temp
end
end
return supertemp
end
The function is called in the following manner:
start_board = {config = {1,0,0,0}, children = {} }
start_board.children = tree_builder(start_board, 1)
When I comment out the recursive element of the function (the line "temp.children = builder(temp, opposite(player))") and only generate the first level of children. the output is correct. When called via code that is conceptually identical to (I am using love2D so formatting is different):
for i in pairs(start_board.children) do
print (start_board.children[i].config)
The three children are:
1,1,0,0
1,0,1,0
1,0,0,1
However, once I add the recursive element, the following is output for the same three children
1,1,2,1
1,1,2,1
1,1,2,1
I have been searching online for help and most of what I have found is conceptual in nature or involves implementation in different languages. I believe I have implemented the recursive element wrongly, but cannot wrap my head around the reasons why.
Don't understand what opposite(player) means in temp.children = tree_builder(temp, opposite(player)).
Notice that a recursion need an end condition.
This is my solution under your structure:
local COL = 3
local ROW = 3
local function printBoard( b )
local output = ""
local i = 1
for _,v in ipairs(b.config) do
output = output .. v .. ( (i % COL == 0) and '\n' or ',' )
i = i + 1
end
print( output )
end
local function shallowCopy( t )
local t2 = {}
for k,v in pairs(t) do
t2[k] = v
end
return t2
end
local MAX_STEP = COL * ROW
local ING = 0
local P1 = 1
local P2 = 2
local TIE = 3
local STATUS = { [P1] = "P1 Win", [P2] = "P2 Win", [TIE] = "Tied" }
local start_board = { config = {P1,0,0,0,0,0,0,0,0}, children = {} }
local function checkIfOver( board, step )
local config = board.config
local over = false
--check rows
for i=0,ROW-1 do
over = true
for j=1,COL do
if 0 == config[i*COL+1] or config[i*COL+j] ~= config[i*COL+1] then
over = false
end
end
if over then
return config[i*COL+1]
end
end
--check cols
for i=1,COL do
over = true
for j=0,ROW-1 do
if 0 == config[i] or config[i] ~= config[i+COL*j] then
over = false
end
end
if over then
return config[i]
end
end
--check diagonals
if config[1] ~= 0 and config[1] == config[5] and config[5] == config[9] then
return config[1]
end
if config[3] ~=0 and config[3] == config[5] and config[5] == config[7] then
return config[3]
end
if step >= MAX_STEP then
return TIE
else
return ING
end
end
local function treeBuilder( board, step )
--check the game is over
local over = checkIfOver( board, step )
if over ~= ING then
printBoard( board )
print( STATUS[over], '\n---------\n' )
return
end
local child
local childCfg
for i,v in ipairs(board.config) do
if 0 == v then
child = { config = {}, children = {} }
childCfg = shallowCopy( board.config )
childCfg[i] = (step % 2 == 0) and P1 or P2
child.config = childCfg
table.insert( board.children, child )
treeBuilder( child, step + 1 )
end
end
end
treeBuilder( start_board, 1 )

Resources