Skip to content

Commit

Permalink
Start a Lua mathx module to hold round, shuffle, random, and a few ot…
Browse files Browse the repository at this point in the history
…her things
  • Loading branch information
CelticMinstrel committed Feb 15, 2021
1 parent b60164b commit 4fff58c
Show file tree
Hide file tree
Showing 8 changed files with 230 additions and 150 deletions.
99 changes: 99 additions & 0 deletions data/lua/core.lua
Expand Up @@ -57,6 +57,102 @@ function stringx.iter_ranges(str)
end)
end

--[========[Additional mathematical functions]========]

function mathx.random_choice(possible_values, random_func)
random_func = random_func or mathx.random
assert(type(possible_values) == "table" or type(possible_values) == "string",
string.format("mathx.random_choice expects a string or table as parameter, got %s instead",
type(possible_values)))

local items = {}
local num_choices = 0

if type(possible_values) == "string" then
-- split on commas
for word in possible_values:split() do
-- does the word contain two dots? If yes, that's a range
local dots_start, dots_end = word:find("%.%.")
if dots_start then
-- split on the dots if so and cast to numbers
local low = tonumber(word:sub(1, dots_start-1))
local high = tonumber(word:sub(dots_end+1))
-- perhaps someone passed a string as part of the range, intercept the issue
if not (low and high) then
wesnoth.message("Malformed range: " .. word)
table.insert(items, word)
num_choices = num_choices + 1
else
if low > high then
-- low is greater than high, swap them
low, high = high, low
end

