Jump to content

Module:Random

Permanently protected module
From Wikipedia, the free encyclopedia
This is an old revision of this page, as edited by Mr. Stradivarius (talk | contribs) at 05:37, 12 November 2013 (module containing various functions that use random numbers). The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.
(diff) ← Previous revision | Latest revision (diff) | Newer revision → (diff)

-- This module contains a number of functions that make use of random numbers.

local cfg = {}
cfg.space = 'space'

local p = {}

-- Set the seed for the random number to the current number of edits made to Wikipedia.
-- The English Wikipedia gets dozens of edits each minute, so this is as close to a random seed
-- as we have. On smaller wikis this will produce the same number if the edit count has not changed,
-- so please use it with caution.
math.randomseed(mw.site.stats.edits)

local function removeBlanks(t)
	-- Removes blank entries from an array so that it can be used with ipairs.
	local ret = {}
	for k, v in pairs(t) do
		if type(k) == 'number' then -- Make sure we have no non-string portal names.
			table.insert(ret, k)
		end
	end
	table.sort(ret)
	for i, v in ipairs(ret) do
		ret[i] = t[v]
	end
	return ret
end

local function makeSeparator(sep)
	if sep == cfg.space then
		-- Include an easy way to use spaces as separators.
		return ' '
	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.
		local mwseparators = {'dot', 'pipe', 'comma', 'tpt-languages'}
		for _, mwsep in ipairs(mwseparators) do
			if sep == mwsep then
				return mw.message.new( sep .. '-separator' ):plain()
			end
		end
		return sep
	end
end

function p._number(args)
	-- Returns a random number. This algorithm is taken from [[Module:Math]].
	first = tonumber(args[1]) -- if it doesn't exist it's NaN, if not a number it's nil
	second = tonumber(args[2])
	if first then -- if NaN or nil, will skip down to final return
		if first <= second then -- could match if both nil, but already checked that first is a number in last line
			return math.random(first, second)
		end
		return math.random(first)
	end   
	return math.random()
end

function p._item(args)
	-- Returns a random item from a numbered list.
	local list = removeBlanks(args)
	if #list >= 1 then
		return list[math.random(#list)]
	end
end

function p._randomize(args)
	-- Randomizes a list and concatenates the result with a separator. It works by iterating through the list backwards,
	-- each time swapping the entry "i" with a random entry.
	-- Randomization algorithm courtesy of Xinhuan at http://forums.wowace.com/showthread.php?p=279756
	local list = removeBlanks(args)
	for i = #list, 2, -1 do
		local r = math.random(i)
		list[i], list[r] = list[r], list[i]
	end
	local sep = makeSeparator(args.sep or args.separator)
	return table.concat(list, sep)
end

local function makeWrapper(func)
	return function (frame)
		-- If called via #invoke, use the args passed into the invoking template, or the args passed to #invoke if any exist.
		-- Otherwise assume args are being passed directly in from the debug console or from another Lua module.
		local origArgs
		if frame == mw.getCurrentFrame() then
			origArgs = frame:getParent().args
			for k, v in pairs(frame.args) do
				origArgs = frame.args
				break
			end
		else
				origArgs = frame
		end
		-- Trim whitespace and remove blank arguments.
		local args = {}
		for k, v in pairs(origArgs) do
				if type(v) == 'string' then
						v = mw.text.trim(v)
				end
				if v ~= '' then
						args[k] = v
				end
		end
		return p[func](args)
	end
end

for funcName in pairs(p) do
	-- Generates a function to be used with #invoke, removing the leading underscore.
	-- For example, p._number will be accessible to #invoke using p.number.
	p[funcName:sub(2, -1)] = makeWrapper(funcName)
end

return p