-- функция украдена со СтекОверфлова с небольшими изменениями
-- htt ps://stackoverflow.com/questions/16026055/lua-printing-specific-key-value-pairs-in-table
-- нужна для отладки, в финальном коде не нужна
function DeepPrint (e, indent)
-- if e is a table, we should iterate over its elements
if type(e) == "table" then
for k,v in pairs(e) do -- for every element in the table
print(indent or "", '[' .. k .. ']', v)
if type(v) == "table" then
DeepPrint(v, (indent or "") .. "\t") -- recursively repeat the same procedure
end
end
else -- if not, we can just print it
print(indent or "", e)
end
end
--[[
diceProb содержит функции для подсчёта вероятности бросков, которые описываются в лиспоподобном формате.
Входные данные ожидаются в таком формате:
{">=", {"+", "d10", "1"}, "7"})
У значений есть только два типа: список вариантов или константа
Константа может выражать вероятность (от 0 до 1) или просто произвольное число
Список вариантов — это бросок с описанием возможных исходов и их количества:
{ {value=2, repeats=1}, {value=3, repeats=2}, ...}
Функции типа >=, >, <, <= и т.п. берут список вариантов и возврщают вероятность.
Функции типа +, - работаают и с тем, и с тем: для констант они просто складывают константы,
а для списков вероятности они добваляют к каждому значению что-то. Операции над константами
чуть дешевле.
Пример использования:
diceProb.evaluate({">", {"+", "2d6", "+3"}, "10"})
-- => 0.41666666666667 (вероятность, что 2d6+3 > 10)
Можно передать список значений переменных как второй аргумент:
diceProb.evaluate({">", {"+", "2d6", "Прибавка"}, "10"}, {["Прибавка"]="+3"})
-- => 0.41666666666667 (вероятность, что 2d6+Прибавка > 10, где Прибавка=+3)
]]
local diceProb = { fn={} }
function diceProb.fn.sum(args)
local probabilities = {}
local constant = 0
for k, v in ipairs(args) do
if type(v) == 'table' then
table.insert(probabilities, v)
else
constant = constant + v
end
end
if #probabilities > 2 then
local summed_probabilities = probabilities[1]
for i=2,#probabilities do
summed_probabilities = diceProb.sum_of_rolls(summed_probabilities, probabilities[i])
end
probabilities = summed_probabilities
end
if #probabilities > 0 then
local summed = {}
for k, v in ipairs(probabilities[1]) do
table.insert(summed, {repeats=v.repeats, value=v.value + constant})
end
return summed
else
return constant
end
end
function diceProb.negate(operand)
if type(operand) == "table" then
local negated = {}
for k,v in ipairs(operand) do
table.insert(negated, {value=-v.value, repeats=v.repeats})
end
return negated
else
return -operand
end
end
function diceProb.fn.difference(args)
if #args == 1 then return diceProb.negate(args[0]) end
local sumArgs = {}
for k,v in ipairs(args) do
if k == 1 then
table.insert(sumArgs, v)
else
table.insert(sumArgs, diceProb.negate(v))
end
end
return diceProb.fn.sum(sumArgs)
end
function diceProb.fn.product(args)
local probabilities = {}
local constant = 1
for k, v in ipairs(args) do
if type(v) == 'table' then
table.insert(probabilities, v)
else
constant = constant * v
end
end
if #probabilities > 2 then
error('multiplication of dice is not supported (yet?)')
end
if #probabilities > 0 then
local multiplied = {}
for k, v in ipairs(probabilities[1]) do
table.insert(multiplied, {repeats=v.repeats, value=v.value * constant})
end
return multiplied
else
return constant
end
end
function diceProb.compare_less_than(lower_range, variants, upper_range, include_equal)
local total_repeats = 0
local correct_repeats = 0
for k, v in ipairs(variants) do
local correct = true
if lower_range ~= nil then
if v.value < lower_range then correct = false end
if v.value == lower_range and not include_equal then correct = false end
end
if upper_range ~= nil then
if v.value > upper_range then correct = false end
if v.value == upper_range and not include_equal then correct = false end
end
total_repeats = total_repeats + v.repeats
if correct then correct_repeats = correct_repeats + v.repeats end
end
return correct_repeats / total_repeats
end
function diceProb.check_equality(variants, const)
local total_repeats = 0
local correct_repeats = 0
for k, v in ipairs(variants) do
total_repeats = total_repeats + v.repeats
if v.value == const then correct_repeats = correct_repeats + v.repeats end
end
return correct_repeats / total_repeats
end
function diceProb.fn.more_or_equals(args)
local left = args[1]
local right = args[2]
if #args ~= 2 then error('≥ operation must have 2 arguments') end
if (type(left) == "table") and (type(right) == "table") then -- tbl ≥ tbl
error("only one ≥ argument should be a table")
elseif type(left) == "table" then -- tbl ≥ const
return diceProb.compare_less_than(right, left, nil, true)
elseif type(right) == "table" then --const ≥ tbl
return diceProb.compare_less_than(nil, right, left, true)
else -- const ≥ const
if left >= right then return 1 else return 0 end
end
end
function diceProb.fn.more(args)
local left = args[1]
local right = args[2]
if #args ~= 2 then error('> operation must have 2 arguments') end
if (type(left) == "table") and (type(right) == "table") then -- tbl ≥ tbl
error("only one ≥ argument should be a table")
elseif type(left) == "table" then -- tbl ≥ const
return diceProb.compare_less_than(right, left, nil, false)
elseif type(right) == "table" then --const ≥ tbl
return diceProb.compare_less_than(nil, right, left, false)
else -- const ≥ const
if left > right then return 1 else return 0 end
end
end
function diceProb.fn.less_or_equal(args)
local left = args[1]
local right = args[2]
if #args ~= 2 then error('≤ operation must have 2 arguments') end
if (type(left) == "table") and (type(right) == "table") then
error("only one ≥ argument should be a table")
elseif type(left) == "table" then
return diceProb.compare_less_than(nil, left, right, true)
elseif type(right) == "table" then
return diceProb.compare_less_than(left, right, nil, true)
else
if left <= right then return 1 else return 0 end
end
end
function diceProb.fn.less(args)
local left = args[1]
local right = args[2]
if #args ~= 2 then error('< operation must have 2 arguments') end
if (type(left) == "table") and (type(right) == "table") then
error("only one ≥ argument should be a table")
elseif type(left) == "table" then
return diceProb.compare_less_than(nil, left, right, false)
elseif type(right) == "table" then
return diceProb.compare_less_than(left, right, false)
else
if left < right then return 1 else return 0 end
end
end
function diceProb.fn.equal(args)
local left = args[1]
local right = args[2]
if #args ~= 2 then error('= operation must have 2 arguments') end
if (type(left) == "table") and (type(right) == "table") then
error("only one = argument should be a table")
elseif type(left) == "table" then
return diceProb.check_equality(left, right)
elseif type(right) == "table" then
return diceProb.check_equality(right, left)
else
if left == right then return 1 else return 0 end
end
end
diceProb.functionHandlers = {
['+']=diceProb.fn.sum,
['-']=diceProb.fn.difference,
['*']=diceProb.fn.product,
["≥"]=diceProb.fn.more_or_equals,
[">="]=diceProb.fn.more_or_equals,
['>']=diceProb.fn.more,
['<=']=diceProb.fn.less_or_equal,
['≤']=diceProb.fn.less_or_equal,
['<']=diceProb.fn.less,
['=']=diceProb.fn.equal,
}
diceProb.die_roll_cache = {}
-- Список вариантов — это таблица типа
-- { {value=2, repeats=1}, {value=3, repeats=2}, ...}
-- Где value — значение, repeats — число повторений значения
-- Возвращает список вариантов
function diceProb.single_die_roll(num_sides)
probabilities = {}
for i=1, num_sides do
table.insert(probabilities, {value=i, repeats=1})
end
return probabilities
end
-- Возвращет список вараиантов суммы двух списков вариантов
function diceProb.sum_of_rolls(first_probs, second_probs)
local repeats_by_val = {}
for _, first in ipairs(first_probs) do
for _, second in ipairs(second_probs) do
local val = first.value + second.value
local repeats = first.repeats * second.repeats
repeats_by_val[val] = (repeats_by_val[val] or 0) + repeats
end
end
local values={}
for k, v in pairs(repeats_by_val) do
table.insert(values, {value=k, repeats=v})
end
return values
end
-- Возвращает список варинатов для XdY
function diceProb.complex_die_roll(repeats, num_sides)
local basic_roll = diceProb.single_die_roll(num_sides)
local result = basic_roll
for i = 2,repeats do
result = diceProb.sum_of_rolls(result, basic_roll)
end
return result
end
-- Читет строку и возвращает либо список вариаантов, либо nil (если это не нотация броска)
function diceProb.evaluate_dice_expression(s)
if not s:match'%d*[dD]%d' then return nil end
local from, to = s:find('[dD]')
local repeats, dice = 1, tonumber(s:sub(to+1))
if from > 1 then repeats = tonumber(s:sub(1, from-1) or "1") end
local cache_id = repeats .. 'd' .. dice
if diceProb.die_roll_cache[cache_id] == nil then
diceProb.die_roll_cache[cache_id] = diceProb.complex_die_roll(repeats, dice)
end
return diceProb.die_roll_cache[cache_id]
end
function diceProb.evaluate_constant(s)
return tonumber(s)
end
function diceProb.evaluate_form(form, environment)
local fn_name = form[1]
local computed_args = {}
for k, v in ipairs(form) do
if k > 1 then
table.insert(computed_args, diceProb.evaluate(v, environment))
end
end
local fn = diceProb.functionHandlers[fn_name]
if fn == nil then error('Unknown function: ' .. fn_name) end
return fn(computed_args)
end
function diceProb.evaluate(datum, environment)
local t = type(datum)
if t == "table" then
return diceProb.evaluate_form(datum, environment)
elseif t == "string" then
local value = (environment and environment[datum]) or
diceProb.evaluate_dice_expression(datum) or
diceProb.evaluate_constant(datum)
if value == nil then
error('cannot evaluate datum: ' .. datum .. '[' .. t .. ']')
end
return value
else
error('cannot evaluate datum: ' .. datum .. '[' .. t .. ']')
end
end
--[[
diceTable содержит рисование таблиц, оформленных в лиспоподобном формате:
{
{"столбец", "ID", "тип", "данные1", "данные2", ... },
...
}
В качестве типа принимаются значения "список" и "вероятность".
Если тип — "список", то дальше идут элементы списка.
Если тип — "вероятность", то дальше идёт ровно одна форма, формула расчёта
вероятности. Эта формула такая же, как ввод для diceProb.evaluate, причём
ID столбцов будут заменяться на элемент списка.
Для ID и элемента списка можно задать альтернативное отображение:
{
{"столбец", {"КостьУмение", "Кость умения"}, "список", {"0", "Нет"}, "d4", "d6", ... },
...
}
]]
local diceTable = {}
function diceTable.id_or_title(val_with_text, num)
if type(val_with_text) == "table" then
return val_with_text[num]
else
return val_with_text
end
end
function diceTable.id(val_with_text)
return diceTable.id_or_title(val_with_text, 1)
end
function diceTable.title(val_with_text)
return diceTable.id_or_title(val_with_text, 2)
end
function diceTable.get_lists(columns)
local lists = {}
for _, column in ipairs(columns) do
if column[3] == "список" then
local items = {}
for i = 4, #column do
table.insert(items, column[i])
end
local id = diceTable.id(column[2])
lists[id] = items
end
end
return lists
end
function diceTable.make_table_rows(columns, a_column_ids, a_current_idx, a_rows)
local lists = diceTable.get_lists(columns)
local column_ids = a_column_ids
if column_ids == nil then
column_ids = {}
for _, column in ipairs(columns) do
if column[3] == "список" then
table.insert(column_ids, diceTable.id(column[2]))
end
end
end
local current_idx = a_current_idx or 1
if current_idx > #column_ids then
return a_rows or {}
end
local current_id = column_ids[current_idx]
local prev_rows = a_rows or {{}}
local new_rows = {}
local items = lists[current_id]
for _,prev_row in ipairs(prev_rows) do
for _,item in ipairs(items) do
-- TODO rowspan'ы
local new_row = {}
new_row[current_id] = item
for k,v in pairs(prev_row) do
new_row[k] = v
end
table.insert(new_rows, new_row)
end
end
return diceTable.make_table_rows(columns, column_ids, current_idx+1, new_rows)
end
function diceTable.formatPercent(x)
local num = math.floor(x * 10000 + 0.5) / 100
return num .. '%'
end
function diceTable.get_cell_contents(columns)
local rows = diceTable.make_table_rows(columns)
local result = {}
for _, row_data in pairs(rows) do
local cells = {}
for idx, column in pairs(columns) do
if column[1] ~= "столбец" then
error("unknown entity: " .. column[1])
end
if column[3] == "список" then
cells[idx] = diceTable.title(row_data[diceTable.id(column[2])])
elseif column[3] == "вероятность" then
environment = {}
for k,v in pairs(row_data) do
environment[k] = diceProb.evaluate(diceTable.id(v))
end
cells[idx] = diceTable.formatPercent(diceProb.evaluate(column[4], environment))
else
error("unknown column type: " .. column[3])
end
end
table.insert(result, cells)
end
return result
end
function diceTable.make_wikitable_contents(columns)
local result = ''
local rows = diceTable.get_cell_contents(columns)
for row_idx, row in ipairs(rows) do
local cell_text = "\n|-\n| "
for cell_idx, cell in ipairs(row) do
cell_text = cell_text .. cell
if cell_idx < #row then
cell_text = cell_text .. " || "
end
end
result = result .. cell_text
end
return result
end
function diceTable.make_wikitable_header(columns)
-- TODO: colspan/rowspan
local result = '! '
for idx, column in ipairs(columns) do
result = result .. diceTable.title(column[2])
if idx < #columns then
result = result .. ' !! '
end
end
return result
end
function diceTable.make_wikitable(columns)
return '{| class="wikitable" style="margin:auto"\n' ..
diceTable.make_wikitable_header(columns) ..
diceTable.make_wikitable_contents(columns) ..
'\n|}'
end
--[[
Тут нужен парсер S-выражений, но я его пока что не сделал.
На входе я хочу что-то лиспообразное, типа такого:
(
(столбец Атрибут список d6 d8 d10 d12)
(столбец Умение список (0 —) d6 d8 d10 d12)
(столбец "Вероятность без переброски" (- 1 (* (< Атрибут 6) (< Умение 6))))
(столбец "Вероятность с переброской" (- 1 (* (< Атрибут 6) (< Умение 6) (< Атрибут 6) (< Умение 6))))
)
Пока что вместо S-выражений просто массивы Lua.
]]
print(diceTable.make_wikitable({
{"столбец", "Атрибут", "список", "d6", "d8", "d10", "d12"},
{"столбец", "Умение", "список", {"0", "—"}, "d6", "d8", "d10", "d12"},
{"столбец", "Вероятность без переброски", "вероятность", {"-", "1", {"*", {"<", "Атрибут", "6"}, {"<", "Умение", "6"}}}},
{"столбец", "Вероятность с переброской", "вероятность", {"-", "1", {"*", {"<", "Атрибут", "6"}, {"<", "Умение", "6"}, {"<", "Атрибут", "6"}, {"<", "Умение", "6"}}}}
}))
Write, Run & Share Lua code online using OneCompiler's Lua online compiler for free. It's one of the robust, feature-rich online compilers for Lua language, running the latest Lua version 5.4. Getting started with the OneCompiler's Lua editor is easy and fast. The editor shows sample boilerplate code when you choose language as Lua and start coding.
OneCompiler's Lua online editor supports stdin and users can give inputs to programs using the STDIN textbox under the I/O tab. Following is a sample Lua program which takes name as input and prints hello message with your name.
name = io.read("*a")
print ("Hello ", name)
Lua is a light weight embeddable scripting language which is built on top of C. It is used in almost all kind of applications like games, web applications, mobile applications, image processing etc. It's a very powerful, fast, easy to learn, open-source scripting language.
-- global variables
a = 10
-- local variables
local x = 30
| Value Type | Description |
|---|---|
| number | Represents numbers |
| string | Represents text |
| nil | Differentiates values whether it has data or not |
| boolean | Value can be either true or false |
| function | Represents a sub-routine |
| userdata | Represents arbitary C data |
| thread | Represents independent threads of execution. |
| table | Can hold any value except nil |
While is also used to iterate a set of statements based on a condition. Usually while is preferred when number of iterations are not known in advance.
while(condition)
do
--code
end
Repeat-Until is also used to iterate a set of statements based on a condition. It is very similar to Do-While, it is mostly used when you need to execute the statements atleast once.
repeat
--code
until( condition )
For loop is used to iterate a set of statements based on a condition.
for init,max/min value, increment
do
--code
end
Function is a sub-routine which contains set of statements. Usually functions are written when multiple calls are required to same set of statements which increase re-usuability and modularity.
optional_function_scope function function_name( argument1, argument2, argument3........, argumentn)
--code
return params with comma seperated
end