• Moegirl.ICU:萌娘百科流亡社群 581077156(QQ),欢迎对萌娘百科运营感到失望的编辑者加入
  • Moegirl.ICU:账号认领正在试运行,有意者请参照账号认领流程

Module:Sandbox/Krypton glow/Crossword

萌娘百科,万物皆可萌的百科全书!转载请标注来源页面的网页链接,并声明引自萌娘百科。内容不可商用。
跳转到导航 跳转到搜索
Template-info.svg 模块文档  [创建] [刷新]
local getArgs = require('Module:Arguments').getArgs
local p = {}

-- 获取单个UTF-8字符的字节数
local function getByteCount(byte)
    if byte <= 127 then return 1 end
    if byte <= 223 then return 2 end
    if byte <= 239 then return 3 end
    if byte <= 247 then return 4 end
    return 1 -- 对于无效的字节序列,默认返回1
end

-- 计算UTF-8字符串中字符的数量
local function countChars(utf8Str)
    local charCount, index = 0, 1
    while index <= #utf8Str do
        local byteCount = getByteCount(string.byte(utf8Str, index))
        index = index + byteCount
        charCount = charCount + 1
    end
    return charCount
end

-- 提取UTF-8字符串中指定位置的字符
local function extractChar(utf8Str, position)
    local charIndex, strIndex = 1, 1
    while strIndex <= #utf8Str do
        if charIndex == position then
            local byteCount = getByteCount(string.byte(utf8Str, strIndex))
            return utf8Str:sub(strIndex, strIndex + byteCount - 1)
        end
        strIndex = strIndex + getByteCount(string.byte(utf8Str, strIndex))
        charIndex = charIndex + 1
    end
    return ""
end

-- 定义方向常量
local HORZ, VERT = 0, 1

-- 解析参数
local function parseArgs(args)
    local entries = {}
    for key, value in pairs(args) do
    	local row, col, dir, word, link, title
        -- 参数形如“行数,列数,方向_词语”
    	row, col, dir, word = string.match(key, "^(%d+),(%d+),(%d+)_([^%[%]]+)$")
	    if not(row and col and dir and word) then
        	-- 含可选参数,形如“行数,列数,方向_词语[链接的条目_外显内容]”
        	row, col, dir, word, link, title = string.match(key, "^(%d+),(%d+),(%d+)_([^%[%]]+)%[([^%]]*)_([^%]]*)%]$")
    	end
    	if row and col and dir and word then
	        local entry = {
	            row = tonumber(row),
	            col = tonumber(col),
	            dir = tonumber(dir) ~= HORZ and VERT or HORZ,
	            word = word,
	            link = link ~= "" and link or nil,
	            title = title ~= "" and title or nil,
	            hint = value
	        }
	    	table.insert(entries, entry)
    	end
    end
    local hide = args.hide
    return entries, hide
end

-- 创建空的游戏板
local function createBoard(entries)
	-- 计算字符占据的最大行、列数
    local maxRow, maxCol = 0, 0
    for _, entry in ipairs(entries) do
        local wordLen = countChars(entry.word)
        if entry.dir == HORZ then
            maxCol = math.max(maxCol, entry.col + wordLen - 1)
            maxRow = math.max(maxRow, entry.row)
        else
            maxRow = math.max(maxRow, entry.row + wordLen - 1)
            maxCol = math.max(maxCol, entry.col)
        end
    end
	-- 创建游戏板
    local board = {}
    for r = 1, maxRow do
        board[r] = {}
        for c = 1, maxCol do
            board[r][c] = {}
        end
    end
    return board
end

-- 分配单词序号
local function setNum(entries)
	-- 横向、纵向分别排序
    table.sort(entries, function(a, b)
        if a.dir == b.dir then
            if a.dir == HORZ then
                return a.row < b.row or (a.row == b.row and a.col < b.col)
            else
                return a.col < b.col or (a.col == b.col and a.row < b.row)
            end
        else
            return a.dir < b.dir
        end
    end)
    -- 分配序号
    local counters = {[HORZ] = 0, [VERT] = 0}
    for _, entry in ipairs(entries) do
        counters[entry.dir] = counters[entry.dir] + 1
        entry.num = counters[entry.dir]
    end

    return entries
end

-- 在游戏板上放置单词
local function placeWord(board, entry)
    local wordLen = countChars(entry.word)
    for i = 1, wordLen do
        local row, col
        if entry.dir == HORZ then
            col = entry.col + i - 1
            row = entry.row
        elseif entry.dir == VERT then
            row = entry.row + i - 1
            col = entry.col
        end

        if row >= 1 and row <= #board and col >= 1 and col <= #board[1] then
        	local cell = board[row][col] or {}
            cell.char = extractChar(entry.word, i)
            -- 每个单词的第一个字添加序号
            if i == 1 then
                if entry.dir == HORZ then
                    cell.hNum = entry.num
                else
                    cell.vNum = entry.num
                end
            end
            board[row][col] = cell
        end
    end
