模块:Standings

来自Ballance Wiki
跳转至: 导航搜索

此模块的文档可以在模块:Standings/doc创建

local p = {} --p stands for package

local mode_col = 1
local level_col = 2
local score_col = 3
local name_col = 4
local video_col = 5
local date_col = 6

-- .0 => .499, .5 => .999
function expand_from_old_SR_time(score)
	if score:len() == 6 then
		if score:sub(-1) == '0' then
			score = score:sub(1, -2) .. '499'
		elseif score:sub(-1) == '5' then
			score = score:sub(1, -2) .. '999'
		end
	end
	return score
end

function score_less_SR(a, b)
	a = expand_from_old_SR_time(a)
	b = expand_from_old_SR_time(b)
	return a > b
end

function score_less_HS(a, b)
	return a < b
end

function remove_asterisk(score)
	if score:sub(-1) == '*' then
		score = score:sub(1, -2)
	end
	return score
end

-- compare score, including HS and SR
function score_less(a, b)
	a = remove_asterisk(a)
	b = remove_asterisk(b)
	if string.find(a, ':') and string.find(b, ':') then
		return score_less_SR(a, b)
	elseif not string.find(a, ':') and not string.find(b, ':') then
		return score_less_HS(a, b)
	end
end

-- sort by mode, level, score and date
function standing_less(a, b)
	if a[mode_col] ~= b[mode_col] then
		return a[mode_col] < b[mode_col]
	elseif a[level_col] ~= b[level_col] then
		return a[level_col] < b[level_col]
	elseif remove_asterisk(a[score_col]) ~= remove_asterisk(b[score_col]) then
		return score_less(b[score_col], a[score_col])
	else
		return a[date_col] < b[date_col]
	end
end

-- sort by mode, level, name and score
function unique_less(a, b)
	if a[mode_col] ~= b[mode_col] then
		return a[mode_col] < b[mode_col]
	elseif a[level_col] ~= b[level_col] then
		return a[level_col] < b[level_col]
	elseif a[name_col] ~= b[name_col] then
		return a[name_col] < b[name_col]
	else
		return score_less(b[score_col], a[score_col])
	end
end

function unique(records)
	local records_new = {}
	local record_last = nil
	table.sort(records, unique_less)
	for key, record in ipairs(records) do
		if record_last == nil then
			table.insert(records_new, record)
		elseif record[mode_col] ~= record_last[mode_col]
			or record[level_col] ~= record_last[level_col]
			or record[name_col] ~= record_last[name_col] then
			table.insert(records_new, record)
		end
		record_last = record
	end
	return records_new
end

function select_standing(records, mode, level)
	local records_new = {}
	for key, record in ipairs(records) do
		if record[mode_col] == mode and record[level_col] == level then
			table.insert(records_new, record)
		end
	end
	records_new = unique(records_new)
	table.sort(records_new, standing_less)
	return records_new
end

function select_WR(records, mode, level)
	local records_new = {}
	local highest = nil
	table.sort(records, standing_less)
	for i = #records, 1, -1 do
		local record = records[i]
		if record[mode_col] == mode and record[level_col] == level then
			if highest == nil or score_less(highest, record[score_col]) then
				table.insert(records_new, record)
				highest = record[score_col]
			end
		end
	end
	return records_new
end

function trim(s)
	return s:match'^()%s*$' and '' or s:match'^%s*(.*%S)'
end

function get_records()
	local content = mw.title.new('Ballance个人纪录列表'):getContent()
	local records = {}
	for line in content:gmatch('[^\r\n]+') do
		if string.sub(line, 1, string.len('| ')) == '| ' then
			line = line:sub(2)
			record = {}
			for element in line:gmatch("[^|]+") do
				if element ~= '' then
					element = trim(element)
					table.insert(record, element)
				end
			end
			table.insert(records, record)
		end
	end
	return records
end

function get_score_limits(mode, level)
	local score_limits = {}
	local content
	local found_level = false
	if mode == '常规HS' then
		content = mw.title.new('Ballance HS排行榜'):getContent()
	elseif mode == '常规SR' or mode == '连打' then
		content = mw.title.new('Ballance SR排行榜'):getContent()
	else
		return score_limits
	end
	
	for line in content:gmatch('[^\r\n]+') do
		if string.sub(line, 1, string.len('| ')) == '| ' then
			for element in line:gmatch("[^|]+") do
				if element ~= '' then
					element = trim(element)
					if found_level then
						table.insert(score_limits, element)
						if #score_limits == 2 then
							score_limits[1], score_limits[2] = score_limits[2], score_limits[1]
							return score_limits
						end
					elseif element == level then
						found_level = true
					end
				end
			end
		end
	end
	return score_limits
end

function get_score_limit_str(highest, score_limits)
	local str = ''
	local i, score_limit = next(score_limits)
	if i ~= nil then
		if highest == nil or score_less(highest, score_limit) then
			local limit_str
			if #score_limits == 1 then
				limit_str = '上榜线'
			elseif #score_limits == 2 then
				limit_str = '精品线'
			end
			str = '|-\n|colspan="4" style="text-align: center;"|' .. limit_str .. ': ' .. score_limit .. '\n'
			table.remove(score_limits, 1)
		end
	end
	return str
end

function p.standings(frame)
	local args = frame.args[1] and frame.args or frame:getParent().args;
	local records = select_standing(get_records(), args[1], args[2])
	local score_limits = get_score_limits(args[1], args[2])
	local had_score_limits = (next(score_limits) ~= nil)
	local ranking = 0
	local ranking_hold = 0
	local highest = nil
	local wikitable_str = '{| class="wikitable" style="display: inline-block; vertical-align: top"\n'
	if args[2]:len() == 2 then
		title = '关卡 ' .. args[2]
	else
		title = args[2]
	end
	wikitable_str = wikitable_str .. '|+ <h3>' .. title .. '</h3>\n'
	wikitable_str = wikitable_str .. '! 名次 !! 分数 !! 纪录持有者 !! 视频链接\n'
	
	for key, record in ipairs(records) do
		ranking = ranking + 1
		if highest == nil or highest ~= remove_asterisk(record[score_col]) then
			ranking_hold = ranking
			highest = remove_asterisk(record[score_col])
			wikitable_str = wikitable_str .. get_score_limit_str(highest, score_limits)
			if next(score_limits) == nil and had_score_limits then
				ranking_hold = '附'
			end
		end
		wikitable_str = wikitable_str .. '|-\n'
		wikitable_str = wikitable_str .. '|' .. ranking_hold
		wikitable_str = wikitable_str .. '||' .. record[score_col]
		wikitable_str = wikitable_str .. '||' .. record[name_col]
		wikitable_str = wikitable_str .. '||' .. record[video_col]
		wikitable_str = wikitable_str .. '\n'
	end
	while next(score_limits) do
		wikitable_str = wikitable_str .. get_score_limit_str(nil, score_limits)
	end
	wikitable_str = wikitable_str .. '|}\n'
	mw.log(wikitable_str)
	return wikitable_str
end

return p