Modul:games

A Wikiszótárból, a nyitott szótárból

A modult a Modul:games/doc lapon tudod dokumentálni

local export = {}

local fun = require "Module:fun"
local m_table = require "Module:table"

local title = mw.title.getCurrentTitle()
local namespace = title.nsText
local fullpagename = title.fullText

--[[
local langbr = "⟨"
local rangbr = "⟩"
--]]

local function quote(word)
	return "“" .. word .. "”"
end

local function link(word, lang)
	if word:find("%[%[") then
		return word
	else
		if lang then
			return "[[" .. lang:makeEntryName(word) .. "|" .. word .. "]]"
		else
			return "[[" .. word .. "]]"
		end
	end
end

local function italicize(word)
	if word:find("''") then
		return word
	else
		return "''" .. word .. "''"
	end
end

local function tag(word, script)
	return '<span class="' .. script .. '">' .. word .. '</span>'
end

local function add_color(color, text, attribute)
	return '<span style="color: ' .. color .. ';" ' .. (attribute or '') .. '>' .. text .. '</span>'
end

local is_combining = require "Module:Unicode data".is_combining

local find_best_script = require "Module:scripts".findBestScriptWithoutLang

local translit_module = {
	Hang = "ko-translit",
	ja = { "ja", "kana_to_romaji" },
}

local non_diacritic_equivalencies = {
	["ð"] = "d", ["ø"] = "o", ["þ"] = "???", ["đ"] = "d", ["ħ"] = "h", ["ı"] = "i",
	["ŋ"] = "n", ["œ"] = "???", ["ŧ"] = "t", ["ſ"] = "s", ["ß"] = "???",
	["ł"] = "l", ["ᵃ"] = "a", ["ᵇ"] = "b", ["ᶜ"] = "c", ["ᵈ"] = "d", ["ᵉ"] = "e",
	["ᶠ"] = "f", ["ᵍ"] = "g", ["ʰ"] = "h", ["ⁱ"] = "i", ["ʲ"] = "j", ["ᵏ"] = "k",
	["ˡ"] = "l", ["ᵐ"] = "m", ["ⁿ"] = "n", ["ᵒ"] = "o", ["ᵖ"] = "p", ["ʳ"] = "r",
	["ˢ"] = "s", ["ᵗ"] = "t", ["ᵘ"] = "u", ["ᵛ"] = "v", ["ʷ"] = "w", ["ˣ"] = "x",
	["ʸ"] = "y", ["ᶻ"] = "z", ["ᴬ"] = "a", ["ᴮ"] = "b", ["ᴰ"] = "d", ["ᴱ"] = "e",
	["ᴳ"] = "g", ["ᴴ"] = "h", ["ᴵ"] = "i", ["ᴶ"] = "j", ["ᴷ"] = "k", ["ᴸ"] = "l",
	["ᴹ"] = "m", ["ᴺ"] = "n", ["ᴼ"] = "o", ["ᴾ"] = "p", ["ᴿ"] = "r", ["ᵀ"] = "t",
	["ᵁ"] = "u", ["ⱽ"] = "v", ["ᵂ"] = "w", ["ₐ"] = "a", ["ₑ"] = "e", ["ₕ"] = "h",
	["ᵢ"] = "i", ["ⱼ"] = "j", ["ₖ"] = "k", ["ₗ"] = "l", ["ₘ"] = "m", ["ₙ"] = "n",
	["ₒ"] = "o", ["ₚ"] = "o", ["ᵣ"] = "r", ["ₛ"] = "s", ["ₜ"] = "t", ["ᵤ"] = "u",
	["ᵥ"] = "v", ["ₓ"] = "x", -- ["ŋ"] = "n", ["ɔ"] = "o",
	["ʿ"] = "", ["ʾ"] = "", ["ʻ"] = "", ["’"] = "",
 -- ...
}

-- Remove all diacritics. Make use of the replacements shown above.
-- Remove spaces and hyphens.
local function regularize(word)
	return (mw.ustring.lower(mw.ustring.toNFD(word))
		:gsub(
			"[\194-\244][\128-\191]+",
			function (non_ASCII_character)
				if is_combining(mw.ustring.codepoint(non_ASCII_character)) then
					return ""
				end
				return non_diacritic_equivalencies[non_ASCII_character]
			end))
		:gsub(
			"[ %-%']",
			"")
end

