Module:Random: Difference between revisions

Content deleted Content added
don't assume that RAND_MAX is 2^31-1
allow newline separators
 
(4 intermediate revisions by 2 users not shown)
Line 1:
-- This module contains a number of functions that make use of random numbers.
 
local pcfg = {}
 
--------------------------------------------------------------------------------------
-- Configuration
--------------------------------------------------------------------------------------
 
-- Set this to true if your wiki has a traffic rate of less than one edit every two minutes or so.
-- This will prevent the same "random" number being generated many times in a row until a new edit is made
-- to the wiki. This setting is only relevant if the |same= parameter is set.
cfg.lowTraffic = false
 
-- If cfg.lowTraffic is set to true, and the |same= parameter is set, this value is used for the refresh rate of the random seed.
-- This is the number of seconds until the seed is changed. Getting this right is tricky. If you set it too high, the same number
-- will be returned many times in a row. If you set it too low, you may get different random numbers appearing on the same page,
-- particularly for pages that take many seconds to process.
cfg.seedRefreshRate = 60
 
--------------------------------------------------------------------------------------
-- End configuration
--------------------------------------------------------------------------------------
 
local p = {} -- For functions available from other Lua modules.
local l = {} -- For functions not available from other Lua modules, but that need to be accessed using table keys.
 
local yesno = require('Module:Yesno')
local makeList = require('Module:List').makeList
 
Line 8 ⟶ 31:
-- Helper functions
--------------------------------------------------------------------------------------
 
local function setRandomSeed()
-- Set the seed for the random number generator. This works well on the English Wikipedia due to the high
-- edit rate, but should also work well on smaller wikis due to the variability of os.time() (the current time)
-- and os.clock() (the time the program takes to run).
math.randomseed(mw.site.stats.edits + mw.site.stats.pages + os.time() + math.floor(os.clock() * 1000000000))
end
 
local function raiseError(msg)
Line 36 ⟶ 52:
end
 
local function getRandoml.number(args)
-- Gets a random number. It is left to the calling function to set the random seed.
first = tonumber(args[1])
second = tonumber(args[2])
Line 53 ⟶ 69:
return math.random()
end
end
 
function p._number(args)
-- Returns a random number. Will return different random numbers even on the same page.
setRandomSeed()
return getRandom(args)
end
 
function p._same_number(args)
-- Returns a random number. Will return the same random number on the same page if called with the same parameters.
-- If you are using a very low traffic wiki this might not update frequently enough, and you may get the same "random"
-- number many times in a row. (Until someone makes a new edit, or creates a new user account, etc.)
-- To fix this, replace the line beginning with "local seed" with the following code:
--
-- local seed = math.floor(os.time() / 60)
--
-- This makes the seed change once a minute. (The "60" is the number of seconds between one seed and the next.)
-- You can decrease the value if you want the seed to update more frequently. However, smaller values increase the risk
-- that the function may return different values, especially for calls to Lua that take several seconds to complete.
local stats = mw.site.stats
local views = stats.views or 0 -- This is not always available, so we need a backup.
local seed = views + stats.pages + stats.articles + stats.files + stats.edits + stats.users + stats.activeUsers + stats.admins -- Make this as random as possible without using os.time() or os.clock()
math.randomseed(seed)
return getRandom(args)
end
 
Line 83 ⟶ 75:
--------------------------------------------------------------------------------------
 
function pl._datedate(args)
-- This function gets random dates, and takes timestamps as positional arguments.
-- With no arguments specified, it outputs a random date in the current year.
Line 90 ⟶ 82:
-- The output can be formatted using the "format" argument, which works in the same way as the #time parser function.
-- The default format is the standard Wikipedia timestamp.
setRandomSeed()
local lang = mw.language.getContentLanguage()
 
Line 154 ⟶ 144:
--------------------------------------------------------------------------------------
 