-- if both ends represent the same number, then just use that number
if low == high then
table.insert(items, low)
num_choices = num_choices + 1
else
-- insert a table representing the range
table.insert(items, {low, high})
-- how many items does the range contain? Increase difference by 1 because we include both ends
num_choices = num_choices + (high - low) + 1
end
end
else
-- handle as a string
table.insert(items, word)
num_choices = num_choices + 1
end
end
else
num_choices = #possible_values
items = possible_values
-- We need to parse ranges separately anyway
for i, val in ipairs(possible_values) do
if type(val) == "table" then
assert(#val == 2 and type(val[1]) == "number" and type(val[2]) == "number", "Malformed range for helper.rand")
if val[1] > val[2] then
val = {val[2], val[1]}
end
num_choices = num_choices + (val[2] - val[1])
end
end
end

local idx = random_func(1, num_choices)

for i, item in ipairs(items) do
if type(item) == "table" then -- that's a range
local elems = item[2] - item[1] + 1 -- amount of elements in the range, both ends included
if elems >= idx then
return item[1] + elems - idx
else
idx = idx - elems
end
else -- that's a single element
idx = idx - 1
if idx == 0 then
return item
end
end
end

return nil
end

function mathx.shuffle(t, random_func)
random_func = random_func or mathx.random
-- since tables are passed by reference, this is an in-place shuffle
-- it uses the Fisher-Yates algorithm, also known as Knuth shuffle
assert(type(t) == "table", string.format("mathx.shuffle expects a table as parameter, got %s instead", type(t)))
local length = #t
for index = length, 2, -1 do
local random = random_func(1, index)
t[index], t[random] = t[random], t[index]
end
end

--[========[Config Manipulation Functions]========]

local function ensure_config(cfg)
Expand Down Expand Up @@ -881,5 +977,8 @@ wesnoth.show_dialog = wesnoth.deprecate_api('wesnoth.show_dialog', 'gui.show_dia
wesnoth.format = wesnoth.deprecate_api('wesnoth.format', 'stringx.vformat', 1, nil, stringx.vformat)
wesnoth.format_conjunct_list = wesnoth.deprecate_api('wesnoth.format_conjunct_list', 'stringx.format_conjunct_list', 1, nil, stringx.format_conjunct_list)
wesnoth.format_disjunct_list = wesnoth.deprecate_api('wesnoth.format_disjunct_list', 'stringx.format_disjunct_list', 1, nil, stringx.format_disjunct_list)
-- MathX module
wesnoth.random = wesnoth.deprecate_api('wesnoth.random', 'mathx.random', 1, nil, mathx.random)
wesnoth.get_time_stamp = wesnoth.deprecate_api('wesnoth.get_time_stamp', 'mathx.current_timestamp', 1, nil, mathx.current_timestamp)
-- Other
unpack = wesnoth.deprecate_api('unpack', 'table.unpack', 3, '1.17', table.unpack)
111 changes: 3 additions & 108 deletions data/lua/helper.lua
Expand Up @@ -93,114 +93,6 @@ function helper.adjacent_tiles(x, y, with_borders)
end
end

function helper.rand (possible_values, random_func)
random_func = random_func or wesnoth.random
assert(type(possible_values) == "table" or type(possible_values) == "string",
string.format("helper.rand expects a string or table as parameter, got %s instead",
type(possible_values)))

local items = {}
local num_choices = 0

if type(possible_values) == "string" then
-- split on commas
for word in possible_values:gmatch("[^,]+") do
-- does the word contain two dots? If yes, that's a range
local dots_start, dots_end = word:find("%.%.")
if dots_start then
-- split on the dots if so and cast as numbers
local low = tonumber(word:sub(1, dots_start-1))
local high = tonumber(word:sub(dots_end+1))
-- perhaps someone passed a string as part of the range, intercept the issue
if not (low and high) then
wesnoth.message("Malformed range: " .. possible_values)
table.insert(items, word)
num_choices = num_choices + 1
else
if low > high then
-- low is greater than high, swap them
low, high = high, low
end

-- if both ends represent the same number, then just use that number
if low == high then
table.insert(items, low)
num_choices = num_choices + 1
else
-- insert a table representing the range
table.insert(items, {low, high})
-- how many items does the range contain? Increase difference by 1 because we include both ends
num_choices = num_choices + (high - low) + 1
end
end
else
-- handle as a string
table.insert(items, word)
num_choices = num_choices + 1
end
end
else
num_choices = #possible_values
items = possible_values
-- We need to parse ranges separately anyway
for i, val in ipairs(possible_values) do
if type(val) == "table" then
assert(#val == 2 and type(val[1]) == "number" and type(val[2]) == "number", "Malformed range for helper.rand")
if val[1] > val[2] then
val = {val[2], val[1]}
end
num_choices = num_choices + (val[2] - val[1])
end
end
end

local idx = random_func(1, num_choices)

for i, item in ipairs(items) do
if type(item) == "table" then -- that's a range
local elems = item[2] - item[1] + 1 -- amount of elements in the range, both ends included
if elems >= idx then
return item[1] + elems - idx
else
idx = idx - elems
end
else -- that's a single element
idx = idx - 1
if idx == 0 then
return item
end
end
end

return nil
end

function helper.round( number )
-- code converted from util.hpp, round_portable function
-- round half away from zero method
if number >= 0 then
number = math.floor( number + 0.5 )
else
number = math.ceil ( number - 0.5 )
end

return number
end

function helper.shuffle( t, random_func )
random_func = random_func or wesnoth.random
-- since tables are passed by reference, this is an in-place shuffle
-- it uses the Fisher-Yates algorithm, also known as Knuth shuffle
assert(
type( t ) == "table",
string.format( "helper.shuffle expects a table as parameter, got %s instead", type( t ) ) )
local length = #t
for index = length, 2, -1 do
local random = random_func( 1, index )
t[index], t[random] = t[random], t[index]
end
end

-- Compatibility and deprecations
helper.distance_between = wesnoth.deprecate_api('helper.distance_between', 'wesnoth.map.distance_between', 1, nil, wesnoth.map.distance_between)
helper.get_child = wesnoth.deprecate_api('helper.get_child', 'wml.get_child', 1, nil, wml.get_child)
Expand All @@ -224,5 +116,8 @@ helper.shallow_parsed = wesnoth.deprecate_api('helper.shallow_parsed', 'wml.shal
helper.set_wml_var_metatable = wesnoth.deprecate_api('helper.set_wml_var_metatable', 'wml.variable.proxy', 2, nil, helper.set_wml_var_metatable)
helper.set_wml_tag_metatable = wesnoth.deprecate_api('helper.set_wml_tag_metatable', 'wml.tag', 2, nil, helper.set_wml_tag_metatable)
helper.get_user_choice = wesnoth.deprecate_api('helper.get_user_choice', 'gui.get_user_choice', 1, nil, gui.get_user_choice)
helper.rand = wesnoth.deprecate_api('helper.rand', 'mathx.random_choice', 1, nil, mathx.random_choice)
helper.round = wesnoth.deprecate_api('helper.round', 'mathx.round', 1, nil, mathx.round)
helper.shuffle = wesnoth.deprecate_api('helper.shuffle', 'mathx.shuffle', 1, nil, mathx.shuffle)

return helper
8 changes: 8 additions & 0 deletions projectfiles/VC16/wesnoth.vcxproj
Expand Up @@ -2702,6 +2702,13 @@
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Test_Debug|x64'">$(IntDir)Scripting\</ObjectFileName>
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Test_Release|x64'">$(IntDir)Scripting\</ObjectFileName>
</ClCompile>
<ClCompile Include="..\..\src\scripting\lua_mathx.cpp">
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(IntDir)Scripting\</ObjectFileName>
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='ReleaseDEBUG|x64'">$(IntDir)Scripting\</ObjectFileName>
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(IntDir)Scripting\</ObjectFileName>
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Test_Debug|x64'">$(IntDir)Scripting\</ObjectFileName>
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Test_Release|x64'">$(IntDir)Scripting\</ObjectFileName>
</ClCompile>
<ClCompile Include="..\..\src\scripting\lua_kernel_base.cpp">
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(IntDir)Scripting\</ObjectFileName>
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='ReleaseDEBUG|x64'">$(IntDir)Scripting\</ObjectFileName>
Expand Down Expand Up @@ -4039,6 +4046,7 @@
<ClInclude Include="..\..\src\scripting\lua_gui2.hpp" />
<ClInclude Include="..\..\src\scripting\lua_wml.hpp" />
<ClInclude Include="..\..\src\scripting\lua_stringx.hpp" />
<ClInclude Include="..\..\src\scripting\lua_mathx.hpp" />
<ClInclude Include="..\..\src\scripting\lua_kernel_base.hpp" />
<ClInclude Include="..\..\src\scripting\lua_map_location_ops.hpp" />
<ClInclude Include="..\..\src\scripting\lua_pathfind_cost_calculator.hpp" />
Expand Down
6 changes: 6 additions & 0 deletions projectfiles/VC16/wesnoth.vcxproj.filters
Expand Up @@ -1572,6 +1572,9 @@
<ClCompile Include="..\..\src\scripting\lua_stringx.cpp">
<Filter>Scripting</Filter>
</ClCompile>
<ClCompile Include="..\..\src\scripting\lua_mathx.cpp">
<Filter>Scripting</Filter>
</ClCompile>
<ClCompile Include="..\..\src\scripting\lua_widget.cpp">
<Filter>Scripting</Filter>
</ClCompile>
Expand Down Expand Up @@ -3075,6 +3078,9 @@
<ClInclude Include="..\..\src\scripting\lua_stringx.hpp">
<Filter>Scripting</Filter>
</ClInclude>
<ClInclude Include="..\..\src\scripting\lua_mathx.hpp">
<Filter>Scripting</Filter>
</ClInclude>
<ClInclude Include="..\..\src\scripting\lua_widget.hpp">
<Filter>Scripting</Filter>
</ClInclude>
Expand Down
1 change: 1 addition & 0 deletions source_lists/wesnoth
Expand Up @@ -321,6 +321,7 @@ scripting/lua_formula_bridge.cpp
scripting/lua_gui2.cpp
scripting/lua_wml.cpp
scripting/lua_stringx.cpp
scripting/lua_mathx.cpp
scripting/lua_kernel_base.cpp
scripting/lua_map_location_ops.cpp
scripting/lua_preferences.cpp
Expand Down
44 changes: 2 additions & 42 deletions src/scripting/lua_kernel_base.cpp
Expand Up @@ -19,7 +19,6 @@
#include "gui/core/gui_definition.hpp" // for remove_single_widget_definition
#include "log.hpp"
#include "lua_jailbreak_exception.hpp" // for lua_jailbreak_exception
#include "random.hpp"
#include "seed_rng.hpp"
#include "deprecation.hpp"
#include "language.hpp" // for get_language
Expand All @@ -37,6 +36,7 @@
#include "scripting/lua_wml.hpp"
#include "scripting/lua_stringx.hpp"
#include "scripting/lua_map_location_ops.hpp"
#include "scripting/lua_mathx.hpp"
#include "scripting/lua_rng.hpp"
#include "scripting/lua_widget.hpp"
#include "scripting/push_check.hpp"
Expand Down Expand Up @@ -271,36 +271,6 @@ static int intf_name_generator(lua_State *L)
return 1;
}

/**
* Returns a random numer, same interface as math.random.
*/
static int intf_random(lua_State *L)
{
if (lua_isnoneornil(L, 1)) {
double r = static_cast<double>(randomness::generator->next_random());
double r_max = static_cast<double>(std::numeric_limits<uint32_t>::max());
lua_push(L, r / (r_max + 1));
return 1;
}
else {
int32_t min;
int32_t max;
if (lua_isnumber(L, 2)) {
min = lua_check<int32_t>(L, 1);
max = lua_check<int32_t>(L, 2);
}
else {
min = 1;
max = lua_check<int32_t>(L, 1);
}
if (min > max) {
return luaL_argerror(L, 1, "min > max");
}
lua_push(L, randomness::generator->get_random_int(min, max));
return 1;
}
}

/**
* Logs a message
* Arg 1: (optional) Logger
Expand Down Expand Up @@ -365,15 +335,6 @@ static int intf_get_image_size(lua_State *L) {
return 2;
}

/**
* Returns the time stamp, exactly as [set_variable] time=stamp does.
* - Ret 1: integer
*/
static int intf_get_time_stamp(lua_State *L) {
lua_pushinteger(L, SDL_GetTicks());
return 1;
}

static int intf_get_language(lua_State* L)
{
lua_push(L, get_language().localename);
Expand Down Expand Up @@ -421,6 +382,7 @@ lua_kernel_base::lua_kernel_base()
{ "utf8", luaopen_utf8 }, // added in Lua 5.3
// Wesnoth libraries
{ "stringx",lua_stringx::luaW_open },
{ "mathx", lua_mathx::luaW_open },
{ "wml", lua_wml::luaW_open },
{ "gui", lua_gui2::luaW_open },
{ nullptr, nullptr }
Expand Down Expand Up @@ -473,10 +435,8 @@ lua_kernel_base::lua_kernel_base()
{ "compile_formula", &lua_formula_bridge::intf_compile_formula},
{ "eval_formula", &lua_formula_bridge::intf_eval_formula},
{ "name_generator", &intf_name_generator },
{ "random", &intf_random },
{ "log", &intf_log },
{ "get_image_size", &intf_get_image_size },
{ "get_time_stamp", &intf_get_time_stamp },
{ "get_language", &intf_get_language },
{ nullptr, nullptr }
};
Expand Down

0 comments on commit 4fff58c

Please sign in to comment.