-- Allowed parameters:
-- -- numbers
-- -- lang, lang1, lang2, ...
-- -- tr, tr1, tr2, ...
-- -- ignore rules (boolean)
local function get_args(args)
	local words = {}
	local langs, translits, ignore_rules
	
	for arg, value in pairs(args) do
		if type(arg) == "number" then
			words[arg] = value
		elseif type(arg) == "string" then
			if arg == "ignore rules" then
				ignore_rules = require("Module:yesno")(value)
			else
				local name, number = arg:match("^(%l+)(%d*)$")
				if not (name == "lang" or name == "tr") then
					error("The parameter |" .. arg .. "= is not used by this template.")
				end
				
				if number == "" then
					number = 1
				else
					number = tonumber(number)
				end
				
				if name == "lang" then
					langs = langs or {}
					langs[number] = langs[number] and error("Two langs with index " .. number .. ".")
						or value and (require("Module:languages").getByCode(value)
						or error("Invalid language code " .. quote(value) .. "."))
				else
					translits = translits or {}
					translits[number] = translits[number] and error("Two translits with index " .. number .. ".")
						or value
				end
			end
		end
	end
	
	return words, langs, translits, ignore_rules
end

local function show_words(previous_word, interposing_words, following_word)
	return link(previous_word) .. " ("
		.. table.concat(
			fun.map(
				function(word)
					return italicize(link(word))
				end,
				interposing_words),
			", ")
		.. ") "
		.. link(following_word)
end

