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