Start a Lua mathx module to hold round, shuffle, random, and a few other things
This commit is contained in:
parent
c4d9590e71
commit
a09f88c1c9
7 changed files with 211 additions and 140 deletions
98
data/lua/core/mathx.lua
Normal file
98
data/lua/core/mathx.lua
Normal file
|
@ -0,0 +1,98 @@
|
|||
--[========[Additional mathematical functions]========]
|
||||
print("Loading mathx module...")
|
||||
|
||||
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 ipairs(possible_values:quoted_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
|
||||
|
||||
wesnoth.random = wesnoth.deprecate_api('wesnoth.random', 'mathx.random', 1, nil, mathx.random)
|
|
@ -86,114 +86,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)
|
||||
|
@ -217,5 +109,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
|
||||
|
|
|
@ -1039,6 +1039,7 @@
|
|||
91E3570A1CACC9B200774252 /* playcampaign.cpp in Sources */ = {isa = PBXBuildFile; fileRef = EC2F600B1A048E210018C9D6 /* playcampaign.cpp */; };
|
||||
91E3570B1CACC9B200774252 /* singleplayer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = EC2F600C1A048E220018C9D6 /* singleplayer.cpp */; };
|
||||
91ECD5D21BA11A5200B25CF1 /* unit_creator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 91ECD5D01BA11A5200B25CF1 /* unit_creator.cpp */; };
|
||||
91F8E12E260A25E2002312BA /* lua_mathx.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 91F8E12D260A25E1002312BA /* lua_mathx.cpp */; };
|
||||
91FAC70A1C7FBC3400DAB2C3 /* lua_formula_bridge.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 91FAC7091C7FBC2C00DAB2C3 /* lua_formula_bridge.cpp */; };
|
||||
91FBBAD81CB6BC3F00470BFE /* filesystem_sdl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 91FBBAD71CB6BC3F00470BFE /* filesystem_sdl.cpp */; };
|
||||
91FBBADB1CB6D1B700470BFE /* markov_generator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 91FBBAD91CB6D1B700470BFE /* markov_generator.cpp */; };
|
||||
|
@ -2232,6 +2233,8 @@
|
|||
91ECD5D11BA11A5200B25CF1 /* unit_creator.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = unit_creator.hpp; sourceTree = "<group>"; };
|
||||
91EF6BFC1C9E22E400E2A733 /* const_clone.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = const_clone.hpp; sourceTree = "<group>"; };
|
||||
91EF6C001C9E22E400E2A733 /* reference_counter.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = reference_counter.hpp; sourceTree = "<group>"; };
|
||||
91F8E12C260A25E1002312BA /* lua_mathx.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = lua_mathx.hpp; sourceTree = "<group>"; };
|
||||
91F8E12D260A25E1002312BA /* lua_mathx.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = lua_mathx.cpp; sourceTree = "<group>"; };
|
||||
91FAC7081C7F931900DAB2C3 /* lua_formula_bridge.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = lua_formula_bridge.hpp; sourceTree = "<group>"; };
|
||||
91FAC7091C7FBC2C00DAB2C3 /* lua_formula_bridge.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = lua_formula_bridge.cpp; sourceTree = "<group>"; };
|
||||
91FBBAD71CB6BC3F00470BFE /* filesystem_sdl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = filesystem_sdl.cpp; sourceTree = "<group>"; };
|
||||
|
@ -4681,6 +4684,8 @@
|
|||
91B621E91B76BB1500B00E0F /* lua_kernel_base.hpp */,
|
||||
ECA4A6791A1EC319006BCCF2 /* lua_map_location_ops.cpp */,
|
||||
91B621EA1B76BB1800B00E0F /* lua_map_location_ops.hpp */,
|
||||
91F8E12D260A25E1002312BA /* lua_mathx.cpp */,
|
||||
91F8E12C260A25E1002312BA /* lua_mathx.hpp */,
|
||||
9190B73A1CA0554900B0EF66 /* lua_pathfind_cost_calculator.hpp */,
|
||||
ECFB61831DA0A0C50055D3F8 /* lua_preferences.cpp */,
|
||||
ECFB61841DA0A0C50055D3F8 /* lua_preferences.hpp */,
|
||||
|
@ -5344,6 +5349,7 @@
|
|||
46685C9D219D518B0009CFFE /* schema_validator.cpp in Sources */,
|
||||
ECF0F80123A09929004A2011 /* lua_stringx.cpp in Sources */,
|
||||
46F92DE72174F6A400602C1C /* game_delete.cpp in Sources */,
|
||||
91F8E12E260A25E2002312BA /* lua_mathx.cpp in Sources */,
|
||||
6295C3C4150FC9750077D8C5 /* map_fragment.cpp in Sources */,
|
||||
EC4E3B1D19B2D7AD0049CBD7 /* map_generator.cpp in Sources */,
|
||||
B5599B2C0EC62181008DD061 /* label.cpp in Sources */,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
@ -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"
|
||||
|
@ -281,36 +281,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
|
||||
|
@ -431,6 +401,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 }
|
||||
|
@ -483,7 +454,6 @@ 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 },
|
||||
|
|
81
src/scripting/lua_mathx.cpp
Normal file
81
src/scripting/lua_mathx.cpp
Normal file
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
Copyright (C) 2014 - 2020 by the Battle for Wesnoth Project https://www.wesnoth.org/
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#include "scripting/lua_mathx.hpp"
|
||||
#include "scripting/lua_kernel_base.hpp"
|
||||
#include "scripting/lua_common.hpp"
|
||||
#include "scripting/push_check.hpp"
|
||||
#include "random.hpp"
|
||||
#include "SDL2/SDL_timer.h" // for SDL_GetTicks
|
||||
|
||||
#include "lua/lauxlib.h"
|
||||
#include "lua/lua.h"
|
||||
#include "lua/lualib.h"
|
||||
|
||||
namespace lua_mathx {
|
||||
|
||||
/**
|
||||
* Returns a random number, 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;
|
||||
}
|
||||
}
|
||||
|
||||
static int intf_round(lua_State* L) {
|
||||
double n = lua_tonumber(L, 1);
|
||||
lua_pushinteger(L, std::round(n));
|
||||
return 1;
|
||||
}
|
||||
|
||||
int luaW_open(lua_State* L) {
|
||||
auto& lk = lua_kernel_base::get_lua_kernel<lua_kernel_base>(L);
|
||||
lk.add_log("Adding mathx module...\n");
|
||||
static luaL_Reg const math_callbacks[] = {
|
||||
{ "random", &intf_random },
|
||||
{ "round", &intf_round },
|
||||
{ nullptr, nullptr },
|
||||
};
|
||||
lua_newtable(L);
|
||||
luaL_setfuncs(L, math_callbacks, 0);
|
||||
// Set the mathx metatable to index the math module
|
||||
lua_createtable(L, 0, 1);
|
||||
lua_getglobal(L, "math");
|
||||
lua_setfield(L, -2, "__index");
|
||||
lua_setmetatable(L, -2);
|
||||
return 1;
|
||||
}
|
||||
|
||||
}
|
20
src/scripting/lua_mathx.hpp
Normal file
20
src/scripting/lua_mathx.hpp
Normal file
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
Copyright (C) 2014 - 2020 by the Battle for Wesnoth Project https://www.wesnoth.org/
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
struct lua_State;
|
||||
|
||||
namespace lua_mathx {
|
||||
int luaW_open(lua_State* L);
|
||||
}
|
Loading…
Add table
Reference in a new issue