local function p.randomizeArray(t, limit)
-- Randomizes an array. It works by iterating through the list backwards, each time swapping the entry
-- "i" with a random entry. Courtesy of Xinhuan at http://forums.wowace.com/showthread.php?p=279756
-- If the limit parameter is set, the array is shortened to that many elements after being randomized.
setRandomSeed()
-- The lowest possible value is 0, and the highest possible is the length of the array.
for i = #t, 2, -1 do
local len = #t
for i = len, 2, -1 do
local r = math.random(i)
t[i], t[r] = t[r], t[i]
end
if limit and limit < len then
return t
local ret = {}
for i, v in ipairs(t) do
if i > limit then
break
end
ret[i] = v
end
return ret
else
return t
end
end
 
Line 184 ⟶ 187:
-- Include an easy way to use spaces as separators.
return ' '
elseif sep == 'newline' then
-- Ditto for newlines
return '\n'
elseif type(sep) == 'string' then
-- If the separator is a recognised MediaWiki separator, use that. Otherwise use the value of sep if it is a string.
Line 198 ⟶ 204:
local function makeRandomList(args)
local list = removeBlanks(args)
list = p.randomizeArray(list, tonumber(args.limit))
return list
end
 
function pl._itemitem(args)
-- Returns a random item from a numbered list.
setRandomSeed()
local list = removeBlanks(args)
iflocal #listlen >= 1 then#list
if len >= 1 then
return list[math.random(#list)]
return list[math.random(len)]
end
end
 
function pl._listlist(args)
-- Randomizes a list and concatenates the result with a separator.
setRandomSeed()
local list = makeRandomList(args)
local sep = makeSeparator(args.sep or args.separator)
Line 219 ⟶ 224:
end
 
function pl._text_listtext_list(args)
-- Randomizes a list and concatenates the result, text-style. Accepts separator and conjunction arguments.
setRandomSeed()
local list = makeRandomList(args)
local sep = makeSeparator(args.sep or args.separator)
local conj = makeSeparator(args.conj or args.conjunction)
return mw.text.listToText(list, sep, conj)
end
 
function l.array(args)
-- Returns a Lua array, randomized. For use from other Lua modules.
return randomizeArray(args.t, args.limit)
end
 
Line 232 ⟶ 241:
--------------------------------------------------------------------------------------
 
function pl.html_list(args, listType)
-- Randomizes a list and turns it into an HTML list. Uses [[Module:List]].
setRandomSeed()
listType = listType or 'bulleted'
local listArgs = makeRandomList(args) -- Arguments for [[Module:List]].
Line 245 ⟶ 253:
end
 
--------------------------------------------------------------------------------------
-- The main function. Called from other Lua modules.
--------------------------------------------------------------------------------------
 
function p.main(funcName, args, listType)
-- Sets the seed for the random number generator and passes control over to the other functions.
local same = yesno(args.same)
if not same then
-- Generates a different number every time the module is called, even from the same page.
-- This is because of the variability of os.clock (the time in seconds that the Lua script has been running for).
math.randomseed(mw.site.stats.edits + mw.site.stats.pages + os.time() + math.floor(os.clock() * 1000000000))
else
if not cfg.lowTraffic then
-- Make the seed as random as possible without using anything time-based. This means that the same random number
-- will be generated for the same input from the same page - necessary behaviour for some wikicode templates that
-- assume bad pseudo-random-number generation.
local stats = mw.site.stats
local views = stats.views or 0 -- This is not always available, so we need a backup.
local seed = views + stats.pages + stats.articles + stats.files + stats.edits + stats.users + stats.activeUsers + stats.admins -- Make this as random as possible without using os.time() or os.clock()
math.randomseed(seed)
else
-- Make the random seed change every n seconds, where n is set by cfg.seedRefreshRate.
-- This is useful for low-traffic wikis where new edits may not happen very often.
math.randomseed(math.floor(os.time() / cfg.seedRefreshRate))
end
end
if type(args) ~= 'table' then
error('the second argument to p.main must be a table')
end
return l[funcName](args, listType)
end
--------------------------------------------------------------------------------------
-- Process arguments from #invoke
Line 275 ⟶ 315:
end
end
return p[funcName].main(funcName, args, listType)
end
end
Line 292 ⟶ 332:
 
-- Process arguments for other functions.
local otherFuncs = {'number', 'same_number', 'date', 'item', 'list', 'text_list'}
for _, funcName in ipairs(otherFuncs) do
p[funcName] = makeWrapper('_' .. funcName)
end