function export.Christmas_Competition_entry(frame)
	local words, langs, translits, ignore_rules = get_args(frame:getParent().args)
	
	if namespace == "" or namespace == "Reconstruction" or namespace == "Appendix" then
		error("This template is only supposed to be used in the Christmas Competition!")
	end
	
	words.length = #words
	
	if words.length < 3 then
		if namespace == "Template" then
			words = { "Shèngdànjié", "jiellat", "llatino" }
		else
			error("This template wants at least three words.")
		end
	end
	
	local regularized_words = {}
	local nonLatin_i = 0
	local last_interposing_word = (words.length or #words) - 1
	
	for i, word in ipairs(words) do
		-- Check for duplicate interposing words.
		if 1 < i and i < last_interposing_word then
			for j = i + 1, last_interposing_word do
				if word == words[j] then
					error("You used the word " .. quote(word) .. " twice, in parameters " .. i .. " and " .. j .. "!")
				end
			end
		end
		
		local tr
		if word:find("[\128-\255]") then -- non-ASCII
			local script = find_best_script(word)
			if script and not script:find "Lat" then -- non-Latin
				-- [[Module:ko-translit]] requires word to be in NFC.
				nonLatin_i = nonLatin_i + 1
				local lang = langs and langs[nonLatin_i]
				if translits and translits[nonLatin_i] then
					tr = translits[nonLatin_i]
				elseif lang then
					tr = lang:transliterate(mw.ustring.toNFC(word), require("Module:scripts").getByCode(script))
						or translit_module[lang:getCode()]
							and require("Module:" .. translit_module[lang:getCode()][1])[translit_module[lang:getCode()][2]](word)
						or error("The " .. lang:getCode()
							.. " transliteration module was unable to generate a transliteration for "
							.. quote(word) .. ".")
				elseif translit_module[script] then
					local success, result = pcall(require("Module:" .. translit_module[script]).tr, mw.ustring.toNFC(word), script)
					tr = success and result or error("[[Module:"
						.. translit_module[script]
						.. "]] wasn't able to generate a transliteration for "
						.. quote(word) .. ".")
				else
					error("You need to give the language code for the word "
						.. quote(word)
						.. " in the |lang" .. nonLatin_i .. "= parameter so that the word can be transliterated.")
				end
				
				if tr then
					if i == 1 or i == words.length then
						word = tag(link(word, lang), script) .. " (" .. tr .. ")"
					else
						word = tag(link(word, lang), script) .. " (" .. italicize(tr) .. ")"
					end
					-- word = italicize(tr) .. " " .. langbr .. tag(link(word), script) .. rangbr
				end
			end
		end
		words[i] = word
		regularized_words[i] = regularize(tr or word)
	end
	
	-- Complain if there aren't as many non-Latin-script words as lang parameters.
	if langs then
		if nonLatin_i < #langs then
			error(#langs .. " |lang= parameter"
				.. (#langs == 1 and "" or "s") .. ", but only "
				.. nonLatin_i .. " non-Latin-script word"
				.. (nonLatin_i == 1 and "" or "s") .. "!")
		end
	end
	
	local previous_word = table.remove(words, 1)
	local last_three = mw.ustring.sub(previous_word, -3)
	local following_word = table.remove(words, #words)
	
	local regularized_previous_word = table.remove(regularized_words, 1)
	local regularized_last_three = regularized_previous_word:sub(-3)
	local regularized_following_word = table.remove(regularized_words, #regularized_words)
	
	-- Check interposing words.
	local messages, message_number, message
	for i, word in ipairs(regularized_words) do
		local ending = word:match("^" .. regularized_last_three .. "(.+)$")
		
		-- Rules:
		--	-- Interposing words must start with last three letters of previous word.
		--	-- After these three letters must be three or more additional letters.
		--	-- These additional letters must begin the following word.
		
		-- WARNING! The color-adding will probably not work with transliteration.
		if not ending then
			if ignore_rules then
				message = true
			else
				message = "The interposing word "
						.. quote(word) .. " must start with the last three letters of "
						.. quote(previous_word) .. ", " .. quote(regularized_last_three) .. "."
			end
		elseif #ending < 3 then
			if ignore_rules then
				message = true
			else
				local difference = 3 - #ending
				local agreement = difference == 1 and "" or "s"
				message = "The interposing word "
						.. quote(word) .. " needs " .. difference .. " more letter" .. agreement .. "."
			end
		elseif not regularized_following_word:find("^" .. ending) then
			if ignore_rules then
				message = true
			else
				message = "The last word " .. quote(following_word) .. " must start with "
					.. quote(ending) .. ", the final letters of "
					.. quote(word) .. " after the last three letters of "
					.. quote(previous_word) .. ", " .. quote(last_three) .. ", are removed."
			end
		end
		
		if message then
			if ignore_rules then
				messages = (messages or 0) + 1
			else
				messages = messages or {}
				message_number = message_number or 1
				messages[message_number] = message
				message_number = message_number + 1
			end
		end
		message = nil
	end
	
	if messages then
		if ignore_rules then
			local plural = messages > 1 and true or false
			return show_words(previous_word, words, following_word) ..
				' <span style="color: red;">Rules broken ' .. messages ..
				' time' .. (plural and "s" or "") .. '!</span>'
		else
			error(table.concat(messages, " "))
		end
	else
		return show_words(previous_word, words, following_word)
	end
end

local Wonderfool_page = "User:AryamanA/Wonderfool"
local function get_Wonderfool_names()
	local list = mw.title.new(Wonderfool_page):getContent():match("==List==\n*(.-)\n*==")
	
	local names = {}
	
	for name in list:gmatch("{{user|([^}]+)}}") do
		names[name] = true
	end
	
	return names
end

local Autotable = require("Module:auto-subtable")

local function compile_points_per_user(games, dont_merge_Wonderfool)
	local users = Autotable()
	
	local merge_Wonderfool = not dont_merge_Wonderfool
	
	local is_Wonderfool
	if merge_Wonderfool then
		is_Wonderfool = get_Wonderfool_names()
	end
	
	for _, game in ipairs(games) do
		for i, entry in ipairs(game) do
			local user = entry.username
			if merge_Wonderfool then
				user = is_Wonderfool[user] and "Wonderfool" or user
			end
			
			local userdata = users[user]
			
			userdata.points = (userdata.points or 0) + entry.points
		end
	end
	return users
end

local function compare(user_table)
	local function get_compare_value(user)
		return user_table[user].points
	end
	return function (user1, user2)
		return get_compare_value(user1) > get_compare_value(user2)
	end
end

local function link_username(username)
	if username == "Wonderfool" then
		return "[[" .. Wonderfool_page .. "|Wonderfool]]"
	else
		return "[[User:" .. username .. "|" .. username .. "]]"
	end
end

local function print_points(users)
	-- Have to remove auto-subtabling; copy in case this causes errors in the
	-- calling function.
	users = require("Module:table").deepcopy(users):un_auto_subtable()
	
	return table.concat(
		fun.mapIter(
			function(data, user)
				return "* " .. link_username(user) .. ": " .. data.points
			end,
			require("Module:table").sortedPairs(users, compare(users))),
		"\n")
end

local function generate_statistics(games)
	local statistics = Autotable()
	local all = {}
	statistics.all = all
	
	for _, game in ipairs(games) do
		for i, entry in ipairs(game) do
			local user = entry.username
			local userdata = statistics[user]
			local interposing_count = entry.interposing.count
			userdata[interposing_count] = (userdata[interposing_count] or 0) + 1
			all[interposing_count] = (all[interposing_count] or 0) + 1
		end
	end
	
	local averages = {}
	for user, interposing_data in pairs(statistics) do
		local total_interposing_words = 0
		local total_entries = 0
		for interposing_count, number_of_times in pairs(interposing_data) do
			total_interposing_words = total_interposing_words + interposing_count * number_of_times
			total_entries = total_entries + number_of_times
		end
		local average = total_interposing_words / total_entries
		averages[user] = math.floor(average * 10 + 0.5 ) / 10
	end
	
	return statistics, averages
end

function print_statistics(statistics, averages)
	local output = {}
	
	for user, data in pairs(statistics) do
		table.insert(output, "* " .. (user ~= "all" and link_username(user) or "all players"))
		table.insert(output, "** " .. averages[user] .. " interposing word" .. (averages[user] * 10 ~= 10 and "s" or "") .. " per entry")
		for interposing_count, number_of_times in pairs(data) do
			table.insert(output, "** " .. interposing_count
				.. " interposing word" .. (interposing_count ~= 1 and "s " or " ")
				.. number_of_times .. " time"
				.. (number_of_times ~= 1 and "s" or ""))
		end
	end
	
	return table.concat(output, "\n")
end

local month_numbers = m_table.invert {
	"January", "February", "March", "April", "May", "June",
	"July", "August", "September", "October", "November", "December"
}

-- Converts a date into a number that will allow the date column to be sorted reliably.
local function make_date_number(date)
	local hour, minute, day_of_month, month, last_two_of_year =
		string.match(date, "^(%d%d):(%d%d), (%d%d?) (%a+) %d%d(%d%d)")
	-- Each part of the date is multiplied by the least power of two greater than
	-- the maximum possible value that that part can have.
	return ((((last_two_of_year - 17) * 2 + month_numbers[month]) * 16 + day_of_month) * 32 + hour) * 64 + minute
end

-- For concatenating virtual interposing word tables produced by mw.loadData.
local function concat(array, sep)
	local str = array[1] or ""
	for i = 2, 15 do
		if array[i] then
			str = str .. sep .. array[i]
		else
			break
		end
	end
	return str
end

function export.show_parsed_games(frame)
	local games = mw.loadData("Module:games/data")
	
	local t = {
		'{| class="wikitable sortable"',
		'! game,<br>entry !! username !! date !! day<br>difference !! preceding<br>word !! interposing words !! count !! following<br>word !! points'
	}
	
	local function detect_and_tag(text)
		if not text:find("[\128-\255]") then
			return tag(link(text), "Latn")
		end
		return tag(link(text), find_best_script(text))
	end
	
	for game_number, game in ipairs(games) do
		for entry_number, entry in ipairs(game) do
			table.insert(
				t,
				('|-\n| data-sort-value="%s" | %d&ndash;%d || %s || data-sort-value="%d" | %s || %s || %s || %s || %d || %s || %d')
					:format(string.format("%02d%02d", game_number, entry_number),
						game_number, entry_number, entry.username,
						make_date_number(entry.date), entry.date:gsub("December", "Dec"):gsub(".+", '<span style="white-space: nowrap;">%1</span>'),
						entry.day_difference and ("%.4f"):format(entry.day_difference) or "&mdash;",
						detect_and_tag(entry.preceding),
						concat(fun.map(detect_and_tag, entry.interposing), ", "), entry.interposing.count,
						detect_and_tag(entry.following),
						entry.points))
		end
	end
	
	table.insert(t, "|}")
	
	return table.concat(t, "\n")
	-- return require("Module:debug").highlight_dump()
end

function export.show_statistics(frame)
	return print_statistics(generate_statistics(mw.loadData("Module:games/data")))
end
	

function export.print_points(frame)
	return print_points(compile_points_per_user(mw.loadData("Module:games/data")))
end

function export.print_game_data(frame)
	return require("Module:debug").highlight_dump(mw.loadData("Module:games/data"))
end

-- fun.logAll(export)

return export