end

-- 构建游戏板
local function buildBoard(entries)
    local board = createBoard(entries)
    setNum(entries)

    for _, entry in ipairs(entries) do
        placeWord(board, entry)
    end
    return board
end

-- 生成表格
local function generateTable(board, hide)
    local cellStyle = "position:relative; border:1px solid; width:38px; height:38px;"
    local hNumStyle = "position:absolute; top:50%; left:5%; font-size:x-small; color:#25B449"
    local vNumStyle = "position:absolute; top:-5%; left:5%; font-size:x-small; color:#25B449"
    local wordStyle = "position:absolute; top:50%; left:50%; transform:translate(-50%, -50%); font-size:x-large"
    
    local wikiTable = "{| style=\"margin:auto; text-align:center;\"\n"
    for r, row in ipairs(board) do
        for c, cell in ipairs(row) do
            if not cell.char then
                wikiTable = wikiTable.."| style=\""..cellStyle.." background:#aaa;\" |\n"
            else
            	local toggleId = r.."-"..c
                wikiTable = wikiTable.."| class=\"mw-customtoggle-"..toggleId.."\" style=\""..cellStyle.."\""
                if not hide then
                	wikiTable = wikiTable.." title=\"点击查看这一格\""
                end
                wikiTable = wikiTable.." | "
                if cell.hNum then
                	wikiTable = wikiTable.."<div style=\""..hNumStyle.."\">{{zhnum4|"..cell.hNum.."}}</div>"
            	end
            	if cell.vNum then
                	wikiTable = wikiTable.."<div style=\""..vNumStyle.."\">"..cell.vNum.."</div>"
            	end
            	if not hide then
                	wikiTable = wikiTable.."<div class=\"mw-collapsible mw-collapsed\" id=\"mw-customcollapsible-"..toggleId.."\" style=\""..wordStyle.."\">"..cell.char.."</div>\n"
            	end
            	wikiTable = wikiTable.."\n"
            end
        end
        wikiTable = wikiTable.."|-\n"
    end
    wikiTable = wikiTable.."|}"
    return wikiTable
end

-- 生成提示
local function generateHints(entries, hide)
    local horzHints, vertHints = {}, {}
    local formattedStr = "%s、%s"
    if not hide then
    	formattedStr = formattedStr.."<span class=\"mw-collapsible mw-collapsed\" id=\"mw-customcollapsible-ans\"> 【%s】</span>"
	end

    for _, entry in ipairs(entries) do
    	if not hide then
	    	if entry.link and entry.title then
	    		link = string.format("[[%s|%s]]", entry.link, entry.title)
			elseif entry.link then
	    		link = string.format("[[%s]]", entry.link)
			elseif entry.title then
	    		link = string.format("%s", entry.title)
			else
				link = string.format("[[%s]]", entry.word)
			end
		end
        if entry.dir == HORZ then
            table.insert(horzHints, string.format(formattedStr, "{{zhnum4|"..entry.num.."}}", entry.hint, link))
        else
            table.insert(vertHints, string.format(formattedStr, entry.num, entry.hint, link))
        end
    end

    local hHints = table.concat(horzHints, "\n\n")
    local vHints = table.concat(vertHints, "\n\n")
    local button = ""
    if not hide then
    	button = "<span class=\"mw-ui-button mw-ui-progressive mw-customtoggle-ans\">查看所有答案</span>"
	end
    return hHints, vHints, button
end

-- 生成最终的维基文本输出
local function generateOutput(wikiTable, hHints, vHints, button)
    local output = "<templatestyles src=\"Multicol/styles.css\"/><div>\n"
    output = output.."{| class=\"multicol\" style=\"\"\n"
    output = output.."|-\n"
    output = output.."| colspan=\"2\" style=\"position:relative;\" | \n"
    output = output..wikiTable.."\n"
    output = output.."|-\n"
    output = output.."| style=\"padding:0.5em;\" | \n"
    output = output.."=== 横向 ===\n"
    output = output..hHints.."\n"
    output = output.."| style=\"padding:0.5em;\" | \n"
    output = output.."=== 纵向 ===\n"
    output = output..vHints.."\n"
    output = output.."|-\n"
    output = output.."| colspan=\"2\" style=\"text-align:right;\" | "..button.."\n"
    output = output.."|}</div>"
    return output
end

function p.main(frame)
	local args = getArgs(frame)
    local entries, hide = parseArgs(args)
	
    local board = buildBoard(entries)
    local wikiTable = generateTable(board, hide)
    local hHints, vHints, button = generateHints(entries, hide)

    return frame:preprocess(generateOutput(wikiTable, hHints, vHints, button))
end

return p